summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src')
-rw-r--r--apps/plugins/puzzles/src/Buildscr194
-rw-r--r--apps/plugins/puzzles/src/CHECKLST.txt70
-rw-r--r--apps/plugins/puzzles/src/LICENCE25
-rw-r--r--apps/plugins/puzzles/src/Makefile727
-rw-r--r--apps/plugins/puzzles/src/Makefile.am446
-rw-r--r--apps/plugins/puzzles/src/Makefile.cyg718
-rw-r--r--apps/plugins/puzzles/src/Makefile.doc17
-rw-r--r--apps/plugins/puzzles/src/Makefile.emcc497
-rw-r--r--apps/plugins/puzzles/src/Makefile.gnustep412
-rw-r--r--apps/plugins/puzzles/src/Makefile.gtk727
-rw-r--r--apps/plugins/puzzles/src/Makefile.in2611
-rw-r--r--apps/plugins/puzzles/src/Makefile.nestedvm556
-rw-r--r--apps/plugins/puzzles/src/Makefile.osx574
-rw-r--r--apps/plugins/puzzles/src/Makefile.vc1040
-rw-r--r--apps/plugins/puzzles/src/Makefile.wce808
-rw-r--r--apps/plugins/puzzles/src/PuzzleApplet.java650
-rw-r--r--apps/plugins/puzzles/src/README54
-rw-r--r--apps/plugins/puzzles/src/Recipe157
-rw-r--r--apps/plugins/puzzles/src/aclocal.m41832
-rwxr-xr-xapps/plugins/puzzles/src/benchmark.pl197
-rwxr-xr-xapps/plugins/puzzles/src/benchmark.sh27
-rw-r--r--apps/plugins/puzzles/src/blackbox.R19
-rw-r--r--apps/plugins/puzzles/src/blackbox.c1543
-rw-r--r--apps/plugins/puzzles/src/blackbox.html125
-rw-r--r--apps/plugins/puzzles/src/bridges.R21
-rw-r--r--apps/plugins/puzzles/src/bridges.c3262
-rw-r--r--apps/plugins/puzzles/src/bridges.html135
-rw-r--r--apps/plugins/puzzles/src/chm.but21
-rw-r--r--apps/plugins/puzzles/src/chm.css7
-rw-r--r--apps/plugins/puzzles/src/combi.c110
-rw-r--r--apps/plugins/puzzles/src/common.html285
-rwxr-xr-xapps/plugins/puzzles/src/compile347
-rw-r--r--apps/plugins/puzzles/src/config.log135
-rwxr-xr-xapps/plugins/puzzles/src/configure5739
-rw-r--r--apps/plugins/puzzles/src/configure.ac85
-rw-r--r--apps/plugins/puzzles/src/cube.R19
-rw-r--r--apps/plugins/puzzles/src/cube.c1773
-rw-r--r--apps/plugins/puzzles/src/cube.html57
-rwxr-xr-xapps/plugins/puzzles/src/depcomp791
-rwxr-xr-xapps/plugins/puzzles/src/desktop.pl52
-rw-r--r--apps/plugins/puzzles/src/devel.but4895
-rw-r--r--apps/plugins/puzzles/src/divvy.c781
-rw-r--r--apps/plugins/puzzles/src/docindex.html217
-rw-r--r--apps/plugins/puzzles/src/dominosa.R21
-rw-r--r--apps/plugins/puzzles/src/dominosa.c1748
-rw-r--r--apps/plugins/puzzles/src/dominosa.html57
-rw-r--r--apps/plugins/puzzles/src/drawing.c351
-rw-r--r--apps/plugins/puzzles/src/dsf.c192
-rw-r--r--apps/plugins/puzzles/src/emcc.c876
-rw-r--r--apps/plugins/puzzles/src/emcclib.js752
-rw-r--r--apps/plugins/puzzles/src/emccpre.js359
-rw-r--r--apps/plugins/puzzles/src/emccx.json29
-rw-r--r--apps/plugins/puzzles/src/fifteen.R22
-rw-r--r--apps/plugins/puzzles/src/fifteen.c1215
-rw-r--r--apps/plugins/puzzles/src/fifteen.html41
-rw-r--r--apps/plugins/puzzles/src/filling.R24
-rw-r--r--apps/plugins/puzzles/src/filling.c2179
-rw-r--r--apps/plugins/puzzles/src/filling.html50
-rw-r--r--apps/plugins/puzzles/src/findloop.c500
-rw-r--r--apps/plugins/puzzles/src/flip.R21
-rw-r--r--apps/plugins/puzzles/src/flip.c1349
-rw-r--r--apps/plugins/puzzles/src/flip.html54
-rw-r--r--apps/plugins/puzzles/src/flood.R19
-rw-r--r--apps/plugins/puzzles/src/flood.c1372
-rw-r--r--apps/plugins/puzzles/src/flood.html64
-rw-r--r--apps/plugins/puzzles/src/galaxies.R28
-rw-r--r--apps/plugins/puzzles/src/galaxies.c3995
-rw-r--r--apps/plugins/puzzles/src/galaxies.html60
-rw-r--r--apps/plugins/puzzles/src/gamedesc.txt39
-rw-r--r--apps/plugins/puzzles/src/grid.c3065
-rw-r--r--apps/plugins/puzzles/src/grid.h133
-rw-r--r--apps/plugins/puzzles/src/gtk.c3311
-rw-r--r--apps/plugins/puzzles/src/guess.R19
-rw-r--r--apps/plugins/puzzles/src/guess.c1518
-rw-r--r--apps/plugins/puzzles/src/guess.html94
-rw-r--r--apps/plugins/puzzles/src/html/blackbox.html16
-rw-r--r--apps/plugins/puzzles/src/html/bridges.html13
-rw-r--r--apps/plugins/puzzles/src/html/cube.html14
-rw-r--r--apps/plugins/puzzles/src/html/dominosa.html10
-rw-r--r--apps/plugins/puzzles/src/html/fifteen.html6
-rw-r--r--apps/plugins/puzzles/src/html/filling.html12
-rw-r--r--apps/plugins/puzzles/src/html/flip.html10
-rw-r--r--apps/plugins/puzzles/src/html/flood.html8
-rw-r--r--apps/plugins/puzzles/src/html/galaxies.html11
-rw-r--r--apps/plugins/puzzles/src/html/group.html52
-rw-r--r--apps/plugins/puzzles/src/html/guess.html12
-rw-r--r--apps/plugins/puzzles/src/html/inertia.html14
-rwxr-xr-xapps/plugins/puzzles/src/html/javapage.pl104
-rwxr-xr-xapps/plugins/puzzles/src/html/jspage.pl242
-rw-r--r--apps/plugins/puzzles/src/html/keen.html15
-rw-r--r--apps/plugins/puzzles/src/html/lightup.html10
-rw-r--r--apps/plugins/puzzles/src/html/loopy.html13
-rw-r--r--apps/plugins/puzzles/src/html/magnets.html17
-rw-r--r--apps/plugins/puzzles/src/html/map.html15
-rw-r--r--apps/plugins/puzzles/src/html/mines.html18
-rw-r--r--apps/plugins/puzzles/src/html/net.html17
-rw-r--r--apps/plugins/puzzles/src/html/netslide.html14
-rw-r--r--apps/plugins/puzzles/src/html/palisade.html11
-rw-r--r--apps/plugins/puzzles/src/html/pattern.html12
-rw-r--r--apps/plugins/puzzles/src/html/pearl.html13
-rw-r--r--apps/plugins/puzzles/src/html/pegs.html8
-rw-r--r--apps/plugins/puzzles/src/html/range.html21
-rw-r--r--apps/plugins/puzzles/src/html/rect.html10
-rw-r--r--apps/plugins/puzzles/src/html/samegame.html14
-rw-r--r--apps/plugins/puzzles/src/html/signpost.html14
-rw-r--r--apps/plugins/puzzles/src/html/singles.html11
-rw-r--r--apps/plugins/puzzles/src/html/sixteen.html8
-rw-r--r--apps/plugins/puzzles/src/html/slant.html9
-rw-r--r--apps/plugins/puzzles/src/html/solo.html20
-rw-r--r--apps/plugins/puzzles/src/html/tents.html20
-rw-r--r--apps/plugins/puzzles/src/html/towers.html22
-rw-r--r--apps/plugins/puzzles/src/html/tracks.html19
-rw-r--r--apps/plugins/puzzles/src/html/twiddle.html15
-rw-r--r--apps/plugins/puzzles/src/html/undead.html22
-rw-r--r--apps/plugins/puzzles/src/html/unequal.html14
-rw-r--r--apps/plugins/puzzles/src/html/unruly.html11
-rw-r--r--apps/plugins/puzzles/src/html/untangle.html5
-rw-r--r--apps/plugins/puzzles/src/icons/Makefile153
-rw-r--r--apps/plugins/puzzles/src/icons/blackbox.sav27
-rw-r--r--apps/plugins/puzzles/src/icons/bridges.sav72
-rwxr-xr-xapps/plugins/puzzles/src/icons/cicon.pl27
-rwxr-xr-xapps/plugins/puzzles/src/icons/crop.sh37
-rw-r--r--apps/plugins/puzzles/src/icons/cube.sav22
-rw-r--r--apps/plugins/puzzles/src/icons/dominosa.sav53
-rw-r--r--apps/plugins/puzzles/src/icons/fifteen.sav74
-rw-r--r--apps/plugins/puzzles/src/icons/filling.sav38
-rw-r--r--apps/plugins/puzzles/src/icons/flip.sav20
-rw-r--r--apps/plugins/puzzles/src/icons/flood.sav14
-rw-r--r--apps/plugins/puzzles/src/icons/galaxies.sav51
-rw-r--r--apps/plugins/puzzles/src/icons/guess.sav15
-rwxr-xr-xapps/plugins/puzzles/src/icons/icon.pl270
-rw-r--r--apps/plugins/puzzles/src/icons/inertia.sav30
-rw-r--r--apps/plugins/puzzles/src/icons/keen.sav62
-rw-r--r--apps/plugins/puzzles/src/icons/lightup.sav24
-rw-r--r--apps/plugins/puzzles/src/icons/loopy.sav120
-rw-r--r--apps/plugins/puzzles/src/icons/magnets.sav33
-rw-r--r--apps/plugins/puzzles/src/icons/map.sav27
-rw-r--r--apps/plugins/puzzles/src/icons/mines.sav67
-rw-r--r--apps/plugins/puzzles/src/icons/net.sav53
-rw-r--r--apps/plugins/puzzles/src/icons/netslide.sav47
-rw-r--r--apps/plugins/puzzles/src/icons/palisade.sav50
-rw-r--r--apps/plugins/puzzles/src/icons/pattern.sav29
-rw-r--r--apps/plugins/puzzles/src/icons/pearl.sav23
-rw-r--r--apps/plugins/puzzles/src/icons/pegs.sav16
-rw-r--r--apps/plugins/puzzles/src/icons/range.sav36
-rw-r--r--apps/plugins/puzzles/src/icons/rect.sav17
-rw-r--r--apps/plugins/puzzles/src/icons/samegame.sav34
-rwxr-xr-xapps/plugins/puzzles/src/icons/screenshot.sh25
-rw-r--r--apps/plugins/puzzles/src/icons/signpost.sav23
-rw-r--r--apps/plugins/puzzles/src/icons/singles.sav45
-rw-r--r--apps/plugins/puzzles/src/icons/sixteen.sav39
-rw-r--r--apps/plugins/puzzles/src/icons/slant.sav51
-rw-r--r--apps/plugins/puzzles/src/icons/solo.sav36
-rwxr-xr-xapps/plugins/puzzles/src/icons/square.pl95
-rw-r--r--apps/plugins/puzzles/src/icons/tents.sav32
-rw-r--r--apps/plugins/puzzles/src/icons/towers.sav26
-rw-r--r--apps/plugins/puzzles/src/icons/tracks.sav31
-rw-r--r--apps/plugins/puzzles/src/icons/twiddle.sav35
-rw-r--r--apps/plugins/puzzles/src/icons/undead.sav14
-rw-r--r--apps/plugins/puzzles/src/icons/unequal.sav25
-rw-r--r--apps/plugins/puzzles/src/icons/unruly.sav22
-rw-r--r--apps/plugins/puzzles/src/icons/untangle.sav16
-rw-r--r--apps/plugins/puzzles/src/icons/win16pal.xpm23
-rw-r--r--apps/plugins/puzzles/src/index.html67
-rw-r--r--apps/plugins/puzzles/src/inertia.R19
-rw-r--r--apps/plugins/puzzles/src/inertia.c2249
-rw-r--r--apps/plugins/puzzles/src/inertia.html54
-rwxr-xr-xapps/plugins/puzzles/src/install-sh501
-rw-r--r--apps/plugins/puzzles/src/intro.html39
-rw-r--r--apps/plugins/puzzles/src/keen.R25
-rw-r--r--apps/plugins/puzzles/src/keen.c2479
-rw-r--r--apps/plugins/puzzles/src/keen.html102
-rw-r--r--apps/plugins/puzzles/src/latin.c1436
-rw-r--r--apps/plugins/puzzles/src/latin.h122
-rw-r--r--apps/plugins/puzzles/src/laydomino.c291
-rw-r--r--apps/plugins/puzzles/src/licence.html33
-rw-r--r--apps/plugins/puzzles/src/lightup.R24
-rw-r--r--apps/plugins/puzzles/src/lightup.c2405
-rw-r--r--apps/plugins/puzzles/src/lightup.html98
-rw-r--r--apps/plugins/puzzles/src/list.c55
-rw-r--r--apps/plugins/puzzles/src/loopgen.c536
-rw-r--r--apps/plugins/puzzles/src/loopgen.h35
-rw-r--r--apps/plugins/puzzles/src/loopy.R31
-rw-r--r--apps/plugins/puzzles/src/loopy.c3786
-rw-r--r--apps/plugins/puzzles/src/loopy.html69
-rw-r--r--apps/plugins/puzzles/src/magnets.R24
-rw-r--r--apps/plugins/puzzles/src/magnets.c2641
-rw-r--r--apps/plugins/puzzles/src/magnets.html76
-rwxr-xr-xapps/plugins/puzzles/src/makedist.sh47
-rw-r--r--apps/plugins/puzzles/src/malloc.c53
-rw-r--r--apps/plugins/puzzles/src/map.R24
-rw-r--r--apps/plugins/puzzles/src/map.c3340
-rw-r--r--apps/plugins/puzzles/src/map.html76
-rw-r--r--apps/plugins/puzzles/src/maxflow.c461
-rw-r--r--apps/plugins/puzzles/src/maxflow.h95
-rw-r--r--apps/plugins/puzzles/src/midend.c2237
-rw-r--r--apps/plugins/puzzles/src/mines.R24
-rw-r--r--apps/plugins/puzzles/src/mines.c3250
-rw-r--r--apps/plugins/puzzles/src/mines.html82
-rw-r--r--apps/plugins/puzzles/src/misc.c376
-rwxr-xr-xapps/plugins/puzzles/src/missing215
-rwxr-xr-xapps/plugins/puzzles/src/mkauto.sh2
-rwxr-xr-xapps/plugins/puzzles/src/mkfiles.pl1807
-rw-r--r--apps/plugins/puzzles/src/nestedvm.c447
-rw-r--r--apps/plugins/puzzles/src/net.R23
-rw-r--r--apps/plugins/puzzles/src/net.c3240
-rw-r--r--apps/plugins/puzzles/src/net.html108
-rw-r--r--apps/plugins/puzzles/src/netslide.R21
-rw-r--r--apps/plugins/puzzles/src/netslide.c1893
-rw-r--r--apps/plugins/puzzles/src/netslide.html30
-rw-r--r--apps/plugins/puzzles/src/no-icon.c8
-rw-r--r--apps/plugins/puzzles/src/noicon.rc11
-rw-r--r--apps/plugins/puzzles/src/nullfe.c74
-rw-r--r--apps/plugins/puzzles/src/nullgame.R12
-rw-r--r--apps/plugins/puzzles/src/nullgame.c303
-rw-r--r--apps/plugins/puzzles/src/obfusc.c130
-rw-r--r--apps/plugins/puzzles/src/osx-help.but14
-rw-r--r--apps/plugins/puzzles/src/osx-info.plist34
-rw-r--r--apps/plugins/puzzles/src/osx.icnsbin0 -> 48589 bytes
-rw-r--r--apps/plugins/puzzles/src/osx.m1775
-rw-r--r--apps/plugins/puzzles/src/padtoolbar.bmpbin0 -> 1198 bytes
-rw-r--r--apps/plugins/puzzles/src/palisade.R21
-rw-r--r--apps/plugins/puzzles/src/palisade.c1383
-rw-r--r--apps/plugins/puzzles/src/palisade.html54
-rw-r--r--apps/plugins/puzzles/src/pattern.R25
-rw-r--r--apps/plugins/puzzles/src/pattern.c2255
-rw-r--r--apps/plugins/puzzles/src/pattern.html50
-rw-r--r--apps/plugins/puzzles/src/pearl.R23
-rw-r--r--apps/plugins/puzzles/src/pearl.c2770
-rw-r--r--apps/plugins/puzzles/src/pearl.html65
-rw-r--r--apps/plugins/puzzles/src/pegs.R21
-rw-r--r--apps/plugins/puzzles/src/pegs.c1340
-rw-r--r--apps/plugins/puzzles/src/pegs.html54
-rw-r--r--apps/plugins/puzzles/src/penrose.c629
-rw-r--r--apps/plugins/puzzles/src/penrose.h59
-rw-r--r--apps/plugins/puzzles/src/printing.c247
-rw-r--r--apps/plugins/puzzles/src/ps.c433
-rw-r--r--apps/plugins/puzzles/src/puzzles.but3401
-rw-r--r--apps/plugins/puzzles/src/puzzles.cnt167
-rw-r--r--apps/plugins/puzzles/src/puzzles.h680
-rw-r--r--apps/plugins/puzzles/src/puzzles.hlpbin0 -> 201514 bytes
-rw-r--r--apps/plugins/puzzles/src/puzzles.rc265
-rw-r--r--apps/plugins/puzzles/src/puzzles.txt3163
-rw-r--r--apps/plugins/puzzles/src/random.c351
-rw-r--r--apps/plugins/puzzles/src/range.R21
-rw-r--r--apps/plugins/puzzles/src/range.c1833
-rw-r--r--apps/plugins/puzzles/src/range.html67
-rw-r--r--apps/plugins/puzzles/src/rect.R19
-rw-r--r--apps/plugins/puzzles/src/rect.c3000
-rw-r--r--apps/plugins/puzzles/src/rect.html76
-rw-r--r--apps/plugins/puzzles/src/resource.h20
-rw-r--r--apps/plugins/puzzles/src/samegame.R19
-rw-r--r--apps/plugins/puzzles/src/samegame.c1679
-rw-r--r--apps/plugins/puzzles/src/samegame.html82
-rw-r--r--apps/plugins/puzzles/src/signpost.R23
-rw-r--r--apps/plugins/puzzles/src/signpost.c2480
-rw-r--r--apps/plugins/puzzles/src/signpost.html72
-rw-r--r--apps/plugins/puzzles/src/singles.R23
-rw-r--r--apps/plugins/puzzles/src/singles.c2004
-rw-r--r--apps/plugins/puzzles/src/singles.html67
-rw-r--r--apps/plugins/puzzles/src/sixteen.R19
-rw-r--r--apps/plugins/puzzles/src/sixteen.c1214
-rw-r--r--apps/plugins/puzzles/src/sixteen.html48
-rw-r--r--apps/plugins/puzzles/src/slant.R24
-rw-r--r--apps/plugins/puzzles/src/slant.c2278
-rw-r--r--apps/plugins/puzzles/src/slant.html64
-rw-r--r--apps/plugins/puzzles/src/solo.R24
-rw-r--r--apps/plugins/puzzles/src/solo.c5656
-rw-r--r--apps/plugins/puzzles/src/solo.html99
-rw-r--r--apps/plugins/puzzles/src/tdq.c88
-rw-r--r--apps/plugins/puzzles/src/tents.R24
-rw-r--r--apps/plugins/puzzles/src/tents.c2740
-rw-r--r--apps/plugins/puzzles/src/tents.html67
-rw-r--r--apps/plugins/puzzles/src/towers.R25
-rw-r--r--apps/plugins/puzzles/src/towers.c2104
-rw-r--r--apps/plugins/puzzles/src/towers.html88
-rw-r--r--apps/plugins/puzzles/src/tracks.R21
-rw-r--r--apps/plugins/puzzles/src/tracks.c2660
-rw-r--r--apps/plugins/puzzles/src/tracks.html63
-rw-r--r--apps/plugins/puzzles/src/tree234.c2200
-rw-r--r--apps/plugins/puzzles/src/tree234.h202
-rw-r--r--apps/plugins/puzzles/src/twiddle.R19
-rw-r--r--apps/plugins/puzzles/src/twiddle.c1319
-rw-r--r--apps/plugins/puzzles/src/twiddle.html63
-rw-r--r--apps/plugins/puzzles/src/undead.R18
-rw-r--r--apps/plugins/puzzles/src/undead.c2738
-rw-r--r--apps/plugins/puzzles/src/undead.html84
-rw-r--r--apps/plugins/puzzles/src/unequal.R27
-rw-r--r--apps/plugins/puzzles/src/unequal.c2267
-rw-r--r--apps/plugins/puzzles/src/unequal.html103
-rw-r--r--apps/plugins/puzzles/src/unfinished/README9
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.R25
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.c2198
-rw-r--r--apps/plugins/puzzles/src/unfinished/group.gap97
-rw-r--r--apps/plugins/puzzles/src/unfinished/numgame.c1290
-rw-r--r--apps/plugins/puzzles/src/unfinished/path.c786
-rw-r--r--apps/plugins/puzzles/src/unfinished/separate.R21
-rw-r--r--apps/plugins/puzzles/src/unfinished/separate.c859
-rw-r--r--apps/plugins/puzzles/src/unfinished/slide.R24
-rw-r--r--apps/plugins/puzzles/src/unfinished/slide.c2445
-rw-r--r--apps/plugins/puzzles/src/unfinished/sokoban.R19
-rw-r--r--apps/plugins/puzzles/src/unfinished/sokoban.c1479
-rw-r--r--apps/plugins/puzzles/src/unruly.R21
-rw-r--r--apps/plugins/puzzles/src/unruly.c2071
-rw-r--r--apps/plugins/puzzles/src/unruly.html63
-rw-r--r--apps/plugins/puzzles/src/untangle.R21
-rw-r--r--apps/plugins/puzzles/src/untangle.c1637
-rw-r--r--apps/plugins/puzzles/src/untangle.html45
-rw-r--r--apps/plugins/puzzles/src/version.c7
-rw-r--r--apps/plugins/puzzles/src/version.h11
-rw-r--r--apps/plugins/puzzles/src/wceinf.pl65
-rwxr-xr-xapps/plugins/puzzles/src/webpage.pl69
-rw-r--r--apps/plugins/puzzles/src/website.url2
-rw-r--r--apps/plugins/puzzles/src/windows.c3800
-rwxr-xr-xapps/plugins/puzzles/src/winiss.pl79
-rw-r--r--apps/plugins/puzzles/src/winwix.mc334
316 files changed, 169289 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/Buildscr b/apps/plugins/puzzles/src/Buildscr
new file mode 100644
index 0000000000..910981f079
--- /dev/null
+++ b/apps/plugins/puzzles/src/Buildscr
@@ -0,0 +1,194 @@
1# -*- sh -*-
2# Build script to build Puzzles.
3
4module puzzles
5
6set Version $(!builddate).$(vcsid)
7
8# Start by substituting the right version number in configure.ac.
9in puzzles do perl -i~ -pe 's/6.66/$(Version)/' configure.ac
10in puzzles do rm configure.ac~
11
12# And put it into the documentation as a versionid.
13# use perl to avoid inconsistent behaviour of echo '\v'
14in puzzles do perl -e 'print "\n\\versionid Simon Tatham'\''s Portable Puzzle Collection, version $$ARGV[0]\n"' $(Version) >> puzzles.but
15in puzzles do perl -e 'print "\n\\versionid Simon Tatham'\''s Portable Puzzle Collection, version $$ARGV[0]\n"' $(Version) >> devel.but
16
17# Write out a version.h that contains the real version number.
18in puzzles do echo '/* Generated by automated build script */' > version.h
19in puzzles do echo '$#define VER "Version $(Version)"' >> version.h
20
21# And do the same substitution in the OS X metadata. (This is a bit
22# icky in principle because it presumes that my version numbers don't
23# need XML escaping, but frankly, if they ever do then I should fix
24# them!)
25in puzzles do perl -i -pe 's/Unidentified build/$(Version)/' osx-info.plist
26
27# First build some local binaries, to run the icon build.
28in puzzles do perl mkfiles.pl -U
29in puzzles do make
30
31# Now build the screenshots and icons.
32in puzzles/icons do xvfb-run -s "-screen 0 1024x768x24" make web winicons gtkicons
33
34# Destroy the local binaries and autoconf detritus, mostly to avoid
35# wasting network bandwidth by transferring them to the delegate
36# servers.
37in puzzles do make distclean
38
39# Re-run mkfiles.pl now that it knows the icons are there.
40in puzzles do perl mkfiles.pl
41
42# Rebuild the configure script.
43in puzzles do ./mkauto.sh
44
45# Build the OS X .dmg archive.
46delegate osx
47 in puzzles do make -f Makefile.osx clean
48 in puzzles do make -f Makefile.osx release VER=-DVER=$(Version)
49 return puzzles/Puzzles.dmg
50enddelegate
51
52# Build the Windows binaries and installer, and the CHM file.
53in puzzles do make -f Makefile.doc clean
54in puzzles do make -f Makefile.doc chm
55in puzzles do make -f Makefile.doc # build help file for installer
56in puzzles do mason.pl --args '{"version":"$(Version)","descfile":"gamedesc.txt"}' winwix.mc > puzzles.wxs
57in puzzles do perl winiss.pl $(Version) gamedesc.txt > puzzles.iss
58delegate windows
59 # Ignore the poorly controlled return value from HHC, and instead
60 # just test that the output file was generated.
61 in puzzles with htmlhelp do/win hhc puzzles.hhp & type puzzles.chm >nul
62 # FIXME: Cygwin alternative?
63 in puzzles with visualstudio do/win nmake -f Makefile.vc clean
64 in puzzles with visualstudio do/win nmake -f Makefile.vc VER=-DVER=$(Version)
65 # Code-sign the binaries, if the local bob config provides a script
66 # to do so. We assume here that the script accepts an -i option to
67 # provide a 'more info' URL, and an optional -n option to provide a
68 # program name, and that it can take multiple .exe filename
69 # arguments and sign them all in place.
70 ifneq "$(winsigncode)" "" in puzzles do $(winsigncode) -i http://www.chiark.greenend.org.uk/~sgtatham/puzzles/ *.exe
71 # Build installers.
72 in puzzles with wix do/win candle puzzles.wxs && light -ext WixUIExtension -sval puzzles.wixobj
73 in puzzles with innosetup do/win iscc puzzles.iss
74 ifneq "$(winsigncode)" "" in puzzles do $(winsigncode) -i http://www.chiark.greenend.org.uk/~sgtatham/puzzles/ -n "Simon Tatham's Portable Puzzle Collection Installer" puzzles.msi Output/installer.exe
75 return puzzles/puzzles.chm
76 return puzzles/*.exe
77 return puzzles/Output/installer.exe
78 return puzzles/puzzles.msi
79enddelegate
80in puzzles do chmod +x *.exe
81
82# Build the Pocket PC binaries and CAB.
83#
84# NOTE: This part of the build script requires the Windows delegate
85# server to have the cabwiz program on its PATH. This will
86# typically be at
87#
88# C:\Program Files\Windows CE Tools\WCE420\POCKET PC 2003\Tools
89#
90# but it might not be if you've installed it somewhere else, or
91# have a different version.
92#
93# NOTE ALSO: This part of the build is commented out, for the
94# moment, because cabwiz does unhelpful things when run from within
95# a bob delegate process (or, more generally, when run from any
96# terminal-based remote login to a Windows machine, including
97# Cygwin opensshd and Windows Telnet). The symptom is that cabwiz
98# just beeps and sits there. Until I figure out how to build the
99# .cab from an automated process (and I'm willing to consider silly
100# approaches such as a third-party CAB generator), I don't think I
101# can sensibly enable this build.
102
103#in puzzles do perl wceinf.pl gamedesc.txt > puzzles.inf
104#delegate windows
105# in puzzles do cmd /c 'wcearmv4 & nmake -f Makefile.wce clean'
106# in puzzles do cmd /c 'wcearmv4 & nmake -f Makefile.wce VER=-DVER=$(Version)'
107# # Nasty piece of sh here which saves the return code from cabwiz,
108# # outputs its errors and/or warnings, and then propagates the
109# # return code back to bob. If only cabwiz could output to
110# # standard error LIKE EVERY OTHER COMMAND-LINE UTILITY IN THE
111# # WORLD, I wouldn't have to do this.
112# in puzzles do cat puzzles.inf
113# in puzzles do cmd /c 'wcearmv4 & bash -c cabwiz puzzles.inf /err cabwiz.err /cpu ARMV4'; a=$$?; cat cabwiz.err; exit $$a
114# return puzzles/puzzles.armv4.cab
115#enddelegate
116
117# Build the help file and the HTML docs.
118in puzzles do make -f Makefile.doc clean # remove CHM-target HTML
119in puzzles do make -f Makefile.doc # and rebuild help file...
120in puzzles do mkdir doc
121in puzzles do mkdir devel
122in puzzles/doc do halibut --html -Chtml-contents-filename:index.html -Chtml-index-filename:indexpage.html -Chtml-template-filename:%k.html -Chtml-template-fragment:%k ../puzzles.but
123in puzzles/devel do halibut --html -Chtml-contents-filename:index.html -Chtml-index-filename:indexpage.html -Chtml-template-filename:%k.html -Chtml-template-fragment:%k ../devel.but
124
125# Move the deliver-worthy Windows binaries (those specified in
126# gamedesc.txt, which is generated by mkfiles.pl and helpfully
127# excludes the command-line auxiliary utilities such as solosolver,
128# and nullgame.exe) into a subdirectory for easy access.
129in puzzles do mkdir winbin
130in puzzles do mv `cut -f2 -d: gamedesc.txt` winbin
131
132# Make a zip file of the Windows binaries and help files.
133in puzzles do zip -j puzzles.zip winbin/*.exe puzzles.chm puzzles.hlp puzzles.cnt
134
135# Create the source archive. (That writes the archive into the
136# _parent_ directory, so be careful when we deliver it.)
137in puzzles do ./makedist.sh $(Version)
138
139# Build the autogenerated pieces of the main web page.
140in puzzles do perl webpage.pl
141
142ifneq "$(JAVA_UNFINISHED)" "" in puzzles do perl -i~ -pe 'print "!srcdir unfinished/\n" if /!srcdir icons/' Recipe
143ifneq "$(JAVA_UNFINISHED)" "" in puzzles do ln -s unfinished/group.R .
144ifneq "$(JAVA_UNFINISHED)" "" in puzzles do perl mkfiles.pl
145
146# Build the Java applets.
147delegate nestedvm
148 in puzzles do make -f Makefile.nestedvm NESTEDVM="$$NESTEDVM" VER=-DVER=$(Version)
149 return puzzles/*.jar
150enddelegate
151
152# Build the Javascript applets. Since my master build machine doesn't
153# have the right dependencies installed for Emscripten, I do this by a
154# delegation.
155in puzzles do mkdir js # so we can tell output .js files from emcc*.js
156delegate emscripten
157 in puzzles do make -f Makefile.emcc OUTPREFIX=js/ clean
158 in puzzles do make -f Makefile.emcc OUTPREFIX=js/
159 return puzzles/js/*.js
160enddelegate
161
162# Set up .htaccess containing a redirect for the archive filename.
163in puzzles do echo "AddType application/octet-stream .chm" > .htaccess
164in puzzles do echo "AddType application/octet-stream .hlp" >> .htaccess
165in puzzles do echo "AddType application/octet-stream .cnt" >> .htaccess
166in . do set -- puzzles*.tar.gz; echo RedirectMatch temp '(.*/)'puzzles.tar.gz '$$1'"$$1" >> puzzles/.htaccess
167in puzzles do echo RedirectMatch temp '(.*/)'puzzles-installer.msi '$$1'puzzles-$(Version)-installer.msi >> .htaccess
168in puzzles do echo RedirectMatch temp '(.*/)'puzzles-installer.exe '$$1'puzzles-$(Version)-installer.exe >> .htaccess
169
170# Phew, we're done. Deliver everything!
171deliver puzzles/icons/*-web.png $@
172deliver puzzles/winbin/*.exe $@
173deliver puzzles/.htaccess $@
174deliver puzzles/doc/*.html doc/$@
175deliver puzzles/devel/*.html devel/$@
176deliver puzzles/Puzzles.dmg $@
177deliver puzzles/puzzles.chm $@
178deliver puzzles/puzzles.hlp $@
179deliver puzzles/puzzles.cnt $@
180deliver puzzles/puzzles.zip $@
181deliver puzzles/puzzles.msi puzzles-$(Version)-installer.msi
182deliver puzzles/Output/installer.exe puzzles-$(Version)-installer.exe
183deliver puzzles/*.jar java/$@
184deliver puzzles/js/*.js js/$@
185deliver puzzles/html/*.html html/$@
186deliver puzzles/html/*.pl html/$@
187deliver puzzles/wwwspans.html $@
188deliver puzzles/wwwlinks.html $@
189
190# deliver puzzles/puzzles.armv4.cab $@ # (not built at the moment)
191
192# This one isn't in the puzzles subdir, because makedist.sh left it
193# one level up.
194deliver puzzles*.tar.gz $@
diff --git a/apps/plugins/puzzles/src/CHECKLST.txt b/apps/plugins/puzzles/src/CHECKLST.txt
new file mode 100644
index 0000000000..2bef909e14
--- /dev/null
+++ b/apps/plugins/puzzles/src/CHECKLST.txt
@@ -0,0 +1,70 @@
1Useful checklists
2=================
3
4Things to remember when adding a new puzzle
5-------------------------------------------
6
7Write the source file for the new puzzle (duhh).
8
9Create a .R file for it which:
10 - defines a <puzzle>_EXTRA symbol for it if it requires auxiliary
11 object files (make sure that symbol doesn't contain the icon)
12 - adds it to the `ALL' definition, to ensure it is compiled into
13 the OS X binary
14 - adds it as a GTK build target, with the optional GTK icon
15 - adds it as a Windows build target, with the optional resource
16 file
17 - adds auxiliary solver binaries if any
18 - adds it to $(GAMES) in both the automake and GTK makefiles, for
19 `make install'
20 - adds it to list.c for the OS X binary
21 - adds it to gamedesc.txt, with its Windows executable name, display
22 name, and slightly longer description.
23
24If the puzzle is by a new author, modify the copyright notice in
25LICENCE and in puzzles.but. (Also in index.html, but that's listed
26below under website changes.)
27
28Double-check that the game structure name in the source file has
29been renamed from `nullgame', so that it'll work on OS X. Actually
30compiling it on OS X would be a good way to check this, if
31convenient.
32
33Add a documentation section in puzzles.but.
34
35Make sure there's a Windows help topic name defined in puzzles.but,
36and that it's referenced by the help topic field in the game
37structure in the source file.
38
39Check that REQUIRE_RBUTTON and/or REQUIRE_NUMPAD are set as
40appropriate.
41
42Add the new Unix binary name, and the names of any auxiliary solver
43binaries, to .gitignore.
44
45Write an instructions fragment for the webified puzzle pages, as
46html/<puzzlename>.html .
47
48Make a screenshot:
49 - create an appropriate save file in `icons'
50 - add the puzzle name to icons/Makefile
51 - set up a REDO property in icons/Makefile if the screenshot wants
52 to display a move halfway through an animation
53 - set up a CROP property in icons/Makefile if the icon wants to be
54 a sub-rectangle of the whole screenshot
55
56Don't forget to `git add' the new source file, the new .R file and the
57save file in `icons', the new .html file, and any other new files that
58might have been involved.
59
60Check in!
61
62Put the puzzle on the web:
63 - run puzzlesnap.sh
64 - adjust the copyright in index-mid.html if the puzzle is by a new
65 author
66 - check that the new puzzle has appeared on the staging web page
67 - test both Windows binary links, the docs link, the Javascript
68 version and the Java version
69 - run webupdate
70 - test all those things once more on the live website
diff --git a/apps/plugins/puzzles/src/LICENCE b/apps/plugins/puzzles/src/LICENCE
new file mode 100644
index 0000000000..4235005ea7
--- /dev/null
+++ b/apps/plugins/puzzles/src/LICENCE
@@ -0,0 +1,25 @@
1This software is copyright (c) 2004-2014 Simon Tatham.
2
3Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
4Kölker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd
5Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens.
6
7Permission is hereby granted, free of charge, to any person
8obtaining a copy of this software and associated documentation files
9(the "Software"), to deal in the Software without restriction,
10including without limitation the rights to use, copy, modify, merge,
11publish, distribute, sublicense, and/or sell copies of the Software,
12and to permit persons to whom the Software is furnished to do so,
13subject to the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25SOFTWARE.
diff --git a/apps/plugins/puzzles/src/Makefile b/apps/plugins/puzzles/src/Makefile
new file mode 100644
index 0000000000..09acd2dd39
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile
@@ -0,0 +1,727 @@
1# Makefile for puzzles under X/GTK and Unix.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6# You can define this path to point at your tools if you need to
7# TOOLPATH = /opt/gcc/bin
8CC := $(TOOLPATH)$(CC)
9# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'
10# (depending on what works on your system) if you want to enforce
11# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0'
12# if you want to enforce 2.0. The default is to try 2.0 and fall back
13# to 1.2 if it isn't found.
14GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 $$0 2>/dev/null || gtk-config $$0'
15
16CFLAGS := -O2 -Wall -ansi -pedantic -g -I./ -Iicons/ `$(GTK_CONFIG) \
17 --cflags` $(CFLAGS)
18XLIBS = `$(GTK_CONFIG) --libs` -lm
19ULIBS = -lm#
20INSTALL=install
21INSTALL_PROGRAM=$(INSTALL)
22INSTALL_DATA=$(INSTALL)
23prefix=/usr/local
24exec_prefix=$(prefix)
25bindir=$(exec_prefix)/bin
26gamesdir=$(exec_prefix)/games
27mandir=$(prefix)/man
28man1dir=$(mandir)/man1
29
30all: $(BINPREFIX)blackbox $(BINPREFIX)bridges $(BINPREFIX)cube \
31 $(BINPREFIX)dominosa $(BINPREFIX)fifteen \
32 $(BINPREFIX)fifteensolver $(BINPREFIX)filling \
33 $(BINPREFIX)fillingsolver $(BINPREFIX)flip $(BINPREFIX)flood \
34 $(BINPREFIX)galaxies $(BINPREFIX)galaxiespicture \
35 $(BINPREFIX)galaxiessolver $(BINPREFIX)guess \
36 $(BINPREFIX)inertia $(BINPREFIX)keen $(BINPREFIX)keensolver \
37 $(BINPREFIX)latincheck $(BINPREFIX)lightup \
38 $(BINPREFIX)lightupsolver $(BINPREFIX)loopy \
39 $(BINPREFIX)loopysolver $(BINPREFIX)magnets \
40 $(BINPREFIX)magnetssolver $(BINPREFIX)map \
41 $(BINPREFIX)mapsolver $(BINPREFIX)mineobfusc \
42 $(BINPREFIX)mines $(BINPREFIX)net $(BINPREFIX)netslide \
43 $(BINPREFIX)nullgame $(BINPREFIX)obfusc $(BINPREFIX)palisade \
44 $(BINPREFIX)pattern $(BINPREFIX)patternpicture \
45 $(BINPREFIX)patternsolver $(BINPREFIX)pearl \
46 $(BINPREFIX)pearlbench $(BINPREFIX)pegs $(BINPREFIX)range \
47 $(BINPREFIX)rect $(BINPREFIX)samegame $(BINPREFIX)signpost \
48 $(BINPREFIX)signpostsolver $(BINPREFIX)singles \
49 $(BINPREFIX)singlessolver $(BINPREFIX)sixteen \
50 $(BINPREFIX)slant $(BINPREFIX)slantsolver $(BINPREFIX)solo \
51 $(BINPREFIX)solosolver $(BINPREFIX)tents \
52 $(BINPREFIX)tentssolver $(BINPREFIX)towers \
53 $(BINPREFIX)towerssolver $(BINPREFIX)tracks \
54 $(BINPREFIX)twiddle $(BINPREFIX)undead $(BINPREFIX)unequal \
55 $(BINPREFIX)unequalsolver $(BINPREFIX)unruly \
56 $(BINPREFIX)unrulysolver $(BINPREFIX)untangle
57
58$(BINPREFIX)blackbox: blackbox.o drawing.o gtk.o malloc.o midend.o misc.o \
59 no-icon.o printing.o ps.o random.o version.o
60 $(CC) -o $@ blackbox.o drawing.o gtk.o malloc.o midend.o misc.o \
61 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
62 $(XLIBS)
63
64$(BINPREFIX)bridges: bridges.o drawing.o dsf.o findloop.o gtk.o malloc.o \
65 midend.o misc.o no-icon.o printing.o ps.o random.o version.o
66 $(CC) -o $@ bridges.o drawing.o dsf.o findloop.o gtk.o malloc.o \
67 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
68 $(XLFLAGS) $(XLIBS)
69
70$(BINPREFIX)cube: cube.o drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
71 printing.o ps.o random.o version.o
72 $(CC) -o $@ cube.o drawing.o gtk.o malloc.o midend.o misc.o \
73 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
74 $(XLIBS)
75
76$(BINPREFIX)dominosa: dominosa.o drawing.o gtk.o laydomino.o malloc.o \
77 midend.o misc.o no-icon.o printing.o ps.o random.o version.o
78 $(CC) -o $@ dominosa.o drawing.o gtk.o laydomino.o malloc.o midend.o \
79 misc.o no-icon.o printing.o ps.o random.o version.o \
80 $(XLFLAGS) $(XLIBS)
81
82$(BINPREFIX)fifteen: drawing.o fifteen.o gtk.o malloc.o midend.o misc.o \
83 no-icon.o printing.o ps.o random.o version.o
84 $(CC) -o $@ drawing.o fifteen.o gtk.o malloc.o midend.o misc.o \
85 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
86 $(XLIBS)
87
88$(BINPREFIX)fifteensolver: fifteen2.o malloc.o misc.o nullfe.o random.o
89 $(CC) -o $@ fifteen2.o malloc.o misc.o nullfe.o random.o $(XLFLAGS) \
90 $(ULIBS)
91
92$(BINPREFIX)filling: drawing.o dsf.o filling.o gtk.o malloc.o midend.o \
93 misc.o no-icon.o printing.o ps.o random.o version.o
94 $(CC) -o $@ drawing.o dsf.o filling.o gtk.o malloc.o midend.o misc.o \
95 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
96 $(XLIBS)
97
98$(BINPREFIX)fillingsolver: dsf.o filling2.o malloc.o misc.o nullfe.o \
99 random.o
100 $(CC) -o $@ dsf.o filling2.o malloc.o misc.o nullfe.o random.o \
101 $(XLFLAGS) $(ULIBS)
102
103$(BINPREFIX)flip: drawing.o flip.o gtk.o malloc.o midend.o misc.o no-icon.o \
104 printing.o ps.o random.o tree234.o version.o
105 $(CC) -o $@ drawing.o flip.o gtk.o malloc.o midend.o misc.o \
106 no-icon.o printing.o ps.o random.o tree234.o version.o \
107 $(XLFLAGS) $(XLIBS)
108
109$(BINPREFIX)flood: drawing.o flood.o gtk.o malloc.o midend.o misc.o \
110 no-icon.o printing.o ps.o random.o version.o
111 $(CC) -o $@ drawing.o flood.o gtk.o malloc.o midend.o misc.o \
112 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
113 $(XLIBS)
114
115$(BINPREFIX)galaxies: drawing.o dsf.o galaxies.o gtk.o malloc.o midend.o \
116 misc.o no-icon.o printing.o ps.o random.o version.o
117 $(CC) -o $@ drawing.o dsf.o galaxies.o gtk.o malloc.o midend.o \
118 misc.o no-icon.o printing.o ps.o random.o version.o \
119 $(XLFLAGS) $(XLIBS)
120
121$(BINPREFIX)galaxiespicture: dsf.o galaxie4.o malloc.o misc.o nullfe.o \
122 random.o
123 $(CC) -o $@ dsf.o galaxie4.o malloc.o misc.o nullfe.o random.o -lm \
124 $(XLFLAGS) $(ULIBS)
125
126$(BINPREFIX)galaxiessolver: dsf.o galaxie2.o malloc.o misc.o nullfe.o \
127 random.o
128 $(CC) -o $@ dsf.o galaxie2.o malloc.o misc.o nullfe.o random.o -lm \
129 $(XLFLAGS) $(ULIBS)
130
131$(BINPREFIX)guess: drawing.o gtk.o guess.o malloc.o midend.o misc.o \
132 no-icon.o printing.o ps.o random.o version.o
133 $(CC) -o $@ drawing.o gtk.o guess.o malloc.o midend.o misc.o \
134 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
135 $(XLIBS)
136
137$(BINPREFIX)inertia: drawing.o gtk.o inertia.o malloc.o midend.o misc.o \
138 no-icon.o printing.o ps.o random.o version.o
139 $(CC) -o $@ drawing.o gtk.o inertia.o malloc.o midend.o misc.o \
140 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
141 $(XLIBS)
142
143$(BINPREFIX)keen: drawing.o dsf.o gtk.o keen.o latin.o malloc.o maxflow.o \
144 midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o \
145 version.o
146 $(CC) -o $@ drawing.o dsf.o gtk.o keen.o latin.o malloc.o maxflow.o \
147 midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o \
148 version.o $(XLFLAGS) $(XLIBS)
149
150$(BINPREFIX)keensolver: dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o \
151 nullfe.o random.o tree234.o
152 $(CC) -o $@ dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o \
153 nullfe.o random.o tree234.o $(XLFLAGS) $(ULIBS)
154
155$(BINPREFIX)latincheck: latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \
156 tree234.o
157 $(CC) -o $@ latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \
158 tree234.o $(XLFLAGS) $(ULIBS)
159
160$(BINPREFIX)lightup: combi.o drawing.o gtk.o lightup.o malloc.o midend.o \
161 misc.o no-icon.o printing.o ps.o random.o version.o
162 $(CC) -o $@ combi.o drawing.o gtk.o lightup.o malloc.o midend.o \
163 misc.o no-icon.o printing.o ps.o random.o version.o \
164 $(XLFLAGS) $(XLIBS)
165
166$(BINPREFIX)lightupsolver: combi.o lightup2.o malloc.o misc.o nullfe.o \
167 random.o
168 $(CC) -o $@ combi.o lightup2.o malloc.o misc.o nullfe.o random.o \
169 $(XLFLAGS) $(ULIBS)
170
171$(BINPREFIX)loopy: drawing.o dsf.o grid.o gtk.o loopgen.o loopy.o malloc.o \
172 midend.o misc.o no-icon.o penrose.o printing.o ps.o random.o \
173 tree234.o version.o
174 $(CC) -o $@ drawing.o dsf.o grid.o gtk.o loopgen.o loopy.o malloc.o \
175 midend.o misc.o no-icon.o penrose.o printing.o ps.o random.o \
176 tree234.o version.o $(XLFLAGS) $(XLIBS)
177
178$(BINPREFIX)loopysolver: dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o \
179 nullfe.o penrose.o random.o tree234.o
180 $(CC) -o $@ dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o nullfe.o \
181 penrose.o random.o tree234.o -lm $(XLFLAGS) $(ULIBS)
182
183$(BINPREFIX)magnets: drawing.o gtk.o laydomino.o magnets.o malloc.o midend.o \
184 misc.o no-icon.o printing.o ps.o random.o version.o
185 $(CC) -o $@ drawing.o gtk.o laydomino.o magnets.o malloc.o midend.o \
186 misc.o no-icon.o printing.o ps.o random.o version.o \
187 $(XLFLAGS) $(XLIBS)
188
189$(BINPREFIX)magnetssolver: laydomino.o magnets2.o malloc.o misc.o nullfe.o \
190 random.o
191 $(CC) -o $@ laydomino.o magnets2.o malloc.o misc.o nullfe.o random.o \
192 -lm $(XLFLAGS) $(ULIBS)
193
194$(BINPREFIX)map: drawing.o dsf.o gtk.o malloc.o map.o midend.o misc.o \
195 no-icon.o printing.o ps.o random.o version.o
196 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o map.o midend.o misc.o \
197 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
198 $(XLIBS)
199
200$(BINPREFIX)mapsolver: dsf.o malloc.o map2.o misc.o nullfe.o random.o
201 $(CC) -o $@ dsf.o malloc.o map2.o misc.o nullfe.o random.o -lm \
202 $(XLFLAGS) $(ULIBS)
203
204$(BINPREFIX)mineobfusc: malloc.o mines2.o misc.o nullfe.o random.o tree234.o
205 $(CC) -o $@ malloc.o mines2.o misc.o nullfe.o random.o tree234.o \
206 $(XLFLAGS) $(ULIBS)
207
208$(BINPREFIX)mines: drawing.o gtk.o malloc.o midend.o mines.o misc.o \
209 no-icon.o printing.o ps.o random.o tree234.o version.o
210 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o mines.o misc.o \
211 no-icon.o printing.o ps.o random.o tree234.o version.o \
212 $(XLFLAGS) $(XLIBS)
213
214$(BINPREFIX)net: drawing.o dsf.o findloop.o gtk.o malloc.o midend.o misc.o \
215 net.o no-icon.o printing.o ps.o random.o tree234.o version.o
216 $(CC) -o $@ drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
217 misc.o net.o no-icon.o printing.o ps.o random.o tree234.o \
218 version.o $(XLFLAGS) $(XLIBS)
219
220$(BINPREFIX)netslide: drawing.o gtk.o malloc.o midend.o misc.o netslide.o \
221 no-icon.o printing.o ps.o random.o tree234.o version.o
222 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o netslide.o \
223 no-icon.o printing.o ps.o random.o tree234.o version.o \
224 $(XLFLAGS) $(XLIBS)
225
226$(BINPREFIX)nullgame: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
227 nullgame.o printing.o ps.o random.o version.o
228 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
229 nullgame.o printing.o ps.o random.o version.o $(XLFLAGS) \
230 $(XLIBS)
231
232$(BINPREFIX)obfusc: malloc.o misc.o nullfe.o obfusc.o random.o
233 $(CC) -o $@ malloc.o misc.o nullfe.o obfusc.o random.o $(XLFLAGS) \
234 $(ULIBS)
235
236$(BINPREFIX)palisade: divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
237 no-icon.o palisade.o printing.o ps.o random.o version.o
238 $(CC) -o $@ divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
239 no-icon.o palisade.o printing.o ps.o random.o version.o \
240 $(XLFLAGS) $(XLIBS)
241
242$(BINPREFIX)pattern: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
243 pattern.o printing.o ps.o random.o version.o
244 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
245 pattern.o printing.o ps.o random.o version.o $(XLFLAGS) \
246 $(XLIBS)
247
248$(BINPREFIX)patternpicture: malloc.o misc.o nullfe.o pattern4.o random.o
249 $(CC) -o $@ malloc.o misc.o nullfe.o pattern4.o random.o $(XLFLAGS) \
250 $(ULIBS)
251
252$(BINPREFIX)patternsolver: malloc.o misc.o nullfe.o pattern2.o random.o
253 $(CC) -o $@ malloc.o misc.o nullfe.o pattern2.o random.o $(XLFLAGS) \
254 $(ULIBS)
255
256$(BINPREFIX)pearl: drawing.o dsf.o grid.o gtk.o loopgen.o malloc.o midend.o \
257 misc.o no-icon.o pearl.o penrose.o printing.o ps.o random.o \
258 tdq.o tree234.o version.o
259 $(CC) -o $@ drawing.o dsf.o grid.o gtk.o loopgen.o malloc.o midend.o \
260 misc.o no-icon.o pearl.o penrose.o printing.o ps.o random.o \
261 tdq.o tree234.o version.o $(XLFLAGS) $(XLIBS)
262
263$(BINPREFIX)pearlbench: dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o \
264 pearl2.o penrose.o random.o tdq.o tree234.o
265 $(CC) -o $@ dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o pearl2.o \
266 penrose.o random.o tdq.o tree234.o -lm $(XLFLAGS) $(ULIBS)
267
268$(BINPREFIX)pegs: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o pegs.o \
269 printing.o ps.o random.o tree234.o version.o
270 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
271 pegs.o printing.o ps.o random.o tree234.o version.o \
272 $(XLFLAGS) $(XLIBS)
273
274$(BINPREFIX)range: drawing.o dsf.o gtk.o malloc.o midend.o misc.o no-icon.o \
275 printing.o ps.o random.o range.o version.o
276 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o midend.o misc.o no-icon.o \
277 printing.o ps.o random.o range.o version.o $(XLFLAGS) \
278 $(XLIBS)
279
280$(BINPREFIX)rect: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
281 printing.o ps.o random.o rect.o version.o
282 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
283 printing.o ps.o random.o rect.o version.o $(XLFLAGS) \
284 $(XLIBS)
285
286$(BINPREFIX)samegame: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
287 printing.o ps.o random.o samegame.o version.o
288 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
289 printing.o ps.o random.o samegame.o version.o $(XLFLAGS) \
290 $(XLIBS)
291
292$(BINPREFIX)signpost: drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
293 no-icon.o printing.o ps.o random.o signpost.o version.o
294 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o midend.o misc.o no-icon.o \
295 printing.o ps.o random.o signpost.o version.o $(XLFLAGS) \
296 $(XLIBS)
297
298$(BINPREFIX)signpostsolver: dsf.o malloc.o misc.o nullfe.o random.o \
299 signpos2.o
300 $(CC) -o $@ dsf.o malloc.o misc.o nullfe.o random.o signpos2.o -lm \
301 $(XLFLAGS) $(ULIBS)
302
303$(BINPREFIX)singles: drawing.o dsf.o gtk.o latin.o malloc.o maxflow.o \
304 midend.o misc.o no-icon.o printing.o ps.o random.o singles.o \
305 tree234.o version.o
306 $(CC) -o $@ drawing.o dsf.o gtk.o latin.o malloc.o maxflow.o \
307 midend.o misc.o no-icon.o printing.o ps.o random.o singles.o \
308 tree234.o version.o $(XLFLAGS) $(XLIBS)
309
310$(BINPREFIX)singlessolver: dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o \
311 random.o singles3.o tree234.o
312 $(CC) -o $@ dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o \
313 random.o singles3.o tree234.o $(XLFLAGS) $(ULIBS)
314
315$(BINPREFIX)sixteen: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
316 printing.o ps.o random.o sixteen.o version.o
317 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
318 printing.o ps.o random.o sixteen.o version.o $(XLFLAGS) \
319 $(XLIBS)
320
321$(BINPREFIX)slant: drawing.o dsf.o findloop.o gtk.o malloc.o midend.o misc.o \
322 no-icon.o printing.o ps.o random.o slant.o version.o
323 $(CC) -o $@ drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
324 misc.o no-icon.o printing.o ps.o random.o slant.o version.o \
325 $(XLFLAGS) $(XLIBS)
326
327$(BINPREFIX)slantsolver: dsf.o findloop.o malloc.o misc.o nullfe.o random.o \
328 slant2.o
329 $(CC) -o $@ dsf.o findloop.o malloc.o misc.o nullfe.o random.o \
330 slant2.o $(XLFLAGS) $(ULIBS)
331
332$(BINPREFIX)solo: divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
333 no-icon.o printing.o ps.o random.o solo.o version.o
334 $(CC) -o $@ divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
335 no-icon.o printing.o ps.o random.o solo.o version.o \
336 $(XLFLAGS) $(XLIBS)
337
338$(BINPREFIX)solosolver: divvy.o dsf.o malloc.o misc.o nullfe.o random.o \
339 solo2.o
340 $(CC) -o $@ divvy.o dsf.o malloc.o misc.o nullfe.o random.o solo2.o \
341 $(XLFLAGS) $(ULIBS)
342
343$(BINPREFIX)tents: drawing.o dsf.o gtk.o malloc.o maxflow.o midend.o misc.o \
344 no-icon.o printing.o ps.o random.o tents.o version.o
345 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o maxflow.o midend.o misc.o \
346 no-icon.o printing.o ps.o random.o tents.o version.o \
347 $(XLFLAGS) $(XLIBS)
348
349$(BINPREFIX)tentssolver: dsf.o malloc.o maxflow.o misc.o nullfe.o random.o \
350 tents3.o
351 $(CC) -o $@ dsf.o malloc.o maxflow.o misc.o nullfe.o random.o \
352 tents3.o $(XLFLAGS) $(ULIBS)
353
354$(BINPREFIX)towers: drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
355 misc.o no-icon.o printing.o ps.o random.o towers.o tree234.o \
356 version.o
357 $(CC) -o $@ drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
358 misc.o no-icon.o printing.o ps.o random.o towers.o tree234.o \
359 version.o $(XLFLAGS) $(XLIBS)
360
361$(BINPREFIX)towerssolver: latin6.o malloc.o maxflow.o misc.o nullfe.o \
362 random.o towers2.o tree234.o
363 $(CC) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
364 towers2.o tree234.o $(XLFLAGS) $(ULIBS)
365
366$(BINPREFIX)tracks: drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
367 misc.o no-icon.o printing.o ps.o random.o tracks.o version.o
368 $(CC) -o $@ drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
369 misc.o no-icon.o printing.o ps.o random.o tracks.o version.o \
370 $(XLFLAGS) $(XLIBS)
371
372$(BINPREFIX)twiddle: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
373 printing.o ps.o random.o twiddle.o version.o
374 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
375 printing.o ps.o random.o twiddle.o version.o $(XLFLAGS) \
376 $(XLIBS)
377
378$(BINPREFIX)undead: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
379 printing.o ps.o random.o undead.o version.o
380 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
381 printing.o ps.o random.o undead.o version.o $(XLFLAGS) \
382 $(XLIBS)
383
384$(BINPREFIX)unequal: drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
385 misc.o no-icon.o printing.o ps.o random.o tree234.o \
386 unequal.o version.o
387 $(CC) -o $@ drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
388 misc.o no-icon.o printing.o ps.o random.o tree234.o \
389 unequal.o version.o $(XLFLAGS) $(XLIBS)
390
391$(BINPREFIX)unequalsolver: latin6.o malloc.o maxflow.o misc.o nullfe.o \
392 random.o tree234.o unequal2.o
393 $(CC) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
394 tree234.o unequal2.o $(XLFLAGS) $(ULIBS)
395
396$(BINPREFIX)unruly: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
397 printing.o ps.o random.o unruly.o version.o
398 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
399 printing.o ps.o random.o unruly.o version.o $(XLFLAGS) \
400 $(XLIBS)
401
402$(BINPREFIX)unrulysolver: malloc.o misc.o nullfe.o random.o unruly2.o
403 $(CC) -o $@ malloc.o misc.o nullfe.o random.o unruly2.o $(XLFLAGS) \
404 $(ULIBS)
405
406$(BINPREFIX)untangle: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
407 printing.o ps.o random.o tree234.o untangle.o version.o
408 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
409 printing.o ps.o random.o tree234.o untangle.o version.o \
410 $(XLFLAGS) $(XLIBS)
411
412blackbox.o: ./blackbox.c ./puzzles.h
413 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
414blackbo3.o: ./blackbox.c ./puzzles.h
415 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
416bridges.o: ./bridges.c ./puzzles.h
417 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
418bridges3.o: ./bridges.c ./puzzles.h
419 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
420combi.o: ./combi.c ./puzzles.h
421 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
422cube.o: ./cube.c ./puzzles.h
423 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
424cube3.o: ./cube.c ./puzzles.h
425 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
426divvy.o: ./divvy.c ./puzzles.h
427 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
428dominosa.o: ./dominosa.c ./puzzles.h
429 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
430dominos3.o: ./dominosa.c ./puzzles.h
431 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
432drawing.o: ./drawing.c ./puzzles.h
433 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
434dsf.o: ./dsf.c ./puzzles.h
435 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
436fifteen.o: ./fifteen.c ./puzzles.h
437 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
438fifteen5.o: ./fifteen.c ./puzzles.h
439 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
440fifteen2.o: ./fifteen.c ./puzzles.h
441 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
442filling.o: ./filling.c ./puzzles.h
443 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
444filling5.o: ./filling.c ./puzzles.h
445 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
446filling2.o: ./filling.c ./puzzles.h
447 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
448findloop.o: ./findloop.c ./puzzles.h
449 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
450flip.o: ./flip.c ./puzzles.h ./tree234.h
451 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
452flip3.o: ./flip.c ./puzzles.h ./tree234.h
453 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
454flood.o: ./flood.c ./puzzles.h
455 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
456flood3.o: ./flood.c ./puzzles.h
457 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
458galaxies.o: ./galaxies.c ./puzzles.h
459 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
460galaxie7.o: ./galaxies.c ./puzzles.h
461 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
462galaxie4.o: ./galaxies.c ./puzzles.h
463 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
464galaxie2.o: ./galaxies.c ./puzzles.h
465 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
466grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h
467 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
468gtk.o: ./gtk.c ./puzzles.h
469 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
470guess.o: ./guess.c ./puzzles.h
471 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
472guess3.o: ./guess.c ./puzzles.h
473 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
474inertia.o: ./inertia.c ./puzzles.h
475 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
476inertia3.o: ./inertia.c ./puzzles.h
477 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
478keen.o: ./keen.c ./puzzles.h ./latin.h
479 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
480keen5.o: ./keen.c ./puzzles.h ./latin.h
481 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
482keen2.o: ./keen.c ./puzzles.h ./latin.h
483 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
484latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
485 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
486latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
487 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@
488latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
489 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
490laydomino.o: ./laydomino.c ./puzzles.h
491 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
492lightup.o: ./lightup.c ./puzzles.h
493 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
494lightup5.o: ./lightup.c ./puzzles.h
495 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
496lightup2.o: ./lightup.c ./puzzles.h
497 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
498list.o: ./list.c ./puzzles.h
499 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
500loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
501 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
502loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
503 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
504loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
505 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
506loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
507 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
508magnets.o: ./magnets.c ./puzzles.h
509 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
510magnets5.o: ./magnets.c ./puzzles.h
511 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
512magnets2.o: ./magnets.c ./puzzles.h
513 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
514malloc.o: ./malloc.c ./puzzles.h
515 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
516map.o: ./map.c ./puzzles.h
517 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
518map5.o: ./map.c ./puzzles.h
519 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
520map2.o: ./map.c ./puzzles.h
521 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
522maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h
523 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
524midend.o: ./midend.c ./puzzles.h
525 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
526mines.o: ./mines.c ./tree234.h ./puzzles.h
527 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
528mines5.o: ./mines.c ./tree234.h ./puzzles.h
529 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
530mines2.o: ./mines.c ./tree234.h ./puzzles.h
531 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@
532misc.o: ./misc.c ./puzzles.h
533 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
534net.o: ./net.c ./puzzles.h ./tree234.h
535 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
536net3.o: ./net.c ./puzzles.h ./tree234.h
537 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
538netslide.o: ./netslide.c ./puzzles.h ./tree234.h
539 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
540netslid3.o: ./netslide.c ./puzzles.h ./tree234.h
541 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
542no-icon.o: ./no-icon.c
543 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
544nullfe.o: ./nullfe.c ./puzzles.h
545 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
546nullgame.o: ./nullgame.c ./puzzles.h
547 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
548obfusc.o: ./obfusc.c ./puzzles.h
549 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
550osx.o: ./osx.m ./puzzles.h
551 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
552palisade.o: ./palisade.c ./puzzles.h
553 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
554palisad3.o: ./palisade.c ./puzzles.h
555 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
556pattern.o: ./pattern.c ./puzzles.h
557 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
558pattern7.o: ./pattern.c ./puzzles.h
559 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
560pattern4.o: ./pattern.c ./puzzles.h
561 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
562pattern2.o: ./pattern.c ./puzzles.h
563 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
564pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
565 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
566pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
567 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
568pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
569 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
570pegs.o: ./pegs.c ./puzzles.h ./tree234.h
571 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
572pegs3.o: ./pegs.c ./puzzles.h ./tree234.h
573 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
574penrose.o: ./penrose.c ./puzzles.h ./penrose.h
575 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
576printing.o: ./printing.c ./puzzles.h
577 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
578ps.o: ./ps.c ./puzzles.h
579 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
580random.o: ./random.c ./puzzles.h
581 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
582range.o: ./range.c ./puzzles.h
583 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
584range3.o: ./range.c ./puzzles.h
585 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
586rect.o: ./rect.c ./puzzles.h
587 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
588rect3.o: ./rect.c ./puzzles.h
589 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
590samegame.o: ./samegame.c ./puzzles.h
591 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
592samegam3.o: ./samegame.c ./puzzles.h
593 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
594signpost.o: ./signpost.c ./puzzles.h
595 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
596signpos5.o: ./signpost.c ./puzzles.h
597 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
598signpos2.o: ./signpost.c ./puzzles.h
599 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
600singles.o: ./singles.c ./puzzles.h ./latin.h
601 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
602singles5.o: ./singles.c ./puzzles.h ./latin.h
603 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
604singles3.o: ./singles.c ./puzzles.h ./latin.h
605 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
606sixteen.o: ./sixteen.c ./puzzles.h
607 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
608sixteen3.o: ./sixteen.c ./puzzles.h
609 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
610slant.o: ./slant.c ./puzzles.h
611 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
612slant5.o: ./slant.c ./puzzles.h
613 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
614slant2.o: ./slant.c ./puzzles.h
615 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
616solo.o: ./solo.c ./puzzles.h
617 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
618solo5.o: ./solo.c ./puzzles.h
619 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
620solo2.o: ./solo.c ./puzzles.h
621 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
622tdq.o: ./tdq.c ./puzzles.h
623 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
624tents.o: ./tents.c ./puzzles.h ./maxflow.h
625 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
626tents5.o: ./tents.c ./puzzles.h ./maxflow.h
627 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
628tents3.o: ./tents.c ./puzzles.h ./maxflow.h
629 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
630towers.o: ./towers.c ./puzzles.h ./latin.h
631 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
632towers5.o: ./towers.c ./puzzles.h ./latin.h
633 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
634towers2.o: ./towers.c ./puzzles.h ./latin.h
635 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
636tracks.o: ./tracks.c ./puzzles.h
637 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
638tracks3.o: ./tracks.c ./puzzles.h
639 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
640tree234.o: ./tree234.c ./tree234.h ./puzzles.h
641 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
642twiddle.o: ./twiddle.c ./puzzles.h
643 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
644twiddle3.o: ./twiddle.c ./puzzles.h
645 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
646undead.o: ./undead.c ./puzzles.h
647 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
648undead3.o: ./undead.c ./puzzles.h
649 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
650unequal.o: ./unequal.c ./puzzles.h ./latin.h
651 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
652unequal5.o: ./unequal.c ./puzzles.h ./latin.h
653 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
654unequal2.o: ./unequal.c ./puzzles.h ./latin.h
655 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
656unruly.o: ./unruly.c ./puzzles.h
657 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
658unruly5.o: ./unruly.c ./puzzles.h
659 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
660unruly2.o: ./unruly.c ./puzzles.h
661 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
662untangle.o: ./untangle.c ./puzzles.h ./tree234.h
663 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
664untangl3.o: ./untangle.c ./puzzles.h ./tree234.h
665 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
666version.o: ./version.c ./version.h
667 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
668windows.o: ./windows.c ./puzzles.h ./resource.h
669 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
670windows1.o: ./windows.c ./puzzles.h ./resource.h
671 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
672
673GAMES += blackbox
674GAMES += bridges
675GAMES += cube
676GAMES += dominosa
677GAMES += fifteen
678GAMES += filling
679GAMES += flip
680GAMES += flood
681GAMES += galaxies
682GAMES += guess
683GAMES += inertia
684GAMES += keen
685GAMES += lightup
686GAMES += loopy
687GAMES += magnets
688GAMES += map
689GAMES += mines
690GAMES += net
691GAMES += netslide
692GAMES += palisade
693GAMES += pattern
694GAMES += pearl
695GAMES += pegs
696GAMES += range
697GAMES += rect
698GAMES += samegame
699GAMES += signpost
700GAMES += singles
701GAMES += sixteen
702GAMES += slant
703GAMES += solo
704GAMES += tents
705GAMES += towers
706GAMES += tracks
707GAMES += twiddle
708GAMES += undead
709GAMES += unequal
710GAMES += unruly
711GAMES += untangle
712install:
713 for i in $(GAMES); do \
714 $(INSTALL_PROGRAM) -m 755 $(BINPREFIX)$$i $(DESTDIR)$(gamesdir)/$(BINPREFIX)$$i \
715 || exit 1; \
716 done
717test: benchmark.html benchmark.txt
718
719benchmark.html: benchmark.txt benchmark.pl
720 ./benchmark.pl benchmark.txt > $@
721
722benchmark.txt: benchmark.sh $(GAMES)
723 ./benchmark.sh > $@
724
725
726clean:
727 rm -f *.o $(BINPREFIX)blackbox $(BINPREFIX)bridges $(BINPREFIX)cube $(BINPREFIX)dominosa $(BINPREFIX)fifteen $(BINPREFIX)fifteensolver $(BINPREFIX)filling $(BINPREFIX)fillingsolver $(BINPREFIX)flip $(BINPREFIX)flood $(BINPREFIX)galaxies $(BINPREFIX)galaxiespicture $(BINPREFIX)galaxiessolver $(BINPREFIX)guess $(BINPREFIX)inertia $(BINPREFIX)keen $(BINPREFIX)keensolver $(BINPREFIX)latincheck $(BINPREFIX)lightup $(BINPREFIX)lightupsolver $(BINPREFIX)loopy $(BINPREFIX)loopysolver $(BINPREFIX)magnets $(BINPREFIX)magnetssolver $(BINPREFIX)map $(BINPREFIX)mapsolver $(BINPREFIX)mineobfusc $(BINPREFIX)mines $(BINPREFIX)net $(BINPREFIX)netslide $(BINPREFIX)nullgame $(BINPREFIX)obfusc $(BINPREFIX)palisade $(BINPREFIX)pattern $(BINPREFIX)patternpicture $(BINPREFIX)patternsolver $(BINPREFIX)pearl $(BINPREFIX)pearlbench $(BINPREFIX)pegs $(BINPREFIX)range $(BINPREFIX)rect $(BINPREFIX)samegame $(BINPREFIX)signpost $(BINPREFIX)signpostsolver $(BINPREFIX)singles $(BINPREFIX)singlessolver $(BINPREFIX)sixteen $(BINPREFIX)slant $(BINPREFIX)slantsolver $(BINPREFIX)solo $(BINPREFIX)solosolver $(BINPREFIX)tents $(BINPREFIX)tentssolver $(BINPREFIX)towers $(BINPREFIX)towerssolver $(BINPREFIX)tracks $(BINPREFIX)twiddle $(BINPREFIX)undead $(BINPREFIX)unequal $(BINPREFIX)unequalsolver $(BINPREFIX)unruly $(BINPREFIX)unrulysolver $(BINPREFIX)untangle
diff --git a/apps/plugins/puzzles/src/Makefile.am b/apps/plugins/puzzles/src/Makefile.am
new file mode 100644
index 0000000000..6e03ed49a5
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.am
@@ -0,0 +1,446 @@
1# Makefile.am for puzzles under Unix with Autoconf/Automake.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6GAMES =
7noinst_PROGRAMS = blackbox bridges cube dominosa fifteen fifteensolver \
8 filling fillingsolver flip flood galaxies galaxiespicture \
9 galaxiessolver guess inertia keen keensolver latincheck \
10 lightup lightupsolver loopy loopysolver magnets \
11 magnetssolver map mapsolver mineobfusc mines net netslide \
12 nullgame obfusc palisade pattern patternpicture \
13 patternsolver pearl pearlbench pegs range rect samegame \
14 signpost signpostsolver singles singlessolver sixteen slant \
15 slantsolver solo solosolver tents tentssolver towers \
16 towerssolver tracks twiddle undead unequal unequalsolver \
17 unruly unrulysolver untangle
18AUTOMAKE_OPTIONS = subdir-objects
19
20allsources = ./blackbox.c ./bridges.c ./combi.c ./cube.c ./divvy.c \
21 ./dominosa.c ./drawing.c ./dsf.c ./fifteen.c ./filling.c \
22 ./findloop.c ./flip.c ./flood.c ./galaxies.c ./grid.c \
23 ./grid.h ./gtk.c ./guess.c ./inertia.c ./keen.c ./latin.c \
24 ./latin.h ./laydomino.c ./lightup.c ./list.c ./loopgen.c \
25 ./loopgen.h ./loopy.c ./magnets.c ./malloc.c ./map.c \
26 ./maxflow.c ./maxflow.h ./midend.c ./mines.c ./misc.c \
27 ./net.c ./netslide.c ./no-icon.c ./nullfe.c ./nullgame.c \
28 ./obfusc.c ./osx.m ./palisade.c ./pattern.c ./pearl.c \
29 ./pegs.c ./penrose.c ./penrose.h ./printing.c ./ps.c \
30 ./puzzles.h ./random.c ./range.c ./rect.c ./resource.h \
31 ./samegame.c ./signpost.c ./singles.c ./sixteen.c ./slant.c \
32 ./solo.c ./tdq.c ./tents.c ./towers.c ./tracks.c ./tree234.c \
33 ./tree234.h ./twiddle.c ./undead.c ./unequal.c ./unruly.c \
34 ./untangle.c ./version.c ./version.h ./windows.c
35
36AM_CPPFLAGS = -I$(srcdir)/./ -I$(srcdir)/icons/
37AM_CFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS)
38blackbox_SOURCES = ./blackbox.c ./drawing.c ./gtk.c ./malloc.c ./midend.c \
39 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
40 ./random.c ./version.c ./version.h
41blackbox_LDADD = $(GTK_LIBS) -lm
42
43bridges_SOURCES = ./bridges.c ./drawing.c ./dsf.c ./findloop.c ./gtk.c \
44 ./malloc.c ./midend.c ./misc.c ./no-icon.c ./printing.c \
45 ./ps.c ./puzzles.h ./random.c ./version.c ./version.h
46bridges_LDADD = $(GTK_LIBS) -lm
47
48cube_SOURCES = ./cube.c ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
49 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
50 ./version.c ./version.h
51cube_LDADD = $(GTK_LIBS) -lm
52
53dominosa_SOURCES = ./dominosa.c ./drawing.c ./gtk.c ./laydomino.c ./malloc.c \
54 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
55 ./puzzles.h ./random.c ./version.c ./version.h
56dominosa_LDADD = $(GTK_LIBS) -lm
57
58fifteen_SOURCES = ./drawing.c ./fifteen.c ./gtk.c ./malloc.c ./midend.c \
59 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
60 ./random.c ./version.c ./version.h
61fifteen_LDADD = $(GTK_LIBS) -lm
62
63fifteensolver_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
64 ./random.c
65fifteensolver_LDADD = libfifteen2_a-fifteen.$(OBJEXT) -lm
66
67filling_SOURCES = ./drawing.c ./dsf.c ./filling.c ./gtk.c ./malloc.c \
68 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
69 ./puzzles.h ./random.c ./version.c ./version.h
70filling_LDADD = $(GTK_LIBS) -lm
71
72fillingsolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
73 ./random.c
74fillingsolver_LDADD = libfilling2_a-filling.$(OBJEXT) -lm
75
76flip_SOURCES = ./drawing.c ./flip.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
77 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
78 ./tree234.c ./tree234.h ./version.c ./version.h
79flip_LDADD = $(GTK_LIBS) -lm
80
81flood_SOURCES = ./drawing.c ./flood.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
82 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
83 ./version.c ./version.h
84flood_LDADD = $(GTK_LIBS) -lm
85
86galaxies_SOURCES = ./drawing.c ./dsf.c ./galaxies.c ./gtk.c ./malloc.c \
87 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
88 ./puzzles.h ./random.c ./version.c ./version.h
89galaxies_LDADD = $(GTK_LIBS) -lm
90
91galaxiespicture_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
92 ./random.c
93galaxiespicture_LDADD = libgalaxie4_a-galaxies.$(OBJEXT) -lm
94
95galaxiessolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
96 ./random.c
97galaxiessolver_LDADD = libgalaxie2_a-galaxies.$(OBJEXT) -lm
98
99guess_SOURCES = ./drawing.c ./gtk.c ./guess.c ./malloc.c ./midend.c ./misc.c \
100 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
101 ./version.c ./version.h
102guess_LDADD = $(GTK_LIBS) -lm
103
104inertia_SOURCES = ./drawing.c ./gtk.c ./inertia.c ./malloc.c ./midend.c \
105 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
106 ./random.c ./version.c ./version.h
107inertia_LDADD = $(GTK_LIBS) -lm
108
109keen_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./keen.c ./latin.c ./latin.h \
110 ./malloc.c ./maxflow.c ./maxflow.h ./midend.c ./misc.c \
111 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
112 ./tree234.c ./tree234.h ./version.c ./version.h
113keen_LDADD = $(GTK_LIBS) -lm
114
115keensolver_SOURCES = ./dsf.c ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
116 ./nullfe.c ./puzzles.h ./random.c ./tree234.c ./tree234.h
117keensolver_LDADD = libkeen2_a-keen.$(OBJEXT) liblatin6_a-latin.$(OBJEXT) -lm
118
119latincheck_SOURCES = ./malloc.c ./maxflow.c ./maxflow.h ./misc.c ./nullfe.c \
120 ./puzzles.h ./random.c ./tree234.c ./tree234.h
121latincheck_LDADD = liblatin8_a-latin.$(OBJEXT) -lm
122
123lightup_SOURCES = ./combi.c ./drawing.c ./gtk.c ./lightup.c ./malloc.c \
124 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
125 ./puzzles.h ./random.c ./version.c ./version.h
126lightup_LDADD = $(GTK_LIBS) -lm
127
128lightupsolver_SOURCES = ./combi.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
129 ./random.c
130lightupsolver_LDADD = liblightup2_a-lightup.$(OBJEXT) -lm
131
132loopy_SOURCES = ./drawing.c ./dsf.c ./grid.c ./grid.h ./gtk.c ./loopgen.c \
133 ./loopgen.h ./loopy.c ./malloc.c ./midend.c ./misc.c \
134 ./no-icon.c ./penrose.c ./penrose.h ./printing.c ./ps.c \
135 ./puzzles.h ./random.c ./tree234.c ./tree234.h ./version.c \
136 ./version.h
137loopy_LDADD = $(GTK_LIBS) -lm
138
139loopysolver_SOURCES = ./dsf.c ./grid.c ./grid.h ./loopgen.c ./loopgen.h \
140 ./malloc.c ./misc.c ./nullfe.c ./penrose.c ./penrose.h \
141 ./puzzles.h ./random.c ./tree234.c ./tree234.h
142loopysolver_LDADD = libloopy2_a-loopy.$(OBJEXT) -lm
143
144magnets_SOURCES = ./drawing.c ./gtk.c ./laydomino.c ./magnets.c ./malloc.c \
145 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
146 ./puzzles.h ./random.c ./version.c ./version.h
147magnets_LDADD = $(GTK_LIBS) -lm
148
149magnetssolver_SOURCES = ./laydomino.c ./malloc.c ./misc.c ./nullfe.c \
150 ./puzzles.h ./random.c
151magnetssolver_LDADD = libmagnets2_a-magnets.$(OBJEXT) -lm
152
153map_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./map.c ./midend.c \
154 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
155 ./random.c ./version.c ./version.h
156map_LDADD = $(GTK_LIBS) -lm
157
158mapsolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
159 ./random.c
160mapsolver_LDADD = libmap2_a-map.$(OBJEXT) -lm
161
162mineobfusc_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h ./random.c \
163 ./tree234.c ./tree234.h
164mineobfusc_LDADD = libmines2_a-mines.$(OBJEXT) -lm
165
166mines_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./mines.c ./misc.c \
167 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
168 ./tree234.c ./tree234.h ./version.c ./version.h
169mines_LDADD = $(GTK_LIBS) -lm
170
171net_SOURCES = ./drawing.c ./dsf.c ./findloop.c ./gtk.c ./malloc.c ./midend.c \
172 ./misc.c ./net.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
173 ./random.c ./tree234.c ./tree234.h ./version.c ./version.h
174net_LDADD = $(GTK_LIBS) -lm
175
176netslide_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
177 ./netslide.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
178 ./random.c ./tree234.c ./tree234.h ./version.c ./version.h
179netslide_LDADD = $(GTK_LIBS) -lm
180
181nullgame_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
182 ./no-icon.c ./nullgame.c ./printing.c ./ps.c ./puzzles.h \
183 ./random.c ./version.c ./version.h
184nullgame_LDADD = $(GTK_LIBS) -lm
185
186obfusc_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./obfusc.c ./puzzles.h \
187 ./random.c
188obfusc_LDADD = -lm
189
190palisade_SOURCES = ./divvy.c ./drawing.c ./dsf.c ./gtk.c ./malloc.c \
191 ./midend.c ./misc.c ./no-icon.c ./palisade.c ./printing.c \
192 ./ps.c ./puzzles.h ./random.c ./version.c ./version.h
193palisade_LDADD = $(GTK_LIBS) -lm
194
195pattern_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
196 ./no-icon.c ./pattern.c ./printing.c ./ps.c ./puzzles.h \
197 ./random.c ./version.c ./version.h
198pattern_LDADD = $(GTK_LIBS) -lm
199
200patternpicture_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
201 ./random.c
202patternpicture_LDADD = libpattern4_a-pattern.$(OBJEXT) -lm
203
204patternsolver_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
205 ./random.c
206patternsolver_LDADD = libpattern2_a-pattern.$(OBJEXT) -lm
207
208pearl_SOURCES = ./drawing.c ./dsf.c ./grid.c ./grid.h ./gtk.c ./loopgen.c \
209 ./loopgen.h ./malloc.c ./midend.c ./misc.c ./no-icon.c \
210 ./pearl.c ./penrose.c ./penrose.h ./printing.c ./ps.c \
211 ./puzzles.h ./random.c ./tdq.c ./tree234.c ./tree234.h \
212 ./version.c ./version.h
213pearl_LDADD = $(GTK_LIBS) -lm
214
215pearlbench_SOURCES = ./dsf.c ./grid.c ./grid.h ./loopgen.c ./loopgen.h \
216 ./malloc.c ./misc.c ./nullfe.c ./penrose.c ./penrose.h \
217 ./puzzles.h ./random.c ./tdq.c ./tree234.c ./tree234.h
218pearlbench_LDADD = libpearl2_a-pearl.$(OBJEXT) -lm
219
220pegs_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
221 ./no-icon.c ./pegs.c ./printing.c ./ps.c ./puzzles.h \
222 ./random.c ./tree234.c ./tree234.h ./version.c ./version.h
223pegs_LDADD = $(GTK_LIBS) -lm
224
225range_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
226 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
227 ./range.c ./version.c ./version.h
228range_LDADD = $(GTK_LIBS) -lm
229
230rect_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
231 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
232 ./rect.c ./version.c ./version.h
233rect_LDADD = $(GTK_LIBS) -lm
234
235samegame_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
236 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
237 ./samegame.c ./version.c ./version.h
238samegame_LDADD = $(GTK_LIBS) -lm
239
240signpost_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./midend.c \
241 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
242 ./random.c ./signpost.c ./version.c ./version.h
243signpost_LDADD = $(GTK_LIBS) -lm
244
245signpostsolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
246 ./random.c
247signpostsolver_LDADD = libsignpos2_a-signpost.$(OBJEXT) -lm
248
249singles_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./latin.c ./latin.h ./malloc.c \
250 ./maxflow.c ./maxflow.h ./midend.c ./misc.c ./no-icon.c \
251 ./printing.c ./ps.c ./puzzles.h ./random.c ./singles.c \
252 ./tree234.c ./tree234.h ./version.c ./version.h
253singles_LDADD = $(GTK_LIBS) -lm
254
255singlessolver_SOURCES = ./dsf.c ./latin.c ./latin.h ./malloc.c ./maxflow.c \
256 ./maxflow.h ./misc.c ./nullfe.c ./puzzles.h ./random.c \
257 ./tree234.c ./tree234.h
258singlessolver_LDADD = libsingles3_a-singles.$(OBJEXT) -lm
259
260sixteen_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
261 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
262 ./sixteen.c ./version.c ./version.h
263sixteen_LDADD = $(GTK_LIBS) -lm
264
265slant_SOURCES = ./drawing.c ./dsf.c ./findloop.c ./gtk.c ./malloc.c \
266 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
267 ./puzzles.h ./random.c ./slant.c ./version.c ./version.h
268slant_LDADD = $(GTK_LIBS) -lm
269
270slantsolver_SOURCES = ./dsf.c ./findloop.c ./malloc.c ./misc.c ./nullfe.c \
271 ./puzzles.h ./random.c
272slantsolver_LDADD = libslant2_a-slant.$(OBJEXT) -lm
273
274solo_SOURCES = ./divvy.c ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./midend.c \
275 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
276 ./random.c ./solo.c ./version.c ./version.h
277solo_LDADD = $(GTK_LIBS) -lm
278
279solosolver_SOURCES = ./divvy.c ./dsf.c ./malloc.c ./misc.c ./nullfe.c \
280 ./puzzles.h ./random.c
281solosolver_LDADD = libsolo2_a-solo.$(OBJEXT) -lm
282
283tents_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./maxflow.c \
284 ./maxflow.h ./midend.c ./misc.c ./no-icon.c ./printing.c \
285 ./ps.c ./puzzles.h ./random.c ./tents.c ./version.c \
286 ./version.h
287tents_LDADD = $(GTK_LIBS) -lm
288
289tentssolver_SOURCES = ./dsf.c ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
290 ./nullfe.c ./puzzles.h ./random.c
291tentssolver_LDADD = libtents3_a-tents.$(OBJEXT) -lm
292
293towers_SOURCES = ./drawing.c ./gtk.c ./latin.c ./latin.h ./malloc.c \
294 ./maxflow.c ./maxflow.h ./midend.c ./misc.c ./no-icon.c \
295 ./printing.c ./ps.c ./puzzles.h ./random.c ./towers.c \
296 ./tree234.c ./tree234.h ./version.c ./version.h
297towers_LDADD = $(GTK_LIBS) -lm
298
299towerssolver_SOURCES = ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
300 ./nullfe.c ./puzzles.h ./random.c ./tree234.c ./tree234.h
301towerssolver_LDADD = liblatin6_a-latin.$(OBJEXT) \
302 libtowers2_a-towers.$(OBJEXT) -lm
303
304tracks_SOURCES = ./drawing.c ./dsf.c ./findloop.c ./gtk.c ./malloc.c \
305 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
306 ./puzzles.h ./random.c ./tracks.c ./version.c ./version.h
307tracks_LDADD = $(GTK_LIBS) -lm
308
309twiddle_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
310 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
311 ./twiddle.c ./version.c ./version.h
312twiddle_LDADD = $(GTK_LIBS) -lm
313
314undead_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
315 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
316 ./undead.c ./version.c ./version.h
317undead_LDADD = $(GTK_LIBS) -lm
318
319unequal_SOURCES = ./drawing.c ./gtk.c ./latin.c ./latin.h ./malloc.c \
320 ./maxflow.c ./maxflow.h ./midend.c ./misc.c ./no-icon.c \
321 ./printing.c ./ps.c ./puzzles.h ./random.c ./tree234.c \
322 ./tree234.h ./unequal.c ./version.c ./version.h
323unequal_LDADD = $(GTK_LIBS) -lm
324
325unequalsolver_SOURCES = ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
326 ./nullfe.c ./puzzles.h ./random.c ./tree234.c ./tree234.h
327unequalsolver_LDADD = liblatin6_a-latin.$(OBJEXT) \
328 libunequal2_a-unequal.$(OBJEXT) -lm
329
330unruly_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
331 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
332 ./unruly.c ./version.c ./version.h
333unruly_LDADD = $(GTK_LIBS) -lm
334
335unrulysolver_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h ./random.c
336unrulysolver_LDADD = libunruly2_a-unruly.$(OBJEXT) -lm
337
338untangle_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
339 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
340 ./tree234.c ./tree234.h ./untangle.c ./version.c ./version.h
341untangle_LDADD = $(GTK_LIBS) -lm
342
343libfifteen2_a_SOURCES = ./fifteen.c ./puzzles.h
344libfifteen2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
345libfilling2_a_SOURCES = ./filling.c ./puzzles.h
346libfilling2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
347libgalaxie2_a_SOURCES = ./galaxies.c ./puzzles.h
348libgalaxie2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
349libgalaxie4_a_SOURCES = ./galaxies.c ./puzzles.h
350libgalaxie4_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) \
351 -DSTANDALONE_PICTURE_GENERATOR
352libkeen2_a_SOURCES = ./keen.c ./puzzles.h ./latin.h
353libkeen2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
354liblatin6_a_SOURCES = ./latin.c ./puzzles.h ./tree234.h ./maxflow.h \
355 ./latin.h
356liblatin6_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
357liblatin8_a_SOURCES = ./latin.c ./puzzles.h ./tree234.h ./maxflow.h \
358 ./latin.h
359liblatin8_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_LATIN_TEST
360liblightup2_a_SOURCES = ./lightup.c ./puzzles.h
361liblightup2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
362libloopy2_a_SOURCES = ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
363libloopy2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
364libmagnets2_a_SOURCES = ./magnets.c ./puzzles.h
365libmagnets2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
366libmap2_a_SOURCES = ./map.c ./puzzles.h
367libmap2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
368libmines2_a_SOURCES = ./mines.c ./tree234.h ./puzzles.h
369libmines2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_OBFUSCATOR
370libpattern2_a_SOURCES = ./pattern.c ./puzzles.h
371libpattern2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
372libpattern4_a_SOURCES = ./pattern.c ./puzzles.h
373libpattern4_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) \
374 -DSTANDALONE_PICTURE_GENERATOR
375libpearl2_a_SOURCES = ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
376libpearl2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
377libsignpos2_a_SOURCES = ./signpost.c ./puzzles.h
378libsignpos2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
379libsingles3_a_SOURCES = ./singles.c ./puzzles.h ./latin.h
380libsingles3_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
381libslant2_a_SOURCES = ./slant.c ./puzzles.h
382libslant2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
383libsolo2_a_SOURCES = ./solo.c ./puzzles.h
384libsolo2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
385libtents3_a_SOURCES = ./tents.c ./puzzles.h ./maxflow.h
386libtents3_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
387libtowers2_a_SOURCES = ./towers.c ./puzzles.h ./latin.h
388libtowers2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
389libunequal2_a_SOURCES = ./unequal.c ./puzzles.h ./latin.h
390libunequal2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
391libunruly2_a_SOURCES = ./unruly.c ./puzzles.h
392libunruly2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
393noinst_LIBRARIES = libfifteen2.a libfilling2.a libgalaxie2.a libgalaxie4.a \
394 libkeen2.a liblatin6.a liblatin8.a liblightup2.a libloopy2.a \
395 libmagnets2.a libmap2.a libmines2.a libpattern2.a \
396 libpattern4.a libpearl2.a libsignpos2.a libsingles3.a \
397 libslant2.a libsolo2.a libtents3.a libtowers2.a \
398 libunequal2.a libunruly2.a
399
400GAMES += blackbox
401GAMES += bridges
402GAMES += cube
403GAMES += dominosa
404GAMES += fifteen
405GAMES += filling
406GAMES += flip
407GAMES += flood
408GAMES += galaxies
409GAMES += guess
410GAMES += inertia
411GAMES += keen
412GAMES += lightup
413GAMES += loopy
414GAMES += magnets
415GAMES += map
416GAMES += mines
417GAMES += net
418GAMES += netslide
419GAMES += palisade
420GAMES += pattern
421GAMES += pearl
422GAMES += pegs
423GAMES += range
424GAMES += rect
425GAMES += samegame
426GAMES += signpost
427GAMES += singles
428GAMES += sixteen
429GAMES += slant
430GAMES += solo
431GAMES += tents
432GAMES += towers
433GAMES += tracks
434GAMES += twiddle
435GAMES += undead
436GAMES += unequal
437GAMES += unruly
438GAMES += untangle
439bin_PROGRAMS = $(GAMES)
440test: benchmark.html benchmark.txt
441
442benchmark.html: benchmark.txt benchmark.pl
443 ./benchmark.pl benchmark.txt > $@
444
445benchmark.txt: benchmark.sh $(GAMES)
446 ./benchmark.sh > $@
diff --git a/apps/plugins/puzzles/src/Makefile.cyg b/apps/plugins/puzzles/src/Makefile.cyg
new file mode 100644
index 0000000000..4dcfd8ae2d
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.cyg
@@ -0,0 +1,718 @@
1# Makefile for puzzles under cygwin.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6# You can define this path to point at your tools if you need to
7# TOOLPATH = c:\cygwin\bin\ # or similar, if you're running Windows
8# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/
9CC = $(TOOLPATH)gcc
10RC = $(TOOLPATH)windres
11# Uncomment the following two lines to compile under Winelib
12# CC = winegcc
13# RC = wrc
14# You may also need to tell windres where to find include files:
15# RCINC = --include-dir c:\cygwin\include\
16
17CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT \
18 -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -I./ -Iicons/
19LDFLAGS = -mno-cygwin -s
20RCFLAGS = $(RCINC) --define WIN32=1 --define _WIN32=1 --define WINVER=0x0400 \
21 --define MINGW32_FIX=1 --include ./ --include icons/
22
23all: blackbox.exe bridges.exe cube.exe dominosa.exe fifteen.exe \
24 fifteensolver.exe filling.exe fillingsolver.exe flip.exe \
25 flood.exe galaxies.exe galaxiespicture.exe \
26 galaxiessolver.exe guess.exe inertia.exe keen.exe \
27 keensolver.exe latincheck.exe lightup.exe lightupsolver.exe \
28 loopy.exe loopysolver.exe magnets.exe magnetssolver.exe \
29 map.exe mapsolver.exe mineobfusc.exe mines.exe netgame.exe \
30 netslide.exe nullgame.exe palisade.exe pattern.exe \
31 patternpicture.exe patternsolver.exe pearl.exe \
32 pearlbench.exe pegs.exe puzzles.exe range.exe rect.exe \
33 samegame.exe signpost.exe signpostsolver.exe singles.exe \
34 singlessolver.exe sixteen.exe slant.exe slantsolver.exe \
35 solo.exe solosolver.exe tents.exe tentssolver.exe towers.exe \
36 towerssolver.exe tracks.exe twiddle.exe undead.exe \
37 unequal.exe unequalsolver.exe unruly.exe unrulysolver.exe \
38 untangle.exe
39
40blackbox.exe: blackbox.o drawing.o malloc.o midend.o misc.o noicon.res.o \
41 printing.o random.o version.o windows.o
42 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,blackbox.map blackbox.o \
43 drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
44 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
45 -luser32 -lwinspool
46
47bridges.exe: bridges.o drawing.o dsf.o findloop.o malloc.o midend.o misc.o \
48 noicon.res.o printing.o random.o version.o windows.o
49 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,bridges.map bridges.o \
50 drawing.o dsf.o findloop.o malloc.o midend.o misc.o \
51 noicon.res.o printing.o random.o version.o windows.o \
52 -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool
53
54cube.exe: cube.o drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
55 random.o version.o windows.o
56 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,cube.map cube.o drawing.o \
57 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
58 version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 \
59 -lwinspool
60
61dominosa.exe: dominosa.o drawing.o laydomino.o malloc.o midend.o misc.o \
62 noicon.res.o printing.o random.o version.o windows.o
63 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,dominosa.map dominosa.o \
64 drawing.o laydomino.o malloc.o midend.o misc.o noicon.res.o \
65 printing.o random.o version.o windows.o -lcomctl32 \
66 -lcomdlg32 -lgdi32 -luser32 -lwinspool
67
68fifteen.exe: drawing.o fifteen.o malloc.o midend.o misc.o noicon.res.o \
69 printing.o random.o version.o windows.o
70 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,fifteen.map drawing.o \
71 fifteen.o malloc.o midend.o misc.o noicon.res.o printing.o \
72 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
73 -luser32 -lwinspool
74
75fifteensolver.exe: fifteen2.o malloc.o misc.o nullfe.o random.o
76 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,fifteensolver.map fifteen2.o \
77 malloc.o misc.o nullfe.o random.o
78
79filling.exe: drawing.o dsf.o filling.o malloc.o midend.o misc.o noicon.res.o \
80 printing.o random.o version.o windows.o
81 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,filling.map drawing.o \
82 dsf.o filling.o malloc.o midend.o misc.o noicon.res.o \
83 printing.o random.o version.o windows.o -lcomctl32 \
84 -lcomdlg32 -lgdi32 -luser32 -lwinspool
85
86fillingsolver.exe: dsf.o filling2.o malloc.o misc.o nullfe.o random.o
87 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,fillingsolver.map dsf.o filling2.o \
88 malloc.o misc.o nullfe.o random.o
89
90flip.exe: drawing.o flip.o malloc.o midend.o misc.o noicon.res.o printing.o \
91 random.o tree234.o version.o windows.o
92 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,flip.map drawing.o flip.o \
93 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
94 tree234.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
95 -luser32 -lwinspool
96
97flood.exe: drawing.o flood.o malloc.o midend.o misc.o noicon.res.o \
98 printing.o random.o version.o windows.o
99 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,flood.map drawing.o \
100 flood.o malloc.o midend.o misc.o noicon.res.o printing.o \
101 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
102 -luser32 -lwinspool
103
104galaxies.exe: drawing.o dsf.o galaxies.o malloc.o midend.o misc.o \
105 noicon.res.o printing.o random.o version.o windows.o
106 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,galaxies.map drawing.o \
107 dsf.o galaxies.o malloc.o midend.o misc.o noicon.res.o \
108 printing.o random.o version.o windows.o -lcomctl32 \
109 -lcomdlg32 -lgdi32 -luser32 -lwinspool
110
111galaxiespicture.exe: dsf.o galaxie4.o malloc.o misc.o nullfe.o random.o
112 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,galaxiespicture.map dsf.o galaxie4.o \
113 malloc.o misc.o nullfe.o random.o
114
115galaxiessolver.exe: dsf.o galaxie2.o malloc.o misc.o nullfe.o random.o
116 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,galaxiessolver.map dsf.o galaxie2.o \
117 malloc.o misc.o nullfe.o random.o
118
119guess.exe: drawing.o guess.o malloc.o midend.o misc.o noicon.res.o \
120 printing.o random.o version.o windows.o
121 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,guess.map drawing.o \
122 guess.o malloc.o midend.o misc.o noicon.res.o printing.o \
123 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
124 -luser32 -lwinspool
125
126inertia.exe: drawing.o inertia.o malloc.o midend.o misc.o noicon.res.o \
127 printing.o random.o version.o windows.o
128 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,inertia.map drawing.o \
129 inertia.o malloc.o midend.o misc.o noicon.res.o printing.o \
130 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
131 -luser32 -lwinspool
132
133keen.exe: drawing.o dsf.o keen.o latin.o malloc.o maxflow.o midend.o misc.o \
134 noicon.res.o printing.o random.o tree234.o version.o \
135 windows.o
136 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,keen.map drawing.o dsf.o \
137 keen.o latin.o malloc.o maxflow.o midend.o misc.o \
138 noicon.res.o printing.o random.o tree234.o version.o \
139 windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool
140
141keensolver.exe: dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o nullfe.o \
142 random.o tree234.o
143 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,keensolver.map dsf.o keen2.o \
144 latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
145 tree234.o
146
147latincheck.exe: latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \
148 tree234.o
149 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,latincheck.map latin8.o malloc.o \
150 maxflow.o misc.o nullfe.o random.o tree234.o
151
152lightup.exe: combi.o drawing.o lightup.o malloc.o midend.o misc.o \
153 noicon.res.o printing.o random.o version.o windows.o
154 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,lightup.map combi.o \
155 drawing.o lightup.o malloc.o midend.o misc.o noicon.res.o \
156 printing.o random.o version.o windows.o -lcomctl32 \
157 -lcomdlg32 -lgdi32 -luser32 -lwinspool
158
159lightupsolver.exe: combi.o lightup2.o malloc.o misc.o nullfe.o random.o
160 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,lightupsolver.map combi.o lightup2.o \
161 malloc.o misc.o nullfe.o random.o
162
163loopy.exe: drawing.o dsf.o grid.o loopgen.o loopy.o malloc.o midend.o misc.o \
164 noicon.res.o penrose.o printing.o random.o tree234.o \
165 version.o windows.o
166 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,loopy.map drawing.o dsf.o \
167 grid.o loopgen.o loopy.o malloc.o midend.o misc.o \
168 noicon.res.o penrose.o printing.o random.o tree234.o \
169 version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 \
170 -lwinspool
171
172loopysolver.exe: dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o nullfe.o \
173 penrose.o random.o tree234.o
174 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,loopysolver.map dsf.o grid.o \
175 loopgen.o loopy2.o malloc.o misc.o nullfe.o penrose.o \
176 random.o tree234.o
177
178magnets.exe: drawing.o laydomino.o magnets.o malloc.o midend.o misc.o \
179 noicon.res.o printing.o random.o version.o windows.o
180 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,magnets.map drawing.o \
181 laydomino.o magnets.o malloc.o midend.o misc.o noicon.res.o \
182 printing.o random.o version.o windows.o -lcomctl32 \
183 -lcomdlg32 -lgdi32 -luser32 -lwinspool
184
185magnetssolver.exe: laydomino.o magnets2.o malloc.o misc.o nullfe.o random.o
186 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,magnetssolver.map laydomino.o \
187 magnets2.o malloc.o misc.o nullfe.o random.o
188
189map.exe: drawing.o dsf.o malloc.o map.o midend.o misc.o noicon.res.o \
190 printing.o random.o version.o windows.o
191 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,map.map drawing.o dsf.o \
192 malloc.o map.o midend.o misc.o noicon.res.o printing.o \
193 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
194 -luser32 -lwinspool
195
196mapsolver.exe: dsf.o malloc.o map2.o misc.o nullfe.o random.o
197 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,mapsolver.map dsf.o malloc.o map2.o \
198 misc.o nullfe.o random.o
199
200mineobfusc.exe: malloc.o mines2.o misc.o nullfe.o random.o tree234.o
201 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,mineobfusc.map malloc.o mines2.o \
202 misc.o nullfe.o random.o tree234.o
203
204mines.exe: drawing.o malloc.o midend.o mines.o misc.o noicon.res.o \
205 printing.o random.o tree234.o version.o windows.o
206 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,mines.map drawing.o \
207 malloc.o midend.o mines.o misc.o noicon.res.o printing.o \
208 random.o tree234.o version.o windows.o -lcomctl32 -lcomdlg32 \
209 -lgdi32 -luser32 -lwinspool
210
211netgame.exe: drawing.o dsf.o findloop.o malloc.o midend.o misc.o net.o \
212 noicon.res.o printing.o random.o tree234.o version.o \
213 windows.o
214 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,netgame.map drawing.o \
215 dsf.o findloop.o malloc.o midend.o misc.o net.o noicon.res.o \
216 printing.o random.o tree234.o version.o windows.o -lcomctl32 \
217 -lcomdlg32 -lgdi32 -luser32 -lwinspool
218
219netslide.exe: drawing.o malloc.o midend.o misc.o netslide.o noicon.res.o \
220 printing.o random.o tree234.o version.o windows.o
221 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,netslide.map drawing.o \
222 malloc.o midend.o misc.o netslide.o noicon.res.o printing.o \
223 random.o tree234.o version.o windows.o -lcomctl32 -lcomdlg32 \
224 -lgdi32 -luser32 -lwinspool
225
226nullgame.exe: drawing.o malloc.o midend.o misc.o noicon.res.o nullgame.o \
227 printing.o random.o version.o windows.o
228 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,nullgame.map drawing.o \
229 malloc.o midend.o misc.o noicon.res.o nullgame.o printing.o \
230 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
231 -luser32 -lwinspool
232
233palisade.exe: divvy.o drawing.o dsf.o malloc.o midend.o misc.o noicon.res.o \
234 palisade.o printing.o random.o version.o windows.o
235 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,palisade.map divvy.o \
236 drawing.o dsf.o malloc.o midend.o misc.o noicon.res.o \
237 palisade.o printing.o random.o version.o windows.o \
238 -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool
239
240pattern.exe: drawing.o malloc.o midend.o misc.o noicon.res.o pattern.o \
241 printing.o random.o version.o windows.o
242 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pattern.map drawing.o \
243 malloc.o midend.o misc.o noicon.res.o pattern.o printing.o \
244 random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
245 -luser32 -lwinspool
246
247patternpicture.exe: malloc.o misc.o nullfe.o pattern4.o random.o
248 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,patternpicture.map malloc.o misc.o \
249 nullfe.o pattern4.o random.o
250
251patternsolver.exe: malloc.o misc.o nullfe.o pattern2.o random.o
252 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,patternsolver.map malloc.o misc.o \
253 nullfe.o pattern2.o random.o
254
255pearl.exe: drawing.o dsf.o grid.o loopgen.o malloc.o midend.o misc.o pearl.o \
256 penrose.o printing.o random.o tdq.o tree234.o version.o \
257 windows.o
258 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pearl.map drawing.o dsf.o \
259 grid.o loopgen.o malloc.o midend.o misc.o pearl.o penrose.o \
260 printing.o random.o tdq.o tree234.o version.o windows.o \
261 -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool
262
263pearlbench.exe: dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o pearl2.o \
264 penrose.o random.o tdq.o tree234.o
265 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pearlbench.map dsf.o grid.o \
266 loopgen.o malloc.o misc.o nullfe.o pearl2.o penrose.o \
267 random.o tdq.o tree234.o
268
269pegs.exe: drawing.o malloc.o midend.o misc.o noicon.res.o pegs.o printing.o \
270 random.o tree234.o version.o windows.o
271 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pegs.map drawing.o \
272 malloc.o midend.o misc.o noicon.res.o pegs.o printing.o \
273 random.o tree234.o version.o windows.o -lcomctl32 -lcomdlg32 \
274 -lgdi32 -luser32 -lwinspool
275
276puzzles.exe: blackbo3.o bridges3.o combi.o cube3.o divvy.o dominos3.o \
277 drawing.o dsf.o fifteen5.o filling5.o findloop.o flip3.o \
278 flood3.o galaxie7.o grid.o guess3.o inertia3.o keen5.o \
279 latin.o laydomino.o lightup5.o list.o loopgen.o loopy5.o \
280 magnets5.o malloc.o map5.o maxflow.o midend.o mines5.o \
281 misc.o net3.o netslid3.o noicon.res.o palisad3.o pattern7.o \
282 pearl5.o pegs3.o penrose.o printing.o random.o range3.o \
283 rect3.o samegam3.o signpos5.o singles5.o sixteen3.o slant5.o \
284 solo5.o tdq.o tents5.o towers5.o tracks3.o tree234.o \
285 twiddle3.o undead3.o unequal5.o unruly5.o untangl3.o \
286 version.o windows1.o
287 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puzzles.map blackbo3.o \
288 bridges3.o combi.o cube3.o divvy.o dominos3.o drawing.o \
289 dsf.o fifteen5.o filling5.o findloop.o flip3.o flood3.o \
290 galaxie7.o grid.o guess3.o inertia3.o keen5.o latin.o \
291 laydomino.o lightup5.o list.o loopgen.o loopy5.o magnets5.o \
292 malloc.o map5.o maxflow.o midend.o mines5.o misc.o net3.o \
293 netslid3.o noicon.res.o palisad3.o pattern7.o pearl5.o \
294 pegs3.o penrose.o printing.o random.o range3.o rect3.o \
295 samegam3.o signpos5.o singles5.o sixteen3.o slant5.o solo5.o \
296 tdq.o tents5.o towers5.o tracks3.o tree234.o twiddle3.o \
297 undead3.o unequal5.o unruly5.o untangl3.o version.o \
298 windows1.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool
299
300range.exe: drawing.o dsf.o malloc.o midend.o misc.o noicon.res.o printing.o \
301 random.o range.o version.o windows.o
302 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,range.map drawing.o dsf.o \
303 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
304 range.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
305 -luser32 -lwinspool
306
307rect.exe: drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
308 random.o rect.o version.o windows.o
309 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,rect.map drawing.o \
310 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
311 rect.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
312 -luser32 -lwinspool
313
314samegame.exe: drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
315 random.o samegame.o version.o windows.o
316 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,samegame.map drawing.o \
317 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
318 samegame.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
319 -luser32 -lwinspool
320
321signpost.exe: drawing.o dsf.o malloc.o midend.o misc.o noicon.res.o \
322 printing.o random.o signpost.o version.o windows.o
323 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,signpost.map drawing.o \
324 dsf.o malloc.o midend.o misc.o noicon.res.o printing.o \
325 random.o signpost.o version.o windows.o -lcomctl32 \
326 -lcomdlg32 -lgdi32 -luser32 -lwinspool
327
328signpostsolver.exe: dsf.o malloc.o misc.o nullfe.o random.o signpos2.o
329 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,signpostsolver.map dsf.o malloc.o \
330 misc.o nullfe.o random.o signpos2.o
331
332singles.exe: drawing.o dsf.o latin.o malloc.o maxflow.o midend.o misc.o \
333 noicon.res.o printing.o random.o singles.o tree234.o \
334 version.o windows.o
335 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,singles.map drawing.o \
336 dsf.o latin.o malloc.o maxflow.o midend.o misc.o \
337 noicon.res.o printing.o random.o singles.o tree234.o \
338 version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 \
339 -lwinspool
340
341singlessolver.exe: dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o random.o \
342 singles3.o tree234.o
343 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,singlessolver.map dsf.o latin.o \
344 malloc.o maxflow.o misc.o nullfe.o random.o singles3.o \
345 tree234.o
346
347sixteen.exe: drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
348 random.o sixteen.o version.o windows.o
349 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,sixteen.map drawing.o \
350 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
351 sixteen.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
352 -luser32 -lwinspool
353
354slant.exe: drawing.o dsf.o findloop.o malloc.o midend.o misc.o noicon.res.o \
355 printing.o random.o slant.o version.o windows.o
356 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,slant.map drawing.o dsf.o \
357 findloop.o malloc.o midend.o misc.o noicon.res.o printing.o \
358 random.o slant.o version.o windows.o -lcomctl32 -lcomdlg32 \
359 -lgdi32 -luser32 -lwinspool
360
361slantsolver.exe: dsf.o findloop.o malloc.o misc.o nullfe.o random.o slant2.o
362 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,slantsolver.map dsf.o findloop.o \
363 malloc.o misc.o nullfe.o random.o slant2.o
364
365solo.exe: divvy.o drawing.o dsf.o malloc.o midend.o misc.o noicon.res.o \
366 printing.o random.o solo.o version.o windows.o
367 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,solo.map divvy.o drawing.o \
368 dsf.o malloc.o midend.o misc.o noicon.res.o printing.o \
369 random.o solo.o version.o windows.o -lcomctl32 -lcomdlg32 \
370 -lgdi32 -luser32 -lwinspool
371
372solosolver.exe: divvy.o dsf.o malloc.o misc.o nullfe.o random.o solo2.o
373 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,solosolver.map divvy.o dsf.o \
374 malloc.o misc.o nullfe.o random.o solo2.o
375
376tents.exe: drawing.o dsf.o malloc.o maxflow.o midend.o misc.o noicon.res.o \
377 printing.o random.o tents.o version.o windows.o
378 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,tents.map drawing.o dsf.o \
379 malloc.o maxflow.o midend.o misc.o noicon.res.o printing.o \
380 random.o tents.o version.o windows.o -lcomctl32 -lcomdlg32 \
381 -lgdi32 -luser32 -lwinspool
382
383tentssolver.exe: dsf.o malloc.o maxflow.o misc.o nullfe.o random.o tents3.o
384 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,tentssolver.map dsf.o malloc.o \
385 maxflow.o misc.o nullfe.o random.o tents3.o
386
387towers.exe: drawing.o latin.o malloc.o maxflow.o midend.o misc.o \
388 noicon.res.o printing.o random.o towers.o tree234.o \
389 version.o windows.o
390 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,towers.map drawing.o \
391 latin.o malloc.o maxflow.o midend.o misc.o noicon.res.o \
392 printing.o random.o towers.o tree234.o version.o windows.o \
393 -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool
394
395towerssolver.exe: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
396 towers2.o tree234.o
397 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,towerssolver.map latin6.o malloc.o \
398 maxflow.o misc.o nullfe.o random.o towers2.o tree234.o
399
400tracks.exe: drawing.o dsf.o findloop.o malloc.o midend.o misc.o noicon.res.o \
401 printing.o random.o tracks.o version.o windows.o
402 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,tracks.map drawing.o dsf.o \
403 findloop.o malloc.o midend.o misc.o noicon.res.o printing.o \
404 random.o tracks.o version.o windows.o -lcomctl32 -lcomdlg32 \
405 -lgdi32 -luser32 -lwinspool
406
407twiddle.exe: drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
408 random.o twiddle.o version.o windows.o
409 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,twiddle.map drawing.o \
410 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
411 twiddle.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
412 -luser32 -lwinspool
413
414undead.exe: drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
415 random.o undead.o version.o windows.o
416 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,undead.map drawing.o \
417 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
418 undead.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
419 -luser32 -lwinspool
420
421unequal.exe: drawing.o latin.o malloc.o maxflow.o midend.o misc.o \
422 noicon.res.o printing.o random.o tree234.o unequal.o \
423 version.o windows.o
424 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,unequal.map drawing.o \
425 latin.o malloc.o maxflow.o midend.o misc.o noicon.res.o \
426 printing.o random.o tree234.o unequal.o version.o windows.o \
427 -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool
428
429unequalsolver.exe: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
430 tree234.o unequal2.o
431 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,unequalsolver.map latin6.o malloc.o \
432 maxflow.o misc.o nullfe.o random.o tree234.o unequal2.o
433
434unruly.exe: drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
435 random.o unruly.o version.o windows.o
436 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,unruly.map drawing.o \
437 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
438 unruly.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \
439 -luser32 -lwinspool
440
441unrulysolver.exe: malloc.o misc.o nullfe.o random.o unruly2.o
442 $(CC) $(LDFLAGS) -o $@ -Wl,-Map,unrulysolver.map malloc.o misc.o \
443 nullfe.o random.o unruly2.o
444
445untangle.exe: drawing.o malloc.o midend.o misc.o noicon.res.o printing.o \
446 random.o tree234.o untangle.o version.o windows.o
447 $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,untangle.map drawing.o \
448 malloc.o midend.o misc.o noicon.res.o printing.o random.o \
449 tree234.o untangle.o version.o windows.o -lcomctl32 \
450 -lcomdlg32 -lgdi32 -luser32 -lwinspool
451
452blackbox.o: ./blackbox.c ./puzzles.h
453 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
454blackbo3.o: ./blackbox.c ./puzzles.h
455 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
456bridges.o: ./bridges.c ./puzzles.h
457 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
458bridges3.o: ./bridges.c ./puzzles.h
459 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
460combi.o: ./combi.c ./puzzles.h
461 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
462cube.o: ./cube.c ./puzzles.h
463 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
464cube3.o: ./cube.c ./puzzles.h
465 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
466divvy.o: ./divvy.c ./puzzles.h
467 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
468dominosa.o: ./dominosa.c ./puzzles.h
469 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
470dominos3.o: ./dominosa.c ./puzzles.h
471 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
472drawing.o: ./drawing.c ./puzzles.h
473 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
474dsf.o: ./dsf.c ./puzzles.h
475 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
476fifteen.o: ./fifteen.c ./puzzles.h
477 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
478fifteen5.o: ./fifteen.c ./puzzles.h
479 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
480fifteen2.o: ./fifteen.c ./puzzles.h
481 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
482filling.o: ./filling.c ./puzzles.h
483 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
484filling5.o: ./filling.c ./puzzles.h
485 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
486filling2.o: ./filling.c ./puzzles.h
487 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
488findloop.o: ./findloop.c ./puzzles.h
489 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
490flip.o: ./flip.c ./puzzles.h ./tree234.h
491 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
492flip3.o: ./flip.c ./puzzles.h ./tree234.h
493 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
494flood.o: ./flood.c ./puzzles.h
495 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
496flood3.o: ./flood.c ./puzzles.h
497 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
498galaxies.o: ./galaxies.c ./puzzles.h
499 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
500galaxie7.o: ./galaxies.c ./puzzles.h
501 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
502galaxie4.o: ./galaxies.c ./puzzles.h
503 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
504galaxie2.o: ./galaxies.c ./puzzles.h
505 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
506grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h
507 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
508gtk.o: ./gtk.c ./puzzles.h
509 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
510guess.o: ./guess.c ./puzzles.h
511 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
512guess3.o: ./guess.c ./puzzles.h
513 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
514inertia.o: ./inertia.c ./puzzles.h
515 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
516inertia3.o: ./inertia.c ./puzzles.h
517 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
518keen.o: ./keen.c ./puzzles.h ./latin.h
519 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
520keen5.o: ./keen.c ./puzzles.h ./latin.h
521 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
522keen2.o: ./keen.c ./puzzles.h ./latin.h
523 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
524latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
525 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
526latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
527 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@
528latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
529 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
530laydomino.o: ./laydomino.c ./puzzles.h
531 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
532lightup.o: ./lightup.c ./puzzles.h
533 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
534lightup5.o: ./lightup.c ./puzzles.h
535 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
536lightup2.o: ./lightup.c ./puzzles.h
537 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
538list.o: ./list.c ./puzzles.h
539 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
540loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
541 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
542loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
543 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
544loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
545 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
546loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
547 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
548magnets.o: ./magnets.c ./puzzles.h
549 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
550magnets5.o: ./magnets.c ./puzzles.h
551 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
552magnets2.o: ./magnets.c ./puzzles.h
553 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
554malloc.o: ./malloc.c ./puzzles.h
555 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
556map.o: ./map.c ./puzzles.h
557 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
558map5.o: ./map.c ./puzzles.h
559 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
560map2.o: ./map.c ./puzzles.h
561 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
562maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h
563 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
564midend.o: ./midend.c ./puzzles.h
565 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
566mines.o: ./mines.c ./tree234.h ./puzzles.h
567 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
568mines5.o: ./mines.c ./tree234.h ./puzzles.h
569 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
570mines2.o: ./mines.c ./tree234.h ./puzzles.h
571 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@
572misc.o: ./misc.c ./puzzles.h
573 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
574net.o: ./net.c ./puzzles.h ./tree234.h
575 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
576net3.o: ./net.c ./puzzles.h ./tree234.h
577 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
578netslide.o: ./netslide.c ./puzzles.h ./tree234.h
579 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
580netslid3.o: ./netslide.c ./puzzles.h ./tree234.h
581 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
582no-icon.o: ./no-icon.c
583 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
584noicon.res.o: ./noicon.rc ./puzzles.rc2 ./resource.h
585 $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@
586nullfe.o: ./nullfe.c ./puzzles.h
587 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
588nullgame.o: ./nullgame.c ./puzzles.h
589 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
590obfusc.o: ./obfusc.c ./puzzles.h
591 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
592osx.o: ./osx.m ./puzzles.h
593 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
594palisade.o: ./palisade.c ./puzzles.h
595 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
596palisad3.o: ./palisade.c ./puzzles.h
597 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
598pattern.o: ./pattern.c ./puzzles.h
599 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
600pattern7.o: ./pattern.c ./puzzles.h
601 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
602pattern4.o: ./pattern.c ./puzzles.h
603 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
604pattern2.o: ./pattern.c ./puzzles.h
605 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
606pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
607 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
608pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
609 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
610pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
611 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
612pegs.o: ./pegs.c ./puzzles.h ./tree234.h
613 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
614pegs3.o: ./pegs.c ./puzzles.h ./tree234.h
615 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
616penrose.o: ./penrose.c ./puzzles.h ./penrose.h
617 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
618printing.o: ./printing.c ./puzzles.h
619 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
620ps.o: ./ps.c ./puzzles.h
621 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
622random.o: ./random.c ./puzzles.h
623 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
624range.o: ./range.c ./puzzles.h
625 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
626range3.o: ./range.c ./puzzles.h
627 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
628rect.o: ./rect.c ./puzzles.h
629 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
630rect3.o: ./rect.c ./puzzles.h
631 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
632samegame.o: ./samegame.c ./puzzles.h
633 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
634samegam3.o: ./samegame.c ./puzzles.h
635 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
636signpost.o: ./signpost.c ./puzzles.h
637 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
638signpos5.o: ./signpost.c ./puzzles.h
639 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
640signpos2.o: ./signpost.c ./puzzles.h
641 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
642singles.o: ./singles.c ./puzzles.h ./latin.h
643 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
644singles5.o: ./singles.c ./puzzles.h ./latin.h
645 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
646singles3.o: ./singles.c ./puzzles.h ./latin.h
647 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
648sixteen.o: ./sixteen.c ./puzzles.h
649 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
650sixteen3.o: ./sixteen.c ./puzzles.h
651 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
652slant.o: ./slant.c ./puzzles.h
653 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
654slant5.o: ./slant.c ./puzzles.h
655 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
656slant2.o: ./slant.c ./puzzles.h
657 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
658solo.o: ./solo.c ./puzzles.h
659 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
660solo5.o: ./solo.c ./puzzles.h
661 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
662solo2.o: ./solo.c ./puzzles.h
663 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
664tdq.o: ./tdq.c ./puzzles.h
665 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
666tents.o: ./tents.c ./puzzles.h ./maxflow.h
667 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
668tents5.o: ./tents.c ./puzzles.h ./maxflow.h
669 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
670tents3.o: ./tents.c ./puzzles.h ./maxflow.h
671 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
672towers.o: ./towers.c ./puzzles.h ./latin.h
673 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
674towers5.o: ./towers.c ./puzzles.h ./latin.h
675 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
676towers2.o: ./towers.c ./puzzles.h ./latin.h
677 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
678tracks.o: ./tracks.c ./puzzles.h
679 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
680tracks3.o: ./tracks.c ./puzzles.h
681 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
682tree234.o: ./tree234.c ./tree234.h ./puzzles.h
683 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
684twiddle.o: ./twiddle.c ./puzzles.h
685 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
686twiddle3.o: ./twiddle.c ./puzzles.h
687 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
688undead.o: ./undead.c ./puzzles.h
689 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
690undead3.o: ./undead.c ./puzzles.h
691 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
692unequal.o: ./unequal.c ./puzzles.h ./latin.h
693 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
694unequal5.o: ./unequal.c ./puzzles.h ./latin.h
695 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
696unequal2.o: ./unequal.c ./puzzles.h ./latin.h
697 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
698unruly.o: ./unruly.c ./puzzles.h
699 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
700unruly5.o: ./unruly.c ./puzzles.h
701 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
702unruly2.o: ./unruly.c ./puzzles.h
703 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
704untangle.o: ./untangle.c ./puzzles.h ./tree234.h
705 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
706untangl3.o: ./untangle.c ./puzzles.h ./tree234.h
707 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
708version.o: ./version.c ./version.h
709 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
710windows.o: ./windows.c ./puzzles.h ./resource.h
711 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
712windows1.o: ./windows.c ./puzzles.h ./resource.h
713 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
714
715
716clean:
717 rm -f *.o *.exe *.res.o *.map
718
diff --git a/apps/plugins/puzzles/src/Makefile.doc b/apps/plugins/puzzles/src/Makefile.doc
new file mode 100644
index 0000000000..c7d6946cf1
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.doc
@@ -0,0 +1,17 @@
1all: puzzles.hlp puzzles.txt HACKING
2
3preprocessed.but: puzzles.but
4 sed 's/PREFIX-/$(BINPREFIX)/g' puzzles.but > preprocessed.but
5
6puzzles.hlp puzzles.txt: preprocessed.but
7 halibut --winhelp=puzzles.hlp --text=puzzles.txt preprocessed.but
8
9HACKING: devel.but
10 halibut --text=HACKING devel.but
11
12chm: puzzles.hhp
13puzzles.hhp: puzzles.but chm.but
14 halibut --html puzzles.but chm.but
15
16clean:
17 rm -f puzzles.hlp puzzles.txt preprocessed.but HACKING *.html *.hh[pck]
diff --git a/apps/plugins/puzzles/src/Makefile.emcc b/apps/plugins/puzzles/src/Makefile.emcc
new file mode 100644
index 0000000000..9677b2a1cf
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.emcc
@@ -0,0 +1,497 @@
1# Makefile for puzzles using Emscripten. Requires GNU make.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6# This can be set on the command line to point at the emcc command,
7# if it is not on your PATH.
8EMCC = emcc
9
10CFLAGS = -DSLOW_SYSTEM -I./ -Iicons/
11
12all: $(OUTPREFIX)blackbox.js $(OUTPREFIX)bridges.js $(OUTPREFIX)cube.js \
13 $(OUTPREFIX)dominosa.js $(OUTPREFIX)fifteen.js \
14 $(OUTPREFIX)filling.js $(OUTPREFIX)flip.js \
15 $(OUTPREFIX)flood.js $(OUTPREFIX)galaxies.js \
16 $(OUTPREFIX)guess.js $(OUTPREFIX)inertia.js \
17 $(OUTPREFIX)keen.js $(OUTPREFIX)lightup.js \
18 $(OUTPREFIX)loopy.js $(OUTPREFIX)magnets.js \
19 $(OUTPREFIX)map.js $(OUTPREFIX)mines.js $(OUTPREFIX)net.js \
20 $(OUTPREFIX)netslide.js $(OUTPREFIX)nullgame.js \
21 $(OUTPREFIX)palisade.js $(OUTPREFIX)pattern.js \
22 $(OUTPREFIX)pearl.js $(OUTPREFIX)pegs.js \
23 $(OUTPREFIX)range.js $(OUTPREFIX)rect.js \
24 $(OUTPREFIX)samegame.js $(OUTPREFIX)signpost.js \
25 $(OUTPREFIX)singles.js $(OUTPREFIX)sixteen.js \
26 $(OUTPREFIX)slant.js $(OUTPREFIX)solo.js \
27 $(OUTPREFIX)tents.js $(OUTPREFIX)towers.js \
28 $(OUTPREFIX)tracks.js $(OUTPREFIX)twiddle.js \
29 $(OUTPREFIX)undead.js $(OUTPREFIX)unequal.js \
30 $(OUTPREFIX)unruly.js $(OUTPREFIX)untangle.js
31
32$(OUTPREFIX)blackbox.js: blackbox.o drawing.o emcc.o malloc.o midend.o \
33 misc.o no-icon.o printing.o ps.o random.o version.o \
34 emccpre.js emcclib.js emccx.json
35 $(EMCC) -o $(OUTPREFIX)blackbox.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" blackbox.o drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
36
37$(OUTPREFIX)bridges.js: bridges.o drawing.o dsf.o findloop.o emcc.o malloc.o \
38 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
39 emccpre.js emcclib.js emccx.json
40 $(EMCC) -o $(OUTPREFIX)bridges.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" bridges.o drawing.o dsf.o findloop.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
41
42$(OUTPREFIX)cube.js: cube.o drawing.o emcc.o malloc.o midend.o misc.o \
43 no-icon.o printing.o ps.o random.o version.o emccpre.js \
44 emcclib.js emccx.json
45 $(EMCC) -o $(OUTPREFIX)cube.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" cube.o drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
46
47$(OUTPREFIX)dominosa.js: dominosa.o drawing.o emcc.o laydomino.o malloc.o \
48 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
49 emccpre.js emcclib.js emccx.json
50 $(EMCC) -o $(OUTPREFIX)dominosa.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" dominosa.o drawing.o emcc.o laydomino.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
51
52$(OUTPREFIX)fifteen.js: drawing.o fifteen.o emcc.o malloc.o midend.o misc.o \
53 no-icon.o printing.o ps.o random.o version.o emccpre.js \
54 emcclib.js emccx.json
55 $(EMCC) -o $(OUTPREFIX)fifteen.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o fifteen.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
56
57$(OUTPREFIX)filling.js: drawing.o dsf.o filling.o emcc.o malloc.o midend.o \
58 misc.o no-icon.o printing.o ps.o random.o version.o \
59 emccpre.js emcclib.js emccx.json
60 $(EMCC) -o $(OUTPREFIX)filling.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o filling.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
61
62$(OUTPREFIX)flip.js: drawing.o flip.o emcc.o malloc.o midend.o misc.o \
63 no-icon.o printing.o ps.o random.o tree234.o version.o \
64 emccpre.js emcclib.js emccx.json
65 $(EMCC) -o $(OUTPREFIX)flip.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o flip.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o version.o
66
67$(OUTPREFIX)flood.js: drawing.o flood.o emcc.o malloc.o midend.o misc.o \
68 no-icon.o printing.o ps.o random.o version.o emccpre.js \
69 emcclib.js emccx.json
70 $(EMCC) -o $(OUTPREFIX)flood.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o flood.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
71
72$(OUTPREFIX)galaxies.js: drawing.o dsf.o galaxies.o emcc.o malloc.o midend.o \
73 misc.o no-icon.o printing.o ps.o random.o version.o \
74 emccpre.js emcclib.js emccx.json
75 $(EMCC) -o $(OUTPREFIX)galaxies.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o galaxies.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
76
77$(OUTPREFIX)guess.js: drawing.o emcc.o guess.o malloc.o midend.o misc.o \
78 no-icon.o printing.o ps.o random.o version.o emccpre.js \
79 emcclib.js emccx.json
80 $(EMCC) -o $(OUTPREFIX)guess.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o guess.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
81
82$(OUTPREFIX)inertia.js: drawing.o emcc.o inertia.o malloc.o midend.o misc.o \
83 no-icon.o printing.o ps.o random.o version.o emccpre.js \
84 emcclib.js emccx.json
85 $(EMCC) -o $(OUTPREFIX)inertia.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o inertia.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
86
87$(OUTPREFIX)keen.js: drawing.o dsf.o emcc.o keen.o latin.o malloc.o \
88 maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o \
89 tree234.o version.o emccpre.js emcclib.js emccx.json
90 $(EMCC) -o $(OUTPREFIX)keen.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o keen.o latin.o malloc.o maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o version.o
91
92$(OUTPREFIX)lightup.js: combi.o drawing.o emcc.o lightup.o malloc.o midend.o \
93 misc.o no-icon.o printing.o ps.o random.o version.o \
94 emccpre.js emcclib.js emccx.json
95 $(EMCC) -o $(OUTPREFIX)lightup.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" combi.o drawing.o emcc.o lightup.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
96
97$(OUTPREFIX)loopy.js: drawing.o dsf.o grid.o emcc.o loopgen.o loopy.o \
98 malloc.o midend.o misc.o no-icon.o penrose.o printing.o ps.o \
99 random.o tree234.o version.o emccpre.js emcclib.js \
100 emccx.json
101 $(EMCC) -o $(OUTPREFIX)loopy.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o grid.o emcc.o loopgen.o loopy.o malloc.o midend.o misc.o no-icon.o penrose.o printing.o ps.o random.o tree234.o version.o
102
103$(OUTPREFIX)magnets.js: drawing.o emcc.o laydomino.o magnets.o malloc.o \
104 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
105 emccpre.js emcclib.js emccx.json
106 $(EMCC) -o $(OUTPREFIX)magnets.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o laydomino.o magnets.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
107
108$(OUTPREFIX)map.js: drawing.o dsf.o emcc.o malloc.o map.o midend.o misc.o \
109 no-icon.o printing.o ps.o random.o version.o emccpre.js \
110 emcclib.js emccx.json
111 $(EMCC) -o $(OUTPREFIX)map.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o map.o midend.o misc.o no-icon.o printing.o ps.o random.o version.o
112
113$(OUTPREFIX)mines.js: drawing.o emcc.o malloc.o midend.o mines.o misc.o \
114 no-icon.o printing.o ps.o random.o tree234.o version.o \
115 emccpre.js emcclib.js emccx.json
116 $(EMCC) -o $(OUTPREFIX)mines.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o mines.o misc.o no-icon.o printing.o ps.o random.o tree234.o version.o
117
118$(OUTPREFIX)net.js: drawing.o dsf.o findloop.o emcc.o malloc.o midend.o \
119 misc.o net.o no-icon.o printing.o ps.o random.o tree234.o \
120 version.o emccpre.js emcclib.js emccx.json
121 $(EMCC) -o $(OUTPREFIX)net.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o findloop.o emcc.o malloc.o midend.o misc.o net.o no-icon.o printing.o ps.o random.o tree234.o version.o
122
123$(OUTPREFIX)netslide.js: drawing.o emcc.o malloc.o midend.o misc.o \
124 netslide.o no-icon.o printing.o ps.o random.o tree234.o \
125 version.o emccpre.js emcclib.js emccx.json
126 $(EMCC) -o $(OUTPREFIX)netslide.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o netslide.o no-icon.o printing.o ps.o random.o tree234.o version.o
127
128$(OUTPREFIX)nullgame.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
129 nullgame.o printing.o ps.o random.o version.o emccpre.js \
130 emcclib.js emccx.json
131 $(EMCC) -o $(OUTPREFIX)nullgame.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o nullgame.o printing.o ps.o random.o version.o
132
133$(OUTPREFIX)palisade.js: divvy.o drawing.o dsf.o emcc.o malloc.o midend.o \
134 misc.o no-icon.o palisade.o printing.o ps.o random.o \
135 version.o emccpre.js emcclib.js emccx.json
136 $(EMCC) -o $(OUTPREFIX)palisade.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" divvy.o drawing.o dsf.o emcc.o malloc.o midend.o misc.o no-icon.o palisade.o printing.o ps.o random.o version.o
137
138$(OUTPREFIX)pattern.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
139 pattern.o printing.o ps.o random.o version.o emccpre.js \
140 emcclib.js emccx.json
141 $(EMCC) -o $(OUTPREFIX)pattern.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o pattern.o printing.o ps.o random.o version.o
142
143$(OUTPREFIX)pearl.js: drawing.o dsf.o grid.o emcc.o loopgen.o malloc.o \
144 midend.o misc.o no-icon.o pearl.o penrose.o printing.o ps.o \
145 random.o tdq.o tree234.o version.o emccpre.js emcclib.js \
146 emccx.json
147 $(EMCC) -o $(OUTPREFIX)pearl.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o grid.o emcc.o loopgen.o malloc.o midend.o misc.o no-icon.o pearl.o penrose.o printing.o ps.o random.o tdq.o tree234.o version.o
148
149$(OUTPREFIX)pegs.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
150 pegs.o printing.o ps.o random.o tree234.o version.o \
151 emccpre.js emcclib.js emccx.json
152 $(EMCC) -o $(OUTPREFIX)pegs.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o pegs.o printing.o ps.o random.o tree234.o version.o
153
154$(OUTPREFIX)range.js: drawing.o dsf.o emcc.o malloc.o midend.o misc.o \
155 no-icon.o printing.o ps.o random.o range.o version.o \
156 emccpre.js emcclib.js emccx.json
157 $(EMCC) -o $(OUTPREFIX)range.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o range.o version.o
158
159$(OUTPREFIX)rect.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
160 printing.o ps.o random.o rect.o version.o emccpre.js \
161 emcclib.js emccx.json
162 $(EMCC) -o $(OUTPREFIX)rect.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o rect.o version.o
163
164$(OUTPREFIX)samegame.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
165 printing.o ps.o random.o samegame.o version.o emccpre.js \
166 emcclib.js emccx.json
167 $(EMCC) -o $(OUTPREFIX)samegame.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o samegame.o version.o
168
169$(OUTPREFIX)signpost.js: drawing.o dsf.o emcc.o malloc.o midend.o misc.o \
170 no-icon.o printing.o ps.o random.o signpost.o version.o \
171 emccpre.js emcclib.js emccx.json
172 $(EMCC) -o $(OUTPREFIX)signpost.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o signpost.o version.o
173
174$(OUTPREFIX)singles.js: drawing.o dsf.o emcc.o latin.o malloc.o maxflow.o \
175 midend.o misc.o no-icon.o printing.o ps.o random.o singles.o \
176 tree234.o version.o emccpre.js emcclib.js emccx.json
177 $(EMCC) -o $(OUTPREFIX)singles.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o latin.o malloc.o maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o singles.o tree234.o version.o
178
179$(OUTPREFIX)sixteen.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
180 printing.o ps.o random.o sixteen.o version.o emccpre.js \
181 emcclib.js emccx.json
182 $(EMCC) -o $(OUTPREFIX)sixteen.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o sixteen.o version.o
183
184$(OUTPREFIX)slant.js: drawing.o dsf.o findloop.o emcc.o malloc.o midend.o \
185 misc.o no-icon.o printing.o ps.o random.o slant.o version.o \
186 emccpre.js emcclib.js emccx.json
187 $(EMCC) -o $(OUTPREFIX)slant.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o findloop.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o slant.o version.o
188
189$(OUTPREFIX)solo.js: divvy.o drawing.o dsf.o emcc.o malloc.o midend.o misc.o \
190 no-icon.o printing.o ps.o random.o solo.o version.o \
191 emccpre.js emcclib.js emccx.json
192 $(EMCC) -o $(OUTPREFIX)solo.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" divvy.o drawing.o dsf.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o solo.o version.o
193
194$(OUTPREFIX)tents.js: drawing.o dsf.o emcc.o malloc.o maxflow.o midend.o \
195 misc.o no-icon.o printing.o ps.o random.o tents.o version.o \
196 emccpre.js emcclib.js emccx.json
197 $(EMCC) -o $(OUTPREFIX)tents.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o tents.o version.o
198
199$(OUTPREFIX)towers.js: drawing.o emcc.o latin.o malloc.o maxflow.o midend.o \
200 misc.o no-icon.o printing.o ps.o random.o towers.o tree234.o \
201 version.o emccpre.js emcclib.js emccx.json
202 $(EMCC) -o $(OUTPREFIX)towers.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o latin.o malloc.o maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o towers.o tree234.o version.o
203
204$(OUTPREFIX)tracks.js: drawing.o dsf.o findloop.o emcc.o malloc.o midend.o \
205 misc.o no-icon.o printing.o ps.o random.o tracks.o version.o \
206 emccpre.js emcclib.js emccx.json
207 $(EMCC) -o $(OUTPREFIX)tracks.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o findloop.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o tracks.o version.o
208
209$(OUTPREFIX)twiddle.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
210 printing.o ps.o random.o twiddle.o version.o emccpre.js \
211 emcclib.js emccx.json
212 $(EMCC) -o $(OUTPREFIX)twiddle.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o twiddle.o version.o
213
214$(OUTPREFIX)undead.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
215 printing.o ps.o random.o undead.o version.o emccpre.js \
216 emcclib.js emccx.json
217 $(EMCC) -o $(OUTPREFIX)undead.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o undead.o version.o
218
219$(OUTPREFIX)unequal.js: drawing.o emcc.o latin.o malloc.o maxflow.o midend.o \
220 misc.o no-icon.o printing.o ps.o random.o tree234.o \
221 unequal.o version.o emccpre.js emcclib.js emccx.json
222 $(EMCC) -o $(OUTPREFIX)unequal.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o latin.o malloc.o maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o unequal.o version.o
223
224$(OUTPREFIX)unruly.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
225 printing.o ps.o random.o unruly.o version.o emccpre.js \
226 emcclib.js emccx.json
227 $(EMCC) -o $(OUTPREFIX)unruly.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o unruly.o version.o
228
229$(OUTPREFIX)untangle.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \
230 printing.o ps.o random.o tree234.o untangle.o version.o \
231 emccpre.js emcclib.js emccx.json
232 $(EMCC) -o $(OUTPREFIX)untangle.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o untangle.o version.o
233
234blackbox.o: ./blackbox.c ./puzzles.h
235 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
236blackbo3.o: ./blackbox.c ./puzzles.h
237 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
238bridges.o: ./bridges.c ./puzzles.h
239 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
240bridges3.o: ./bridges.c ./puzzles.h
241 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
242combi.o: ./combi.c ./puzzles.h
243 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
244cube.o: ./cube.c ./puzzles.h
245 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
246cube3.o: ./cube.c ./puzzles.h
247 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
248divvy.o: ./divvy.c ./puzzles.h
249 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
250dominosa.o: ./dominosa.c ./puzzles.h
251 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
252dominos3.o: ./dominosa.c ./puzzles.h
253 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
254drawing.o: ./drawing.c ./puzzles.h
255 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
256dsf.o: ./dsf.c ./puzzles.h
257 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
258fifteen.o: ./fifteen.c ./puzzles.h
259 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
260fifteen5.o: ./fifteen.c ./puzzles.h
261 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
262fifteen2.o: ./fifteen.c ./puzzles.h
263 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
264filling.o: ./filling.c ./puzzles.h
265 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
266filling5.o: ./filling.c ./puzzles.h
267 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
268filling2.o: ./filling.c ./puzzles.h
269 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
270findloop.o: ./findloop.c ./puzzles.h
271 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
272flip.o: ./flip.c ./puzzles.h ./tree234.h
273 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
274flip3.o: ./flip.c ./puzzles.h ./tree234.h
275 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
276flood.o: ./flood.c ./puzzles.h
277 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
278flood3.o: ./flood.c ./puzzles.h
279 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
280galaxies.o: ./galaxies.c ./puzzles.h
281 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
282galaxie7.o: ./galaxies.c ./puzzles.h
283 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
284galaxie4.o: ./galaxies.c ./puzzles.h
285 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
286galaxie2.o: ./galaxies.c ./puzzles.h
287 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
288grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h
289 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
290emcc.o: ./emcc.c ./puzzles.h
291 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
292guess.o: ./guess.c ./puzzles.h
293 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
294guess3.o: ./guess.c ./puzzles.h
295 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
296inertia.o: ./inertia.c ./puzzles.h
297 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
298inertia3.o: ./inertia.c ./puzzles.h
299 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
300keen.o: ./keen.c ./puzzles.h ./latin.h
301 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
302keen5.o: ./keen.c ./puzzles.h ./latin.h
303 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
304keen2.o: ./keen.c ./puzzles.h ./latin.h
305 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
306latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
307 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
308latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
309 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@
310latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
311 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
312laydomino.o: ./laydomino.c ./puzzles.h
313 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
314lightup.o: ./lightup.c ./puzzles.h
315 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
316lightup5.o: ./lightup.c ./puzzles.h
317 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
318lightup2.o: ./lightup.c ./puzzles.h
319 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
320list.o: ./list.c ./puzzles.h
321 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
322loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
323 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
324loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
325 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
326loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
327 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
328loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
329 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
330magnets.o: ./magnets.c ./puzzles.h
331 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
332magnets5.o: ./magnets.c ./puzzles.h
333 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
334magnets2.o: ./magnets.c ./puzzles.h
335 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
336malloc.o: ./malloc.c ./puzzles.h
337 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
338map.o: ./map.c ./puzzles.h
339 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
340map5.o: ./map.c ./puzzles.h
341 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
342map2.o: ./map.c ./puzzles.h
343 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
344maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h
345 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
346midend.o: ./midend.c ./puzzles.h
347 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
348mines.o: ./mines.c ./tree234.h ./puzzles.h
349 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
350mines5.o: ./mines.c ./tree234.h ./puzzles.h
351 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
352mines2.o: ./mines.c ./tree234.h ./puzzles.h
353 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@
354misc.o: ./misc.c ./puzzles.h
355 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
356net.o: ./net.c ./puzzles.h ./tree234.h
357 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
358net3.o: ./net.c ./puzzles.h ./tree234.h
359 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
360netslide.o: ./netslide.c ./puzzles.h ./tree234.h
361 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
362netslid3.o: ./netslide.c ./puzzles.h ./tree234.h
363 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
364no-icon.o: ./no-icon.c
365 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
366nullfe.o: ./nullfe.c ./puzzles.h
367 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
368nullgame.o: ./nullgame.c ./puzzles.h
369 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
370obfusc.o: ./obfusc.c ./puzzles.h
371 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
372osx.o: ./osx.m ./puzzles.h
373 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
374palisade.o: ./palisade.c ./puzzles.h
375 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
376palisad3.o: ./palisade.c ./puzzles.h
377 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
378pattern.o: ./pattern.c ./puzzles.h
379 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
380pattern7.o: ./pattern.c ./puzzles.h
381 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
382pattern4.o: ./pattern.c ./puzzles.h
383 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
384pattern2.o: ./pattern.c ./puzzles.h
385 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
386pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
387 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
388pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
389 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
390pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
391 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
392pegs.o: ./pegs.c ./puzzles.h ./tree234.h
393 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
394pegs3.o: ./pegs.c ./puzzles.h ./tree234.h
395 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
396penrose.o: ./penrose.c ./puzzles.h ./penrose.h
397 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
398printing.o: ./printing.c ./puzzles.h
399 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
400ps.o: ./ps.c ./puzzles.h
401 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
402random.o: ./random.c ./puzzles.h
403 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
404range.o: ./range.c ./puzzles.h
405 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
406range3.o: ./range.c ./puzzles.h
407 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
408rect.o: ./rect.c ./puzzles.h
409 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
410rect3.o: ./rect.c ./puzzles.h
411 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
412samegame.o: ./samegame.c ./puzzles.h
413 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
414samegam3.o: ./samegame.c ./puzzles.h
415 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
416signpost.o: ./signpost.c ./puzzles.h
417 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
418signpos5.o: ./signpost.c ./puzzles.h
419 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
420signpos2.o: ./signpost.c ./puzzles.h
421 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
422singles.o: ./singles.c ./puzzles.h ./latin.h
423 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
424singles5.o: ./singles.c ./puzzles.h ./latin.h
425 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
426singles3.o: ./singles.c ./puzzles.h ./latin.h
427 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
428sixteen.o: ./sixteen.c ./puzzles.h
429 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
430sixteen3.o: ./sixteen.c ./puzzles.h
431 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
432slant.o: ./slant.c ./puzzles.h
433 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
434slant5.o: ./slant.c ./puzzles.h
435 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
436slant2.o: ./slant.c ./puzzles.h
437 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
438solo.o: ./solo.c ./puzzles.h
439 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
440solo5.o: ./solo.c ./puzzles.h
441 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
442solo2.o: ./solo.c ./puzzles.h
443 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
444tdq.o: ./tdq.c ./puzzles.h
445 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
446tents.o: ./tents.c ./puzzles.h ./maxflow.h
447 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
448tents5.o: ./tents.c ./puzzles.h ./maxflow.h
449 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
450tents3.o: ./tents.c ./puzzles.h ./maxflow.h
451 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
452towers.o: ./towers.c ./puzzles.h ./latin.h
453 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
454towers5.o: ./towers.c ./puzzles.h ./latin.h
455 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
456towers2.o: ./towers.c ./puzzles.h ./latin.h
457 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
458tracks.o: ./tracks.c ./puzzles.h
459 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
460tracks3.o: ./tracks.c ./puzzles.h
461 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
462tree234.o: ./tree234.c ./tree234.h ./puzzles.h
463 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
464twiddle.o: ./twiddle.c ./puzzles.h
465 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
466twiddle3.o: ./twiddle.c ./puzzles.h
467 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
468undead.o: ./undead.c ./puzzles.h
469 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
470undead3.o: ./undead.c ./puzzles.h
471 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
472unequal.o: ./unequal.c ./puzzles.h ./latin.h
473 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
474unequal5.o: ./unequal.c ./puzzles.h ./latin.h
475 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
476unequal2.o: ./unequal.c ./puzzles.h ./latin.h
477 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
478unruly.o: ./unruly.c ./puzzles.h
479 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
480unruly5.o: ./unruly.c ./puzzles.h
481 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
482unruly2.o: ./unruly.c ./puzzles.h
483 $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
484untangle.o: ./untangle.c ./puzzles.h ./tree234.h
485 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
486untangl3.o: ./untangle.c ./puzzles.h ./tree234.h
487 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
488version.o: ./version.c ./version.h
489 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
490windows.o: ./windows.c ./puzzles.h ./resource.h
491 $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@
492windows1.o: ./windows.c ./puzzles.h ./resource.h
493 $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
494
495
496clean:
497 rm -rf *.o $(OUTPREFIX)blackbox.js $(OUTPREFIX)bridges.js $(OUTPREFIX)cube.js $(OUTPREFIX)dominosa.js $(OUTPREFIX)fifteen.js $(OUTPREFIX)filling.js $(OUTPREFIX)flip.js $(OUTPREFIX)flood.js $(OUTPREFIX)galaxies.js $(OUTPREFIX)guess.js $(OUTPREFIX)inertia.js $(OUTPREFIX)keen.js $(OUTPREFIX)lightup.js $(OUTPREFIX)loopy.js $(OUTPREFIX)magnets.js $(OUTPREFIX)map.js $(OUTPREFIX)mines.js $(OUTPREFIX)net.js $(OUTPREFIX)netslide.js $(OUTPREFIX)nullgame.js $(OUTPREFIX)palisade.js $(OUTPREFIX)pattern.js $(OUTPREFIX)pearl.js $(OUTPREFIX)pegs.js $(OUTPREFIX)range.js $(OUTPREFIX)rect.js $(OUTPREFIX)samegame.js $(OUTPREFIX)signpost.js $(OUTPREFIX)singles.js $(OUTPREFIX)sixteen.js $(OUTPREFIX)slant.js $(OUTPREFIX)solo.js $(OUTPREFIX)tents.js $(OUTPREFIX)towers.js $(OUTPREFIX)tracks.js $(OUTPREFIX)twiddle.js $(OUTPREFIX)undead.js $(OUTPREFIX)unequal.js $(OUTPREFIX)unruly.js $(OUTPREFIX)untangle.js
diff --git a/apps/plugins/puzzles/src/Makefile.gnustep b/apps/plugins/puzzles/src/Makefile.gnustep
new file mode 100644
index 0000000000..79677f2728
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.gnustep
@@ -0,0 +1,412 @@
1# Makefile for puzzles under GNUstep.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5NEEDS_GUI=yes
6include $(GNUSTEP_MAKEFILES)/common.make
7include $(GNUSTEP_MAKEFILES)/rules.make
8include $(GNUSTEP_MAKEFILES)/Instance/rules.make
9
10all:: Puzzles fifteensolver fillingsolver galaxiespicture galaxiessolver \
11 keensolver latincheck lightupsolver loopysolver \
12 magnetssolver mapsolver mineobfusc obfusc patternpicture \
13 patternsolver pearlbench signpostsolver singlessolver \
14 slantsolver solosolver tentssolver towerssolver \
15 unequalsolver unrulysolver
16
17.SUFFIXES: .o .c .m
18
19
20
21Puzzles.app:
22 mkdir -p $@
23Puzzles.app/Resources: Puzzles.app
24 mkdir -p $@
25Puzzles.app/Resources/Puzzles.icns: Puzzles.app/Resources osx.icns
26 cp osx.icns $@
27Puzzles.app/Info.plist: Puzzles.app osx-info.plist
28 cp osx-info.plist $@
29Puzzles: Puzzles.app Puzzles.app/Puzzles \
30 Puzzles.app/Resources/Puzzles.icns Puzzles.app/Info.plist \
31 $(Puzzles_extra)
32
33Puzzles.app/Puzzles: blackbo3.o bridges3.o combi.o cube3.o divvy.o \
34 dominos3.o drawing.o dsf.o fifteen5.o filling5.o findloop.o \
35 flip3.o flood3.o galaxie7.o grid.o guess3.o inertia3.o \
36 keen5.o latin.o laydomino.o lightup5.o list.o loopgen.o \
37 loopy5.o magnets5.o malloc.o map5.o maxflow.o midend.o \
38 mines5.o misc.o net3.o netslid3.o osx.o palisad3.o \
39 pattern7.o pearl5.o pegs3.o penrose.o random.o range3.o \
40 rect3.o samegam3.o signpos5.o singles5.o sixteen3.o slant5.o \
41 solo5.o tdq.o tents5.o towers5.o tracks3.o tree234.o \
42 twiddle3.o undead3.o unequal5.o unruly5.o untangl3.o \
43 version.o
44 $(CC) $(ALL_LDFLAGS) -o $@ blackbo3.o bridges3.o combi.o cube3.o \
45 divvy.o dominos3.o drawing.o dsf.o fifteen5.o filling5.o \
46 findloop.o flip3.o flood3.o galaxie7.o grid.o guess3.o \
47 inertia3.o keen5.o latin.o laydomino.o lightup5.o list.o \
48 loopgen.o loopy5.o magnets5.o malloc.o map5.o maxflow.o \
49 midend.o mines5.o misc.o net3.o netslid3.o osx.o palisad3.o \
50 pattern7.o pearl5.o pegs3.o penrose.o random.o range3.o \
51 rect3.o samegam3.o signpos5.o singles5.o sixteen3.o slant5.o \
52 solo5.o tdq.o tents5.o towers5.o tracks3.o tree234.o \
53 twiddle3.o undead3.o unequal5.o unruly5.o untangl3.o \
54 version.o $(ALL_LIB_DIRS) $(ALL_LIBS)
55
56fifteensolver: fifteen2.o malloc.o misc.o nullfe.o random.o
57 $(CC) $(ULDFLAGS) -o $@ fifteen2.o malloc.o misc.o nullfe.o random.o
58
59fillingsolver: dsf.o filling2.o malloc.o misc.o nullfe.o random.o
60 $(CC) $(ULDFLAGS) -o $@ dsf.o filling2.o malloc.o misc.o nullfe.o \
61 random.o
62
63galaxiespicture: dsf.o galaxie4.o malloc.o misc.o nullfe.o random.o
64 $(CC) $(ULDFLAGS) -o $@ dsf.o galaxie4.o malloc.o misc.o nullfe.o \
65 random.o -lm
66
67galaxiessolver: dsf.o galaxie2.o malloc.o misc.o nullfe.o random.o
68 $(CC) $(ULDFLAGS) -o $@ dsf.o galaxie2.o malloc.o misc.o nullfe.o \
69 random.o -lm
70
71keensolver: dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o nullfe.o \
72 random.o tree234.o
73 $(CC) $(ULDFLAGS) -o $@ dsf.o keen2.o latin6.o malloc.o maxflow.o \
74 misc.o nullfe.o random.o tree234.o
75
76latincheck: latin8.o malloc.o maxflow.o misc.o nullfe.o random.o tree234.o
77 $(CC) $(ULDFLAGS) -o $@ latin8.o malloc.o maxflow.o misc.o nullfe.o \
78 random.o tree234.o
79
80lightupsolver: combi.o lightup2.o malloc.o misc.o nullfe.o random.o
81 $(CC) $(ULDFLAGS) -o $@ combi.o lightup2.o malloc.o misc.o nullfe.o \
82 random.o
83
84loopysolver: dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o nullfe.o \
85 penrose.o random.o tree234.o
86 $(CC) $(ULDFLAGS) -o $@ dsf.o grid.o loopgen.o loopy2.o malloc.o \
87 misc.o nullfe.o penrose.o random.o tree234.o -lm
88
89magnetssolver: laydomino.o magnets2.o malloc.o misc.o nullfe.o random.o
90 $(CC) $(ULDFLAGS) -o $@ laydomino.o magnets2.o malloc.o misc.o \
91 nullfe.o random.o -lm
92
93mapsolver: dsf.o malloc.o map2.o misc.o nullfe.o random.o
94 $(CC) $(ULDFLAGS) -o $@ dsf.o malloc.o map2.o misc.o nullfe.o \
95 random.o -lm
96
97mineobfusc: malloc.o mines2.o misc.o nullfe.o random.o tree234.o
98 $(CC) $(ULDFLAGS) -o $@ malloc.o mines2.o misc.o nullfe.o random.o \
99 tree234.o
100
101obfusc: malloc.o misc.o nullfe.o obfusc.o random.o
102 $(CC) $(ULDFLAGS) -o $@ malloc.o misc.o nullfe.o obfusc.o random.o
103
104patternpicture: malloc.o misc.o nullfe.o pattern4.o random.o
105 $(CC) $(ULDFLAGS) -o $@ malloc.o misc.o nullfe.o pattern4.o random.o
106
107patternsolver: malloc.o misc.o nullfe.o pattern2.o random.o
108 $(CC) $(ULDFLAGS) -o $@ malloc.o misc.o nullfe.o pattern2.o random.o
109
110pearlbench: dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o pearl2.o \
111 penrose.o random.o tdq.o tree234.o
112 $(CC) $(ULDFLAGS) -o $@ dsf.o grid.o loopgen.o malloc.o misc.o \
113 nullfe.o pearl2.o penrose.o random.o tdq.o tree234.o -lm
114
115signpostsolver: dsf.o malloc.o misc.o nullfe.o random.o signpos2.o
116 $(CC) $(ULDFLAGS) -o $@ dsf.o malloc.o misc.o nullfe.o random.o \
117 signpos2.o -lm
118
119singlessolver: dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o random.o \
120 singles3.o tree234.o
121 $(CC) $(ULDFLAGS) -o $@ dsf.o latin.o malloc.o maxflow.o misc.o \
122 nullfe.o random.o singles3.o tree234.o
123
124slantsolver: dsf.o findloop.o malloc.o misc.o nullfe.o random.o slant2.o
125 $(CC) $(ULDFLAGS) -o $@ dsf.o findloop.o malloc.o misc.o nullfe.o \
126 random.o slant2.o
127
128solosolver: divvy.o dsf.o malloc.o misc.o nullfe.o random.o solo2.o
129 $(CC) $(ULDFLAGS) -o $@ divvy.o dsf.o malloc.o misc.o nullfe.o \
130 random.o solo2.o
131
132tentssolver: dsf.o malloc.o maxflow.o misc.o nullfe.o random.o tents3.o
133 $(CC) $(ULDFLAGS) -o $@ dsf.o malloc.o maxflow.o misc.o nullfe.o \
134 random.o tents3.o
135
136towerssolver: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o towers2.o \
137 tree234.o
138 $(CC) $(ULDFLAGS) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o \
139 random.o towers2.o tree234.o
140
141unequalsolver: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
142 tree234.o unequal2.o
143 $(CC) $(ULDFLAGS) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o \
144 random.o tree234.o unequal2.o
145
146unrulysolver: malloc.o misc.o nullfe.o random.o unruly2.o
147 $(CC) $(ULDFLAGS) -o $@ malloc.o misc.o nullfe.o random.o unruly2.o
148
149blackbox.o: ./blackbox.c ./puzzles.h
150 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
151blackbo3.o: ./blackbox.c ./puzzles.h
152 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
153bridges.o: ./bridges.c ./puzzles.h
154 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
155bridges3.o: ./bridges.c ./puzzles.h
156 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
157combi.o: ./combi.c ./puzzles.h
158 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
159cube.o: ./cube.c ./puzzles.h
160 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
161cube3.o: ./cube.c ./puzzles.h
162 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
163divvy.o: ./divvy.c ./puzzles.h
164 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
165dominosa.o: ./dominosa.c ./puzzles.h
166 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
167dominos3.o: ./dominosa.c ./puzzles.h
168 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
169drawing.o: ./drawing.c ./puzzles.h
170 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
171dsf.o: ./dsf.c ./puzzles.h
172 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
173fifteen.o: ./fifteen.c ./puzzles.h
174 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
175fifteen5.o: ./fifteen.c ./puzzles.h
176 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
177fifteen2.o: ./fifteen.c ./puzzles.h
178 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
179filling.o: ./filling.c ./puzzles.h
180 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
181filling5.o: ./filling.c ./puzzles.h
182 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
183filling2.o: ./filling.c ./puzzles.h
184 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
185findloop.o: ./findloop.c ./puzzles.h
186 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
187flip.o: ./flip.c ./puzzles.h ./tree234.h
188 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
189flip3.o: ./flip.c ./puzzles.h ./tree234.h
190 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
191flood.o: ./flood.c ./puzzles.h
192 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
193flood3.o: ./flood.c ./puzzles.h
194 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
195galaxies.o: ./galaxies.c ./puzzles.h
196 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
197galaxie7.o: ./galaxies.c ./puzzles.h
198 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
199galaxie4.o: ./galaxies.c ./puzzles.h
200 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
201galaxie2.o: ./galaxies.c ./puzzles.h
202 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
203grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h
204 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
205gtk.o: ./gtk.c ./puzzles.h
206 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
207guess.o: ./guess.c ./puzzles.h
208 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
209guess3.o: ./guess.c ./puzzles.h
210 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
211inertia.o: ./inertia.c ./puzzles.h
212 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
213inertia3.o: ./inertia.c ./puzzles.h
214 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
215keen.o: ./keen.c ./puzzles.h ./latin.h
216 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
217keen5.o: ./keen.c ./puzzles.h ./latin.h
218 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
219keen2.o: ./keen.c ./puzzles.h ./latin.h
220 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
221latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
222 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
223latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
224 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@
225latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
226 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
227laydomino.o: ./laydomino.c ./puzzles.h
228 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
229lightup.o: ./lightup.c ./puzzles.h
230 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
231lightup5.o: ./lightup.c ./puzzles.h
232 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
233lightup2.o: ./lightup.c ./puzzles.h
234 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
235list.o: ./list.c ./puzzles.h
236 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
237loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
238 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
239loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
240 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
241loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
242 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
243loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
244 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
245magnets.o: ./magnets.c ./puzzles.h
246 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
247magnets5.o: ./magnets.c ./puzzles.h
248 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
249magnets2.o: ./magnets.c ./puzzles.h
250 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
251malloc.o: ./malloc.c ./puzzles.h
252 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
253map.o: ./map.c ./puzzles.h
254 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
255map5.o: ./map.c ./puzzles.h
256 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
257map2.o: ./map.c ./puzzles.h
258 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
259maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h
260 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
261midend.o: ./midend.c ./puzzles.h
262 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
263mines.o: ./mines.c ./tree234.h ./puzzles.h
264 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
265mines5.o: ./mines.c ./tree234.h ./puzzles.h
266 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
267mines2.o: ./mines.c ./tree234.h ./puzzles.h
268 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@
269misc.o: ./misc.c ./puzzles.h
270 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
271net.o: ./net.c ./puzzles.h ./tree234.h
272 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
273net3.o: ./net.c ./puzzles.h ./tree234.h
274 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
275netslide.o: ./netslide.c ./puzzles.h ./tree234.h
276 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
277netslid3.o: ./netslide.c ./puzzles.h ./tree234.h
278 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
279no-icon.o: ./no-icon.c
280 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
281nullfe.o: ./nullfe.c ./puzzles.h
282 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
283nullgame.o: ./nullgame.c ./puzzles.h
284 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
285obfusc.o: ./obfusc.c ./puzzles.h
286 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
287osx.o: ./osx.m ./puzzles.h
288 $(CC) -DGNUSTEP $(ALL_OBJCFLAGS) $(COMPAT) $(FWHACK) $(OBJCFLAGS) $(XFLAGS) -c $< -o $@
289palisade.o: ./palisade.c ./puzzles.h
290 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
291palisad3.o: ./palisade.c ./puzzles.h
292 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
293pattern.o: ./pattern.c ./puzzles.h
294 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
295pattern7.o: ./pattern.c ./puzzles.h
296 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
297pattern4.o: ./pattern.c ./puzzles.h
298 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
299pattern2.o: ./pattern.c ./puzzles.h
300 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
301pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
302 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
303pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
304 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
305pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
306 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
307pegs.o: ./pegs.c ./puzzles.h ./tree234.h
308 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
309pegs3.o: ./pegs.c ./puzzles.h ./tree234.h
310 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
311penrose.o: ./penrose.c ./puzzles.h ./penrose.h
312 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
313printing.o: ./printing.c ./puzzles.h
314 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
315ps.o: ./ps.c ./puzzles.h
316 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
317random.o: ./random.c ./puzzles.h
318 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
319range.o: ./range.c ./puzzles.h
320 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
321range3.o: ./range.c ./puzzles.h
322 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
323rect.o: ./rect.c ./puzzles.h
324 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
325rect3.o: ./rect.c ./puzzles.h
326 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
327samegame.o: ./samegame.c ./puzzles.h
328 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
329samegam3.o: ./samegame.c ./puzzles.h
330 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
331signpost.o: ./signpost.c ./puzzles.h
332 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
333signpos5.o: ./signpost.c ./puzzles.h
334 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
335signpos2.o: ./signpost.c ./puzzles.h
336 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
337singles.o: ./singles.c ./puzzles.h ./latin.h
338 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
339singles5.o: ./singles.c ./puzzles.h ./latin.h
340 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
341singles3.o: ./singles.c ./puzzles.h ./latin.h
342 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
343sixteen.o: ./sixteen.c ./puzzles.h
344 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
345sixteen3.o: ./sixteen.c ./puzzles.h
346 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
347slant.o: ./slant.c ./puzzles.h
348 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
349slant5.o: ./slant.c ./puzzles.h
350 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
351slant2.o: ./slant.c ./puzzles.h
352 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
353solo.o: ./solo.c ./puzzles.h
354 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
355solo5.o: ./solo.c ./puzzles.h
356 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
357solo2.o: ./solo.c ./puzzles.h
358 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
359tdq.o: ./tdq.c ./puzzles.h
360 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
361tents.o: ./tents.c ./puzzles.h ./maxflow.h
362 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
363tents5.o: ./tents.c ./puzzles.h ./maxflow.h
364 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
365tents3.o: ./tents.c ./puzzles.h ./maxflow.h
366 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
367towers.o: ./towers.c ./puzzles.h ./latin.h
368 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
369towers5.o: ./towers.c ./puzzles.h ./latin.h
370 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
371towers2.o: ./towers.c ./puzzles.h ./latin.h
372 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
373tracks.o: ./tracks.c ./puzzles.h
374 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
375tracks3.o: ./tracks.c ./puzzles.h
376 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
377tree234.o: ./tree234.c ./tree234.h ./puzzles.h
378 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
379twiddle.o: ./twiddle.c ./puzzles.h
380 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
381twiddle3.o: ./twiddle.c ./puzzles.h
382 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
383undead.o: ./undead.c ./puzzles.h
384 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
385undead3.o: ./undead.c ./puzzles.h
386 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
387unequal.o: ./unequal.c ./puzzles.h ./latin.h
388 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
389unequal5.o: ./unequal.c ./puzzles.h ./latin.h
390 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
391unequal2.o: ./unequal.c ./puzzles.h ./latin.h
392 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
393unruly.o: ./unruly.c ./puzzles.h
394 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
395unruly5.o: ./unruly.c ./puzzles.h
396 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
397unruly2.o: ./unruly.c ./puzzles.h
398 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
399untangle.o: ./untangle.c ./puzzles.h ./tree234.h
400 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
401untangl3.o: ./untangle.c ./puzzles.h ./tree234.h
402 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
403version.o: ./version.c ./version.h
404 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
405windows.o: ./windows.c ./puzzles.h ./resource.h
406 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
407windows1.o: ./windows.c ./puzzles.h ./resource.h
408 $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
409
410clean::
411 rm -f *.o fifteensolver fillingsolver galaxiespicture galaxiessolver keensolver latincheck lightupsolver loopysolver magnetssolver mapsolver mineobfusc obfusc patternpicture patternsolver pearlbench signpostsolver singlessolver slantsolver solosolver tentssolver towerssolver unequalsolver unrulysolver
412 rm -rf *.app
diff --git a/apps/plugins/puzzles/src/Makefile.gtk b/apps/plugins/puzzles/src/Makefile.gtk
new file mode 100644
index 0000000000..e5c1d40b90
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.gtk
@@ -0,0 +1,727 @@
1# Makefile for puzzles under X/GTK and Unix.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6# You can define this path to point at your tools if you need to
7# TOOLPATH = /opt/gcc/bin
8CC := $(TOOLPATH)$(CC)
9# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'
10# (depending on what works on your system) if you want to enforce
11# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0'
12# if you want to enforce 2.0. The default is to try 2.0 and fall back
13# to 1.2 if it isn't found.
14GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 $$0 2>/dev/null || gtk-config $$0'
15
16CFLAGS := -O2 -Wall -Werror -ansi -pedantic -g -I./ -Iicons/ `$(GTK_CONFIG) \
17 --cflags` $(CFLAGS)
18XLIBS = `$(GTK_CONFIG) --libs` -lm
19ULIBS = -lm#
20INSTALL=install
21INSTALL_PROGRAM=$(INSTALL)
22INSTALL_DATA=$(INSTALL)
23prefix=/usr/local
24exec_prefix=$(prefix)
25bindir=$(exec_prefix)/bin
26gamesdir=$(exec_prefix)/games
27mandir=$(prefix)/man
28man1dir=$(mandir)/man1
29
30all: $(BINPREFIX)blackbox $(BINPREFIX)bridges $(BINPREFIX)cube \
31 $(BINPREFIX)dominosa $(BINPREFIX)fifteen \
32 $(BINPREFIX)fifteensolver $(BINPREFIX)filling \
33 $(BINPREFIX)fillingsolver $(BINPREFIX)flip $(BINPREFIX)flood \
34 $(BINPREFIX)galaxies $(BINPREFIX)galaxiespicture \
35 $(BINPREFIX)galaxiessolver $(BINPREFIX)guess \
36 $(BINPREFIX)inertia $(BINPREFIX)keen $(BINPREFIX)keensolver \
37 $(BINPREFIX)latincheck $(BINPREFIX)lightup \
38 $(BINPREFIX)lightupsolver $(BINPREFIX)loopy \
39 $(BINPREFIX)loopysolver $(BINPREFIX)magnets \
40 $(BINPREFIX)magnetssolver $(BINPREFIX)map \
41 $(BINPREFIX)mapsolver $(BINPREFIX)mineobfusc \
42 $(BINPREFIX)mines $(BINPREFIX)net $(BINPREFIX)netslide \
43 $(BINPREFIX)nullgame $(BINPREFIX)obfusc $(BINPREFIX)palisade \
44 $(BINPREFIX)pattern $(BINPREFIX)patternpicture \
45 $(BINPREFIX)patternsolver $(BINPREFIX)pearl \
46 $(BINPREFIX)pearlbench $(BINPREFIX)pegs $(BINPREFIX)range \
47 $(BINPREFIX)rect $(BINPREFIX)samegame $(BINPREFIX)signpost \
48 $(BINPREFIX)signpostsolver $(BINPREFIX)singles \
49 $(BINPREFIX)singlessolver $(BINPREFIX)sixteen \
50 $(BINPREFIX)slant $(BINPREFIX)slantsolver $(BINPREFIX)solo \
51 $(BINPREFIX)solosolver $(BINPREFIX)tents \
52 $(BINPREFIX)tentssolver $(BINPREFIX)towers \
53 $(BINPREFIX)towerssolver $(BINPREFIX)tracks \
54 $(BINPREFIX)twiddle $(BINPREFIX)undead $(BINPREFIX)unequal \
55 $(BINPREFIX)unequalsolver $(BINPREFIX)unruly \
56 $(BINPREFIX)unrulysolver $(BINPREFIX)untangle
57
58$(BINPREFIX)blackbox: blackbox.o drawing.o gtk.o malloc.o midend.o misc.o \
59 no-icon.o printing.o ps.o random.o version.o
60 $(CC) -o $@ blackbox.o drawing.o gtk.o malloc.o midend.o misc.o \
61 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
62 $(XLIBS)
63
64$(BINPREFIX)bridges: bridges.o drawing.o dsf.o findloop.o gtk.o malloc.o \
65 midend.o misc.o no-icon.o printing.o ps.o random.o version.o
66 $(CC) -o $@ bridges.o drawing.o dsf.o findloop.o gtk.o malloc.o \
67 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
68 $(XLFLAGS) $(XLIBS)
69
70$(BINPREFIX)cube: cube.o drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
71 printing.o ps.o random.o version.o
72 $(CC) -o $@ cube.o drawing.o gtk.o malloc.o midend.o misc.o \
73 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
74 $(XLIBS)
75
76$(BINPREFIX)dominosa: dominosa.o drawing.o gtk.o laydomino.o malloc.o \
77 midend.o misc.o no-icon.o printing.o ps.o random.o version.o
78 $(CC) -o $@ dominosa.o drawing.o gtk.o laydomino.o malloc.o midend.o \
79 misc.o no-icon.o printing.o ps.o random.o version.o \
80 $(XLFLAGS) $(XLIBS)
81
82$(BINPREFIX)fifteen: drawing.o fifteen.o gtk.o malloc.o midend.o misc.o \
83 no-icon.o printing.o ps.o random.o version.o
84 $(CC) -o $@ drawing.o fifteen.o gtk.o malloc.o midend.o misc.o \
85 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
86 $(XLIBS)
87
88$(BINPREFIX)fifteensolver: fifteen2.o malloc.o misc.o nullfe.o random.o
89 $(CC) -o $@ fifteen2.o malloc.o misc.o nullfe.o random.o $(XLFLAGS) \
90 $(ULIBS)
91
92$(BINPREFIX)filling: drawing.o dsf.o filling.o gtk.o malloc.o midend.o \
93 misc.o no-icon.o printing.o ps.o random.o version.o
94 $(CC) -o $@ drawing.o dsf.o filling.o gtk.o malloc.o midend.o misc.o \
95 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
96 $(XLIBS)
97
98$(BINPREFIX)fillingsolver: dsf.o filling2.o malloc.o misc.o nullfe.o \
99 random.o
100 $(CC) -o $@ dsf.o filling2.o malloc.o misc.o nullfe.o random.o \
101 $(XLFLAGS) $(ULIBS)
102
103$(BINPREFIX)flip: drawing.o flip.o gtk.o malloc.o midend.o misc.o no-icon.o \
104 printing.o ps.o random.o tree234.o version.o
105 $(CC) -o $@ drawing.o flip.o gtk.o malloc.o midend.o misc.o \
106 no-icon.o printing.o ps.o random.o tree234.o version.o \
107 $(XLFLAGS) $(XLIBS)
108
109$(BINPREFIX)flood: drawing.o flood.o gtk.o malloc.o midend.o misc.o \
110 no-icon.o printing.o ps.o random.o version.o
111 $(CC) -o $@ drawing.o flood.o gtk.o malloc.o midend.o misc.o \
112 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
113 $(XLIBS)
114
115$(BINPREFIX)galaxies: drawing.o dsf.o galaxies.o gtk.o malloc.o midend.o \
116 misc.o no-icon.o printing.o ps.o random.o version.o
117 $(CC) -o $@ drawing.o dsf.o galaxies.o gtk.o malloc.o midend.o \
118 misc.o no-icon.o printing.o ps.o random.o version.o \
119 $(XLFLAGS) $(XLIBS)
120
121$(BINPREFIX)galaxiespicture: dsf.o galaxie4.o malloc.o misc.o nullfe.o \
122 random.o
123 $(CC) -o $@ dsf.o galaxie4.o malloc.o misc.o nullfe.o random.o -lm \
124 $(XLFLAGS) $(ULIBS)
125
126$(BINPREFIX)galaxiessolver: dsf.o galaxie2.o malloc.o misc.o nullfe.o \
127 random.o
128 $(CC) -o $@ dsf.o galaxie2.o malloc.o misc.o nullfe.o random.o -lm \
129 $(XLFLAGS) $(ULIBS)
130
131$(BINPREFIX)guess: drawing.o gtk.o guess.o malloc.o midend.o misc.o \
132 no-icon.o printing.o ps.o random.o version.o
133 $(CC) -o $@ drawing.o gtk.o guess.o malloc.o midend.o misc.o \
134 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
135 $(XLIBS)
136
137$(BINPREFIX)inertia: drawing.o gtk.o inertia.o malloc.o midend.o misc.o \
138 no-icon.o printing.o ps.o random.o version.o
139 $(CC) -o $@ drawing.o gtk.o inertia.o malloc.o midend.o misc.o \
140 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
141 $(XLIBS)
142
143$(BINPREFIX)keen: drawing.o dsf.o gtk.o keen.o latin.o malloc.o maxflow.o \
144 midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o \
145 version.o
146 $(CC) -o $@ drawing.o dsf.o gtk.o keen.o latin.o malloc.o maxflow.o \
147 midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o \
148 version.o $(XLFLAGS) $(XLIBS)
149
150$(BINPREFIX)keensolver: dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o \
151 nullfe.o random.o tree234.o
152 $(CC) -o $@ dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o \
153 nullfe.o random.o tree234.o $(XLFLAGS) $(ULIBS)
154
155$(BINPREFIX)latincheck: latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \
156 tree234.o
157 $(CC) -o $@ latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \
158 tree234.o $(XLFLAGS) $(ULIBS)
159
160$(BINPREFIX)lightup: combi.o drawing.o gtk.o lightup.o malloc.o midend.o \
161 misc.o no-icon.o printing.o ps.o random.o version.o
162 $(CC) -o $@ combi.o drawing.o gtk.o lightup.o malloc.o midend.o \
163 misc.o no-icon.o printing.o ps.o random.o version.o \
164 $(XLFLAGS) $(XLIBS)
165
166$(BINPREFIX)lightupsolver: combi.o lightup2.o malloc.o misc.o nullfe.o \
167 random.o
168 $(CC) -o $@ combi.o lightup2.o malloc.o misc.o nullfe.o random.o \
169 $(XLFLAGS) $(ULIBS)
170
171$(BINPREFIX)loopy: drawing.o dsf.o grid.o gtk.o loopgen.o loopy.o malloc.o \
172 midend.o misc.o no-icon.o penrose.o printing.o ps.o random.o \
173 tree234.o version.o
174 $(CC) -o $@ drawing.o dsf.o grid.o gtk.o loopgen.o loopy.o malloc.o \
175 midend.o misc.o no-icon.o penrose.o printing.o ps.o random.o \
176 tree234.o version.o $(XLFLAGS) $(XLIBS)
177
178$(BINPREFIX)loopysolver: dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o \
179 nullfe.o penrose.o random.o tree234.o
180 $(CC) -o $@ dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o nullfe.o \
181 penrose.o random.o tree234.o -lm $(XLFLAGS) $(ULIBS)
182
183$(BINPREFIX)magnets: drawing.o gtk.o laydomino.o magnets.o malloc.o midend.o \
184 misc.o no-icon.o printing.o ps.o random.o version.o
185 $(CC) -o $@ drawing.o gtk.o laydomino.o magnets.o malloc.o midend.o \
186 misc.o no-icon.o printing.o ps.o random.o version.o \
187 $(XLFLAGS) $(XLIBS)
188
189$(BINPREFIX)magnetssolver: laydomino.o magnets2.o malloc.o misc.o nullfe.o \
190 random.o
191 $(CC) -o $@ laydomino.o magnets2.o malloc.o misc.o nullfe.o random.o \
192 -lm $(XLFLAGS) $(ULIBS)
193
194$(BINPREFIX)map: drawing.o dsf.o gtk.o malloc.o map.o midend.o misc.o \
195 no-icon.o printing.o ps.o random.o version.o
196 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o map.o midend.o misc.o \
197 no-icon.o printing.o ps.o random.o version.o $(XLFLAGS) \
198 $(XLIBS)
199
200$(BINPREFIX)mapsolver: dsf.o malloc.o map2.o misc.o nullfe.o random.o
201 $(CC) -o $@ dsf.o malloc.o map2.o misc.o nullfe.o random.o -lm \
202 $(XLFLAGS) $(ULIBS)
203
204$(BINPREFIX)mineobfusc: malloc.o mines2.o misc.o nullfe.o random.o tree234.o
205 $(CC) -o $@ malloc.o mines2.o misc.o nullfe.o random.o tree234.o \
206 $(XLFLAGS) $(ULIBS)
207
208$(BINPREFIX)mines: drawing.o gtk.o malloc.o midend.o mines.o misc.o \
209 no-icon.o printing.o ps.o random.o tree234.o version.o
210 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o mines.o misc.o \
211 no-icon.o printing.o ps.o random.o tree234.o version.o \
212 $(XLFLAGS) $(XLIBS)
213
214$(BINPREFIX)net: drawing.o dsf.o findloop.o gtk.o malloc.o midend.o misc.o \
215 net.o no-icon.o printing.o ps.o random.o tree234.o version.o
216 $(CC) -o $@ drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
217 misc.o net.o no-icon.o printing.o ps.o random.o tree234.o \
218 version.o $(XLFLAGS) $(XLIBS)
219
220$(BINPREFIX)netslide: drawing.o gtk.o malloc.o midend.o misc.o netslide.o \
221 no-icon.o printing.o ps.o random.o tree234.o version.o
222 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o netslide.o \
223 no-icon.o printing.o ps.o random.o tree234.o version.o \
224 $(XLFLAGS) $(XLIBS)
225
226$(BINPREFIX)nullgame: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
227 nullgame.o printing.o ps.o random.o version.o
228 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
229 nullgame.o printing.o ps.o random.o version.o $(XLFLAGS) \
230 $(XLIBS)
231
232$(BINPREFIX)obfusc: malloc.o misc.o nullfe.o obfusc.o random.o
233 $(CC) -o $@ malloc.o misc.o nullfe.o obfusc.o random.o $(XLFLAGS) \
234 $(ULIBS)
235
236$(BINPREFIX)palisade: divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
237 no-icon.o palisade.o printing.o ps.o random.o version.o
238 $(CC) -o $@ divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
239 no-icon.o palisade.o printing.o ps.o random.o version.o \
240 $(XLFLAGS) $(XLIBS)
241
242$(BINPREFIX)pattern: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
243 pattern.o printing.o ps.o random.o version.o
244 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
245 pattern.o printing.o ps.o random.o version.o $(XLFLAGS) \
246 $(XLIBS)
247
248$(BINPREFIX)patternpicture: malloc.o misc.o nullfe.o pattern4.o random.o
249 $(CC) -o $@ malloc.o misc.o nullfe.o pattern4.o random.o $(XLFLAGS) \
250 $(ULIBS)
251
252$(BINPREFIX)patternsolver: malloc.o misc.o nullfe.o pattern2.o random.o
253 $(CC) -o $@ malloc.o misc.o nullfe.o pattern2.o random.o $(XLFLAGS) \
254 $(ULIBS)
255
256$(BINPREFIX)pearl: drawing.o dsf.o grid.o gtk.o loopgen.o malloc.o midend.o \
257 misc.o no-icon.o pearl.o penrose.o printing.o ps.o random.o \
258 tdq.o tree234.o version.o
259 $(CC) -o $@ drawing.o dsf.o grid.o gtk.o loopgen.o malloc.o midend.o \
260 misc.o no-icon.o pearl.o penrose.o printing.o ps.o random.o \
261 tdq.o tree234.o version.o $(XLFLAGS) $(XLIBS)
262
263$(BINPREFIX)pearlbench: dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o \
264 pearl2.o penrose.o random.o tdq.o tree234.o
265 $(CC) -o $@ dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o pearl2.o \
266 penrose.o random.o tdq.o tree234.o -lm $(XLFLAGS) $(ULIBS)
267
268$(BINPREFIX)pegs: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o pegs.o \
269 printing.o ps.o random.o tree234.o version.o
270 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
271 pegs.o printing.o ps.o random.o tree234.o version.o \
272 $(XLFLAGS) $(XLIBS)
273
274$(BINPREFIX)range: drawing.o dsf.o gtk.o malloc.o midend.o misc.o no-icon.o \
275 printing.o ps.o random.o range.o version.o
276 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o midend.o misc.o no-icon.o \
277 printing.o ps.o random.o range.o version.o $(XLFLAGS) \
278 $(XLIBS)
279
280$(BINPREFIX)rect: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
281 printing.o ps.o random.o rect.o version.o
282 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
283 printing.o ps.o random.o rect.o version.o $(XLFLAGS) \
284 $(XLIBS)
285
286$(BINPREFIX)samegame: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
287 printing.o ps.o random.o samegame.o version.o
288 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
289 printing.o ps.o random.o samegame.o version.o $(XLFLAGS) \
290 $(XLIBS)
291
292$(BINPREFIX)signpost: drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
293 no-icon.o printing.o ps.o random.o signpost.o version.o
294 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o midend.o misc.o no-icon.o \
295 printing.o ps.o random.o signpost.o version.o $(XLFLAGS) \
296 $(XLIBS)
297
298$(BINPREFIX)signpostsolver: dsf.o malloc.o misc.o nullfe.o random.o \
299 signpos2.o
300 $(CC) -o $@ dsf.o malloc.o misc.o nullfe.o random.o signpos2.o -lm \
301 $(XLFLAGS) $(ULIBS)
302
303$(BINPREFIX)singles: drawing.o dsf.o gtk.o latin.o malloc.o maxflow.o \
304 midend.o misc.o no-icon.o printing.o ps.o random.o singles.o \
305 tree234.o version.o
306 $(CC) -o $@ drawing.o dsf.o gtk.o latin.o malloc.o maxflow.o \
307 midend.o misc.o no-icon.o printing.o ps.o random.o singles.o \
308 tree234.o version.o $(XLFLAGS) $(XLIBS)
309
310$(BINPREFIX)singlessolver: dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o \
311 random.o singles3.o tree234.o
312 $(CC) -o $@ dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o \
313 random.o singles3.o tree234.o $(XLFLAGS) $(ULIBS)
314
315$(BINPREFIX)sixteen: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
316 printing.o ps.o random.o sixteen.o version.o
317 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
318 printing.o ps.o random.o sixteen.o version.o $(XLFLAGS) \
319 $(XLIBS)
320
321$(BINPREFIX)slant: drawing.o dsf.o findloop.o gtk.o malloc.o midend.o misc.o \
322 no-icon.o printing.o ps.o random.o slant.o version.o
323 $(CC) -o $@ drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
324 misc.o no-icon.o printing.o ps.o random.o slant.o version.o \
325 $(XLFLAGS) $(XLIBS)
326
327$(BINPREFIX)slantsolver: dsf.o findloop.o malloc.o misc.o nullfe.o random.o \
328 slant2.o
329 $(CC) -o $@ dsf.o findloop.o malloc.o misc.o nullfe.o random.o \
330 slant2.o $(XLFLAGS) $(ULIBS)
331
332$(BINPREFIX)solo: divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
333 no-icon.o printing.o ps.o random.o solo.o version.o
334 $(CC) -o $@ divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \
335 no-icon.o printing.o ps.o random.o solo.o version.o \
336 $(XLFLAGS) $(XLIBS)
337
338$(BINPREFIX)solosolver: divvy.o dsf.o malloc.o misc.o nullfe.o random.o \
339 solo2.o
340 $(CC) -o $@ divvy.o dsf.o malloc.o misc.o nullfe.o random.o solo2.o \
341 $(XLFLAGS) $(ULIBS)
342
343$(BINPREFIX)tents: drawing.o dsf.o gtk.o malloc.o maxflow.o midend.o misc.o \
344 no-icon.o printing.o ps.o random.o tents.o version.o
345 $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o maxflow.o midend.o misc.o \
346 no-icon.o printing.o ps.o random.o tents.o version.o \
347 $(XLFLAGS) $(XLIBS)
348
349$(BINPREFIX)tentssolver: dsf.o malloc.o maxflow.o misc.o nullfe.o random.o \
350 tents3.o
351 $(CC) -o $@ dsf.o malloc.o maxflow.o misc.o nullfe.o random.o \
352 tents3.o $(XLFLAGS) $(ULIBS)
353
354$(BINPREFIX)towers: drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
355 misc.o no-icon.o printing.o ps.o random.o towers.o tree234.o \
356 version.o
357 $(CC) -o $@ drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
358 misc.o no-icon.o printing.o ps.o random.o towers.o tree234.o \
359 version.o $(XLFLAGS) $(XLIBS)
360
361$(BINPREFIX)towerssolver: latin6.o malloc.o maxflow.o misc.o nullfe.o \
362 random.o towers2.o tree234.o
363 $(CC) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
364 towers2.o tree234.o $(XLFLAGS) $(ULIBS)
365
366$(BINPREFIX)tracks: drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
367 misc.o no-icon.o printing.o ps.o random.o tracks.o version.o
368 $(CC) -o $@ drawing.o dsf.o findloop.o gtk.o malloc.o midend.o \
369 misc.o no-icon.o printing.o ps.o random.o tracks.o version.o \
370 $(XLFLAGS) $(XLIBS)
371
372$(BINPREFIX)twiddle: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
373 printing.o ps.o random.o twiddle.o version.o
374 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
375 printing.o ps.o random.o twiddle.o version.o $(XLFLAGS) \
376 $(XLIBS)
377
378$(BINPREFIX)undead: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
379 printing.o ps.o random.o undead.o version.o
380 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
381 printing.o ps.o random.o undead.o version.o $(XLFLAGS) \
382 $(XLIBS)
383
384$(BINPREFIX)unequal: drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
385 misc.o no-icon.o printing.o ps.o random.o tree234.o \
386 unequal.o version.o
387 $(CC) -o $@ drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \
388 misc.o no-icon.o printing.o ps.o random.o tree234.o \
389 unequal.o version.o $(XLFLAGS) $(XLIBS)
390
391$(BINPREFIX)unequalsolver: latin6.o malloc.o maxflow.o misc.o nullfe.o \
392 random.o tree234.o unequal2.o
393 $(CC) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \
394 tree234.o unequal2.o $(XLFLAGS) $(ULIBS)
395
396$(BINPREFIX)unruly: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
397 printing.o ps.o random.o unruly.o version.o
398 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
399 printing.o ps.o random.o unruly.o version.o $(XLFLAGS) \
400 $(XLIBS)
401
402$(BINPREFIX)unrulysolver: malloc.o misc.o nullfe.o random.o unruly2.o
403 $(CC) -o $@ malloc.o misc.o nullfe.o random.o unruly2.o $(XLFLAGS) \
404 $(ULIBS)
405
406$(BINPREFIX)untangle: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
407 printing.o ps.o random.o tree234.o untangle.o version.o
408 $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \
409 printing.o ps.o random.o tree234.o untangle.o version.o \
410 $(XLFLAGS) $(XLIBS)
411
412blackbox.o: ./blackbox.c ./puzzles.h
413 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
414blackbo3.o: ./blackbox.c ./puzzles.h
415 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
416bridges.o: ./bridges.c ./puzzles.h
417 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
418bridges3.o: ./bridges.c ./puzzles.h
419 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
420combi.o: ./combi.c ./puzzles.h
421 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
422cube.o: ./cube.c ./puzzles.h
423 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
424cube3.o: ./cube.c ./puzzles.h
425 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
426divvy.o: ./divvy.c ./puzzles.h
427 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
428dominosa.o: ./dominosa.c ./puzzles.h
429 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
430dominos3.o: ./dominosa.c ./puzzles.h
431 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
432drawing.o: ./drawing.c ./puzzles.h
433 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
434dsf.o: ./dsf.c ./puzzles.h
435 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
436fifteen.o: ./fifteen.c ./puzzles.h
437 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
438fifteen5.o: ./fifteen.c ./puzzles.h
439 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
440fifteen2.o: ./fifteen.c ./puzzles.h
441 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
442filling.o: ./filling.c ./puzzles.h
443 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
444filling5.o: ./filling.c ./puzzles.h
445 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
446filling2.o: ./filling.c ./puzzles.h
447 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
448findloop.o: ./findloop.c ./puzzles.h
449 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
450flip.o: ./flip.c ./puzzles.h ./tree234.h
451 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
452flip3.o: ./flip.c ./puzzles.h ./tree234.h
453 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
454flood.o: ./flood.c ./puzzles.h
455 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
456flood3.o: ./flood.c ./puzzles.h
457 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
458galaxies.o: ./galaxies.c ./puzzles.h
459 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
460galaxie7.o: ./galaxies.c ./puzzles.h
461 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
462galaxie4.o: ./galaxies.c ./puzzles.h
463 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
464galaxie2.o: ./galaxies.c ./puzzles.h
465 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
466grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h
467 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
468gtk.o: ./gtk.c ./puzzles.h
469 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
470guess.o: ./guess.c ./puzzles.h
471 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
472guess3.o: ./guess.c ./puzzles.h
473 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
474inertia.o: ./inertia.c ./puzzles.h
475 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
476inertia3.o: ./inertia.c ./puzzles.h
477 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
478keen.o: ./keen.c ./puzzles.h ./latin.h
479 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
480keen5.o: ./keen.c ./puzzles.h ./latin.h
481 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
482keen2.o: ./keen.c ./puzzles.h ./latin.h
483 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
484latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
485 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
486latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
487 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@
488latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
489 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
490laydomino.o: ./laydomino.c ./puzzles.h
491 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
492lightup.o: ./lightup.c ./puzzles.h
493 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
494lightup5.o: ./lightup.c ./puzzles.h
495 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
496lightup2.o: ./lightup.c ./puzzles.h
497 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
498list.o: ./list.c ./puzzles.h
499 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
500loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
501 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
502loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
503 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
504loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
505 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
506loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
507 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
508magnets.o: ./magnets.c ./puzzles.h
509 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
510magnets5.o: ./magnets.c ./puzzles.h
511 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
512magnets2.o: ./magnets.c ./puzzles.h
513 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
514malloc.o: ./malloc.c ./puzzles.h
515 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
516map.o: ./map.c ./puzzles.h
517 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
518map5.o: ./map.c ./puzzles.h
519 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
520map2.o: ./map.c ./puzzles.h
521 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
522maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h
523 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
524midend.o: ./midend.c ./puzzles.h
525 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
526mines.o: ./mines.c ./tree234.h ./puzzles.h
527 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
528mines5.o: ./mines.c ./tree234.h ./puzzles.h
529 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
530mines2.o: ./mines.c ./tree234.h ./puzzles.h
531 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@
532misc.o: ./misc.c ./puzzles.h
533 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
534net.o: ./net.c ./puzzles.h ./tree234.h
535 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
536net3.o: ./net.c ./puzzles.h ./tree234.h
537 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
538netslide.o: ./netslide.c ./puzzles.h ./tree234.h
539 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
540netslid3.o: ./netslide.c ./puzzles.h ./tree234.h
541 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
542no-icon.o: ./no-icon.c
543 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
544nullfe.o: ./nullfe.c ./puzzles.h
545 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
546nullgame.o: ./nullgame.c ./puzzles.h
547 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
548obfusc.o: ./obfusc.c ./puzzles.h
549 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
550osx.o: ./osx.m ./puzzles.h
551 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
552palisade.o: ./palisade.c ./puzzles.h
553 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
554palisad3.o: ./palisade.c ./puzzles.h
555 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
556pattern.o: ./pattern.c ./puzzles.h
557 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
558pattern7.o: ./pattern.c ./puzzles.h
559 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
560pattern4.o: ./pattern.c ./puzzles.h
561 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
562pattern2.o: ./pattern.c ./puzzles.h
563 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
564pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
565 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
566pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
567 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
568pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
569 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
570pegs.o: ./pegs.c ./puzzles.h ./tree234.h
571 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
572pegs3.o: ./pegs.c ./puzzles.h ./tree234.h
573 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
574penrose.o: ./penrose.c ./puzzles.h ./penrose.h
575 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
576printing.o: ./printing.c ./puzzles.h
577 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
578ps.o: ./ps.c ./puzzles.h
579 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
580random.o: ./random.c ./puzzles.h
581 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
582range.o: ./range.c ./puzzles.h
583 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
584range3.o: ./range.c ./puzzles.h
585 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
586rect.o: ./rect.c ./puzzles.h
587 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
588rect3.o: ./rect.c ./puzzles.h
589 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
590samegame.o: ./samegame.c ./puzzles.h
591 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
592samegam3.o: ./samegame.c ./puzzles.h
593 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
594signpost.o: ./signpost.c ./puzzles.h
595 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
596signpos5.o: ./signpost.c ./puzzles.h
597 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
598signpos2.o: ./signpost.c ./puzzles.h
599 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
600singles.o: ./singles.c ./puzzles.h ./latin.h
601 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
602singles5.o: ./singles.c ./puzzles.h ./latin.h
603 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
604singles3.o: ./singles.c ./puzzles.h ./latin.h
605 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
606sixteen.o: ./sixteen.c ./puzzles.h
607 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
608sixteen3.o: ./sixteen.c ./puzzles.h
609 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
610slant.o: ./slant.c ./puzzles.h
611 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
612slant5.o: ./slant.c ./puzzles.h
613 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
614slant2.o: ./slant.c ./puzzles.h
615 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
616solo.o: ./solo.c ./puzzles.h
617 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
618solo5.o: ./solo.c ./puzzles.h
619 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
620solo2.o: ./solo.c ./puzzles.h
621 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
622tdq.o: ./tdq.c ./puzzles.h
623 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
624tents.o: ./tents.c ./puzzles.h ./maxflow.h
625 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
626tents5.o: ./tents.c ./puzzles.h ./maxflow.h
627 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
628tents3.o: ./tents.c ./puzzles.h ./maxflow.h
629 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
630towers.o: ./towers.c ./puzzles.h ./latin.h
631 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
632towers5.o: ./towers.c ./puzzles.h ./latin.h
633 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
634towers2.o: ./towers.c ./puzzles.h ./latin.h
635 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
636tracks.o: ./tracks.c ./puzzles.h
637 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
638tracks3.o: ./tracks.c ./puzzles.h
639 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
640tree234.o: ./tree234.c ./tree234.h ./puzzles.h
641 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
642twiddle.o: ./twiddle.c ./puzzles.h
643 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
644twiddle3.o: ./twiddle.c ./puzzles.h
645 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
646undead.o: ./undead.c ./puzzles.h
647 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
648undead3.o: ./undead.c ./puzzles.h
649 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
650unequal.o: ./unequal.c ./puzzles.h ./latin.h
651 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
652unequal5.o: ./unequal.c ./puzzles.h ./latin.h
653 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
654unequal2.o: ./unequal.c ./puzzles.h ./latin.h
655 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
656unruly.o: ./unruly.c ./puzzles.h
657 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
658unruly5.o: ./unruly.c ./puzzles.h
659 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
660unruly2.o: ./unruly.c ./puzzles.h
661 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
662untangle.o: ./untangle.c ./puzzles.h ./tree234.h
663 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
664untangl3.o: ./untangle.c ./puzzles.h ./tree234.h
665 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
666version.o: ./version.c ./version.h
667 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
668windows.o: ./windows.c ./puzzles.h ./resource.h
669 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
670windows1.o: ./windows.c ./puzzles.h ./resource.h
671 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
672
673GAMES += blackbox
674GAMES += bridges
675GAMES += cube
676GAMES += dominosa
677GAMES += fifteen
678GAMES += filling
679GAMES += flip
680GAMES += flood
681GAMES += galaxies
682GAMES += guess
683GAMES += inertia
684GAMES += keen
685GAMES += lightup
686GAMES += loopy
687GAMES += magnets
688GAMES += map
689GAMES += mines
690GAMES += net
691GAMES += netslide
692GAMES += palisade
693GAMES += pattern
694GAMES += pearl
695GAMES += pegs
696GAMES += range
697GAMES += rect
698GAMES += samegame
699GAMES += signpost
700GAMES += singles
701GAMES += sixteen
702GAMES += slant
703GAMES += solo
704GAMES += tents
705GAMES += towers
706GAMES += tracks
707GAMES += twiddle
708GAMES += undead
709GAMES += unequal
710GAMES += unruly
711GAMES += untangle
712install:
713 for i in $(GAMES); do \
714 $(INSTALL_PROGRAM) -m 755 $(BINPREFIX)$$i $(DESTDIR)$(gamesdir)/$(BINPREFIX)$$i \
715 || exit 1; \
716 done
717test: benchmark.html benchmark.txt
718
719benchmark.html: benchmark.txt benchmark.pl
720 ./benchmark.pl benchmark.txt > $@
721
722benchmark.txt: benchmark.sh $(GAMES)
723 ./benchmark.sh > $@
724
725
726clean:
727 rm -f *.o $(BINPREFIX)blackbox $(BINPREFIX)bridges $(BINPREFIX)cube $(BINPREFIX)dominosa $(BINPREFIX)fifteen $(BINPREFIX)fifteensolver $(BINPREFIX)filling $(BINPREFIX)fillingsolver $(BINPREFIX)flip $(BINPREFIX)flood $(BINPREFIX)galaxies $(BINPREFIX)galaxiespicture $(BINPREFIX)galaxiessolver $(BINPREFIX)guess $(BINPREFIX)inertia $(BINPREFIX)keen $(BINPREFIX)keensolver $(BINPREFIX)latincheck $(BINPREFIX)lightup $(BINPREFIX)lightupsolver $(BINPREFIX)loopy $(BINPREFIX)loopysolver $(BINPREFIX)magnets $(BINPREFIX)magnetssolver $(BINPREFIX)map $(BINPREFIX)mapsolver $(BINPREFIX)mineobfusc $(BINPREFIX)mines $(BINPREFIX)net $(BINPREFIX)netslide $(BINPREFIX)nullgame $(BINPREFIX)obfusc $(BINPREFIX)palisade $(BINPREFIX)pattern $(BINPREFIX)patternpicture $(BINPREFIX)patternsolver $(BINPREFIX)pearl $(BINPREFIX)pearlbench $(BINPREFIX)pegs $(BINPREFIX)range $(BINPREFIX)rect $(BINPREFIX)samegame $(BINPREFIX)signpost $(BINPREFIX)signpostsolver $(BINPREFIX)singles $(BINPREFIX)singlessolver $(BINPREFIX)sixteen $(BINPREFIX)slant $(BINPREFIX)slantsolver $(BINPREFIX)solo $(BINPREFIX)solosolver $(BINPREFIX)tents $(BINPREFIX)tentssolver $(BINPREFIX)towers $(BINPREFIX)towerssolver $(BINPREFIX)tracks $(BINPREFIX)twiddle $(BINPREFIX)undead $(BINPREFIX)unequal $(BINPREFIX)unequalsolver $(BINPREFIX)unruly $(BINPREFIX)unrulysolver $(BINPREFIX)untangle
diff --git a/apps/plugins/puzzles/src/Makefile.in b/apps/plugins/puzzles/src/Makefile.in
new file mode 100644
index 0000000000..b86194f893
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.in
@@ -0,0 +1,2611 @@
1# Makefile.in generated by automake 1.15 from Makefile.am.
2# @configure_input@
3
4# Copyright (C) 1994-2014 Free Software Foundation, Inc.
5
6# This Makefile.in is free software; the Free Software Foundation
7# gives unlimited permission to copy and/or distribute it,
8# with or without modifications, as long as this notice is preserved.
9
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
12# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13# PARTICULAR PURPOSE.
14
15@SET_MAKE@
16
17# Makefile.am for puzzles under Unix with Autoconf/Automake.
18#
19# This file was created by `mkfiles.pl' from the `Recipe' file.
20# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
21
22
23VPATH = @srcdir@
24am__is_gnu_make = { \
25 if test -z '$(MAKELEVEL)'; then \
26 false; \
27 elif test -n '$(MAKE_HOST)'; then \
28 true; \
29 elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
30 true; \
31 else \
32 false; \
33 fi; \
34}
35am__make_running_with_option = \
36 case $${target_option-} in \
37 ?) ;; \
38 *) echo "am__make_running_with_option: internal error: invalid" \
39 "target option '$${target_option-}' specified" >&2; \
40 exit 1;; \
41 esac; \
42 has_opt=no; \
43 sane_makeflags=$$MAKEFLAGS; \
44 if $(am__is_gnu_make); then \
45 sane_makeflags=$$MFLAGS; \
46 else \
47 case $$MAKEFLAGS in \
48 *\\[\ \ ]*) \
49 bs=\\; \
50 sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
51 | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
52 esac; \
53 fi; \
54 skip_next=no; \
55 strip_trailopt () \
56 { \
57 flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
58 }; \
59 for flg in $$sane_makeflags; do \
60 test $$skip_next = yes && { skip_next=no; continue; }; \
61 case $$flg in \
62 *=*|--*) continue;; \
63 -*I) strip_trailopt 'I'; skip_next=yes;; \
64 -*I?*) strip_trailopt 'I';; \
65 -*O) strip_trailopt 'O'; skip_next=yes;; \
66 -*O?*) strip_trailopt 'O';; \
67 -*l) strip_trailopt 'l'; skip_next=yes;; \
68 -*l?*) strip_trailopt 'l';; \
69 -[dEDm]) skip_next=yes;; \
70 -[JT]) skip_next=yes;; \
71 esac; \
72 case $$flg in \
73 *$$target_option*) has_opt=yes; break;; \
74 esac; \
75 done; \
76 test $$has_opt = yes
77am__make_dryrun = (target_option=n; $(am__make_running_with_option))
78am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
79pkgdatadir = $(datadir)/@PACKAGE@
80pkgincludedir = $(includedir)/@PACKAGE@
81pkglibdir = $(libdir)/@PACKAGE@
82pkglibexecdir = $(libexecdir)/@PACKAGE@
83am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
84install_sh_DATA = $(install_sh) -c -m 644
85install_sh_PROGRAM = $(install_sh) -c
86install_sh_SCRIPT = $(install_sh) -c
87INSTALL_HEADER = $(INSTALL_DATA)
88transform = $(program_transform_name)
89NORMAL_INSTALL = :
90PRE_INSTALL = :
91POST_INSTALL = :
92NORMAL_UNINSTALL = :
93PRE_UNINSTALL = :
94POST_UNINSTALL = :
95noinst_PROGRAMS = blackbox$(EXEEXT) bridges$(EXEEXT) cube$(EXEEXT) \
96 dominosa$(EXEEXT) fifteen$(EXEEXT) fifteensolver$(EXEEXT) \
97 filling$(EXEEXT) fillingsolver$(EXEEXT) flip$(EXEEXT) \
98 flood$(EXEEXT) galaxies$(EXEEXT) galaxiespicture$(EXEEXT) \
99 galaxiessolver$(EXEEXT) guess$(EXEEXT) inertia$(EXEEXT) \
100 keen$(EXEEXT) keensolver$(EXEEXT) latincheck$(EXEEXT) \
101 lightup$(EXEEXT) lightupsolver$(EXEEXT) loopy$(EXEEXT) \
102 loopysolver$(EXEEXT) magnets$(EXEEXT) magnetssolver$(EXEEXT) \
103 map$(EXEEXT) mapsolver$(EXEEXT) mineobfusc$(EXEEXT) \
104 mines$(EXEEXT) net$(EXEEXT) netslide$(EXEEXT) \
105 nullgame$(EXEEXT) obfusc$(EXEEXT) palisade$(EXEEXT) \
106 pattern$(EXEEXT) patternpicture$(EXEEXT) \
107 patternsolver$(EXEEXT) pearl$(EXEEXT) pearlbench$(EXEEXT) \
108 pegs$(EXEEXT) range$(EXEEXT) rect$(EXEEXT) samegame$(EXEEXT) \
109 signpost$(EXEEXT) signpostsolver$(EXEEXT) singles$(EXEEXT) \
110 singlessolver$(EXEEXT) sixteen$(EXEEXT) slant$(EXEEXT) \
111 slantsolver$(EXEEXT) solo$(EXEEXT) solosolver$(EXEEXT) \
112 tents$(EXEEXT) tentssolver$(EXEEXT) towers$(EXEEXT) \
113 towerssolver$(EXEEXT) tracks$(EXEEXT) twiddle$(EXEEXT) \
114 undead$(EXEEXT) unequal$(EXEEXT) unequalsolver$(EXEEXT) \
115 unruly$(EXEEXT) unrulysolver$(EXEEXT) untangle$(EXEEXT)
116bin_PROGRAMS = $(am__EXEEXT_1)
117subdir = .
118ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
119am__aclocal_m4_deps = $(top_srcdir)/configure.ac
120am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
121 $(ACLOCAL_M4)
122DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
123 $(am__configure_deps) $(am__DIST_COMMON)
124am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
125 configure.lineno config.status.lineno
126mkinstalldirs = $(install_sh) -d
127CONFIG_CLEAN_FILES =
128CONFIG_CLEAN_VPATH_FILES =
129LIBRARIES = $(noinst_LIBRARIES)
130AR = ar
131ARFLAGS = cru
132AM_V_AR = $(am__v_AR_@AM_V@)
133am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
134am__v_AR_0 = @echo " AR " $@;
135am__v_AR_1 =
136libfifteen2_a_AR = $(AR) $(ARFLAGS)
137libfifteen2_a_LIBADD =
138am__dirstamp = $(am__leading_dot)dirstamp
139am_libfifteen2_a_OBJECTS = ./libfifteen2_a-fifteen.$(OBJEXT)
140libfifteen2_a_OBJECTS = $(am_libfifteen2_a_OBJECTS)
141libfilling2_a_AR = $(AR) $(ARFLAGS)
142libfilling2_a_LIBADD =
143am_libfilling2_a_OBJECTS = ./libfilling2_a-filling.$(OBJEXT)
144libfilling2_a_OBJECTS = $(am_libfilling2_a_OBJECTS)
145libgalaxie2_a_AR = $(AR) $(ARFLAGS)
146libgalaxie2_a_LIBADD =
147am_libgalaxie2_a_OBJECTS = ./libgalaxie2_a-galaxies.$(OBJEXT)
148libgalaxie2_a_OBJECTS = $(am_libgalaxie2_a_OBJECTS)
149libgalaxie4_a_AR = $(AR) $(ARFLAGS)
150libgalaxie4_a_LIBADD =
151am_libgalaxie4_a_OBJECTS = ./libgalaxie4_a-galaxies.$(OBJEXT)
152libgalaxie4_a_OBJECTS = $(am_libgalaxie4_a_OBJECTS)
153libkeen2_a_AR = $(AR) $(ARFLAGS)
154libkeen2_a_LIBADD =
155am_libkeen2_a_OBJECTS = ./libkeen2_a-keen.$(OBJEXT)
156libkeen2_a_OBJECTS = $(am_libkeen2_a_OBJECTS)
157liblatin6_a_AR = $(AR) $(ARFLAGS)
158liblatin6_a_LIBADD =
159am_liblatin6_a_OBJECTS = ./liblatin6_a-latin.$(OBJEXT)
160liblatin6_a_OBJECTS = $(am_liblatin6_a_OBJECTS)
161liblatin8_a_AR = $(AR) $(ARFLAGS)
162liblatin8_a_LIBADD =
163am_liblatin8_a_OBJECTS = ./liblatin8_a-latin.$(OBJEXT)
164liblatin8_a_OBJECTS = $(am_liblatin8_a_OBJECTS)
165liblightup2_a_AR = $(AR) $(ARFLAGS)
166liblightup2_a_LIBADD =
167am_liblightup2_a_OBJECTS = ./liblightup2_a-lightup.$(OBJEXT)
168liblightup2_a_OBJECTS = $(am_liblightup2_a_OBJECTS)
169libloopy2_a_AR = $(AR) $(ARFLAGS)
170libloopy2_a_LIBADD =
171am_libloopy2_a_OBJECTS = ./libloopy2_a-loopy.$(OBJEXT)
172libloopy2_a_OBJECTS = $(am_libloopy2_a_OBJECTS)
173libmagnets2_a_AR = $(AR) $(ARFLAGS)
174libmagnets2_a_LIBADD =
175am_libmagnets2_a_OBJECTS = ./libmagnets2_a-magnets.$(OBJEXT)
176libmagnets2_a_OBJECTS = $(am_libmagnets2_a_OBJECTS)
177libmap2_a_AR = $(AR) $(ARFLAGS)
178libmap2_a_LIBADD =
179am_libmap2_a_OBJECTS = ./libmap2_a-map.$(OBJEXT)
180libmap2_a_OBJECTS = $(am_libmap2_a_OBJECTS)
181libmines2_a_AR = $(AR) $(ARFLAGS)
182libmines2_a_LIBADD =
183am_libmines2_a_OBJECTS = ./libmines2_a-mines.$(OBJEXT)
184libmines2_a_OBJECTS = $(am_libmines2_a_OBJECTS)
185libpattern2_a_AR = $(AR) $(ARFLAGS)
186libpattern2_a_LIBADD =
187am_libpattern2_a_OBJECTS = ./libpattern2_a-pattern.$(OBJEXT)
188libpattern2_a_OBJECTS = $(am_libpattern2_a_OBJECTS)
189libpattern4_a_AR = $(AR) $(ARFLAGS)
190libpattern4_a_LIBADD =
191am_libpattern4_a_OBJECTS = ./libpattern4_a-pattern.$(OBJEXT)
192libpattern4_a_OBJECTS = $(am_libpattern4_a_OBJECTS)
193libpearl2_a_AR = $(AR) $(ARFLAGS)
194libpearl2_a_LIBADD =
195am_libpearl2_a_OBJECTS = ./libpearl2_a-pearl.$(OBJEXT)
196libpearl2_a_OBJECTS = $(am_libpearl2_a_OBJECTS)
197libsignpos2_a_AR = $(AR) $(ARFLAGS)
198libsignpos2_a_LIBADD =
199am_libsignpos2_a_OBJECTS = ./libsignpos2_a-signpost.$(OBJEXT)
200libsignpos2_a_OBJECTS = $(am_libsignpos2_a_OBJECTS)
201libsingles3_a_AR = $(AR) $(ARFLAGS)
202libsingles3_a_LIBADD =
203am_libsingles3_a_OBJECTS = ./libsingles3_a-singles.$(OBJEXT)
204libsingles3_a_OBJECTS = $(am_libsingles3_a_OBJECTS)
205libslant2_a_AR = $(AR) $(ARFLAGS)
206libslant2_a_LIBADD =
207am_libslant2_a_OBJECTS = ./libslant2_a-slant.$(OBJEXT)
208libslant2_a_OBJECTS = $(am_libslant2_a_OBJECTS)
209libsolo2_a_AR = $(AR) $(ARFLAGS)
210libsolo2_a_LIBADD =
211am_libsolo2_a_OBJECTS = ./libsolo2_a-solo.$(OBJEXT)
212libsolo2_a_OBJECTS = $(am_libsolo2_a_OBJECTS)
213libtents3_a_AR = $(AR) $(ARFLAGS)
214libtents3_a_LIBADD =
215am_libtents3_a_OBJECTS = ./libtents3_a-tents.$(OBJEXT)
216libtents3_a_OBJECTS = $(am_libtents3_a_OBJECTS)
217libtowers2_a_AR = $(AR) $(ARFLAGS)
218libtowers2_a_LIBADD =
219am_libtowers2_a_OBJECTS = ./libtowers2_a-towers.$(OBJEXT)
220libtowers2_a_OBJECTS = $(am_libtowers2_a_OBJECTS)
221libunequal2_a_AR = $(AR) $(ARFLAGS)
222libunequal2_a_LIBADD =
223am_libunequal2_a_OBJECTS = ./libunequal2_a-unequal.$(OBJEXT)
224libunequal2_a_OBJECTS = $(am_libunequal2_a_OBJECTS)
225libunruly2_a_AR = $(AR) $(ARFLAGS)
226libunruly2_a_LIBADD =
227am_libunruly2_a_OBJECTS = ./libunruly2_a-unruly.$(OBJEXT)
228libunruly2_a_OBJECTS = $(am_libunruly2_a_OBJECTS)
229am__EXEEXT_1 = blackbox$(EXEEXT) bridges$(EXEEXT) cube$(EXEEXT) \
230 dominosa$(EXEEXT) fifteen$(EXEEXT) filling$(EXEEXT) \
231 flip$(EXEEXT) flood$(EXEEXT) galaxies$(EXEEXT) guess$(EXEEXT) \
232 inertia$(EXEEXT) keen$(EXEEXT) lightup$(EXEEXT) loopy$(EXEEXT) \
233 magnets$(EXEEXT) map$(EXEEXT) mines$(EXEEXT) net$(EXEEXT) \
234 netslide$(EXEEXT) palisade$(EXEEXT) pattern$(EXEEXT) \
235 pearl$(EXEEXT) pegs$(EXEEXT) range$(EXEEXT) rect$(EXEEXT) \
236 samegame$(EXEEXT) signpost$(EXEEXT) singles$(EXEEXT) \
237 sixteen$(EXEEXT) slant$(EXEEXT) solo$(EXEEXT) tents$(EXEEXT) \
238 towers$(EXEEXT) tracks$(EXEEXT) twiddle$(EXEEXT) \
239 undead$(EXEEXT) unequal$(EXEEXT) unruly$(EXEEXT) \
240 untangle$(EXEEXT)
241am__installdirs = "$(DESTDIR)$(bindir)"
242PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
243am_blackbox_OBJECTS = ./blackbox.$(OBJEXT) ./drawing.$(OBJEXT) \
244 ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) ./midend.$(OBJEXT) \
245 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
246 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
247blackbox_OBJECTS = $(am_blackbox_OBJECTS)
248am__DEPENDENCIES_1 =
249blackbox_DEPENDENCIES = $(am__DEPENDENCIES_1)
250am_bridges_OBJECTS = ./bridges.$(OBJEXT) ./drawing.$(OBJEXT) \
251 ./dsf.$(OBJEXT) ./findloop.$(OBJEXT) ./gtk.$(OBJEXT) \
252 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
253 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
254 ./random.$(OBJEXT) ./version.$(OBJEXT)
255bridges_OBJECTS = $(am_bridges_OBJECTS)
256bridges_DEPENDENCIES = $(am__DEPENDENCIES_1)
257am_cube_OBJECTS = ./cube.$(OBJEXT) ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
258 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
259 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
260 ./random.$(OBJEXT) ./version.$(OBJEXT)
261cube_OBJECTS = $(am_cube_OBJECTS)
262cube_DEPENDENCIES = $(am__DEPENDENCIES_1)
263am_dominosa_OBJECTS = ./dominosa.$(OBJEXT) ./drawing.$(OBJEXT) \
264 ./gtk.$(OBJEXT) ./laydomino.$(OBJEXT) ./malloc.$(OBJEXT) \
265 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
266 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
267 ./version.$(OBJEXT)
268dominosa_OBJECTS = $(am_dominosa_OBJECTS)
269dominosa_DEPENDENCIES = $(am__DEPENDENCIES_1)
270am_fifteen_OBJECTS = ./drawing.$(OBJEXT) ./fifteen.$(OBJEXT) \
271 ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) ./midend.$(OBJEXT) \
272 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
273 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
274fifteen_OBJECTS = $(am_fifteen_OBJECTS)
275fifteen_DEPENDENCIES = $(am__DEPENDENCIES_1)
276am_fifteensolver_OBJECTS = ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
277 ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
278fifteensolver_OBJECTS = $(am_fifteensolver_OBJECTS)
279fifteensolver_DEPENDENCIES = libfifteen2_a-fifteen.$(OBJEXT)
280am_filling_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
281 ./filling.$(OBJEXT) ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) \
282 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
283 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
284 ./version.$(OBJEXT)
285filling_OBJECTS = $(am_filling_OBJECTS)
286filling_DEPENDENCIES = $(am__DEPENDENCIES_1)
287am_fillingsolver_OBJECTS = ./dsf.$(OBJEXT) ./malloc.$(OBJEXT) \
288 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
289fillingsolver_OBJECTS = $(am_fillingsolver_OBJECTS)
290fillingsolver_DEPENDENCIES = libfilling2_a-filling.$(OBJEXT)
291am_flip_OBJECTS = ./drawing.$(OBJEXT) ./flip.$(OBJEXT) ./gtk.$(OBJEXT) \
292 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
293 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
294 ./random.$(OBJEXT) ./tree234.$(OBJEXT) ./version.$(OBJEXT)
295flip_OBJECTS = $(am_flip_OBJECTS)
296flip_DEPENDENCIES = $(am__DEPENDENCIES_1)
297am_flood_OBJECTS = ./drawing.$(OBJEXT) ./flood.$(OBJEXT) \
298 ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) ./midend.$(OBJEXT) \
299 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
300 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
301flood_OBJECTS = $(am_flood_OBJECTS)
302flood_DEPENDENCIES = $(am__DEPENDENCIES_1)
303am_galaxies_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
304 ./galaxies.$(OBJEXT) ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) \
305 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
306 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
307 ./version.$(OBJEXT)
308galaxies_OBJECTS = $(am_galaxies_OBJECTS)
309galaxies_DEPENDENCIES = $(am__DEPENDENCIES_1)
310am_galaxiespicture_OBJECTS = ./dsf.$(OBJEXT) ./malloc.$(OBJEXT) \
311 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
312galaxiespicture_OBJECTS = $(am_galaxiespicture_OBJECTS)
313galaxiespicture_DEPENDENCIES = libgalaxie4_a-galaxies.$(OBJEXT)
314am_galaxiessolver_OBJECTS = ./dsf.$(OBJEXT) ./malloc.$(OBJEXT) \
315 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
316galaxiessolver_OBJECTS = $(am_galaxiessolver_OBJECTS)
317galaxiessolver_DEPENDENCIES = libgalaxie2_a-galaxies.$(OBJEXT)
318am_guess_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
319 ./guess.$(OBJEXT) ./malloc.$(OBJEXT) ./midend.$(OBJEXT) \
320 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
321 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
322guess_OBJECTS = $(am_guess_OBJECTS)
323guess_DEPENDENCIES = $(am__DEPENDENCIES_1)
324am_inertia_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
325 ./inertia.$(OBJEXT) ./malloc.$(OBJEXT) ./midend.$(OBJEXT) \
326 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
327 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
328inertia_OBJECTS = $(am_inertia_OBJECTS)
329inertia_DEPENDENCIES = $(am__DEPENDENCIES_1)
330am_keen_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) ./gtk.$(OBJEXT) \
331 ./keen.$(OBJEXT) ./latin.$(OBJEXT) ./malloc.$(OBJEXT) \
332 ./maxflow.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
333 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
334 ./random.$(OBJEXT) ./tree234.$(OBJEXT) ./version.$(OBJEXT)
335keen_OBJECTS = $(am_keen_OBJECTS)
336keen_DEPENDENCIES = $(am__DEPENDENCIES_1)
337am_keensolver_OBJECTS = ./dsf.$(OBJEXT) ./malloc.$(OBJEXT) \
338 ./maxflow.$(OBJEXT) ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) \
339 ./random.$(OBJEXT) ./tree234.$(OBJEXT)
340keensolver_OBJECTS = $(am_keensolver_OBJECTS)
341keensolver_DEPENDENCIES = libkeen2_a-keen.$(OBJEXT) \
342 liblatin6_a-latin.$(OBJEXT)
343am_latincheck_OBJECTS = ./malloc.$(OBJEXT) ./maxflow.$(OBJEXT) \
344 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT) \
345 ./tree234.$(OBJEXT)
346latincheck_OBJECTS = $(am_latincheck_OBJECTS)
347latincheck_DEPENDENCIES = liblatin8_a-latin.$(OBJEXT)
348am_lightup_OBJECTS = ./combi.$(OBJEXT) ./drawing.$(OBJEXT) \
349 ./gtk.$(OBJEXT) ./lightup.$(OBJEXT) ./malloc.$(OBJEXT) \
350 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
351 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
352 ./version.$(OBJEXT)
353lightup_OBJECTS = $(am_lightup_OBJECTS)
354lightup_DEPENDENCIES = $(am__DEPENDENCIES_1)
355am_lightupsolver_OBJECTS = ./combi.$(OBJEXT) ./malloc.$(OBJEXT) \
356 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
357lightupsolver_OBJECTS = $(am_lightupsolver_OBJECTS)
358lightupsolver_DEPENDENCIES = liblightup2_a-lightup.$(OBJEXT)
359am_loopy_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
360 ./grid.$(OBJEXT) ./gtk.$(OBJEXT) ./loopgen.$(OBJEXT) \
361 ./loopy.$(OBJEXT) ./malloc.$(OBJEXT) ./midend.$(OBJEXT) \
362 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./penrose.$(OBJEXT) \
363 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
364 ./tree234.$(OBJEXT) ./version.$(OBJEXT)
365loopy_OBJECTS = $(am_loopy_OBJECTS)
366loopy_DEPENDENCIES = $(am__DEPENDENCIES_1)
367am_loopysolver_OBJECTS = ./dsf.$(OBJEXT) ./grid.$(OBJEXT) \
368 ./loopgen.$(OBJEXT) ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
369 ./nullfe.$(OBJEXT) ./penrose.$(OBJEXT) ./random.$(OBJEXT) \
370 ./tree234.$(OBJEXT)
371loopysolver_OBJECTS = $(am_loopysolver_OBJECTS)
372loopysolver_DEPENDENCIES = libloopy2_a-loopy.$(OBJEXT)
373am_magnets_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
374 ./laydomino.$(OBJEXT) ./magnets.$(OBJEXT) ./malloc.$(OBJEXT) \
375 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
376 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
377 ./version.$(OBJEXT)
378magnets_OBJECTS = $(am_magnets_OBJECTS)
379magnets_DEPENDENCIES = $(am__DEPENDENCIES_1)
380am_magnetssolver_OBJECTS = ./laydomino.$(OBJEXT) ./malloc.$(OBJEXT) \
381 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
382magnetssolver_OBJECTS = $(am_magnetssolver_OBJECTS)
383magnetssolver_DEPENDENCIES = libmagnets2_a-magnets.$(OBJEXT)
384am_map_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) ./gtk.$(OBJEXT) \
385 ./malloc.$(OBJEXT) ./map.$(OBJEXT) ./midend.$(OBJEXT) \
386 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
387 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
388map_OBJECTS = $(am_map_OBJECTS)
389map_DEPENDENCIES = $(am__DEPENDENCIES_1)
390am_mapsolver_OBJECTS = ./dsf.$(OBJEXT) ./malloc.$(OBJEXT) \
391 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
392mapsolver_OBJECTS = $(am_mapsolver_OBJECTS)
393mapsolver_DEPENDENCIES = libmap2_a-map.$(OBJEXT)
394am_mineobfusc_OBJECTS = ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
395 ./nullfe.$(OBJEXT) ./random.$(OBJEXT) ./tree234.$(OBJEXT)
396mineobfusc_OBJECTS = $(am_mineobfusc_OBJECTS)
397mineobfusc_DEPENDENCIES = libmines2_a-mines.$(OBJEXT)
398am_mines_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
399 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./mines.$(OBJEXT) \
400 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
401 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./tree234.$(OBJEXT) \
402 ./version.$(OBJEXT)
403mines_OBJECTS = $(am_mines_OBJECTS)
404mines_DEPENDENCIES = $(am__DEPENDENCIES_1)
405am_net_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
406 ./findloop.$(OBJEXT) ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) \
407 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./net.$(OBJEXT) \
408 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
409 ./random.$(OBJEXT) ./tree234.$(OBJEXT) ./version.$(OBJEXT)
410net_OBJECTS = $(am_net_OBJECTS)
411net_DEPENDENCIES = $(am__DEPENDENCIES_1)
412am_netslide_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
413 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
414 ./netslide.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
415 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./tree234.$(OBJEXT) \
416 ./version.$(OBJEXT)
417netslide_OBJECTS = $(am_netslide_OBJECTS)
418netslide_DEPENDENCIES = $(am__DEPENDENCIES_1)
419am_nullgame_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
420 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
421 ./no-icon.$(OBJEXT) ./nullgame.$(OBJEXT) ./printing.$(OBJEXT) \
422 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
423nullgame_OBJECTS = $(am_nullgame_OBJECTS)
424nullgame_DEPENDENCIES = $(am__DEPENDENCIES_1)
425am_obfusc_OBJECTS = ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
426 ./nullfe.$(OBJEXT) ./obfusc.$(OBJEXT) ./random.$(OBJEXT)
427obfusc_OBJECTS = $(am_obfusc_OBJECTS)
428obfusc_DEPENDENCIES =
429am_palisade_OBJECTS = ./divvy.$(OBJEXT) ./drawing.$(OBJEXT) \
430 ./dsf.$(OBJEXT) ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) \
431 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
432 ./palisade.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
433 ./random.$(OBJEXT) ./version.$(OBJEXT)
434palisade_OBJECTS = $(am_palisade_OBJECTS)
435palisade_DEPENDENCIES = $(am__DEPENDENCIES_1)
436am_pattern_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
437 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
438 ./no-icon.$(OBJEXT) ./pattern.$(OBJEXT) ./printing.$(OBJEXT) \
439 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./version.$(OBJEXT)
440pattern_OBJECTS = $(am_pattern_OBJECTS)
441pattern_DEPENDENCIES = $(am__DEPENDENCIES_1)
442am_patternpicture_OBJECTS = ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
443 ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
444patternpicture_OBJECTS = $(am_patternpicture_OBJECTS)
445patternpicture_DEPENDENCIES = libpattern4_a-pattern.$(OBJEXT)
446am_patternsolver_OBJECTS = ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
447 ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
448patternsolver_OBJECTS = $(am_patternsolver_OBJECTS)
449patternsolver_DEPENDENCIES = libpattern2_a-pattern.$(OBJEXT)
450am_pearl_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
451 ./grid.$(OBJEXT) ./gtk.$(OBJEXT) ./loopgen.$(OBJEXT) \
452 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
453 ./no-icon.$(OBJEXT) ./pearl.$(OBJEXT) ./penrose.$(OBJEXT) \
454 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
455 ./tdq.$(OBJEXT) ./tree234.$(OBJEXT) ./version.$(OBJEXT)
456pearl_OBJECTS = $(am_pearl_OBJECTS)
457pearl_DEPENDENCIES = $(am__DEPENDENCIES_1)
458am_pearlbench_OBJECTS = ./dsf.$(OBJEXT) ./grid.$(OBJEXT) \
459 ./loopgen.$(OBJEXT) ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
460 ./nullfe.$(OBJEXT) ./penrose.$(OBJEXT) ./random.$(OBJEXT) \
461 ./tdq.$(OBJEXT) ./tree234.$(OBJEXT)
462pearlbench_OBJECTS = $(am_pearlbench_OBJECTS)
463pearlbench_DEPENDENCIES = libpearl2_a-pearl.$(OBJEXT)
464am_pegs_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
465 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
466 ./no-icon.$(OBJEXT) ./pegs.$(OBJEXT) ./printing.$(OBJEXT) \
467 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./tree234.$(OBJEXT) \
468 ./version.$(OBJEXT)
469pegs_OBJECTS = $(am_pegs_OBJECTS)
470pegs_DEPENDENCIES = $(am__DEPENDENCIES_1)
471am_range_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) ./gtk.$(OBJEXT) \
472 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
473 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
474 ./random.$(OBJEXT) ./range.$(OBJEXT) ./version.$(OBJEXT)
475range_OBJECTS = $(am_range_OBJECTS)
476range_DEPENDENCIES = $(am__DEPENDENCIES_1)
477am_rect_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
478 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
479 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
480 ./random.$(OBJEXT) ./rect.$(OBJEXT) ./version.$(OBJEXT)
481rect_OBJECTS = $(am_rect_OBJECTS)
482rect_DEPENDENCIES = $(am__DEPENDENCIES_1)
483am_samegame_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
484 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
485 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
486 ./random.$(OBJEXT) ./samegame.$(OBJEXT) ./version.$(OBJEXT)
487samegame_OBJECTS = $(am_samegame_OBJECTS)
488samegame_DEPENDENCIES = $(am__DEPENDENCIES_1)
489am_signpost_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
490 ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) ./midend.$(OBJEXT) \
491 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
492 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./signpost.$(OBJEXT) \
493 ./version.$(OBJEXT)
494signpost_OBJECTS = $(am_signpost_OBJECTS)
495signpost_DEPENDENCIES = $(am__DEPENDENCIES_1)
496am_signpostsolver_OBJECTS = ./dsf.$(OBJEXT) ./malloc.$(OBJEXT) \
497 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
498signpostsolver_OBJECTS = $(am_signpostsolver_OBJECTS)
499signpostsolver_DEPENDENCIES = libsignpos2_a-signpost.$(OBJEXT)
500am_singles_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
501 ./gtk.$(OBJEXT) ./latin.$(OBJEXT) ./malloc.$(OBJEXT) \
502 ./maxflow.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
503 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
504 ./random.$(OBJEXT) ./singles.$(OBJEXT) ./tree234.$(OBJEXT) \
505 ./version.$(OBJEXT)
506singles_OBJECTS = $(am_singles_OBJECTS)
507singles_DEPENDENCIES = $(am__DEPENDENCIES_1)
508am_singlessolver_OBJECTS = ./dsf.$(OBJEXT) ./latin.$(OBJEXT) \
509 ./malloc.$(OBJEXT) ./maxflow.$(OBJEXT) ./misc.$(OBJEXT) \
510 ./nullfe.$(OBJEXT) ./random.$(OBJEXT) ./tree234.$(OBJEXT)
511singlessolver_OBJECTS = $(am_singlessolver_OBJECTS)
512singlessolver_DEPENDENCIES = libsingles3_a-singles.$(OBJEXT)
513am_sixteen_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
514 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
515 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
516 ./random.$(OBJEXT) ./sixteen.$(OBJEXT) ./version.$(OBJEXT)
517sixteen_OBJECTS = $(am_sixteen_OBJECTS)
518sixteen_DEPENDENCIES = $(am__DEPENDENCIES_1)
519am_slant_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
520 ./findloop.$(OBJEXT) ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) \
521 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
522 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
523 ./slant.$(OBJEXT) ./version.$(OBJEXT)
524slant_OBJECTS = $(am_slant_OBJECTS)
525slant_DEPENDENCIES = $(am__DEPENDENCIES_1)
526am_slantsolver_OBJECTS = ./dsf.$(OBJEXT) ./findloop.$(OBJEXT) \
527 ./malloc.$(OBJEXT) ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) \
528 ./random.$(OBJEXT)
529slantsolver_OBJECTS = $(am_slantsolver_OBJECTS)
530slantsolver_DEPENDENCIES = libslant2_a-slant.$(OBJEXT)
531am_solo_OBJECTS = ./divvy.$(OBJEXT) ./drawing.$(OBJEXT) \
532 ./dsf.$(OBJEXT) ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) \
533 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
534 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
535 ./solo.$(OBJEXT) ./version.$(OBJEXT)
536solo_OBJECTS = $(am_solo_OBJECTS)
537solo_DEPENDENCIES = $(am__DEPENDENCIES_1)
538am_solosolver_OBJECTS = ./divvy.$(OBJEXT) ./dsf.$(OBJEXT) \
539 ./malloc.$(OBJEXT) ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) \
540 ./random.$(OBJEXT)
541solosolver_OBJECTS = $(am_solosolver_OBJECTS)
542solosolver_DEPENDENCIES = libsolo2_a-solo.$(OBJEXT)
543am_tents_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) ./gtk.$(OBJEXT) \
544 ./malloc.$(OBJEXT) ./maxflow.$(OBJEXT) ./midend.$(OBJEXT) \
545 ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) \
546 ./ps.$(OBJEXT) ./random.$(OBJEXT) ./tents.$(OBJEXT) \
547 ./version.$(OBJEXT)
548tents_OBJECTS = $(am_tents_OBJECTS)
549tents_DEPENDENCIES = $(am__DEPENDENCIES_1)
550am_tentssolver_OBJECTS = ./dsf.$(OBJEXT) ./malloc.$(OBJEXT) \
551 ./maxflow.$(OBJEXT) ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) \
552 ./random.$(OBJEXT)
553tentssolver_OBJECTS = $(am_tentssolver_OBJECTS)
554tentssolver_DEPENDENCIES = libtents3_a-tents.$(OBJEXT)
555am_towers_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
556 ./latin.$(OBJEXT) ./malloc.$(OBJEXT) ./maxflow.$(OBJEXT) \
557 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
558 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
559 ./towers.$(OBJEXT) ./tree234.$(OBJEXT) ./version.$(OBJEXT)
560towers_OBJECTS = $(am_towers_OBJECTS)
561towers_DEPENDENCIES = $(am__DEPENDENCIES_1)
562am_towerssolver_OBJECTS = ./malloc.$(OBJEXT) ./maxflow.$(OBJEXT) \
563 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT) \
564 ./tree234.$(OBJEXT)
565towerssolver_OBJECTS = $(am_towerssolver_OBJECTS)
566towerssolver_DEPENDENCIES = liblatin6_a-latin.$(OBJEXT) \
567 libtowers2_a-towers.$(OBJEXT)
568am_tracks_OBJECTS = ./drawing.$(OBJEXT) ./dsf.$(OBJEXT) \
569 ./findloop.$(OBJEXT) ./gtk.$(OBJEXT) ./malloc.$(OBJEXT) \
570 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
571 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
572 ./tracks.$(OBJEXT) ./version.$(OBJEXT)
573tracks_OBJECTS = $(am_tracks_OBJECTS)
574tracks_DEPENDENCIES = $(am__DEPENDENCIES_1)
575am_twiddle_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
576 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
577 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
578 ./random.$(OBJEXT) ./twiddle.$(OBJEXT) ./version.$(OBJEXT)
579twiddle_OBJECTS = $(am_twiddle_OBJECTS)
580twiddle_DEPENDENCIES = $(am__DEPENDENCIES_1)
581am_undead_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
582 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
583 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
584 ./random.$(OBJEXT) ./undead.$(OBJEXT) ./version.$(OBJEXT)
585undead_OBJECTS = $(am_undead_OBJECTS)
586undead_DEPENDENCIES = $(am__DEPENDENCIES_1)
587am_unequal_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
588 ./latin.$(OBJEXT) ./malloc.$(OBJEXT) ./maxflow.$(OBJEXT) \
589 ./midend.$(OBJEXT) ./misc.$(OBJEXT) ./no-icon.$(OBJEXT) \
590 ./printing.$(OBJEXT) ./ps.$(OBJEXT) ./random.$(OBJEXT) \
591 ./tree234.$(OBJEXT) ./unequal.$(OBJEXT) ./version.$(OBJEXT)
592unequal_OBJECTS = $(am_unequal_OBJECTS)
593unequal_DEPENDENCIES = $(am__DEPENDENCIES_1)
594am_unequalsolver_OBJECTS = ./malloc.$(OBJEXT) ./maxflow.$(OBJEXT) \
595 ./misc.$(OBJEXT) ./nullfe.$(OBJEXT) ./random.$(OBJEXT) \
596 ./tree234.$(OBJEXT)
597unequalsolver_OBJECTS = $(am_unequalsolver_OBJECTS)
598unequalsolver_DEPENDENCIES = liblatin6_a-latin.$(OBJEXT) \
599 libunequal2_a-unequal.$(OBJEXT)
600am_unruly_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
601 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
602 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
603 ./random.$(OBJEXT) ./unruly.$(OBJEXT) ./version.$(OBJEXT)
604unruly_OBJECTS = $(am_unruly_OBJECTS)
605unruly_DEPENDENCIES = $(am__DEPENDENCIES_1)
606am_unrulysolver_OBJECTS = ./malloc.$(OBJEXT) ./misc.$(OBJEXT) \
607 ./nullfe.$(OBJEXT) ./random.$(OBJEXT)
608unrulysolver_OBJECTS = $(am_unrulysolver_OBJECTS)
609unrulysolver_DEPENDENCIES = libunruly2_a-unruly.$(OBJEXT)
610am_untangle_OBJECTS = ./drawing.$(OBJEXT) ./gtk.$(OBJEXT) \
611 ./malloc.$(OBJEXT) ./midend.$(OBJEXT) ./misc.$(OBJEXT) \
612 ./no-icon.$(OBJEXT) ./printing.$(OBJEXT) ./ps.$(OBJEXT) \
613 ./random.$(OBJEXT) ./tree234.$(OBJEXT) ./untangle.$(OBJEXT) \
614 ./version.$(OBJEXT)
615untangle_OBJECTS = $(am_untangle_OBJECTS)
616untangle_DEPENDENCIES = $(am__DEPENDENCIES_1)
617AM_V_P = $(am__v_P_@AM_V@)
618am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
619am__v_P_0 = false
620am__v_P_1 = :
621AM_V_GEN = $(am__v_GEN_@AM_V@)
622am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
623am__v_GEN_0 = @echo " GEN " $@;
624am__v_GEN_1 =
625AM_V_at = $(am__v_at_@AM_V@)
626am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
627am__v_at_0 = @
628am__v_at_1 =
629DEFAULT_INCLUDES = -I.@am__isrc@
630depcomp = $(SHELL) $(top_srcdir)/depcomp
631am__depfiles_maybe = depfiles
632am__mv = mv -f
633AM_V_lt = $(am__v_lt_@AM_V@)
634am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
635am__v_lt_0 = --silent
636am__v_lt_1 =
637COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
638 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
639AM_V_CC = $(am__v_CC_@AM_V@)
640am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
641am__v_CC_0 = @echo " CC " $@;
642am__v_CC_1 =
643CCLD = $(CC)
644LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
645AM_V_CCLD = $(am__v_CCLD_@AM_V@)
646am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
647am__v_CCLD_0 = @echo " CCLD " $@;
648am__v_CCLD_1 =
649SOURCES = $(libfifteen2_a_SOURCES) $(libfilling2_a_SOURCES) \
650 $(libgalaxie2_a_SOURCES) $(libgalaxie4_a_SOURCES) \
651 $(libkeen2_a_SOURCES) $(liblatin6_a_SOURCES) \
652 $(liblatin8_a_SOURCES) $(liblightup2_a_SOURCES) \
653 $(libloopy2_a_SOURCES) $(libmagnets2_a_SOURCES) \
654 $(libmap2_a_SOURCES) $(libmines2_a_SOURCES) \
655 $(libpattern2_a_SOURCES) $(libpattern4_a_SOURCES) \
656 $(libpearl2_a_SOURCES) $(libsignpos2_a_SOURCES) \
657 $(libsingles3_a_SOURCES) $(libslant2_a_SOURCES) \
658 $(libsolo2_a_SOURCES) $(libtents3_a_SOURCES) \
659 $(libtowers2_a_SOURCES) $(libunequal2_a_SOURCES) \
660 $(libunruly2_a_SOURCES) $(blackbox_SOURCES) $(bridges_SOURCES) \
661 $(cube_SOURCES) $(dominosa_SOURCES) $(fifteen_SOURCES) \
662 $(fifteensolver_SOURCES) $(filling_SOURCES) \
663 $(fillingsolver_SOURCES) $(flip_SOURCES) $(flood_SOURCES) \
664 $(galaxies_SOURCES) $(galaxiespicture_SOURCES) \
665 $(galaxiessolver_SOURCES) $(guess_SOURCES) $(inertia_SOURCES) \
666 $(keen_SOURCES) $(keensolver_SOURCES) $(latincheck_SOURCES) \
667 $(lightup_SOURCES) $(lightupsolver_SOURCES) $(loopy_SOURCES) \
668 $(loopysolver_SOURCES) $(magnets_SOURCES) \
669 $(magnetssolver_SOURCES) $(map_SOURCES) $(mapsolver_SOURCES) \
670 $(mineobfusc_SOURCES) $(mines_SOURCES) $(net_SOURCES) \
671 $(netslide_SOURCES) $(nullgame_SOURCES) $(obfusc_SOURCES) \
672 $(palisade_SOURCES) $(pattern_SOURCES) \
673 $(patternpicture_SOURCES) $(patternsolver_SOURCES) \
674 $(pearl_SOURCES) $(pearlbench_SOURCES) $(pegs_SOURCES) \
675 $(range_SOURCES) $(rect_SOURCES) $(samegame_SOURCES) \
676 $(signpost_SOURCES) $(signpostsolver_SOURCES) \
677 $(singles_SOURCES) $(singlessolver_SOURCES) $(sixteen_SOURCES) \
678 $(slant_SOURCES) $(slantsolver_SOURCES) $(solo_SOURCES) \
679 $(solosolver_SOURCES) $(tents_SOURCES) $(tentssolver_SOURCES) \
680 $(towers_SOURCES) $(towerssolver_SOURCES) $(tracks_SOURCES) \
681 $(twiddle_SOURCES) $(undead_SOURCES) $(unequal_SOURCES) \
682 $(unequalsolver_SOURCES) $(unruly_SOURCES) \
683 $(unrulysolver_SOURCES) $(untangle_SOURCES)
684DIST_SOURCES = $(libfifteen2_a_SOURCES) $(libfilling2_a_SOURCES) \
685 $(libgalaxie2_a_SOURCES) $(libgalaxie4_a_SOURCES) \
686 $(libkeen2_a_SOURCES) $(liblatin6_a_SOURCES) \
687 $(liblatin8_a_SOURCES) $(liblightup2_a_SOURCES) \
688 $(libloopy2_a_SOURCES) $(libmagnets2_a_SOURCES) \
689 $(libmap2_a_SOURCES) $(libmines2_a_SOURCES) \
690 $(libpattern2_a_SOURCES) $(libpattern4_a_SOURCES) \
691 $(libpearl2_a_SOURCES) $(libsignpos2_a_SOURCES) \
692 $(libsingles3_a_SOURCES) $(libslant2_a_SOURCES) \
693 $(libsolo2_a_SOURCES) $(libtents3_a_SOURCES) \
694 $(libtowers2_a_SOURCES) $(libunequal2_a_SOURCES) \
695 $(libunruly2_a_SOURCES) $(blackbox_SOURCES) $(bridges_SOURCES) \
696 $(cube_SOURCES) $(dominosa_SOURCES) $(fifteen_SOURCES) \
697 $(fifteensolver_SOURCES) $(filling_SOURCES) \
698 $(fillingsolver_SOURCES) $(flip_SOURCES) $(flood_SOURCES) \
699 $(galaxies_SOURCES) $(galaxiespicture_SOURCES) \
700 $(galaxiessolver_SOURCES) $(guess_SOURCES) $(inertia_SOURCES) \
701 $(keen_SOURCES) $(keensolver_SOURCES) $(latincheck_SOURCES) \
702 $(lightup_SOURCES) $(lightupsolver_SOURCES) $(loopy_SOURCES) \
703 $(loopysolver_SOURCES) $(magnets_SOURCES) \
704 $(magnetssolver_SOURCES) $(map_SOURCES) $(mapsolver_SOURCES) \
705 $(mineobfusc_SOURCES) $(mines_SOURCES) $(net_SOURCES) \
706 $(netslide_SOURCES) $(nullgame_SOURCES) $(obfusc_SOURCES) \
707 $(palisade_SOURCES) $(pattern_SOURCES) \
708 $(patternpicture_SOURCES) $(patternsolver_SOURCES) \
709 $(pearl_SOURCES) $(pearlbench_SOURCES) $(pegs_SOURCES) \
710 $(range_SOURCES) $(rect_SOURCES) $(samegame_SOURCES) \
711 $(signpost_SOURCES) $(signpostsolver_SOURCES) \
712 $(singles_SOURCES) $(singlessolver_SOURCES) $(sixteen_SOURCES) \
713 $(slant_SOURCES) $(slantsolver_SOURCES) $(solo_SOURCES) \
714 $(solosolver_SOURCES) $(tents_SOURCES) $(tentssolver_SOURCES) \
715 $(towers_SOURCES) $(towerssolver_SOURCES) $(tracks_SOURCES) \
716 $(twiddle_SOURCES) $(undead_SOURCES) $(unequal_SOURCES) \
717 $(unequalsolver_SOURCES) $(unruly_SOURCES) \
718 $(unrulysolver_SOURCES) $(untangle_SOURCES)
719am__can_run_installinfo = \
720 case $$AM_UPDATE_INFO_DIR in \
721 n|no|NO) false;; \
722 *) (install-info --version) >/dev/null 2>&1;; \
723 esac
724am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
725# Read a list of newline-separated strings from the standard input,
726# and print each of them once, without duplicates. Input order is
727# *not* preserved.
728am__uniquify_input = $(AWK) '\
729 BEGIN { nonempty = 0; } \
730 { items[$$0] = 1; nonempty = 1; } \
731 END { if (nonempty) { for (i in items) print i; }; } \
732'
733# Make sure the list of sources is unique. This is necessary because,
734# e.g., the same source file might be shared among _SOURCES variables
735# for different programs/libraries.
736am__define_uniq_tagged_files = \
737 list='$(am__tagged_files)'; \
738 unique=`for i in $$list; do \
739 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
740 done | $(am__uniquify_input)`
741ETAGS = etags
742CTAGS = ctags
743CSCOPE = cscope
744AM_RECURSIVE_TARGETS = cscope
745am__DIST_COMMON = $(srcdir)/Makefile.in README compile depcomp \
746 install-sh missing
747DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
748distdir = $(PACKAGE)-$(VERSION)
749top_distdir = $(distdir)
750am__remove_distdir = \
751 if test -d "$(distdir)"; then \
752 find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
753 && rm -rf "$(distdir)" \
754 || { sleep 5 && rm -rf "$(distdir)"; }; \
755 else :; fi
756am__post_remove_distdir = $(am__remove_distdir)
757DIST_ARCHIVES = $(distdir).tar.gz
758GZIP_ENV = --best
759DIST_TARGETS = dist-gzip
760distuninstallcheck_listfiles = find . -type f -print
761am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
762 | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
763distcleancheck_listfiles = find . -type f -print
764ACLOCAL = @ACLOCAL@
765AMTAR = @AMTAR@
766AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
767AUTOCONF = @AUTOCONF@
768AUTOHEADER = @AUTOHEADER@
769AUTOMAKE = @AUTOMAKE@
770AWK = @AWK@
771CC = @CC@
772CCDEPMODE = @CCDEPMODE@
773CFLAGS = @CFLAGS@
774CPPFLAGS = @CPPFLAGS@
775CYGPATH_W = @CYGPATH_W@
776DEFS = @DEFS@
777DEPDIR = @DEPDIR@
778ECHO_C = @ECHO_C@
779ECHO_N = @ECHO_N@
780ECHO_T = @ECHO_T@
781EXEEXT = @EXEEXT@
782GTK_CFLAGS = @GTK_CFLAGS@
783GTK_LIBS = @GTK_LIBS@
784INSTALL = @INSTALL@
785INSTALL_DATA = @INSTALL_DATA@
786INSTALL_PROGRAM = @INSTALL_PROGRAM@
787INSTALL_SCRIPT = @INSTALL_SCRIPT@
788INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
789LDFLAGS = @LDFLAGS@
790LIBOBJS = @LIBOBJS@
791LIBS = @LIBS@
792LTLIBOBJS = @LTLIBOBJS@
793MAKEINFO = @MAKEINFO@
794MKDIR_P = @MKDIR_P@
795OBJEXT = @OBJEXT@
796PACKAGE = @PACKAGE@
797PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
798PACKAGE_NAME = @PACKAGE_NAME@
799PACKAGE_STRING = @PACKAGE_STRING@
800PACKAGE_TARNAME = @PACKAGE_TARNAME@
801PACKAGE_URL = @PACKAGE_URL@
802PACKAGE_VERSION = @PACKAGE_VERSION@
803PATH_SEPARATOR = @PATH_SEPARATOR@
804PKG_CONFIG = @PKG_CONFIG@
805PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
806PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
807RANLIB = @RANLIB@
808SET_MAKE = @SET_MAKE@
809SHELL = @SHELL@
810STRIP = @STRIP@
811VERSION = @VERSION@
812abs_builddir = @abs_builddir@
813abs_srcdir = @abs_srcdir@
814abs_top_builddir = @abs_top_builddir@
815abs_top_srcdir = @abs_top_srcdir@
816ac_ct_CC = @ac_ct_CC@
817am__include = @am__include@
818am__leading_dot = @am__leading_dot@
819am__quote = @am__quote@
820am__tar = @am__tar@
821am__untar = @am__untar@
822bindir = @bindir@
823build_alias = @build_alias@
824builddir = @builddir@
825datadir = @datadir@
826datarootdir = @datarootdir@
827docdir = @docdir@
828dvidir = @dvidir@
829exec_prefix = @exec_prefix@
830host_alias = @host_alias@
831htmldir = @htmldir@
832includedir = @includedir@
833infodir = @infodir@
834install_sh = @install_sh@
835libdir = @libdir@
836libexecdir = @libexecdir@
837localedir = @localedir@
838localstatedir = @localstatedir@
839mandir = @mandir@
840mkdir_p = @mkdir_p@
841oldincludedir = @oldincludedir@
842pdfdir = @pdfdir@
843prefix = @prefix@
844program_transform_name = @program_transform_name@
845psdir = @psdir@
846sbindir = @sbindir@
847sharedstatedir = @sharedstatedir@
848srcdir = @srcdir@
849sysconfdir = @sysconfdir@
850target_alias = @target_alias@
851top_build_prefix = @top_build_prefix@
852top_builddir = @top_builddir@
853top_srcdir = @top_srcdir@
854GAMES = blackbox bridges cube dominosa fifteen filling flip flood \
855 galaxies guess inertia keen lightup loopy magnets map mines \
856 net netslide palisade pattern pearl pegs range rect samegame \
857 signpost singles sixteen slant solo tents towers tracks \
858 twiddle undead unequal unruly untangle
859AUTOMAKE_OPTIONS = subdir-objects
860allsources = ./blackbox.c ./bridges.c ./combi.c ./cube.c ./divvy.c \
861 ./dominosa.c ./drawing.c ./dsf.c ./fifteen.c ./filling.c \
862 ./findloop.c ./flip.c ./flood.c ./galaxies.c ./grid.c \
863 ./grid.h ./gtk.c ./guess.c ./inertia.c ./keen.c ./latin.c \
864 ./latin.h ./laydomino.c ./lightup.c ./list.c ./loopgen.c \
865 ./loopgen.h ./loopy.c ./magnets.c ./malloc.c ./map.c \
866 ./maxflow.c ./maxflow.h ./midend.c ./mines.c ./misc.c \
867 ./net.c ./netslide.c ./no-icon.c ./nullfe.c ./nullgame.c \
868 ./obfusc.c ./osx.m ./palisade.c ./pattern.c ./pearl.c \
869 ./pegs.c ./penrose.c ./penrose.h ./printing.c ./ps.c \
870 ./puzzles.h ./random.c ./range.c ./rect.c ./resource.h \
871 ./samegame.c ./signpost.c ./singles.c ./sixteen.c ./slant.c \
872 ./solo.c ./tdq.c ./tents.c ./towers.c ./tracks.c ./tree234.c \
873 ./tree234.h ./twiddle.c ./undead.c ./unequal.c ./unruly.c \
874 ./untangle.c ./version.c ./version.h ./windows.c
875
876AM_CPPFLAGS = -I$(srcdir)/./ -I$(srcdir)/icons/
877AM_CFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS)
878blackbox_SOURCES = ./blackbox.c ./drawing.c ./gtk.c ./malloc.c ./midend.c \
879 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
880 ./random.c ./version.c ./version.h
881
882blackbox_LDADD = $(GTK_LIBS) -lm
883bridges_SOURCES = ./bridges.c ./drawing.c ./dsf.c ./findloop.c ./gtk.c \
884 ./malloc.c ./midend.c ./misc.c ./no-icon.c ./printing.c \
885 ./ps.c ./puzzles.h ./random.c ./version.c ./version.h
886
887bridges_LDADD = $(GTK_LIBS) -lm
888cube_SOURCES = ./cube.c ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
889 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
890 ./version.c ./version.h
891
892cube_LDADD = $(GTK_LIBS) -lm
893dominosa_SOURCES = ./dominosa.c ./drawing.c ./gtk.c ./laydomino.c ./malloc.c \
894 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
895 ./puzzles.h ./random.c ./version.c ./version.h
896
897dominosa_LDADD = $(GTK_LIBS) -lm
898fifteen_SOURCES = ./drawing.c ./fifteen.c ./gtk.c ./malloc.c ./midend.c \
899 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
900 ./random.c ./version.c ./version.h
901
902fifteen_LDADD = $(GTK_LIBS) -lm
903fifteensolver_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
904 ./random.c
905
906fifteensolver_LDADD = libfifteen2_a-fifteen.$(OBJEXT) -lm
907filling_SOURCES = ./drawing.c ./dsf.c ./filling.c ./gtk.c ./malloc.c \
908 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
909 ./puzzles.h ./random.c ./version.c ./version.h
910
911filling_LDADD = $(GTK_LIBS) -lm
912fillingsolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
913 ./random.c
914
915fillingsolver_LDADD = libfilling2_a-filling.$(OBJEXT) -lm
916flip_SOURCES = ./drawing.c ./flip.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
917 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
918 ./tree234.c ./tree234.h ./version.c ./version.h
919
920flip_LDADD = $(GTK_LIBS) -lm
921flood_SOURCES = ./drawing.c ./flood.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
922 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
923 ./version.c ./version.h
924
925flood_LDADD = $(GTK_LIBS) -lm
926galaxies_SOURCES = ./drawing.c ./dsf.c ./galaxies.c ./gtk.c ./malloc.c \
927 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
928 ./puzzles.h ./random.c ./version.c ./version.h
929
930galaxies_LDADD = $(GTK_LIBS) -lm
931galaxiespicture_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
932 ./random.c
933
934galaxiespicture_LDADD = libgalaxie4_a-galaxies.$(OBJEXT) -lm
935galaxiessolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
936 ./random.c
937
938galaxiessolver_LDADD = libgalaxie2_a-galaxies.$(OBJEXT) -lm
939guess_SOURCES = ./drawing.c ./gtk.c ./guess.c ./malloc.c ./midend.c ./misc.c \
940 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
941 ./version.c ./version.h
942
943guess_LDADD = $(GTK_LIBS) -lm
944inertia_SOURCES = ./drawing.c ./gtk.c ./inertia.c ./malloc.c ./midend.c \
945 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
946 ./random.c ./version.c ./version.h
947
948inertia_LDADD = $(GTK_LIBS) -lm
949keen_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./keen.c ./latin.c ./latin.h \
950 ./malloc.c ./maxflow.c ./maxflow.h ./midend.c ./misc.c \
951 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
952 ./tree234.c ./tree234.h ./version.c ./version.h
953
954keen_LDADD = $(GTK_LIBS) -lm
955keensolver_SOURCES = ./dsf.c ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
956 ./nullfe.c ./puzzles.h ./random.c ./tree234.c ./tree234.h
957
958keensolver_LDADD = libkeen2_a-keen.$(OBJEXT) liblatin6_a-latin.$(OBJEXT) -lm
959latincheck_SOURCES = ./malloc.c ./maxflow.c ./maxflow.h ./misc.c ./nullfe.c \
960 ./puzzles.h ./random.c ./tree234.c ./tree234.h
961
962latincheck_LDADD = liblatin8_a-latin.$(OBJEXT) -lm
963lightup_SOURCES = ./combi.c ./drawing.c ./gtk.c ./lightup.c ./malloc.c \
964 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
965 ./puzzles.h ./random.c ./version.c ./version.h
966
967lightup_LDADD = $(GTK_LIBS) -lm
968lightupsolver_SOURCES = ./combi.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
969 ./random.c
970
971lightupsolver_LDADD = liblightup2_a-lightup.$(OBJEXT) -lm
972loopy_SOURCES = ./drawing.c ./dsf.c ./grid.c ./grid.h ./gtk.c ./loopgen.c \
973 ./loopgen.h ./loopy.c ./malloc.c ./midend.c ./misc.c \
974 ./no-icon.c ./penrose.c ./penrose.h ./printing.c ./ps.c \
975 ./puzzles.h ./random.c ./tree234.c ./tree234.h ./version.c \
976 ./version.h
977
978loopy_LDADD = $(GTK_LIBS) -lm
979loopysolver_SOURCES = ./dsf.c ./grid.c ./grid.h ./loopgen.c ./loopgen.h \
980 ./malloc.c ./misc.c ./nullfe.c ./penrose.c ./penrose.h \
981 ./puzzles.h ./random.c ./tree234.c ./tree234.h
982
983loopysolver_LDADD = libloopy2_a-loopy.$(OBJEXT) -lm
984magnets_SOURCES = ./drawing.c ./gtk.c ./laydomino.c ./magnets.c ./malloc.c \
985 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
986 ./puzzles.h ./random.c ./version.c ./version.h
987
988magnets_LDADD = $(GTK_LIBS) -lm
989magnetssolver_SOURCES = ./laydomino.c ./malloc.c ./misc.c ./nullfe.c \
990 ./puzzles.h ./random.c
991
992magnetssolver_LDADD = libmagnets2_a-magnets.$(OBJEXT) -lm
993map_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./map.c ./midend.c \
994 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
995 ./random.c ./version.c ./version.h
996
997map_LDADD = $(GTK_LIBS) -lm
998mapsolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
999 ./random.c
1000
1001mapsolver_LDADD = libmap2_a-map.$(OBJEXT) -lm
1002mineobfusc_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h ./random.c \
1003 ./tree234.c ./tree234.h
1004
1005mineobfusc_LDADD = libmines2_a-mines.$(OBJEXT) -lm
1006mines_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./mines.c ./misc.c \
1007 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1008 ./tree234.c ./tree234.h ./version.c ./version.h
1009
1010mines_LDADD = $(GTK_LIBS) -lm
1011net_SOURCES = ./drawing.c ./dsf.c ./findloop.c ./gtk.c ./malloc.c ./midend.c \
1012 ./misc.c ./net.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
1013 ./random.c ./tree234.c ./tree234.h ./version.c ./version.h
1014
1015net_LDADD = $(GTK_LIBS) -lm
1016netslide_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1017 ./netslide.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
1018 ./random.c ./tree234.c ./tree234.h ./version.c ./version.h
1019
1020netslide_LDADD = $(GTK_LIBS) -lm
1021nullgame_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1022 ./no-icon.c ./nullgame.c ./printing.c ./ps.c ./puzzles.h \
1023 ./random.c ./version.c ./version.h
1024
1025nullgame_LDADD = $(GTK_LIBS) -lm
1026obfusc_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./obfusc.c ./puzzles.h \
1027 ./random.c
1028
1029obfusc_LDADD = -lm
1030palisade_SOURCES = ./divvy.c ./drawing.c ./dsf.c ./gtk.c ./malloc.c \
1031 ./midend.c ./misc.c ./no-icon.c ./palisade.c ./printing.c \
1032 ./ps.c ./puzzles.h ./random.c ./version.c ./version.h
1033
1034palisade_LDADD = $(GTK_LIBS) -lm
1035pattern_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1036 ./no-icon.c ./pattern.c ./printing.c ./ps.c ./puzzles.h \
1037 ./random.c ./version.c ./version.h
1038
1039pattern_LDADD = $(GTK_LIBS) -lm
1040patternpicture_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
1041 ./random.c
1042
1043patternpicture_LDADD = libpattern4_a-pattern.$(OBJEXT) -lm
1044patternsolver_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
1045 ./random.c
1046
1047patternsolver_LDADD = libpattern2_a-pattern.$(OBJEXT) -lm
1048pearl_SOURCES = ./drawing.c ./dsf.c ./grid.c ./grid.h ./gtk.c ./loopgen.c \
1049 ./loopgen.h ./malloc.c ./midend.c ./misc.c ./no-icon.c \
1050 ./pearl.c ./penrose.c ./penrose.h ./printing.c ./ps.c \
1051 ./puzzles.h ./random.c ./tdq.c ./tree234.c ./tree234.h \
1052 ./version.c ./version.h
1053
1054pearl_LDADD = $(GTK_LIBS) -lm
1055pearlbench_SOURCES = ./dsf.c ./grid.c ./grid.h ./loopgen.c ./loopgen.h \
1056 ./malloc.c ./misc.c ./nullfe.c ./penrose.c ./penrose.h \
1057 ./puzzles.h ./random.c ./tdq.c ./tree234.c ./tree234.h
1058
1059pearlbench_LDADD = libpearl2_a-pearl.$(OBJEXT) -lm
1060pegs_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1061 ./no-icon.c ./pegs.c ./printing.c ./ps.c ./puzzles.h \
1062 ./random.c ./tree234.c ./tree234.h ./version.c ./version.h
1063
1064pegs_LDADD = $(GTK_LIBS) -lm
1065range_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1066 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1067 ./range.c ./version.c ./version.h
1068
1069range_LDADD = $(GTK_LIBS) -lm
1070rect_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1071 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1072 ./rect.c ./version.c ./version.h
1073
1074rect_LDADD = $(GTK_LIBS) -lm
1075samegame_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1076 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1077 ./samegame.c ./version.c ./version.h
1078
1079samegame_LDADD = $(GTK_LIBS) -lm
1080signpost_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./midend.c \
1081 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
1082 ./random.c ./signpost.c ./version.c ./version.h
1083
1084signpost_LDADD = $(GTK_LIBS) -lm
1085signpostsolver_SOURCES = ./dsf.c ./malloc.c ./misc.c ./nullfe.c ./puzzles.h \
1086 ./random.c
1087
1088signpostsolver_LDADD = libsignpos2_a-signpost.$(OBJEXT) -lm
1089singles_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./latin.c ./latin.h ./malloc.c \
1090 ./maxflow.c ./maxflow.h ./midend.c ./misc.c ./no-icon.c \
1091 ./printing.c ./ps.c ./puzzles.h ./random.c ./singles.c \
1092 ./tree234.c ./tree234.h ./version.c ./version.h
1093
1094singles_LDADD = $(GTK_LIBS) -lm
1095singlessolver_SOURCES = ./dsf.c ./latin.c ./latin.h ./malloc.c ./maxflow.c \
1096 ./maxflow.h ./misc.c ./nullfe.c ./puzzles.h ./random.c \
1097 ./tree234.c ./tree234.h
1098
1099singlessolver_LDADD = libsingles3_a-singles.$(OBJEXT) -lm
1100sixteen_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1101 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1102 ./sixteen.c ./version.c ./version.h
1103
1104sixteen_LDADD = $(GTK_LIBS) -lm
1105slant_SOURCES = ./drawing.c ./dsf.c ./findloop.c ./gtk.c ./malloc.c \
1106 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
1107 ./puzzles.h ./random.c ./slant.c ./version.c ./version.h
1108
1109slant_LDADD = $(GTK_LIBS) -lm
1110slantsolver_SOURCES = ./dsf.c ./findloop.c ./malloc.c ./misc.c ./nullfe.c \
1111 ./puzzles.h ./random.c
1112
1113slantsolver_LDADD = libslant2_a-slant.$(OBJEXT) -lm
1114solo_SOURCES = ./divvy.c ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./midend.c \
1115 ./misc.c ./no-icon.c ./printing.c ./ps.c ./puzzles.h \
1116 ./random.c ./solo.c ./version.c ./version.h
1117
1118solo_LDADD = $(GTK_LIBS) -lm
1119solosolver_SOURCES = ./divvy.c ./dsf.c ./malloc.c ./misc.c ./nullfe.c \
1120 ./puzzles.h ./random.c
1121
1122solosolver_LDADD = libsolo2_a-solo.$(OBJEXT) -lm
1123tents_SOURCES = ./drawing.c ./dsf.c ./gtk.c ./malloc.c ./maxflow.c \
1124 ./maxflow.h ./midend.c ./misc.c ./no-icon.c ./printing.c \
1125 ./ps.c ./puzzles.h ./random.c ./tents.c ./version.c \
1126 ./version.h
1127
1128tents_LDADD = $(GTK_LIBS) -lm
1129tentssolver_SOURCES = ./dsf.c ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
1130 ./nullfe.c ./puzzles.h ./random.c
1131
1132tentssolver_LDADD = libtents3_a-tents.$(OBJEXT) -lm
1133towers_SOURCES = ./drawing.c ./gtk.c ./latin.c ./latin.h ./malloc.c \
1134 ./maxflow.c ./maxflow.h ./midend.c ./misc.c ./no-icon.c \
1135 ./printing.c ./ps.c ./puzzles.h ./random.c ./towers.c \
1136 ./tree234.c ./tree234.h ./version.c ./version.h
1137
1138towers_LDADD = $(GTK_LIBS) -lm
1139towerssolver_SOURCES = ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
1140 ./nullfe.c ./puzzles.h ./random.c ./tree234.c ./tree234.h
1141
1142towerssolver_LDADD = liblatin6_a-latin.$(OBJEXT) \
1143 libtowers2_a-towers.$(OBJEXT) -lm
1144
1145tracks_SOURCES = ./drawing.c ./dsf.c ./findloop.c ./gtk.c ./malloc.c \
1146 ./midend.c ./misc.c ./no-icon.c ./printing.c ./ps.c \
1147 ./puzzles.h ./random.c ./tracks.c ./version.c ./version.h
1148
1149tracks_LDADD = $(GTK_LIBS) -lm
1150twiddle_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1151 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1152 ./twiddle.c ./version.c ./version.h
1153
1154twiddle_LDADD = $(GTK_LIBS) -lm
1155undead_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1156 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1157 ./undead.c ./version.c ./version.h
1158
1159undead_LDADD = $(GTK_LIBS) -lm
1160unequal_SOURCES = ./drawing.c ./gtk.c ./latin.c ./latin.h ./malloc.c \
1161 ./maxflow.c ./maxflow.h ./midend.c ./misc.c ./no-icon.c \
1162 ./printing.c ./ps.c ./puzzles.h ./random.c ./tree234.c \
1163 ./tree234.h ./unequal.c ./version.c ./version.h
1164
1165unequal_LDADD = $(GTK_LIBS) -lm
1166unequalsolver_SOURCES = ./malloc.c ./maxflow.c ./maxflow.h ./misc.c \
1167 ./nullfe.c ./puzzles.h ./random.c ./tree234.c ./tree234.h
1168
1169unequalsolver_LDADD = liblatin6_a-latin.$(OBJEXT) \
1170 libunequal2_a-unequal.$(OBJEXT) -lm
1171
1172unruly_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1173 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1174 ./unruly.c ./version.c ./version.h
1175
1176unruly_LDADD = $(GTK_LIBS) -lm
1177unrulysolver_SOURCES = ./malloc.c ./misc.c ./nullfe.c ./puzzles.h ./random.c
1178unrulysolver_LDADD = libunruly2_a-unruly.$(OBJEXT) -lm
1179untangle_SOURCES = ./drawing.c ./gtk.c ./malloc.c ./midend.c ./misc.c \
1180 ./no-icon.c ./printing.c ./ps.c ./puzzles.h ./random.c \
1181 ./tree234.c ./tree234.h ./untangle.c ./version.c ./version.h
1182
1183untangle_LDADD = $(GTK_LIBS) -lm
1184libfifteen2_a_SOURCES = ./fifteen.c ./puzzles.h
1185libfifteen2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1186libfilling2_a_SOURCES = ./filling.c ./puzzles.h
1187libfilling2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1188libgalaxie2_a_SOURCES = ./galaxies.c ./puzzles.h
1189libgalaxie2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1190libgalaxie4_a_SOURCES = ./galaxies.c ./puzzles.h
1191libgalaxie4_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) \
1192 -DSTANDALONE_PICTURE_GENERATOR
1193
1194libkeen2_a_SOURCES = ./keen.c ./puzzles.h ./latin.h
1195libkeen2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1196liblatin6_a_SOURCES = ./latin.c ./puzzles.h ./tree234.h ./maxflow.h \
1197 ./latin.h
1198
1199liblatin6_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1200liblatin8_a_SOURCES = ./latin.c ./puzzles.h ./tree234.h ./maxflow.h \
1201 ./latin.h
1202
1203liblatin8_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_LATIN_TEST
1204liblightup2_a_SOURCES = ./lightup.c ./puzzles.h
1205liblightup2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1206libloopy2_a_SOURCES = ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
1207libloopy2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1208libmagnets2_a_SOURCES = ./magnets.c ./puzzles.h
1209libmagnets2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1210libmap2_a_SOURCES = ./map.c ./puzzles.h
1211libmap2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1212libmines2_a_SOURCES = ./mines.c ./tree234.h ./puzzles.h
1213libmines2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_OBFUSCATOR
1214libpattern2_a_SOURCES = ./pattern.c ./puzzles.h
1215libpattern2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1216libpattern4_a_SOURCES = ./pattern.c ./puzzles.h
1217libpattern4_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) \
1218 -DSTANDALONE_PICTURE_GENERATOR
1219
1220libpearl2_a_SOURCES = ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
1221libpearl2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1222libsignpos2_a_SOURCES = ./signpost.c ./puzzles.h
1223libsignpos2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1224libsingles3_a_SOURCES = ./singles.c ./puzzles.h ./latin.h
1225libsingles3_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1226libslant2_a_SOURCES = ./slant.c ./puzzles.h
1227libslant2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1228libsolo2_a_SOURCES = ./solo.c ./puzzles.h
1229libsolo2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1230libtents3_a_SOURCES = ./tents.c ./puzzles.h ./maxflow.h
1231libtents3_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1232libtowers2_a_SOURCES = ./towers.c ./puzzles.h ./latin.h
1233libtowers2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1234libunequal2_a_SOURCES = ./unequal.c ./puzzles.h ./latin.h
1235libunequal2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1236libunruly2_a_SOURCES = ./unruly.c ./puzzles.h
1237libunruly2_a_CPPFLAGS = $(GTK_CFLAGS) $(WARNINGOPTS) -DSTANDALONE_SOLVER
1238noinst_LIBRARIES = libfifteen2.a libfilling2.a libgalaxie2.a libgalaxie4.a \
1239 libkeen2.a liblatin6.a liblatin8.a liblightup2.a libloopy2.a \
1240 libmagnets2.a libmap2.a libmines2.a libpattern2.a \
1241 libpattern4.a libpearl2.a libsignpos2.a libsingles3.a \
1242 libslant2.a libsolo2.a libtents3.a libtowers2.a \
1243 libunequal2.a libunruly2.a
1244
1245all: all-am
1246
1247.SUFFIXES:
1248.SUFFIXES: .c .o .obj
1249am--refresh: Makefile
1250 @:
1251$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
1252 @for dep in $?; do \
1253 case '$(am__configure_deps)' in \
1254 *$$dep*) \
1255 echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
1256 $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
1257 && exit 0; \
1258 exit 1;; \
1259 esac; \
1260 done; \
1261 echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
1262 $(am__cd) $(top_srcdir) && \
1263 $(AUTOMAKE) --foreign Makefile
1264Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
1265 @case '$?' in \
1266 *config.status*) \
1267 echo ' $(SHELL) ./config.status'; \
1268 $(SHELL) ./config.status;; \
1269 *) \
1270 echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
1271 cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
1272 esac;
1273
1274$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
1275 $(SHELL) ./config.status --recheck
1276
1277$(top_srcdir)/configure: $(am__configure_deps)
1278 $(am__cd) $(srcdir) && $(AUTOCONF)
1279$(ACLOCAL_M4): $(am__aclocal_m4_deps)
1280 $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
1281$(am__aclocal_m4_deps):
1282
1283clean-noinstLIBRARIES:
1284 -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
1285./$(am__dirstamp):
1286 @$(MKDIR_P) .
1287 @: > ./$(am__dirstamp)
1288$(DEPDIR)/$(am__dirstamp):
1289 @$(MKDIR_P) ./$(DEPDIR)
1290 @: > $(DEPDIR)/$(am__dirstamp)
1291./libfifteen2_a-fifteen.$(OBJEXT): ./$(am__dirstamp) \
1292 $(DEPDIR)/$(am__dirstamp)
1293
1294libfifteen2.a: $(libfifteen2_a_OBJECTS) $(libfifteen2_a_DEPENDENCIES) $(EXTRA_libfifteen2_a_DEPENDENCIES)
1295 $(AM_V_at)-rm -f libfifteen2.a
1296 $(AM_V_AR)$(libfifteen2_a_AR) libfifteen2.a $(libfifteen2_a_OBJECTS) $(libfifteen2_a_LIBADD)
1297 $(AM_V_at)$(RANLIB) libfifteen2.a
1298./libfilling2_a-filling.$(OBJEXT): ./$(am__dirstamp) \
1299 $(DEPDIR)/$(am__dirstamp)
1300
1301libfilling2.a: $(libfilling2_a_OBJECTS) $(libfilling2_a_DEPENDENCIES) $(EXTRA_libfilling2_a_DEPENDENCIES)
1302 $(AM_V_at)-rm -f libfilling2.a
1303 $(AM_V_AR)$(libfilling2_a_AR) libfilling2.a $(libfilling2_a_OBJECTS) $(libfilling2_a_LIBADD)
1304 $(AM_V_at)$(RANLIB) libfilling2.a
1305./libgalaxie2_a-galaxies.$(OBJEXT): ./$(am__dirstamp) \
1306 $(DEPDIR)/$(am__dirstamp)
1307
1308libgalaxie2.a: $(libgalaxie2_a_OBJECTS) $(libgalaxie2_a_DEPENDENCIES) $(EXTRA_libgalaxie2_a_DEPENDENCIES)
1309 $(AM_V_at)-rm -f libgalaxie2.a
1310 $(AM_V_AR)$(libgalaxie2_a_AR) libgalaxie2.a $(libgalaxie2_a_OBJECTS) $(libgalaxie2_a_LIBADD)
1311 $(AM_V_at)$(RANLIB) libgalaxie2.a
1312./libgalaxie4_a-galaxies.$(OBJEXT): ./$(am__dirstamp) \
1313 $(DEPDIR)/$(am__dirstamp)
1314
1315libgalaxie4.a: $(libgalaxie4_a_OBJECTS) $(libgalaxie4_a_DEPENDENCIES) $(EXTRA_libgalaxie4_a_DEPENDENCIES)
1316 $(AM_V_at)-rm -f libgalaxie4.a
1317 $(AM_V_AR)$(libgalaxie4_a_AR) libgalaxie4.a $(libgalaxie4_a_OBJECTS) $(libgalaxie4_a_LIBADD)
1318 $(AM_V_at)$(RANLIB) libgalaxie4.a
1319./libkeen2_a-keen.$(OBJEXT): ./$(am__dirstamp) \
1320 $(DEPDIR)/$(am__dirstamp)
1321
1322libkeen2.a: $(libkeen2_a_OBJECTS) $(libkeen2_a_DEPENDENCIES) $(EXTRA_libkeen2_a_DEPENDENCIES)
1323 $(AM_V_at)-rm -f libkeen2.a
1324 $(AM_V_AR)$(libkeen2_a_AR) libkeen2.a $(libkeen2_a_OBJECTS) $(libkeen2_a_LIBADD)
1325 $(AM_V_at)$(RANLIB) libkeen2.a
1326./liblatin6_a-latin.$(OBJEXT): ./$(am__dirstamp) \
1327 $(DEPDIR)/$(am__dirstamp)
1328
1329liblatin6.a: $(liblatin6_a_OBJECTS) $(liblatin6_a_DEPENDENCIES) $(EXTRA_liblatin6_a_DEPENDENCIES)
1330 $(AM_V_at)-rm -f liblatin6.a
1331 $(AM_V_AR)$(liblatin6_a_AR) liblatin6.a $(liblatin6_a_OBJECTS) $(liblatin6_a_LIBADD)
1332 $(AM_V_at)$(RANLIB) liblatin6.a
1333./liblatin8_a-latin.$(OBJEXT): ./$(am__dirstamp) \
1334 $(DEPDIR)/$(am__dirstamp)
1335
1336liblatin8.a: $(liblatin8_a_OBJECTS) $(liblatin8_a_DEPENDENCIES) $(EXTRA_liblatin8_a_DEPENDENCIES)
1337 $(AM_V_at)-rm -f liblatin8.a
1338 $(AM_V_AR)$(liblatin8_a_AR) liblatin8.a $(liblatin8_a_OBJECTS) $(liblatin8_a_LIBADD)
1339 $(AM_V_at)$(RANLIB) liblatin8.a
1340./liblightup2_a-lightup.$(OBJEXT): ./$(am__dirstamp) \
1341 $(DEPDIR)/$(am__dirstamp)
1342
1343liblightup2.a: $(liblightup2_a_OBJECTS) $(liblightup2_a_DEPENDENCIES) $(EXTRA_liblightup2_a_DEPENDENCIES)
1344 $(AM_V_at)-rm -f liblightup2.a
1345 $(AM_V_AR)$(liblightup2_a_AR) liblightup2.a $(liblightup2_a_OBJECTS) $(liblightup2_a_LIBADD)
1346 $(AM_V_at)$(RANLIB) liblightup2.a
1347./libloopy2_a-loopy.$(OBJEXT): ./$(am__dirstamp) \
1348 $(DEPDIR)/$(am__dirstamp)
1349
1350libloopy2.a: $(libloopy2_a_OBJECTS) $(libloopy2_a_DEPENDENCIES) $(EXTRA_libloopy2_a_DEPENDENCIES)
1351 $(AM_V_at)-rm -f libloopy2.a
1352 $(AM_V_AR)$(libloopy2_a_AR) libloopy2.a $(libloopy2_a_OBJECTS) $(libloopy2_a_LIBADD)
1353 $(AM_V_at)$(RANLIB) libloopy2.a
1354./libmagnets2_a-magnets.$(OBJEXT): ./$(am__dirstamp) \
1355 $(DEPDIR)/$(am__dirstamp)
1356
1357libmagnets2.a: $(libmagnets2_a_OBJECTS) $(libmagnets2_a_DEPENDENCIES) $(EXTRA_libmagnets2_a_DEPENDENCIES)
1358 $(AM_V_at)-rm -f libmagnets2.a
1359 $(AM_V_AR)$(libmagnets2_a_AR) libmagnets2.a $(libmagnets2_a_OBJECTS) $(libmagnets2_a_LIBADD)
1360 $(AM_V_at)$(RANLIB) libmagnets2.a
1361./libmap2_a-map.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1362
1363libmap2.a: $(libmap2_a_OBJECTS) $(libmap2_a_DEPENDENCIES) $(EXTRA_libmap2_a_DEPENDENCIES)
1364 $(AM_V_at)-rm -f libmap2.a
1365 $(AM_V_AR)$(libmap2_a_AR) libmap2.a $(libmap2_a_OBJECTS) $(libmap2_a_LIBADD)
1366 $(AM_V_at)$(RANLIB) libmap2.a
1367./libmines2_a-mines.$(OBJEXT): ./$(am__dirstamp) \
1368 $(DEPDIR)/$(am__dirstamp)
1369
1370libmines2.a: $(libmines2_a_OBJECTS) $(libmines2_a_DEPENDENCIES) $(EXTRA_libmines2_a_DEPENDENCIES)
1371 $(AM_V_at)-rm -f libmines2.a
1372 $(AM_V_AR)$(libmines2_a_AR) libmines2.a $(libmines2_a_OBJECTS) $(libmines2_a_LIBADD)
1373 $(AM_V_at)$(RANLIB) libmines2.a
1374./libpattern2_a-pattern.$(OBJEXT): ./$(am__dirstamp) \
1375 $(DEPDIR)/$(am__dirstamp)
1376
1377libpattern2.a: $(libpattern2_a_OBJECTS) $(libpattern2_a_DEPENDENCIES) $(EXTRA_libpattern2_a_DEPENDENCIES)
1378 $(AM_V_at)-rm -f libpattern2.a
1379 $(AM_V_AR)$(libpattern2_a_AR) libpattern2.a $(libpattern2_a_OBJECTS) $(libpattern2_a_LIBADD)
1380 $(AM_V_at)$(RANLIB) libpattern2.a
1381./libpattern4_a-pattern.$(OBJEXT): ./$(am__dirstamp) \
1382 $(DEPDIR)/$(am__dirstamp)
1383
1384libpattern4.a: $(libpattern4_a_OBJECTS) $(libpattern4_a_DEPENDENCIES) $(EXTRA_libpattern4_a_DEPENDENCIES)
1385 $(AM_V_at)-rm -f libpattern4.a
1386 $(AM_V_AR)$(libpattern4_a_AR) libpattern4.a $(libpattern4_a_OBJECTS) $(libpattern4_a_LIBADD)
1387 $(AM_V_at)$(RANLIB) libpattern4.a
1388./libpearl2_a-pearl.$(OBJEXT): ./$(am__dirstamp) \
1389 $(DEPDIR)/$(am__dirstamp)
1390
1391libpearl2.a: $(libpearl2_a_OBJECTS) $(libpearl2_a_DEPENDENCIES) $(EXTRA_libpearl2_a_DEPENDENCIES)
1392 $(AM_V_at)-rm -f libpearl2.a
1393 $(AM_V_AR)$(libpearl2_a_AR) libpearl2.a $(libpearl2_a_OBJECTS) $(libpearl2_a_LIBADD)
1394 $(AM_V_at)$(RANLIB) libpearl2.a
1395./libsignpos2_a-signpost.$(OBJEXT): ./$(am__dirstamp) \
1396 $(DEPDIR)/$(am__dirstamp)
1397
1398libsignpos2.a: $(libsignpos2_a_OBJECTS) $(libsignpos2_a_DEPENDENCIES) $(EXTRA_libsignpos2_a_DEPENDENCIES)
1399 $(AM_V_at)-rm -f libsignpos2.a
1400 $(AM_V_AR)$(libsignpos2_a_AR) libsignpos2.a $(libsignpos2_a_OBJECTS) $(libsignpos2_a_LIBADD)
1401 $(AM_V_at)$(RANLIB) libsignpos2.a
1402./libsingles3_a-singles.$(OBJEXT): ./$(am__dirstamp) \
1403 $(DEPDIR)/$(am__dirstamp)
1404
1405libsingles3.a: $(libsingles3_a_OBJECTS) $(libsingles3_a_DEPENDENCIES) $(EXTRA_libsingles3_a_DEPENDENCIES)
1406 $(AM_V_at)-rm -f libsingles3.a
1407 $(AM_V_AR)$(libsingles3_a_AR) libsingles3.a $(libsingles3_a_OBJECTS) $(libsingles3_a_LIBADD)
1408 $(AM_V_at)$(RANLIB) libsingles3.a
1409./libslant2_a-slant.$(OBJEXT): ./$(am__dirstamp) \
1410 $(DEPDIR)/$(am__dirstamp)
1411
1412libslant2.a: $(libslant2_a_OBJECTS) $(libslant2_a_DEPENDENCIES) $(EXTRA_libslant2_a_DEPENDENCIES)
1413 $(AM_V_at)-rm -f libslant2.a
1414 $(AM_V_AR)$(libslant2_a_AR) libslant2.a $(libslant2_a_OBJECTS) $(libslant2_a_LIBADD)
1415 $(AM_V_at)$(RANLIB) libslant2.a
1416./libsolo2_a-solo.$(OBJEXT): ./$(am__dirstamp) \
1417 $(DEPDIR)/$(am__dirstamp)
1418
1419libsolo2.a: $(libsolo2_a_OBJECTS) $(libsolo2_a_DEPENDENCIES) $(EXTRA_libsolo2_a_DEPENDENCIES)
1420 $(AM_V_at)-rm -f libsolo2.a
1421 $(AM_V_AR)$(libsolo2_a_AR) libsolo2.a $(libsolo2_a_OBJECTS) $(libsolo2_a_LIBADD)
1422 $(AM_V_at)$(RANLIB) libsolo2.a
1423./libtents3_a-tents.$(OBJEXT): ./$(am__dirstamp) \
1424 $(DEPDIR)/$(am__dirstamp)
1425
1426libtents3.a: $(libtents3_a_OBJECTS) $(libtents3_a_DEPENDENCIES) $(EXTRA_libtents3_a_DEPENDENCIES)
1427 $(AM_V_at)-rm -f libtents3.a
1428 $(AM_V_AR)$(libtents3_a_AR) libtents3.a $(libtents3_a_OBJECTS) $(libtents3_a_LIBADD)
1429 $(AM_V_at)$(RANLIB) libtents3.a
1430./libtowers2_a-towers.$(OBJEXT): ./$(am__dirstamp) \
1431 $(DEPDIR)/$(am__dirstamp)
1432
1433libtowers2.a: $(libtowers2_a_OBJECTS) $(libtowers2_a_DEPENDENCIES) $(EXTRA_libtowers2_a_DEPENDENCIES)
1434 $(AM_V_at)-rm -f libtowers2.a
1435 $(AM_V_AR)$(libtowers2_a_AR) libtowers2.a $(libtowers2_a_OBJECTS) $(libtowers2_a_LIBADD)
1436 $(AM_V_at)$(RANLIB) libtowers2.a
1437./libunequal2_a-unequal.$(OBJEXT): ./$(am__dirstamp) \
1438 $(DEPDIR)/$(am__dirstamp)
1439
1440libunequal2.a: $(libunequal2_a_OBJECTS) $(libunequal2_a_DEPENDENCIES) $(EXTRA_libunequal2_a_DEPENDENCIES)
1441 $(AM_V_at)-rm -f libunequal2.a
1442 $(AM_V_AR)$(libunequal2_a_AR) libunequal2.a $(libunequal2_a_OBJECTS) $(libunequal2_a_LIBADD)
1443 $(AM_V_at)$(RANLIB) libunequal2.a
1444./libunruly2_a-unruly.$(OBJEXT): ./$(am__dirstamp) \
1445 $(DEPDIR)/$(am__dirstamp)
1446
1447libunruly2.a: $(libunruly2_a_OBJECTS) $(libunruly2_a_DEPENDENCIES) $(EXTRA_libunruly2_a_DEPENDENCIES)
1448 $(AM_V_at)-rm -f libunruly2.a
1449 $(AM_V_AR)$(libunruly2_a_AR) libunruly2.a $(libunruly2_a_OBJECTS) $(libunruly2_a_LIBADD)
1450 $(AM_V_at)$(RANLIB) libunruly2.a
1451install-binPROGRAMS: $(bin_PROGRAMS)
1452 @$(NORMAL_INSTALL)
1453 @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
1454 if test -n "$$list"; then \
1455 echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
1456 $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
1457 fi; \
1458 for p in $$list; do echo "$$p $$p"; done | \
1459 sed 's/$(EXEEXT)$$//' | \
1460 while read p p1; do if test -f $$p \
1461 ; then echo "$$p"; echo "$$p"; else :; fi; \
1462 done | \
1463 sed -e 'p;s,.*/,,;n;h' \
1464 -e 's|.*|.|' \
1465 -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
1466 sed 'N;N;N;s,\n, ,g' | \
1467 $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
1468 { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
1469 if ($$2 == $$4) files[d] = files[d] " " $$1; \
1470 else { print "f", $$3 "/" $$4, $$1; } } \
1471 END { for (d in files) print "f", d, files[d] }' | \
1472 while read type dir files; do \
1473 if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
1474 test -z "$$files" || { \
1475 echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
1476 $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
1477 } \
1478 ; done
1479
1480uninstall-binPROGRAMS:
1481 @$(NORMAL_UNINSTALL)
1482 @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
1483 files=`for p in $$list; do echo "$$p"; done | \
1484 sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
1485 -e 's/$$/$(EXEEXT)/' \
1486 `; \
1487 test -n "$$list" || exit 0; \
1488 echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
1489 cd "$(DESTDIR)$(bindir)" && rm -f $$files
1490
1491clean-binPROGRAMS:
1492 -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
1493
1494clean-noinstPROGRAMS:
1495 -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
1496./blackbox.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1497./drawing.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1498./gtk.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1499./malloc.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1500./midend.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1501./misc.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1502./no-icon.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1503./printing.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1504./ps.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1505./random.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1506./version.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1507
1508blackbox$(EXEEXT): $(blackbox_OBJECTS) $(blackbox_DEPENDENCIES) $(EXTRA_blackbox_DEPENDENCIES)
1509 @rm -f blackbox$(EXEEXT)
1510 $(AM_V_CCLD)$(LINK) $(blackbox_OBJECTS) $(blackbox_LDADD) $(LIBS)
1511./bridges.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1512./dsf.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1513./findloop.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1514
1515bridges$(EXEEXT): $(bridges_OBJECTS) $(bridges_DEPENDENCIES) $(EXTRA_bridges_DEPENDENCIES)
1516 @rm -f bridges$(EXEEXT)
1517 $(AM_V_CCLD)$(LINK) $(bridges_OBJECTS) $(bridges_LDADD) $(LIBS)
1518./cube.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1519
1520cube$(EXEEXT): $(cube_OBJECTS) $(cube_DEPENDENCIES) $(EXTRA_cube_DEPENDENCIES)
1521 @rm -f cube$(EXEEXT)
1522 $(AM_V_CCLD)$(LINK) $(cube_OBJECTS) $(cube_LDADD) $(LIBS)
1523./dominosa.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1524./laydomino.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1525
1526dominosa$(EXEEXT): $(dominosa_OBJECTS) $(dominosa_DEPENDENCIES) $(EXTRA_dominosa_DEPENDENCIES)
1527 @rm -f dominosa$(EXEEXT)
1528 $(AM_V_CCLD)$(LINK) $(dominosa_OBJECTS) $(dominosa_LDADD) $(LIBS)
1529./fifteen.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1530
1531fifteen$(EXEEXT): $(fifteen_OBJECTS) $(fifteen_DEPENDENCIES) $(EXTRA_fifteen_DEPENDENCIES)
1532 @rm -f fifteen$(EXEEXT)
1533 $(AM_V_CCLD)$(LINK) $(fifteen_OBJECTS) $(fifteen_LDADD) $(LIBS)
1534./nullfe.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1535
1536fifteensolver$(EXEEXT): $(fifteensolver_OBJECTS) $(fifteensolver_DEPENDENCIES) $(EXTRA_fifteensolver_DEPENDENCIES)
1537 @rm -f fifteensolver$(EXEEXT)
1538 $(AM_V_CCLD)$(LINK) $(fifteensolver_OBJECTS) $(fifteensolver_LDADD) $(LIBS)
1539./filling.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1540
1541filling$(EXEEXT): $(filling_OBJECTS) $(filling_DEPENDENCIES) $(EXTRA_filling_DEPENDENCIES)
1542 @rm -f filling$(EXEEXT)
1543 $(AM_V_CCLD)$(LINK) $(filling_OBJECTS) $(filling_LDADD) $(LIBS)
1544
1545fillingsolver$(EXEEXT): $(fillingsolver_OBJECTS) $(fillingsolver_DEPENDENCIES) $(EXTRA_fillingsolver_DEPENDENCIES)
1546 @rm -f fillingsolver$(EXEEXT)
1547 $(AM_V_CCLD)$(LINK) $(fillingsolver_OBJECTS) $(fillingsolver_LDADD) $(LIBS)
1548./flip.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1549./tree234.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1550
1551flip$(EXEEXT): $(flip_OBJECTS) $(flip_DEPENDENCIES) $(EXTRA_flip_DEPENDENCIES)
1552 @rm -f flip$(EXEEXT)
1553 $(AM_V_CCLD)$(LINK) $(flip_OBJECTS) $(flip_LDADD) $(LIBS)
1554./flood.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1555
1556flood$(EXEEXT): $(flood_OBJECTS) $(flood_DEPENDENCIES) $(EXTRA_flood_DEPENDENCIES)
1557 @rm -f flood$(EXEEXT)
1558 $(AM_V_CCLD)$(LINK) $(flood_OBJECTS) $(flood_LDADD) $(LIBS)
1559./galaxies.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1560
1561galaxies$(EXEEXT): $(galaxies_OBJECTS) $(galaxies_DEPENDENCIES) $(EXTRA_galaxies_DEPENDENCIES)
1562 @rm -f galaxies$(EXEEXT)
1563 $(AM_V_CCLD)$(LINK) $(galaxies_OBJECTS) $(galaxies_LDADD) $(LIBS)
1564
1565galaxiespicture$(EXEEXT): $(galaxiespicture_OBJECTS) $(galaxiespicture_DEPENDENCIES) $(EXTRA_galaxiespicture_DEPENDENCIES)
1566 @rm -f galaxiespicture$(EXEEXT)
1567 $(AM_V_CCLD)$(LINK) $(galaxiespicture_OBJECTS) $(galaxiespicture_LDADD) $(LIBS)
1568
1569galaxiessolver$(EXEEXT): $(galaxiessolver_OBJECTS) $(galaxiessolver_DEPENDENCIES) $(EXTRA_galaxiessolver_DEPENDENCIES)
1570 @rm -f galaxiessolver$(EXEEXT)
1571 $(AM_V_CCLD)$(LINK) $(galaxiessolver_OBJECTS) $(galaxiessolver_LDADD) $(LIBS)
1572./guess.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1573
1574guess$(EXEEXT): $(guess_OBJECTS) $(guess_DEPENDENCIES) $(EXTRA_guess_DEPENDENCIES)
1575 @rm -f guess$(EXEEXT)
1576 $(AM_V_CCLD)$(LINK) $(guess_OBJECTS) $(guess_LDADD) $(LIBS)
1577./inertia.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1578
1579inertia$(EXEEXT): $(inertia_OBJECTS) $(inertia_DEPENDENCIES) $(EXTRA_inertia_DEPENDENCIES)
1580 @rm -f inertia$(EXEEXT)
1581 $(AM_V_CCLD)$(LINK) $(inertia_OBJECTS) $(inertia_LDADD) $(LIBS)
1582./keen.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1583./latin.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1584./maxflow.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1585
1586keen$(EXEEXT): $(keen_OBJECTS) $(keen_DEPENDENCIES) $(EXTRA_keen_DEPENDENCIES)
1587 @rm -f keen$(EXEEXT)
1588 $(AM_V_CCLD)$(LINK) $(keen_OBJECTS) $(keen_LDADD) $(LIBS)
1589
1590keensolver$(EXEEXT): $(keensolver_OBJECTS) $(keensolver_DEPENDENCIES) $(EXTRA_keensolver_DEPENDENCIES)
1591 @rm -f keensolver$(EXEEXT)
1592 $(AM_V_CCLD)$(LINK) $(keensolver_OBJECTS) $(keensolver_LDADD) $(LIBS)
1593
1594latincheck$(EXEEXT): $(latincheck_OBJECTS) $(latincheck_DEPENDENCIES) $(EXTRA_latincheck_DEPENDENCIES)
1595 @rm -f latincheck$(EXEEXT)
1596 $(AM_V_CCLD)$(LINK) $(latincheck_OBJECTS) $(latincheck_LDADD) $(LIBS)
1597./combi.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1598./lightup.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1599
1600lightup$(EXEEXT): $(lightup_OBJECTS) $(lightup_DEPENDENCIES) $(EXTRA_lightup_DEPENDENCIES)
1601 @rm -f lightup$(EXEEXT)
1602 $(AM_V_CCLD)$(LINK) $(lightup_OBJECTS) $(lightup_LDADD) $(LIBS)
1603
1604lightupsolver$(EXEEXT): $(lightupsolver_OBJECTS) $(lightupsolver_DEPENDENCIES) $(EXTRA_lightupsolver_DEPENDENCIES)
1605 @rm -f lightupsolver$(EXEEXT)
1606 $(AM_V_CCLD)$(LINK) $(lightupsolver_OBJECTS) $(lightupsolver_LDADD) $(LIBS)
1607./grid.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1608./loopgen.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1609./loopy.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1610./penrose.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1611
1612loopy$(EXEEXT): $(loopy_OBJECTS) $(loopy_DEPENDENCIES) $(EXTRA_loopy_DEPENDENCIES)
1613 @rm -f loopy$(EXEEXT)
1614 $(AM_V_CCLD)$(LINK) $(loopy_OBJECTS) $(loopy_LDADD) $(LIBS)
1615
1616loopysolver$(EXEEXT): $(loopysolver_OBJECTS) $(loopysolver_DEPENDENCIES) $(EXTRA_loopysolver_DEPENDENCIES)
1617 @rm -f loopysolver$(EXEEXT)
1618 $(AM_V_CCLD)$(LINK) $(loopysolver_OBJECTS) $(loopysolver_LDADD) $(LIBS)
1619./magnets.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1620
1621magnets$(EXEEXT): $(magnets_OBJECTS) $(magnets_DEPENDENCIES) $(EXTRA_magnets_DEPENDENCIES)
1622 @rm -f magnets$(EXEEXT)
1623 $(AM_V_CCLD)$(LINK) $(magnets_OBJECTS) $(magnets_LDADD) $(LIBS)
1624
1625magnetssolver$(EXEEXT): $(magnetssolver_OBJECTS) $(magnetssolver_DEPENDENCIES) $(EXTRA_magnetssolver_DEPENDENCIES)
1626 @rm -f magnetssolver$(EXEEXT)
1627 $(AM_V_CCLD)$(LINK) $(magnetssolver_OBJECTS) $(magnetssolver_LDADD) $(LIBS)
1628./map.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1629
1630map$(EXEEXT): $(map_OBJECTS) $(map_DEPENDENCIES) $(EXTRA_map_DEPENDENCIES)
1631 @rm -f map$(EXEEXT)
1632 $(AM_V_CCLD)$(LINK) $(map_OBJECTS) $(map_LDADD) $(LIBS)
1633
1634mapsolver$(EXEEXT): $(mapsolver_OBJECTS) $(mapsolver_DEPENDENCIES) $(EXTRA_mapsolver_DEPENDENCIES)
1635 @rm -f mapsolver$(EXEEXT)
1636 $(AM_V_CCLD)$(LINK) $(mapsolver_OBJECTS) $(mapsolver_LDADD) $(LIBS)
1637
1638mineobfusc$(EXEEXT): $(mineobfusc_OBJECTS) $(mineobfusc_DEPENDENCIES) $(EXTRA_mineobfusc_DEPENDENCIES)
1639 @rm -f mineobfusc$(EXEEXT)
1640 $(AM_V_CCLD)$(LINK) $(mineobfusc_OBJECTS) $(mineobfusc_LDADD) $(LIBS)
1641./mines.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1642
1643mines$(EXEEXT): $(mines_OBJECTS) $(mines_DEPENDENCIES) $(EXTRA_mines_DEPENDENCIES)
1644 @rm -f mines$(EXEEXT)
1645 $(AM_V_CCLD)$(LINK) $(mines_OBJECTS) $(mines_LDADD) $(LIBS)
1646./net.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1647
1648net$(EXEEXT): $(net_OBJECTS) $(net_DEPENDENCIES) $(EXTRA_net_DEPENDENCIES)
1649 @rm -f net$(EXEEXT)
1650 $(AM_V_CCLD)$(LINK) $(net_OBJECTS) $(net_LDADD) $(LIBS)
1651./netslide.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1652
1653netslide$(EXEEXT): $(netslide_OBJECTS) $(netslide_DEPENDENCIES) $(EXTRA_netslide_DEPENDENCIES)
1654 @rm -f netslide$(EXEEXT)
1655 $(AM_V_CCLD)$(LINK) $(netslide_OBJECTS) $(netslide_LDADD) $(LIBS)
1656./nullgame.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1657
1658nullgame$(EXEEXT): $(nullgame_OBJECTS) $(nullgame_DEPENDENCIES) $(EXTRA_nullgame_DEPENDENCIES)
1659 @rm -f nullgame$(EXEEXT)
1660 $(AM_V_CCLD)$(LINK) $(nullgame_OBJECTS) $(nullgame_LDADD) $(LIBS)
1661./obfusc.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1662
1663obfusc$(EXEEXT): $(obfusc_OBJECTS) $(obfusc_DEPENDENCIES) $(EXTRA_obfusc_DEPENDENCIES)
1664 @rm -f obfusc$(EXEEXT)
1665 $(AM_V_CCLD)$(LINK) $(obfusc_OBJECTS) $(obfusc_LDADD) $(LIBS)
1666./divvy.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1667./palisade.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1668
1669palisade$(EXEEXT): $(palisade_OBJECTS) $(palisade_DEPENDENCIES) $(EXTRA_palisade_DEPENDENCIES)
1670 @rm -f palisade$(EXEEXT)
1671 $(AM_V_CCLD)$(LINK) $(palisade_OBJECTS) $(palisade_LDADD) $(LIBS)
1672./pattern.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1673
1674pattern$(EXEEXT): $(pattern_OBJECTS) $(pattern_DEPENDENCIES) $(EXTRA_pattern_DEPENDENCIES)
1675 @rm -f pattern$(EXEEXT)
1676 $(AM_V_CCLD)$(LINK) $(pattern_OBJECTS) $(pattern_LDADD) $(LIBS)
1677
1678patternpicture$(EXEEXT): $(patternpicture_OBJECTS) $(patternpicture_DEPENDENCIES) $(EXTRA_patternpicture_DEPENDENCIES)
1679 @rm -f patternpicture$(EXEEXT)
1680 $(AM_V_CCLD)$(LINK) $(patternpicture_OBJECTS) $(patternpicture_LDADD) $(LIBS)
1681
1682patternsolver$(EXEEXT): $(patternsolver_OBJECTS) $(patternsolver_DEPENDENCIES) $(EXTRA_patternsolver_DEPENDENCIES)
1683 @rm -f patternsolver$(EXEEXT)
1684 $(AM_V_CCLD)$(LINK) $(patternsolver_OBJECTS) $(patternsolver_LDADD) $(LIBS)
1685./pearl.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1686./tdq.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1687
1688pearl$(EXEEXT): $(pearl_OBJECTS) $(pearl_DEPENDENCIES) $(EXTRA_pearl_DEPENDENCIES)
1689 @rm -f pearl$(EXEEXT)
1690 $(AM_V_CCLD)$(LINK) $(pearl_OBJECTS) $(pearl_LDADD) $(LIBS)
1691
1692pearlbench$(EXEEXT): $(pearlbench_OBJECTS) $(pearlbench_DEPENDENCIES) $(EXTRA_pearlbench_DEPENDENCIES)
1693 @rm -f pearlbench$(EXEEXT)
1694 $(AM_V_CCLD)$(LINK) $(pearlbench_OBJECTS) $(pearlbench_LDADD) $(LIBS)
1695./pegs.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1696
1697pegs$(EXEEXT): $(pegs_OBJECTS) $(pegs_DEPENDENCIES) $(EXTRA_pegs_DEPENDENCIES)
1698 @rm -f pegs$(EXEEXT)
1699 $(AM_V_CCLD)$(LINK) $(pegs_OBJECTS) $(pegs_LDADD) $(LIBS)
1700./range.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1701
1702range$(EXEEXT): $(range_OBJECTS) $(range_DEPENDENCIES) $(EXTRA_range_DEPENDENCIES)
1703 @rm -f range$(EXEEXT)
1704 $(AM_V_CCLD)$(LINK) $(range_OBJECTS) $(range_LDADD) $(LIBS)
1705./rect.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1706
1707rect$(EXEEXT): $(rect_OBJECTS) $(rect_DEPENDENCIES) $(EXTRA_rect_DEPENDENCIES)
1708 @rm -f rect$(EXEEXT)
1709 $(AM_V_CCLD)$(LINK) $(rect_OBJECTS) $(rect_LDADD) $(LIBS)
1710./samegame.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1711
1712samegame$(EXEEXT): $(samegame_OBJECTS) $(samegame_DEPENDENCIES) $(EXTRA_samegame_DEPENDENCIES)
1713 @rm -f samegame$(EXEEXT)
1714 $(AM_V_CCLD)$(LINK) $(samegame_OBJECTS) $(samegame_LDADD) $(LIBS)
1715./signpost.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1716
1717signpost$(EXEEXT): $(signpost_OBJECTS) $(signpost_DEPENDENCIES) $(EXTRA_signpost_DEPENDENCIES)
1718 @rm -f signpost$(EXEEXT)
1719 $(AM_V_CCLD)$(LINK) $(signpost_OBJECTS) $(signpost_LDADD) $(LIBS)
1720
1721signpostsolver$(EXEEXT): $(signpostsolver_OBJECTS) $(signpostsolver_DEPENDENCIES) $(EXTRA_signpostsolver_DEPENDENCIES)
1722 @rm -f signpostsolver$(EXEEXT)
1723 $(AM_V_CCLD)$(LINK) $(signpostsolver_OBJECTS) $(signpostsolver_LDADD) $(LIBS)
1724./singles.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1725
1726singles$(EXEEXT): $(singles_OBJECTS) $(singles_DEPENDENCIES) $(EXTRA_singles_DEPENDENCIES)
1727 @rm -f singles$(EXEEXT)
1728 $(AM_V_CCLD)$(LINK) $(singles_OBJECTS) $(singles_LDADD) $(LIBS)
1729
1730singlessolver$(EXEEXT): $(singlessolver_OBJECTS) $(singlessolver_DEPENDENCIES) $(EXTRA_singlessolver_DEPENDENCIES)
1731 @rm -f singlessolver$(EXEEXT)
1732 $(AM_V_CCLD)$(LINK) $(singlessolver_OBJECTS) $(singlessolver_LDADD) $(LIBS)
1733./sixteen.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1734
1735sixteen$(EXEEXT): $(sixteen_OBJECTS) $(sixteen_DEPENDENCIES) $(EXTRA_sixteen_DEPENDENCIES)
1736 @rm -f sixteen$(EXEEXT)
1737 $(AM_V_CCLD)$(LINK) $(sixteen_OBJECTS) $(sixteen_LDADD) $(LIBS)
1738./slant.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1739
1740slant$(EXEEXT): $(slant_OBJECTS) $(slant_DEPENDENCIES) $(EXTRA_slant_DEPENDENCIES)
1741 @rm -f slant$(EXEEXT)
1742 $(AM_V_CCLD)$(LINK) $(slant_OBJECTS) $(slant_LDADD) $(LIBS)
1743
1744slantsolver$(EXEEXT): $(slantsolver_OBJECTS) $(slantsolver_DEPENDENCIES) $(EXTRA_slantsolver_DEPENDENCIES)
1745 @rm -f slantsolver$(EXEEXT)
1746 $(AM_V_CCLD)$(LINK) $(slantsolver_OBJECTS) $(slantsolver_LDADD) $(LIBS)
1747./solo.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1748
1749solo$(EXEEXT): $(solo_OBJECTS) $(solo_DEPENDENCIES) $(EXTRA_solo_DEPENDENCIES)
1750 @rm -f solo$(EXEEXT)
1751 $(AM_V_CCLD)$(LINK) $(solo_OBJECTS) $(solo_LDADD) $(LIBS)
1752
1753solosolver$(EXEEXT): $(solosolver_OBJECTS) $(solosolver_DEPENDENCIES) $(EXTRA_solosolver_DEPENDENCIES)
1754 @rm -f solosolver$(EXEEXT)
1755 $(AM_V_CCLD)$(LINK) $(solosolver_OBJECTS) $(solosolver_LDADD) $(LIBS)
1756./tents.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1757
1758tents$(EXEEXT): $(tents_OBJECTS) $(tents_DEPENDENCIES) $(EXTRA_tents_DEPENDENCIES)
1759 @rm -f tents$(EXEEXT)
1760 $(AM_V_CCLD)$(LINK) $(tents_OBJECTS) $(tents_LDADD) $(LIBS)
1761
1762tentssolver$(EXEEXT): $(tentssolver_OBJECTS) $(tentssolver_DEPENDENCIES) $(EXTRA_tentssolver_DEPENDENCIES)
1763 @rm -f tentssolver$(EXEEXT)
1764 $(AM_V_CCLD)$(LINK) $(tentssolver_OBJECTS) $(tentssolver_LDADD) $(LIBS)
1765./towers.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1766
1767towers$(EXEEXT): $(towers_OBJECTS) $(towers_DEPENDENCIES) $(EXTRA_towers_DEPENDENCIES)
1768 @rm -f towers$(EXEEXT)
1769 $(AM_V_CCLD)$(LINK) $(towers_OBJECTS) $(towers_LDADD) $(LIBS)
1770
1771towerssolver$(EXEEXT): $(towerssolver_OBJECTS) $(towerssolver_DEPENDENCIES) $(EXTRA_towerssolver_DEPENDENCIES)
1772 @rm -f towerssolver$(EXEEXT)
1773 $(AM_V_CCLD)$(LINK) $(towerssolver_OBJECTS) $(towerssolver_LDADD) $(LIBS)
1774./tracks.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1775
1776tracks$(EXEEXT): $(tracks_OBJECTS) $(tracks_DEPENDENCIES) $(EXTRA_tracks_DEPENDENCIES)
1777 @rm -f tracks$(EXEEXT)
1778 $(AM_V_CCLD)$(LINK) $(tracks_OBJECTS) $(tracks_LDADD) $(LIBS)
1779./twiddle.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1780
1781twiddle$(EXEEXT): $(twiddle_OBJECTS) $(twiddle_DEPENDENCIES) $(EXTRA_twiddle_DEPENDENCIES)
1782 @rm -f twiddle$(EXEEXT)
1783 $(AM_V_CCLD)$(LINK) $(twiddle_OBJECTS) $(twiddle_LDADD) $(LIBS)
1784./undead.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1785
1786undead$(EXEEXT): $(undead_OBJECTS) $(undead_DEPENDENCIES) $(EXTRA_undead_DEPENDENCIES)
1787 @rm -f undead$(EXEEXT)
1788 $(AM_V_CCLD)$(LINK) $(undead_OBJECTS) $(undead_LDADD) $(LIBS)
1789./unequal.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1790
1791unequal$(EXEEXT): $(unequal_OBJECTS) $(unequal_DEPENDENCIES) $(EXTRA_unequal_DEPENDENCIES)
1792 @rm -f unequal$(EXEEXT)
1793 $(AM_V_CCLD)$(LINK) $(unequal_OBJECTS) $(unequal_LDADD) $(LIBS)
1794
1795unequalsolver$(EXEEXT): $(unequalsolver_OBJECTS) $(unequalsolver_DEPENDENCIES) $(EXTRA_unequalsolver_DEPENDENCIES)
1796 @rm -f unequalsolver$(EXEEXT)
1797 $(AM_V_CCLD)$(LINK) $(unequalsolver_OBJECTS) $(unequalsolver_LDADD) $(LIBS)
1798./unruly.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1799
1800unruly$(EXEEXT): $(unruly_OBJECTS) $(unruly_DEPENDENCIES) $(EXTRA_unruly_DEPENDENCIES)
1801 @rm -f unruly$(EXEEXT)
1802 $(AM_V_CCLD)$(LINK) $(unruly_OBJECTS) $(unruly_LDADD) $(LIBS)
1803
1804unrulysolver$(EXEEXT): $(unrulysolver_OBJECTS) $(unrulysolver_DEPENDENCIES) $(EXTRA_unrulysolver_DEPENDENCIES)
1805 @rm -f unrulysolver$(EXEEXT)
1806 $(AM_V_CCLD)$(LINK) $(unrulysolver_OBJECTS) $(unrulysolver_LDADD) $(LIBS)
1807./untangle.$(OBJEXT): ./$(am__dirstamp) $(DEPDIR)/$(am__dirstamp)
1808
1809untangle$(EXEEXT): $(untangle_OBJECTS) $(untangle_DEPENDENCIES) $(EXTRA_untangle_DEPENDENCIES)
1810 @rm -f untangle$(EXEEXT)
1811 $(AM_V_CCLD)$(LINK) $(untangle_OBJECTS) $(untangle_LDADD) $(LIBS)
1812
1813mostlyclean-compile:
1814 -rm -f *.$(OBJEXT)
1815 -rm -f ./*.$(OBJEXT)
1816
1817distclean-compile:
1818 -rm -f *.tab.c
1819
1820@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blackbox.Po@am__quote@
1821@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bridges.Po@am__quote@
1822@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/combi.Po@am__quote@
1823@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cube.Po@am__quote@
1824@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/divvy.Po@am__quote@
1825@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dominosa.Po@am__quote@
1826@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drawing.Po@am__quote@
1827@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsf.Po@am__quote@
1828@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fifteen.Po@am__quote@
1829@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filling.Po@am__quote@
1830@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/findloop.Po@am__quote@
1831@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flip.Po@am__quote@
1832@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flood.Po@am__quote@
1833@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/galaxies.Po@am__quote@
1834@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid.Po@am__quote@
1835@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk.Po@am__quote@
1836@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/guess.Po@am__quote@
1837@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inertia.Po@am__quote@
1838@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keen.Po@am__quote@
1839@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/latin.Po@am__quote@
1840@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/laydomino.Po@am__quote@
1841@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfifteen2_a-fifteen.Po@am__quote@
1842@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfilling2_a-filling.Po@am__quote@
1843@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgalaxie2_a-galaxies.Po@am__quote@
1844@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgalaxie4_a-galaxies.Po@am__quote@
1845@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkeen2_a-keen.Po@am__quote@
1846@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblatin6_a-latin.Po@am__quote@
1847@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblatin8_a-latin.Po@am__quote@
1848@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightup2_a-lightup.Po@am__quote@
1849@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libloopy2_a-loopy.Po@am__quote@
1850@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmagnets2_a-magnets.Po@am__quote@
1851@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmap2_a-map.Po@am__quote@
1852@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmines2_a-mines.Po@am__quote@
1853@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpattern2_a-pattern.Po@am__quote@
1854@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpattern4_a-pattern.Po@am__quote@
1855@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpearl2_a-pearl.Po@am__quote@
1856@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsignpos2_a-signpost.Po@am__quote@
1857@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsingles3_a-singles.Po@am__quote@
1858@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libslant2_a-slant.Po@am__quote@
1859@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsolo2_a-solo.Po@am__quote@
1860@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtents3_a-tents.Po@am__quote@
1861@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtowers2_a-towers.Po@am__quote@
1862@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libunequal2_a-unequal.Po@am__quote@
1863@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libunruly2_a-unruly.Po@am__quote@
1864@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lightup.Po@am__quote@
1865@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loopgen.Po@am__quote@
1866@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loopy.Po@am__quote@
1867@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/magnets.Po@am__quote@
1868@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Po@am__quote@
1869@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map.Po@am__quote@
1870@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maxflow.Po@am__quote@
1871@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/midend.Po@am__quote@
1872@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mines.Po@am__quote@
1873@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
1874@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net.Po@am__quote@
1875@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netslide.Po@am__quote@
1876@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/no-icon.Po@am__quote@
1877@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nullfe.Po@am__quote@
1878@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nullgame.Po@am__quote@
1879@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obfusc.Po@am__quote@
1880@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/palisade.Po@am__quote@
1881@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pattern.Po@am__quote@
1882@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pearl.Po@am__quote@
1883@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pegs.Po@am__quote@
1884@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/penrose.Po@am__quote@
1885@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printing.Po@am__quote@
1886@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ps.Po@am__quote@
1887@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Po@am__quote@
1888@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/range.Po@am__quote@
1889@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rect.Po@am__quote@
1890@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/samegame.Po@am__quote@
1891@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signpost.Po@am__quote@
1892@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/singles.Po@am__quote@
1893@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sixteen.Po@am__quote@
1894@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slant.Po@am__quote@
1895@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/solo.Po@am__quote@
1896@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tdq.Po@am__quote@
1897@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tents.Po@am__quote@
1898@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/towers.Po@am__quote@
1899@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tracks.Po@am__quote@
1900@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree234.Po@am__quote@
1901@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/twiddle.Po@am__quote@
1902@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/undead.Po@am__quote@
1903@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unequal.Po@am__quote@
1904@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unruly.Po@am__quote@
1905@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/untangle.Po@am__quote@
1906@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@
1907
1908.c.o:
1909@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
1910@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
1911@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
1912@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
1913@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1914@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
1915
1916.c.obj:
1917@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
1918@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
1919@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
1920@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
1921@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1922@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
1923
1924./libfifteen2_a-fifteen.o: ./fifteen.c
1925@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfifteen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libfifteen2_a-fifteen.o -MD -MP -MF $(DEPDIR)/libfifteen2_a-fifteen.Tpo -c -o ./libfifteen2_a-fifteen.o `test -f './fifteen.c' || echo '$(srcdir)/'`./fifteen.c
1926@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfifteen2_a-fifteen.Tpo $(DEPDIR)/libfifteen2_a-fifteen.Po
1927@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./fifteen.c' object='./libfifteen2_a-fifteen.o' libtool=no @AMDEPBACKSLASH@
1928@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1929@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfifteen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libfifteen2_a-fifteen.o `test -f './fifteen.c' || echo '$(srcdir)/'`./fifteen.c
1930
1931./libfifteen2_a-fifteen.obj: ./fifteen.c
1932@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfifteen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libfifteen2_a-fifteen.obj -MD -MP -MF $(DEPDIR)/libfifteen2_a-fifteen.Tpo -c -o ./libfifteen2_a-fifteen.obj `if test -f './fifteen.c'; then $(CYGPATH_W) './fifteen.c'; else $(CYGPATH_W) '$(srcdir)/./fifteen.c'; fi`
1933@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfifteen2_a-fifteen.Tpo $(DEPDIR)/libfifteen2_a-fifteen.Po
1934@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./fifteen.c' object='./libfifteen2_a-fifteen.obj' libtool=no @AMDEPBACKSLASH@
1935@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1936@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfifteen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libfifteen2_a-fifteen.obj `if test -f './fifteen.c'; then $(CYGPATH_W) './fifteen.c'; else $(CYGPATH_W) '$(srcdir)/./fifteen.c'; fi`
1937
1938./libfilling2_a-filling.o: ./filling.c
1939@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfilling2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libfilling2_a-filling.o -MD -MP -MF $(DEPDIR)/libfilling2_a-filling.Tpo -c -o ./libfilling2_a-filling.o `test -f './filling.c' || echo '$(srcdir)/'`./filling.c
1940@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfilling2_a-filling.Tpo $(DEPDIR)/libfilling2_a-filling.Po
1941@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./filling.c' object='./libfilling2_a-filling.o' libtool=no @AMDEPBACKSLASH@
1942@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1943@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfilling2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libfilling2_a-filling.o `test -f './filling.c' || echo '$(srcdir)/'`./filling.c
1944
1945./libfilling2_a-filling.obj: ./filling.c
1946@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfilling2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libfilling2_a-filling.obj -MD -MP -MF $(DEPDIR)/libfilling2_a-filling.Tpo -c -o ./libfilling2_a-filling.obj `if test -f './filling.c'; then $(CYGPATH_W) './filling.c'; else $(CYGPATH_W) '$(srcdir)/./filling.c'; fi`
1947@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfilling2_a-filling.Tpo $(DEPDIR)/libfilling2_a-filling.Po
1948@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./filling.c' object='./libfilling2_a-filling.obj' libtool=no @AMDEPBACKSLASH@
1949@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1950@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfilling2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libfilling2_a-filling.obj `if test -f './filling.c'; then $(CYGPATH_W) './filling.c'; else $(CYGPATH_W) '$(srcdir)/./filling.c'; fi`
1951
1952./libgalaxie2_a-galaxies.o: ./galaxies.c
1953@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libgalaxie2_a-galaxies.o -MD -MP -MF $(DEPDIR)/libgalaxie2_a-galaxies.Tpo -c -o ./libgalaxie2_a-galaxies.o `test -f './galaxies.c' || echo '$(srcdir)/'`./galaxies.c
1954@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgalaxie2_a-galaxies.Tpo $(DEPDIR)/libgalaxie2_a-galaxies.Po
1955@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./galaxies.c' object='./libgalaxie2_a-galaxies.o' libtool=no @AMDEPBACKSLASH@
1956@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1957@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libgalaxie2_a-galaxies.o `test -f './galaxies.c' || echo '$(srcdir)/'`./galaxies.c
1958
1959./libgalaxie2_a-galaxies.obj: ./galaxies.c
1960@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libgalaxie2_a-galaxies.obj -MD -MP -MF $(DEPDIR)/libgalaxie2_a-galaxies.Tpo -c -o ./libgalaxie2_a-galaxies.obj `if test -f './galaxies.c'; then $(CYGPATH_W) './galaxies.c'; else $(CYGPATH_W) '$(srcdir)/./galaxies.c'; fi`
1961@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgalaxie2_a-galaxies.Tpo $(DEPDIR)/libgalaxie2_a-galaxies.Po
1962@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./galaxies.c' object='./libgalaxie2_a-galaxies.obj' libtool=no @AMDEPBACKSLASH@
1963@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1964@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libgalaxie2_a-galaxies.obj `if test -f './galaxies.c'; then $(CYGPATH_W) './galaxies.c'; else $(CYGPATH_W) '$(srcdir)/./galaxies.c'; fi`
1965
1966./libgalaxie4_a-galaxies.o: ./galaxies.c
1967@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libgalaxie4_a-galaxies.o -MD -MP -MF $(DEPDIR)/libgalaxie4_a-galaxies.Tpo -c -o ./libgalaxie4_a-galaxies.o `test -f './galaxies.c' || echo '$(srcdir)/'`./galaxies.c
1968@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgalaxie4_a-galaxies.Tpo $(DEPDIR)/libgalaxie4_a-galaxies.Po
1969@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./galaxies.c' object='./libgalaxie4_a-galaxies.o' libtool=no @AMDEPBACKSLASH@
1970@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1971@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libgalaxie4_a-galaxies.o `test -f './galaxies.c' || echo '$(srcdir)/'`./galaxies.c
1972
1973./libgalaxie4_a-galaxies.obj: ./galaxies.c
1974@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libgalaxie4_a-galaxies.obj -MD -MP -MF $(DEPDIR)/libgalaxie4_a-galaxies.Tpo -c -o ./libgalaxie4_a-galaxies.obj `if test -f './galaxies.c'; then $(CYGPATH_W) './galaxies.c'; else $(CYGPATH_W) '$(srcdir)/./galaxies.c'; fi`
1975@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgalaxie4_a-galaxies.Tpo $(DEPDIR)/libgalaxie4_a-galaxies.Po
1976@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./galaxies.c' object='./libgalaxie4_a-galaxies.obj' libtool=no @AMDEPBACKSLASH@
1977@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1978@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgalaxie4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libgalaxie4_a-galaxies.obj `if test -f './galaxies.c'; then $(CYGPATH_W) './galaxies.c'; else $(CYGPATH_W) '$(srcdir)/./galaxies.c'; fi`
1979
1980./libkeen2_a-keen.o: ./keen.c
1981@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkeen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libkeen2_a-keen.o -MD -MP -MF $(DEPDIR)/libkeen2_a-keen.Tpo -c -o ./libkeen2_a-keen.o `test -f './keen.c' || echo '$(srcdir)/'`./keen.c
1982@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkeen2_a-keen.Tpo $(DEPDIR)/libkeen2_a-keen.Po
1983@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./keen.c' object='./libkeen2_a-keen.o' libtool=no @AMDEPBACKSLASH@
1984@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1985@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkeen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libkeen2_a-keen.o `test -f './keen.c' || echo '$(srcdir)/'`./keen.c
1986
1987./libkeen2_a-keen.obj: ./keen.c
1988@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkeen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libkeen2_a-keen.obj -MD -MP -MF $(DEPDIR)/libkeen2_a-keen.Tpo -c -o ./libkeen2_a-keen.obj `if test -f './keen.c'; then $(CYGPATH_W) './keen.c'; else $(CYGPATH_W) '$(srcdir)/./keen.c'; fi`
1989@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkeen2_a-keen.Tpo $(DEPDIR)/libkeen2_a-keen.Po
1990@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./keen.c' object='./libkeen2_a-keen.obj' libtool=no @AMDEPBACKSLASH@
1991@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1992@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkeen2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libkeen2_a-keen.obj `if test -f './keen.c'; then $(CYGPATH_W) './keen.c'; else $(CYGPATH_W) '$(srcdir)/./keen.c'; fi`
1993
1994./liblatin6_a-latin.o: ./latin.c
1995@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin6_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./liblatin6_a-latin.o -MD -MP -MF $(DEPDIR)/liblatin6_a-latin.Tpo -c -o ./liblatin6_a-latin.o `test -f './latin.c' || echo '$(srcdir)/'`./latin.c
1996@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblatin6_a-latin.Tpo $(DEPDIR)/liblatin6_a-latin.Po
1997@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./latin.c' object='./liblatin6_a-latin.o' libtool=no @AMDEPBACKSLASH@
1998@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1999@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin6_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./liblatin6_a-latin.o `test -f './latin.c' || echo '$(srcdir)/'`./latin.c
2000
2001./liblatin6_a-latin.obj: ./latin.c
2002@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin6_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./liblatin6_a-latin.obj -MD -MP -MF $(DEPDIR)/liblatin6_a-latin.Tpo -c -o ./liblatin6_a-latin.obj `if test -f './latin.c'; then $(CYGPATH_W) './latin.c'; else $(CYGPATH_W) '$(srcdir)/./latin.c'; fi`
2003@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblatin6_a-latin.Tpo $(DEPDIR)/liblatin6_a-latin.Po
2004@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./latin.c' object='./liblatin6_a-latin.obj' libtool=no @AMDEPBACKSLASH@
2005@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2006@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin6_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./liblatin6_a-latin.obj `if test -f './latin.c'; then $(CYGPATH_W) './latin.c'; else $(CYGPATH_W) '$(srcdir)/./latin.c'; fi`
2007
2008./liblatin8_a-latin.o: ./latin.c
2009@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin8_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./liblatin8_a-latin.o -MD -MP -MF $(DEPDIR)/liblatin8_a-latin.Tpo -c -o ./liblatin8_a-latin.o `test -f './latin.c' || echo '$(srcdir)/'`./latin.c
2010@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblatin8_a-latin.Tpo $(DEPDIR)/liblatin8_a-latin.Po
2011@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./latin.c' object='./liblatin8_a-latin.o' libtool=no @AMDEPBACKSLASH@
2012@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2013@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin8_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./liblatin8_a-latin.o `test -f './latin.c' || echo '$(srcdir)/'`./latin.c
2014
2015./liblatin8_a-latin.obj: ./latin.c
2016@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin8_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./liblatin8_a-latin.obj -MD -MP -MF $(DEPDIR)/liblatin8_a-latin.Tpo -c -o ./liblatin8_a-latin.obj `if test -f './latin.c'; then $(CYGPATH_W) './latin.c'; else $(CYGPATH_W) '$(srcdir)/./latin.c'; fi`
2017@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblatin8_a-latin.Tpo $(DEPDIR)/liblatin8_a-latin.Po
2018@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./latin.c' object='./liblatin8_a-latin.obj' libtool=no @AMDEPBACKSLASH@
2019@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2020@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblatin8_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./liblatin8_a-latin.obj `if test -f './latin.c'; then $(CYGPATH_W) './latin.c'; else $(CYGPATH_W) '$(srcdir)/./latin.c'; fi`
2021
2022./liblightup2_a-lightup.o: ./lightup.c
2023@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblightup2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./liblightup2_a-lightup.o -MD -MP -MF $(DEPDIR)/liblightup2_a-lightup.Tpo -c -o ./liblightup2_a-lightup.o `test -f './lightup.c' || echo '$(srcdir)/'`./lightup.c
2024@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightup2_a-lightup.Tpo $(DEPDIR)/liblightup2_a-lightup.Po
2025@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./lightup.c' object='./liblightup2_a-lightup.o' libtool=no @AMDEPBACKSLASH@
2026@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2027@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblightup2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./liblightup2_a-lightup.o `test -f './lightup.c' || echo '$(srcdir)/'`./lightup.c
2028
2029./liblightup2_a-lightup.obj: ./lightup.c
2030@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblightup2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./liblightup2_a-lightup.obj -MD -MP -MF $(DEPDIR)/liblightup2_a-lightup.Tpo -c -o ./liblightup2_a-lightup.obj `if test -f './lightup.c'; then $(CYGPATH_W) './lightup.c'; else $(CYGPATH_W) '$(srcdir)/./lightup.c'; fi`
2031@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightup2_a-lightup.Tpo $(DEPDIR)/liblightup2_a-lightup.Po
2032@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./lightup.c' object='./liblightup2_a-lightup.obj' libtool=no @AMDEPBACKSLASH@
2033@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2034@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblightup2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./liblightup2_a-lightup.obj `if test -f './lightup.c'; then $(CYGPATH_W) './lightup.c'; else $(CYGPATH_W) '$(srcdir)/./lightup.c'; fi`
2035
2036./libloopy2_a-loopy.o: ./loopy.c
2037@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libloopy2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libloopy2_a-loopy.o -MD -MP -MF $(DEPDIR)/libloopy2_a-loopy.Tpo -c -o ./libloopy2_a-loopy.o `test -f './loopy.c' || echo '$(srcdir)/'`./loopy.c
2038@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libloopy2_a-loopy.Tpo $(DEPDIR)/libloopy2_a-loopy.Po
2039@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./loopy.c' object='./libloopy2_a-loopy.o' libtool=no @AMDEPBACKSLASH@
2040@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2041@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libloopy2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libloopy2_a-loopy.o `test -f './loopy.c' || echo '$(srcdir)/'`./loopy.c
2042
2043./libloopy2_a-loopy.obj: ./loopy.c
2044@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libloopy2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libloopy2_a-loopy.obj -MD -MP -MF $(DEPDIR)/libloopy2_a-loopy.Tpo -c -o ./libloopy2_a-loopy.obj `if test -f './loopy.c'; then $(CYGPATH_W) './loopy.c'; else $(CYGPATH_W) '$(srcdir)/./loopy.c'; fi`
2045@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libloopy2_a-loopy.Tpo $(DEPDIR)/libloopy2_a-loopy.Po
2046@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./loopy.c' object='./libloopy2_a-loopy.obj' libtool=no @AMDEPBACKSLASH@
2047@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2048@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libloopy2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libloopy2_a-loopy.obj `if test -f './loopy.c'; then $(CYGPATH_W) './loopy.c'; else $(CYGPATH_W) '$(srcdir)/./loopy.c'; fi`
2049
2050./libmagnets2_a-magnets.o: ./magnets.c
2051@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmagnets2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libmagnets2_a-magnets.o -MD -MP -MF $(DEPDIR)/libmagnets2_a-magnets.Tpo -c -o ./libmagnets2_a-magnets.o `test -f './magnets.c' || echo '$(srcdir)/'`./magnets.c
2052@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmagnets2_a-magnets.Tpo $(DEPDIR)/libmagnets2_a-magnets.Po
2053@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./magnets.c' object='./libmagnets2_a-magnets.o' libtool=no @AMDEPBACKSLASH@
2054@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2055@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmagnets2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libmagnets2_a-magnets.o `test -f './magnets.c' || echo '$(srcdir)/'`./magnets.c
2056
2057./libmagnets2_a-magnets.obj: ./magnets.c
2058@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmagnets2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libmagnets2_a-magnets.obj -MD -MP -MF $(DEPDIR)/libmagnets2_a-magnets.Tpo -c -o ./libmagnets2_a-magnets.obj `if test -f './magnets.c'; then $(CYGPATH_W) './magnets.c'; else $(CYGPATH_W) '$(srcdir)/./magnets.c'; fi`
2059@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmagnets2_a-magnets.Tpo $(DEPDIR)/libmagnets2_a-magnets.Po
2060@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./magnets.c' object='./libmagnets2_a-magnets.obj' libtool=no @AMDEPBACKSLASH@
2061@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2062@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmagnets2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libmagnets2_a-magnets.obj `if test -f './magnets.c'; then $(CYGPATH_W) './magnets.c'; else $(CYGPATH_W) '$(srcdir)/./magnets.c'; fi`
2063
2064./libmap2_a-map.o: ./map.c
2065@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmap2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libmap2_a-map.o -MD -MP -MF $(DEPDIR)/libmap2_a-map.Tpo -c -o ./libmap2_a-map.o `test -f './map.c' || echo '$(srcdir)/'`./map.c
2066@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmap2_a-map.Tpo $(DEPDIR)/libmap2_a-map.Po
2067@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./map.c' object='./libmap2_a-map.o' libtool=no @AMDEPBACKSLASH@
2068@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2069@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmap2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libmap2_a-map.o `test -f './map.c' || echo '$(srcdir)/'`./map.c
2070
2071./libmap2_a-map.obj: ./map.c
2072@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmap2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libmap2_a-map.obj -MD -MP -MF $(DEPDIR)/libmap2_a-map.Tpo -c -o ./libmap2_a-map.obj `if test -f './map.c'; then $(CYGPATH_W) './map.c'; else $(CYGPATH_W) '$(srcdir)/./map.c'; fi`
2073@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmap2_a-map.Tpo $(DEPDIR)/libmap2_a-map.Po
2074@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./map.c' object='./libmap2_a-map.obj' libtool=no @AMDEPBACKSLASH@
2075@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2076@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmap2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libmap2_a-map.obj `if test -f './map.c'; then $(CYGPATH_W) './map.c'; else $(CYGPATH_W) '$(srcdir)/./map.c'; fi`
2077
2078./libmines2_a-mines.o: ./mines.c
2079@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmines2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libmines2_a-mines.o -MD -MP -MF $(DEPDIR)/libmines2_a-mines.Tpo -c -o ./libmines2_a-mines.o `test -f './mines.c' || echo '$(srcdir)/'`./mines.c
2080@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmines2_a-mines.Tpo $(DEPDIR)/libmines2_a-mines.Po
2081@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./mines.c' object='./libmines2_a-mines.o' libtool=no @AMDEPBACKSLASH@
2082@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2083@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmines2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libmines2_a-mines.o `test -f './mines.c' || echo '$(srcdir)/'`./mines.c
2084
2085./libmines2_a-mines.obj: ./mines.c
2086@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmines2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libmines2_a-mines.obj -MD -MP -MF $(DEPDIR)/libmines2_a-mines.Tpo -c -o ./libmines2_a-mines.obj `if test -f './mines.c'; then $(CYGPATH_W) './mines.c'; else $(CYGPATH_W) '$(srcdir)/./mines.c'; fi`
2087@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmines2_a-mines.Tpo $(DEPDIR)/libmines2_a-mines.Po
2088@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./mines.c' object='./libmines2_a-mines.obj' libtool=no @AMDEPBACKSLASH@
2089@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2090@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmines2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libmines2_a-mines.obj `if test -f './mines.c'; then $(CYGPATH_W) './mines.c'; else $(CYGPATH_W) '$(srcdir)/./mines.c'; fi`
2091
2092./libpattern2_a-pattern.o: ./pattern.c
2093@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libpattern2_a-pattern.o -MD -MP -MF $(DEPDIR)/libpattern2_a-pattern.Tpo -c -o ./libpattern2_a-pattern.o `test -f './pattern.c' || echo '$(srcdir)/'`./pattern.c
2094@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpattern2_a-pattern.Tpo $(DEPDIR)/libpattern2_a-pattern.Po
2095@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./pattern.c' object='./libpattern2_a-pattern.o' libtool=no @AMDEPBACKSLASH@
2096@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2097@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libpattern2_a-pattern.o `test -f './pattern.c' || echo '$(srcdir)/'`./pattern.c
2098
2099./libpattern2_a-pattern.obj: ./pattern.c
2100@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libpattern2_a-pattern.obj -MD -MP -MF $(DEPDIR)/libpattern2_a-pattern.Tpo -c -o ./libpattern2_a-pattern.obj `if test -f './pattern.c'; then $(CYGPATH_W) './pattern.c'; else $(CYGPATH_W) '$(srcdir)/./pattern.c'; fi`
2101@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpattern2_a-pattern.Tpo $(DEPDIR)/libpattern2_a-pattern.Po
2102@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./pattern.c' object='./libpattern2_a-pattern.obj' libtool=no @AMDEPBACKSLASH@
2103@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2104@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libpattern2_a-pattern.obj `if test -f './pattern.c'; then $(CYGPATH_W) './pattern.c'; else $(CYGPATH_W) '$(srcdir)/./pattern.c'; fi`
2105
2106./libpattern4_a-pattern.o: ./pattern.c
2107@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libpattern4_a-pattern.o -MD -MP -MF $(DEPDIR)/libpattern4_a-pattern.Tpo -c -o ./libpattern4_a-pattern.o `test -f './pattern.c' || echo '$(srcdir)/'`./pattern.c
2108@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpattern4_a-pattern.Tpo $(DEPDIR)/libpattern4_a-pattern.Po
2109@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./pattern.c' object='./libpattern4_a-pattern.o' libtool=no @AMDEPBACKSLASH@
2110@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2111@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libpattern4_a-pattern.o `test -f './pattern.c' || echo '$(srcdir)/'`./pattern.c
2112
2113./libpattern4_a-pattern.obj: ./pattern.c
2114@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libpattern4_a-pattern.obj -MD -MP -MF $(DEPDIR)/libpattern4_a-pattern.Tpo -c -o ./libpattern4_a-pattern.obj `if test -f './pattern.c'; then $(CYGPATH_W) './pattern.c'; else $(CYGPATH_W) '$(srcdir)/./pattern.c'; fi`
2115@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpattern4_a-pattern.Tpo $(DEPDIR)/libpattern4_a-pattern.Po
2116@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./pattern.c' object='./libpattern4_a-pattern.obj' libtool=no @AMDEPBACKSLASH@
2117@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2118@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpattern4_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libpattern4_a-pattern.obj `if test -f './pattern.c'; then $(CYGPATH_W) './pattern.c'; else $(CYGPATH_W) '$(srcdir)/./pattern.c'; fi`
2119
2120./libpearl2_a-pearl.o: ./pearl.c
2121@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpearl2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libpearl2_a-pearl.o -MD -MP -MF $(DEPDIR)/libpearl2_a-pearl.Tpo -c -o ./libpearl2_a-pearl.o `test -f './pearl.c' || echo '$(srcdir)/'`./pearl.c
2122@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpearl2_a-pearl.Tpo $(DEPDIR)/libpearl2_a-pearl.Po
2123@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./pearl.c' object='./libpearl2_a-pearl.o' libtool=no @AMDEPBACKSLASH@
2124@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2125@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpearl2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libpearl2_a-pearl.o `test -f './pearl.c' || echo '$(srcdir)/'`./pearl.c
2126
2127./libpearl2_a-pearl.obj: ./pearl.c
2128@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpearl2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libpearl2_a-pearl.obj -MD -MP -MF $(DEPDIR)/libpearl2_a-pearl.Tpo -c -o ./libpearl2_a-pearl.obj `if test -f './pearl.c'; then $(CYGPATH_W) './pearl.c'; else $(CYGPATH_W) '$(srcdir)/./pearl.c'; fi`
2129@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpearl2_a-pearl.Tpo $(DEPDIR)/libpearl2_a-pearl.Po
2130@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./pearl.c' object='./libpearl2_a-pearl.obj' libtool=no @AMDEPBACKSLASH@
2131@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2132@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpearl2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libpearl2_a-pearl.obj `if test -f './pearl.c'; then $(CYGPATH_W) './pearl.c'; else $(CYGPATH_W) '$(srcdir)/./pearl.c'; fi`
2133
2134./libsignpos2_a-signpost.o: ./signpost.c
2135@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsignpos2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libsignpos2_a-signpost.o -MD -MP -MF $(DEPDIR)/libsignpos2_a-signpost.Tpo -c -o ./libsignpos2_a-signpost.o `test -f './signpost.c' || echo '$(srcdir)/'`./signpost.c
2136@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsignpos2_a-signpost.Tpo $(DEPDIR)/libsignpos2_a-signpost.Po
2137@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./signpost.c' object='./libsignpos2_a-signpost.o' libtool=no @AMDEPBACKSLASH@
2138@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2139@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsignpos2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libsignpos2_a-signpost.o `test -f './signpost.c' || echo '$(srcdir)/'`./signpost.c
2140
2141./libsignpos2_a-signpost.obj: ./signpost.c
2142@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsignpos2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libsignpos2_a-signpost.obj -MD -MP -MF $(DEPDIR)/libsignpos2_a-signpost.Tpo -c -o ./libsignpos2_a-signpost.obj `if test -f './signpost.c'; then $(CYGPATH_W) './signpost.c'; else $(CYGPATH_W) '$(srcdir)/./signpost.c'; fi`
2143@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsignpos2_a-signpost.Tpo $(DEPDIR)/libsignpos2_a-signpost.Po
2144@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./signpost.c' object='./libsignpos2_a-signpost.obj' libtool=no @AMDEPBACKSLASH@
2145@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2146@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsignpos2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libsignpos2_a-signpost.obj `if test -f './signpost.c'; then $(CYGPATH_W) './signpost.c'; else $(CYGPATH_W) '$(srcdir)/./signpost.c'; fi`
2147
2148./libsingles3_a-singles.o: ./singles.c
2149@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsingles3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libsingles3_a-singles.o -MD -MP -MF $(DEPDIR)/libsingles3_a-singles.Tpo -c -o ./libsingles3_a-singles.o `test -f './singles.c' || echo '$(srcdir)/'`./singles.c
2150@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsingles3_a-singles.Tpo $(DEPDIR)/libsingles3_a-singles.Po
2151@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./singles.c' object='./libsingles3_a-singles.o' libtool=no @AMDEPBACKSLASH@
2152@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2153@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsingles3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libsingles3_a-singles.o `test -f './singles.c' || echo '$(srcdir)/'`./singles.c
2154
2155./libsingles3_a-singles.obj: ./singles.c
2156@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsingles3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libsingles3_a-singles.obj -MD -MP -MF $(DEPDIR)/libsingles3_a-singles.Tpo -c -o ./libsingles3_a-singles.obj `if test -f './singles.c'; then $(CYGPATH_W) './singles.c'; else $(CYGPATH_W) '$(srcdir)/./singles.c'; fi`
2157@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsingles3_a-singles.Tpo $(DEPDIR)/libsingles3_a-singles.Po
2158@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./singles.c' object='./libsingles3_a-singles.obj' libtool=no @AMDEPBACKSLASH@
2159@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2160@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsingles3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libsingles3_a-singles.obj `if test -f './singles.c'; then $(CYGPATH_W) './singles.c'; else $(CYGPATH_W) '$(srcdir)/./singles.c'; fi`
2161
2162./libslant2_a-slant.o: ./slant.c
2163@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libslant2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libslant2_a-slant.o -MD -MP -MF $(DEPDIR)/libslant2_a-slant.Tpo -c -o ./libslant2_a-slant.o `test -f './slant.c' || echo '$(srcdir)/'`./slant.c
2164@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libslant2_a-slant.Tpo $(DEPDIR)/libslant2_a-slant.Po
2165@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./slant.c' object='./libslant2_a-slant.o' libtool=no @AMDEPBACKSLASH@
2166@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2167@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libslant2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libslant2_a-slant.o `test -f './slant.c' || echo '$(srcdir)/'`./slant.c
2168
2169./libslant2_a-slant.obj: ./slant.c
2170@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libslant2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libslant2_a-slant.obj -MD -MP -MF $(DEPDIR)/libslant2_a-slant.Tpo -c -o ./libslant2_a-slant.obj `if test -f './slant.c'; then $(CYGPATH_W) './slant.c'; else $(CYGPATH_W) '$(srcdir)/./slant.c'; fi`
2171@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libslant2_a-slant.Tpo $(DEPDIR)/libslant2_a-slant.Po
2172@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./slant.c' object='./libslant2_a-slant.obj' libtool=no @AMDEPBACKSLASH@
2173@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2174@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libslant2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libslant2_a-slant.obj `if test -f './slant.c'; then $(CYGPATH_W) './slant.c'; else $(CYGPATH_W) '$(srcdir)/./slant.c'; fi`
2175
2176./libsolo2_a-solo.o: ./solo.c
2177@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsolo2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libsolo2_a-solo.o -MD -MP -MF $(DEPDIR)/libsolo2_a-solo.Tpo -c -o ./libsolo2_a-solo.o `test -f './solo.c' || echo '$(srcdir)/'`./solo.c
2178@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsolo2_a-solo.Tpo $(DEPDIR)/libsolo2_a-solo.Po
2179@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./solo.c' object='./libsolo2_a-solo.o' libtool=no @AMDEPBACKSLASH@
2180@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2181@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsolo2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libsolo2_a-solo.o `test -f './solo.c' || echo '$(srcdir)/'`./solo.c
2182
2183./libsolo2_a-solo.obj: ./solo.c
2184@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsolo2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libsolo2_a-solo.obj -MD -MP -MF $(DEPDIR)/libsolo2_a-solo.Tpo -c -o ./libsolo2_a-solo.obj `if test -f './solo.c'; then $(CYGPATH_W) './solo.c'; else $(CYGPATH_W) '$(srcdir)/./solo.c'; fi`
2185@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsolo2_a-solo.Tpo $(DEPDIR)/libsolo2_a-solo.Po
2186@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./solo.c' object='./libsolo2_a-solo.obj' libtool=no @AMDEPBACKSLASH@
2187@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2188@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsolo2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libsolo2_a-solo.obj `if test -f './solo.c'; then $(CYGPATH_W) './solo.c'; else $(CYGPATH_W) '$(srcdir)/./solo.c'; fi`
2189
2190./libtents3_a-tents.o: ./tents.c
2191@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtents3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libtents3_a-tents.o -MD -MP -MF $(DEPDIR)/libtents3_a-tents.Tpo -c -o ./libtents3_a-tents.o `test -f './tents.c' || echo '$(srcdir)/'`./tents.c
2192@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtents3_a-tents.Tpo $(DEPDIR)/libtents3_a-tents.Po
2193@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./tents.c' object='./libtents3_a-tents.o' libtool=no @AMDEPBACKSLASH@
2194@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2195@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtents3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libtents3_a-tents.o `test -f './tents.c' || echo '$(srcdir)/'`./tents.c
2196
2197./libtents3_a-tents.obj: ./tents.c
2198@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtents3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libtents3_a-tents.obj -MD -MP -MF $(DEPDIR)/libtents3_a-tents.Tpo -c -o ./libtents3_a-tents.obj `if test -f './tents.c'; then $(CYGPATH_W) './tents.c'; else $(CYGPATH_W) '$(srcdir)/./tents.c'; fi`
2199@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtents3_a-tents.Tpo $(DEPDIR)/libtents3_a-tents.Po
2200@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./tents.c' object='./libtents3_a-tents.obj' libtool=no @AMDEPBACKSLASH@
2201@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2202@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtents3_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libtents3_a-tents.obj `if test -f './tents.c'; then $(CYGPATH_W) './tents.c'; else $(CYGPATH_W) '$(srcdir)/./tents.c'; fi`
2203
2204./libtowers2_a-towers.o: ./towers.c
2205@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtowers2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libtowers2_a-towers.o -MD -MP -MF $(DEPDIR)/libtowers2_a-towers.Tpo -c -o ./libtowers2_a-towers.o `test -f './towers.c' || echo '$(srcdir)/'`./towers.c
2206@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtowers2_a-towers.Tpo $(DEPDIR)/libtowers2_a-towers.Po
2207@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./towers.c' object='./libtowers2_a-towers.o' libtool=no @AMDEPBACKSLASH@
2208@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2209@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtowers2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libtowers2_a-towers.o `test -f './towers.c' || echo '$(srcdir)/'`./towers.c
2210
2211./libtowers2_a-towers.obj: ./towers.c
2212@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtowers2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libtowers2_a-towers.obj -MD -MP -MF $(DEPDIR)/libtowers2_a-towers.Tpo -c -o ./libtowers2_a-towers.obj `if test -f './towers.c'; then $(CYGPATH_W) './towers.c'; else $(CYGPATH_W) '$(srcdir)/./towers.c'; fi`
2213@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtowers2_a-towers.Tpo $(DEPDIR)/libtowers2_a-towers.Po
2214@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./towers.c' object='./libtowers2_a-towers.obj' libtool=no @AMDEPBACKSLASH@
2215@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2216@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtowers2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libtowers2_a-towers.obj `if test -f './towers.c'; then $(CYGPATH_W) './towers.c'; else $(CYGPATH_W) '$(srcdir)/./towers.c'; fi`
2217
2218./libunequal2_a-unequal.o: ./unequal.c
2219@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunequal2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libunequal2_a-unequal.o -MD -MP -MF $(DEPDIR)/libunequal2_a-unequal.Tpo -c -o ./libunequal2_a-unequal.o `test -f './unequal.c' || echo '$(srcdir)/'`./unequal.c
2220@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libunequal2_a-unequal.Tpo $(DEPDIR)/libunequal2_a-unequal.Po
2221@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./unequal.c' object='./libunequal2_a-unequal.o' libtool=no @AMDEPBACKSLASH@
2222@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2223@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunequal2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libunequal2_a-unequal.o `test -f './unequal.c' || echo '$(srcdir)/'`./unequal.c
2224
2225./libunequal2_a-unequal.obj: ./unequal.c
2226@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunequal2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libunequal2_a-unequal.obj -MD -MP -MF $(DEPDIR)/libunequal2_a-unequal.Tpo -c -o ./libunequal2_a-unequal.obj `if test -f './unequal.c'; then $(CYGPATH_W) './unequal.c'; else $(CYGPATH_W) '$(srcdir)/./unequal.c'; fi`
2227@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libunequal2_a-unequal.Tpo $(DEPDIR)/libunequal2_a-unequal.Po
2228@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./unequal.c' object='./libunequal2_a-unequal.obj' libtool=no @AMDEPBACKSLASH@
2229@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2230@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunequal2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libunequal2_a-unequal.obj `if test -f './unequal.c'; then $(CYGPATH_W) './unequal.c'; else $(CYGPATH_W) '$(srcdir)/./unequal.c'; fi`
2231
2232./libunruly2_a-unruly.o: ./unruly.c
2233@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunruly2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libunruly2_a-unruly.o -MD -MP -MF $(DEPDIR)/libunruly2_a-unruly.Tpo -c -o ./libunruly2_a-unruly.o `test -f './unruly.c' || echo '$(srcdir)/'`./unruly.c
2234@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libunruly2_a-unruly.Tpo $(DEPDIR)/libunruly2_a-unruly.Po
2235@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./unruly.c' object='./libunruly2_a-unruly.o' libtool=no @AMDEPBACKSLASH@
2236@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2237@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunruly2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libunruly2_a-unruly.o `test -f './unruly.c' || echo '$(srcdir)/'`./unruly.c
2238
2239./libunruly2_a-unruly.obj: ./unruly.c
2240@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunruly2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ./libunruly2_a-unruly.obj -MD -MP -MF $(DEPDIR)/libunruly2_a-unruly.Tpo -c -o ./libunruly2_a-unruly.obj `if test -f './unruly.c'; then $(CYGPATH_W) './unruly.c'; else $(CYGPATH_W) '$(srcdir)/./unruly.c'; fi`
2241@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libunruly2_a-unruly.Tpo $(DEPDIR)/libunruly2_a-unruly.Po
2242@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='./unruly.c' object='./libunruly2_a-unruly.obj' libtool=no @AMDEPBACKSLASH@
2243@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2244@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libunruly2_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ./libunruly2_a-unruly.obj `if test -f './unruly.c'; then $(CYGPATH_W) './unruly.c'; else $(CYGPATH_W) '$(srcdir)/./unruly.c'; fi`
2245
2246ID: $(am__tagged_files)
2247 $(am__define_uniq_tagged_files); mkid -fID $$unique
2248tags: tags-am
2249TAGS: tags
2250
2251tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
2252 set x; \
2253 here=`pwd`; \
2254 $(am__define_uniq_tagged_files); \
2255 shift; \
2256 if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
2257 test -n "$$unique" || unique=$$empty_fix; \
2258 if test $$# -gt 0; then \
2259 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
2260 "$$@" $$unique; \
2261 else \
2262 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
2263 $$unique; \
2264 fi; \
2265 fi
2266ctags: ctags-am
2267
2268CTAGS: ctags
2269ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
2270 $(am__define_uniq_tagged_files); \
2271 test -z "$(CTAGS_ARGS)$$unique" \
2272 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
2273 $$unique
2274
2275GTAGS:
2276 here=`$(am__cd) $(top_builddir) && pwd` \
2277 && $(am__cd) $(top_srcdir) \
2278 && gtags -i $(GTAGS_ARGS) "$$here"
2279cscope: cscope.files
2280 test ! -s cscope.files \
2281 || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
2282clean-cscope:
2283 -rm -f cscope.files
2284cscope.files: clean-cscope cscopelist
2285cscopelist: cscopelist-am
2286
2287cscopelist-am: $(am__tagged_files)
2288 list='$(am__tagged_files)'; \
2289 case "$(srcdir)" in \
2290 [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
2291 *) sdir=$(subdir)/$(srcdir) ;; \
2292 esac; \
2293 for i in $$list; do \
2294 if test -f "$$i"; then \
2295 echo "$(subdir)/$$i"; \
2296 else \
2297 echo "$$sdir/$$i"; \
2298 fi; \
2299 done >> $(top_builddir)/cscope.files
2300
2301distclean-tags:
2302 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
2303 -rm -f cscope.out cscope.in.out cscope.po.out cscope.files
2304
2305distdir: $(DISTFILES)
2306 $(am__remove_distdir)
2307 test -d "$(distdir)" || mkdir "$(distdir)"
2308 @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
2309 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
2310 list='$(DISTFILES)'; \
2311 dist_files=`for file in $$list; do echo $$file; done | \
2312 sed -e "s|^$$srcdirstrip/||;t" \
2313 -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
2314 case $$dist_files in \
2315 */*) $(MKDIR_P) `echo "$$dist_files" | \
2316 sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
2317 sort -u` ;; \
2318 esac; \
2319 for file in $$dist_files; do \
2320 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
2321 if test -d $$d/$$file; then \
2322 dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
2323 if test -d "$(distdir)/$$file"; then \
2324 find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
2325 fi; \
2326 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
2327 cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
2328 find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
2329 fi; \
2330 cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
2331 else \
2332 test -f "$(distdir)/$$file" \
2333 || cp -p $$d/$$file "$(distdir)/$$file" \
2334 || exit 1; \
2335 fi; \
2336 done
2337 -test -n "$(am__skip_mode_fix)" \
2338 || find "$(distdir)" -type d ! -perm -755 \
2339 -exec chmod u+rwx,go+rx {} \; -o \
2340 ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
2341 ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
2342 ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
2343 || chmod -R a+r "$(distdir)"
2344dist-gzip: distdir
2345 tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
2346 $(am__post_remove_distdir)
2347
2348dist-bzip2: distdir
2349 tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
2350 $(am__post_remove_distdir)
2351
2352dist-lzip: distdir
2353 tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
2354 $(am__post_remove_distdir)
2355
2356dist-xz: distdir
2357 tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
2358 $(am__post_remove_distdir)
2359
2360dist-tarZ: distdir
2361 @echo WARNING: "Support for distribution archives compressed with" \
2362 "legacy program 'compress' is deprecated." >&2
2363 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
2364 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
2365 $(am__post_remove_distdir)
2366
2367dist-shar: distdir
2368 @echo WARNING: "Support for shar distribution archives is" \
2369 "deprecated." >&2
2370 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
2371 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
2372 $(am__post_remove_distdir)
2373
2374dist-zip: distdir
2375 -rm -f $(distdir).zip
2376 zip -rq $(distdir).zip $(distdir)
2377 $(am__post_remove_distdir)
2378
2379dist dist-all:
2380 $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
2381 $(am__post_remove_distdir)
2382
2383# This target untars the dist file and tries a VPATH configuration. Then
2384# it guarantees that the distribution is self-contained by making another
2385# tarfile.
2386distcheck: dist
2387 case '$(DIST_ARCHIVES)' in \
2388 *.tar.gz*) \
2389 eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
2390 *.tar.bz2*) \
2391 bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
2392 *.tar.lz*) \
2393 lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
2394 *.tar.xz*) \
2395 xz -dc $(distdir).tar.xz | $(am__untar) ;;\
2396 *.tar.Z*) \
2397 uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
2398 *.shar.gz*) \
2399 eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
2400 *.zip*) \
2401 unzip $(distdir).zip ;;\
2402 esac
2403 chmod -R a-w $(distdir)
2404 chmod u+w $(distdir)
2405 mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
2406 chmod a-w $(distdir)
2407 test -d $(distdir)/_build || exit 0; \
2408 dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
2409 && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
2410 && am__cwd=`pwd` \
2411 && $(am__cd) $(distdir)/_build/sub \
2412 && ../../configure \
2413 $(AM_DISTCHECK_CONFIGURE_FLAGS) \
2414 $(DISTCHECK_CONFIGURE_FLAGS) \
2415 --srcdir=../.. --prefix="$$dc_install_base" \
2416 && $(MAKE) $(AM_MAKEFLAGS) \
2417 && $(MAKE) $(AM_MAKEFLAGS) dvi \
2418 && $(MAKE) $(AM_MAKEFLAGS) check \
2419 && $(MAKE) $(AM_MAKEFLAGS) install \
2420 && $(MAKE) $(AM_MAKEFLAGS) installcheck \
2421 && $(MAKE) $(AM_MAKEFLAGS) uninstall \
2422 && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
2423 distuninstallcheck \
2424 && chmod -R a-w "$$dc_install_base" \
2425 && ({ \
2426 (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
2427 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
2428 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
2429 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
2430 distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
2431 } || { rm -rf "$$dc_destdir"; exit 1; }) \
2432 && rm -rf "$$dc_destdir" \
2433 && $(MAKE) $(AM_MAKEFLAGS) dist \
2434 && rm -rf $(DIST_ARCHIVES) \
2435 && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
2436 && cd "$$am__cwd" \
2437 || exit 1
2438 $(am__post_remove_distdir)
2439 @(echo "$(distdir) archives ready for distribution: "; \
2440 list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
2441 sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
2442distuninstallcheck:
2443 @test -n '$(distuninstallcheck_dir)' || { \
2444 echo 'ERROR: trying to run $@ with an empty' \
2445 '$$(distuninstallcheck_dir)' >&2; \
2446 exit 1; \
2447 }; \
2448 $(am__cd) '$(distuninstallcheck_dir)' || { \
2449 echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
2450 exit 1; \
2451 }; \
2452 test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
2453 || { echo "ERROR: files left after uninstall:" ; \
2454 if test -n "$(DESTDIR)"; then \
2455 echo " (check DESTDIR support)"; \
2456 fi ; \
2457 $(distuninstallcheck_listfiles) ; \
2458 exit 1; } >&2
2459distcleancheck: distclean
2460 @if test '$(srcdir)' = . ; then \
2461 echo "ERROR: distcleancheck can only run from a VPATH build" ; \
2462 exit 1 ; \
2463 fi
2464 @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
2465 || { echo "ERROR: files left in build directory after distclean:" ; \
2466 $(distcleancheck_listfiles) ; \
2467 exit 1; } >&2
2468check-am: all-am
2469check: check-am
2470all-am: Makefile $(LIBRARIES) $(PROGRAMS)
2471installdirs:
2472 for dir in "$(DESTDIR)$(bindir)"; do \
2473 test -z "$$dir" || $(MKDIR_P) "$$dir"; \
2474 done
2475install: install-am
2476install-exec: install-exec-am
2477install-data: install-data-am
2478uninstall: uninstall-am
2479
2480install-am: all-am
2481 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
2482
2483installcheck: installcheck-am
2484install-strip:
2485 if test -z '$(STRIP)'; then \
2486 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
2487 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
2488 install; \
2489 else \
2490 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
2491 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
2492 "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
2493 fi
2494mostlyclean-generic:
2495
2496clean-generic:
2497
2498distclean-generic:
2499 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
2500 -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
2501 -rm -f ./$(am__dirstamp)
2502 -test -z "$(DEPDIR)/$(am__dirstamp)" || rm -f $(DEPDIR)/$(am__dirstamp)
2503
2504maintainer-clean-generic:
2505 @echo "This command is intended for maintainers to use"
2506 @echo "it deletes files that may require special tools to rebuild."
2507clean: clean-am
2508
2509clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \
2510 clean-noinstPROGRAMS mostlyclean-am
2511
2512distclean: distclean-am
2513 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
2514 -rm -rf ./$(DEPDIR)
2515 -rm -f Makefile
2516distclean-am: clean-am distclean-compile distclean-generic \
2517 distclean-tags
2518
2519dvi: dvi-am
2520
2521dvi-am:
2522
2523html: html-am
2524
2525html-am:
2526
2527info: info-am
2528
2529info-am:
2530
2531install-data-am:
2532
2533install-dvi: install-dvi-am
2534
2535install-dvi-am:
2536
2537install-exec-am: install-binPROGRAMS
2538
2539install-html: install-html-am
2540
2541install-html-am:
2542
2543install-info: install-info-am
2544
2545install-info-am:
2546
2547install-man:
2548
2549install-pdf: install-pdf-am
2550
2551install-pdf-am:
2552
2553install-ps: install-ps-am
2554
2555install-ps-am:
2556
2557installcheck-am:
2558
2559maintainer-clean: maintainer-clean-am
2560 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
2561 -rm -rf $(top_srcdir)/autom4te.cache
2562 -rm -rf ./$(DEPDIR)
2563 -rm -f Makefile
2564maintainer-clean-am: distclean-am maintainer-clean-generic
2565
2566mostlyclean: mostlyclean-am
2567
2568mostlyclean-am: mostlyclean-compile mostlyclean-generic
2569
2570pdf: pdf-am
2571
2572pdf-am:
2573
2574ps: ps-am
2575
2576ps-am:
2577
2578uninstall-am: uninstall-binPROGRAMS
2579
2580.MAKE: install-am install-strip
2581
2582.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-am clean \
2583 clean-binPROGRAMS clean-cscope clean-generic \
2584 clean-noinstLIBRARIES clean-noinstPROGRAMS cscope \
2585 cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \
2586 dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \
2587 distcheck distclean distclean-compile distclean-generic \
2588 distclean-tags distcleancheck distdir distuninstallcheck dvi \
2589 dvi-am html html-am info info-am install install-am \
2590 install-binPROGRAMS install-data install-data-am install-dvi \
2591 install-dvi-am install-exec install-exec-am install-html \
2592 install-html-am install-info install-info-am install-man \
2593 install-pdf install-pdf-am install-ps install-ps-am \
2594 install-strip installcheck installcheck-am installdirs \
2595 maintainer-clean maintainer-clean-generic mostlyclean \
2596 mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
2597 tags tags-am uninstall uninstall-am uninstall-binPROGRAMS
2598
2599.PRECIOUS: Makefile
2600
2601test: benchmark.html benchmark.txt
2602
2603benchmark.html: benchmark.txt benchmark.pl
2604 ./benchmark.pl benchmark.txt > $@
2605
2606benchmark.txt: benchmark.sh $(GAMES)
2607 ./benchmark.sh > $@
2608
2609# Tell versions [3.59,3.63) of GNU make to not export all variables.
2610# Otherwise a system limit (for SysV at least) may be exceeded.
2611.NOEXPORT:
diff --git a/apps/plugins/puzzles/src/Makefile.nestedvm b/apps/plugins/puzzles/src/Makefile.nestedvm
new file mode 100644
index 0000000000..cf91de2e53
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.nestedvm
@@ -0,0 +1,556 @@
1# Makefile for puzzles under NestedVM.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6# This path points at the nestedvm root directory
7NESTEDVM = /opt/nestedvm
8# You can define this path to point at your tools if you need to
9TOOLPATH = $(NESTEDVM)/upstream/install/bin
10CC = $(TOOLPATH)/mips-unknown-elf-gcc
11
12CFLAGS = -O2 -Wall -Werror -DSLOW_SYSTEM -g -I./ -Iicons/
13
14all: blackbox.jar bridges.jar cube.jar dominosa.jar fifteen.jar filling.jar \
15 flip.jar flood.jar galaxies.jar guess.jar inertia.jar \
16 keen.jar lightup.jar loopy.jar magnets.jar map.jar mines.jar \
17 net.jar netslide.jar nullgame.jar palisade.jar pattern.jar \
18 pearl.jar pegs.jar range.jar rect.jar samegame.jar \
19 signpost.jar singles.jar sixteen.jar slant.jar solo.jar \
20 tents.jar towers.jar tracks.jar twiddle.jar undead.jar \
21 unequal.jar unruly.jar untangle.jar
22
23blackbox.mips: blackbox.o drawing.o nestedvm.o malloc.o midend.o misc.o \
24 no-icon.o printing.o ps.o random.o version.o
25 $(CC) $(XLDFLAGS) -o $@ blackbox.o drawing.o nestedvm.o malloc.o \
26 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
27 -lm
28
29bridges.mips: bridges.o drawing.o dsf.o findloop.o nestedvm.o malloc.o \
30 midend.o misc.o no-icon.o printing.o ps.o random.o version.o
31 $(CC) $(XLDFLAGS) -o $@ bridges.o drawing.o dsf.o findloop.o \
32 nestedvm.o malloc.o midend.o misc.o no-icon.o printing.o \
33 ps.o random.o version.o -lm
34
35cube.mips: cube.o drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
36 printing.o ps.o random.o version.o
37 $(CC) $(XLDFLAGS) -o $@ cube.o drawing.o nestedvm.o malloc.o \
38 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
39 -lm
40
41dominosa.mips: dominosa.o drawing.o nestedvm.o laydomino.o malloc.o midend.o \
42 misc.o no-icon.o printing.o ps.o random.o version.o
43 $(CC) $(XLDFLAGS) -o $@ dominosa.o drawing.o nestedvm.o laydomino.o \
44 malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o \
45 version.o -lm
46
47fifteen.mips: drawing.o fifteen.o nestedvm.o malloc.o midend.o misc.o \
48 no-icon.o printing.o ps.o random.o version.o
49 $(CC) $(XLDFLAGS) -o $@ drawing.o fifteen.o nestedvm.o malloc.o \
50 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
51 -lm
52
53filling.mips: drawing.o dsf.o filling.o nestedvm.o malloc.o midend.o misc.o \
54 no-icon.o printing.o ps.o random.o version.o
55 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o filling.o nestedvm.o \
56 malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o \
57 version.o -lm
58
59flip.mips: drawing.o flip.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
60 printing.o ps.o random.o tree234.o version.o
61 $(CC) $(XLDFLAGS) -o $@ drawing.o flip.o nestedvm.o malloc.o \
62 midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o \
63 version.o -lm
64
65flood.mips: drawing.o flood.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
66 printing.o ps.o random.o version.o
67 $(CC) $(XLDFLAGS) -o $@ drawing.o flood.o nestedvm.o malloc.o \
68 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
69 -lm
70
71galaxies.mips: drawing.o dsf.o galaxies.o nestedvm.o malloc.o midend.o \
72 misc.o no-icon.o printing.o ps.o random.o version.o
73 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o galaxies.o nestedvm.o \
74 malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o \
75 version.o -lm
76
77guess.mips: drawing.o nestedvm.o guess.o malloc.o midend.o misc.o no-icon.o \
78 printing.o ps.o random.o version.o
79 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o guess.o malloc.o \
80 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
81 -lm
82
83inertia.mips: drawing.o nestedvm.o inertia.o malloc.o midend.o misc.o \
84 no-icon.o printing.o ps.o random.o version.o
85 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o inertia.o malloc.o \
86 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
87 -lm
88
89keen.mips: drawing.o dsf.o nestedvm.o keen.o latin.o malloc.o maxflow.o \
90 midend.o misc.o no-icon.o printing.o ps.o random.o tree234.o \
91 version.o
92 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o keen.o latin.o \
93 malloc.o maxflow.o midend.o misc.o no-icon.o printing.o ps.o \
94 random.o tree234.o version.o -lm
95
96lightup.mips: combi.o drawing.o nestedvm.o lightup.o malloc.o midend.o \
97 misc.o no-icon.o printing.o ps.o random.o version.o
98 $(CC) $(XLDFLAGS) -o $@ combi.o drawing.o nestedvm.o lightup.o \
99 malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o \
100 version.o -lm
101
102loopy.mips: drawing.o dsf.o grid.o nestedvm.o loopgen.o loopy.o malloc.o \
103 midend.o misc.o no-icon.o penrose.o printing.o ps.o random.o \
104 tree234.o version.o
105 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o grid.o nestedvm.o loopgen.o \
106 loopy.o malloc.o midend.o misc.o no-icon.o penrose.o \
107 printing.o ps.o random.o tree234.o version.o -lm
108
109magnets.mips: drawing.o nestedvm.o laydomino.o magnets.o malloc.o midend.o \
110 misc.o no-icon.o printing.o ps.o random.o version.o
111 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o laydomino.o magnets.o \
112 malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o \
113 version.o -lm
114
115map.mips: drawing.o dsf.o nestedvm.o malloc.o map.o midend.o misc.o \
116 no-icon.o printing.o ps.o random.o version.o
117 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o map.o \
118 midend.o misc.o no-icon.o printing.o ps.o random.o version.o \
119 -lm
120
121mines.mips: drawing.o nestedvm.o malloc.o midend.o mines.o misc.o no-icon.o \
122 printing.o ps.o random.o tree234.o version.o
123 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
124 mines.o misc.o no-icon.o printing.o ps.o random.o tree234.o \
125 version.o -lm
126
127net.mips: drawing.o dsf.o findloop.o nestedvm.o malloc.o midend.o misc.o \
128 net.o no-icon.o printing.o ps.o random.o tree234.o version.o
129 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o findloop.o nestedvm.o \
130 malloc.o midend.o misc.o net.o no-icon.o printing.o ps.o \
131 random.o tree234.o version.o -lm
132
133netslide.mips: drawing.o nestedvm.o malloc.o midend.o misc.o netslide.o \
134 no-icon.o printing.o ps.o random.o tree234.o version.o
135 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
136 misc.o netslide.o no-icon.o printing.o ps.o random.o \
137 tree234.o version.o -lm
138
139nullgame.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
140 nullgame.o printing.o ps.o random.o version.o
141 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
142 misc.o no-icon.o nullgame.o printing.o ps.o random.o \
143 version.o -lm
144
145palisade.mips: divvy.o drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o \
146 no-icon.o palisade.o printing.o ps.o random.o version.o
147 $(CC) $(XLDFLAGS) -o $@ divvy.o drawing.o dsf.o nestedvm.o malloc.o \
148 midend.o misc.o no-icon.o palisade.o printing.o ps.o \
149 random.o version.o -lm
150
151pattern.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
152 pattern.o printing.o ps.o random.o version.o
153 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
154 misc.o no-icon.o pattern.o printing.o ps.o random.o \
155 version.o -lm
156
157pearl.mips: drawing.o dsf.o grid.o nestedvm.o loopgen.o malloc.o midend.o \
158 misc.o no-icon.o pearl.o penrose.o printing.o ps.o random.o \
159 tdq.o tree234.o version.o
160 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o grid.o nestedvm.o loopgen.o \
161 malloc.o midend.o misc.o no-icon.o pearl.o penrose.o \
162 printing.o ps.o random.o tdq.o tree234.o version.o -lm
163
164pegs.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o pegs.o \
165 printing.o ps.o random.o tree234.o version.o
166 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
167 misc.o no-icon.o pegs.o printing.o ps.o random.o tree234.o \
168 version.o -lm
169
170range.mips: drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
171 printing.o ps.o random.o range.o version.o
172 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o midend.o \
173 misc.o no-icon.o printing.o ps.o random.o range.o version.o \
174 -lm
175
176rect.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
177 printing.o ps.o random.o rect.o version.o
178 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
179 misc.o no-icon.o printing.o ps.o random.o rect.o version.o \
180 -lm
181
182samegame.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
183 printing.o ps.o random.o samegame.o version.o
184 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
185 misc.o no-icon.o printing.o ps.o random.o samegame.o \
186 version.o -lm
187
188signpost.mips: drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
189 printing.o ps.o random.o signpost.o version.o
190 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o midend.o \
191 misc.o no-icon.o printing.o ps.o random.o signpost.o \
192 version.o -lm
193
194singles.mips: drawing.o dsf.o nestedvm.o latin.o malloc.o maxflow.o midend.o \
195 misc.o no-icon.o printing.o ps.o random.o singles.o \
196 tree234.o version.o
197 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o latin.o malloc.o \
198 maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o \
199 singles.o tree234.o version.o -lm
200
201sixteen.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
202 printing.o ps.o random.o sixteen.o version.o
203 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
204 misc.o no-icon.o printing.o ps.o random.o sixteen.o \
205 version.o -lm
206
207slant.mips: drawing.o dsf.o findloop.o nestedvm.o malloc.o midend.o misc.o \
208 no-icon.o printing.o ps.o random.o slant.o version.o
209 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o findloop.o nestedvm.o \
210 malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o \
211 slant.o version.o -lm
212
213solo.mips: divvy.o drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o \
214 no-icon.o printing.o ps.o random.o solo.o version.o
215 $(CC) $(XLDFLAGS) -o $@ divvy.o drawing.o dsf.o nestedvm.o malloc.o \
216 midend.o misc.o no-icon.o printing.o ps.o random.o solo.o \
217 version.o -lm
218
219tents.mips: drawing.o dsf.o nestedvm.o malloc.o maxflow.o midend.o misc.o \
220 no-icon.o printing.o ps.o random.o tents.o version.o
221 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o \
222 maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o \
223 tents.o version.o -lm
224
225towers.mips: drawing.o nestedvm.o latin.o malloc.o maxflow.o midend.o misc.o \
226 no-icon.o printing.o ps.o random.o towers.o tree234.o \
227 version.o
228 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o latin.o malloc.o \
229 maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o \
230 towers.o tree234.o version.o -lm
231
232tracks.mips: drawing.o dsf.o findloop.o nestedvm.o malloc.o midend.o misc.o \
233 no-icon.o printing.o ps.o random.o tracks.o version.o
234 $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o findloop.o nestedvm.o \
235 malloc.o midend.o misc.o no-icon.o printing.o ps.o random.o \
236 tracks.o version.o -lm
237
238twiddle.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
239 printing.o ps.o random.o twiddle.o version.o
240 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
241 misc.o no-icon.o printing.o ps.o random.o twiddle.o \
242 version.o -lm
243
244undead.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
245 printing.o ps.o random.o undead.o version.o
246 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
247 misc.o no-icon.o printing.o ps.o random.o undead.o version.o \
248 -lm
249
250unequal.mips: drawing.o nestedvm.o latin.o malloc.o maxflow.o midend.o \
251 misc.o no-icon.o printing.o ps.o random.o tree234.o \
252 unequal.o version.o
253 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o latin.o malloc.o \
254 maxflow.o midend.o misc.o no-icon.o printing.o ps.o random.o \
255 tree234.o unequal.o version.o -lm
256
257unruly.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
258 printing.o ps.o random.o unruly.o version.o
259 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
260 misc.o no-icon.o printing.o ps.o random.o unruly.o version.o \
261 -lm
262
263untangle.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \
264 printing.o ps.o random.o tree234.o untangle.o version.o
265 $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \
266 misc.o no-icon.o printing.o ps.o random.o tree234.o \
267 untangle.o version.o -lm
268
269blackbox.o: ./blackbox.c ./puzzles.h
270 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
271blackbo3.o: ./blackbox.c ./puzzles.h
272 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
273bridges.o: ./bridges.c ./puzzles.h
274 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
275bridges3.o: ./bridges.c ./puzzles.h
276 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
277combi.o: ./combi.c ./puzzles.h
278 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
279cube.o: ./cube.c ./puzzles.h
280 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
281cube3.o: ./cube.c ./puzzles.h
282 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
283divvy.o: ./divvy.c ./puzzles.h
284 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
285dominosa.o: ./dominosa.c ./puzzles.h
286 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
287dominos3.o: ./dominosa.c ./puzzles.h
288 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
289drawing.o: ./drawing.c ./puzzles.h
290 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
291dsf.o: ./dsf.c ./puzzles.h
292 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
293fifteen.o: ./fifteen.c ./puzzles.h
294 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
295fifteen5.o: ./fifteen.c ./puzzles.h
296 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
297fifteen2.o: ./fifteen.c ./puzzles.h
298 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
299filling.o: ./filling.c ./puzzles.h
300 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
301filling5.o: ./filling.c ./puzzles.h
302 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
303filling2.o: ./filling.c ./puzzles.h
304 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
305findloop.o: ./findloop.c ./puzzles.h
306 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
307flip.o: ./flip.c ./puzzles.h ./tree234.h
308 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
309flip3.o: ./flip.c ./puzzles.h ./tree234.h
310 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
311flood.o: ./flood.c ./puzzles.h
312 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
313flood3.o: ./flood.c ./puzzles.h
314 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
315galaxies.o: ./galaxies.c ./puzzles.h
316 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
317galaxie7.o: ./galaxies.c ./puzzles.h
318 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
319galaxie4.o: ./galaxies.c ./puzzles.h
320 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
321galaxie2.o: ./galaxies.c ./puzzles.h
322 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
323grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h
324 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
325nestedvm.o: ./nestedvm.c ./puzzles.h
326 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
327guess.o: ./guess.c ./puzzles.h
328 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
329guess3.o: ./guess.c ./puzzles.h
330 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
331inertia.o: ./inertia.c ./puzzles.h
332 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
333inertia3.o: ./inertia.c ./puzzles.h
334 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
335keen.o: ./keen.c ./puzzles.h ./latin.h
336 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
337keen5.o: ./keen.c ./puzzles.h ./latin.h
338 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
339keen2.o: ./keen.c ./puzzles.h ./latin.h
340 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
341latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
342 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
343latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
344 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@
345latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
346 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
347laydomino.o: ./laydomino.c ./puzzles.h
348 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
349lightup.o: ./lightup.c ./puzzles.h
350 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
351lightup5.o: ./lightup.c ./puzzles.h
352 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
353lightup2.o: ./lightup.c ./puzzles.h
354 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
355list.o: ./list.c ./puzzles.h
356 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
357loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
358 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
359loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
360 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
361loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
362 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
363loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
364 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
365magnets.o: ./magnets.c ./puzzles.h
366 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
367magnets5.o: ./magnets.c ./puzzles.h
368 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
369magnets2.o: ./magnets.c ./puzzles.h
370 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
371malloc.o: ./malloc.c ./puzzles.h
372 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
373map.o: ./map.c ./puzzles.h
374 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
375map5.o: ./map.c ./puzzles.h
376 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
377map2.o: ./map.c ./puzzles.h
378 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
379maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h
380 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
381midend.o: ./midend.c ./puzzles.h
382 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
383mines.o: ./mines.c ./tree234.h ./puzzles.h
384 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
385mines5.o: ./mines.c ./tree234.h ./puzzles.h
386 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
387mines2.o: ./mines.c ./tree234.h ./puzzles.h
388 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@
389misc.o: ./misc.c ./puzzles.h
390 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
391net.o: ./net.c ./puzzles.h ./tree234.h
392 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
393net3.o: ./net.c ./puzzles.h ./tree234.h
394 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
395netslide.o: ./netslide.c ./puzzles.h ./tree234.h
396 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
397netslid3.o: ./netslide.c ./puzzles.h ./tree234.h
398 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
399no-icon.o: ./no-icon.c
400 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
401nullfe.o: ./nullfe.c ./puzzles.h
402 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
403nullgame.o: ./nullgame.c ./puzzles.h
404 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
405obfusc.o: ./obfusc.c ./puzzles.h
406 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
407osx.o: ./osx.m ./puzzles.h
408 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
409palisade.o: ./palisade.c ./puzzles.h
410 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
411palisad3.o: ./palisade.c ./puzzles.h
412 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
413pattern.o: ./pattern.c ./puzzles.h
414 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
415pattern7.o: ./pattern.c ./puzzles.h
416 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
417pattern4.o: ./pattern.c ./puzzles.h
418 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
419pattern2.o: ./pattern.c ./puzzles.h
420 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
421pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
422 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
423pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
424 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
425pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
426 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
427pegs.o: ./pegs.c ./puzzles.h ./tree234.h
428 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
429pegs3.o: ./pegs.c ./puzzles.h ./tree234.h
430 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
431penrose.o: ./penrose.c ./puzzles.h ./penrose.h
432 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
433printing.o: ./printing.c ./puzzles.h
434 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
435ps.o: ./ps.c ./puzzles.h
436 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
437random.o: ./random.c ./puzzles.h
438 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
439range.o: ./range.c ./puzzles.h
440 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
441range3.o: ./range.c ./puzzles.h
442 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
443rect.o: ./rect.c ./puzzles.h
444 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
445rect3.o: ./rect.c ./puzzles.h
446 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
447samegame.o: ./samegame.c ./puzzles.h
448 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
449samegam3.o: ./samegame.c ./puzzles.h
450 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
451signpost.o: ./signpost.c ./puzzles.h
452 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
453signpos5.o: ./signpost.c ./puzzles.h
454 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
455signpos2.o: ./signpost.c ./puzzles.h
456 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
457singles.o: ./singles.c ./puzzles.h ./latin.h
458 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
459singles5.o: ./singles.c ./puzzles.h ./latin.h
460 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
461singles3.o: ./singles.c ./puzzles.h ./latin.h
462 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
463sixteen.o: ./sixteen.c ./puzzles.h
464 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
465sixteen3.o: ./sixteen.c ./puzzles.h
466 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
467slant.o: ./slant.c ./puzzles.h
468 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
469slant5.o: ./slant.c ./puzzles.h
470 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
471slant2.o: ./slant.c ./puzzles.h
472 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
473solo.o: ./solo.c ./puzzles.h
474 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
475solo5.o: ./solo.c ./puzzles.h
476 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
477solo2.o: ./solo.c ./puzzles.h
478 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
479tdq.o: ./tdq.c ./puzzles.h
480 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
481tents.o: ./tents.c ./puzzles.h ./maxflow.h
482 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
483tents5.o: ./tents.c ./puzzles.h ./maxflow.h
484 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
485tents3.o: ./tents.c ./puzzles.h ./maxflow.h
486 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
487towers.o: ./towers.c ./puzzles.h ./latin.h
488 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
489towers5.o: ./towers.c ./puzzles.h ./latin.h
490 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
491towers2.o: ./towers.c ./puzzles.h ./latin.h
492 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
493tracks.o: ./tracks.c ./puzzles.h
494 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
495tracks3.o: ./tracks.c ./puzzles.h
496 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
497tree234.o: ./tree234.c ./tree234.h ./puzzles.h
498 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
499twiddle.o: ./twiddle.c ./puzzles.h
500 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
501twiddle3.o: ./twiddle.c ./puzzles.h
502 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
503undead.o: ./undead.c ./puzzles.h
504 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
505undead3.o: ./undead.c ./puzzles.h
506 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
507unequal.o: ./unequal.c ./puzzles.h ./latin.h
508 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
509unequal5.o: ./unequal.c ./puzzles.h ./latin.h
510 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
511unequal2.o: ./unequal.c ./puzzles.h ./latin.h
512 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
513unruly.o: ./unruly.c ./puzzles.h
514 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
515unruly5.o: ./unruly.c ./puzzles.h
516 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
517unruly2.o: ./unruly.c ./puzzles.h
518 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
519untangle.o: ./untangle.c ./puzzles.h ./tree234.h
520 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
521untangl3.o: ./untangle.c ./puzzles.h ./tree234.h
522 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
523version.o: ./version.c ./version.h
524 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
525windows.o: ./windows.c ./puzzles.h ./resource.h
526 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
527windows1.o: ./windows.c ./puzzles.h ./resource.h
528 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
529
530.PRECIOUS: %.class
531%.class: %.mips
532 java -cp $(NESTEDVM)/build:$(NESTEDVM)/upstream/build/classgen/build \
533 org.ibex.nestedvm.Compiler -outformat class -d . \
534 PuzzleEngine $<
535 mv PuzzleEngine.class $@
536
537org:
538 mkdir -p org/ibex/nestedvm/util
539 cp $(NESTEDVM)/build/org/ibex/nestedvm/Registers.class org/ibex/nestedvm
540 cp $(NESTEDVM)/build/org/ibex/nestedvm/UsermodeConstants.class org/ibex/nestedvm
541 cp $(NESTEDVM)/build/org/ibex/nestedvm/Runtime*.class org/ibex/nestedvm
542 cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Platform*.class org/ibex/nestedvm/util
543 cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Seekable*.class org/ibex/nestedvm/util
544 echo "Main-Class: PuzzleApplet" >applet.manifest
545
546PuzzleApplet.class: PuzzleApplet.java org
547 javac -source 1.3 -target 1.3 PuzzleApplet.java
548
549%.jar: %.class PuzzleApplet.class org
550 mv $< PuzzleEngine.class
551 jar cfm $@ applet.manifest PuzzleEngine.class PuzzleApplet*.class org
552 echo '<applet archive="'$@'" code="PuzzleApplet" width="700" height="500"></applet>' >$*.html
553 mv PuzzleEngine.class $<
554
555clean:
556 rm -rf *.o *.mips *.class *.html *.jar org applet.manifest
diff --git a/apps/plugins/puzzles/src/Makefile.osx b/apps/plugins/puzzles/src/Makefile.osx
new file mode 100644
index 0000000000..5d607d88e3
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.osx
@@ -0,0 +1,574 @@
1# Makefile for puzzles under Mac OS X.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5CC = $(TOOLPATH)gcc
6LIPO = $(TOOLPATH)lipo
7
8CFLAGS = -O2 -Wall -Werror -g -I./ -Iicons/
9LDFLAGS = -framework Cocoa
10all: Puzzles fifteensolver fillingsolver galaxiespicture galaxiessolver \
11 keensolver latincheck lightupsolver loopysolver \
12 magnetssolver mapsolver mineobfusc obfusc patternpicture \
13 patternsolver pearlbench signpostsolver singlessolver \
14 slantsolver solosolver tentssolver towerssolver \
15 unequalsolver unrulysolver
16Puzzles_extra = Puzzles.app/Contents/Resources/Help/index.html
17Puzzles.app/Contents/Resources/Help/index.html: \
18 Puzzles.app/Contents/Resources/Help osx-help.but puzzles.but
19 cd Puzzles.app/Contents/Resources/Help; \
20 halibut --html ../../../../osx-help.but ../../../../puzzles.but
21Puzzles.app/Contents/Resources/Help: Puzzles.app/Contents/Resources
22 mkdir -p Puzzles.app/Contents/Resources/Help
23
24release: Puzzles.dmg
25Puzzles.dmg: Puzzles
26 rm -f raw.dmg
27 hdiutil create -megabytes 5 -layout NONE raw.dmg
28 hdid -nomount raw.dmg > devicename
29 newfs_hfs -v "Simon Tatham's Puzzle Collection" `cat devicename`
30 hdiutil eject `cat devicename`
31 hdid raw.dmg | cut -f1 -d' ' > devicename
32 cp -R Puzzles.app /Volumes/"Simon Tatham's Puzzle Collection"
33 hdiutil eject `cat devicename`
34 rm -f Puzzles.dmg
35 hdiutil convert -format UDCO raw.dmg -o Puzzles.dmg
36 rm -f raw.dmg devicename
37
38.SUFFIXES: .o .c .m
39
40
41
42Puzzles.app:
43 mkdir -p $@
44Puzzles.app/Contents: Puzzles.app
45 mkdir -p $@
46Puzzles.app/Contents/MacOS: Puzzles.app/Contents
47 mkdir -p $@
48Puzzles.app/Contents/Resources: Puzzles.app/Contents
49 mkdir -p $@
50Puzzles.app/Contents/Resources/Puzzles.icns: Puzzles.app/Contents/Resources osx.icns
51 cp osx.icns $@
52Puzzles.app/Contents/Info.plist: Puzzles.app/Contents/Resources osx-info.plist
53 cp osx-info.plist $@
54Puzzles: Puzzles.app/Contents/MacOS/Puzzles \
55 Puzzles.app/Contents/Resources/Puzzles.icns \
56 Puzzles.app/Contents/Info.plist $(Puzzles_extra)
57
58Puzzles.i386.bin: blackbo3.i386.o bridges3.i386.o combi.i386.o cube3.i386.o \
59 divvy.i386.o dominos3.i386.o drawing.i386.o dsf.i386.o \
60 fifteen5.i386.o filling5.i386.o findloop.i386.o flip3.i386.o \
61 flood3.i386.o galaxie7.i386.o grid.i386.o guess3.i386.o \
62 inertia3.i386.o keen5.i386.o latin.i386.o laydomino.i386.o \
63 lightup5.i386.o list.i386.o loopgen.i386.o loopy5.i386.o \
64 magnets5.i386.o malloc.i386.o map5.i386.o maxflow.i386.o \
65 midend.i386.o mines5.i386.o misc.i386.o net3.i386.o \
66 netslid3.i386.o osx.i386.o palisad3.i386.o pattern7.i386.o \
67 pearl5.i386.o pegs3.i386.o penrose.i386.o random.i386.o \
68 range3.i386.o rect3.i386.o samegam3.i386.o signpos5.i386.o \
69 singles5.i386.o sixteen3.i386.o slant5.i386.o solo5.i386.o \
70 tdq.i386.o tents5.i386.o towers5.i386.o tracks3.i386.o \
71 tree234.i386.o twiddle3.i386.o undead3.i386.o \
72 unequal5.i386.o unruly5.i386.o untangl3.i386.o \
73 version.i386.o
74 $(CC) -arch i386 -mmacosx-version-min=10.4 $(LDFLAGS) -o $@ \
75 blackbo3.i386.o bridges3.i386.o combi.i386.o cube3.i386.o \
76 divvy.i386.o dominos3.i386.o drawing.i386.o dsf.i386.o \
77 fifteen5.i386.o filling5.i386.o findloop.i386.o flip3.i386.o \
78 flood3.i386.o galaxie7.i386.o grid.i386.o guess3.i386.o \
79 inertia3.i386.o keen5.i386.o latin.i386.o laydomino.i386.o \
80 lightup5.i386.o list.i386.o loopgen.i386.o loopy5.i386.o \
81 magnets5.i386.o malloc.i386.o map5.i386.o maxflow.i386.o \
82 midend.i386.o mines5.i386.o misc.i386.o net3.i386.o \
83 netslid3.i386.o osx.i386.o palisad3.i386.o pattern7.i386.o \
84 pearl5.i386.o pegs3.i386.o penrose.i386.o random.i386.o \
85 range3.i386.o rect3.i386.o samegam3.i386.o signpos5.i386.o \
86 singles5.i386.o sixteen3.i386.o slant5.i386.o solo5.i386.o \
87 tdq.i386.o tents5.i386.o towers5.i386.o tracks3.i386.o \
88 tree234.i386.o twiddle3.i386.o undead3.i386.o \
89 unequal5.i386.o unruly5.i386.o untangl3.i386.o \
90 version.i386.o
91
92Puzzles.app/Contents/MacOS/Puzzles: Puzzles.app/Contents/MacOS \
93 Puzzles.i386.bin
94 $(LIPO) -create Puzzles.i386.bin -output $@
95
96fifteensolver.i386: fifteen2.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \
97 random.i386.o
98 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
99 fifteen2.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \
100 random.i386.o
101
102fifteensolver: fifteensolver.i386
103 $(LIPO) -create fifteensolver.i386 -output $@
104
105fillingsolver.i386: dsf.i386.o filling2.i386.o malloc.i386.o misc.i386.o \
106 nullfe.i386.o random.i386.o
107 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
108 dsf.i386.o filling2.i386.o malloc.i386.o misc.i386.o \
109 nullfe.i386.o random.i386.o
110
111fillingsolver: fillingsolver.i386
112 $(LIPO) -create fillingsolver.i386 -output $@
113
114galaxiespicture.i386: dsf.i386.o galaxie4.i386.o malloc.i386.o misc.i386.o \
115 nullfe.i386.o random.i386.o
116 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
117 dsf.i386.o galaxie4.i386.o malloc.i386.o misc.i386.o \
118 nullfe.i386.o random.i386.o -lm
119
120galaxiespicture: galaxiespicture.i386
121 $(LIPO) -create galaxiespicture.i386 -output $@
122
123galaxiessolver.i386: dsf.i386.o galaxie2.i386.o malloc.i386.o misc.i386.o \
124 nullfe.i386.o random.i386.o
125 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
126 dsf.i386.o galaxie2.i386.o malloc.i386.o misc.i386.o \
127 nullfe.i386.o random.i386.o -lm
128
129galaxiessolver: galaxiessolver.i386
130 $(LIPO) -create galaxiessolver.i386 -output $@
131
132keensolver.i386: dsf.i386.o keen2.i386.o latin6.i386.o malloc.i386.o \
133 maxflow.i386.o misc.i386.o nullfe.i386.o random.i386.o \
134 tree234.i386.o
135 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
136 dsf.i386.o keen2.i386.o latin6.i386.o malloc.i386.o \
137 maxflow.i386.o misc.i386.o nullfe.i386.o random.i386.o \
138 tree234.i386.o
139
140keensolver: keensolver.i386
141 $(LIPO) -create keensolver.i386 -output $@
142
143latincheck.i386: latin8.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
144 nullfe.i386.o random.i386.o tree234.i386.o
145 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
146 latin8.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
147 nullfe.i386.o random.i386.o tree234.i386.o
148
149latincheck: latincheck.i386
150 $(LIPO) -create latincheck.i386 -output $@
151
152lightupsolver.i386: combi.i386.o lightup2.i386.o malloc.i386.o misc.i386.o \
153 nullfe.i386.o random.i386.o
154 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
155 combi.i386.o lightup2.i386.o malloc.i386.o misc.i386.o \
156 nullfe.i386.o random.i386.o
157
158lightupsolver: lightupsolver.i386
159 $(LIPO) -create lightupsolver.i386 -output $@
160
161loopysolver.i386: dsf.i386.o grid.i386.o loopgen.i386.o loopy2.i386.o \
162 malloc.i386.o misc.i386.o nullfe.i386.o penrose.i386.o \
163 random.i386.o tree234.i386.o
164 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
165 dsf.i386.o grid.i386.o loopgen.i386.o loopy2.i386.o \
166 malloc.i386.o misc.i386.o nullfe.i386.o penrose.i386.o \
167 random.i386.o tree234.i386.o -lm
168
169loopysolver: loopysolver.i386
170 $(LIPO) -create loopysolver.i386 -output $@
171
172magnetssolver.i386: laydomino.i386.o magnets2.i386.o malloc.i386.o \
173 misc.i386.o nullfe.i386.o random.i386.o
174 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
175 laydomino.i386.o magnets2.i386.o malloc.i386.o misc.i386.o \
176 nullfe.i386.o random.i386.o -lm
177
178magnetssolver: magnetssolver.i386
179 $(LIPO) -create magnetssolver.i386 -output $@
180
181mapsolver.i386: dsf.i386.o malloc.i386.o map2.i386.o misc.i386.o \
182 nullfe.i386.o random.i386.o
183 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
184 dsf.i386.o malloc.i386.o map2.i386.o misc.i386.o \
185 nullfe.i386.o random.i386.o -lm
186
187mapsolver: mapsolver.i386
188 $(LIPO) -create mapsolver.i386 -output $@
189
190mineobfusc.i386: malloc.i386.o mines2.i386.o misc.i386.o nullfe.i386.o \
191 random.i386.o tree234.i386.o
192 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
193 malloc.i386.o mines2.i386.o misc.i386.o nullfe.i386.o \
194 random.i386.o tree234.i386.o
195
196mineobfusc: mineobfusc.i386
197 $(LIPO) -create mineobfusc.i386 -output $@
198
199obfusc.i386: malloc.i386.o misc.i386.o nullfe.i386.o obfusc.i386.o \
200 random.i386.o
201 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
202 malloc.i386.o misc.i386.o nullfe.i386.o obfusc.i386.o \
203 random.i386.o
204
205obfusc: obfusc.i386
206 $(LIPO) -create obfusc.i386 -output $@
207
208patternpicture.i386: malloc.i386.o misc.i386.o nullfe.i386.o pattern4.i386.o \
209 random.i386.o
210 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
211 malloc.i386.o misc.i386.o nullfe.i386.o pattern4.i386.o \
212 random.i386.o
213
214patternpicture: patternpicture.i386
215 $(LIPO) -create patternpicture.i386 -output $@
216
217patternsolver.i386: malloc.i386.o misc.i386.o nullfe.i386.o pattern2.i386.o \
218 random.i386.o
219 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
220 malloc.i386.o misc.i386.o nullfe.i386.o pattern2.i386.o \
221 random.i386.o
222
223patternsolver: patternsolver.i386
224 $(LIPO) -create patternsolver.i386 -output $@
225
226pearlbench.i386: dsf.i386.o grid.i386.o loopgen.i386.o malloc.i386.o \
227 misc.i386.o nullfe.i386.o pearl2.i386.o penrose.i386.o \
228 random.i386.o tdq.i386.o tree234.i386.o
229 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
230 dsf.i386.o grid.i386.o loopgen.i386.o malloc.i386.o \
231 misc.i386.o nullfe.i386.o pearl2.i386.o penrose.i386.o \
232 random.i386.o tdq.i386.o tree234.i386.o -lm
233
234pearlbench: pearlbench.i386
235 $(LIPO) -create pearlbench.i386 -output $@
236
237signpostsolver.i386: dsf.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \
238 random.i386.o signpos2.i386.o
239 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
240 dsf.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \
241 random.i386.o signpos2.i386.o -lm
242
243signpostsolver: signpostsolver.i386
244 $(LIPO) -create signpostsolver.i386 -output $@
245
246singlessolver.i386: dsf.i386.o latin.i386.o malloc.i386.o maxflow.i386.o \
247 misc.i386.o nullfe.i386.o random.i386.o singles3.i386.o \
248 tree234.i386.o
249 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
250 dsf.i386.o latin.i386.o malloc.i386.o maxflow.i386.o \
251 misc.i386.o nullfe.i386.o random.i386.o singles3.i386.o \
252 tree234.i386.o
253
254singlessolver: singlessolver.i386
255 $(LIPO) -create singlessolver.i386 -output $@
256
257slantsolver.i386: dsf.i386.o findloop.i386.o malloc.i386.o misc.i386.o \
258 nullfe.i386.o random.i386.o slant2.i386.o
259 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
260 dsf.i386.o findloop.i386.o malloc.i386.o misc.i386.o \
261 nullfe.i386.o random.i386.o slant2.i386.o
262
263slantsolver: slantsolver.i386
264 $(LIPO) -create slantsolver.i386 -output $@
265
266solosolver.i386: divvy.i386.o dsf.i386.o malloc.i386.o misc.i386.o \
267 nullfe.i386.o random.i386.o solo2.i386.o
268 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
269 divvy.i386.o dsf.i386.o malloc.i386.o misc.i386.o \
270 nullfe.i386.o random.i386.o solo2.i386.o
271
272solosolver: solosolver.i386
273 $(LIPO) -create solosolver.i386 -output $@
274
275tentssolver.i386: dsf.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
276 nullfe.i386.o random.i386.o tents3.i386.o
277 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
278 dsf.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
279 nullfe.i386.o random.i386.o tents3.i386.o
280
281tentssolver: tentssolver.i386
282 $(LIPO) -create tentssolver.i386 -output $@
283
284towerssolver.i386: latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
285 nullfe.i386.o random.i386.o towers2.i386.o tree234.i386.o
286 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
287 latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
288 nullfe.i386.o random.i386.o towers2.i386.o tree234.i386.o
289
290towerssolver: towerssolver.i386
291 $(LIPO) -create towerssolver.i386 -output $@
292
293unequalsolver.i386: latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
294 nullfe.i386.o random.i386.o tree234.i386.o unequal2.i386.o
295 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
296 latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \
297 nullfe.i386.o random.i386.o tree234.i386.o unequal2.i386.o
298
299unequalsolver: unequalsolver.i386
300 $(LIPO) -create unequalsolver.i386 -output $@
301
302unrulysolver.i386: malloc.i386.o misc.i386.o nullfe.i386.o random.i386.o \
303 unruly2.i386.o
304 $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \
305 malloc.i386.o misc.i386.o nullfe.i386.o random.i386.o \
306 unruly2.i386.o
307
308unrulysolver: unrulysolver.i386
309 $(LIPO) -create unrulysolver.i386 -output $@
310
311blackbox.i386.o: ./blackbox.c ./puzzles.h
312 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
313blackbo3.i386.o: ./blackbox.c ./puzzles.h
314 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
315bridges.i386.o: ./bridges.c ./puzzles.h
316 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
317bridges3.i386.o: ./bridges.c ./puzzles.h
318 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
319combi.i386.o: ./combi.c ./puzzles.h
320 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
321cube.i386.o: ./cube.c ./puzzles.h
322 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
323cube3.i386.o: ./cube.c ./puzzles.h
324 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
325divvy.i386.o: ./divvy.c ./puzzles.h
326 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
327dominosa.i386.o: ./dominosa.c ./puzzles.h
328 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
329dominos3.i386.o: ./dominosa.c ./puzzles.h
330 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
331drawing.i386.o: ./drawing.c ./puzzles.h
332 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
333dsf.i386.o: ./dsf.c ./puzzles.h
334 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
335fifteen.i386.o: ./fifteen.c ./puzzles.h
336 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
337fifteen5.i386.o: ./fifteen.c ./puzzles.h
338 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
339fifteen2.i386.o: ./fifteen.c ./puzzles.h
340 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
341filling.i386.o: ./filling.c ./puzzles.h
342 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
343filling5.i386.o: ./filling.c ./puzzles.h
344 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
345filling2.i386.o: ./filling.c ./puzzles.h
346 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
347findloop.i386.o: ./findloop.c ./puzzles.h
348 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
349flip.i386.o: ./flip.c ./puzzles.h ./tree234.h
350 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
351flip3.i386.o: ./flip.c ./puzzles.h ./tree234.h
352 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
353flood.i386.o: ./flood.c ./puzzles.h
354 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
355flood3.i386.o: ./flood.c ./puzzles.h
356 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
357galaxies.i386.o: ./galaxies.c ./puzzles.h
358 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
359galaxie7.i386.o: ./galaxies.c ./puzzles.h
360 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
361galaxie4.i386.o: ./galaxies.c ./puzzles.h
362 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
363galaxie2.i386.o: ./galaxies.c ./puzzles.h
364 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
365grid.i386.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h
366 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
367gtk.i386.o: ./gtk.c ./puzzles.h
368 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
369guess.i386.o: ./guess.c ./puzzles.h
370 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
371guess3.i386.o: ./guess.c ./puzzles.h
372 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
373inertia.i386.o: ./inertia.c ./puzzles.h
374 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
375inertia3.i386.o: ./inertia.c ./puzzles.h
376 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
377keen.i386.o: ./keen.c ./puzzles.h ./latin.h
378 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
379keen5.i386.o: ./keen.c ./puzzles.h ./latin.h
380 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
381keen2.i386.o: ./keen.c ./puzzles.h ./latin.h
382 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
383latin.i386.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
384 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
385latin8.i386.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
386 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@
387latin6.i386.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h
388 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
389laydomino.i386.o: ./laydomino.c ./puzzles.h
390 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
391lightup.i386.o: ./lightup.c ./puzzles.h
392 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
393lightup5.i386.o: ./lightup.c ./puzzles.h
394 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
395lightup2.i386.o: ./lightup.c ./puzzles.h
396 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
397list.i386.o: ./list.c ./puzzles.h
398 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
399loopgen.i386.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
400 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
401loopy.i386.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
402 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
403loopy5.i386.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
404 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
405loopy2.i386.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h
406 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
407magnets.i386.o: ./magnets.c ./puzzles.h
408 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
409magnets5.i386.o: ./magnets.c ./puzzles.h
410 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
411magnets2.i386.o: ./magnets.c ./puzzles.h
412 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
413malloc.i386.o: ./malloc.c ./puzzles.h
414 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
415map.i386.o: ./map.c ./puzzles.h
416 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
417map5.i386.o: ./map.c ./puzzles.h
418 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
419map2.i386.o: ./map.c ./puzzles.h
420 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
421maxflow.i386.o: ./maxflow.c ./maxflow.h ./puzzles.h
422 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
423midend.i386.o: ./midend.c ./puzzles.h
424 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
425mines.i386.o: ./mines.c ./tree234.h ./puzzles.h
426 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
427mines5.i386.o: ./mines.c ./tree234.h ./puzzles.h
428 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
429mines2.i386.o: ./mines.c ./tree234.h ./puzzles.h
430 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@
431misc.i386.o: ./misc.c ./puzzles.h
432 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
433net.i386.o: ./net.c ./puzzles.h ./tree234.h
434 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
435net3.i386.o: ./net.c ./puzzles.h ./tree234.h
436 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
437netslide.i386.o: ./netslide.c ./puzzles.h ./tree234.h
438 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
439netslid3.i386.o: ./netslide.c ./puzzles.h ./tree234.h
440 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
441no-icon.i386.o: ./no-icon.c
442 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
443nullfe.i386.o: ./nullfe.c ./puzzles.h
444 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
445nullgame.i386.o: ./nullgame.c ./puzzles.h
446 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
447obfusc.i386.o: ./obfusc.c ./puzzles.h
448 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
449osx.i386.o: ./osx.m ./puzzles.h
450 $(CC) -arch i386 -mmacosx-version-min=10.4 -x objective-c $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
451palisade.i386.o: ./palisade.c ./puzzles.h
452 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
453palisad3.i386.o: ./palisade.c ./puzzles.h
454 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
455pattern.i386.o: ./pattern.c ./puzzles.h
456 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
457pattern7.i386.o: ./pattern.c ./puzzles.h
458 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
459pattern4.i386.o: ./pattern.c ./puzzles.h
460 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@
461pattern2.i386.o: ./pattern.c ./puzzles.h
462 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
463pearl.i386.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
464 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
465pearl5.i386.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
466 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
467pearl2.i386.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h
468 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
469pegs.i386.o: ./pegs.c ./puzzles.h ./tree234.h
470 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
471pegs3.i386.o: ./pegs.c ./puzzles.h ./tree234.h
472 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
473penrose.i386.o: ./penrose.c ./puzzles.h ./penrose.h
474 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
475printing.i386.o: ./printing.c ./puzzles.h
476 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
477ps.i386.o: ./ps.c ./puzzles.h
478 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
479random.i386.o: ./random.c ./puzzles.h
480 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
481range.i386.o: ./range.c ./puzzles.h
482 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
483range3.i386.o: ./range.c ./puzzles.h
484 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
485rect.i386.o: ./rect.c ./puzzles.h
486 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
487rect3.i386.o: ./rect.c ./puzzles.h
488 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
489samegame.i386.o: ./samegame.c ./puzzles.h
490 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
491samegam3.i386.o: ./samegame.c ./puzzles.h
492 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
493signpost.i386.o: ./signpost.c ./puzzles.h
494 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
495signpos5.i386.o: ./signpost.c ./puzzles.h
496 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
497signpos2.i386.o: ./signpost.c ./puzzles.h
498 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
499singles.i386.o: ./singles.c ./puzzles.h ./latin.h
500 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
501singles5.i386.o: ./singles.c ./puzzles.h ./latin.h
502 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
503singles3.i386.o: ./singles.c ./puzzles.h ./latin.h
504 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
505sixteen.i386.o: ./sixteen.c ./puzzles.h
506 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
507sixteen3.i386.o: ./sixteen.c ./puzzles.h
508 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
509slant.i386.o: ./slant.c ./puzzles.h
510 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
511slant5.i386.o: ./slant.c ./puzzles.h
512 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
513slant2.i386.o: ./slant.c ./puzzles.h
514 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
515solo.i386.o: ./solo.c ./puzzles.h
516 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
517solo5.i386.o: ./solo.c ./puzzles.h
518 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
519solo2.i386.o: ./solo.c ./puzzles.h
520 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
521tdq.i386.o: ./tdq.c ./puzzles.h
522 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
523tents.i386.o: ./tents.c ./puzzles.h ./maxflow.h
524 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
525tents5.i386.o: ./tents.c ./puzzles.h ./maxflow.h
526 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
527tents3.i386.o: ./tents.c ./puzzles.h ./maxflow.h
528 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
529towers.i386.o: ./towers.c ./puzzles.h ./latin.h
530 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
531towers5.i386.o: ./towers.c ./puzzles.h ./latin.h
532 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
533towers2.i386.o: ./towers.c ./puzzles.h ./latin.h
534 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
535tracks.i386.o: ./tracks.c ./puzzles.h
536 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
537tracks3.i386.o: ./tracks.c ./puzzles.h
538 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
539tree234.i386.o: ./tree234.c ./tree234.h ./puzzles.h
540 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
541twiddle.i386.o: ./twiddle.c ./puzzles.h
542 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
543twiddle3.i386.o: ./twiddle.c ./puzzles.h
544 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
545undead.i386.o: ./undead.c ./puzzles.h
546 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
547undead3.i386.o: ./undead.c ./puzzles.h
548 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
549unequal.i386.o: ./unequal.c ./puzzles.h ./latin.h
550 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
551unequal5.i386.o: ./unequal.c ./puzzles.h ./latin.h
552 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
553unequal2.i386.o: ./unequal.c ./puzzles.h ./latin.h
554 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
555unruly.i386.o: ./unruly.c ./puzzles.h
556 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
557unruly5.i386.o: ./unruly.c ./puzzles.h
558 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
559unruly2.i386.o: ./unruly.c ./puzzles.h
560 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@
561untangle.i386.o: ./untangle.c ./puzzles.h ./tree234.h
562 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
563untangl3.i386.o: ./untangle.c ./puzzles.h ./tree234.h
564 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
565version.i386.o: ./version.c ./version.h
566 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
567windows.i386.o: ./windows.c ./puzzles.h ./resource.h
568 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@
569windows1.i386.o: ./windows.c ./puzzles.h ./resource.h
570 $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@
571
572clean:
573 rm -f *.o *.dmg fifteensolver fifteensolver.i386 fillingsolver fillingsolver.i386 galaxiespicture galaxiespicture.i386 galaxiessolver galaxiessolver.i386 keensolver keensolver.i386 latincheck latincheck.i386 lightupsolver lightupsolver.i386 loopysolver loopysolver.i386 magnetssolver magnetssolver.i386 mapsolver mapsolver.i386 mineobfusc mineobfusc.i386 obfusc obfusc.i386 patternpicture patternpicture.i386 patternsolver patternsolver.i386 pearlbench pearlbench.i386 signpostsolver signpostsolver.i386 singlessolver singlessolver.i386 slantsolver slantsolver.i386 solosolver solosolver.i386 tentssolver tentssolver.i386 towerssolver towerssolver.i386 unequalsolver unequalsolver.i386 unrulysolver unrulysolver.i386
574 rm -rf *.app
diff --git a/apps/plugins/puzzles/src/Makefile.vc b/apps/plugins/puzzles/src/Makefile.vc
new file mode 100644
index 0000000000..45e77a5d8a
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.vc
@@ -0,0 +1,1040 @@
1# Makefile for puzzles under Visual C.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6# If you rename this file to `Makefile', you should change this line,
7# so that the .rsp files still depend on the correct makefile.
8MAKEFILE = Makefile.vc
9
10# C compilation flags
11CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 /I.
12LFLAGS = /incremental:no /fixed
13
14all: blackbox.exe bridges.exe cube.exe dominosa.exe fifteen.exe \
15 fifteensolver.exe filling.exe fillingsolver.exe flip.exe \
16 flood.exe galaxies.exe galaxiespicture.exe \
17 galaxiessolver.exe guess.exe inertia.exe keen.exe \
18 keensolver.exe latincheck.exe lightup.exe lightupsolver.exe \
19 loopy.exe loopysolver.exe magnets.exe magnetssolver.exe \
20 map.exe mapsolver.exe mineobfusc.exe mines.exe netgame.exe \
21 netslide.exe nullgame.exe palisade.exe pattern.exe \
22 patternpicture.exe patternsolver.exe pearl.exe \
23 pearlbench.exe pegs.exe puzzles.exe range.exe rect.exe \
24 samegame.exe signpost.exe signpostsolver.exe singles.exe \
25 singlessolver.exe sixteen.exe slant.exe slantsolver.exe \
26 solo.exe solosolver.exe tents.exe tentssolver.exe towers.exe \
27 towerssolver.exe tracks.exe twiddle.exe undead.exe \
28 unequal.exe unequalsolver.exe unruly.exe unrulysolver.exe \
29 untangle.exe
30
31blackbox.exe: blackbox.obj drawing.obj malloc.obj midend.obj misc.obj \
32 noicon.res printing.obj random.obj version.obj windows.obj \
33 blackbox.rsp
34 link $(LFLAGS) -out:blackbox.exe -map:blackbox.map @blackbox.rsp
35
36bridges.exe: bridges.obj drawing.obj dsf.obj findloop.obj malloc.obj \
37 midend.obj misc.obj noicon.res printing.obj random.obj \
38 version.obj windows.obj bridges.rsp
39 link $(LFLAGS) -out:bridges.exe -map:bridges.map @bridges.rsp
40
41cube.exe: cube.obj drawing.obj malloc.obj midend.obj misc.obj noicon.res \
42 printing.obj random.obj version.obj windows.obj cube.rsp
43 link $(LFLAGS) -out:cube.exe -map:cube.map @cube.rsp
44
45dominosa.exe: dominosa.obj drawing.obj laydomino.obj malloc.obj midend.obj \
46 misc.obj noicon.res printing.obj random.obj version.obj \
47 windows.obj dominosa.rsp
48 link $(LFLAGS) -out:dominosa.exe -map:dominosa.map @dominosa.rsp
49
50fifteen.exe: drawing.obj fifteen.obj malloc.obj midend.obj misc.obj \
51 noicon.res printing.obj random.obj version.obj windows.obj \
52 fifteen.rsp
53 link $(LFLAGS) -out:fifteen.exe -map:fifteen.map @fifteen.rsp
54
55fifteensolver.exe: fifteen2.obj malloc.obj misc.obj nullfe.obj random.obj \
56 fifteensolver.rsp
57 link $(LFLAGS) -out:fifteensolver.exe -map:fifteensolver.map @fifteensolver.rsp
58
59filling.exe: drawing.obj dsf.obj filling.obj malloc.obj midend.obj misc.obj \
60 noicon.res printing.obj random.obj version.obj windows.obj \
61 filling.rsp
62 link $(LFLAGS) -out:filling.exe -map:filling.map @filling.rsp
63
64fillingsolver.exe: dsf.obj filling2.obj malloc.obj misc.obj nullfe.obj \
65 random.obj fillingsolver.rsp
66 link $(LFLAGS) -out:fillingsolver.exe -map:fillingsolver.map @fillingsolver.rsp
67
68flip.exe: drawing.obj flip.obj malloc.obj midend.obj misc.obj noicon.res \
69 printing.obj random.obj tree234.obj version.obj windows.obj \
70 flip.rsp
71 link $(LFLAGS) -out:flip.exe -map:flip.map @flip.rsp
72
73flood.exe: drawing.obj flood.obj malloc.obj midend.obj misc.obj noicon.res \
74 printing.obj random.obj version.obj windows.obj flood.rsp
75 link $(LFLAGS) -out:flood.exe -map:flood.map @flood.rsp
76
77galaxies.exe: drawing.obj dsf.obj galaxies.obj malloc.obj midend.obj \
78 misc.obj noicon.res printing.obj random.obj version.obj \
79 windows.obj galaxies.rsp
80 link $(LFLAGS) -out:galaxies.exe -map:galaxies.map @galaxies.rsp
81
82galaxiespicture.exe: dsf.obj galaxie4.obj malloc.obj misc.obj nullfe.obj \
83 random.obj galaxiespicture.rsp
84 link $(LFLAGS) -out:galaxiespicture.exe -map:galaxiespicture.map @galaxiespicture.rsp
85
86galaxiessolver.exe: dsf.obj galaxie2.obj malloc.obj misc.obj nullfe.obj \
87 random.obj galaxiessolver.rsp
88 link $(LFLAGS) -out:galaxiessolver.exe -map:galaxiessolver.map @galaxiessolver.rsp
89
90guess.exe: drawing.obj guess.obj malloc.obj midend.obj misc.obj noicon.res \
91 printing.obj random.obj version.obj windows.obj guess.rsp
92 link $(LFLAGS) -out:guess.exe -map:guess.map @guess.rsp
93
94inertia.exe: drawing.obj inertia.obj malloc.obj midend.obj misc.obj \
95 noicon.res printing.obj random.obj version.obj windows.obj \
96 inertia.rsp
97 link $(LFLAGS) -out:inertia.exe -map:inertia.map @inertia.rsp
98
99keen.exe: drawing.obj dsf.obj keen.obj latin.obj malloc.obj maxflow.obj \
100 midend.obj misc.obj noicon.res printing.obj random.obj \
101 tree234.obj version.obj windows.obj keen.rsp
102 link $(LFLAGS) -out:keen.exe -map:keen.map @keen.rsp
103
104keensolver.exe: dsf.obj keen2.obj latin6.obj malloc.obj maxflow.obj misc.obj \
105 nullfe.obj random.obj tree234.obj keensolver.rsp
106 link $(LFLAGS) -out:keensolver.exe -map:keensolver.map @keensolver.rsp
107
108latincheck.exe: latin8.obj malloc.obj maxflow.obj misc.obj nullfe.obj \
109 random.obj tree234.obj latincheck.rsp
110 link $(LFLAGS) -out:latincheck.exe -map:latincheck.map @latincheck.rsp
111
112lightup.exe: combi.obj drawing.obj lightup.obj malloc.obj midend.obj \
113 misc.obj noicon.res printing.obj random.obj version.obj \
114 windows.obj lightup.rsp
115 link $(LFLAGS) -out:lightup.exe -map:lightup.map @lightup.rsp
116
117lightupsolver.exe: combi.obj lightup2.obj malloc.obj misc.obj nullfe.obj \
118 random.obj lightupsolver.rsp
119 link $(LFLAGS) -out:lightupsolver.exe -map:lightupsolver.map @lightupsolver.rsp
120
121loopy.exe: drawing.obj dsf.obj grid.obj loopgen.obj loopy.obj malloc.obj \
122 midend.obj misc.obj noicon.res penrose.obj printing.obj \
123 random.obj tree234.obj version.obj windows.obj loopy.rsp
124 link $(LFLAGS) -out:loopy.exe -map:loopy.map @loopy.rsp
125
126loopysolver.exe: dsf.obj grid.obj loopgen.obj loopy2.obj malloc.obj misc.obj \
127 nullfe.obj penrose.obj random.obj tree234.obj \
128 loopysolver.rsp
129 link $(LFLAGS) -out:loopysolver.exe -map:loopysolver.map @loopysolver.rsp
130
131magnets.exe: drawing.obj laydomino.obj magnets.obj malloc.obj midend.obj \
132 misc.obj noicon.res printing.obj random.obj version.obj \
133 windows.obj magnets.rsp
134 link $(LFLAGS) -out:magnets.exe -map:magnets.map @magnets.rsp
135
136magnetssolver.exe: laydomino.obj magnets2.obj malloc.obj misc.obj nullfe.obj \
137 random.obj magnetssolver.rsp
138 link $(LFLAGS) -out:magnetssolver.exe -map:magnetssolver.map @magnetssolver.rsp
139
140map.exe: drawing.obj dsf.obj malloc.obj map.obj midend.obj misc.obj \
141 noicon.res printing.obj random.obj version.obj windows.obj \
142 map.rsp
143 link $(LFLAGS) -out:map.exe -map:map.map @map.rsp
144
145mapsolver.exe: dsf.obj malloc.obj map2.obj misc.obj nullfe.obj random.obj \
146 mapsolver.rsp
147 link $(LFLAGS) -out:mapsolver.exe -map:mapsolver.map @mapsolver.rsp
148
149mineobfusc.exe: malloc.obj mines2.obj misc.obj nullfe.obj random.obj \
150 tree234.obj mineobfusc.rsp
151 link $(LFLAGS) -out:mineobfusc.exe -map:mineobfusc.map @mineobfusc.rsp
152
153mines.exe: drawing.obj malloc.obj midend.obj mines.obj misc.obj noicon.res \
154 printing.obj random.obj tree234.obj version.obj windows.obj \
155 mines.rsp
156 link $(LFLAGS) -out:mines.exe -map:mines.map @mines.rsp
157
158netgame.exe: drawing.obj dsf.obj findloop.obj malloc.obj midend.obj misc.obj \
159 net.obj noicon.res printing.obj random.obj tree234.obj \
160 version.obj windows.obj netgame.rsp
161 link $(LFLAGS) -out:netgame.exe -map:netgame.map @netgame.rsp
162
163netslide.exe: drawing.obj malloc.obj midend.obj misc.obj netslide.obj \
164 noicon.res printing.obj random.obj tree234.obj version.obj \
165 windows.obj netslide.rsp
166 link $(LFLAGS) -out:netslide.exe -map:netslide.map @netslide.rsp
167
168nullgame.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
169 nullgame.obj printing.obj random.obj version.obj windows.obj \
170 nullgame.rsp
171 link $(LFLAGS) -out:nullgame.exe -map:nullgame.map @nullgame.rsp
172
173palisade.exe: divvy.obj drawing.obj dsf.obj malloc.obj midend.obj misc.obj \
174 noicon.res palisade.obj printing.obj random.obj version.obj \
175 windows.obj palisade.rsp
176 link $(LFLAGS) -out:palisade.exe -map:palisade.map @palisade.rsp
177
178pattern.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
179 pattern.obj printing.obj random.obj version.obj windows.obj \
180 pattern.rsp
181 link $(LFLAGS) -out:pattern.exe -map:pattern.map @pattern.rsp
182
183patternpicture.exe: malloc.obj misc.obj nullfe.obj pattern4.obj random.obj \
184 patternpicture.rsp
185 link $(LFLAGS) -out:patternpicture.exe -map:patternpicture.map @patternpicture.rsp
186
187patternsolver.exe: malloc.obj misc.obj nullfe.obj pattern2.obj random.obj \
188 patternsolver.rsp
189 link $(LFLAGS) -out:patternsolver.exe -map:patternsolver.map @patternsolver.rsp
190
191pearl.exe: drawing.obj dsf.obj grid.obj loopgen.obj malloc.obj midend.obj \
192 misc.obj pearl.obj penrose.obj printing.obj random.obj \
193 tdq.obj tree234.obj version.obj windows.obj pearl.rsp
194 link $(LFLAGS) -out:pearl.exe -map:pearl.map @pearl.rsp
195
196pearlbench.exe: dsf.obj grid.obj loopgen.obj malloc.obj misc.obj nullfe.obj \
197 pearl2.obj penrose.obj random.obj tdq.obj tree234.obj \
198 pearlbench.rsp
199 link $(LFLAGS) -out:pearlbench.exe -map:pearlbench.map @pearlbench.rsp
200
201pegs.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res pegs.obj \
202 printing.obj random.obj tree234.obj version.obj windows.obj \
203 pegs.rsp
204 link $(LFLAGS) -out:pegs.exe -map:pegs.map @pegs.rsp
205
206puzzles.exe: blackbo3.obj bridges3.obj combi.obj cube3.obj divvy.obj \
207 dominos3.obj drawing.obj dsf.obj fifteen5.obj filling5.obj \
208 findloop.obj flip3.obj flood3.obj galaxie7.obj grid.obj \
209 guess3.obj inertia3.obj keen5.obj latin.obj laydomino.obj \
210 lightup5.obj list.obj loopgen.obj loopy5.obj magnets5.obj \
211 malloc.obj map5.obj maxflow.obj midend.obj mines5.obj \
212 misc.obj net3.obj netslid3.obj noicon.res palisad3.obj \
213 pattern7.obj pearl5.obj pegs3.obj penrose.obj printing.obj \
214 random.obj range3.obj rect3.obj samegam3.obj signpos5.obj \
215 singles5.obj sixteen3.obj slant5.obj solo5.obj tdq.obj \
216 tents5.obj towers5.obj tracks3.obj tree234.obj twiddle3.obj \
217 undead3.obj unequal5.obj unruly5.obj untangl3.obj \
218 version.obj windows1.obj puzzles.rsp
219 link $(LFLAGS) -out:puzzles.exe -map:puzzles.map @puzzles.rsp
220
221range.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj noicon.res \
222 printing.obj random.obj range.obj version.obj windows.obj \
223 range.rsp
224 link $(LFLAGS) -out:range.exe -map:range.map @range.rsp
225
226rect.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res printing.obj \
227 random.obj rect.obj version.obj windows.obj rect.rsp
228 link $(LFLAGS) -out:rect.exe -map:rect.map @rect.rsp
229
230samegame.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
231 printing.obj random.obj samegame.obj version.obj windows.obj \
232 samegame.rsp
233 link $(LFLAGS) -out:samegame.exe -map:samegame.map @samegame.rsp
234
235signpost.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj noicon.res \
236 printing.obj random.obj signpost.obj version.obj windows.obj \
237 signpost.rsp
238 link $(LFLAGS) -out:signpost.exe -map:signpost.map @signpost.rsp
239
240signpostsolver.exe: dsf.obj malloc.obj misc.obj nullfe.obj random.obj \
241 signpos2.obj signpostsolver.rsp
242 link $(LFLAGS) -out:signpostsolver.exe -map:signpostsolver.map @signpostsolver.rsp
243
244singles.exe: drawing.obj dsf.obj latin.obj malloc.obj maxflow.obj midend.obj \
245 misc.obj noicon.res printing.obj random.obj singles.obj \
246 tree234.obj version.obj windows.obj singles.rsp
247 link $(LFLAGS) -out:singles.exe -map:singles.map @singles.rsp
248
249singlessolver.exe: dsf.obj latin.obj malloc.obj maxflow.obj misc.obj \
250 nullfe.obj random.obj singles3.obj tree234.obj \
251 singlessolver.rsp
252 link $(LFLAGS) -out:singlessolver.exe -map:singlessolver.map @singlessolver.rsp
253
254sixteen.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
255 printing.obj random.obj sixteen.obj version.obj windows.obj \
256 sixteen.rsp
257 link $(LFLAGS) -out:sixteen.exe -map:sixteen.map @sixteen.rsp
258
259slant.exe: drawing.obj dsf.obj findloop.obj malloc.obj midend.obj misc.obj \
260 noicon.res printing.obj random.obj slant.obj version.obj \
261 windows.obj slant.rsp
262 link $(LFLAGS) -out:slant.exe -map:slant.map @slant.rsp
263
264slantsolver.exe: dsf.obj findloop.obj malloc.obj misc.obj nullfe.obj \
265 random.obj slant2.obj slantsolver.rsp
266 link $(LFLAGS) -out:slantsolver.exe -map:slantsolver.map @slantsolver.rsp
267
268solo.exe: divvy.obj drawing.obj dsf.obj malloc.obj midend.obj misc.obj \
269 noicon.res printing.obj random.obj solo.obj version.obj \
270 windows.obj solo.rsp
271 link $(LFLAGS) -out:solo.exe -map:solo.map @solo.rsp
272
273solosolver.exe: divvy.obj dsf.obj malloc.obj misc.obj nullfe.obj random.obj \
274 solo2.obj solosolver.rsp
275 link $(LFLAGS) -out:solosolver.exe -map:solosolver.map @solosolver.rsp
276
277tents.exe: drawing.obj dsf.obj malloc.obj maxflow.obj midend.obj misc.obj \
278 noicon.res printing.obj random.obj tents.obj version.obj \
279 windows.obj tents.rsp
280 link $(LFLAGS) -out:tents.exe -map:tents.map @tents.rsp
281
282tentssolver.exe: dsf.obj malloc.obj maxflow.obj misc.obj nullfe.obj \
283 random.obj tents3.obj tentssolver.rsp
284 link $(LFLAGS) -out:tentssolver.exe -map:tentssolver.map @tentssolver.rsp
285
286towers.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj misc.obj \
287 noicon.res printing.obj random.obj towers.obj tree234.obj \
288 version.obj windows.obj towers.rsp
289 link $(LFLAGS) -out:towers.exe -map:towers.map @towers.rsp
290
291towerssolver.exe: latin6.obj malloc.obj maxflow.obj misc.obj nullfe.obj \
292 random.obj towers2.obj tree234.obj towerssolver.rsp
293 link $(LFLAGS) -out:towerssolver.exe -map:towerssolver.map @towerssolver.rsp
294
295tracks.exe: drawing.obj dsf.obj findloop.obj malloc.obj midend.obj misc.obj \
296 noicon.res printing.obj random.obj tracks.obj version.obj \
297 windows.obj tracks.rsp
298 link $(LFLAGS) -out:tracks.exe -map:tracks.map @tracks.rsp
299
300twiddle.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
301 printing.obj random.obj twiddle.obj version.obj windows.obj \
302 twiddle.rsp
303 link $(LFLAGS) -out:twiddle.exe -map:twiddle.map @twiddle.rsp
304
305undead.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
306 printing.obj random.obj undead.obj version.obj windows.obj \
307 undead.rsp
308 link $(LFLAGS) -out:undead.exe -map:undead.map @undead.rsp
309
310unequal.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj \
311 misc.obj noicon.res printing.obj random.obj tree234.obj \
312 unequal.obj version.obj windows.obj unequal.rsp
313 link $(LFLAGS) -out:unequal.exe -map:unequal.map @unequal.rsp
314
315unequalsolver.exe: latin6.obj malloc.obj maxflow.obj misc.obj nullfe.obj \
316 random.obj tree234.obj unequal2.obj unequalsolver.rsp
317 link $(LFLAGS) -out:unequalsolver.exe -map:unequalsolver.map @unequalsolver.rsp
318
319unruly.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
320 printing.obj random.obj unruly.obj version.obj windows.obj \
321 unruly.rsp
322 link $(LFLAGS) -out:unruly.exe -map:unruly.map @unruly.rsp
323
324unrulysolver.exe: malloc.obj misc.obj nullfe.obj random.obj unruly2.obj \
325 unrulysolver.rsp
326 link $(LFLAGS) -out:unrulysolver.exe -map:unrulysolver.map @unrulysolver.rsp
327
328untangle.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
329 printing.obj random.obj tree234.obj untangle.obj version.obj \
330 windows.obj untangle.rsp
331 link $(LFLAGS) -out:untangle.exe -map:untangle.map @untangle.rsp
332
333blackbox.rsp: $(MAKEFILE)
334 echo /nologo /subsystem:windows > blackbox.rsp
335 echo blackbox.obj comctl32.lib comdlg32.lib >> blackbox.rsp
336 echo drawing.obj gdi32.lib malloc.obj midend.obj >> blackbox.rsp
337 echo misc.obj noicon.res printing.obj random.obj >> blackbox.rsp
338 echo user32.lib version.obj windows.obj winspool.lib >> blackbox.rsp
339
340bridges.rsp: $(MAKEFILE)
341 echo /nologo /subsystem:windows > bridges.rsp
342 echo bridges.obj comctl32.lib comdlg32.lib drawing.obj >> bridges.rsp
343 echo dsf.obj findloop.obj gdi32.lib malloc.obj >> bridges.rsp
344 echo midend.obj misc.obj noicon.res printing.obj >> bridges.rsp
345 echo random.obj user32.lib version.obj windows.obj >> bridges.rsp
346 echo winspool.lib >> bridges.rsp
347
348cube.rsp: $(MAKEFILE)
349 echo /nologo /subsystem:windows > cube.rsp
350 echo comctl32.lib comdlg32.lib cube.obj drawing.obj >> cube.rsp
351 echo gdi32.lib malloc.obj midend.obj misc.obj >> cube.rsp
352 echo noicon.res printing.obj random.obj user32.lib >> cube.rsp
353 echo version.obj windows.obj winspool.lib >> cube.rsp
354
355dominosa.rsp: $(MAKEFILE)
356 echo /nologo /subsystem:windows > dominosa.rsp
357 echo comctl32.lib comdlg32.lib dominosa.obj >> dominosa.rsp
358 echo drawing.obj gdi32.lib laydomino.obj malloc.obj >> dominosa.rsp
359 echo midend.obj misc.obj noicon.res printing.obj >> dominosa.rsp
360 echo random.obj user32.lib version.obj windows.obj >> dominosa.rsp
361 echo winspool.lib >> dominosa.rsp
362
363fifteen.rsp: $(MAKEFILE)
364 echo /nologo /subsystem:windows > fifteen.rsp
365 echo comctl32.lib comdlg32.lib drawing.obj fifteen.obj >> fifteen.rsp
366 echo gdi32.lib malloc.obj midend.obj misc.obj >> fifteen.rsp
367 echo noicon.res printing.obj random.obj user32.lib >> fifteen.rsp
368 echo version.obj windows.obj winspool.lib >> fifteen.rsp
369
370fifteensolver.rsp: $(MAKEFILE)
371 echo /nologo /subsystem:console > fifteensolver.rsp
372 echo fifteen2.obj malloc.obj misc.obj nullfe.obj >> fifteensolver.rsp
373 echo random.obj >> fifteensolver.rsp
374
375filling.rsp: $(MAKEFILE)
376 echo /nologo /subsystem:windows > filling.rsp
377 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> filling.rsp
378 echo filling.obj gdi32.lib malloc.obj midend.obj >> filling.rsp
379 echo misc.obj noicon.res printing.obj random.obj >> filling.rsp
380 echo user32.lib version.obj windows.obj winspool.lib >> filling.rsp
381
382fillingsolver.rsp: $(MAKEFILE)
383 echo /nologo /subsystem:console > fillingsolver.rsp
384 echo dsf.obj filling2.obj malloc.obj misc.obj >> fillingsolver.rsp
385 echo nullfe.obj random.obj >> fillingsolver.rsp
386
387flip.rsp: $(MAKEFILE)
388 echo /nologo /subsystem:windows > flip.rsp
389 echo comctl32.lib comdlg32.lib drawing.obj flip.obj >> flip.rsp
390 echo gdi32.lib malloc.obj midend.obj misc.obj >> flip.rsp
391 echo noicon.res printing.obj random.obj tree234.obj >> flip.rsp
392 echo user32.lib version.obj windows.obj winspool.lib >> flip.rsp
393
394flood.rsp: $(MAKEFILE)
395 echo /nologo /subsystem:windows > flood.rsp
396 echo comctl32.lib comdlg32.lib drawing.obj flood.obj >> flood.rsp
397 echo gdi32.lib malloc.obj midend.obj misc.obj >> flood.rsp
398 echo noicon.res printing.obj random.obj user32.lib >> flood.rsp
399 echo version.obj windows.obj winspool.lib >> flood.rsp
400
401galaxies.rsp: $(MAKEFILE)
402 echo /nologo /subsystem:windows > galaxies.rsp
403 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> galaxies.rsp
404 echo galaxies.obj gdi32.lib malloc.obj midend.obj >> galaxies.rsp
405 echo misc.obj noicon.res printing.obj random.obj >> galaxies.rsp
406 echo user32.lib version.obj windows.obj winspool.lib >> galaxies.rsp
407
408galaxiespicture.rsp: $(MAKEFILE)
409 echo /nologo /subsystem:console > galaxiespicture.rsp
410 echo dsf.obj galaxie4.obj malloc.obj misc.obj >> galaxiespicture.rsp
411 echo nullfe.obj random.obj >> galaxiespicture.rsp
412
413galaxiessolver.rsp: $(MAKEFILE)
414 echo /nologo /subsystem:console > galaxiessolver.rsp
415 echo dsf.obj galaxie2.obj malloc.obj misc.obj >> galaxiessolver.rsp
416 echo nullfe.obj random.obj >> galaxiessolver.rsp
417
418guess.rsp: $(MAKEFILE)
419 echo /nologo /subsystem:windows > guess.rsp
420 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> guess.rsp
421 echo guess.obj malloc.obj midend.obj misc.obj >> guess.rsp
422 echo noicon.res printing.obj random.obj user32.lib >> guess.rsp
423 echo version.obj windows.obj winspool.lib >> guess.rsp
424
425inertia.rsp: $(MAKEFILE)
426 echo /nologo /subsystem:windows > inertia.rsp
427 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> inertia.rsp
428 echo inertia.obj malloc.obj midend.obj misc.obj >> inertia.rsp
429 echo noicon.res printing.obj random.obj user32.lib >> inertia.rsp
430 echo version.obj windows.obj winspool.lib >> inertia.rsp
431
432keen.rsp: $(MAKEFILE)
433 echo /nologo /subsystem:windows > keen.rsp
434 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> keen.rsp
435 echo gdi32.lib keen.obj latin.obj malloc.obj >> keen.rsp
436 echo maxflow.obj midend.obj misc.obj noicon.res >> keen.rsp
437 echo printing.obj random.obj tree234.obj user32.lib >> keen.rsp
438 echo version.obj windows.obj winspool.lib >> keen.rsp
439
440keensolver.rsp: $(MAKEFILE)
441 echo /nologo /subsystem:console > keensolver.rsp
442 echo dsf.obj keen2.obj latin6.obj malloc.obj >> keensolver.rsp
443 echo maxflow.obj misc.obj nullfe.obj random.obj >> keensolver.rsp
444 echo tree234.obj >> keensolver.rsp
445
446latincheck.rsp: $(MAKEFILE)
447 echo /nologo /subsystem:console > latincheck.rsp
448 echo latin8.obj malloc.obj maxflow.obj misc.obj >> latincheck.rsp
449 echo nullfe.obj random.obj tree234.obj >> latincheck.rsp
450
451lightup.rsp: $(MAKEFILE)
452 echo /nologo /subsystem:windows > lightup.rsp
453 echo combi.obj comctl32.lib comdlg32.lib drawing.obj >> lightup.rsp
454 echo gdi32.lib lightup.obj malloc.obj midend.obj >> lightup.rsp
455 echo misc.obj noicon.res printing.obj random.obj >> lightup.rsp
456 echo user32.lib version.obj windows.obj winspool.lib >> lightup.rsp
457
458lightupsolver.rsp: $(MAKEFILE)
459 echo /nologo /subsystem:console > lightupsolver.rsp
460 echo combi.obj lightup2.obj malloc.obj misc.obj >> lightupsolver.rsp
461 echo nullfe.obj random.obj >> lightupsolver.rsp
462
463loopy.rsp: $(MAKEFILE)
464 echo /nologo /subsystem:windows > loopy.rsp
465 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> loopy.rsp
466 echo gdi32.lib grid.obj loopgen.obj loopy.obj >> loopy.rsp
467 echo malloc.obj midend.obj misc.obj noicon.res >> loopy.rsp
468 echo penrose.obj printing.obj random.obj tree234.obj >> loopy.rsp
469 echo user32.lib version.obj windows.obj winspool.lib >> loopy.rsp
470
471loopysolver.rsp: $(MAKEFILE)
472 echo /nologo /subsystem:console > loopysolver.rsp
473 echo dsf.obj grid.obj loopgen.obj loopy2.obj >> loopysolver.rsp
474 echo malloc.obj misc.obj nullfe.obj penrose.obj >> loopysolver.rsp
475 echo random.obj tree234.obj >> loopysolver.rsp
476
477magnets.rsp: $(MAKEFILE)
478 echo /nologo /subsystem:windows > magnets.rsp
479 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> magnets.rsp
480 echo laydomino.obj magnets.obj malloc.obj midend.obj >> magnets.rsp
481 echo misc.obj noicon.res printing.obj random.obj >> magnets.rsp
482 echo user32.lib version.obj windows.obj winspool.lib >> magnets.rsp
483
484magnetssolver.rsp: $(MAKEFILE)
485 echo /nologo /subsystem:console > magnetssolver.rsp
486 echo laydomino.obj magnets2.obj malloc.obj misc.obj >> magnetssolver.rsp
487 echo nullfe.obj random.obj >> magnetssolver.rsp
488
489map.rsp: $(MAKEFILE)
490 echo /nologo /subsystem:windows > map.rsp
491 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> map.rsp
492 echo gdi32.lib malloc.obj map.obj midend.obj misc.obj >> map.rsp
493 echo noicon.res printing.obj random.obj user32.lib >> map.rsp
494 echo version.obj windows.obj winspool.lib >> map.rsp
495
496mapsolver.rsp: $(MAKEFILE)
497 echo /nologo /subsystem:console > mapsolver.rsp
498 echo dsf.obj malloc.obj map2.obj misc.obj nullfe.obj >> mapsolver.rsp
499 echo random.obj >> mapsolver.rsp
500
501mineobfusc.rsp: $(MAKEFILE)
502 echo /nologo /subsystem:console > mineobfusc.rsp
503 echo malloc.obj mines2.obj misc.obj nullfe.obj >> mineobfusc.rsp
504 echo random.obj tree234.obj >> mineobfusc.rsp
505
506mines.rsp: $(MAKEFILE)
507 echo /nologo /subsystem:windows > mines.rsp
508 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> mines.rsp
509 echo malloc.obj midend.obj mines.obj misc.obj >> mines.rsp
510 echo noicon.res printing.obj random.obj tree234.obj >> mines.rsp
511 echo user32.lib version.obj windows.obj winspool.lib >> mines.rsp
512
513netgame.rsp: $(MAKEFILE)
514 echo /nologo /subsystem:windows > netgame.rsp
515 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> netgame.rsp
516 echo findloop.obj gdi32.lib malloc.obj midend.obj >> netgame.rsp
517 echo misc.obj net.obj noicon.res printing.obj >> netgame.rsp
518 echo random.obj tree234.obj user32.lib version.obj >> netgame.rsp
519 echo windows.obj winspool.lib >> netgame.rsp
520
521netslide.rsp: $(MAKEFILE)
522 echo /nologo /subsystem:windows > netslide.rsp
523 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> netslide.rsp
524 echo malloc.obj midend.obj misc.obj netslide.obj >> netslide.rsp
525 echo noicon.res printing.obj random.obj tree234.obj >> netslide.rsp
526 echo user32.lib version.obj windows.obj winspool.lib >> netslide.rsp
527
528nullgame.rsp: $(MAKEFILE)
529 echo /nologo /subsystem:windows > nullgame.rsp
530 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> nullgame.rsp
531 echo malloc.obj midend.obj misc.obj noicon.res >> nullgame.rsp
532 echo nullgame.obj printing.obj random.obj user32.lib >> nullgame.rsp
533 echo version.obj windows.obj winspool.lib >> nullgame.rsp
534
535palisade.rsp: $(MAKEFILE)
536 echo /nologo /subsystem:windows > palisade.rsp
537 echo comctl32.lib comdlg32.lib divvy.obj drawing.obj >> palisade.rsp
538 echo dsf.obj gdi32.lib malloc.obj midend.obj misc.obj >> palisade.rsp
539 echo noicon.res palisade.obj printing.obj random.obj >> palisade.rsp
540 echo user32.lib version.obj windows.obj winspool.lib >> palisade.rsp
541
542pattern.rsp: $(MAKEFILE)
543 echo /nologo /subsystem:windows > pattern.rsp
544 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> pattern.rsp
545 echo malloc.obj midend.obj misc.obj noicon.res >> pattern.rsp
546 echo pattern.obj printing.obj random.obj user32.lib >> pattern.rsp
547 echo version.obj windows.obj winspool.lib >> pattern.rsp
548
549patternpicture.rsp: $(MAKEFILE)
550 echo /nologo /subsystem:console > patternpicture.rsp
551 echo malloc.obj misc.obj nullfe.obj pattern4.obj >> patternpicture.rsp
552 echo random.obj >> patternpicture.rsp
553
554patternsolver.rsp: $(MAKEFILE)
555 echo /nologo /subsystem:console > patternsolver.rsp
556 echo malloc.obj misc.obj nullfe.obj pattern2.obj >> patternsolver.rsp
557 echo random.obj >> patternsolver.rsp
558
559pearl.rsp: $(MAKEFILE)
560 echo /nologo /subsystem:windows > pearl.rsp
561 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> pearl.rsp
562 echo gdi32.lib grid.obj loopgen.obj malloc.obj >> pearl.rsp
563 echo midend.obj misc.obj pearl.obj penrose.obj >> pearl.rsp
564 echo printing.obj random.obj tdq.obj tree234.obj >> pearl.rsp
565 echo user32.lib version.obj windows.obj winspool.lib >> pearl.rsp
566
567pearlbench.rsp: $(MAKEFILE)
568 echo /nologo /subsystem:console > pearlbench.rsp
569 echo dsf.obj grid.obj loopgen.obj malloc.obj misc.obj >> pearlbench.rsp
570 echo nullfe.obj pearl2.obj penrose.obj random.obj >> pearlbench.rsp
571 echo tdq.obj tree234.obj >> pearlbench.rsp
572
573pegs.rsp: $(MAKEFILE)
574 echo /nologo /subsystem:windows > pegs.rsp
575 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> pegs.rsp
576 echo malloc.obj midend.obj misc.obj noicon.res >> pegs.rsp
577 echo pegs.obj printing.obj random.obj tree234.obj >> pegs.rsp
578 echo user32.lib version.obj windows.obj winspool.lib >> pegs.rsp
579
580puzzles.rsp: $(MAKEFILE)
581 echo /nologo /subsystem:windows > puzzles.rsp
582 echo blackbo3.obj bridges3.obj combi.obj comctl32.lib >> puzzles.rsp
583 echo comdlg32.lib cube3.obj divvy.obj dominos3.obj >> puzzles.rsp
584 echo drawing.obj dsf.obj fifteen5.obj filling5.obj >> puzzles.rsp
585 echo findloop.obj flip3.obj flood3.obj galaxie7.obj >> puzzles.rsp
586 echo gdi32.lib grid.obj guess3.obj inertia3.obj >> puzzles.rsp
587 echo keen5.obj latin.obj laydomino.obj lightup5.obj >> puzzles.rsp
588 echo list.obj loopgen.obj loopy5.obj magnets5.obj >> puzzles.rsp
589 echo malloc.obj map5.obj maxflow.obj midend.obj >> puzzles.rsp
590 echo mines5.obj misc.obj net3.obj netslid3.obj >> puzzles.rsp
591 echo noicon.res palisad3.obj pattern7.obj pearl5.obj >> puzzles.rsp
592 echo pegs3.obj penrose.obj printing.obj random.obj >> puzzles.rsp
593 echo range3.obj rect3.obj samegam3.obj signpos5.obj >> puzzles.rsp
594 echo singles5.obj sixteen3.obj slant5.obj solo5.obj >> puzzles.rsp
595 echo tdq.obj tents5.obj towers5.obj tracks3.obj >> puzzles.rsp
596 echo tree234.obj twiddle3.obj undead3.obj unequal5.obj >> puzzles.rsp
597 echo unruly5.obj untangl3.obj user32.lib version.obj >> puzzles.rsp
598 echo windows1.obj winspool.lib >> puzzles.rsp
599
600range.rsp: $(MAKEFILE)
601 echo /nologo /subsystem:windows > range.rsp
602 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> range.rsp
603 echo gdi32.lib malloc.obj midend.obj misc.obj >> range.rsp
604 echo noicon.res printing.obj random.obj range.obj >> range.rsp
605 echo user32.lib version.obj windows.obj winspool.lib >> range.rsp
606
607rect.rsp: $(MAKEFILE)
608 echo /nologo /subsystem:windows > rect.rsp
609 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> rect.rsp
610 echo malloc.obj midend.obj misc.obj noicon.res >> rect.rsp
611 echo printing.obj random.obj rect.obj user32.lib >> rect.rsp
612 echo version.obj windows.obj winspool.lib >> rect.rsp
613
614samegame.rsp: $(MAKEFILE)
615 echo /nologo /subsystem:windows > samegame.rsp
616 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> samegame.rsp
617 echo malloc.obj midend.obj misc.obj noicon.res >> samegame.rsp
618 echo printing.obj random.obj samegame.obj user32.lib >> samegame.rsp
619 echo version.obj windows.obj winspool.lib >> samegame.rsp
620
621signpost.rsp: $(MAKEFILE)
622 echo /nologo /subsystem:windows > signpost.rsp
623 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> signpost.rsp
624 echo gdi32.lib malloc.obj midend.obj misc.obj >> signpost.rsp
625 echo noicon.res printing.obj random.obj signpost.obj >> signpost.rsp
626 echo user32.lib version.obj windows.obj winspool.lib >> signpost.rsp
627
628signpostsolver.rsp: $(MAKEFILE)
629 echo /nologo /subsystem:console > signpostsolver.rsp
630 echo dsf.obj malloc.obj misc.obj nullfe.obj random.obj >> signpostsolver.rsp
631 echo signpos2.obj >> signpostsolver.rsp
632
633singles.rsp: $(MAKEFILE)
634 echo /nologo /subsystem:windows > singles.rsp
635 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> singles.rsp
636 echo gdi32.lib latin.obj malloc.obj maxflow.obj >> singles.rsp
637 echo midend.obj misc.obj noicon.res printing.obj >> singles.rsp
638 echo random.obj singles.obj tree234.obj user32.lib >> singles.rsp
639 echo version.obj windows.obj winspool.lib >> singles.rsp
640
641singlessolver.rsp: $(MAKEFILE)
642 echo /nologo /subsystem:console > singlessolver.rsp
643 echo dsf.obj latin.obj malloc.obj maxflow.obj misc.obj >> singlessolver.rsp
644 echo nullfe.obj random.obj singles3.obj tree234.obj >> singlessolver.rsp
645
646sixteen.rsp: $(MAKEFILE)
647 echo /nologo /subsystem:windows > sixteen.rsp
648 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> sixteen.rsp
649 echo malloc.obj midend.obj misc.obj noicon.res >> sixteen.rsp
650 echo printing.obj random.obj sixteen.obj user32.lib >> sixteen.rsp
651 echo version.obj windows.obj winspool.lib >> sixteen.rsp
652
653slant.rsp: $(MAKEFILE)
654 echo /nologo /subsystem:windows > slant.rsp
655 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> slant.rsp
656 echo findloop.obj gdi32.lib malloc.obj midend.obj >> slant.rsp
657 echo misc.obj noicon.res printing.obj random.obj >> slant.rsp
658 echo slant.obj user32.lib version.obj windows.obj >> slant.rsp
659 echo winspool.lib >> slant.rsp
660
661slantsolver.rsp: $(MAKEFILE)
662 echo /nologo /subsystem:console > slantsolver.rsp
663 echo dsf.obj findloop.obj malloc.obj misc.obj >> slantsolver.rsp
664 echo nullfe.obj random.obj slant2.obj >> slantsolver.rsp
665
666solo.rsp: $(MAKEFILE)
667 echo /nologo /subsystem:windows > solo.rsp
668 echo comctl32.lib comdlg32.lib divvy.obj drawing.obj >> solo.rsp
669 echo dsf.obj gdi32.lib malloc.obj midend.obj misc.obj >> solo.rsp
670 echo noicon.res printing.obj random.obj solo.obj >> solo.rsp
671 echo user32.lib version.obj windows.obj winspool.lib >> solo.rsp
672
673solosolver.rsp: $(MAKEFILE)
674 echo /nologo /subsystem:console > solosolver.rsp
675 echo divvy.obj dsf.obj malloc.obj misc.obj nullfe.obj >> solosolver.rsp
676 echo random.obj solo2.obj >> solosolver.rsp
677
678tents.rsp: $(MAKEFILE)
679 echo /nologo /subsystem:windows > tents.rsp
680 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> tents.rsp
681 echo gdi32.lib malloc.obj maxflow.obj midend.obj >> tents.rsp
682 echo misc.obj noicon.res printing.obj random.obj >> tents.rsp
683 echo tents.obj user32.lib version.obj windows.obj >> tents.rsp
684 echo winspool.lib >> tents.rsp
685
686tentssolver.rsp: $(MAKEFILE)
687 echo /nologo /subsystem:console > tentssolver.rsp
688 echo dsf.obj malloc.obj maxflow.obj misc.obj >> tentssolver.rsp
689 echo nullfe.obj random.obj tents3.obj >> tentssolver.rsp
690
691towers.rsp: $(MAKEFILE)
692 echo /nologo /subsystem:windows > towers.rsp
693 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> towers.rsp
694 echo latin.obj malloc.obj maxflow.obj midend.obj >> towers.rsp
695 echo misc.obj noicon.res printing.obj random.obj >> towers.rsp
696 echo towers.obj tree234.obj user32.lib version.obj >> towers.rsp
697 echo windows.obj winspool.lib >> towers.rsp
698
699towerssolver.rsp: $(MAKEFILE)
700 echo /nologo /subsystem:console > towerssolver.rsp
701 echo latin6.obj malloc.obj maxflow.obj misc.obj >> towerssolver.rsp
702 echo nullfe.obj random.obj towers2.obj tree234.obj >> towerssolver.rsp
703
704tracks.rsp: $(MAKEFILE)
705 echo /nologo /subsystem:windows > tracks.rsp
706 echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> tracks.rsp
707 echo findloop.obj gdi32.lib malloc.obj midend.obj >> tracks.rsp
708 echo misc.obj noicon.res printing.obj random.obj >> tracks.rsp
709 echo tracks.obj user32.lib version.obj windows.obj >> tracks.rsp
710 echo winspool.lib >> tracks.rsp
711
712twiddle.rsp: $(MAKEFILE)
713 echo /nologo /subsystem:windows > twiddle.rsp
714 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> twiddle.rsp
715 echo malloc.obj midend.obj misc.obj noicon.res >> twiddle.rsp
716 echo printing.obj random.obj twiddle.obj user32.lib >> twiddle.rsp
717 echo version.obj windows.obj winspool.lib >> twiddle.rsp
718
719undead.rsp: $(MAKEFILE)
720 echo /nologo /subsystem:windows > undead.rsp
721 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> undead.rsp
722 echo malloc.obj midend.obj misc.obj noicon.res >> undead.rsp
723 echo printing.obj random.obj undead.obj user32.lib >> undead.rsp
724 echo version.obj windows.obj winspool.lib >> undead.rsp
725
726unequal.rsp: $(MAKEFILE)
727 echo /nologo /subsystem:windows > unequal.rsp
728 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> unequal.rsp
729 echo latin.obj malloc.obj maxflow.obj midend.obj >> unequal.rsp
730 echo misc.obj noicon.res printing.obj random.obj >> unequal.rsp
731 echo tree234.obj unequal.obj user32.lib version.obj >> unequal.rsp
732 echo windows.obj winspool.lib >> unequal.rsp
733
734unequalsolver.rsp: $(MAKEFILE)
735 echo /nologo /subsystem:console > unequalsolver.rsp
736 echo latin6.obj malloc.obj maxflow.obj misc.obj >> unequalsolver.rsp
737 echo nullfe.obj random.obj tree234.obj unequal2.obj >> unequalsolver.rsp
738
739unruly.rsp: $(MAKEFILE)
740 echo /nologo /subsystem:windows > unruly.rsp
741 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> unruly.rsp
742 echo malloc.obj midend.obj misc.obj noicon.res >> unruly.rsp
743 echo printing.obj random.obj unruly.obj user32.lib >> unruly.rsp
744 echo version.obj windows.obj winspool.lib >> unruly.rsp
745
746unrulysolver.rsp: $(MAKEFILE)
747 echo /nologo /subsystem:console > unrulysolver.rsp
748 echo malloc.obj misc.obj nullfe.obj random.obj >> unrulysolver.rsp
749 echo unruly2.obj >> unrulysolver.rsp
750
751untangle.rsp: $(MAKEFILE)
752 echo /nologo /subsystem:windows > untangle.rsp
753 echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> untangle.rsp
754 echo malloc.obj midend.obj misc.obj noicon.res >> untangle.rsp
755 echo printing.obj random.obj tree234.obj untangle.obj >> untangle.rsp
756 echo user32.lib version.obj windows.obj winspool.lib >> untangle.rsp
757
758blackbox.obj: .\blackbox.c .\puzzles.h
759 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\blackbox.c /Foblackbox.obj
760blackbo3.obj: .\blackbox.c .\puzzles.h
761 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\blackbox.c /Foblackbo3.obj
762bridges.obj: .\bridges.c .\puzzles.h
763 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\bridges.c /Fobridges.obj
764bridges3.obj: .\bridges.c .\puzzles.h
765 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\bridges.c /Fobridges3.obj
766combi.obj: .\combi.c .\puzzles.h
767 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\combi.c /Focombi.obj
768cube.obj: .\cube.c .\puzzles.h
769 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\cube.c /Focube.obj
770cube3.obj: .\cube.c .\puzzles.h
771 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\cube.c /Focube3.obj
772divvy.obj: .\divvy.c .\puzzles.h
773 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\divvy.c /Fodivvy.obj
774dominosa.obj: .\dominosa.c .\puzzles.h
775 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dominosa.c /Fodominosa.obj
776dominos3.obj: .\dominosa.c .\puzzles.h
777 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\dominosa.c /Fodominos3.obj
778drawing.obj: .\drawing.c .\puzzles.h
779 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\drawing.c /Fodrawing.obj
780dsf.obj: .\dsf.c .\puzzles.h
781 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dsf.c /Fodsf.obj
782fifteen.obj: .\fifteen.c .\puzzles.h
783 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\fifteen.c /Fofifteen.obj
784fifteen5.obj: .\fifteen.c .\puzzles.h
785 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\fifteen.c /Fofifteen5.obj
786fifteen2.obj: .\fifteen.c .\puzzles.h
787 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\fifteen.c /Fofifteen2.obj
788filling.obj: .\filling.c .\puzzles.h
789 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\filling.c /Fofilling.obj
790filling5.obj: .\filling.c .\puzzles.h
791 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\filling.c /Fofilling5.obj
792filling2.obj: .\filling.c .\puzzles.h
793 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\filling.c /Fofilling2.obj
794findloop.obj: .\findloop.c .\puzzles.h
795 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\findloop.c /Fofindloop.obj
796flip.obj: .\flip.c .\puzzles.h .\tree234.h
797 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\flip.c /Foflip.obj
798flip3.obj: .\flip.c .\puzzles.h .\tree234.h
799 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\flip.c /Foflip3.obj
800flood.obj: .\flood.c .\puzzles.h
801 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\flood.c /Foflood.obj
802flood3.obj: .\flood.c .\puzzles.h
803 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\flood.c /Foflood3.obj
804galaxies.obj: .\galaxies.c .\puzzles.h
805 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\galaxies.c /Fogalaxies.obj
806galaxie7.obj: .\galaxies.c .\puzzles.h
807 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\galaxies.c /Fogalaxie7.obj
808galaxie4.obj: .\galaxies.c .\puzzles.h
809 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_PICTURE_GENERATOR /c .\galaxies.c /Fogalaxie4.obj
810galaxie2.obj: .\galaxies.c .\puzzles.h
811 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\galaxies.c /Fogalaxie2.obj
812grid.obj: .\grid.c .\puzzles.h .\tree234.h .\grid.h .\penrose.h
813 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\grid.c /Fogrid.obj
814gtk.obj: .\gtk.c .\puzzles.h
815 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\gtk.c /Fogtk.obj
816guess.obj: .\guess.c .\puzzles.h
817 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\guess.c /Foguess.obj
818guess3.obj: .\guess.c .\puzzles.h
819 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\guess.c /Foguess3.obj
820inertia.obj: .\inertia.c .\puzzles.h
821 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\inertia.c /Foinertia.obj
822inertia3.obj: .\inertia.c .\puzzles.h
823 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\inertia.c /Foinertia3.obj
824keen.obj: .\keen.c .\puzzles.h .\latin.h
825 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\keen.c /Fokeen.obj
826keen5.obj: .\keen.c .\puzzles.h .\latin.h
827 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\keen.c /Fokeen5.obj
828keen2.obj: .\keen.c .\puzzles.h .\latin.h
829 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\keen.c /Fokeen2.obj
830latin.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h
831 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\latin.c /Folatin.obj
832latin8.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h
833 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_LATIN_TEST /c .\latin.c /Folatin8.obj
834latin6.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h
835 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\latin.c /Folatin6.obj
836laydomino.obj: .\laydomino.c .\puzzles.h
837 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\laydomino.c /Folaydomino.obj
838lightup.obj: .\lightup.c .\puzzles.h
839 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\lightup.c /Folightup.obj
840lightup5.obj: .\lightup.c .\puzzles.h
841 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\lightup.c /Folightup5.obj
842lightup2.obj: .\lightup.c .\puzzles.h
843 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\lightup.c /Folightup2.obj
844list.obj: .\list.c .\puzzles.h
845 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\list.c /Folist.obj
846loopgen.obj: .\loopgen.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
847 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopgen.c /Foloopgen.obj
848loopy.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
849 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopy.c /Foloopy.obj
850loopy5.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
851 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\loopy.c /Foloopy5.obj
852loopy2.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
853 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\loopy.c /Foloopy2.obj
854magnets.obj: .\magnets.c .\puzzles.h
855 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\magnets.c /Fomagnets.obj
856magnets5.obj: .\magnets.c .\puzzles.h
857 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\magnets.c /Fomagnets5.obj
858magnets2.obj: .\magnets.c .\puzzles.h
859 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\magnets.c /Fomagnets2.obj
860malloc.obj: .\malloc.c .\puzzles.h
861 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\malloc.c /Fomalloc.obj
862map.obj: .\map.c .\puzzles.h
863 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\map.c /Fomap.obj
864map5.obj: .\map.c .\puzzles.h
865 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\map.c /Fomap5.obj
866map2.obj: .\map.c .\puzzles.h
867 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\map.c /Fomap2.obj
868maxflow.obj: .\maxflow.c .\maxflow.h .\puzzles.h
869 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\maxflow.c /Fomaxflow.obj
870midend.obj: .\midend.c .\puzzles.h
871 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\midend.c /Fomidend.obj
872mines.obj: .\mines.c .\tree234.h .\puzzles.h
873 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\mines.c /Fomines.obj
874mines5.obj: .\mines.c .\tree234.h .\puzzles.h
875 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\mines.c /Fomines5.obj
876mines2.obj: .\mines.c .\tree234.h .\puzzles.h
877 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_OBFUSCATOR /c .\mines.c /Fomines2.obj
878misc.obj: .\misc.c .\puzzles.h
879 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\misc.c /Fomisc.obj
880net.obj: .\net.c .\puzzles.h .\tree234.h
881 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\net.c /Fonet.obj
882net3.obj: .\net.c .\puzzles.h .\tree234.h
883 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\net.c /Fonet3.obj
884netslide.obj: .\netslide.c .\puzzles.h .\tree234.h
885 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\netslide.c /Fonetslide.obj
886netslid3.obj: .\netslide.c .\puzzles.h .\tree234.h
887 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\netslide.c /Fonetslid3.obj
888no-icon.obj: .\no-icon.c
889 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\no-icon.c /Fono-icon.obj
890noicon.res: .\noicon.rc .\puzzles.rc2 .\resource.h
891 rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fonoicon.res .\noicon.rc
892nullfe.obj: .\nullfe.c .\puzzles.h
893 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullfe.c /Fonullfe.obj
894nullgame.obj: .\nullgame.c .\puzzles.h
895 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullgame.c /Fonullgame.obj
896obfusc.obj: .\obfusc.c .\puzzles.h
897 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\obfusc.c /Foobfusc.obj
898osx.obj: .\osx.m .\puzzles.h
899 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\osx.m /Foosx.obj
900palisade.obj: .\palisade.c .\puzzles.h
901 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\palisade.c /Fopalisade.obj
902palisad3.obj: .\palisade.c .\puzzles.h
903 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\palisade.c /Fopalisad3.obj
904pattern.obj: .\pattern.c .\puzzles.h
905 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pattern.c /Fopattern.obj
906pattern7.obj: .\pattern.c .\puzzles.h
907 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pattern.c /Fopattern7.obj
908pattern4.obj: .\pattern.c .\puzzles.h
909 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_PICTURE_GENERATOR /c .\pattern.c /Fopattern4.obj
910pattern2.obj: .\pattern.c .\puzzles.h
911 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pattern.c /Fopattern2.obj
912pearl.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h
913 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pearl.c /Fopearl.obj
914pearl5.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h
915 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pearl.c /Fopearl5.obj
916pearl2.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h
917 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pearl.c /Fopearl2.obj
918pegs.obj: .\pegs.c .\puzzles.h .\tree234.h
919 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pegs.c /Fopegs.obj
920pegs3.obj: .\pegs.c .\puzzles.h .\tree234.h
921 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pegs.c /Fopegs3.obj
922penrose.obj: .\penrose.c .\puzzles.h .\penrose.h
923 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\penrose.c /Fopenrose.obj
924printing.obj: .\printing.c .\puzzles.h
925 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\printing.c /Foprinting.obj
926ps.obj: .\ps.c .\puzzles.h
927 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\ps.c /Fops.obj
928random.obj: .\random.c .\puzzles.h
929 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\random.c /Forandom.obj
930range.obj: .\range.c .\puzzles.h
931 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\range.c /Forange.obj
932range3.obj: .\range.c .\puzzles.h
933 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\range.c /Forange3.obj
934rect.obj: .\rect.c .\puzzles.h
935 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\rect.c /Forect.obj
936rect3.obj: .\rect.c .\puzzles.h
937 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\rect.c /Forect3.obj
938samegame.obj: .\samegame.c .\puzzles.h
939 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\samegame.c /Fosamegame.obj
940samegam3.obj: .\samegame.c .\puzzles.h
941 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\samegame.c /Fosamegam3.obj
942signpost.obj: .\signpost.c .\puzzles.h
943 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\signpost.c /Fosignpost.obj
944signpos5.obj: .\signpost.c .\puzzles.h
945 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\signpost.c /Fosignpos5.obj
946signpos2.obj: .\signpost.c .\puzzles.h
947 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\signpost.c /Fosignpos2.obj
948singles.obj: .\singles.c .\puzzles.h .\latin.h
949 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\singles.c /Fosingles.obj
950singles5.obj: .\singles.c .\puzzles.h .\latin.h
951 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\singles.c /Fosingles5.obj
952singles3.obj: .\singles.c .\puzzles.h .\latin.h
953 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\singles.c /Fosingles3.obj
954sixteen.obj: .\sixteen.c .\puzzles.h
955 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\sixteen.c /Fosixteen.obj
956sixteen3.obj: .\sixteen.c .\puzzles.h
957 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\sixteen.c /Fosixteen3.obj
958slant.obj: .\slant.c .\puzzles.h
959 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\slant.c /Foslant.obj
960slant5.obj: .\slant.c .\puzzles.h
961 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\slant.c /Foslant5.obj
962slant2.obj: .\slant.c .\puzzles.h
963 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\slant.c /Foslant2.obj
964solo.obj: .\solo.c .\puzzles.h
965 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\solo.c /Fosolo.obj
966solo5.obj: .\solo.c .\puzzles.h
967 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\solo.c /Fosolo5.obj
968solo2.obj: .\solo.c .\puzzles.h
969 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\solo.c /Fosolo2.obj
970tdq.obj: .\tdq.c .\puzzles.h
971 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tdq.c /Fotdq.obj
972tents.obj: .\tents.c .\puzzles.h .\maxflow.h
973 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tents.c /Fotents.obj
974tents5.obj: .\tents.c .\puzzles.h .\maxflow.h
975 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\tents.c /Fotents5.obj
976tents3.obj: .\tents.c .\puzzles.h .\maxflow.h
977 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\tents.c /Fotents3.obj
978towers.obj: .\towers.c .\puzzles.h .\latin.h
979 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\towers.c /Fotowers.obj
980towers5.obj: .\towers.c .\puzzles.h .\latin.h
981 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\towers.c /Fotowers5.obj
982towers2.obj: .\towers.c .\puzzles.h .\latin.h
983 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\towers.c /Fotowers2.obj
984tracks.obj: .\tracks.c .\puzzles.h
985 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tracks.c /Fotracks.obj
986tracks3.obj: .\tracks.c .\puzzles.h
987 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\tracks.c /Fotracks3.obj
988tree234.obj: .\tree234.c .\tree234.h .\puzzles.h
989 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tree234.c /Fotree234.obj
990twiddle.obj: .\twiddle.c .\puzzles.h
991 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\twiddle.c /Fotwiddle.obj
992twiddle3.obj: .\twiddle.c .\puzzles.h
993 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\twiddle.c /Fotwiddle3.obj
994undead.obj: .\undead.c .\puzzles.h
995 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\undead.c /Foundead.obj
996undead3.obj: .\undead.c .\puzzles.h
997 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\undead.c /Foundead3.obj
998unequal.obj: .\unequal.c .\puzzles.h .\latin.h
999 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unequal.c /Founequal.obj
1000unequal5.obj: .\unequal.c .\puzzles.h .\latin.h
1001 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unequal.c /Founequal5.obj
1002unequal2.obj: .\unequal.c .\puzzles.h .\latin.h
1003 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unequal.c /Founequal2.obj
1004unruly.obj: .\unruly.c .\puzzles.h
1005 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unruly.c /Founruly.obj
1006unruly5.obj: .\unruly.c .\puzzles.h
1007 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unruly.c /Founruly5.obj
1008unruly2.obj: .\unruly.c .\puzzles.h
1009 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unruly.c /Founruly2.obj
1010untangle.obj: .\untangle.c .\puzzles.h .\tree234.h
1011 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\untangle.c /Fountangle.obj
1012untangl3.obj: .\untangle.c .\puzzles.h .\tree234.h
1013 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\untangle.c /Fountangl3.obj
1014version.obj: .\version.c .\version.h
1015 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\version.c /Foversion.obj
1016windows.obj: .\windows.c .\puzzles.h .\resource.h
1017 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\windows.c /Fowindows.obj
1018windows1.obj: .\windows.c .\puzzles.h .\resource.h
1019 cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\windows.c /Fowindows1.obj
1020
1021
1022clean: tidy
1023 -del *.exe
1024
1025tidy:
1026 -del *.obj
1027 -del *.res
1028 -del *.pch
1029 -del *.aps
1030 -del *.ilk
1031 -del *.pdb
1032 -del *.rsp
1033 -del *.dsp
1034 -del *.dsw
1035 -del *.ncb
1036 -del *.opt
1037 -del *.plg
1038 -del *.map
1039 -del *.idb
1040 -del debug.log
diff --git a/apps/plugins/puzzles/src/Makefile.wce b/apps/plugins/puzzles/src/Makefile.wce
new file mode 100644
index 0000000000..241e30b8df
--- /dev/null
+++ b/apps/plugins/puzzles/src/Makefile.wce
@@ -0,0 +1,808 @@
1# Makefile for puzzles on PocketPC using eMbedded Visual C.
2#
3# This file was created by `mkfiles.pl' from the `Recipe' file.
4# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.
5
6# If you rename this file to `Makefile', you should change this line,
7# so that the .rsp files still depend on the correct makefile.
8MAKEFILE = Makefile.wce
9
10# This makefile expects the environment to have been set up by one
11# of the PocketPC batch files wcearmv4.bat and wceemulator.bat. No
12# other build targets are currently supported, because they would
13# need a section in this if statement.
14!if "$(TARGETCPU)" == "emulator"
15PLATFORM_DEFS=/D "_i386_" /D "i_386_" /D "_X86_" /D "x86"
16CC=cl
17BASELIBS=commctrl.lib coredll.lib corelibc.lib aygshell.lib
18MACHINE=IX86
19!else
20PLATFORM_DEFS=/D "ARM" /D "_ARM_" /D "ARMV4"
21CC=clarm
22BASELIBS=commctrl.lib coredll.lib aygshell.lib
23MACHINE=ARM
24!endif
25
26# C compilation flags
27CFLAGS = /nologo /W3 /O1 /MC /D _WIN32_WCE=420 /D "WIN32_PLATFORM_PSPC=400" /D UNDER_CE=420 \
28 $(PLATFORM_DEFS) \
29 /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "NO_HTMLHELP"
30
31LFLAGS = /nologo /incremental:no \
32 /base:0x00010000 /stack:0x10000,0x1000 /entry:WinMainCRTStartup \
33 /nodefaultlib:libc.lib /nodefaultlib:libcmt.lib /nodefaultlib:msvcrt.lib /nodefaultlib:OLDNAMES.lib \
34 /subsystem:windowsce,4.20 /align:4096 /MACHINE:$(MACHINE)
35
36RCFL = /d UNDER_CE=420 /d _WIN32_WCE=420 /d "WIN32_PLATFORM_PSPC=400" \
37 $(PLATFORM_DEFS) \
38 /d "NDEBUG" /d "UNICODE" /d "_UNICODE"
39
40all: blackbox.exe bridges.exe cube.exe dominosa.exe fifteen.exe filling.exe \
41 flip.exe flood.exe galaxies.exe guess.exe inertia.exe \
42 keen.exe lightup.exe loopy.exe magnets.exe map.exe mines.exe \
43 netgame.exe netslide.exe nullgame.exe palisade.exe \
44 pattern.exe pearl.exe pegs.exe puzzles.exe range.exe \
45 rect.exe samegame.exe signpost.exe singles.exe sixteen.exe \
46 slant.exe solo.exe tents.exe towers.exe tracks.exe \
47 twiddle.exe undead.exe unequal.exe unruly.exe untangle.exe
48
49blackbox.exe: blackbox.obj drawing.obj malloc.obj midend.obj misc.obj \
50 noicon.res printing.obj random.obj version.obj windows.obj \
51 blackbox.rsp
52 link $(LFLAGS) -out:blackbox.exe -map:blackbox.map @blackbox.rsp
53
54bridges.exe: bridges.obj drawing.obj dsf.obj findloop.obj malloc.obj \
55 midend.obj misc.obj noicon.res printing.obj random.obj \
56 version.obj windows.obj bridges.rsp
57 link $(LFLAGS) -out:bridges.exe -map:bridges.map @bridges.rsp
58
59cube.exe: cube.obj drawing.obj malloc.obj midend.obj misc.obj noicon.res \
60 printing.obj random.obj version.obj windows.obj cube.rsp
61 link $(LFLAGS) -out:cube.exe -map:cube.map @cube.rsp
62
63dominosa.exe: dominosa.obj drawing.obj laydomino.obj malloc.obj midend.obj \
64 misc.obj noicon.res printing.obj random.obj version.obj \
65 windows.obj dominosa.rsp
66 link $(LFLAGS) -out:dominosa.exe -map:dominosa.map @dominosa.rsp
67
68fifteen.exe: drawing.obj fifteen.obj malloc.obj midend.obj misc.obj \
69 noicon.res printing.obj random.obj version.obj windows.obj \
70 fifteen.rsp
71 link $(LFLAGS) -out:fifteen.exe -map:fifteen.map @fifteen.rsp
72
73filling.exe: drawing.obj dsf.obj filling.obj malloc.obj midend.obj misc.obj \
74 noicon.res printing.obj random.obj version.obj windows.obj \
75 filling.rsp
76 link $(LFLAGS) -out:filling.exe -map:filling.map @filling.rsp
77
78flip.exe: drawing.obj flip.obj malloc.obj midend.obj misc.obj noicon.res \
79 printing.obj random.obj tree234.obj version.obj windows.obj \
80 flip.rsp
81 link $(LFLAGS) -out:flip.exe -map:flip.map @flip.rsp
82
83flood.exe: drawing.obj flood.obj malloc.obj midend.obj misc.obj noicon.res \
84 printing.obj random.obj version.obj windows.obj flood.rsp
85 link $(LFLAGS) -out:flood.exe -map:flood.map @flood.rsp
86
87galaxies.exe: drawing.obj dsf.obj galaxies.obj malloc.obj midend.obj \
88 misc.obj noicon.res printing.obj random.obj version.obj \
89 windows.obj galaxies.rsp
90 link $(LFLAGS) -out:galaxies.exe -map:galaxies.map @galaxies.rsp
91
92guess.exe: drawing.obj guess.obj malloc.obj midend.obj misc.obj noicon.res \
93 printing.obj random.obj version.obj windows.obj guess.rsp
94 link $(LFLAGS) -out:guess.exe -map:guess.map @guess.rsp
95
96inertia.exe: drawing.obj inertia.obj malloc.obj midend.obj misc.obj \
97 noicon.res printing.obj random.obj version.obj windows.obj \
98 inertia.rsp
99 link $(LFLAGS) -out:inertia.exe -map:inertia.map @inertia.rsp
100
101keen.exe: drawing.obj dsf.obj keen.obj latin.obj malloc.obj maxflow.obj \
102 midend.obj misc.obj noicon.res printing.obj random.obj \
103 tree234.obj version.obj windows.obj keen.rsp
104 link $(LFLAGS) -out:keen.exe -map:keen.map @keen.rsp
105
106lightup.exe: combi.obj drawing.obj lightup.obj malloc.obj midend.obj \
107 misc.obj noicon.res printing.obj random.obj version.obj \
108 windows.obj lightup.rsp
109 link $(LFLAGS) -out:lightup.exe -map:lightup.map @lightup.rsp
110
111loopy.exe: drawing.obj dsf.obj grid.obj loopgen.obj loopy.obj malloc.obj \
112 midend.obj misc.obj noicon.res penrose.obj printing.obj \
113 random.obj tree234.obj version.obj windows.obj loopy.rsp
114 link $(LFLAGS) -out:loopy.exe -map:loopy.map @loopy.rsp
115
116magnets.exe: drawing.obj laydomino.obj magnets.obj malloc.obj midend.obj \
117 misc.obj noicon.res printing.obj random.obj version.obj \
118 windows.obj magnets.rsp
119 link $(LFLAGS) -out:magnets.exe -map:magnets.map @magnets.rsp
120
121map.exe: drawing.obj dsf.obj malloc.obj map.obj midend.obj misc.obj \
122 noicon.res printing.obj random.obj version.obj windows.obj \
123 map.rsp
124 link $(LFLAGS) -out:map.exe -map:map.map @map.rsp
125
126mines.exe: drawing.obj malloc.obj midend.obj mines.obj misc.obj noicon.res \
127 printing.obj random.obj tree234.obj version.obj windows.obj \
128 mines.rsp
129 link $(LFLAGS) -out:mines.exe -map:mines.map @mines.rsp
130
131netgame.exe: drawing.obj dsf.obj findloop.obj malloc.obj midend.obj misc.obj \
132 net.obj noicon.res printing.obj random.obj tree234.obj \
133 version.obj windows.obj netgame.rsp
134 link $(LFLAGS) -out:netgame.exe -map:netgame.map @netgame.rsp
135
136netslide.exe: drawing.obj malloc.obj midend.obj misc.obj netslide.obj \
137 noicon.res printing.obj random.obj tree234.obj version.obj \
138 windows.obj netslide.rsp
139 link $(LFLAGS) -out:netslide.exe -map:netslide.map @netslide.rsp
140
141nullgame.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
142 nullgame.obj printing.obj random.obj version.obj windows.obj \
143 nullgame.rsp
144 link $(LFLAGS) -out:nullgame.exe -map:nullgame.map @nullgame.rsp
145
146palisade.exe: divvy.obj drawing.obj dsf.obj malloc.obj midend.obj misc.obj \
147 noicon.res palisade.obj printing.obj random.obj version.obj \
148 windows.obj palisade.rsp
149 link $(LFLAGS) -out:palisade.exe -map:palisade.map @palisade.rsp
150
151pattern.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
152 pattern.obj printing.obj random.obj version.obj windows.obj \
153 pattern.rsp
154 link $(LFLAGS) -out:pattern.exe -map:pattern.map @pattern.rsp
155
156pearl.exe: drawing.obj dsf.obj grid.obj loopgen.obj malloc.obj midend.obj \
157 misc.obj pearl.obj penrose.obj printing.obj random.obj \
158 tdq.obj tree234.obj version.obj windows.obj pearl.rsp
159 link $(LFLAGS) -out:pearl.exe -map:pearl.map @pearl.rsp
160
161pegs.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res pegs.obj \
162 printing.obj random.obj tree234.obj version.obj windows.obj \
163 pegs.rsp
164 link $(LFLAGS) -out:pegs.exe -map:pegs.map @pegs.rsp
165
166puzzles.exe: blackbo3.obj bridges3.obj combi.obj cube3.obj divvy.obj \
167 dominos3.obj drawing.obj dsf.obj fifteen5.obj filling5.obj \
168 findloop.obj flip3.obj flood3.obj galaxie7.obj grid.obj \
169 guess3.obj inertia3.obj keen5.obj latin.obj laydomino.obj \
170 lightup5.obj list.obj loopgen.obj loopy5.obj magnets5.obj \
171 malloc.obj map5.obj maxflow.obj midend.obj mines5.obj \
172 misc.obj net3.obj netslid3.obj noicon.res palisad3.obj \
173 pattern7.obj pearl5.obj pegs3.obj penrose.obj printing.obj \
174 random.obj range3.obj rect3.obj samegam3.obj signpos5.obj \
175 singles5.obj sixteen3.obj slant5.obj solo5.obj tdq.obj \
176 tents5.obj towers5.obj tracks3.obj tree234.obj twiddle3.obj \
177 undead3.obj unequal5.obj unruly5.obj untangl3.obj \
178 version.obj windows1.obj puzzles.rsp
179 link $(LFLAGS) -out:puzzles.exe -map:puzzles.map @puzzles.rsp
180
181range.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj noicon.res \
182 printing.obj random.obj range.obj version.obj windows.obj \
183 range.rsp
184 link $(LFLAGS) -out:range.exe -map:range.map @range.rsp
185
186rect.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res printing.obj \
187 random.obj rect.obj version.obj windows.obj rect.rsp
188 link $(LFLAGS) -out:rect.exe -map:rect.map @rect.rsp
189
190samegame.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
191 printing.obj random.obj samegame.obj version.obj windows.obj \
192 samegame.rsp
193 link $(LFLAGS) -out:samegame.exe -map:samegame.map @samegame.rsp
194
195signpost.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj noicon.res \
196 printing.obj random.obj signpost.obj version.obj windows.obj \
197 signpost.rsp
198 link $(LFLAGS) -out:signpost.exe -map:signpost.map @signpost.rsp
199
200singles.exe: drawing.obj dsf.obj latin.obj malloc.obj maxflow.obj midend.obj \
201 misc.obj noicon.res printing.obj random.obj singles.obj \
202 tree234.obj version.obj windows.obj singles.rsp
203 link $(LFLAGS) -out:singles.exe -map:singles.map @singles.rsp
204
205sixteen.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
206 printing.obj random.obj sixteen.obj version.obj windows.obj \
207 sixteen.rsp
208 link $(LFLAGS) -out:sixteen.exe -map:sixteen.map @sixteen.rsp
209
210slant.exe: drawing.obj dsf.obj findloop.obj malloc.obj midend.obj misc.obj \
211 noicon.res printing.obj random.obj slant.obj version.obj \
212 windows.obj slant.rsp
213 link $(LFLAGS) -out:slant.exe -map:slant.map @slant.rsp
214
215solo.exe: divvy.obj drawing.obj dsf.obj malloc.obj midend.obj misc.obj \
216 noicon.res printing.obj random.obj solo.obj version.obj \
217 windows.obj solo.rsp
218 link $(LFLAGS) -out:solo.exe -map:solo.map @solo.rsp
219
220tents.exe: drawing.obj dsf.obj malloc.obj maxflow.obj midend.obj misc.obj \
221 noicon.res printing.obj random.obj tents.obj version.obj \
222 windows.obj tents.rsp
223 link $(LFLAGS) -out:tents.exe -map:tents.map @tents.rsp
224
225towers.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj misc.obj \
226 noicon.res printing.obj random.obj towers.obj tree234.obj \
227 version.obj windows.obj towers.rsp
228 link $(LFLAGS) -out:towers.exe -map:towers.map @towers.rsp
229
230tracks.exe: drawing.obj dsf.obj findloop.obj malloc.obj midend.obj misc.obj \
231 noicon.res printing.obj random.obj tracks.obj version.obj \
232 windows.obj tracks.rsp
233 link $(LFLAGS) -out:tracks.exe -map:tracks.map @tracks.rsp
234
235twiddle.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
236 printing.obj random.obj twiddle.obj version.obj windows.obj \
237 twiddle.rsp
238 link $(LFLAGS) -out:twiddle.exe -map:twiddle.map @twiddle.rsp
239
240undead.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
241 printing.obj random.obj undead.obj version.obj windows.obj \
242 undead.rsp
243 link $(LFLAGS) -out:undead.exe -map:undead.map @undead.rsp
244
245unequal.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj \
246 misc.obj noicon.res printing.obj random.obj tree234.obj \
247 unequal.obj version.obj windows.obj unequal.rsp
248 link $(LFLAGS) -out:unequal.exe -map:unequal.map @unequal.rsp
249
250unruly.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
251 printing.obj random.obj unruly.obj version.obj windows.obj \
252 unruly.rsp
253 link $(LFLAGS) -out:unruly.exe -map:unruly.map @unruly.rsp
254
255untangle.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \
256 printing.obj random.obj tree234.obj untangle.obj version.obj \
257 windows.obj untangle.rsp
258 link $(LFLAGS) -out:untangle.exe -map:untangle.map @untangle.rsp
259
260blackbox.rsp: $(MAKEFILE)
261 echo $(BASELIBS) > blackbox.rsp
262 echo blackbox.obj drawing.obj malloc.obj midend.obj >> blackbox.rsp
263 echo misc.obj noicon.res printing.obj random.obj >> blackbox.rsp
264 echo version.obj windows.obj >> blackbox.rsp
265
266bridges.rsp: $(MAKEFILE)
267 echo $(BASELIBS) > bridges.rsp
268 echo bridges.obj drawing.obj dsf.obj findloop.obj >> bridges.rsp
269 echo malloc.obj midend.obj misc.obj noicon.res >> bridges.rsp
270 echo printing.obj random.obj version.obj windows.obj >> bridges.rsp
271
272cube.rsp: $(MAKEFILE)
273 echo $(BASELIBS) > cube.rsp
274 echo cube.obj drawing.obj malloc.obj midend.obj >> cube.rsp
275 echo misc.obj noicon.res printing.obj random.obj >> cube.rsp
276 echo version.obj windows.obj >> cube.rsp
277
278dominosa.rsp: $(MAKEFILE)
279 echo $(BASELIBS) > dominosa.rsp
280 echo dominosa.obj drawing.obj laydomino.obj malloc.obj >> dominosa.rsp
281 echo midend.obj misc.obj noicon.res printing.obj >> dominosa.rsp
282 echo random.obj version.obj windows.obj >> dominosa.rsp
283
284fifteen.rsp: $(MAKEFILE)
285 echo $(BASELIBS) > fifteen.rsp
286 echo drawing.obj fifteen.obj malloc.obj midend.obj >> fifteen.rsp
287 echo misc.obj noicon.res printing.obj random.obj >> fifteen.rsp
288 echo version.obj windows.obj >> fifteen.rsp
289
290filling.rsp: $(MAKEFILE)
291 echo $(BASELIBS) > filling.rsp
292 echo drawing.obj dsf.obj filling.obj malloc.obj >> filling.rsp
293 echo midend.obj misc.obj noicon.res printing.obj >> filling.rsp
294 echo random.obj version.obj windows.obj >> filling.rsp
295
296flip.rsp: $(MAKEFILE)
297 echo $(BASELIBS) > flip.rsp
298 echo drawing.obj flip.obj malloc.obj midend.obj >> flip.rsp
299 echo misc.obj noicon.res printing.obj random.obj >> flip.rsp
300 echo tree234.obj version.obj windows.obj >> flip.rsp
301
302flood.rsp: $(MAKEFILE)
303 echo $(BASELIBS) > flood.rsp
304 echo drawing.obj flood.obj malloc.obj midend.obj >> flood.rsp
305 echo misc.obj noicon.res printing.obj random.obj >> flood.rsp
306 echo version.obj windows.obj >> flood.rsp
307
308galaxies.rsp: $(MAKEFILE)
309 echo $(BASELIBS) > galaxies.rsp
310 echo drawing.obj dsf.obj galaxies.obj malloc.obj >> galaxies.rsp
311 echo midend.obj misc.obj noicon.res printing.obj >> galaxies.rsp
312 echo random.obj version.obj windows.obj >> galaxies.rsp
313
314guess.rsp: $(MAKEFILE)
315 echo $(BASELIBS) > guess.rsp
316 echo drawing.obj guess.obj malloc.obj midend.obj >> guess.rsp
317 echo misc.obj noicon.res printing.obj random.obj >> guess.rsp
318 echo version.obj windows.obj >> guess.rsp
319
320inertia.rsp: $(MAKEFILE)
321 echo $(BASELIBS) > inertia.rsp
322 echo drawing.obj inertia.obj malloc.obj midend.obj >> inertia.rsp
323 echo misc.obj noicon.res printing.obj random.obj >> inertia.rsp
324 echo version.obj windows.obj >> inertia.rsp
325
326keen.rsp: $(MAKEFILE)
327 echo $(BASELIBS) > keen.rsp
328 echo drawing.obj dsf.obj keen.obj latin.obj malloc.obj >> keen.rsp
329 echo maxflow.obj midend.obj misc.obj noicon.res >> keen.rsp
330 echo printing.obj random.obj tree234.obj version.obj >> keen.rsp
331 echo windows.obj >> keen.rsp
332
333lightup.rsp: $(MAKEFILE)
334 echo $(BASELIBS) > lightup.rsp
335 echo combi.obj drawing.obj lightup.obj malloc.obj >> lightup.rsp
336 echo midend.obj misc.obj noicon.res printing.obj >> lightup.rsp
337 echo random.obj version.obj windows.obj >> lightup.rsp
338
339loopy.rsp: $(MAKEFILE)
340 echo $(BASELIBS) > loopy.rsp
341 echo drawing.obj dsf.obj grid.obj loopgen.obj >> loopy.rsp
342 echo loopy.obj malloc.obj midend.obj misc.obj >> loopy.rsp
343 echo noicon.res penrose.obj printing.obj random.obj >> loopy.rsp
344 echo tree234.obj version.obj windows.obj >> loopy.rsp
345
346magnets.rsp: $(MAKEFILE)
347 echo $(BASELIBS) > magnets.rsp
348 echo drawing.obj laydomino.obj magnets.obj malloc.obj >> magnets.rsp
349 echo midend.obj misc.obj noicon.res printing.obj >> magnets.rsp
350 echo random.obj version.obj windows.obj >> magnets.rsp
351
352map.rsp: $(MAKEFILE)
353 echo $(BASELIBS) > map.rsp
354 echo drawing.obj dsf.obj malloc.obj map.obj midend.obj >> map.rsp
355 echo misc.obj noicon.res printing.obj random.obj >> map.rsp
356 echo version.obj windows.obj >> map.rsp
357
358mines.rsp: $(MAKEFILE)
359 echo $(BASELIBS) > mines.rsp
360 echo drawing.obj malloc.obj midend.obj mines.obj >> mines.rsp
361 echo misc.obj noicon.res printing.obj random.obj >> mines.rsp
362 echo tree234.obj version.obj windows.obj >> mines.rsp
363
364netgame.rsp: $(MAKEFILE)
365 echo $(BASELIBS) > netgame.rsp
366 echo drawing.obj dsf.obj findloop.obj malloc.obj >> netgame.rsp
367 echo midend.obj misc.obj net.obj noicon.res >> netgame.rsp
368 echo printing.obj random.obj tree234.obj version.obj >> netgame.rsp
369 echo windows.obj >> netgame.rsp
370
371netslide.rsp: $(MAKEFILE)
372 echo $(BASELIBS) > netslide.rsp
373 echo drawing.obj malloc.obj midend.obj misc.obj >> netslide.rsp
374 echo netslide.obj noicon.res printing.obj random.obj >> netslide.rsp
375 echo tree234.obj version.obj windows.obj >> netslide.rsp
376
377nullgame.rsp: $(MAKEFILE)
378 echo $(BASELIBS) > nullgame.rsp
379 echo drawing.obj malloc.obj midend.obj misc.obj >> nullgame.rsp
380 echo noicon.res nullgame.obj printing.obj random.obj >> nullgame.rsp
381 echo version.obj windows.obj >> nullgame.rsp
382
383palisade.rsp: $(MAKEFILE)
384 echo $(BASELIBS) > palisade.rsp
385 echo divvy.obj drawing.obj dsf.obj malloc.obj >> palisade.rsp
386 echo midend.obj misc.obj noicon.res palisade.obj >> palisade.rsp
387 echo printing.obj random.obj version.obj windows.obj >> palisade.rsp
388
389pattern.rsp: $(MAKEFILE)
390 echo $(BASELIBS) > pattern.rsp
391 echo drawing.obj malloc.obj midend.obj misc.obj >> pattern.rsp
392 echo noicon.res pattern.obj printing.obj random.obj >> pattern.rsp
393 echo version.obj windows.obj >> pattern.rsp
394
395pearl.rsp: $(MAKEFILE)
396 echo $(BASELIBS) > pearl.rsp
397 echo drawing.obj dsf.obj grid.obj loopgen.obj >> pearl.rsp
398 echo malloc.obj midend.obj misc.obj pearl.obj >> pearl.rsp
399 echo penrose.obj printing.obj random.obj tdq.obj >> pearl.rsp
400 echo tree234.obj version.obj windows.obj >> pearl.rsp
401
402pegs.rsp: $(MAKEFILE)
403 echo $(BASELIBS) > pegs.rsp
404 echo drawing.obj malloc.obj midend.obj misc.obj >> pegs.rsp
405 echo noicon.res pegs.obj printing.obj random.obj >> pegs.rsp
406 echo tree234.obj version.obj windows.obj >> pegs.rsp
407
408puzzles.rsp: $(MAKEFILE)
409 echo $(BASELIBS) > puzzles.rsp
410 echo blackbo3.obj bridges3.obj combi.obj cube3.obj >> puzzles.rsp
411 echo divvy.obj dominos3.obj drawing.obj dsf.obj >> puzzles.rsp
412 echo fifteen5.obj filling5.obj findloop.obj flip3.obj >> puzzles.rsp
413 echo flood3.obj galaxie7.obj grid.obj guess3.obj >> puzzles.rsp
414 echo inertia3.obj keen5.obj latin.obj laydomino.obj >> puzzles.rsp
415 echo lightup5.obj list.obj loopgen.obj loopy5.obj >> puzzles.rsp
416 echo magnets5.obj malloc.obj map5.obj maxflow.obj >> puzzles.rsp
417 echo midend.obj mines5.obj misc.obj net3.obj >> puzzles.rsp
418 echo netslid3.obj noicon.res palisad3.obj pattern7.obj >> puzzles.rsp
419 echo pearl5.obj pegs3.obj penrose.obj printing.obj >> puzzles.rsp
420 echo random.obj range3.obj rect3.obj samegam3.obj >> puzzles.rsp
421 echo signpos5.obj singles5.obj sixteen3.obj slant5.obj >> puzzles.rsp
422 echo solo5.obj tdq.obj tents5.obj towers5.obj >> puzzles.rsp
423 echo tracks3.obj tree234.obj twiddle3.obj undead3.obj >> puzzles.rsp
424 echo unequal5.obj unruly5.obj untangl3.obj version.obj >> puzzles.rsp
425 echo windows1.obj >> puzzles.rsp
426
427range.rsp: $(MAKEFILE)
428 echo $(BASELIBS) > range.rsp
429 echo drawing.obj dsf.obj malloc.obj midend.obj >> range.rsp
430 echo misc.obj noicon.res printing.obj random.obj >> range.rsp
431 echo range.obj version.obj windows.obj >> range.rsp
432
433rect.rsp: $(MAKEFILE)
434 echo $(BASELIBS) > rect.rsp
435 echo drawing.obj malloc.obj midend.obj misc.obj >> rect.rsp
436 echo noicon.res printing.obj random.obj rect.obj >> rect.rsp
437 echo version.obj windows.obj >> rect.rsp
438
439samegame.rsp: $(MAKEFILE)
440 echo $(BASELIBS) > samegame.rsp
441 echo drawing.obj malloc.obj midend.obj misc.obj >> samegame.rsp
442 echo noicon.res printing.obj random.obj samegame.obj >> samegame.rsp
443 echo version.obj windows.obj >> samegame.rsp
444
445signpost.rsp: $(MAKEFILE)
446 echo $(BASELIBS) > signpost.rsp
447 echo drawing.obj dsf.obj malloc.obj midend.obj >> signpost.rsp
448 echo misc.obj noicon.res printing.obj random.obj >> signpost.rsp
449 echo signpost.obj version.obj windows.obj >> signpost.rsp
450
451singles.rsp: $(MAKEFILE)
452 echo $(BASELIBS) > singles.rsp
453 echo drawing.obj dsf.obj latin.obj malloc.obj >> singles.rsp
454 echo maxflow.obj midend.obj misc.obj noicon.res >> singles.rsp
455 echo printing.obj random.obj singles.obj tree234.obj >> singles.rsp
456 echo version.obj windows.obj >> singles.rsp
457
458sixteen.rsp: $(MAKEFILE)
459 echo $(BASELIBS) > sixteen.rsp
460 echo drawing.obj malloc.obj midend.obj misc.obj >> sixteen.rsp
461 echo noicon.res printing.obj random.obj sixteen.obj >> sixteen.rsp
462 echo version.obj windows.obj >> sixteen.rsp
463
464slant.rsp: $(MAKEFILE)
465 echo $(BASELIBS) > slant.rsp
466 echo drawing.obj dsf.obj findloop.obj malloc.obj >> slant.rsp
467 echo midend.obj misc.obj noicon.res printing.obj >> slant.rsp
468 echo random.obj slant.obj version.obj windows.obj >> slant.rsp
469
470solo.rsp: $(MAKEFILE)
471 echo $(BASELIBS) > solo.rsp
472 echo divvy.obj drawing.obj dsf.obj malloc.obj >> solo.rsp
473 echo midend.obj misc.obj noicon.res printing.obj >> solo.rsp
474 echo random.obj solo.obj version.obj windows.obj >> solo.rsp
475
476tents.rsp: $(MAKEFILE)
477 echo $(BASELIBS) > tents.rsp
478 echo drawing.obj dsf.obj malloc.obj maxflow.obj >> tents.rsp
479 echo midend.obj misc.obj noicon.res printing.obj >> tents.rsp
480 echo random.obj tents.obj version.obj windows.obj >> tents.rsp
481
482towers.rsp: $(MAKEFILE)
483 echo $(BASELIBS) > towers.rsp
484 echo drawing.obj latin.obj malloc.obj maxflow.obj >> towers.rsp
485 echo midend.obj misc.obj noicon.res printing.obj >> towers.rsp
486 echo random.obj towers.obj tree234.obj version.obj >> towers.rsp
487 echo windows.obj >> towers.rsp
488
489tracks.rsp: $(MAKEFILE)
490 echo $(BASELIBS) > tracks.rsp
491 echo drawing.obj dsf.obj findloop.obj malloc.obj >> tracks.rsp
492 echo midend.obj misc.obj noicon.res printing.obj >> tracks.rsp
493 echo random.obj tracks.obj version.obj windows.obj >> tracks.rsp
494
495twiddle.rsp: $(MAKEFILE)
496 echo $(BASELIBS) > twiddle.rsp
497 echo drawing.obj malloc.obj midend.obj misc.obj >> twiddle.rsp
498 echo noicon.res printing.obj random.obj twiddle.obj >> twiddle.rsp
499 echo version.obj windows.obj >> twiddle.rsp
500
501undead.rsp: $(MAKEFILE)
502 echo $(BASELIBS) > undead.rsp
503 echo drawing.obj malloc.obj midend.obj misc.obj >> undead.rsp
504 echo noicon.res printing.obj random.obj undead.obj >> undead.rsp
505 echo version.obj windows.obj >> undead.rsp
506
507unequal.rsp: $(MAKEFILE)
508 echo $(BASELIBS) > unequal.rsp
509 echo drawing.obj latin.obj malloc.obj maxflow.obj >> unequal.rsp
510 echo midend.obj misc.obj noicon.res printing.obj >> unequal.rsp
511 echo random.obj tree234.obj unequal.obj version.obj >> unequal.rsp
512 echo windows.obj >> unequal.rsp
513
514unruly.rsp: $(MAKEFILE)
515 echo $(BASELIBS) > unruly.rsp
516 echo drawing.obj malloc.obj midend.obj misc.obj >> unruly.rsp
517 echo noicon.res printing.obj random.obj unruly.obj >> unruly.rsp
518 echo version.obj windows.obj >> unruly.rsp
519
520untangle.rsp: $(MAKEFILE)
521 echo $(BASELIBS) > untangle.rsp
522 echo drawing.obj malloc.obj midend.obj misc.obj >> untangle.rsp
523 echo noicon.res printing.obj random.obj tree234.obj >> untangle.rsp
524 echo untangle.obj version.obj windows.obj >> untangle.rsp
525
526blackbox.obj: .\blackbox.c .\puzzles.h
527 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\blackbox.c /Foblackbox.obj
528blackbo3.obj: .\blackbox.c .\puzzles.h
529 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\blackbox.c /Foblackbo3.obj
530bridges.obj: .\bridges.c .\puzzles.h
531 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\bridges.c /Fobridges.obj
532bridges3.obj: .\bridges.c .\puzzles.h
533 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\bridges.c /Fobridges3.obj
534combi.obj: .\combi.c .\puzzles.h
535 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\combi.c /Focombi.obj
536cube.obj: .\cube.c .\puzzles.h
537 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\cube.c /Focube.obj
538cube3.obj: .\cube.c .\puzzles.h
539 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\cube.c /Focube3.obj
540divvy.obj: .\divvy.c .\puzzles.h
541 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\divvy.c /Fodivvy.obj
542dominosa.obj: .\dominosa.c .\puzzles.h
543 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dominosa.c /Fodominosa.obj
544dominos3.obj: .\dominosa.c .\puzzles.h
545 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\dominosa.c /Fodominos3.obj
546drawing.obj: .\drawing.c .\puzzles.h
547 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\drawing.c /Fodrawing.obj
548dsf.obj: .\dsf.c .\puzzles.h
549 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dsf.c /Fodsf.obj
550fifteen.obj: .\fifteen.c .\puzzles.h
551 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\fifteen.c /Fofifteen.obj
552fifteen5.obj: .\fifteen.c .\puzzles.h
553 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\fifteen.c /Fofifteen5.obj
554fifteen2.obj: .\fifteen.c .\puzzles.h
555 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\fifteen.c /Fofifteen2.obj
556filling.obj: .\filling.c .\puzzles.h
557 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\filling.c /Fofilling.obj
558filling5.obj: .\filling.c .\puzzles.h
559 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\filling.c /Fofilling5.obj
560filling2.obj: .\filling.c .\puzzles.h
561 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\filling.c /Fofilling2.obj
562findloop.obj: .\findloop.c .\puzzles.h
563 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\findloop.c /Fofindloop.obj
564flip.obj: .\flip.c .\puzzles.h .\tree234.h
565 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\flip.c /Foflip.obj
566flip3.obj: .\flip.c .\puzzles.h .\tree234.h
567 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\flip.c /Foflip3.obj
568flood.obj: .\flood.c .\puzzles.h
569 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\flood.c /Foflood.obj
570flood3.obj: .\flood.c .\puzzles.h
571 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\flood.c /Foflood3.obj
572galaxies.obj: .\galaxies.c .\puzzles.h
573 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\galaxies.c /Fogalaxies.obj
574galaxie7.obj: .\galaxies.c .\puzzles.h
575 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\galaxies.c /Fogalaxie7.obj
576galaxie4.obj: .\galaxies.c .\puzzles.h
577 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_PICTURE_GENERATOR /c .\galaxies.c /Fogalaxie4.obj
578galaxie2.obj: .\galaxies.c .\puzzles.h
579 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\galaxies.c /Fogalaxie2.obj
580grid.obj: .\grid.c .\puzzles.h .\tree234.h .\grid.h .\penrose.h
581 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\grid.c /Fogrid.obj
582gtk.obj: .\gtk.c .\puzzles.h
583 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\gtk.c /Fogtk.obj
584guess.obj: .\guess.c .\puzzles.h
585 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\guess.c /Foguess.obj
586guess3.obj: .\guess.c .\puzzles.h
587 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\guess.c /Foguess3.obj
588inertia.obj: .\inertia.c .\puzzles.h
589 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\inertia.c /Foinertia.obj
590inertia3.obj: .\inertia.c .\puzzles.h
591 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\inertia.c /Foinertia3.obj
592keen.obj: .\keen.c .\puzzles.h .\latin.h
593 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\keen.c /Fokeen.obj
594keen5.obj: .\keen.c .\puzzles.h .\latin.h
595 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\keen.c /Fokeen5.obj
596keen2.obj: .\keen.c .\puzzles.h .\latin.h
597 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\keen.c /Fokeen2.obj
598latin.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h
599 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\latin.c /Folatin.obj
600latin8.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h
601 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_LATIN_TEST /c .\latin.c /Folatin8.obj
602latin6.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h
603 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\latin.c /Folatin6.obj
604laydomino.obj: .\laydomino.c .\puzzles.h
605 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\laydomino.c /Folaydomino.obj
606lightup.obj: .\lightup.c .\puzzles.h
607 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\lightup.c /Folightup.obj
608lightup5.obj: .\lightup.c .\puzzles.h
609 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\lightup.c /Folightup5.obj
610lightup2.obj: .\lightup.c .\puzzles.h
611 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\lightup.c /Folightup2.obj
612list.obj: .\list.c .\puzzles.h
613 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\list.c /Folist.obj
614loopgen.obj: .\loopgen.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
615 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopgen.c /Foloopgen.obj
616loopy.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
617 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopy.c /Foloopy.obj
618loopy5.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
619 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\loopy.c /Foloopy5.obj
620loopy2.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h
621 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\loopy.c /Foloopy2.obj
622magnets.obj: .\magnets.c .\puzzles.h
623 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\magnets.c /Fomagnets.obj
624magnets5.obj: .\magnets.c .\puzzles.h
625 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\magnets.c /Fomagnets5.obj
626magnets2.obj: .\magnets.c .\puzzles.h
627 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\magnets.c /Fomagnets2.obj
628malloc.obj: .\malloc.c .\puzzles.h
629 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\malloc.c /Fomalloc.obj
630map.obj: .\map.c .\puzzles.h
631 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\map.c /Fomap.obj
632map5.obj: .\map.c .\puzzles.h
633 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\map.c /Fomap5.obj
634map2.obj: .\map.c .\puzzles.h
635 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\map.c /Fomap2.obj
636maxflow.obj: .\maxflow.c .\maxflow.h .\puzzles.h
637 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\maxflow.c /Fomaxflow.obj
638midend.obj: .\midend.c .\puzzles.h
639 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\midend.c /Fomidend.obj
640mines.obj: .\mines.c .\tree234.h .\puzzles.h
641 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\mines.c /Fomines.obj
642mines5.obj: .\mines.c .\tree234.h .\puzzles.h
643 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\mines.c /Fomines5.obj
644mines2.obj: .\mines.c .\tree234.h .\puzzles.h
645 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_OBFUSCATOR /c .\mines.c /Fomines2.obj
646misc.obj: .\misc.c .\puzzles.h
647 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\misc.c /Fomisc.obj
648net.obj: .\net.c .\puzzles.h .\tree234.h
649 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\net.c /Fonet.obj
650net3.obj: .\net.c .\puzzles.h .\tree234.h
651 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\net.c /Fonet3.obj
652netslide.obj: .\netslide.c .\puzzles.h .\tree234.h
653 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\netslide.c /Fonetslide.obj
654netslid3.obj: .\netslide.c .\puzzles.h .\tree234.h
655 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\netslide.c /Fonetslid3.obj
656no-icon.obj: .\no-icon.c
657 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\no-icon.c /Fono-icon.obj
658noicon.res: .\noicon.rc .\puzzles.rc2 .\resource.h
659 rc $(FWHACK) $(RCFL) -r -fonoicon.res .\noicon.rc
660nullfe.obj: .\nullfe.c .\puzzles.h
661 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullfe.c /Fonullfe.obj
662nullgame.obj: .\nullgame.c .\puzzles.h
663 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullgame.c /Fonullgame.obj
664obfusc.obj: .\obfusc.c .\puzzles.h
665 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\obfusc.c /Foobfusc.obj
666osx.obj: .\osx.m .\puzzles.h
667 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\osx.m /Foosx.obj
668palisade.obj: .\palisade.c .\puzzles.h
669 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\palisade.c /Fopalisade.obj
670palisad3.obj: .\palisade.c .\puzzles.h
671 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\palisade.c /Fopalisad3.obj
672pattern.obj: .\pattern.c .\puzzles.h
673 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pattern.c /Fopattern.obj
674pattern7.obj: .\pattern.c .\puzzles.h
675 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pattern.c /Fopattern7.obj
676pattern4.obj: .\pattern.c .\puzzles.h
677 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_PICTURE_GENERATOR /c .\pattern.c /Fopattern4.obj
678pattern2.obj: .\pattern.c .\puzzles.h
679 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pattern.c /Fopattern2.obj
680pearl.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h
681 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pearl.c /Fopearl.obj
682pearl5.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h
683 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pearl.c /Fopearl5.obj
684pearl2.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h
685 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pearl.c /Fopearl2.obj
686pegs.obj: .\pegs.c .\puzzles.h .\tree234.h
687 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pegs.c /Fopegs.obj
688pegs3.obj: .\pegs.c .\puzzles.h .\tree234.h
689 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pegs.c /Fopegs3.obj
690penrose.obj: .\penrose.c .\puzzles.h .\penrose.h
691 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\penrose.c /Fopenrose.obj
692printing.obj: .\printing.c .\puzzles.h
693 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\printing.c /Foprinting.obj
694ps.obj: .\ps.c .\puzzles.h
695 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\ps.c /Fops.obj
696random.obj: .\random.c .\puzzles.h
697 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\random.c /Forandom.obj
698range.obj: .\range.c .\puzzles.h
699 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\range.c /Forange.obj
700range3.obj: .\range.c .\puzzles.h
701 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\range.c /Forange3.obj
702rect.obj: .\rect.c .\puzzles.h
703 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\rect.c /Forect.obj
704rect3.obj: .\rect.c .\puzzles.h
705 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\rect.c /Forect3.obj
706samegame.obj: .\samegame.c .\puzzles.h
707 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\samegame.c /Fosamegame.obj
708samegam3.obj: .\samegame.c .\puzzles.h
709 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\samegame.c /Fosamegam3.obj
710signpost.obj: .\signpost.c .\puzzles.h
711 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\signpost.c /Fosignpost.obj
712signpos5.obj: .\signpost.c .\puzzles.h
713 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\signpost.c /Fosignpos5.obj
714signpos2.obj: .\signpost.c .\puzzles.h
715 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\signpost.c /Fosignpos2.obj
716singles.obj: .\singles.c .\puzzles.h .\latin.h
717 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\singles.c /Fosingles.obj
718singles5.obj: .\singles.c .\puzzles.h .\latin.h
719 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\singles.c /Fosingles5.obj
720singles3.obj: .\singles.c .\puzzles.h .\latin.h
721 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\singles.c /Fosingles3.obj
722sixteen.obj: .\sixteen.c .\puzzles.h
723 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\sixteen.c /Fosixteen.obj
724sixteen3.obj: .\sixteen.c .\puzzles.h
725 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\sixteen.c /Fosixteen3.obj
726slant.obj: .\slant.c .\puzzles.h
727 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\slant.c /Foslant.obj
728slant5.obj: .\slant.c .\puzzles.h
729 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\slant.c /Foslant5.obj
730slant2.obj: .\slant.c .\puzzles.h
731 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\slant.c /Foslant2.obj
732solo.obj: .\solo.c .\puzzles.h
733 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\solo.c /Fosolo.obj
734solo5.obj: .\solo.c .\puzzles.h
735 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\solo.c /Fosolo5.obj
736solo2.obj: .\solo.c .\puzzles.h
737 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\solo.c /Fosolo2.obj
738tdq.obj: .\tdq.c .\puzzles.h
739 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tdq.c /Fotdq.obj
740tents.obj: .\tents.c .\puzzles.h .\maxflow.h
741 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tents.c /Fotents.obj
742tents5.obj: .\tents.c .\puzzles.h .\maxflow.h
743 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\tents.c /Fotents5.obj
744tents3.obj: .\tents.c .\puzzles.h .\maxflow.h
745 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\tents.c /Fotents3.obj
746towers.obj: .\towers.c .\puzzles.h .\latin.h
747 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\towers.c /Fotowers.obj
748towers5.obj: .\towers.c .\puzzles.h .\latin.h
749 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\towers.c /Fotowers5.obj
750towers2.obj: .\towers.c .\puzzles.h .\latin.h
751 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\towers.c /Fotowers2.obj
752tracks.obj: .\tracks.c .\puzzles.h
753 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tracks.c /Fotracks.obj
754tracks3.obj: .\tracks.c .\puzzles.h
755 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\tracks.c /Fotracks3.obj
756tree234.obj: .\tree234.c .\tree234.h .\puzzles.h
757 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tree234.c /Fotree234.obj
758twiddle.obj: .\twiddle.c .\puzzles.h
759 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\twiddle.c /Fotwiddle.obj
760twiddle3.obj: .\twiddle.c .\puzzles.h
761 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\twiddle.c /Fotwiddle3.obj
762undead.obj: .\undead.c .\puzzles.h
763 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\undead.c /Foundead.obj
764undead3.obj: .\undead.c .\puzzles.h
765 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\undead.c /Foundead3.obj
766unequal.obj: .\unequal.c .\puzzles.h .\latin.h
767 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unequal.c /Founequal.obj
768unequal5.obj: .\unequal.c .\puzzles.h .\latin.h
769 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unequal.c /Founequal5.obj
770unequal2.obj: .\unequal.c .\puzzles.h .\latin.h
771 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unequal.c /Founequal2.obj
772unruly.obj: .\unruly.c .\puzzles.h
773 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unruly.c /Founruly.obj
774unruly5.obj: .\unruly.c .\puzzles.h
775 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unruly.c /Founruly5.obj
776unruly2.obj: .\unruly.c .\puzzles.h
777 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unruly.c /Founruly2.obj
778untangle.obj: .\untangle.c .\puzzles.h .\tree234.h
779 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\untangle.c /Fountangle.obj
780untangl3.obj: .\untangle.c .\puzzles.h .\tree234.h
781 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\untangle.c /Fountangl3.obj
782version.obj: .\version.c .\version.h
783 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\version.c /Foversion.obj
784windows.obj: .\windows.c .\puzzles.h .\resource.h
785 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\windows.c /Fowindows.obj
786windows1.obj: .\windows.c .\puzzles.h .\resource.h
787 $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\windows.c /Fowindows1.obj
788
789
790clean: tidy
791 -del *.exe
792
793tidy:
794 -del *.obj
795 -del *.res
796 -del *.pch
797 -del *.aps
798 -del *.ilk
799 -del *.pdb
800 -del *.rsp
801 -del *.dsp
802 -del *.dsw
803 -del *.ncb
804 -del *.opt
805 -del *.plg
806 -del *.map
807 -del *.idb
808 -del debug.log
diff --git a/apps/plugins/puzzles/src/PuzzleApplet.java b/apps/plugins/puzzles/src/PuzzleApplet.java
new file mode 100644
index 0000000000..512aede580
--- /dev/null
+++ b/apps/plugins/puzzles/src/PuzzleApplet.java
@@ -0,0 +1,650 @@
1/*
2 * PuzzleApplet.java: NestedVM applet for the puzzle collection
3 */
4import java.awt.*;
5import java.awt.event.*;
6import java.awt.image.BufferedImage;
7import java.util.*;
8import javax.swing.*;
9import javax.swing.border.BevelBorder;
10import javax.swing.Timer;
11import java.util.List;
12
13import org.ibex.nestedvm.Runtime;
14
15public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
16
17 private static final long serialVersionUID = 1L;
18
19 private static final int CFG_SETTINGS = 0, CFG_SEED = 1, CFG_DESC = 2,
20 LEFT_BUTTON = 0x0200, MIDDLE_BUTTON = 0x201, RIGHT_BUTTON = 0x202,
21 LEFT_DRAG = 0x203, MIDDLE_DRAG = 0x204, RIGHT_DRAG = 0x205,
22 LEFT_RELEASE = 0x206, CURSOR_UP = 0x209, CURSOR_DOWN = 0x20a,
23 CURSOR_LEFT = 0x20b, CURSOR_RIGHT = 0x20c, MOD_CTRL = 0x1000,
24 MOD_SHFT = 0x2000, MOD_NUM_KEYPAD = 0x4000, ALIGN_VCENTRE = 0x100,
25 ALIGN_HCENTRE = 0x001, ALIGN_HRIGHT = 0x002, C_STRING = 0,
26 C_CHOICES = 1, C_BOOLEAN = 2;
27
28 private JFrame mainWindow;
29
30 private JMenu typeMenu;
31 private JMenuItem[] typeMenuItems;
32 private int customMenuItemIndex;
33
34 private JMenuItem solveCommand;
35 private Color[] colors;
36 private JLabel statusBar;
37 private PuzzlePanel pp;
38 private Runtime runtime;
39 private String[] puzzle_args;
40 private Graphics2D gg;
41 private Timer timer;
42 private int xarg1, xarg2, xarg3;
43 private int[] xPoints, yPoints;
44 private BufferedImage[] blitters = new BufferedImage[512];
45 private ConfigDialog dlg;
46
47 static {
48 try {
49 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
50 } catch (Exception ex) {
51 ex.printStackTrace();
52 }
53 }
54
55 public void init() {
56 try {
57 Container cp = getContentPane();
58 cp.setLayout(new BorderLayout());
59 runtime = (Runtime) Class.forName("PuzzleEngine").newInstance();
60 runtime.setCallJavaCB(this);
61 JMenuBar menubar = new JMenuBar();
62 JMenu jm;
63 menubar.add(jm = new JMenu("Game"));
64 addMenuItemWithKey(jm, "New", 'n');
65 addMenuItemCallback(jm, "Restart", "jcallback_restart_event");
66 addMenuItemCallback(jm, "Specific...", "jcallback_config_event", CFG_DESC);
67 addMenuItemCallback(jm, "Random Seed...", "jcallback_config_event", CFG_SEED);
68 jm.addSeparator();
69 addMenuItemWithKey(jm, "Undo", 'u');
70 addMenuItemWithKey(jm, "Redo", 'r');
71 jm.addSeparator();
72 solveCommand = addMenuItemCallback(jm, "Solve", "jcallback_solve_event");
73 solveCommand.setEnabled(false);
74 if (mainWindow != null) {
75 jm.addSeparator();
76 addMenuItemWithKey(jm, "Exit", 'q');
77 }
78 menubar.add(typeMenu = new JMenu("Type"));
79 typeMenu.setVisible(false);
80 menubar.add(jm = new JMenu("Help"));
81 addMenuItemCallback(jm, "About", "jcallback_about_event");
82 setJMenuBar(menubar);
83 cp.add(pp = new PuzzlePanel(), BorderLayout.CENTER);
84 pp.addKeyListener(new KeyAdapter() {
85 public void keyPressed(KeyEvent e) {
86 int key = -1;
87 int shift = e.isShiftDown() ? MOD_SHFT : 0;
88 int ctrl = e.isControlDown() ? MOD_CTRL : 0;
89 switch (e.getKeyCode()) {
90 case KeyEvent.VK_LEFT:
91 case KeyEvent.VK_KP_LEFT:
92 key = shift | ctrl | CURSOR_LEFT;
93 break;
94 case KeyEvent.VK_RIGHT:
95 case KeyEvent.VK_KP_RIGHT:
96 key = shift | ctrl | CURSOR_RIGHT;
97 break;
98 case KeyEvent.VK_UP:
99 case KeyEvent.VK_KP_UP:
100 key = shift | ctrl | CURSOR_UP;
101 break;
102 case KeyEvent.VK_DOWN:
103 case KeyEvent.VK_KP_DOWN:
104 key = shift | ctrl | CURSOR_DOWN;
105 break;
106 case KeyEvent.VK_PAGE_UP:
107 key = shift | ctrl | MOD_NUM_KEYPAD | '9';
108 break;
109 case KeyEvent.VK_PAGE_DOWN:
110 key = shift | ctrl | MOD_NUM_KEYPAD | '3';
111 break;
112 case KeyEvent.VK_HOME:
113 key = shift | ctrl | MOD_NUM_KEYPAD | '7';
114 break;
115 case KeyEvent.VK_END:
116 key = shift | ctrl | MOD_NUM_KEYPAD | '1';
117 break;
118 default:
119 if (e.getKeyCode() >= KeyEvent.VK_NUMPAD0 && e.getKeyCode() <=KeyEvent.VK_NUMPAD9) {
120 key = MOD_NUM_KEYPAD | (e.getKeyCode() - KeyEvent.VK_NUMPAD0+'0');
121 }
122 break;
123 }
124 if (key != -1) {
125 runtimeCall("jcallback_key_event", new int[] {0, 0, key});
126 }
127 }
128 public void keyTyped(KeyEvent e) {
129 runtimeCall("jcallback_key_event", new int[] {0, 0, e.getKeyChar()});
130 }
131 });
132 pp.addMouseListener(new MouseAdapter() {
133 public void mouseReleased(MouseEvent e) {
134 mousePressedReleased(e, true);
135 }
136 public void mousePressed(MouseEvent e) {
137 pp.requestFocus();
138 mousePressedReleased(e, false);
139 }
140 private void mousePressedReleased(MouseEvent e, boolean released) {
141 int button;
142 if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
143 button = MIDDLE_BUTTON;
144 else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
145 button = RIGHT_BUTTON;
146 else if ((e.getModifiers() & (InputEvent.BUTTON1_MASK)) != 0)
147 button = LEFT_BUTTON;
148 else
149 return;
150 if (released)
151 button += LEFT_RELEASE - LEFT_BUTTON;
152 runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
153 }
154 });
155 pp.addMouseMotionListener(new MouseMotionAdapter() {
156 public void mouseDragged(MouseEvent e) {
157 int button;
158 if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
159 button = MIDDLE_DRAG;
160 else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
161 button = RIGHT_DRAG;
162 else
163 button = LEFT_DRAG;
164 runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
165 }
166 });
167 pp.addComponentListener(new ComponentAdapter() {
168 public void componentResized(ComponentEvent e) {
169 handleResized();
170 }
171 });
172 pp.setFocusable(true);
173 pp.requestFocus();
174 timer = new Timer(20, new ActionListener() {
175 public void actionPerformed(ActionEvent e) {
176 runtimeCall("jcallback_timer_func", new int[0]);
177 }
178 });
179 String gameid;
180 try {
181 gameid = getParameter("game_id");
182 } catch (java.lang.NullPointerException ex) {
183 gameid = null;
184 }
185 if (gameid == null) {
186 puzzle_args = null;
187 } else {
188 puzzle_args = new String[2];
189 puzzle_args[0] = "puzzle";
190 puzzle_args[1] = gameid;
191 }
192 SwingUtilities.invokeLater(new Runnable() {
193 public void run() {
194 runtime.start(puzzle_args);
195 runtime.execute();
196 }
197 });
198 } catch (Exception ex) {
199 ex.printStackTrace();
200 }
201 }
202
203 public void destroy() {
204 SwingUtilities.invokeLater(new Runnable() {
205 public void run() {
206 runtime.execute();
207 if (mainWindow != null) {
208 mainWindow.dispose();
209 System.exit(0);
210 }
211 }
212 });
213 }
214
215 protected void handleResized() {
216 pp.createBackBuffer(pp.getWidth(), pp.getHeight(), colors[0]);
217 runtimeCall("jcallback_resize", new int[] {pp.getWidth(), pp.getHeight()});
218 }
219
220 private void addMenuItemWithKey(JMenu jm, String name, int key) {
221 addMenuItemCallback(jm, name, "jcallback_menu_key_event", key);
222 }
223
224 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int arg) {
225 return addMenuItemCallback(jm, name, callback, new int[] {arg}, false);
226 }
227
228 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback) {
229 return addMenuItemCallback(jm, name, callback, new int[0], false);
230 }
231
232 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int[] args, boolean checkbox) {
233 JMenuItem jmi;
234 if (checkbox)
235 jm.add(jmi = new JCheckBoxMenuItem(name));
236 else
237 jm.add(jmi = new JMenuItem(name));
238 jmi.addActionListener(new ActionListener() {
239 public void actionPerformed(ActionEvent e) {
240 runtimeCall(callback, args);
241 }
242 });
243 return jmi;
244 }
245
246 protected void runtimeCall(String func, int[] args) {
247 if (runtimeCallWithResult(func, args) == 42 && mainWindow != null) {
248 destroy();
249 }
250 }
251
252 protected int runtimeCallWithResult(String func, int[] args) {
253 try {
254 return runtime.call(func, args);
255 } catch (Runtime.CallException ex) {
256 ex.printStackTrace();
257 return 42;
258 }
259 }
260
261 private void buildConfigureMenuItem() {
262 if (typeMenu.isVisible()) {
263 typeMenu.addSeparator();
264 } else {
265 typeMenu.setVisible(true);
266 }
267 typeMenuItems[customMenuItemIndex] =
268 addMenuItemCallback(typeMenu, "Custom...",
269 "jcallback_config_event",
270 new int[] {CFG_SETTINGS}, true);
271 }
272
273 private void addTypeItem
274 (JMenu targetMenu, String name, int newId, final int ptrGameParams) {
275
276 typeMenu.setVisible(true);
277 typeMenuItems[newId] =
278 addMenuItemCallback(targetMenu, name,
279 "jcallback_preset_event",
280 new int[] {ptrGameParams}, true);
281 }
282
283 private void addTypeSubmenu
284 (JMenu targetMenu, String name, int newId) {
285
286 JMenu newMenu = new JMenu(name);
287 newMenu.setVisible(true);
288 typeMenuItems[newId] = newMenu;
289 targetMenu.add(newMenu);
290 }
291
292 public int call(int cmd, int arg1, int arg2, int arg3) {
293 try {
294 switch(cmd) {
295 case 0: // initialize
296 if (mainWindow != null) mainWindow.setTitle(runtime.cstring(arg1));
297 if ((arg2 & 1) != 0) buildConfigureMenuItem();
298 if ((arg2 & 2) != 0) addStatusBar();
299 if ((arg2 & 4) != 0) solveCommand.setEnabled(true);
300 colors = new Color[arg3];
301 return 0;
302 case 1: // configure Type menu
303 if (arg1 == 0) {
304 // preliminary setup
305 typeMenuItems = new JMenuItem[arg2 + 2];
306 typeMenuItems[arg2] = typeMenu;
307 customMenuItemIndex = arg2 + 1;
308 return arg2;
309 } else if (xarg1 != 0) {
310 addTypeItem((JMenu)typeMenuItems[arg2],
311 runtime.cstring(arg1), arg3, xarg1);
312 } else {
313 addTypeSubmenu((JMenu)typeMenuItems[arg2],
314 runtime.cstring(arg1), arg3);
315 }
316 return 0;
317 case 2: // MessageBox
318 JOptionPane.showMessageDialog(this, runtime.cstring(arg2), runtime.cstring(arg1), arg3 == 0 ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE);
319 return 0;
320 case 3: // Resize
321 pp.setPreferredSize(new Dimension(arg1, arg2));
322 if (mainWindow != null) mainWindow.pack();
323 handleResized();
324 if (mainWindow != null) mainWindow.setVisible(true);
325 return 0;
326 case 4: // drawing tasks
327 switch(arg1) {
328 case 0:
329 String text = runtime.cstring(arg2);
330 if (text.equals("")) text = " ";
331 statusBar.setText(text);
332 break;
333 case 1:
334 gg = pp.backBuffer.createGraphics();
335 if (arg2 != 0 || arg3 != 0 ||
336 arg2 + xarg2 != getWidth() ||
337 arg3 + xarg3 != getHeight()) {
338 int left = arg2, right = arg2 + xarg2;
339 int top = arg3, bottom = arg3 + xarg3;
340 int width = getWidth(), height = getHeight();
341 gg.setColor(colors != null ? colors[0] : Color.black);
342 gg.fillRect(0, 0, left, height);
343 gg.fillRect(right, 0, width-right, height);
344 gg.fillRect(0, 0, width, top);
345 gg.fillRect(0, bottom, width, height-bottom);
346 gg.setClip(left, top, right-left, bottom-top);
347 }
348 break;
349 case 2: gg.dispose(); pp.repaint(); break;
350 case 3: gg.setClip(arg2, arg3, xarg1, xarg2); break;
351 case 4:
352 if (arg2 == 0 && arg3 == 0) {
353 gg.setClip(0, 0, getWidth(), getHeight());
354 } else {
355 gg.setClip(arg2, arg3, getWidth()-2*arg2, getHeight()-2*arg3);
356 }
357 break;
358 case 5:
359 gg.setColor(colors[xarg3]);
360 gg.fillRect(arg2, arg3, xarg1, xarg2);
361 break;
362 case 6:
363 gg.setColor(colors[xarg3]);
364 gg.drawLine(arg2, arg3, xarg1, xarg2);
365 break;
366 case 7:
367 xPoints = new int[arg2];
368 yPoints = new int[arg2];
369 break;
370 case 8:
371 if (arg3 != -1) {
372 gg.setColor(colors[arg3]);
373 gg.fillPolygon(xPoints, yPoints, xPoints.length);
374 }
375 gg.setColor(colors[arg2]);
376 gg.drawPolygon(xPoints, yPoints, xPoints.length);
377 break;
378 case 9:
379 if (arg3 != -1) {
380 gg.setColor(colors[arg3]);
381 gg.fillOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
382 }
383 gg.setColor(colors[arg2]);
384 gg.drawOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
385 break;
386 case 10:
387 for(int i=0; i<blitters.length; i++) {
388 if (blitters[i] == null) {
389 blitters[i] = new BufferedImage(arg2, arg3, BufferedImage.TYPE_3BYTE_BGR);
390 return i;
391 }
392 }
393 throw new RuntimeException("No free blitter found!");
394 case 11: blitters[arg2] = null; break;
395 case 12:
396 timer.start(); break;
397 case 13:
398 timer.stop(); break;
399 }
400 return 0;
401 case 5: // more arguments
402 xarg1 = arg1;
403 xarg2 = arg2;
404 xarg3 = arg3;
405 return 0;
406 case 6: // polygon vertex
407 xPoints[arg1]=arg2;
408 yPoints[arg1]=arg3;
409 return 0;
410 case 7: // string
411 gg.setColor(colors[arg2]);
412 {
413 String text = runtime.utfstring(arg3);
414 Font ft = new Font((xarg3 & 0x10) != 0 ? "Monospaced" : "Dialog",
415 Font.PLAIN, 100);
416 int height100 = this.getFontMetrics(ft).getHeight();
417 ft = ft.deriveFont(arg1 * 100 / (float)height100);
418 FontMetrics fm = this.getFontMetrics(ft);
419 int asc = fm.getAscent(), desc = fm.getDescent();
420 if ((xarg3 & ALIGN_VCENTRE) != 0)
421 xarg2 += asc - (asc+desc)/2;
422 int wid = fm.stringWidth(text);
423 if ((xarg3 & ALIGN_HCENTRE) != 0)
424 xarg1 -= wid / 2;
425 else if ((xarg3 & ALIGN_HRIGHT) != 0)
426 xarg1 -= wid;
427 gg.setFont(ft);
428 gg.drawString(text, xarg1, xarg2);
429 }
430 return 0;
431 case 8: // blitter_save
432 Graphics g2 = blitters[arg1].createGraphics();
433 g2.drawImage(pp.backBuffer, 0, 0, blitters[arg1].getWidth(), blitters[arg1].getHeight(),
434 arg2, arg3, arg2 + blitters[arg1].getWidth(), arg3 + blitters[arg1].getHeight(), this);
435 g2.dispose();
436 return 0;
437 case 9: // blitter_load
438 gg.drawImage(blitters[arg1], arg2, arg3, this);
439 return 0;
440 case 10: // dialog_init
441 dlg= new ConfigDialog(this, runtime.cstring(arg1));
442 return 0;
443 case 11: // dialog_add_control
444 {
445 int sval_ptr = arg1;
446 int ival = arg2;
447 int ptr = xarg1;
448 int type=xarg2;
449 String name = runtime.cstring(xarg3);
450 switch(type) {
451 case C_STRING:
452 dlg.addTextBox(ptr, name, runtime.cstring(sval_ptr));
453 break;
454 case C_BOOLEAN:
455 dlg.addCheckBox(ptr, name, ival != 0);
456 break;
457 case C_CHOICES:
458 dlg.addComboBox(ptr, name, runtime.cstring(sval_ptr), ival);
459 }
460 }
461 return 0;
462 case 12:
463 dlg.finish();
464 dlg = null;
465 return 0;
466 case 13: // tick a menu item
467 if (arg1 < 0) arg1 = customMenuItemIndex;
468 for (int i = 0; i < typeMenuItems.length; i++) {
469 if (typeMenuItems[i] instanceof JCheckBoxMenuItem) {
470 ((JCheckBoxMenuItem)typeMenuItems[i]).setSelected
471 (arg1 == i);
472 }
473 }
474 return 0;
475 default:
476 if (cmd >= 1024 && cmd < 2048) { // palette
477 colors[cmd-1024] = new Color(arg1, arg2, arg3);
478 }
479 if (cmd == 1024) {
480 pp.setBackground(colors[0]);
481 if (statusBar != null) statusBar.setBackground(colors[0]);
482 this.setBackground(colors[0]);
483 }
484 return 0;
485 }
486 } catch (Throwable ex) {
487 ex.printStackTrace();
488 System.exit(-1);
489 return 0;
490 }
491 }
492
493 private void addStatusBar() {
494 statusBar = new JLabel("test");
495 statusBar.setBorder(new BevelBorder(BevelBorder.LOWERED));
496 getContentPane().add(BorderLayout.SOUTH,statusBar);
497 }
498
499 // Standalone runner
500 public static void main(String[] args) {
501 final PuzzleApplet a = new PuzzleApplet();
502 JFrame jf = new JFrame("Loading...");
503 jf.getContentPane().setLayout(new BorderLayout());
504 jf.getContentPane().add(a, BorderLayout.CENTER);
505 a.mainWindow=jf;
506 a.init();
507 a.start();
508 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
509 jf.addWindowListener(new WindowAdapter() {
510 public void windowClosing(WindowEvent e) {
511 a.stop();
512 a.destroy();
513 }
514 });
515 jf.setVisible(true);
516 }
517
518 public static class PuzzlePanel extends JPanel {
519
520 private static final long serialVersionUID = 1L;
521 protected BufferedImage backBuffer;
522
523 public PuzzlePanel() {
524 setPreferredSize(new Dimension(100,100));
525 createBackBuffer(100,100, Color.black);
526 }
527
528 public void createBackBuffer(int w, int h, Color bg) {
529 if (w > 0 && h > 0) {
530 backBuffer = new BufferedImage(w,h, BufferedImage.TYPE_3BYTE_BGR);
531 Graphics g = backBuffer.createGraphics();
532 g.setColor(bg);
533 g.fillRect(0, 0, w, h);
534 g.dispose();
535 }
536 }
537
538 protected void paintComponent(Graphics g) {
539 g.drawImage(backBuffer, 0, 0, this);
540 }
541 }
542
543 public static class ConfigComponent {
544 public int type;
545 public int configItemPointer;
546 public JComponent component;
547
548 public ConfigComponent(int type, int configItemPointer, JComponent component) {
549 this.type = type;
550 this.configItemPointer = configItemPointer;
551 this.component = component;
552 }
553 }
554
555 public class ConfigDialog extends JDialog {
556
557 private GridBagConstraints gbcLeft = new GridBagConstraints(
558 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1,
559 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
560 new Insets(0, 0, 0, 0), 0, 0);
561 private GridBagConstraints gbcRight = new GridBagConstraints(
562 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
563 GridBagConstraints.REMAINDER, 1, 1.0, 0,
564 GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
565 new Insets(5, 5, 5, 5), 0, 0);
566 private GridBagConstraints gbcBottom = new GridBagConstraints(
567 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
568 GridBagConstraints.REMAINDER, GridBagConstraints.REMAINDER,
569 1.0, 1.0, GridBagConstraints.CENTER,
570 GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0);
571
572 private static final long serialVersionUID = 1L;
573 private List components = new ArrayList();
574
575 public ConfigDialog(JApplet parent, String title) {
576 super(JOptionPane.getFrameForComponent(parent), title, true);
577 getContentPane().setLayout(new GridBagLayout());
578 }
579
580 public void addTextBox(int ptr, String name, String value) {
581 getContentPane().add(new JLabel(name), gbcLeft);
582 JComponent c = new JTextField(value, 25);
583 getContentPane().add(c, gbcRight);
584 components.add(new ConfigComponent(C_STRING, ptr, c));
585 }
586
587
588 public void addCheckBox(int ptr, String name, boolean selected) {
589 JComponent c = new JCheckBox(name, selected);
590 getContentPane().add(c, gbcRight);
591 components.add(new ConfigComponent(C_BOOLEAN, ptr, c));
592 }
593
594 public void addComboBox(int ptr, String name, String values, int selected) {
595 getContentPane().add(new JLabel(name), gbcLeft);
596 StringTokenizer st = new StringTokenizer(values.substring(1), values.substring(0,1));
597 JComboBox c = new JComboBox();
598 c.setEditable(false);
599 while(st.hasMoreTokens())
600 c.addItem(st.nextToken());
601 c.setSelectedIndex(selected);
602 getContentPane().add(c, gbcRight);
603 components.add(new ConfigComponent(C_CHOICES, ptr, c));
604 }
605
606 public void finish() {
607 JPanel buttons = new JPanel(new GridLayout(1, 2, 5, 5));
608 getContentPane().add(buttons, gbcBottom);
609 JButton b;
610 buttons.add(b=new JButton("OK"));
611 b.addActionListener(new ActionListener() {
612 public void actionPerformed(ActionEvent e) {
613 save();
614 dispose();
615 }
616 });
617 getRootPane().setDefaultButton(b);
618 buttons.add(b=new JButton("Cancel"));
619 b.addActionListener(new ActionListener() {
620 public void actionPerformed(ActionEvent e) {
621 dispose();
622 }
623 });
624 setDefaultCloseOperation(DISPOSE_ON_CLOSE);
625 pack();
626 setLocationRelativeTo(null);
627 setVisible(true);
628 }
629 private void save() {
630 for (int i = 0; i < components.size(); i++) {
631 ConfigComponent cc = (ConfigComponent) components.get(i);
632 switch(cc.type) {
633 case C_STRING:
634 JTextField jtf = (JTextField)cc.component;
635 runtimeCall("jcallback_config_set_string", new int[] {cc.configItemPointer, runtime.strdup(jtf.getText())});
636 break;
637 case C_BOOLEAN:
638 JCheckBox jcb = (JCheckBox)cc.component;
639 runtimeCall("jcallback_config_set_boolean", new int[] {cc.configItemPointer, jcb.isSelected()?1:0});
640 break;
641 case C_CHOICES:
642 JComboBox jcm = (JComboBox)cc.component;
643 runtimeCall("jcallback_config_set_choice", new int[] {cc.configItemPointer, jcm.getSelectedIndex()});
644 break;
645 }
646 }
647 runtimeCall("jcallback_config_ok", new int[0]);
648 }
649 }
650}
diff --git a/apps/plugins/puzzles/src/README b/apps/plugins/puzzles/src/README
new file mode 100644
index 0000000000..890db56771
--- /dev/null
+++ b/apps/plugins/puzzles/src/README
@@ -0,0 +1,54 @@
1This is the README accompanying the source code to Simon Tatham's
2puzzle collection. The collection's web site is at
3<http://www.chiark.greenend.org.uk/~sgtatham/puzzles/>.
4
5If you've obtained the source code by downloading a .tar.gz archive
6from the Puzzles web site, you should find several Makefiles in the
7source code. However, if you've checked the source code out from the
8Puzzles git repository, you won't find the Makefiles: they're
9automatically generated by `mkfiles.pl', so run that to create them.
10
11The Makefiles include:
12
13 - `Makefile.am', together with the static `configure.ac', is intended
14 as input to automake. Run `mkauto.sh' to turn these into a
15 configure script and Makefile.in, after which you can then run
16 `./configure' to create an actual Unix Makefile.
17
18 - `Makefile.vc' should work under MS Visual C++ on Windows. Run
19 'nmake /f Makefile.vc' in a Visual Studio command prompt.
20
21 - `Makefile.cyg' should work under Cygwin / MinGW. With appropriate
22 tweaks and setting of TOOLPATH, it should work for both compiling
23 on Windows and cross-compiling on Unix.
24
25 - `Makefile.osx' should work under Mac OS X, provided the Xcode
26 tools are installed. It builds a single monolithic OS X
27 application capable of running any of the puzzles, or even more
28 than one of them at a time.
29
30 - `Makefile.wce' should work under MS eMbedded Visual C++ on
31 Windows and the Pocket PC SDK; it builds Pocket PC binaries.
32
33Many of these Makefiles build a program called `nullgame' in
34addition to the actual game binaries. This program doesn't do
35anything; it's just a template for people to start from when adding
36a new game to the collection, and it's compiled every time to ensure
37that it _does_ compile and link successfully (because otherwise it
38wouldn't be much use as a template). Once it's built, you can run it
39if you really want to (but it's very boring), and then you should
40ignore it.
41
42DO NOT EDIT THE MAKEFILES DIRECTLY, if you plan to send any changes
43back to the maintainer. The makefiles are generated automatically by
44the Perl script `mkfiles.pl' from the file `Recipe' and the various
45.R files. If you need to change the makefiles as part of a patch,
46you should change Recipe, *.R, and/or mkfiles.pl.
47
48The manual is provided in Windows Help format for the Windows build;
49in text format for anyone who needs it; and in HTML for the Mac OS X
50application and for the web site. It is generated from a Halibut
51source file (puzzles.but), which is the preferred form for
52modification. To generate the manual in other formats, rebuild it,
53or learn about Halibut, visit the Halibut website at
54<http://www.chiark.greenend.org.uk/~sgtatham/halibut/>.
diff --git a/apps/plugins/puzzles/src/Recipe b/apps/plugins/puzzles/src/Recipe
new file mode 100644
index 0000000000..ba8317f51a
--- /dev/null
+++ b/apps/plugins/puzzles/src/Recipe
@@ -0,0 +1,157 @@
1# -*- makefile -*-
2#
3# This file describes which puzzle binaries are made up from which
4# object and resource files. It is processed into the various
5# Makefiles by means of a Perl script. Makefile changes should
6# really be made by editing this file and/or the Perl script, not
7# by editing the actual Makefiles.
8
9!name puzzles
10
11!makefile gtk Makefile.gtk
12!makefile am Makefile.am
13!makefile vc Makefile.vc
14!makefile wce Makefile.wce
15!makefile cygwin Makefile.cyg
16!makefile osx Makefile.osx
17!makefile gnustep Makefile.gnustep
18!makefile nestedvm Makefile.nestedvm
19!makefile emcc Makefile.emcc
20
21!srcdir icons/
22
23WINDOWS_COMMON = printing
24 + user32.lib gdi32.lib comctl32.lib comdlg32.lib winspool.lib
25WINDOWS = windows WINDOWS_COMMON
26COMMON = midend drawing misc malloc random version
27GTK = gtk printing ps
28# Objects needed for auxiliary command-line programs.
29STANDALONE = nullfe random misc malloc
30
31ALL = list
32
33# First half of list.c.
34!begin >list.c
35/*
36 * list.c: List of pointers to puzzle structures, for monolithic
37 * platforms.
38 *
39 * This file is automatically generated by mkfiles.pl. Do not edit
40 * it directly, or the changes will be lost next time mkfiles.pl runs.
41 * Instead, edit Recipe and/or its *.R subfiles.
42 */
43#include "puzzles.h"
44#define GAMELIST(A) \
45!end
46
47# Now each .R file adds part of the macro definition of GAMELIST to list.c.
48!include *.R
49
50# Then we finish up list.c as follows:
51!begin >list.c
52
53#define DECL(x) extern const game x;
54#define REF(x) &x,
55GAMELIST(DECL)
56const game *gamelist[] = { GAMELIST(REF) };
57const int gamecount = lenof(gamelist);
58!end
59
60# Unix standalone application for special-purpose obfuscation.
61obfusc : [U] obfusc STANDALONE
62
63puzzles : [G] windows[COMBINED] WINDOWS_COMMON COMMON ALL noicon.res
64
65# Mac OS X unified application containing all the puzzles.
66Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL
67# For OS X, we must create the online help and include it in the
68# application bundle.) Also we add -DCOMBINED to the compiler flags
69# so as to inform the code that we're building a single binary for
70# all the puzzles. Then I've also got some code in here to build a
71# distributable .dmg disk image.
72!begin osx
73Puzzles_extra = Puzzles.app/Contents/Resources/Help/index.html
74Puzzles.app/Contents/Resources/Help/index.html: \
75 Puzzles.app/Contents/Resources/Help osx-help.but puzzles.but
76 cd Puzzles.app/Contents/Resources/Help; \
77 halibut --html ../../../../osx-help.but ../../../../puzzles.but
78Puzzles.app/Contents/Resources/Help: Puzzles.app/Contents/Resources
79 mkdir -p Puzzles.app/Contents/Resources/Help
80
81release: Puzzles.dmg
82Puzzles.dmg: Puzzles
83 rm -f raw.dmg
84 hdiutil create -megabytes 5 -layout NONE raw.dmg
85 hdid -nomount raw.dmg > devicename
86 newfs_hfs -v "Simon Tatham's Puzzle Collection" `cat devicename`
87 hdiutil eject `cat devicename`
88 hdid raw.dmg | cut -f1 -d' ' > devicename
89 cp -R Puzzles.app /Volumes/"Simon Tatham's Puzzle Collection"
90 hdiutil eject `cat devicename`
91 rm -f Puzzles.dmg
92 hdiutil convert -format UDCO raw.dmg -o Puzzles.dmg
93 rm -f raw.dmg devicename
94!end
95
96!begin am
97bin_PROGRAMS = $(GAMES)
98!end
99!begin am_begin
100GAMES =
101!end
102
103# make install for Unix.
104!begin gtk
105install:
106 for i in $(GAMES); do \
107 $(INSTALL_PROGRAM) -m 755 $(BINPREFIX)$$i $(DESTDIR)$(gamesdir)/$(BINPREFIX)$$i \
108 || exit 1; \
109 done
110!end
111!begin nestedvm
112.PRECIOUS: %.class
113%.class: %.mips
114 java -cp $(NESTEDVM)/build:$(NESTEDVM)/upstream/build/classgen/build \
115 org.ibex.nestedvm.Compiler -outformat class -d . \
116 PuzzleEngine $<
117 mv PuzzleEngine.class $@
118
119org:
120 mkdir -p org/ibex/nestedvm/util
121 cp $(NESTEDVM)/build/org/ibex/nestedvm/Registers.class org/ibex/nestedvm
122 cp $(NESTEDVM)/build/org/ibex/nestedvm/UsermodeConstants.class org/ibex/nestedvm
123 cp $(NESTEDVM)/build/org/ibex/nestedvm/Runtime*.class org/ibex/nestedvm
124 cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Platform*.class org/ibex/nestedvm/util
125 cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Seekable*.class org/ibex/nestedvm/util
126 echo "Main-Class: PuzzleApplet" >applet.manifest
127
128PuzzleApplet.class: PuzzleApplet.java org
129 javac -source 1.3 -target 1.3 PuzzleApplet.java
130
131%.jar: %.class PuzzleApplet.class org
132 mv $< PuzzleEngine.class
133 jar cfm $@ applet.manifest PuzzleEngine.class PuzzleApplet*.class org
134 echo '<applet archive="'$@'" code="PuzzleApplet" width="700" height="500"></applet>' >$*.html
135 mv PuzzleEngine.class $<
136!end
137
138# A benchmarking and testing target for the GTK puzzles.
139!begin gtk
140test: benchmark.html benchmark.txt
141
142benchmark.html: benchmark.txt benchmark.pl
143 ./benchmark.pl benchmark.txt > $@
144
145benchmark.txt: benchmark.sh $(GAMES)
146 ./benchmark.sh > $@
147
148!end
149!begin am
150test: benchmark.html benchmark.txt
151
152benchmark.html: benchmark.txt benchmark.pl
153 ./benchmark.pl benchmark.txt > $@
154
155benchmark.txt: benchmark.sh $(GAMES)
156 ./benchmark.sh > $@
157!end
diff --git a/apps/plugins/puzzles/src/aclocal.m4 b/apps/plugins/puzzles/src/aclocal.m4
new file mode 100644
index 0000000000..6e4efedfb6
--- /dev/null
+++ b/apps/plugins/puzzles/src/aclocal.m4
@@ -0,0 +1,1832 @@
1# generated automatically by aclocal 1.15 -*- Autoconf -*-
2
3# Copyright (C) 1996-2014 Free Software Foundation, Inc.
4
5# This file is free software; the Free Software Foundation
6# gives unlimited permission to copy and/or distribute it,
7# with or without modifications, as long as this notice is preserved.
8
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12# PARTICULAR PURPOSE.
13
14m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
15m4_ifndef([AC_AUTOCONF_VERSION],
16 [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
17m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
18[m4_warning([this file was generated for autoconf 2.69.
19You have another version of autoconf. It may work, but is not guaranteed to.
20If you have problems, you may need to regenerate the build system entirely.
21To do so, use the procedure documented by the package, typically 'autoreconf'.])])
22
23# Configure paths for GTK+
24# Owen Taylor 1997-2001
25
26dnl AM_PATH_GTK_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
27dnl Test for GTK+, and define GTK_CFLAGS and GTK_LIBS, if gthread is specified in MODULES,
28dnl pass to pkg-config
29dnl
30AC_DEFUN([AM_PATH_GTK_2_0],
31[dnl
32dnl Get the cflags and libraries from pkg-config
33dnl
34AC_ARG_ENABLE(gtktest, [ --disable-gtktest do not try to compile and run a test GTK+ program],
35 , enable_gtktest=yes)
36
37 pkg_config_args=gtk+-2.0
38 for module in . $4
39 do
40 case "$module" in
41 gthread)
42 pkg_config_args="$pkg_config_args gthread-2.0"
43 ;;
44 esac
45 done
46
47 no_gtk=""
48
49 AC_REQUIRE([PKG_PROG_PKG_CONFIG])
50 PKG_PROG_PKG_CONFIG([0.7])
51
52 min_gtk_version=ifelse([$1], ,2.0.0,$1)
53 AC_MSG_CHECKING(for GTK+ - version >= $min_gtk_version)
54
55 if test x$PKG_CONFIG != xno ; then
56 ## don't try to run the test against uninstalled libtool libs
57 if $PKG_CONFIG --uninstalled $pkg_config_args; then
58 echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH"
59 enable_gtktest=no
60 fi
61
62 if $PKG_CONFIG --atleast-version $min_gtk_version $pkg_config_args; then
63 :
64 else
65 no_gtk=yes
66 fi
67 fi
68
69 if test x"$no_gtk" = x ; then
70 GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags`
71 GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs`
72 gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
73 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
74 gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
75 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
76 gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
77 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
78 if test "x$enable_gtktest" = "xyes" ; then
79 ac_save_CFLAGS="$CFLAGS"
80 ac_save_LIBS="$LIBS"
81 CFLAGS="$CFLAGS $GTK_CFLAGS"
82 LIBS="$GTK_LIBS $LIBS"
83dnl
84dnl Now check if the installed GTK+ is sufficiently new. (Also sanity
85dnl checks the results of pkg-config to some extent)
86dnl
87 rm -f conf.gtktest
88 AC_TRY_RUN([
89#include <gtk/gtk.h>
90#include <stdio.h>
91#include <stdlib.h>
92
93int
94main ()
95{
96 int major, minor, micro;
97 char *tmp_version;
98
99 fclose (fopen ("conf.gtktest", "w"));
100
101 /* HP/UX 9 (%@#!) writes to sscanf strings */
102 tmp_version = g_strdup("$min_gtk_version");
103 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
104 printf("%s, bad version string\n", "$min_gtk_version");
105 exit(1);
106 }
107
108 if ((gtk_major_version != $gtk_config_major_version) ||
109 (gtk_minor_version != $gtk_config_minor_version) ||
110 (gtk_micro_version != $gtk_config_micro_version))
111 {
112 printf("\n*** 'pkg-config --modversion gtk+-2.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
113 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
114 gtk_major_version, gtk_minor_version, gtk_micro_version);
115 printf ("*** was found! If pkg-config was correct, then it is best\n");
116 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
117 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
118 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
119 printf("*** required on your system.\n");
120 printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
121 printf("*** to point to the correct configuration files\n");
122 }
123 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
124 (gtk_minor_version != GTK_MINOR_VERSION) ||
125 (gtk_micro_version != GTK_MICRO_VERSION))
126 {
127 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
128 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
129 printf("*** library (version %d.%d.%d)\n",
130 gtk_major_version, gtk_minor_version, gtk_micro_version);
131 }
132 else
133 {
134 if ((gtk_major_version > major) ||
135 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
136 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
137 {
138 return 0;
139 }
140 else
141 {
142 printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
143 gtk_major_version, gtk_minor_version, gtk_micro_version);
144 printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
145 major, minor, micro);
146 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
147 printf("***\n");
148 printf("*** If you have already installed a sufficiently new version, this error\n");
149 printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
150 printf("*** being found. The easiest way to fix this is to remove the old version\n");
151 printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n");
152 printf("*** correct copy of pkg-config. (In this case, you will have to\n");
153 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
154 printf("*** so that the correct libraries are found at run-time))\n");
155 }
156 }
157 return 1;
158}
159],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
160 CFLAGS="$ac_save_CFLAGS"
161 LIBS="$ac_save_LIBS"
162 fi
163 fi
164 if test "x$no_gtk" = x ; then
165 AC_MSG_RESULT(yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version))
166 ifelse([$2], , :, [$2])
167 else
168 AC_MSG_RESULT(no)
169 if test "$PKG_CONFIG" = "no" ; then
170 echo "*** A new enough version of pkg-config was not found."
171 echo "*** See http://pkgconfig.sourceforge.net"
172 else
173 if test -f conf.gtktest ; then
174 :
175 else
176 echo "*** Could not run GTK+ test program, checking why..."
177 ac_save_CFLAGS="$CFLAGS"
178 ac_save_LIBS="$LIBS"
179 CFLAGS="$CFLAGS $GTK_CFLAGS"
180 LIBS="$LIBS $GTK_LIBS"
181 AC_TRY_LINK([
182#include <gtk/gtk.h>
183#include <stdio.h>
184], [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ],
185 [ echo "*** The test program compiled, but did not run. This usually means"
186 echo "*** that the run-time linker is not finding GTK+ or finding the wrong"
187 echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your"
188 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
189 echo "*** to the installed location Also, make sure you have run ldconfig if that"
190 echo "*** is required on your system"
191 echo "***"
192 echo "*** If you have an old version installed, it is best to remove it, although"
193 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
194 [ echo "*** The test program failed to compile or link. See the file config.log for the"
195 echo "*** exact error that occured. This usually means GTK+ is incorrectly installed."])
196 CFLAGS="$ac_save_CFLAGS"
197 LIBS="$ac_save_LIBS"
198 fi
199 fi
200 GTK_CFLAGS=""
201 GTK_LIBS=""
202 ifelse([$3], , :, [$3])
203 fi
204 AC_SUBST(GTK_CFLAGS)
205 AC_SUBST(GTK_LIBS)
206 rm -f conf.gtktest
207])
208
209# Configure paths for GTK+
210# Owen Taylor 1997-2001
211
212dnl AM_PATH_GTK_3_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
213dnl Test for GTK+, and define GTK_CFLAGS and GTK_LIBS, if gthread is specified in MODULES,
214dnl pass to pkg-config
215dnl
216AC_DEFUN([AM_PATH_GTK_3_0],
217[m4_warn([obsolete], [AM_PATH_GTK_3_0 is deprecated, use PKG_CHECK_MODULES([GTK], [gtk+-3.0]) instead])
218dnl Get the cflags and libraries from pkg-config
219dnl
220AC_ARG_ENABLE(gtktest, [ --disable-gtktest do not try to compile and run a test GTK+ program],
221 , enable_gtktest=yes)
222 min_gtk_version=ifelse([$1], [], [3.0.0], [$1])
223
224 pkg_config_args="gtk+-3.0 >= $min_gtk_version"
225 for module in . $4
226 do
227 case "$module" in
228 gthread)
229 pkg_config_args="$pkg_config_args gthread-2.0"
230 ;;
231 esac
232 done
233
234 no_gtk=""
235
236 AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
237
238 if test x$PKG_CONFIG != xno ; then
239 if $PKG_CONFIG --atleast-pkgconfig-version 0.7 ; then
240 :
241 else
242 echo "*** pkg-config too old; version 0.7 or better required."
243 no_gtk=yes
244 PKG_CONFIG=no
245 fi
246 else
247 no_gtk=yes
248 fi
249
250 AC_MSG_CHECKING(for GTK+ - version >= $min_gtk_version)
251
252 if test x$PKG_CONFIG != xno ; then
253 ## don't try to run the test against uninstalled libtool libs
254 if $PKG_CONFIG --uninstalled $pkg_config_args; then
255 echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH"
256 enable_gtktest=no
257 fi
258
259 if $PKG_CONFIG $pkg_config_args; then
260 :
261 else
262 no_gtk=yes
263 fi
264 fi
265
266 if test x"$no_gtk" = x ; then
267 GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags`
268 GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs`
269 gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
270 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
271 gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
272 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
273 gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
274 sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
275 if test "x$enable_gtktest" = "xyes" ; then
276 ac_save_CFLAGS="$CFLAGS"
277 ac_save_LIBS="$LIBS"
278 CFLAGS="$CFLAGS $GTK_CFLAGS"
279 LIBS="$GTK_LIBS $LIBS"
280dnl
281dnl Now check if the installed GTK+ is sufficiently new. (Also sanity
282dnl checks the results of pkg-config to some extent)
283dnl
284 rm -f conf.gtktest
285 AC_TRY_RUN([
286#include <gtk/gtk.h>
287#include <stdio.h>
288#include <stdlib.h>
289
290int
291main ()
292{
293 unsigned int major, minor, micro;
294
295 fclose (fopen ("conf.gtktest", "w"));
296
297 if (sscanf("$min_gtk_version", "%u.%u.%u", &major, &minor, &micro) != 3) {
298 printf("%s, bad version string\n", "$min_gtk_version");
299 exit(1);
300 }
301
302 if ((gtk_major_version != $gtk_config_major_version) ||
303 (gtk_minor_version != $gtk_config_minor_version) ||
304 (gtk_micro_version != $gtk_config_micro_version))
305 {
306 printf("\n*** 'pkg-config --modversion gtk+-3.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
307 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
308 gtk_major_version, gtk_minor_version, gtk_micro_version);
309 printf ("*** was found! If pkg-config was correct, then it is best\n");
310 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
311 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
312 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
313 printf("*** required on your system.\n");
314 printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
315 printf("*** to point to the correct configuration files\n");
316 }
317 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
318 (gtk_minor_version != GTK_MINOR_VERSION) ||
319 (gtk_micro_version != GTK_MICRO_VERSION))
320 {
321 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
322 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
323 printf("*** library (version %d.%d.%d)\n",
324 gtk_major_version, gtk_minor_version, gtk_micro_version);
325 }
326 else
327 {
328 if ((gtk_major_version > major) ||
329 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
330 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
331 {
332 return 0;
333 }
334 else
335 {
336 printf("\n*** An old version of GTK+ (%u.%u.%u) was found.\n",
337 gtk_major_version, gtk_minor_version, gtk_micro_version);
338 printf("*** You need a version of GTK+ newer than %u.%u.%u. The latest version of\n",
339 major, minor, micro);
340 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
341 printf("***\n");
342 printf("*** If you have already installed a sufficiently new version, this error\n");
343 printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
344 printf("*** being found. The easiest way to fix this is to remove the old version\n");
345 printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n");
346 printf("*** correct copy of pkg-config. (In this case, you will have to\n");
347 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
348 printf("*** so that the correct libraries are found at run-time))\n");
349 }
350 }
351 return 1;
352}
353],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
354 CFLAGS="$ac_save_CFLAGS"
355 LIBS="$ac_save_LIBS"
356 fi
357 fi
358 if test "x$no_gtk" = x ; then
359 AC_MSG_RESULT(yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version))
360 ifelse([$2], , :, [$2])
361 else
362 AC_MSG_RESULT(no)
363 if test "$PKG_CONFIG" = "no" ; then
364 echo "*** A new enough version of pkg-config was not found."
365 echo "*** See http://pkgconfig.sourceforge.net"
366 else
367 if test -f conf.gtktest ; then
368 :
369 else
370 echo "*** Could not run GTK+ test program, checking why..."
371 ac_save_CFLAGS="$CFLAGS"
372 ac_save_LIBS="$LIBS"
373 CFLAGS="$CFLAGS $GTK_CFLAGS"
374 LIBS="$LIBS $GTK_LIBS"
375 AC_TRY_LINK([
376#include <gtk/gtk.h>
377#include <stdio.h>
378], [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ],
379 [ echo "*** The test program compiled, but did not run. This usually means"
380 echo "*** that the run-time linker is not finding GTK+ or finding the wrong"
381 echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your"
382 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
383 echo "*** to the installed location Also, make sure you have run ldconfig if that"
384 echo "*** is required on your system"
385 echo "***"
386 echo "*** If you have an old version installed, it is best to remove it, although"
387 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
388 [ echo "*** The test program failed to compile or link. See the file config.log for the"
389 echo "*** exact error that occurred. This usually means GTK+ is incorrectly installed."])
390 CFLAGS="$ac_save_CFLAGS"
391 LIBS="$ac_save_LIBS"
392 fi
393 fi
394 GTK_CFLAGS=""
395 GTK_LIBS=""
396 ifelse([$3], , :, [$3])
397 fi
398 AC_SUBST(GTK_CFLAGS)
399 AC_SUBST(GTK_LIBS)
400 rm -f conf.gtktest
401])
402
403dnl GTK_CHECK_BACKEND(BACKEND-NAME [, MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
404dnl Tests for BACKEND-NAME in the GTK targets list
405dnl
406AC_DEFUN([GTK_CHECK_BACKEND],
407[m4_warn([obsolete], [GTK_CHECK_BACKEND is deprecated, use PKG_CHECK_MODULES([GTK_X11], [gtk+-x11-3.0]) or similar instead])
408 pkg_config_args=ifelse([$1],,gtk+-3.0, gtk+-$1-3.0)
409 min_gtk_version=ifelse([$2],,3.0.0,$2)
410 pkg_config_args="$pkg_config_args >= $min_gtk_version"
411
412 AC_PATH_PROG(PKG_CONFIG, [pkg-config], [AC_MSG_ERROR([No pkg-config found])])
413
414 if $PKG_CONFIG $pkg_config_args ; then
415 target_found=yes
416 else
417 target_found=no
418 fi
419
420 if test "x$target_found" = "xno"; then
421 ifelse([$4],,[AC_MSG_ERROR([Backend $backend not found.])],[$4])
422 else
423 ifelse([$3],,[:],[$3])
424 fi
425])
426
427dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
428dnl serial 11 (pkg-config-0.29.1)
429dnl
430dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
431dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
432dnl
433dnl This program is free software; you can redistribute it and/or modify
434dnl it under the terms of the GNU General Public License as published by
435dnl the Free Software Foundation; either version 2 of the License, or
436dnl (at your option) any later version.
437dnl
438dnl This program is distributed in the hope that it will be useful, but
439dnl WITHOUT ANY WARRANTY; without even the implied warranty of
440dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
441dnl General Public License for more details.
442dnl
443dnl You should have received a copy of the GNU General Public License
444dnl along with this program; if not, write to the Free Software
445dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
446dnl 02111-1307, USA.
447dnl
448dnl As a special exception to the GNU General Public License, if you
449dnl distribute this file as part of a program that contains a
450dnl configuration script generated by Autoconf, you may include it under
451dnl the same distribution terms that you use for the rest of that
452dnl program.
453
454dnl PKG_PREREQ(MIN-VERSION)
455dnl -----------------------
456dnl Since: 0.29
457dnl
458dnl Verify that the version of the pkg-config macros are at least
459dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
460dnl installed version of pkg-config, this checks the developer's version
461dnl of pkg.m4 when generating configure.
462dnl
463dnl To ensure that this macro is defined, also add:
464dnl m4_ifndef([PKG_PREREQ],
465dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
466dnl
467dnl See the "Since" comment for each macro you use to see what version
468dnl of the macros you require.
469m4_defun([PKG_PREREQ],
470[m4_define([PKG_MACROS_VERSION], [0.29.1])
471m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
472 [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
473])dnl PKG_PREREQ
474
475dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
476dnl ----------------------------------
477dnl Since: 0.16
478dnl
479dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
480dnl first found in the path. Checks that the version of pkg-config found
481dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
482dnl used since that's the first version where most current features of
483dnl pkg-config existed.
484AC_DEFUN([PKG_PROG_PKG_CONFIG],
485[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
486m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
487m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
488AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
489AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
490AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
491
492if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
493 AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
494fi
495if test -n "$PKG_CONFIG"; then
496 _pkg_min_version=m4_default([$1], [0.9.0])
497 AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
498 if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
499 AC_MSG_RESULT([yes])
500 else
501 AC_MSG_RESULT([no])
502 PKG_CONFIG=""
503 fi
504fi[]dnl
505])dnl PKG_PROG_PKG_CONFIG
506
507dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
508dnl -------------------------------------------------------------------
509dnl Since: 0.18
510dnl
511dnl Check to see whether a particular set of modules exists. Similar to
512dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
513dnl
514dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
515dnl only at the first occurence in configure.ac, so if the first place
516dnl it's called might be skipped (such as if it is within an "if", you
517dnl have to call PKG_CHECK_EXISTS manually
518AC_DEFUN([PKG_CHECK_EXISTS],
519[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
520if test -n "$PKG_CONFIG" && \
521 AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
522 m4_default([$2], [:])
523m4_ifvaln([$3], [else
524 $3])dnl
525fi])
526
527dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
528dnl ---------------------------------------------
529dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
530dnl pkg_failed based on the result.
531m4_define([_PKG_CONFIG],
532[if test -n "$$1"; then
533 pkg_cv_[]$1="$$1"
534 elif test -n "$PKG_CONFIG"; then
535 PKG_CHECK_EXISTS([$3],
536 [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
537 test "x$?" != "x0" && pkg_failed=yes ],
538 [pkg_failed=yes])
539 else
540 pkg_failed=untried
541fi[]dnl
542])dnl _PKG_CONFIG
543
544dnl _PKG_SHORT_ERRORS_SUPPORTED
545dnl ---------------------------
546dnl Internal check to see if pkg-config supports short errors.
547AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
548[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
549if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
550 _pkg_short_errors_supported=yes
551else
552 _pkg_short_errors_supported=no
553fi[]dnl
554])dnl _PKG_SHORT_ERRORS_SUPPORTED
555
556
557dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
558dnl [ACTION-IF-NOT-FOUND])
559dnl --------------------------------------------------------------
560dnl Since: 0.4.0
561dnl
562dnl Note that if there is a possibility the first call to
563dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
564dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
565AC_DEFUN([PKG_CHECK_MODULES],
566[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
567AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
568AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
569
570pkg_failed=no
571AC_MSG_CHECKING([for $1])
572
573_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
574_PKG_CONFIG([$1][_LIBS], [libs], [$2])
575
576m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
577and $1[]_LIBS to avoid the need to call pkg-config.
578See the pkg-config man page for more details.])
579
580if test $pkg_failed = yes; then
581 AC_MSG_RESULT([no])
582 _PKG_SHORT_ERRORS_SUPPORTED
583 if test $_pkg_short_errors_supported = yes; then
584 $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
585 else
586 $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
587 fi
588 # Put the nasty error message in config.log where it belongs
589 echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
590
591 m4_default([$4], [AC_MSG_ERROR(
592[Package requirements ($2) were not met:
593
594$$1_PKG_ERRORS
595
596Consider adjusting the PKG_CONFIG_PATH environment variable if you
597installed software in a non-standard prefix.
598
599_PKG_TEXT])[]dnl
600 ])
601elif test $pkg_failed = untried; then
602 AC_MSG_RESULT([no])
603 m4_default([$4], [AC_MSG_FAILURE(
604[The pkg-config script could not be found or is too old. Make sure it
605is in your PATH or set the PKG_CONFIG environment variable to the full
606path to pkg-config.
607
608_PKG_TEXT
609
610To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
611 ])
612else
613 $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
614 $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
615 AC_MSG_RESULT([yes])
616 $3
617fi[]dnl
618])dnl PKG_CHECK_MODULES
619
620
621dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
622dnl [ACTION-IF-NOT-FOUND])
623dnl ---------------------------------------------------------------------
624dnl Since: 0.29
625dnl
626dnl Checks for existence of MODULES and gathers its build flags with
627dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
628dnl and VARIABLE-PREFIX_LIBS from --libs.
629dnl
630dnl Note that if there is a possibility the first call to
631dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
632dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
633dnl configure.ac.
634AC_DEFUN([PKG_CHECK_MODULES_STATIC],
635[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
636_save_PKG_CONFIG=$PKG_CONFIG
637PKG_CONFIG="$PKG_CONFIG --static"
638PKG_CHECK_MODULES($@)
639PKG_CONFIG=$_save_PKG_CONFIG[]dnl
640])dnl PKG_CHECK_MODULES_STATIC
641
642
643dnl PKG_INSTALLDIR([DIRECTORY])
644dnl -------------------------
645dnl Since: 0.27
646dnl
647dnl Substitutes the variable pkgconfigdir as the location where a module
648dnl should install pkg-config .pc files. By default the directory is
649dnl $libdir/pkgconfig, but the default can be changed by passing
650dnl DIRECTORY. The user can override through the --with-pkgconfigdir
651dnl parameter.
652AC_DEFUN([PKG_INSTALLDIR],
653[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
654m4_pushdef([pkg_description],
655 [pkg-config installation directory @<:@]pkg_default[@:>@])
656AC_ARG_WITH([pkgconfigdir],
657 [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
658 [with_pkgconfigdir=]pkg_default)
659AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
660m4_popdef([pkg_default])
661m4_popdef([pkg_description])
662])dnl PKG_INSTALLDIR
663
664
665dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
666dnl --------------------------------
667dnl Since: 0.27
668dnl
669dnl Substitutes the variable noarch_pkgconfigdir as the location where a
670dnl module should install arch-independent pkg-config .pc files. By
671dnl default the directory is $datadir/pkgconfig, but the default can be
672dnl changed by passing DIRECTORY. The user can override through the
673dnl --with-noarch-pkgconfigdir parameter.
674AC_DEFUN([PKG_NOARCH_INSTALLDIR],
675[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
676m4_pushdef([pkg_description],
677 [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
678AC_ARG_WITH([noarch-pkgconfigdir],
679 [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
680 [with_noarch_pkgconfigdir=]pkg_default)
681AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
682m4_popdef([pkg_default])
683m4_popdef([pkg_description])
684])dnl PKG_NOARCH_INSTALLDIR
685
686
687dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
688dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
689dnl -------------------------------------------
690dnl Since: 0.28
691dnl
692dnl Retrieves the value of the pkg-config variable for the given module.
693AC_DEFUN([PKG_CHECK_VAR],
694[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
695AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
696
697_PKG_CONFIG([$1], [variable="][$3]["], [$2])
698AS_VAR_COPY([$1], [pkg_cv_][$1])
699
700AS_VAR_IF([$1], [""], [$5], [$4])dnl
701])dnl PKG_CHECK_VAR
702
703# Copyright (C) 2002-2014 Free Software Foundation, Inc.
704#
705# This file is free software; the Free Software Foundation
706# gives unlimited permission to copy and/or distribute it,
707# with or without modifications, as long as this notice is preserved.
708
709# AM_AUTOMAKE_VERSION(VERSION)
710# ----------------------------
711# Automake X.Y traces this macro to ensure aclocal.m4 has been
712# generated from the m4 files accompanying Automake X.Y.
713# (This private macro should not be called outside this file.)
714AC_DEFUN([AM_AUTOMAKE_VERSION],
715[am__api_version='1.15'
716dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
717dnl require some minimum version. Point them to the right macro.
718m4_if([$1], [1.15], [],
719 [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
720])
721
722# _AM_AUTOCONF_VERSION(VERSION)
723# -----------------------------
724# aclocal traces this macro to find the Autoconf version.
725# This is a private macro too. Using m4_define simplifies
726# the logic in aclocal, which can simply ignore this definition.
727m4_define([_AM_AUTOCONF_VERSION], [])
728
729# AM_SET_CURRENT_AUTOMAKE_VERSION
730# -------------------------------
731# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
732# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
733AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
734[AM_AUTOMAKE_VERSION([1.15])dnl
735m4_ifndef([AC_AUTOCONF_VERSION],
736 [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
737_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
738
739# AM_AUX_DIR_EXPAND -*- Autoconf -*-
740
741# Copyright (C) 2001-2014 Free Software Foundation, Inc.
742#
743# This file is free software; the Free Software Foundation
744# gives unlimited permission to copy and/or distribute it,
745# with or without modifications, as long as this notice is preserved.
746
747# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
748# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
749# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
750#
751# Of course, Automake must honor this variable whenever it calls a
752# tool from the auxiliary directory. The problem is that $srcdir (and
753# therefore $ac_aux_dir as well) can be either absolute or relative,
754# depending on how configure is run. This is pretty annoying, since
755# it makes $ac_aux_dir quite unusable in subdirectories: in the top
756# source directory, any form will work fine, but in subdirectories a
757# relative path needs to be adjusted first.
758#
759# $ac_aux_dir/missing
760# fails when called from a subdirectory if $ac_aux_dir is relative
761# $top_srcdir/$ac_aux_dir/missing
762# fails if $ac_aux_dir is absolute,
763# fails when called from a subdirectory in a VPATH build with
764# a relative $ac_aux_dir
765#
766# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
767# are both prefixed by $srcdir. In an in-source build this is usually
768# harmless because $srcdir is '.', but things will broke when you
769# start a VPATH build or use an absolute $srcdir.
770#
771# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
772# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
773# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
774# and then we would define $MISSING as
775# MISSING="\${SHELL} $am_aux_dir/missing"
776# This will work as long as MISSING is not called from configure, because
777# unfortunately $(top_srcdir) has no meaning in configure.
778# However there are other variables, like CC, which are often used in
779# configure, and could therefore not use this "fixed" $ac_aux_dir.
780#
781# Another solution, used here, is to always expand $ac_aux_dir to an
782# absolute PATH. The drawback is that using absolute paths prevent a
783# configured tree to be moved without reconfiguration.
784
785AC_DEFUN([AM_AUX_DIR_EXPAND],
786[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
787# Expand $ac_aux_dir to an absolute path.
788am_aux_dir=`cd "$ac_aux_dir" && pwd`
789])
790
791# AM_CONDITIONAL -*- Autoconf -*-
792
793# Copyright (C) 1997-2014 Free Software Foundation, Inc.
794#
795# This file is free software; the Free Software Foundation
796# gives unlimited permission to copy and/or distribute it,
797# with or without modifications, as long as this notice is preserved.
798
799# AM_CONDITIONAL(NAME, SHELL-CONDITION)
800# -------------------------------------
801# Define a conditional.
802AC_DEFUN([AM_CONDITIONAL],
803[AC_PREREQ([2.52])dnl
804 m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
805 [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
806AC_SUBST([$1_TRUE])dnl
807AC_SUBST([$1_FALSE])dnl
808_AM_SUBST_NOTMAKE([$1_TRUE])dnl
809_AM_SUBST_NOTMAKE([$1_FALSE])dnl
810m4_define([_AM_COND_VALUE_$1], [$2])dnl
811if $2; then
812 $1_TRUE=
813 $1_FALSE='#'
814else
815 $1_TRUE='#'
816 $1_FALSE=
817fi
818AC_CONFIG_COMMANDS_PRE(
819[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
820 AC_MSG_ERROR([[conditional "$1" was never defined.
821Usually this means the macro was only invoked conditionally.]])
822fi])])
823
824# Copyright (C) 1999-2014 Free Software Foundation, Inc.
825#
826# This file is free software; the Free Software Foundation
827# gives unlimited permission to copy and/or distribute it,
828# with or without modifications, as long as this notice is preserved.
829
830
831# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
832# written in clear, in which case automake, when reading aclocal.m4,
833# will think it sees a *use*, and therefore will trigger all it's
834# C support machinery. Also note that it means that autoscan, seeing
835# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
836
837
838# _AM_DEPENDENCIES(NAME)
839# ----------------------
840# See how the compiler implements dependency checking.
841# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
842# We try a few techniques and use that to set a single cache variable.
843#
844# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
845# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
846# dependency, and given that the user is not expected to run this macro,
847# just rely on AC_PROG_CC.
848AC_DEFUN([_AM_DEPENDENCIES],
849[AC_REQUIRE([AM_SET_DEPDIR])dnl
850AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
851AC_REQUIRE([AM_MAKE_INCLUDE])dnl
852AC_REQUIRE([AM_DEP_TRACK])dnl
853
854m4_if([$1], [CC], [depcc="$CC" am_compiler_list=],
855 [$1], [CXX], [depcc="$CXX" am_compiler_list=],
856 [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
857 [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
858 [$1], [UPC], [depcc="$UPC" am_compiler_list=],
859 [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
860 [depcc="$$1" am_compiler_list=])
861
862AC_CACHE_CHECK([dependency style of $depcc],
863 [am_cv_$1_dependencies_compiler_type],
864[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
865 # We make a subdir and do the tests there. Otherwise we can end up
866 # making bogus files that we don't know about and never remove. For
867 # instance it was reported that on HP-UX the gcc test will end up
868 # making a dummy file named 'D' -- because '-MD' means "put the output
869 # in D".
870 rm -rf conftest.dir
871 mkdir conftest.dir
872 # Copy depcomp to subdir because otherwise we won't find it if we're
873 # using a relative directory.
874 cp "$am_depcomp" conftest.dir
875 cd conftest.dir
876 # We will build objects and dependencies in a subdirectory because
877 # it helps to detect inapplicable dependency modes. For instance
878 # both Tru64's cc and ICC support -MD to output dependencies as a
879 # side effect of compilation, but ICC will put the dependencies in
880 # the current directory while Tru64 will put them in the object
881 # directory.
882 mkdir sub
883
884 am_cv_$1_dependencies_compiler_type=none
885 if test "$am_compiler_list" = ""; then
886 am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
887 fi
888 am__universal=false
889 m4_case([$1], [CC],
890 [case " $depcc " in #(
891 *\ -arch\ *\ -arch\ *) am__universal=true ;;
892 esac],
893 [CXX],
894 [case " $depcc " in #(
895 *\ -arch\ *\ -arch\ *) am__universal=true ;;
896 esac])
897
898 for depmode in $am_compiler_list; do
899 # Setup a source with many dependencies, because some compilers
900 # like to wrap large dependency lists on column 80 (with \), and
901 # we should not choose a depcomp mode which is confused by this.
902 #
903 # We need to recreate these files for each test, as the compiler may
904 # overwrite some of them when testing with obscure command lines.
905 # This happens at least with the AIX C compiler.
906 : > sub/conftest.c
907 for i in 1 2 3 4 5 6; do
908 echo '#include "conftst'$i'.h"' >> sub/conftest.c
909 # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
910 # Solaris 10 /bin/sh.
911 echo '/* dummy */' > sub/conftst$i.h
912 done
913 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
914
915 # We check with '-c' and '-o' for the sake of the "dashmstdout"
916 # mode. It turns out that the SunPro C++ compiler does not properly
917 # handle '-M -o', and we need to detect this. Also, some Intel
918 # versions had trouble with output in subdirs.
919 am__obj=sub/conftest.${OBJEXT-o}
920 am__minus_obj="-o $am__obj"
921 case $depmode in
922 gcc)
923 # This depmode causes a compiler race in universal mode.
924 test "$am__universal" = false || continue
925 ;;
926 nosideeffect)
927 # After this tag, mechanisms are not by side-effect, so they'll
928 # only be used when explicitly requested.
929 if test "x$enable_dependency_tracking" = xyes; then
930 continue
931 else
932 break
933 fi
934 ;;
935 msvc7 | msvc7msys | msvisualcpp | msvcmsys)
936 # This compiler won't grok '-c -o', but also, the minuso test has
937 # not run yet. These depmodes are late enough in the game, and
938 # so weak that their functioning should not be impacted.
939 am__obj=conftest.${OBJEXT-o}
940 am__minus_obj=
941 ;;
942 none) break ;;
943 esac
944 if depmode=$depmode \
945 source=sub/conftest.c object=$am__obj \
946 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
947 $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
948 >/dev/null 2>conftest.err &&
949 grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
950 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
951 grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
952 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
953 # icc doesn't choke on unknown options, it will just issue warnings
954 # or remarks (even with -Werror). So we grep stderr for any message
955 # that says an option was ignored or not supported.
956 # When given -MP, icc 7.0 and 7.1 complain thusly:
957 # icc: Command line warning: ignoring option '-M'; no argument required
958 # The diagnosis changed in icc 8.0:
959 # icc: Command line remark: option '-MP' not supported
960 if (grep 'ignoring option' conftest.err ||
961 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
962 am_cv_$1_dependencies_compiler_type=$depmode
963 break
964 fi
965 fi
966 done
967
968 cd ..
969 rm -rf conftest.dir
970else
971 am_cv_$1_dependencies_compiler_type=none
972fi
973])
974AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
975AM_CONDITIONAL([am__fastdep$1], [
976 test "x$enable_dependency_tracking" != xno \
977 && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
978])
979
980
981# AM_SET_DEPDIR
982# -------------
983# Choose a directory name for dependency files.
984# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
985AC_DEFUN([AM_SET_DEPDIR],
986[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
987AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
988])
989
990
991# AM_DEP_TRACK
992# ------------
993AC_DEFUN([AM_DEP_TRACK],
994[AC_ARG_ENABLE([dependency-tracking], [dnl
995AS_HELP_STRING(
996 [--enable-dependency-tracking],
997 [do not reject slow dependency extractors])
998AS_HELP_STRING(
999 [--disable-dependency-tracking],
1000 [speeds up one-time build])])
1001if test "x$enable_dependency_tracking" != xno; then
1002 am_depcomp="$ac_aux_dir/depcomp"
1003 AMDEPBACKSLASH='\'
1004 am__nodep='_no'
1005fi
1006AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
1007AC_SUBST([AMDEPBACKSLASH])dnl
1008_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
1009AC_SUBST([am__nodep])dnl
1010_AM_SUBST_NOTMAKE([am__nodep])dnl
1011])
1012
1013# Generate code to set up dependency tracking. -*- Autoconf -*-
1014
1015# Copyright (C) 1999-2014 Free Software Foundation, Inc.
1016#
1017# This file is free software; the Free Software Foundation
1018# gives unlimited permission to copy and/or distribute it,
1019# with or without modifications, as long as this notice is preserved.
1020
1021
1022# _AM_OUTPUT_DEPENDENCY_COMMANDS
1023# ------------------------------
1024AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
1025[{
1026 # Older Autoconf quotes --file arguments for eval, but not when files
1027 # are listed without --file. Let's play safe and only enable the eval
1028 # if we detect the quoting.
1029 case $CONFIG_FILES in
1030 *\'*) eval set x "$CONFIG_FILES" ;;
1031 *) set x $CONFIG_FILES ;;
1032 esac
1033 shift
1034 for mf
1035 do
1036 # Strip MF so we end up with the name of the file.
1037 mf=`echo "$mf" | sed -e 's/:.*$//'`
1038 # Check whether this is an Automake generated Makefile or not.
1039 # We used to match only the files named 'Makefile.in', but
1040 # some people rename them; so instead we look at the file content.
1041 # Grep'ing the first line is not enough: some people post-process
1042 # each Makefile.in and add a new line on top of each file to say so.
1043 # Grep'ing the whole file is not good either: AIX grep has a line
1044 # limit of 2048, but all sed's we know have understand at least 4000.
1045 if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
1046 dirpart=`AS_DIRNAME("$mf")`
1047 else
1048 continue
1049 fi
1050 # Extract the definition of DEPDIR, am__include, and am__quote
1051 # from the Makefile without running 'make'.
1052 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
1053 test -z "$DEPDIR" && continue
1054 am__include=`sed -n 's/^am__include = //p' < "$mf"`
1055 test -z "$am__include" && continue
1056 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
1057 # Find all dependency output files, they are included files with
1058 # $(DEPDIR) in their names. We invoke sed twice because it is the
1059 # simplest approach to changing $(DEPDIR) to its actual value in the
1060 # expansion.
1061 for file in `sed -n "
1062 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
1063 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
1064 # Make sure the directory exists.
1065 test -f "$dirpart/$file" && continue
1066 fdir=`AS_DIRNAME(["$file"])`
1067 AS_MKDIR_P([$dirpart/$fdir])
1068 # echo "creating $dirpart/$file"
1069 echo '# dummy' > "$dirpart/$file"
1070 done
1071 done
1072}
1073])# _AM_OUTPUT_DEPENDENCY_COMMANDS
1074
1075
1076# AM_OUTPUT_DEPENDENCY_COMMANDS
1077# -----------------------------
1078# This macro should only be invoked once -- use via AC_REQUIRE.
1079#
1080# This code is only required when automatic dependency tracking
1081# is enabled. FIXME. This creates each '.P' file that we will
1082# need in order to bootstrap the dependency handling code.
1083AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
1084[AC_CONFIG_COMMANDS([depfiles],
1085 [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
1086 [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
1087])
1088
1089# Do all the work for Automake. -*- Autoconf -*-
1090
1091# Copyright (C) 1996-2014 Free Software Foundation, Inc.
1092#
1093# This file is free software; the Free Software Foundation
1094# gives unlimited permission to copy and/or distribute it,
1095# with or without modifications, as long as this notice is preserved.
1096
1097# This macro actually does too much. Some checks are only needed if
1098# your package does certain things. But this isn't really a big deal.
1099
1100dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
1101m4_define([AC_PROG_CC],
1102m4_defn([AC_PROG_CC])
1103[_AM_PROG_CC_C_O
1104])
1105
1106# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
1107# AM_INIT_AUTOMAKE([OPTIONS])
1108# -----------------------------------------------
1109# The call with PACKAGE and VERSION arguments is the old style
1110# call (pre autoconf-2.50), which is being phased out. PACKAGE
1111# and VERSION should now be passed to AC_INIT and removed from
1112# the call to AM_INIT_AUTOMAKE.
1113# We support both call styles for the transition. After
1114# the next Automake release, Autoconf can make the AC_INIT
1115# arguments mandatory, and then we can depend on a new Autoconf
1116# release and drop the old call support.
1117AC_DEFUN([AM_INIT_AUTOMAKE],
1118[AC_PREREQ([2.65])dnl
1119dnl Autoconf wants to disallow AM_ names. We explicitly allow
1120dnl the ones we care about.
1121m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
1122AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
1123AC_REQUIRE([AC_PROG_INSTALL])dnl
1124if test "`cd $srcdir && pwd`" != "`pwd`"; then
1125 # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
1126 # is not polluted with repeated "-I."
1127 AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
1128 # test to see if srcdir already configured
1129 if test -f $srcdir/config.status; then
1130 AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
1131 fi
1132fi
1133
1134# test whether we have cygpath
1135if test -z "$CYGPATH_W"; then
1136 if (cygpath --version) >/dev/null 2>/dev/null; then
1137 CYGPATH_W='cygpath -w'
1138 else
1139 CYGPATH_W=echo
1140 fi
1141fi
1142AC_SUBST([CYGPATH_W])
1143
1144# Define the identity of the package.
1145dnl Distinguish between old-style and new-style calls.
1146m4_ifval([$2],
1147[AC_DIAGNOSE([obsolete],
1148 [$0: two- and three-arguments forms are deprecated.])
1149m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
1150 AC_SUBST([PACKAGE], [$1])dnl
1151 AC_SUBST([VERSION], [$2])],
1152[_AM_SET_OPTIONS([$1])dnl
1153dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
1154m4_if(
1155 m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
1156 [ok:ok],,
1157 [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
1158 AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
1159 AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
1160
1161_AM_IF_OPTION([no-define],,
1162[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
1163 AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
1164
1165# Some tools Automake needs.
1166AC_REQUIRE([AM_SANITY_CHECK])dnl
1167AC_REQUIRE([AC_ARG_PROGRAM])dnl
1168AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
1169AM_MISSING_PROG([AUTOCONF], [autoconf])
1170AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
1171AM_MISSING_PROG([AUTOHEADER], [autoheader])
1172AM_MISSING_PROG([MAKEINFO], [makeinfo])
1173AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
1174AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
1175AC_REQUIRE([AC_PROG_MKDIR_P])dnl
1176# For better backward compatibility. To be removed once Automake 1.9.x
1177# dies out for good. For more background, see:
1178# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
1179# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
1180AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
1181# We need awk for the "check" target (and possibly the TAP driver). The
1182# system "awk" is bad on some platforms.
1183AC_REQUIRE([AC_PROG_AWK])dnl
1184AC_REQUIRE([AC_PROG_MAKE_SET])dnl
1185AC_REQUIRE([AM_SET_LEADING_DOT])dnl
1186_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
1187 [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
1188 [_AM_PROG_TAR([v7])])])
1189_AM_IF_OPTION([no-dependencies],,
1190[AC_PROVIDE_IFELSE([AC_PROG_CC],
1191 [_AM_DEPENDENCIES([CC])],
1192 [m4_define([AC_PROG_CC],
1193 m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
1194AC_PROVIDE_IFELSE([AC_PROG_CXX],
1195 [_AM_DEPENDENCIES([CXX])],
1196 [m4_define([AC_PROG_CXX],
1197 m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
1198AC_PROVIDE_IFELSE([AC_PROG_OBJC],
1199 [_AM_DEPENDENCIES([OBJC])],
1200 [m4_define([AC_PROG_OBJC],
1201 m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
1202AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
1203 [_AM_DEPENDENCIES([OBJCXX])],
1204 [m4_define([AC_PROG_OBJCXX],
1205 m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
1206])
1207AC_REQUIRE([AM_SILENT_RULES])dnl
1208dnl The testsuite driver may need to know about EXEEXT, so add the
1209dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
1210dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
1211AC_CONFIG_COMMANDS_PRE(dnl
1212[m4_provide_if([_AM_COMPILER_EXEEXT],
1213 [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
1214
1215# POSIX will say in a future version that running "rm -f" with no argument
1216# is OK; and we want to be able to make that assumption in our Makefile
1217# recipes. So use an aggressive probe to check that the usage we want is
1218# actually supported "in the wild" to an acceptable degree.
1219# See automake bug#10828.
1220# To make any issue more visible, cause the running configure to be aborted
1221# by default if the 'rm' program in use doesn't match our expectations; the
1222# user can still override this though.
1223if rm -f && rm -fr && rm -rf; then : OK; else
1224 cat >&2 <<'END'
1225Oops!
1226
1227Your 'rm' program seems unable to run without file operands specified
1228on the command line, even when the '-f' option is present. This is contrary
1229to the behaviour of most rm programs out there, and not conforming with
1230the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
1231
1232Please tell bug-automake@gnu.org about your system, including the value
1233of your $PATH and any error possibly output before this message. This
1234can help us improve future automake versions.
1235
1236END
1237 if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
1238 echo 'Configuration will proceed anyway, since you have set the' >&2
1239 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
1240 echo >&2
1241 else
1242 cat >&2 <<'END'
1243Aborting the configuration process, to ensure you take notice of the issue.
1244
1245You can download and install GNU coreutils to get an 'rm' implementation
1246that behaves properly: <http://www.gnu.org/software/coreutils/>.
1247
1248If you want to complete the configuration process using your problematic
1249'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
1250to "yes", and re-run configure.
1251
1252END
1253 AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
1254 fi
1255fi
1256dnl The trailing newline in this macro's definition is deliberate, for
1257dnl backward compatibility and to allow trailing 'dnl'-style comments
1258dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
1259])
1260
1261dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
1262dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
1263dnl mangled by Autoconf and run in a shell conditional statement.
1264m4_define([_AC_COMPILER_EXEEXT],
1265m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
1266
1267# When config.status generates a header, we must update the stamp-h file.
1268# This file resides in the same directory as the config header
1269# that is generated. The stamp files are numbered to have different names.
1270
1271# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
1272# loop where config.status creates the headers, so we can generate
1273# our stamp files there.
1274AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
1275[# Compute $1's index in $config_headers.
1276_am_arg=$1
1277_am_stamp_count=1
1278for _am_header in $config_headers :; do
1279 case $_am_header in
1280 $_am_arg | $_am_arg:* )
1281 break ;;
1282 * )
1283 _am_stamp_count=`expr $_am_stamp_count + 1` ;;
1284 esac
1285done
1286echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
1287
1288# Copyright (C) 2001-2014 Free Software Foundation, Inc.
1289#
1290# This file is free software; the Free Software Foundation
1291# gives unlimited permission to copy and/or distribute it,
1292# with or without modifications, as long as this notice is preserved.
1293
1294# AM_PROG_INSTALL_SH
1295# ------------------
1296# Define $install_sh.
1297AC_DEFUN([AM_PROG_INSTALL_SH],
1298[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
1299if test x"${install_sh+set}" != xset; then
1300 case $am_aux_dir in
1301 *\ * | *\ *)
1302 install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
1303 *)
1304 install_sh="\${SHELL} $am_aux_dir/install-sh"
1305 esac
1306fi
1307AC_SUBST([install_sh])])
1308
1309# Copyright (C) 2003-2014 Free Software Foundation, Inc.
1310#
1311# This file is free software; the Free Software Foundation
1312# gives unlimited permission to copy and/or distribute it,
1313# with or without modifications, as long as this notice is preserved.
1314
1315# Check whether the underlying file-system supports filenames
1316# with a leading dot. For instance MS-DOS doesn't.
1317AC_DEFUN([AM_SET_LEADING_DOT],
1318[rm -rf .tst 2>/dev/null
1319mkdir .tst 2>/dev/null
1320if test -d .tst; then
1321 am__leading_dot=.
1322else
1323 am__leading_dot=_
1324fi
1325rmdir .tst 2>/dev/null
1326AC_SUBST([am__leading_dot])])
1327
1328# Check to see how 'make' treats includes. -*- Autoconf -*-
1329
1330# Copyright (C) 2001-2014 Free Software Foundation, Inc.
1331#
1332# This file is free software; the Free Software Foundation
1333# gives unlimited permission to copy and/or distribute it,
1334# with or without modifications, as long as this notice is preserved.
1335
1336# AM_MAKE_INCLUDE()
1337# -----------------
1338# Check to see how make treats includes.
1339AC_DEFUN([AM_MAKE_INCLUDE],
1340[am_make=${MAKE-make}
1341cat > confinc << 'END'
1342am__doit:
1343 @echo this is the am__doit target
1344.PHONY: am__doit
1345END
1346# If we don't find an include directive, just comment out the code.
1347AC_MSG_CHECKING([for style of include used by $am_make])
1348am__include="#"
1349am__quote=
1350_am_result=none
1351# First try GNU make style include.
1352echo "include confinc" > confmf
1353# Ignore all kinds of additional output from 'make'.
1354case `$am_make -s -f confmf 2> /dev/null` in #(
1355*the\ am__doit\ target*)
1356 am__include=include
1357 am__quote=
1358 _am_result=GNU
1359 ;;
1360esac
1361# Now try BSD make style include.
1362if test "$am__include" = "#"; then
1363 echo '.include "confinc"' > confmf
1364 case `$am_make -s -f confmf 2> /dev/null` in #(
1365 *the\ am__doit\ target*)
1366 am__include=.include
1367 am__quote="\""
1368 _am_result=BSD
1369 ;;
1370 esac
1371fi
1372AC_SUBST([am__include])
1373AC_SUBST([am__quote])
1374AC_MSG_RESULT([$_am_result])
1375rm -f confinc confmf
1376])
1377
1378# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
1379
1380# Copyright (C) 1997-2014 Free Software Foundation, Inc.
1381#
1382# This file is free software; the Free Software Foundation
1383# gives unlimited permission to copy and/or distribute it,
1384# with or without modifications, as long as this notice is preserved.
1385
1386# AM_MISSING_PROG(NAME, PROGRAM)
1387# ------------------------------
1388AC_DEFUN([AM_MISSING_PROG],
1389[AC_REQUIRE([AM_MISSING_HAS_RUN])
1390$1=${$1-"${am_missing_run}$2"}
1391AC_SUBST($1)])
1392
1393# AM_MISSING_HAS_RUN
1394# ------------------
1395# Define MISSING if not defined so far and test if it is modern enough.
1396# If it is, set am_missing_run to use it, otherwise, to nothing.
1397AC_DEFUN([AM_MISSING_HAS_RUN],
1398[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
1399AC_REQUIRE_AUX_FILE([missing])dnl
1400if test x"${MISSING+set}" != xset; then
1401 case $am_aux_dir in
1402 *\ * | *\ *)
1403 MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
1404 *)
1405 MISSING="\${SHELL} $am_aux_dir/missing" ;;
1406 esac
1407fi
1408# Use eval to expand $SHELL
1409if eval "$MISSING --is-lightweight"; then
1410 am_missing_run="$MISSING "
1411else
1412 am_missing_run=
1413 AC_MSG_WARN(['missing' script is too old or missing])
1414fi
1415])
1416
1417# Helper functions for option handling. -*- Autoconf -*-
1418
1419# Copyright (C) 2001-2014 Free Software Foundation, Inc.
1420#
1421# This file is free software; the Free Software Foundation
1422# gives unlimited permission to copy and/or distribute it,
1423# with or without modifications, as long as this notice is preserved.
1424
1425# _AM_MANGLE_OPTION(NAME)
1426# -----------------------
1427AC_DEFUN([_AM_MANGLE_OPTION],
1428[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
1429
1430# _AM_SET_OPTION(NAME)
1431# --------------------
1432# Set option NAME. Presently that only means defining a flag for this option.
1433AC_DEFUN([_AM_SET_OPTION],
1434[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
1435
1436# _AM_SET_OPTIONS(OPTIONS)
1437# ------------------------
1438# OPTIONS is a space-separated list of Automake options.
1439AC_DEFUN([_AM_SET_OPTIONS],
1440[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
1441
1442# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
1443# -------------------------------------------
1444# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
1445AC_DEFUN([_AM_IF_OPTION],
1446[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
1447
1448# Copyright (C) 1999-2014 Free Software Foundation, Inc.
1449#
1450# This file is free software; the Free Software Foundation
1451# gives unlimited permission to copy and/or distribute it,
1452# with or without modifications, as long as this notice is preserved.
1453
1454# _AM_PROG_CC_C_O
1455# ---------------
1456# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC
1457# to automatically call this.
1458AC_DEFUN([_AM_PROG_CC_C_O],
1459[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
1460AC_REQUIRE_AUX_FILE([compile])dnl
1461AC_LANG_PUSH([C])dnl
1462AC_CACHE_CHECK(
1463 [whether $CC understands -c and -o together],
1464 [am_cv_prog_cc_c_o],
1465 [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
1466 # Make sure it works both with $CC and with simple cc.
1467 # Following AC_PROG_CC_C_O, we do the test twice because some
1468 # compilers refuse to overwrite an existing .o file with -o,
1469 # though they will create one.
1470 am_cv_prog_cc_c_o=yes
1471 for am_i in 1 2; do
1472 if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
1473 && test -f conftest2.$ac_objext; then
1474 : OK
1475 else
1476 am_cv_prog_cc_c_o=no
1477 break
1478 fi
1479 done
1480 rm -f core conftest*
1481 unset am_i])
1482if test "$am_cv_prog_cc_c_o" != yes; then
1483 # Losing compiler, so override with the script.
1484 # FIXME: It is wrong to rewrite CC.
1485 # But if we don't then we get into trouble of one sort or another.
1486 # A longer-term fix would be to have automake use am__CC in this case,
1487 # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
1488 CC="$am_aux_dir/compile $CC"
1489fi
1490AC_LANG_POP([C])])
1491
1492# For backward compatibility.
1493AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
1494
1495# Copyright (C) 2001-2014 Free Software Foundation, Inc.
1496#
1497# This file is free software; the Free Software Foundation
1498# gives unlimited permission to copy and/or distribute it,
1499# with or without modifications, as long as this notice is preserved.
1500
1501# AM_RUN_LOG(COMMAND)
1502# -------------------
1503# Run COMMAND, save the exit status in ac_status, and log it.
1504# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
1505AC_DEFUN([AM_RUN_LOG],
1506[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
1507 ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
1508 ac_status=$?
1509 echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
1510 (exit $ac_status); }])
1511
1512# Check to make sure that the build environment is sane. -*- Autoconf -*-
1513
1514# Copyright (C) 1996-2014 Free Software Foundation, Inc.
1515#
1516# This file is free software; the Free Software Foundation
1517# gives unlimited permission to copy and/or distribute it,
1518# with or without modifications, as long as this notice is preserved.
1519
1520# AM_SANITY_CHECK
1521# ---------------
1522AC_DEFUN([AM_SANITY_CHECK],
1523[AC_MSG_CHECKING([whether build environment is sane])
1524# Reject unsafe characters in $srcdir or the absolute working directory
1525# name. Accept space and tab only in the latter.
1526am_lf='
1527'
1528case `pwd` in
1529 *[[\\\"\#\$\&\'\`$am_lf]]*)
1530 AC_MSG_ERROR([unsafe absolute working directory name]);;
1531esac
1532case $srcdir in
1533 *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
1534 AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
1535esac
1536
1537# Do 'set' in a subshell so we don't clobber the current shell's
1538# arguments. Must try -L first in case configure is actually a
1539# symlink; some systems play weird games with the mod time of symlinks
1540# (eg FreeBSD returns the mod time of the symlink's containing
1541# directory).
1542if (
1543 am_has_slept=no
1544 for am_try in 1 2; do
1545 echo "timestamp, slept: $am_has_slept" > conftest.file
1546 set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
1547 if test "$[*]" = "X"; then
1548 # -L didn't work.
1549 set X `ls -t "$srcdir/configure" conftest.file`
1550 fi
1551 if test "$[*]" != "X $srcdir/configure conftest.file" \
1552 && test "$[*]" != "X conftest.file $srcdir/configure"; then
1553
1554 # If neither matched, then we have a broken ls. This can happen
1555 # if, for instance, CONFIG_SHELL is bash and it inherits a
1556 # broken ls alias from the environment. This has actually
1557 # happened. Such a system could not be considered "sane".
1558 AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
1559 alias in your environment])
1560 fi
1561 if test "$[2]" = conftest.file || test $am_try -eq 2; then
1562 break
1563 fi
1564 # Just in case.
1565 sleep 1
1566 am_has_slept=yes
1567 done
1568 test "$[2]" = conftest.file
1569 )
1570then
1571 # Ok.
1572 :
1573else
1574 AC_MSG_ERROR([newly created file is older than distributed files!
1575Check your system clock])
1576fi
1577AC_MSG_RESULT([yes])
1578# If we didn't sleep, we still need to ensure time stamps of config.status and
1579# generated files are strictly newer.
1580am_sleep_pid=
1581if grep 'slept: no' conftest.file >/dev/null 2>&1; then
1582 ( sleep 1 ) &
1583 am_sleep_pid=$!
1584fi
1585AC_CONFIG_COMMANDS_PRE(
1586 [AC_MSG_CHECKING([that generated files are newer than configure])
1587 if test -n "$am_sleep_pid"; then
1588 # Hide warnings about reused PIDs.
1589 wait $am_sleep_pid 2>/dev/null
1590 fi
1591 AC_MSG_RESULT([done])])
1592rm -f conftest.file
1593])
1594
1595# Copyright (C) 2009-2014 Free Software Foundation, Inc.
1596#
1597# This file is free software; the Free Software Foundation
1598# gives unlimited permission to copy and/or distribute it,
1599# with or without modifications, as long as this notice is preserved.
1600
1601# AM_SILENT_RULES([DEFAULT])
1602# --------------------------
1603# Enable less verbose build rules; with the default set to DEFAULT
1604# ("yes" being less verbose, "no" or empty being verbose).
1605AC_DEFUN([AM_SILENT_RULES],
1606[AC_ARG_ENABLE([silent-rules], [dnl
1607AS_HELP_STRING(
1608 [--enable-silent-rules],
1609 [less verbose build output (undo: "make V=1")])
1610AS_HELP_STRING(
1611 [--disable-silent-rules],
1612 [verbose build output (undo: "make V=0")])dnl
1613])
1614case $enable_silent_rules in @%:@ (((
1615 yes) AM_DEFAULT_VERBOSITY=0;;
1616 no) AM_DEFAULT_VERBOSITY=1;;
1617 *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
1618esac
1619dnl
1620dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
1621dnl do not support nested variable expansions.
1622dnl See automake bug#9928 and bug#10237.
1623am_make=${MAKE-make}
1624AC_CACHE_CHECK([whether $am_make supports nested variables],
1625 [am_cv_make_support_nested_variables],
1626 [if AS_ECHO([['TRUE=$(BAR$(V))
1627BAR0=false
1628BAR1=true
1629V=1
1630am__doit:
1631 @$(TRUE)
1632.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
1633 am_cv_make_support_nested_variables=yes
1634else
1635 am_cv_make_support_nested_variables=no
1636fi])
1637if test $am_cv_make_support_nested_variables = yes; then
1638 dnl Using '$V' instead of '$(V)' breaks IRIX make.
1639 AM_V='$(V)'
1640 AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
1641else
1642 AM_V=$AM_DEFAULT_VERBOSITY
1643 AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
1644fi
1645AC_SUBST([AM_V])dnl
1646AM_SUBST_NOTMAKE([AM_V])dnl
1647AC_SUBST([AM_DEFAULT_V])dnl
1648AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
1649AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
1650AM_BACKSLASH='\'
1651AC_SUBST([AM_BACKSLASH])dnl
1652_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
1653])
1654
1655# Copyright (C) 2001-2014 Free Software Foundation, Inc.
1656#
1657# This file is free software; the Free Software Foundation
1658# gives unlimited permission to copy and/or distribute it,
1659# with or without modifications, as long as this notice is preserved.
1660
1661# AM_PROG_INSTALL_STRIP
1662# ---------------------
1663# One issue with vendor 'install' (even GNU) is that you can't
1664# specify the program used to strip binaries. This is especially
1665# annoying in cross-compiling environments, where the build's strip
1666# is unlikely to handle the host's binaries.
1667# Fortunately install-sh will honor a STRIPPROG variable, so we
1668# always use install-sh in "make install-strip", and initialize
1669# STRIPPROG with the value of the STRIP variable (set by the user).
1670AC_DEFUN([AM_PROG_INSTALL_STRIP],
1671[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
1672# Installed binaries are usually stripped using 'strip' when the user
1673# run "make install-strip". However 'strip' might not be the right
1674# tool to use in cross-compilation environments, therefore Automake
1675# will honor the 'STRIP' environment variable to overrule this program.
1676dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
1677if test "$cross_compiling" != no; then
1678 AC_CHECK_TOOL([STRIP], [strip], :)
1679fi
1680INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
1681AC_SUBST([INSTALL_STRIP_PROGRAM])])
1682
1683# Copyright (C) 2006-2014 Free Software Foundation, Inc.
1684#
1685# This file is free software; the Free Software Foundation
1686# gives unlimited permission to copy and/or distribute it,
1687# with or without modifications, as long as this notice is preserved.
1688
1689# _AM_SUBST_NOTMAKE(VARIABLE)
1690# ---------------------------
1691# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
1692# This macro is traced by Automake.
1693AC_DEFUN([_AM_SUBST_NOTMAKE])
1694
1695# AM_SUBST_NOTMAKE(VARIABLE)
1696# --------------------------
1697# Public sister of _AM_SUBST_NOTMAKE.
1698AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
1699
1700# Check how to create a tarball. -*- Autoconf -*-
1701
1702# Copyright (C) 2004-2014 Free Software Foundation, Inc.
1703#
1704# This file is free software; the Free Software Foundation
1705# gives unlimited permission to copy and/or distribute it,
1706# with or without modifications, as long as this notice is preserved.
1707
1708# _AM_PROG_TAR(FORMAT)
1709# --------------------
1710# Check how to create a tarball in format FORMAT.
1711# FORMAT should be one of 'v7', 'ustar', or 'pax'.
1712#
1713# Substitute a variable $(am__tar) that is a command
1714# writing to stdout a FORMAT-tarball containing the directory
1715# $tardir.
1716# tardir=directory && $(am__tar) > result.tar
1717#
1718# Substitute a variable $(am__untar) that extract such
1719# a tarball read from stdin.
1720# $(am__untar) < result.tar
1721#
1722AC_DEFUN([_AM_PROG_TAR],
1723[# Always define AMTAR for backward compatibility. Yes, it's still used
1724# in the wild :-( We should find a proper way to deprecate it ...
1725AC_SUBST([AMTAR], ['$${TAR-tar}'])
1726
1727# We'll loop over all known methods to create a tar archive until one works.
1728_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
1729
1730m4_if([$1], [v7],
1731 [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
1732
1733 [m4_case([$1],
1734 [ustar],
1735 [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
1736 # There is notably a 21 bits limit for the UID and the GID. In fact,
1737 # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
1738 # and bug#13588).
1739 am_max_uid=2097151 # 2^21 - 1
1740 am_max_gid=$am_max_uid
1741 # The $UID and $GID variables are not portable, so we need to resort
1742 # to the POSIX-mandated id(1) utility. Errors in the 'id' calls
1743 # below are definitely unexpected, so allow the users to see them
1744 # (that is, avoid stderr redirection).
1745 am_uid=`id -u || echo unknown`
1746 am_gid=`id -g || echo unknown`
1747 AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
1748 if test $am_uid -le $am_max_uid; then
1749 AC_MSG_RESULT([yes])
1750 else
1751 AC_MSG_RESULT([no])
1752 _am_tools=none
1753 fi
1754 AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
1755 if test $am_gid -le $am_max_gid; then
1756 AC_MSG_RESULT([yes])
1757 else
1758 AC_MSG_RESULT([no])
1759 _am_tools=none
1760 fi],
1761
1762 [pax],
1763 [],
1764
1765 [m4_fatal([Unknown tar format])])
1766
1767 AC_MSG_CHECKING([how to create a $1 tar archive])
1768
1769 # Go ahead even if we have the value already cached. We do so because we
1770 # need to set the values for the 'am__tar' and 'am__untar' variables.
1771 _am_tools=${am_cv_prog_tar_$1-$_am_tools}
1772
1773 for _am_tool in $_am_tools; do
1774 case $_am_tool in
1775 gnutar)
1776 for _am_tar in tar gnutar gtar; do
1777 AM_RUN_LOG([$_am_tar --version]) && break
1778 done
1779 am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
1780 am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
1781 am__untar="$_am_tar -xf -"
1782 ;;
1783 plaintar)
1784 # Must skip GNU tar: if it does not support --format= it doesn't create
1785 # ustar tarball either.
1786 (tar --version) >/dev/null 2>&1 && continue
1787 am__tar='tar chf - "$$tardir"'
1788 am__tar_='tar chf - "$tardir"'
1789 am__untar='tar xf -'
1790 ;;
1791 pax)
1792 am__tar='pax -L -x $1 -w "$$tardir"'
1793 am__tar_='pax -L -x $1 -w "$tardir"'
1794 am__untar='pax -r'
1795 ;;
1796 cpio)
1797 am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
1798 am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
1799 am__untar='cpio -i -H $1 -d'
1800 ;;
1801 none)
1802 am__tar=false
1803 am__tar_=false
1804 am__untar=false
1805 ;;
1806 esac
1807
1808 # If the value was cached, stop now. We just wanted to have am__tar
1809 # and am__untar set.
1810 test -n "${am_cv_prog_tar_$1}" && break
1811
1812 # tar/untar a dummy directory, and stop if the command works.
1813 rm -rf conftest.dir
1814 mkdir conftest.dir
1815 echo GrepMe > conftest.dir/file
1816 AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
1817 rm -rf conftest.dir
1818 if test -s conftest.tar; then
1819 AM_RUN_LOG([$am__untar <conftest.tar])
1820 AM_RUN_LOG([cat conftest.dir/file])
1821 grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
1822 fi
1823 done
1824 rm -rf conftest.dir
1825
1826 AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
1827 AC_MSG_RESULT([$am_cv_prog_tar_$1])])
1828
1829AC_SUBST([am__tar])
1830AC_SUBST([am__untar])
1831]) # _AM_PROG_TAR
1832
diff --git a/apps/plugins/puzzles/src/benchmark.pl b/apps/plugins/puzzles/src/benchmark.pl
new file mode 100755
index 0000000000..98763859e8
--- /dev/null
+++ b/apps/plugins/puzzles/src/benchmark.pl
@@ -0,0 +1,197 @@
1#!/usr/bin/perl
2
3# Process the raw output from benchmark.sh into Javascript-ified HTML.
4
5use strict;
6use warnings;
7
8my @presets = ();
9my %presets = ();
10my $maxval = 0;
11
12while (<>) {
13 chomp;
14 if (/^(.*)(#.*): ([\d\.]+)$/) {
15 push @presets, $1 unless defined $presets{$1};
16 push @{$presets{$1}}, $3;
17 $maxval = $3 if $maxval < $3;
18 }
19}
20
21print <<EOF;
22<!DOCTYPE html>
23<html>
24<head>
25<meta http-equiv="Content-Type" content="text/html; charset=ASCII" />
26<title>Puzzle generation-time benchmarks</title>
27<script type="text/javascript">
28//<![CDATA[
29function choose_scale_ticks(scale) {
30 var nscale = 1, j = 0, factors = [2,2.5,2];
31 while (scale / nscale > 20) {
32 nscale *= factors[j];
33 j = (j+1) % factors.length;
34 }
35 return nscale;
36}
37function initPlots() {
38 var canvases = document.getElementsByTagName('canvas');
39 for (var i = 0; i < canvases.length; i++) {
40 var canvas = canvases[i];
41 var scale = eval(canvas.getAttribute("data-scale"));
42 var add = 20.5, mult = (canvas.width - 2*add) / scale;
43 var data = eval(canvas.getAttribute("data-points"));
44 var ctx = canvas.getContext('2d');
45 ctx.lineWidth = '1px';
46 ctx.lineCap = 'round';
47 ctx.lineJoin = 'round';
48 ctx.strokeStyle = ctx.fillStyle = '#000000';
49 if (data === "scale") {
50 // Draw scale.
51 ctx.font = "16px sans-serif";
52 ctx.textAlign = "center";
53 ctx.textBaseline = "alphabetic";
54 var nscale = choose_scale_ticks(scale);
55 for (var x = 0; x <= scale; x += nscale) {
56 ctx.beginPath();
57 ctx.moveTo(add+mult*x, canvas.height);
58 ctx.lineTo(add+mult*x, canvas.height - 3);
59 ctx.stroke();
60 ctx.fillText(x + "s", add+mult*x, canvas.height - 6);
61 }
62 } else {
63 // Draw a box plot.
64 function quantile(x) {
65 var n = (data.length * x) | 0;
66 return (data[n-1] + data[n]) / 2;
67 }
68
69 var q1 = quantile(0.25), q2 = quantile(0.5), q3 = quantile(0.75);
70 var iqr = q3 - q1;
71 var top = 0.5, bot = canvas.height - 1.5, mid = (top+bot)/2;
72 var wlo = null, whi = null; // whisker ends
73
74 ctx.strokeStyle = '#bbbbbb';
75 var nscale = choose_scale_ticks(scale);
76 for (var x = 0; x <= scale; x += nscale) {
77 ctx.beginPath();
78 ctx.moveTo(add+mult*x, 0);
79 ctx.lineTo(add+mult*x, canvas.height);
80 ctx.stroke();
81 }
82 ctx.strokeStyle = '#000000';
83
84 for (var j in data) {
85 var x = data[j];
86 if (x >= q1 - 1.5 * iqr && x <= q3 + 1.5 * iqr) {
87 if (wlo === null || wlo > x)
88 wlo = x;
89 if (whi === null || whi < x)
90 whi = x;
91 } else {
92 ctx.beginPath();
93 ctx.arc(add+mult*x, mid, 2, 0, 2*Math.PI);
94 ctx.stroke();
95 if (x >= q1 - 3 * iqr && x <= q3 + 3 * iqr)
96 ctx.fill();
97 }
98 }
99
100 ctx.beginPath();
101
102 // Box
103 ctx.moveTo(add+mult*q1, top);
104 ctx.lineTo(add+mult*q3, top);
105 ctx.lineTo(add+mult*q3, bot);
106 ctx.lineTo(add+mult*q1, bot);
107 ctx.closePath();
108
109 // Line at median
110 ctx.moveTo(add+mult*q2, top);
111 ctx.lineTo(add+mult*q2, bot);
112
113 // Lower whisker
114 ctx.moveTo(add+mult*q1, mid);
115 ctx.lineTo(add+mult*wlo, mid);
116 ctx.moveTo(add+mult*wlo, top);
117 ctx.lineTo(add+mult*wlo, bot);
118
119 // Upper whisker
120 ctx.moveTo(add+mult*q3, mid);
121 ctx.lineTo(add+mult*whi, mid);
122 ctx.moveTo(add+mult*whi, top);
123 ctx.lineTo(add+mult*whi, bot);
124
125 ctx.stroke();
126 }
127 }
128 document.getElementById('sort_orig').onclick = function() {
129 sort(function(e) {
130 return parseFloat(e.getAttribute("data-index"));
131 });
132 };
133 document.getElementById('sort_median').onclick = function() {
134 sort(function(e) {
135 return -parseFloat(e.getAttribute("data-median"));
136 });
137 };
138 document.getElementById('sort_mean').onclick = function() {
139 sort(function(e) {
140 return -parseFloat(e.getAttribute("data-mean"));
141 });
142 };
143}
144function sort(keyfn) {
145 var rows = document.getElementsByTagName("tr");
146 var trs = [];
147 for (var i = 0; i < rows.length; i++)
148 trs.push(rows[i]);
149 trs.sort(function(a,b) {
150 var akey = keyfn(a);
151 var bkey = keyfn(b);
152 return akey < bkey ? -1 : akey > bkey ? +1 : 0;
153 });
154 var parent = trs[0].parentElement;
155 for (var i = 0; i < trs.length; i++)
156 parent.removeChild(trs[i]);
157 for (var i = 0; i < trs.length; i++)
158 parent.appendChild(trs[i]);
159}
160//]]>
161</script>
162</head>
163<body onLoad="initPlots();">
164<h1 align=center>Puzzle generation-time benchmarks</h1>
165<p>Sort order:
166<button id="sort_orig">Original</button>
167<button id="sort_median">Median</button>
168<button id="sort_mean">Mean</button>
169<table>
170<tr><th>Preset</th><td><canvas width=700 height=30 data-points='"scale"' data-scale="$maxval"></td></tr>
171EOF
172
173my $index = 0;
174for my $preset (@presets) {
175 my @data = sort { $a <=> $b } @{$presets{$preset}};
176 my $median = ($#data % 2 ?
177 ($data[($#data-1)/2]+$data[($#data+1)/2])/2 :
178 $data[$#data/2]);
179 my $mean = 0; map { $mean += $_ } @data; $mean /= @data;
180 print "<tr data-index=\"$index\" data-mean=\"$mean\" data-median=\"$median\"><td>", &escape($preset), "</td><td><canvas width=700 height=15 data-points=\"[";
181 print join ",", @data;
182 print "]\" data-scale=\"$maxval\"></td></tr>\n";
183 $index++;
184}
185
186print <<EOF;
187</body>
188</html>
189EOF
190
191sub escape {
192 my ($text) = @_;
193 $text =~ s/&/&amp;/g;
194 $text =~ s/</&lt;/g;
195 $text =~ s/>/&gt;/g;
196 return $text;
197}
diff --git a/apps/plugins/puzzles/src/benchmark.sh b/apps/plugins/puzzles/src/benchmark.sh
new file mode 100755
index 0000000000..b3af27765e
--- /dev/null
+++ b/apps/plugins/puzzles/src/benchmark.sh
@@ -0,0 +1,27 @@
1#!/bin/sh
2
3# Run every puzzle in benchmarking mode, and generate a file of raw
4# data that benchmark.pl will format into a web page.
5
6# If any arguments are provided, use those as the list of games to
7# benchmark. Otherwise, read the full list from gamedesc.txt.
8if test $# = 0; then
9 set -- $(cut -f1 -d: < gamedesc.txt)
10fi
11
12failures=false
13
14for game in "$@"; do
15 # Use 'env -i' to suppress any environment variables that might
16 # change the preset list for a puzzle (e.g. user-defined extras)
17 presets=$(env -i ./$game --list-presets | cut -f1 -d' ')
18 for preset in $presets; do
19 if ! env -i ./$game --test-solve --time-generation \
20 --generate 100 $preset;
21 then
22 echo "${game} ${preset} failed to generate" >&2
23 fi
24 done
25done
26
27if $failures; then exit 1; fi
diff --git a/apps/plugins/puzzles/src/blackbox.R b/apps/plugins/puzzles/src/blackbox.R
new file mode 100644
index 0000000000..116225206d
--- /dev/null
+++ b/apps/plugins/puzzles/src/blackbox.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3blackbox : [X] GTK COMMON blackbox blackbox-icon|no-icon
4
5blackbox : [G] WINDOWS COMMON blackbox blackbox.res|noicon.res
6
7ALL += blackbox[COMBINED]
8
9!begin am gtk
10GAMES += blackbox
11!end
12
13!begin >list.c
14 A(blackbox) \
15!end
16
17!begin >gamedesc.txt
18blackbox:blackbox.exe:Black Box:Ball-finding puzzle:Find the hidden balls in the box by bouncing laser beams off them.
19!end
diff --git a/apps/plugins/puzzles/src/blackbox.c b/apps/plugins/puzzles/src/blackbox.c
new file mode 100644
index 0000000000..b334cf7117
--- /dev/null
+++ b/apps/plugins/puzzles/src/blackbox.c
@@ -0,0 +1,1543 @@
1/*
2 * blackbox.c: implementation of 'Black Box'.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13
14#define PREFERRED_TILE_SIZE 32
15#define FLASH_FRAME 0.2F
16
17/* Terminology, for ease of reading various macros scattered about the place.
18 *
19 * The 'arena' is the inner area where the balls are placed. This is
20 * indexed from (0,0) to (w-1,h-1) but its offset in the grid is (1,1).
21 *
22 * The 'range' (firing range) is the bit around the edge where
23 * the lasers are fired from. This is indexed from 0 --> (2*(w+h) - 1),
24 * starting at the top left ((1,0) on the grid) and moving clockwise.
25 *
26 * The 'grid' is just the big array containing arena and range;
27 * locations (0,0), (0,w+1), (h+1,w+1) and (h+1,0) are unused.
28 */
29
30enum {
31 COL_BACKGROUND, COL_COVER, COL_LOCK,
32 COL_TEXT, COL_FLASHTEXT,
33 COL_HIGHLIGHT, COL_LOWLIGHT, COL_GRID,
34 COL_BALL, COL_WRONG, COL_BUTTON,
35 COL_CURSOR,
36 NCOLOURS
37};
38
39struct game_params {
40 int w, h;
41 int minballs, maxballs;
42};
43
44static game_params *default_params(void)
45{
46 game_params *ret = snew(game_params);
47
48 ret->w = ret->h = 8;
49 ret->minballs = ret->maxballs = 5;
50
51 return ret;
52}
53
54static const game_params blackbox_presets[] = {
55 { 5, 5, 3, 3 },
56 { 8, 8, 5, 5 },
57 { 8, 8, 3, 6 },
58 { 10, 10, 5, 5 },
59 { 10, 10, 4, 10 }
60};
61
62static int game_fetch_preset(int i, char **name, game_params **params)
63{
64 char str[80];
65 game_params *ret;
66
67 if (i < 0 || i >= lenof(blackbox_presets))
68 return FALSE;
69
70 ret = snew(game_params);
71 *ret = blackbox_presets[i];
72
73 if (ret->minballs == ret->maxballs)
74 sprintf(str, "%dx%d, %d balls",
75 ret->w, ret->h, ret->minballs);
76 else
77 sprintf(str, "%dx%d, %d-%d balls",
78 ret->w, ret->h, ret->minballs, ret->maxballs);
79
80 *name = dupstr(str);
81 *params = ret;
82 return TRUE;
83}
84
85static void free_params(game_params *params)
86{
87 sfree(params);
88}
89
90static game_params *dup_params(const game_params *params)
91{
92 game_params *ret = snew(game_params);
93 *ret = *params; /* structure copy */
94 return ret;
95}
96
97static void decode_params(game_params *params, char const *string)
98{
99 char const *p = string;
100 game_params *defs = default_params();
101
102 *params = *defs; free_params(defs);
103
104 while (*p) {
105 switch (*p++) {
106 case 'w':
107 params->w = atoi(p);
108 while (*p && isdigit((unsigned char)*p)) p++;
109 break;
110
111 case 'h':
112 params->h = atoi(p);
113 while (*p && isdigit((unsigned char)*p)) p++;
114 break;
115
116 case 'm':
117 params->minballs = atoi(p);
118 while (*p && isdigit((unsigned char)*p)) p++;
119 break;
120
121 case 'M':
122 params->maxballs = atoi(p);
123 while (*p && isdigit((unsigned char)*p)) p++;
124 break;
125
126 default:
127 ;
128 }
129 }
130}
131
132static char *encode_params(const game_params *params, int full)
133{
134 char str[256];
135
136 sprintf(str, "w%dh%dm%dM%d",
137 params->w, params->h, params->minballs, params->maxballs);
138 return dupstr(str);
139}
140
141static config_item *game_configure(const game_params *params)
142{
143 config_item *ret;
144 char buf[80];
145
146 ret = snewn(4, config_item);
147
148 ret[0].name = "Width";
149 ret[0].type = C_STRING;
150 sprintf(buf, "%d", params->w);
151 ret[0].sval = dupstr(buf);
152 ret[0].ival = 0;
153
154 ret[1].name = "Height";
155 ret[1].type = C_STRING;
156 sprintf(buf, "%d", params->h);
157 ret[1].sval = dupstr(buf);
158 ret[1].ival = 0;
159
160 ret[2].name = "No. of balls";
161 ret[2].type = C_STRING;
162 if (params->minballs == params->maxballs)
163 sprintf(buf, "%d", params->minballs);
164 else
165 sprintf(buf, "%d-%d", params->minballs, params->maxballs);
166 ret[2].sval = dupstr(buf);
167 ret[2].ival = 0;
168
169 ret[3].name = NULL;
170 ret[3].type = C_END;
171 ret[3].sval = NULL;
172 ret[3].ival = 0;
173
174 return ret;
175}
176
177static game_params *custom_params(const config_item *cfg)
178{
179 game_params *ret = snew(game_params);
180
181 ret->w = atoi(cfg[0].sval);
182 ret->h = atoi(cfg[1].sval);
183
184 /* Allow 'a-b' for a range, otherwise assume a single number. */
185 if (sscanf(cfg[2].sval, "%d-%d", &ret->minballs, &ret->maxballs) < 2)
186 ret->minballs = ret->maxballs = atoi(cfg[2].sval);
187
188 return ret;
189}
190
191static char *validate_params(const game_params *params, int full)
192{
193 if (params->w < 2 || params->h < 2)
194 return "Width and height must both be at least two";
195 /* next one is just for ease of coding stuff into 'char'
196 * types, and could be worked around if required. */
197 if (params->w > 255 || params->h > 255)
198 return "Widths and heights greater than 255 are not supported";
199 if (params->minballs > params->maxballs)
200 return "Minimum number of balls may not be greater than maximum";
201 if (params->minballs >= params->w * params->h)
202 return "Too many balls to fit in grid";
203 return NULL;
204}
205
206/*
207 * We store: width | height | ball1x | ball1y | [ ball2x | ball2y | [...] ]
208 * all stored as unsigned chars; validate_params has already
209 * checked this won't overflow an 8-bit char.
210 * Then we obfuscate it.
211 */
212
213static char *new_game_desc(const game_params *params, random_state *rs,
214 char **aux, int interactive)
215{
216 int nballs = params->minballs, i;
217 char *grid, *ret;
218 unsigned char *bmp;
219
220 if (params->maxballs > params->minballs)
221 nballs += random_upto(rs, params->maxballs - params->minballs + 1);
222
223 grid = snewn(params->w*params->h, char);
224 memset(grid, 0, params->w * params->h * sizeof(char));
225
226 bmp = snewn(nballs*2 + 2, unsigned char);
227 memset(bmp, 0, (nballs*2 + 2) * sizeof(unsigned char));
228
229 bmp[0] = params->w;
230 bmp[1] = params->h;
231
232 for (i = 0; i < nballs; i++) {
233 int x, y;
234
235 do {
236 x = random_upto(rs, params->w);
237 y = random_upto(rs, params->h);
238 } while (grid[y*params->w + x]);
239
240 grid[y*params->w + x] = 1;
241
242 bmp[(i+1)*2 + 0] = x;
243 bmp[(i+1)*2 + 1] = y;
244 }
245 sfree(grid);
246
247 obfuscate_bitmap(bmp, (nballs*2 + 2) * 8, FALSE);
248 ret = bin2hex(bmp, nballs*2 + 2);
249 sfree(bmp);
250
251 return ret;
252}
253
254static char *validate_desc(const game_params *params, const char *desc)
255{
256 int nballs, dlen = strlen(desc), i;
257 unsigned char *bmp;
258 char *ret;
259
260 /* the bitmap is 2+(nballs*2) long; the hex version is double that. */
261 nballs = ((dlen/2)-2)/2;
262
263 if (dlen < 4 || dlen % 4 ||
264 nballs < params->minballs || nballs > params->maxballs)
265 return "Game description is wrong length";
266
267 bmp = hex2bin(desc, nballs*2 + 2);
268 obfuscate_bitmap(bmp, (nballs*2 + 2) * 8, TRUE);
269 ret = "Game description is corrupted";
270 /* check general grid size */
271 if (bmp[0] != params->w || bmp[1] != params->h)
272 goto done;
273 /* check each ball will fit on that grid */
274 for (i = 0; i < nballs; i++) {
275 int x = bmp[(i+1)*2 + 0], y = bmp[(i+1)*2 + 1];
276 if (x < 0 || y < 0 || x >= params->w || y >= params->h)
277 goto done;
278 }
279 ret = NULL;
280
281done:
282 sfree(bmp);
283 return ret;
284}
285
286#define BALL_CORRECT 0x01
287#define BALL_GUESS 0x02
288#define BALL_LOCK 0x04
289
290#define LASER_FLAGMASK 0x1f800
291#define LASER_OMITTED 0x0800
292#define LASER_REFLECT 0x1000
293#define LASER_HIT 0x2000
294#define LASER_WRONG 0x4000
295#define LASER_FLASHED 0x8000
296#define LASER_EMPTY (~0)
297
298#define FLAG_CURSOR 0x10000 /* needs to be disjoint from both sets */
299
300struct game_state {
301 int w, h, minballs, maxballs, nballs, nlasers;
302 unsigned int *grid; /* (w+2)x(h+2), to allow for laser firing range */
303 unsigned int *exits; /* one per laser */
304 int done; /* user has finished placing his own balls. */
305 int laserno; /* number of next laser to be fired. */
306 int nguesses, reveal, justwrong, nright, nwrong, nmissed;
307};
308
309#define GRID(s,x,y) ((s)->grid[(y)*((s)->w+2) + (x)])
310
311#define RANGECHECK(s,x) ((x) >= 0 && (x) <= (s)->nlasers)
312
313/* specify numbers because they must match array indexes. */
314enum { DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3 };
315
316struct offset { int x, y; };
317
318static const struct offset offsets[] = {
319 { 0, -1 }, /* up */
320 { 1, 0 }, /* right */
321 { 0, 1 }, /* down */
322 { -1, 0 } /* left */
323};
324
325#ifdef DEBUGGING
326static const char *dirstrs[] = {
327 "UP", "RIGHT", "DOWN", "LEFT"
328};
329#endif
330
331static int range2grid(const game_state *state, int rangeno, int *x, int *y,
332 int *direction)
333{
334 if (rangeno < 0)
335 return 0;
336
337 if (rangeno < state->w) {
338 /* top row; from (1,0) to (w,0) */
339 *x = rangeno + 1;
340 *y = 0;
341 *direction = DIR_DOWN;
342 return 1;
343 }
344 rangeno -= state->w;
345 if (rangeno < state->h) {
346 /* RHS; from (w+1, 1) to (w+1, h) */
347 *x = state->w+1;
348 *y = rangeno + 1;
349 *direction = DIR_LEFT;
350 return 1;
351 }
352 rangeno -= state->h;
353 if (rangeno < state->w) {
354 /* bottom row; from (1, h+1) to (w, h+1); counts backwards */
355 *x = (state->w - rangeno);
356 *y = state->h+1;
357 *direction = DIR_UP;
358 return 1;
359 }
360 rangeno -= state->w;
361 if (rangeno < state->h) {
362 /* LHS; from (0, 1) to (0, h); counts backwards */
363 *x = 0;
364 *y = (state->h - rangeno);
365 *direction = DIR_RIGHT;
366 return 1;
367 }
368 return 0;
369}
370
371static int grid2range(const game_state *state, int x, int y, int *rangeno)
372{
373 int ret, x1 = state->w+1, y1 = state->h+1;
374
375 if (x > 0 && x < x1 && y > 0 && y < y1) return 0; /* in arena */
376 if (x < 0 || x > x1 || y < 0 || y > y1) return 0; /* outside grid */
377
378 if ((x == 0 || x == x1) && (y == 0 || y == y1))
379 return 0; /* one of 4 corners */
380
381 if (y == 0) { /* top line */
382 ret = x - 1;
383 } else if (x == x1) { /* RHS */
384 ret = y - 1 + state->w;
385 } else if (y == y1) { /* Bottom [and counts backwards] */
386 ret = (state->w - x) + state->w + state->h;
387 } else { /* LHS [and counts backwards ] */
388 ret = (state->h-y) + state->w + state->w + state->h;
389 }
390 *rangeno = ret;
391 debug(("grid2range: (%d,%d) rangeno = %d\n", x, y, ret));
392 return 1;
393}
394
395static game_state *new_game(midend *me, const game_params *params,
396 const char *desc)
397{
398 game_state *state = snew(game_state);
399 int dlen = strlen(desc), i;
400 unsigned char *bmp;
401
402 state->minballs = params->minballs;
403 state->maxballs = params->maxballs;
404 state->nballs = ((dlen/2)-2)/2;
405
406 bmp = hex2bin(desc, state->nballs*2 + 2);
407 obfuscate_bitmap(bmp, (state->nballs*2 + 2) * 8, TRUE);
408
409 state->w = bmp[0]; state->h = bmp[1];
410 state->nlasers = 2 * (state->w + state->h);
411
412 state->grid = snewn((state->w+2)*(state->h+2), unsigned int);
413 memset(state->grid, 0, (state->w+2)*(state->h+2) * sizeof(unsigned int));
414
415 state->exits = snewn(state->nlasers, unsigned int);
416 memset(state->exits, LASER_EMPTY, state->nlasers * sizeof(unsigned int));
417
418 for (i = 0; i < state->nballs; i++) {
419 GRID(state, bmp[(i+1)*2 + 0]+1, bmp[(i+1)*2 + 1]+1) = BALL_CORRECT;
420 }
421 sfree(bmp);
422
423 state->done = state->nguesses = state->reveal = state->justwrong =
424 state->nright = state->nwrong = state->nmissed = 0;
425 state->laserno = 1;
426
427 return state;
428}
429
430#define XFER(x) ret->x = state->x
431
432static game_state *dup_game(const game_state *state)
433{
434 game_state *ret = snew(game_state);
435
436 XFER(w); XFER(h);
437 XFER(minballs); XFER(maxballs);
438 XFER(nballs); XFER(nlasers);
439
440 ret->grid = snewn((ret->w+2)*(ret->h+2), unsigned int);
441 memcpy(ret->grid, state->grid, (ret->w+2)*(ret->h+2) * sizeof(unsigned int));
442 ret->exits = snewn(ret->nlasers, unsigned int);
443 memcpy(ret->exits, state->exits, ret->nlasers * sizeof(unsigned int));
444
445 XFER(done);
446 XFER(laserno);
447 XFER(nguesses);
448 XFER(reveal);
449 XFER(justwrong);
450 XFER(nright); XFER(nwrong); XFER(nmissed);
451
452 return ret;
453}
454
455#undef XFER
456
457static void free_game(game_state *state)
458{
459 sfree(state->exits);
460 sfree(state->grid);
461 sfree(state);
462}
463
464static char *solve_game(const game_state *state, const game_state *currstate,
465 const char *aux, char **error)
466{
467 return dupstr("S");
468}
469
470static int game_can_format_as_text_now(const game_params *params)
471{
472 return TRUE;
473}
474
475static char *game_text_format(const game_state *state)
476{
477 return NULL;
478}
479
480struct game_ui {
481 int flash_laserno;
482 int errors, newmove;
483 int cur_x, cur_y, cur_visible;
484 int flash_laser; /* 0 = never, 1 = always, 2 = if anim. */
485};
486
487static game_ui *new_ui(const game_state *state)
488{
489 game_ui *ui = snew(game_ui);
490 ui->flash_laserno = LASER_EMPTY;
491 ui->errors = 0;
492 ui->newmove = FALSE;
493
494 ui->cur_x = ui->cur_y = 1;
495 ui->cur_visible = 0;
496
497 ui->flash_laser = 0;
498
499 return ui;
500}
501
502static void free_ui(game_ui *ui)
503{
504 sfree(ui);
505}
506
507static char *encode_ui(const game_ui *ui)
508{
509 char buf[80];
510 /*
511 * The error counter needs preserving across a serialisation.
512 */
513 sprintf(buf, "E%d", ui->errors);
514 return dupstr(buf);
515}
516
517static void decode_ui(game_ui *ui, const char *encoding)
518{
519 sscanf(encoding, "E%d", &ui->errors);
520}
521
522static void game_changed_state(game_ui *ui, const game_state *oldstate,
523 const game_state *newstate)
524{
525 /*
526 * If we've encountered a `justwrong' state as a result of
527 * actually making a move, increment the ui error counter.
528 */
529 if (newstate->justwrong && ui->newmove)
530 ui->errors++;
531 ui->newmove = FALSE;
532}
533
534#define OFFSET(gx,gy,o) do { \
535 int off = (4 + (o) % 4) % 4; \
536 (gx) += offsets[off].x; \
537 (gy) += offsets[off].y; \
538} while(0)
539
540enum { LOOK_LEFT, LOOK_FORWARD, LOOK_RIGHT };
541
542/* Given a position and a direction, check whether we can see a ball in front
543 * of us, or to our front-left or front-right. */
544static int isball(game_state *state, int gx, int gy, int direction, int lookwhere)
545{
546 debug(("isball, (%d, %d), dir %s, lookwhere %s\n", gx, gy, dirstrs[direction],
547 lookwhere == LOOK_LEFT ? "LEFT" :
548 lookwhere == LOOK_FORWARD ? "FORWARD" : "RIGHT"));
549 OFFSET(gx,gy,direction);
550 if (lookwhere == LOOK_LEFT)
551 OFFSET(gx,gy,direction-1);
552 else if (lookwhere == LOOK_RIGHT)
553 OFFSET(gx,gy,direction+1);
554 else if (lookwhere != LOOK_FORWARD)
555 assert(!"unknown lookwhere");
556
557 debug(("isball, new (%d, %d)\n", gx, gy));
558
559 /* if we're off the grid (into the firing range) there's never a ball. */
560 if (gx < 1 || gy < 1 || gx > state->w || gy > state->h)
561 return 0;
562
563 if (GRID(state, gx,gy) & BALL_CORRECT)
564 return 1;
565
566 return 0;
567}
568
569static int fire_laser_internal(game_state *state, int x, int y, int direction)
570{
571 int unused, lno, tmp;
572
573 tmp = grid2range(state, x, y, &lno);
574 assert(tmp);
575
576 /* deal with strange initial reflection rules (that stop
577 * you turning down the laser range) */
578
579 /* I've just chosen to prioritise instant-hit over instant-reflection;
580 * I can't find anywhere that gives me a definite algorithm for this. */
581 if (isball(state, x, y, direction, LOOK_FORWARD)) {
582 debug(("Instant hit at (%d, %d)\n", x, y));
583 return LASER_HIT; /* hit */
584 }
585
586 if (isball(state, x, y, direction, LOOK_LEFT) ||
587 isball(state, x, y, direction, LOOK_RIGHT)) {
588 debug(("Instant reflection at (%d, %d)\n", x, y));
589 return LASER_REFLECT; /* reflection */
590 }
591 /* move us onto the grid. */
592 OFFSET(x, y, direction);
593
594 while (1) {
595 debug(("fire_laser: looping at (%d, %d) pointing %s\n",
596 x, y, dirstrs[direction]));
597 if (grid2range(state, x, y, &unused)) {
598 int exitno;
599
600 tmp = grid2range(state, x, y, &exitno);
601 assert(tmp);
602
603 return (lno == exitno ? LASER_REFLECT : exitno);
604 }
605 /* paranoia. This obviously should never happen */
606 assert(!(GRID(state, x, y) & BALL_CORRECT));
607
608 if (isball(state, x, y, direction, LOOK_FORWARD)) {
609 /* we're facing a ball; send back a reflection. */
610 debug(("Ball ahead of (%d, %d)", x, y));
611 return LASER_HIT; /* hit */
612 }
613
614 if (isball(state, x, y, direction, LOOK_LEFT)) {
615 /* ball to our left; rotate clockwise and look again. */
616 debug(("Ball to left; turning clockwise.\n"));
617 direction += 1; direction %= 4;
618 continue;
619 }
620 if (isball(state, x, y, direction, LOOK_RIGHT)) {
621 /* ball to our right; rotate anti-clockwise and look again. */
622 debug(("Ball to rightl turning anti-clockwise.\n"));
623 direction += 3; direction %= 4;
624 continue;
625 }
626 /* ... otherwise, we have no balls ahead of us so just move one step. */
627 debug(("No balls; moving forwards.\n"));
628 OFFSET(x, y, direction);
629 }
630}
631
632static int laser_exit(game_state *state, int entryno)
633{
634 int tmp, x, y, direction;
635
636 tmp = range2grid(state, entryno, &x, &y, &direction);
637 assert(tmp);
638
639 return fire_laser_internal(state, x, y, direction);
640}
641
642static void fire_laser(game_state *state, int entryno)
643{
644 int tmp, exitno, x, y, direction;
645
646 tmp = range2grid(state, entryno, &x, &y, &direction);
647 assert(tmp);
648
649 exitno = fire_laser_internal(state, x, y, direction);
650
651 if (exitno == LASER_HIT || exitno == LASER_REFLECT) {
652 GRID(state, x, y) = state->exits[entryno] = exitno;
653 } else {
654 int newno = state->laserno++;
655 int xend, yend, unused;
656 tmp = range2grid(state, exitno, &xend, &yend, &unused);
657 assert(tmp);
658 GRID(state, x, y) = GRID(state, xend, yend) = newno;
659 state->exits[entryno] = exitno;
660 state->exits[exitno] = entryno;
661 }
662}
663
664/* Checks that the guessed balls in the state match up with the real balls
665 * for all possible lasers (i.e. not just the ones that the player might
666 * have already guessed). This is required because any layout with >4 balls
667 * might have multiple valid solutions. Returns non-zero for a 'correct'
668 * (i.e. consistent) layout. */
669static int check_guesses(game_state *state, int cagey)
670{
671 game_state *solution, *guesses;
672 int i, x, y, n, unused, tmp;
673 int ret = 0;
674
675 if (cagey) {
676 /*
677 * First, check that each laser the player has already
678 * fired is consistent with the layout. If not, show them
679 * one error they've made and reveal no further
680 * information.
681 *
682 * Failing that, check to see whether the player would have
683 * been able to fire any laser which distinguished the real
684 * solution from their guess. If so, show them one such
685 * laser and reveal no further information.
686 */
687 guesses = dup_game(state);
688 /* clear out BALL_CORRECT on guess, make BALL_GUESS BALL_CORRECT. */
689 for (x = 1; x <= state->w; x++) {
690 for (y = 1; y <= state->h; y++) {
691 GRID(guesses, x, y) &= ~BALL_CORRECT;
692 if (GRID(guesses, x, y) & BALL_GUESS)
693 GRID(guesses, x, y) |= BALL_CORRECT;
694 }
695 }
696 n = 0;
697 for (i = 0; i < guesses->nlasers; i++) {
698 if (guesses->exits[i] != LASER_EMPTY &&
699 guesses->exits[i] != laser_exit(guesses, i))
700 n++;
701 }
702 if (n) {
703 /*
704 * At least one of the player's existing lasers
705 * contradicts their ball placement. Pick a random one,
706 * highlight it, and return.
707 *
708 * A temporary random state is created from the current
709 * grid, so that repeating the same marking will give
710 * the same answer instead of a different one.
711 */
712 random_state *rs = random_new((char *)guesses->grid,
713 (state->w+2)*(state->h+2) *
714 sizeof(unsigned int));
715 n = random_upto(rs, n);
716 random_free(rs);
717 for (i = 0; i < guesses->nlasers; i++) {
718 if (guesses->exits[i] != LASER_EMPTY &&
719 guesses->exits[i] != laser_exit(guesses, i) &&
720 n-- == 0) {
721 state->exits[i] |= LASER_WRONG;
722 tmp = laser_exit(state, i);
723 if (RANGECHECK(state, tmp))
724 state->exits[tmp] |= LASER_WRONG;
725 state->justwrong = TRUE;
726 free_game(guesses);
727 return 0;
728 }
729 }
730 }
731 n = 0;
732 for (i = 0; i < guesses->nlasers; i++) {
733 if (guesses->exits[i] == LASER_EMPTY &&
734 laser_exit(state, i) != laser_exit(guesses, i))
735 n++;
736 }
737 if (n) {
738 /*
739 * At least one of the player's unfired lasers would
740 * demonstrate their ball placement to be wrong. Pick a
741 * random one, highlight it, and return.
742 *
743 * A temporary random state is created from the current
744 * grid, so that repeating the same marking will give
745 * the same answer instead of a different one.
746 */
747 random_state *rs = random_new((char *)guesses->grid,
748 (state->w+2)*(state->h+2) *
749 sizeof(unsigned int));
750 n = random_upto(rs, n);
751 random_free(rs);
752 for (i = 0; i < guesses->nlasers; i++) {
753 if (guesses->exits[i] == LASER_EMPTY &&
754 laser_exit(state, i) != laser_exit(guesses, i) &&
755 n-- == 0) {
756 fire_laser(state, i);
757 state->exits[i] |= LASER_OMITTED;
758 tmp = laser_exit(state, i);
759 if (RANGECHECK(state, tmp))
760 state->exits[tmp] |= LASER_OMITTED;
761 state->justwrong = TRUE;
762 free_game(guesses);
763 return 0;
764 }
765 }
766 }
767 free_game(guesses);
768 }
769
770 /* duplicate the state (to solution) */
771 solution = dup_game(state);
772
773 /* clear out the lasers of solution */
774 for (i = 0; i < solution->nlasers; i++) {
775 tmp = range2grid(solution, i, &x, &y, &unused);
776 assert(tmp);
777 GRID(solution, x, y) = 0;
778 solution->exits[i] = LASER_EMPTY;
779 }
780
781 /* duplicate solution to guess. */
782 guesses = dup_game(solution);
783
784 /* clear out BALL_CORRECT on guess, make BALL_GUESS BALL_CORRECT. */
785 for (x = 1; x <= state->w; x++) {
786 for (y = 1; y <= state->h; y++) {
787 GRID(guesses, x, y) &= ~BALL_CORRECT;
788 if (GRID(guesses, x, y) & BALL_GUESS)
789 GRID(guesses, x, y) |= BALL_CORRECT;
790 }
791 }
792
793 /* for each laser (on both game_states), fire it if it hasn't been fired.
794 * If one has been fired (or received a hit) and another hasn't, we know
795 * the ball layouts didn't match and can short-circuit return. */
796 for (i = 0; i < solution->nlasers; i++) {
797 if (solution->exits[i] == LASER_EMPTY)
798 fire_laser(solution, i);
799 if (guesses->exits[i] == LASER_EMPTY)
800 fire_laser(guesses, i);
801 }
802
803 /* check each game_state's laser against the other; if any differ, return 0 */
804 ret = 1;
805 for (i = 0; i < solution->nlasers; i++) {
806 tmp = range2grid(solution, i, &x, &y, &unused);
807 assert(tmp);
808
809 if (solution->exits[i] != guesses->exits[i]) {
810 /* If the original state didn't have this shot fired,
811 * and it would be wrong between the guess and the solution,
812 * add it. */
813 if (state->exits[i] == LASER_EMPTY) {
814 state->exits[i] = solution->exits[i];
815 if (state->exits[i] == LASER_REFLECT ||
816 state->exits[i] == LASER_HIT)
817 GRID(state, x, y) = state->exits[i];
818 else {
819 /* add a new shot, incrementing state's laser count. */
820 int ex, ey, newno = state->laserno++;
821 tmp = range2grid(state, state->exits[i], &ex, &ey, &unused);
822 assert(tmp);
823 GRID(state, x, y) = newno;
824 GRID(state, ex, ey) = newno;
825 }
826 state->exits[i] |= LASER_OMITTED;
827 } else {
828 state->exits[i] |= LASER_WRONG;
829 }
830 ret = 0;
831 }
832 }
833 if (ret == 0 ||
834 state->nguesses < state->minballs ||
835 state->nguesses > state->maxballs) goto done;
836
837 /* fix up original state so the 'correct' balls end up matching the guesses,
838 * as we've just proved that they were equivalent. */
839 for (x = 1; x <= state->w; x++) {
840 for (y = 1; y <= state->h; y++) {
841 if (GRID(state, x, y) & BALL_GUESS)
842 GRID(state, x, y) |= BALL_CORRECT;
843 else
844 GRID(state, x, y) &= ~BALL_CORRECT;
845 }
846 }
847
848done:
849 /* fill in nright and nwrong. */
850 state->nright = state->nwrong = state->nmissed = 0;
851 for (x = 1; x <= state->w; x++) {
852 for (y = 1; y <= state->h; y++) {
853 int bs = GRID(state, x, y) & (BALL_GUESS | BALL_CORRECT);
854 if (bs == (BALL_GUESS | BALL_CORRECT))
855 state->nright++;
856 else if (bs == BALL_GUESS)
857 state->nwrong++;
858 else if (bs == BALL_CORRECT)
859 state->nmissed++;
860 }
861 }
862 free_game(solution);
863 free_game(guesses);
864 state->reveal = 1;
865 return ret;
866}
867
868#define TILE_SIZE (ds->tilesize)
869
870#define TODRAW(x) ((TILE_SIZE * (x)) + (TILE_SIZE / 2))
871#define FROMDRAW(x) (((x) - (TILE_SIZE / 2)) / TILE_SIZE)
872
873#define CAN_REVEAL(state) ((state)->nguesses >= (state)->minballs && \
874 (state)->nguesses <= (state)->maxballs && \
875 !(state)->reveal && !(state)->justwrong)
876
877struct game_drawstate {
878 int tilesize, crad, rrad, w, h; /* w and h to make macros work... */
879 unsigned int *grid; /* as the game_state grid */
880 int started, reveal;
881 int flash_laserno, isflash;
882};
883
884static char *interpret_move(const game_state *state, game_ui *ui,
885 const game_drawstate *ds,
886 int x, int y, int button)
887{
888 int gx = -1, gy = -1, rangeno = -1, wouldflash = 0;
889 enum { NONE, TOGGLE_BALL, TOGGLE_LOCK, FIRE, REVEAL,
890 TOGGLE_COLUMN_LOCK, TOGGLE_ROW_LOCK} action = NONE;
891 char buf[80], *nullret = NULL;
892
893 if (IS_CURSOR_MOVE(button)) {
894 int cx = ui->cur_x, cy = ui->cur_y;
895
896 move_cursor(button, &cx, &cy, state->w+2, state->h+2, 0);
897 if ((cx == 0 && cy == 0 && !CAN_REVEAL(state)) ||
898 (cx == 0 && cy == state->h+1) ||
899 (cx == state->w+1 && cy == 0) ||
900 (cx == state->w+1 && cy == state->h+1))
901 return NULL; /* disallow moving cursor to corners. */
902 ui->cur_x = cx;
903 ui->cur_y = cy;
904 ui->cur_visible = 1;
905 return "";
906 }
907
908 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
909 gx = FROMDRAW(x);
910 gy = FROMDRAW(y);
911 ui->cur_visible = 0;
912 wouldflash = 1;
913 } else if (button == LEFT_RELEASE) {
914 ui->flash_laser = 0;
915 return "";
916 } else if (IS_CURSOR_SELECT(button)) {
917 if (ui->cur_visible) {
918 gx = ui->cur_x;
919 gy = ui->cur_y;
920 ui->flash_laser = 0;
921 wouldflash = 2;
922 } else {
923 ui->cur_visible = 1;
924 return "";
925 }
926 /* Fix up 'button' for the below logic. */
927 if (button == CURSOR_SELECT2) button = RIGHT_BUTTON;
928 else button = LEFT_BUTTON;
929 }
930
931 if (gx != -1 && gy != -1) {
932 if (gx == 0 && gy == 0 && button == LEFT_BUTTON)
933 action = REVEAL;
934 if (gx >= 1 && gx <= state->w && gy >= 1 && gy <= state->h) {
935 if (button == LEFT_BUTTON) {
936 if (!(GRID(state, gx,gy) & BALL_LOCK))
937 action = TOGGLE_BALL;
938 } else
939 action = TOGGLE_LOCK;
940 }
941 if (grid2range(state, gx, gy, &rangeno)) {
942 if (button == LEFT_BUTTON)
943 action = FIRE;
944 else if (gy == 0 || gy > state->h)
945 action = TOGGLE_COLUMN_LOCK; /* and use gx */
946 else
947 action = TOGGLE_ROW_LOCK; /* and use gy */
948 }
949 }
950
951 switch (action) {
952 case TOGGLE_BALL:
953 sprintf(buf, "T%d,%d", gx, gy);
954 break;
955
956 case TOGGLE_LOCK:
957 sprintf(buf, "LB%d,%d", gx, gy);
958 break;
959
960 case TOGGLE_COLUMN_LOCK:
961 sprintf(buf, "LC%d", gx);
962 break;
963
964 case TOGGLE_ROW_LOCK:
965 sprintf(buf, "LR%d", gy);
966 break;
967
968 case FIRE:
969 if (state->reveal && state->exits[rangeno] == LASER_EMPTY)
970 return nullret;
971 ui->flash_laserno = rangeno;
972 ui->flash_laser = wouldflash;
973 nullret = "";
974 if (state->exits[rangeno] != LASER_EMPTY)
975 return "";
976 sprintf(buf, "F%d", rangeno);
977 break;
978
979 case REVEAL:
980 if (!CAN_REVEAL(state)) return nullret;
981 if (ui->cur_visible == 1) ui->cur_x = ui->cur_y = 1;
982 sprintf(buf, "R");
983 break;
984
985 default:
986 return nullret;
987 }
988 if (state->reveal) return nullret;
989 ui->newmove = TRUE;
990 return dupstr(buf);
991}
992
993static game_state *execute_move(const game_state *from, const char *move)
994{
995 game_state *ret = dup_game(from);
996 int gx = -1, gy = -1, rangeno = -1;
997
998 if (ret->justwrong) {
999 int i;
1000 ret->justwrong = FALSE;
1001 for (i = 0; i < ret->nlasers; i++)
1002 if (ret->exits[i] != LASER_EMPTY)
1003 ret->exits[i] &= ~(LASER_OMITTED | LASER_WRONG);
1004 }
1005
1006 if (!strcmp(move, "S")) {
1007 check_guesses(ret, FALSE);
1008 return ret;
1009 }
1010
1011 if (from->reveal) goto badmove;
1012 if (!*move) goto badmove;
1013
1014 switch (move[0]) {
1015 case 'T':
1016 sscanf(move+1, "%d,%d", &gx, &gy);
1017 if (gx < 1 || gy < 1 || gx > ret->w || gy > ret->h)
1018 goto badmove;
1019 if (GRID(ret, gx, gy) & BALL_GUESS) {
1020 ret->nguesses--;
1021 GRID(ret, gx, gy) &= ~BALL_GUESS;
1022 } else {
1023 ret->nguesses++;
1024 GRID(ret, gx, gy) |= BALL_GUESS;
1025 }
1026 break;
1027
1028 case 'F':
1029 sscanf(move+1, "%d", &rangeno);
1030 if (ret->exits[rangeno] != LASER_EMPTY)
1031 goto badmove;
1032 if (!RANGECHECK(ret, rangeno))
1033 goto badmove;
1034 fire_laser(ret, rangeno);
1035 break;
1036
1037 case 'R':
1038 if (ret->nguesses < ret->minballs ||
1039 ret->nguesses > ret->maxballs)
1040 goto badmove;
1041 check_guesses(ret, TRUE);
1042 break;
1043
1044 case 'L':
1045 {
1046 int lcount = 0;
1047 if (strlen(move) < 2) goto badmove;
1048 switch (move[1]) {
1049 case 'B':
1050 sscanf(move+2, "%d,%d", &gx, &gy);
1051 if (gx < 1 || gy < 1 || gx > ret->w || gy > ret->h)
1052 goto badmove;
1053 GRID(ret, gx, gy) ^= BALL_LOCK;
1054 break;
1055
1056#define COUNTLOCK do { if (GRID(ret, gx, gy) & BALL_LOCK) lcount++; } while (0)
1057#define SETLOCKIF(c) do { \
1058 if (lcount > (c)) GRID(ret, gx, gy) &= ~BALL_LOCK; \
1059 else GRID(ret, gx, gy) |= BALL_LOCK; \
1060} while(0)
1061
1062 case 'C':
1063 sscanf(move+2, "%d", &gx);
1064 if (gx < 1 || gx > ret->w) goto badmove;
1065 for (gy = 1; gy <= ret->h; gy++) { COUNTLOCK; }
1066 for (gy = 1; gy <= ret->h; gy++) { SETLOCKIF(ret->h/2); }
1067 break;
1068
1069 case 'R':
1070 sscanf(move+2, "%d", &gy);
1071 if (gy < 1 || gy > ret->h) goto badmove;
1072 for (gx = 1; gx <= ret->w; gx++) { COUNTLOCK; }
1073 for (gx = 1; gx <= ret->w; gx++) { SETLOCKIF(ret->w/2); }
1074 break;
1075
1076#undef COUNTLOCK
1077#undef SETLOCKIF
1078
1079 default:
1080 goto badmove;
1081 }
1082 }
1083 break;
1084
1085 default:
1086 goto badmove;
1087 }
1088
1089 return ret;
1090
1091badmove:
1092 free_game(ret);
1093 return NULL;
1094}
1095
1096/* ----------------------------------------------------------------------
1097 * Drawing routines.
1098 */
1099
1100static void game_compute_size(const game_params *params, int tilesize,
1101 int *x, int *y)
1102{
1103 /* Border is ts/2, to make things easier.
1104 * Thus we have (width) + 2 (firing range*2) + 1 (border*2) tiles
1105 * across, and similarly height + 2 + 1 tiles down. */
1106 *x = (params->w + 3) * tilesize;
1107 *y = (params->h + 3) * tilesize;
1108}
1109
1110static void game_set_size(drawing *dr, game_drawstate *ds,
1111 const game_params *params, int tilesize)
1112{
1113 ds->tilesize = tilesize;
1114 ds->crad = (tilesize-1)/2;
1115 ds->rrad = (3*tilesize)/8;
1116}
1117
1118static float *game_colours(frontend *fe, int *ncolours)
1119{
1120 float *ret = snewn(3 * NCOLOURS, float);
1121 int i;
1122
1123 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1124
1125 ret[COL_BALL * 3 + 0] = 0.0F;
1126 ret[COL_BALL * 3 + 1] = 0.0F;
1127 ret[COL_BALL * 3 + 2] = 0.0F;
1128
1129 ret[COL_WRONG * 3 + 0] = 1.0F;
1130 ret[COL_WRONG * 3 + 1] = 0.0F;
1131 ret[COL_WRONG * 3 + 2] = 0.0F;
1132
1133 ret[COL_BUTTON * 3 + 0] = 0.0F;
1134 ret[COL_BUTTON * 3 + 1] = 1.0F;
1135 ret[COL_BUTTON * 3 + 2] = 0.0F;
1136
1137 ret[COL_CURSOR * 3 + 0] = 1.0F;
1138 ret[COL_CURSOR * 3 + 1] = 0.0F;
1139 ret[COL_CURSOR * 3 + 2] = 0.0F;
1140
1141 for (i = 0; i < 3; i++) {
1142 ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F;
1143 ret[COL_LOCK * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.7F;
1144 ret[COL_COVER * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.5F;
1145 ret[COL_TEXT * 3 + i] = 0.0F;
1146 }
1147
1148 ret[COL_FLASHTEXT * 3 + 0] = 0.0F;
1149 ret[COL_FLASHTEXT * 3 + 1] = 1.0F;
1150 ret[COL_FLASHTEXT * 3 + 2] = 0.0F;
1151
1152 *ncolours = NCOLOURS;
1153 return ret;
1154}
1155
1156static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1157{
1158 struct game_drawstate *ds = snew(struct game_drawstate);
1159
1160 ds->tilesize = 0;
1161 ds->w = state->w; ds->h = state->h;
1162 ds->grid = snewn((state->w+2)*(state->h+2), unsigned int);
1163 memset(ds->grid, 0, (state->w+2)*(state->h+2)*sizeof(unsigned int));
1164 ds->started = ds->reveal = 0;
1165 ds->flash_laserno = LASER_EMPTY;
1166 ds->isflash = 0;
1167
1168 return ds;
1169}
1170
1171static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1172{
1173 sfree(ds->grid);
1174 sfree(ds);
1175}
1176
1177static void draw_square_cursor(drawing *dr, game_drawstate *ds, int dx, int dy)
1178{
1179 int coff = TILE_SIZE/8;
1180 draw_rect_outline(dr, dx + coff, dy + coff,
1181 TILE_SIZE - coff*2,
1182 TILE_SIZE - coff*2,
1183 COL_CURSOR);
1184}
1185
1186
1187static void draw_arena_tile(drawing *dr, const game_state *gs,
1188 game_drawstate *ds, const game_ui *ui,
1189 int ax, int ay, int force, int isflash)
1190{
1191 int gx = ax+1, gy = ay+1;
1192 int gs_tile = GRID(gs, gx, gy), ds_tile = GRID(ds, gx, gy);
1193 int dx = TODRAW(gx), dy = TODRAW(gy);
1194
1195 if (ui->cur_visible && ui->cur_x == gx && ui->cur_y == gy)
1196 gs_tile |= FLAG_CURSOR;
1197
1198 if (gs_tile != ds_tile || gs->reveal != ds->reveal || force) {
1199 int bcol, ocol, bg;
1200
1201 bg = (gs->reveal ? COL_BACKGROUND :
1202 (gs_tile & BALL_LOCK) ? COL_LOCK : COL_COVER);
1203
1204 draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, bg);
1205 draw_rect_outline(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_GRID);
1206
1207 if (gs->reveal) {
1208 /* Guessed balls are always black; if they're incorrect they'll
1209 * have a red cross added later.
1210 * Missing balls are red. */
1211 if (gs_tile & BALL_GUESS) {
1212 bcol = isflash ? bg : COL_BALL;
1213 } else if (gs_tile & BALL_CORRECT) {
1214 bcol = isflash ? bg : COL_WRONG;
1215 } else {
1216 bcol = bg;
1217 }
1218 } else {
1219 /* guesses are black/black, all else background. */
1220 if (gs_tile & BALL_GUESS) {
1221 bcol = COL_BALL;
1222 } else {
1223 bcol = bg;
1224 }
1225 }
1226 ocol = (gs_tile & FLAG_CURSOR && bcol != bg) ? COL_CURSOR : bcol;
1227
1228 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, ds->crad-1,
1229 ocol, ocol);
1230 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, ds->crad-3,
1231 bcol, bcol);
1232
1233
1234 if (gs_tile & FLAG_CURSOR && bcol == bg)
1235 draw_square_cursor(dr, ds, dx, dy);
1236
1237 if (gs->reveal &&
1238 (gs_tile & BALL_GUESS) &&
1239 !(gs_tile & BALL_CORRECT)) {
1240 int x1 = dx + 3, y1 = dy + 3;
1241 int x2 = dx + TILE_SIZE - 3, y2 = dy + TILE_SIZE-3;
1242 int coords[8];
1243
1244 /* Incorrect guess; draw a red cross over the ball. */
1245 coords[0] = x1-1;
1246 coords[1] = y1+1;
1247 coords[2] = x1+1;
1248 coords[3] = y1-1;
1249 coords[4] = x2+1;
1250 coords[5] = y2-1;
1251 coords[6] = x2-1;
1252 coords[7] = y2+1;
1253 draw_polygon(dr, coords, 4, COL_WRONG, COL_WRONG);
1254 coords[0] = x2+1;
1255 coords[1] = y1+1;
1256 coords[2] = x2-1;
1257 coords[3] = y1-1;
1258 coords[4] = x1-1;
1259 coords[5] = y2-1;
1260 coords[6] = x1+1;
1261 coords[7] = y2+1;
1262 draw_polygon(dr, coords, 4, COL_WRONG, COL_WRONG);
1263 }
1264 draw_update(dr, dx, dy, TILE_SIZE, TILE_SIZE);
1265 }
1266 GRID(ds,gx,gy) = gs_tile;
1267}
1268
1269static void draw_laser_tile(drawing *dr, const game_state *gs,
1270 game_drawstate *ds, const game_ui *ui,
1271 int lno, int force)
1272{
1273 int gx, gy, dx, dy, unused;
1274 int wrong, omitted, reflect, hit, laserval, flash = 0, tmp;
1275 unsigned int gs_tile, ds_tile, exitno;
1276
1277 tmp = range2grid(gs, lno, &gx, &gy, &unused);
1278 assert(tmp);
1279 gs_tile = GRID(gs, gx, gy);
1280 ds_tile = GRID(ds, gx, gy);
1281 dx = TODRAW(gx);
1282 dy = TODRAW(gy);
1283
1284 wrong = gs->exits[lno] & LASER_WRONG;
1285 omitted = gs->exits[lno] & LASER_OMITTED;
1286 exitno = gs->exits[lno] & ~LASER_FLAGMASK;
1287
1288 reflect = gs_tile & LASER_REFLECT;
1289 hit = gs_tile & LASER_HIT;
1290 laserval = gs_tile & ~LASER_FLAGMASK;
1291
1292 if (lno == ds->flash_laserno)
1293 gs_tile |= LASER_FLASHED;
1294 else if (!(gs->exits[lno] & (LASER_HIT | LASER_REFLECT))) {
1295 if (exitno == ds->flash_laserno)
1296 gs_tile |= LASER_FLASHED;
1297 }
1298 if (gs_tile & LASER_FLASHED) flash = 1;
1299
1300 gs_tile |= wrong | omitted;
1301
1302 if (ui->cur_visible && ui->cur_x == gx && ui->cur_y == gy)
1303 gs_tile |= FLAG_CURSOR;
1304
1305 if (gs_tile != ds_tile || force) {
1306 draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
1307 draw_rect_outline(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_GRID);
1308
1309 if (gs_tile &~ (LASER_WRONG | LASER_OMITTED | FLAG_CURSOR)) {
1310 char str[32];
1311 int tcol = flash ? COL_FLASHTEXT : omitted ? COL_WRONG : COL_TEXT;
1312
1313 if (reflect || hit)
1314 sprintf(str, "%s", reflect ? "R" : "H");
1315 else
1316 sprintf(str, "%d", laserval);
1317
1318 if (wrong) {
1319 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2,
1320 ds->rrad,
1321 COL_WRONG, COL_WRONG);
1322 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2,
1323 ds->rrad - TILE_SIZE/16,
1324 COL_BACKGROUND, COL_WRONG);
1325 }
1326
1327 draw_text(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2,
1328 FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1329 tcol, str);
1330 }
1331 if (gs_tile & FLAG_CURSOR)
1332 draw_square_cursor(dr, ds, dx, dy);
1333
1334 draw_update(dr, dx, dy, TILE_SIZE, TILE_SIZE);
1335 }
1336 GRID(ds, gx, gy) = gs_tile;
1337}
1338
1339#define CUR_ANIM 0.2F
1340
1341static void game_redraw(drawing *dr, game_drawstate *ds,
1342 const game_state *oldstate, const game_state *state,
1343 int dir, const game_ui *ui,
1344 float animtime, float flashtime)
1345{
1346 int i, x, y, ts = TILE_SIZE, isflash = 0, force = 0;
1347
1348 if (flashtime > 0) {
1349 int frame = (int)(flashtime / FLASH_FRAME);
1350 isflash = (frame % 2) == 0;
1351 debug(("game_redraw: flashtime = %f", flashtime));
1352 }
1353
1354 if (!ds->started) {
1355 int x0 = TODRAW(0)-1, y0 = TODRAW(0)-1;
1356 int x1 = TODRAW(state->w+2), y1 = TODRAW(state->h+2);
1357
1358 draw_rect(dr, 0, 0,
1359 TILE_SIZE * (state->w+3), TILE_SIZE * (state->h+3),
1360 COL_BACKGROUND);
1361
1362 /* clockwise around the outline starting at pt behind (1,1). */
1363 draw_line(dr, x0+ts, y0+ts, x0+ts, y0, COL_HIGHLIGHT);
1364 draw_line(dr, x0+ts, y0, x1-ts, y0, COL_HIGHLIGHT);
1365 draw_line(dr, x1-ts, y0, x1-ts, y0+ts, COL_LOWLIGHT);
1366 draw_line(dr, x1-ts, y0+ts, x1, y0+ts, COL_HIGHLIGHT);
1367 draw_line(dr, x1, y0+ts, x1, y1-ts, COL_LOWLIGHT);
1368 draw_line(dr, x1, y1-ts, x1-ts, y1-ts, COL_LOWLIGHT);
1369 draw_line(dr, x1-ts, y1-ts, x1-ts, y1, COL_LOWLIGHT);
1370 draw_line(dr, x1-ts, y1, x0+ts, y1, COL_LOWLIGHT);
1371 draw_line(dr, x0+ts, y1, x0+ts, y1-ts, COL_HIGHLIGHT);
1372 draw_line(dr, x0+ts, y1-ts, x0, y1-ts, COL_LOWLIGHT);
1373 draw_line(dr, x0, y1-ts, x0, y0+ts, COL_HIGHLIGHT);
1374 draw_line(dr, x0, y0+ts, x0+ts, y0+ts, COL_HIGHLIGHT);
1375 /* phew... */
1376
1377 draw_update(dr, 0, 0,
1378 TILE_SIZE * (state->w+3), TILE_SIZE * (state->h+3));
1379 force = 1;
1380 ds->started = 1;
1381 }
1382
1383 if (isflash != ds->isflash) force = 1;
1384
1385 /* draw the arena */
1386 for (x = 0; x < state->w; x++) {
1387 for (y = 0; y < state->h; y++) {
1388 draw_arena_tile(dr, state, ds, ui, x, y, force, isflash);
1389 }
1390 }
1391
1392 /* draw the lasers */
1393 ds->flash_laserno = LASER_EMPTY;
1394 if (ui->flash_laser == 1)
1395 ds->flash_laserno = ui->flash_laserno;
1396 else if (ui->flash_laser == 2 && animtime > 0)
1397 ds->flash_laserno = ui->flash_laserno;
1398
1399 for (i = 0; i < 2*(state->w+state->h); i++) {
1400 draw_laser_tile(dr, state, ds, ui, i, force);
1401 }
1402
1403 /* draw the 'finish' button */
1404 if (CAN_REVEAL(state)) {
1405 int outline = (ui->cur_visible && ui->cur_x == 0 && ui->cur_y == 0)
1406 ? COL_CURSOR : COL_BALL;
1407 clip(dr, TODRAW(0)-1, TODRAW(0)-1, TILE_SIZE+1, TILE_SIZE+1);
1408 draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad,
1409 outline, outline);
1410 draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad-2,
1411 COL_BUTTON, COL_BUTTON);
1412 unclip(dr);
1413 } else {
1414 draw_rect(dr, TODRAW(0)-1, TODRAW(0)-1,
1415 TILE_SIZE+1, TILE_SIZE+1, COL_BACKGROUND);
1416 }
1417 draw_update(dr, TODRAW(0), TODRAW(0), TILE_SIZE, TILE_SIZE);
1418 ds->reveal = state->reveal;
1419 ds->isflash = isflash;
1420
1421 {
1422 char buf[256];
1423
1424 if (ds->reveal) {
1425 if (state->nwrong == 0 &&
1426 state->nmissed == 0 &&
1427 state->nright >= state->minballs)
1428 sprintf(buf, "CORRECT!");
1429 else
1430 sprintf(buf, "%d wrong and %d missed balls.",
1431 state->nwrong, state->nmissed);
1432 } else if (state->justwrong) {
1433 sprintf(buf, "Wrong! Guess again.");
1434 } else {
1435 if (state->nguesses > state->maxballs)
1436 sprintf(buf, "%d too many balls marked.",
1437 state->nguesses - state->maxballs);
1438 else if (state->nguesses <= state->maxballs &&
1439 state->nguesses >= state->minballs)
1440 sprintf(buf, "Click button to verify guesses.");
1441 else if (state->maxballs == state->minballs)
1442 sprintf(buf, "Balls marked: %d / %d",
1443 state->nguesses, state->minballs);
1444 else
1445 sprintf(buf, "Balls marked: %d / %d-%d.",
1446 state->nguesses, state->minballs, state->maxballs);
1447 }
1448 if (ui->errors) {
1449 sprintf(buf + strlen(buf), " (%d error%s)",
1450 ui->errors, ui->errors > 1 ? "s" : "");
1451 }
1452 status_bar(dr, buf);
1453 }
1454}
1455
1456static float game_anim_length(const game_state *oldstate,
1457 const game_state *newstate, int dir, game_ui *ui)
1458{
1459 return (ui->flash_laser == 2) ? CUR_ANIM : 0.0F;
1460}
1461
1462static float game_flash_length(const game_state *oldstate,
1463 const game_state *newstate, int dir, game_ui *ui)
1464{
1465 if (!oldstate->reveal && newstate->reveal)
1466 return 4.0F * FLASH_FRAME;
1467 else
1468 return 0.0F;
1469}
1470
1471static int game_status(const game_state *state)
1472{
1473 if (state->reveal) {
1474 /*
1475 * We return nonzero whenever the solution has been revealed,
1476 * even (on spoiler grounds) if it wasn't guessed correctly.
1477 */
1478 if (state->nwrong == 0 &&
1479 state->nmissed == 0 &&
1480 state->nright >= state->minballs)
1481 return +1;
1482 else
1483 return -1;
1484 }
1485 return 0;
1486}
1487
1488static int game_timing_state(const game_state *state, game_ui *ui)
1489{
1490 return TRUE;
1491}
1492
1493static void game_print_size(const game_params *params, float *x, float *y)
1494{
1495}
1496
1497static void game_print(drawing *dr, const game_state *state, int tilesize)
1498{
1499}
1500
1501#ifdef COMBINED
1502#define thegame blackbox
1503#endif
1504
1505const struct game thegame = {
1506 "Black Box", "games.blackbox", "blackbox",
1507 default_params,
1508 game_fetch_preset, NULL,
1509 decode_params,
1510 encode_params,
1511 free_params,
1512 dup_params,
1513 TRUE, game_configure, custom_params,
1514 validate_params,
1515 new_game_desc,
1516 validate_desc,
1517 new_game,
1518 dup_game,
1519 free_game,
1520 TRUE, solve_game,
1521 FALSE, game_can_format_as_text_now, game_text_format,
1522 new_ui,
1523 free_ui,
1524 encode_ui,
1525 decode_ui,
1526 game_changed_state,
1527 interpret_move,
1528 execute_move,
1529 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1530 game_colours,
1531 game_new_drawstate,
1532 game_free_drawstate,
1533 game_redraw,
1534 game_anim_length,
1535 game_flash_length,
1536 game_status,
1537 FALSE, FALSE, game_print_size, game_print,
1538 TRUE, /* wants_statusbar */
1539 FALSE, game_timing_state,
1540 REQUIRE_RBUTTON, /* flags */
1541};
1542
1543/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/blackbox.html b/apps/plugins/puzzles/src/blackbox.html
new file mode 100644
index 0000000000..f7bb340f41
--- /dev/null
+++ b/apps/plugins/puzzles/src/blackbox.html
@@ -0,0 +1,125 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Black Box</title>
7<link rel="previous" href="untangle.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="slant.html">
12</head>
13<body>
14<p><a href="untangle.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="slant.html">Next</a></p>
15<h1><a name="C19"></a>Chapter 19: <a name="i0"></a>Black Box</h1>
16<p>
17A number of balls are hidden in a rectangular arena. You have to deduce the positions of the balls by firing lasers positioned at the edges of the arena and observing how their beams are deflected.
18</p>
19<p>
20Beams will travel straight from their origin until they hit the opposite side of the arena (at which point they emerge), unless affected by balls in one of the following ways:
21</p>
22<ul><li>
23A beam that hits a ball head-on is absorbed and will never re-emerge. This includes beams that meet a ball on the first rank of the arena.
24</li>
25<li>
26A beam with a ball in its front-left square and no ball ahead of it gets deflected 90 degrees to the right.
27</li>
28<li>
29A beam with a ball in its front-right square and no ball ahead of it gets similarly deflected to the left.
30</li>
31<li>
32A beam that would re-emerge from its entry location is considered to be &#8216;reflected&#8217;.
33</li>
34<li>
35A beam which would get deflected before entering the arena by a ball to the front-left or front-right of its entry point is also considered to be &#8216;reflected&#8217;.
36</li>
37</ul>
38<p>
39Beams that are reflected appear as a &#8216;R&#8217;; beams that hit balls head-on appear as &#8216;H&#8217;. Otherwise, a number appears at the firing point and the location where the beam emerges (this number is unique to that shot).
40</p>
41<p>
42You can place guesses as to the location of the balls, based on the entry and exit patterns of the beams; once you have placed enough balls a button appears enabling you to have your guesses checked.
43</p>
44<p>
45Here is a diagram showing how the positions of balls can create each of the beam behaviours shown above:
46</p>
47<pre><code> 1RHR----
48|..O.O...|
492........3
50|........|
51|........|
523........|
53|......O.|
54H........|
55|.....O..|
56 12-RR---
57</code></pre>
58<p>
59As shown, it is possible for a beam to receive multiple reflections before re-emerging (see turn 3). Similarly, a beam may be reflected (possibly more than once) before receiving a hit (the &#8216;H&#8217; on the left side of the example).
60</p>
61<p>
62Note that any layout with more than 4 balls may have a non-unique solution. The following diagram illustrates this; if you know the board contains 5 balls, it is impossible to determine where the fifth ball is (possible positions marked with an <code>x</code>):
63</p>
64<pre><code> --------
65|........|
66|........|
67|..O..O..|
68|...xx...|
69|...xx...|
70|..O..O..|
71|........|
72|........|
73 --------
74</code></pre>
75<p>
76For this reason, when you have your guesses checked, the game will check that your solution <em>produces the same results</em> as the computer's, rather than that your solution is identical to the computer's. So in the above example, you could put the fifth ball at <em>any</em> of the locations marked with an <code>x</code>, and you would still win.
77</p>
78<p>
79Black Box was contributed to this collection by James Harvey.
80</p>
81<h2><a name="S19.1"></a>19.1 <a name="i1"></a>Black Box controls</h2>
82<p>
83To fire a laser beam, left-click in a square around the edge of the arena. The results will be displayed immediately. Clicking or holding the left button on one of these squares will highlight the current go (or a previous go) to confirm the exit point for that laser, if applicable.
84</p>
85<p>
86To guess the location of a ball, left-click within the arena and a black circle will appear marking the guess; click again to remove the guessed ball.
87</p>
88<p>
89Locations in the arena may be locked against modification by right-clicking; whole rows and columns may be similarly locked by right-clicking in the laser square above/below that column, or to the left/right of that row.
90</p>
91<p>
92The cursor keys may also be used to move around the grid. Pressing the Enter key will fire a laser or add a new ball-location guess, and pressing Space will lock a cell, row, or column.
93</p>
94<p>
95When an appropriate number of balls have been guessed, a button will appear at the top-left corner of the grid; clicking that (with mouse or cursor) will check your guesses.
96</p>
97<p>
98If you click the &#8216;check&#8217; button and your guesses are not correct, the game will show you the minimum information necessary to demonstrate this to you, so you can try again. If your ball positions are not consistent with the beam paths you already know about, one beam path will be circled to indicate that it proves you wrong. If your positions match all the existing beam paths but are still wrong, one new beam path will be revealed (written in red) which is not consistent with your current guesses.
99</p>
100<p>
101If you decide to give up completely, you can select Solve to reveal the actual ball positions. At this point, correctly-placed balls will be displayed as filled black circles, incorrectly-placed balls as filled black circles with red crosses, and missing balls as filled red circles. In addition, a red circle marks any laser you had already fired which is not consistent with your ball layout (just as when you press the &#8216;check&#8217; button), and red text marks any laser you <em>could</em> have fired in order to distinguish your ball layout from the correct one.
102</p>
103<p>
104(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
105</p>
106<h2><a name="S19.2"></a>19.2 <a name="i2"></a>Black Box parameters</h2>
107<p>
108These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
109</p>
110<dl><dt>
111<em>Width</em>, <em>Height</em>
112</dt>
113<dd>
114Size of grid in squares. There are 2 &#215; <em>Width</em> &#215; <em>Height</em> lasers per grid, two per row and two per column.
115</dd>
116<dt>
117<em>No. of balls</em>
118</dt>
119<dd>
120Number of balls to place in the grid. This can be a single number, or a range (separated with a hyphen, like &#8216;2-6&#8217;), and determines the number of balls to place on the grid. The &#8216;reveal&#8217; button is only enabled if you have guessed an appropriate number of balls; a guess using a different number to the original solution is still acceptable, if all the beam inputs and outputs match.
121</dd>
122</dl>
123
124<hr><address></address></body>
125</html>
diff --git a/apps/plugins/puzzles/src/bridges.R b/apps/plugins/puzzles/src/bridges.R
new file mode 100644
index 0000000000..75df309152
--- /dev/null
+++ b/apps/plugins/puzzles/src/bridges.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3BRIDGES_EXTRA = dsf findloop
4
5bridges : [X] GTK COMMON bridges BRIDGES_EXTRA bridges-icon|no-icon
6
7bridges : [G] WINDOWS COMMON bridges BRIDGES_EXTRA bridges.res|noicon.res
8
9ALL += bridges[COMBINED] BRIDGES_EXTRA
10
11!begin am gtk
12GAMES += bridges
13!end
14
15!begin >list.c
16 A(bridges) \
17!end
18
19!begin >gamedesc.txt
20bridges:bridges.exe:Bridges:Bridge-placing puzzle:Connect all the islands with a network of bridges.
21!end
diff --git a/apps/plugins/puzzles/src/bridges.c b/apps/plugins/puzzles/src/bridges.c
new file mode 100644
index 0000000000..6975208fd6
--- /dev/null
+++ b/apps/plugins/puzzles/src/bridges.c
@@ -0,0 +1,3262 @@
1/*
2 * bridges.c: Implementation of the Nikoli game 'Bridges'.
3 *
4 * Things still to do:
5 *
6 * - The solver's algorithmic design is not really ideal. It makes
7 * use of the same data representation as gameplay uses, which
8 * often looks like a tempting reuse of code but isn't always a
9 * good idea. In this case, it's unpleasant that each edge of the
10 * graph ends up represented as multiple squares on a grid, with
11 * flags indicating when edges and non-edges cross; that's useful
12 * when the result can be directly translated into positions of
13 * graphics on the display, but in purely internal work it makes
14 * even simple manipulations during solving more painful than they
15 * should be, and complex ones have no choice but to modify the
16 * data structures temporarily, test things, and put them back. I
17 * envisage a complete solver rewrite along the following lines:
18 * + We have a collection of vertices (islands) and edges
19 * (potential bridge locations, i.e. pairs of horizontal or
20 * vertical islands with no other island in between).
21 * + Each edge has an associated list of edges that cross it, and
22 * hence with which it is mutually exclusive.
23 * + For each edge, we track the min and max number of bridges we
24 * currently think possible.
25 * + For each vertex, we track the number of _liberties_ it has,
26 * i.e. its clue number minus the min bridge count for each edge
27 * out of it.
28 * + We also maintain a dsf that identifies sets of vertices which
29 * are connected components of the puzzle so far, and for each
30 * equivalence class we track the total number of liberties for
31 * that component. (The dsf mechanism will also already track
32 * the size of each component, i.e. number of islands.)
33 * + So incrementing the min for an edge requires processing along
34 * the lines of:
35 * - set the max for all edges crossing that one to zero
36 * - decrement the liberty count for the vertex at each end,
37 * and also for each vertex's equivalence class (NB they may
38 * be the same class)
39 * - unify the two equivalence classes if they're not already,
40 * and if so, set the liberty count for the new class to be
41 * the sum of the previous two.
42 * + Decrementing the max is much easier, however.
43 * + With this data structure the really fiddly stuff in stage3()
44 * becomes more or less trivial, because it's now a quick job to
45 * find out whether an island would form an isolated subgraph if
46 * connected to a given subset of its neighbours:
47 * - identify the connected components containing the test
48 * vertex and its putative new neighbours (but be careful not
49 * to count a component more than once if two or more of the
50 * vertices involved are already in the same one)
51 * - find the sum of those components' liberty counts, and also
52 * the total number of islands involved
53 * - if the total liberty count of the connected components is
54 * exactly equal to twice the number of edges we'd be adding
55 * (of course each edge destroys two liberties, one at each
56 * end) then these components would become a subgraph with
57 * zero liberties if connected together.
58 * - therefore, if that subgraph also contains fewer than the
59 * total number of islands, it's disallowed.
60 * - As mentioned in stage3(), once we've identified such a
61 * disallowed pattern, we have two choices for what to do
62 * with it: if the candidate set of neighbours has size 1 we
63 * can reduce the max for the edge to that one neighbour,
64 * whereas if its complement has size 1 we can increase the
65 * min for the edge to the _omitted_ neighbour.
66 *
67 * - write a recursive solver?
68 */
69
70#include <stdio.h>
71#include <stdlib.h>
72#include <string.h>
73#include <assert.h>
74#include <ctype.h>
75#include <math.h>
76
77#include "puzzles.h"
78
79/* Turn this on for hints about which lines are considered possibilities. */
80#undef DRAW_GRID
81
82/* --- structures for params, state, etc. --- */
83
84#define MAX_BRIDGES 4
85
86#define PREFERRED_TILE_SIZE 24
87#define TILE_SIZE (ds->tilesize)
88#define BORDER (TILE_SIZE / 2)
89
90#define COORD(x) ( (x) * TILE_SIZE + BORDER )
91#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
92
93#define FLASH_TIME 0.50F
94
95enum {
96 COL_BACKGROUND,
97 COL_FOREGROUND,
98 COL_HIGHLIGHT, COL_LOWLIGHT,
99 COL_SELECTED, COL_MARK,
100 COL_HINT, COL_GRID,
101 COL_WARNING,
102 COL_CURSOR,
103 NCOLOURS
104};
105
106struct game_params {
107 int w, h, maxb;
108 int islands, expansion; /* %age of island squares, %age chance of expansion */
109 int allowloops, difficulty;
110};
111
112/* general flags used by all structs */
113#define G_ISLAND 0x0001
114#define G_LINEV 0x0002 /* contains a vert. line */
115#define G_LINEH 0x0004 /* contains a horiz. line (mutex with LINEV) */
116#define G_LINE (G_LINEV|G_LINEH)
117#define G_MARKV 0x0008
118#define G_MARKH 0x0010
119#define G_MARK (G_MARKV|G_MARKH)
120#define G_NOLINEV 0x0020
121#define G_NOLINEH 0x0040
122#define G_NOLINE (G_NOLINEV|G_NOLINEH)
123
124/* flags used by the error checker */
125#define G_WARN 0x0080
126
127/* flags used by the solver etc. */
128#define G_SWEEP 0x1000
129
130#define G_FLAGSH (G_LINEH|G_MARKH|G_NOLINEH)
131#define G_FLAGSV (G_LINEV|G_MARKV|G_NOLINEV)
132
133typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */
134
135struct solver_state {
136 int *dsf, *comptspaces;
137 int *tmpdsf, *tmpcompspaces;
138 int refcount;
139};
140
141/* state->gridi is an optimisation; it stores the pointer to the island
142 * structs indexed by (x,y). It's not strictly necessary (we could use
143 * find234 instead), but Purify showed that board generation (mostly the solver)
144 * was spending 60% of its time in find234. */
145
146struct surrounds { /* cloned from lightup.c */
147 struct { int x, y, dx, dy, off; } points[4];
148 int npoints, nislands;
149};
150
151struct island {
152 game_state *state;
153 int x, y, count;
154 struct surrounds adj;
155};
156
157struct game_state {
158 int w, h, completed, solved, allowloops, maxb;
159 grid_type *grid;
160 struct island *islands;
161 int n_islands, n_islands_alloc;
162 game_params params; /* used by the aux solver. */
163#define N_WH_ARRAYS 5
164 char *wha, *possv, *possh, *lines, *maxv, *maxh;
165 struct island **gridi;
166 struct solver_state *solver; /* refcounted */
167};
168
169#define GRIDSZ(s) ((s)->w * (s)->h * sizeof(grid_type))
170
171#define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
172
173#define DINDEX(x,y) ((y)*state->w + (x))
174
175#define INDEX(s,g,x,y) ((s)->g[(y)*((s)->w) + (x)])
176#define IDX(s,g,i) ((s)->g[(i)])
177#define GRID(s,x,y) INDEX(s,grid,x,y)
178#define POSSIBLES(s,dx,x,y) ((dx) ? (INDEX(s,possh,x,y)) : (INDEX(s,possv,x,y)))
179#define MAXIMUM(s,dx,x,y) ((dx) ? (INDEX(s,maxh,x,y)) : (INDEX(s,maxv,x,y)))
180
181#define GRIDCOUNT(s,x,y,f) ((GRID(s,x,y) & (f)) ? (INDEX(s,lines,x,y)) : 0)
182
183#define WITHIN2(x,min,max) (((x) < (min)) ? 0 : (((x) > (max)) ? 0 : 1))
184#define WITHIN(x,min,max) ((min) > (max) ? \
185 WITHIN2(x,max,min) : WITHIN2(x,min,max))
186
187/* --- island struct and tree support functions --- */
188
189#define ISLAND_ORTH(is,j,f,df) \
190 (is->f + (is->adj.points[(j)].off*is->adj.points[(j)].df))
191
192#define ISLAND_ORTHX(is,j) ISLAND_ORTH(is,j,x,dx)
193#define ISLAND_ORTHY(is,j) ISLAND_ORTH(is,j,y,dy)
194
195static void fixup_islands_for_realloc(game_state *state)
196{
197 int i;
198
199 for (i = 0; i < state->w*state->h; i++) state->gridi[i] = NULL;
200 for (i = 0; i < state->n_islands; i++) {
201 struct island *is = &state->islands[i];
202 is->state = state;
203 INDEX(state, gridi, is->x, is->y) = is;
204 }
205}
206
207static int game_can_format_as_text_now(const game_params *params)
208{
209 return TRUE;
210}
211
212static char *game_text_format(const game_state *state)
213{
214 int x, y, len, nl;
215 char *ret, *p;
216 struct island *is;
217 grid_type grid;
218
219 len = (state->h) * (state->w+1) + 1;
220 ret = snewn(len, char);
221 p = ret;
222
223 for (y = 0; y < state->h; y++) {
224 for (x = 0; x < state->w; x++) {
225 grid = GRID(state,x,y);
226 nl = INDEX(state,lines,x,y);
227 is = INDEX(state, gridi, x, y);
228 if (is) {
229 *p++ = '0' + is->count;
230 } else if (grid & G_LINEV) {
231 *p++ = (nl > 1) ? '"' : (nl == 1) ? '|' : '!'; /* gaah, want a double-bar. */
232 } else if (grid & G_LINEH) {
233 *p++ = (nl > 1) ? '=' : (nl == 1) ? '-' : '~';
234 } else {
235 *p++ = '.';
236 }
237 }
238 *p++ = '\n';
239 }
240 *p++ = '\0';
241
242 assert(p - ret == len);
243 return ret;
244}
245
246static void debug_state(game_state *state)
247{
248 char *textversion = game_text_format(state);
249 debug(("%s", textversion));
250 sfree(textversion);
251}
252
253/*static void debug_possibles(game_state *state)
254{
255 int x, y;
256 debug(("possh followed by possv\n"));
257 for (y = 0; y < state->h; y++) {
258 for (x = 0; x < state->w; x++) {
259 debug(("%d", POSSIBLES(state, 1, x, y)));
260 }
261 debug((" "));
262 for (x = 0; x < state->w; x++) {
263 debug(("%d", POSSIBLES(state, 0, x, y)));
264 }
265 debug(("\n"));
266 }
267 debug(("\n"));
268 for (y = 0; y < state->h; y++) {
269 for (x = 0; x < state->w; x++) {
270 debug(("%d", MAXIMUM(state, 1, x, y)));
271 }
272 debug((" "));
273 for (x = 0; x < state->w; x++) {
274 debug(("%d", MAXIMUM(state, 0, x, y)));
275 }
276 debug(("\n"));
277 }
278 debug(("\n"));
279}*/
280
281static void island_set_surrounds(struct island *is)
282{
283 assert(INGRID(is->state,is->x,is->y));
284 is->adj.npoints = is->adj.nislands = 0;
285#define ADDPOINT(cond,ddx,ddy) do {\
286 if (cond) { \
287 is->adj.points[is->adj.npoints].x = is->x+(ddx); \
288 is->adj.points[is->adj.npoints].y = is->y+(ddy); \
289 is->adj.points[is->adj.npoints].dx = (ddx); \
290 is->adj.points[is->adj.npoints].dy = (ddy); \
291 is->adj.points[is->adj.npoints].off = 0; \
292 is->adj.npoints++; \
293 } } while(0)
294 ADDPOINT(is->x > 0, -1, 0);
295 ADDPOINT(is->x < (is->state->w-1), +1, 0);
296 ADDPOINT(is->y > 0, 0, -1);
297 ADDPOINT(is->y < (is->state->h-1), 0, +1);
298}
299
300static void island_find_orthogonal(struct island *is)
301{
302 /* fills in the rest of the 'surrounds' structure, assuming
303 * all other islands are now in place. */
304 int i, x, y, dx, dy, off;
305
306 is->adj.nislands = 0;
307 for (i = 0; i < is->adj.npoints; i++) {
308 dx = is->adj.points[i].dx;
309 dy = is->adj.points[i].dy;
310 x = is->x + dx;
311 y = is->y + dy;
312 off = 1;
313 is->adj.points[i].off = 0;
314 while (INGRID(is->state, x, y)) {
315 if (GRID(is->state, x, y) & G_ISLAND) {
316 is->adj.points[i].off = off;
317 is->adj.nislands++;
318 /*debug(("island (%d,%d) has orth is. %d*(%d,%d) away at (%d,%d).\n",
319 is->x, is->y, off, dx, dy,
320 ISLAND_ORTHX(is,i), ISLAND_ORTHY(is,i)));*/
321 goto foundisland;
322 }
323 off++; x += dx; y += dy;
324 }
325foundisland:
326 ;
327 }
328}
329
330static int island_hasbridge(struct island *is, int direction)
331{
332 int x = is->adj.points[direction].x;
333 int y = is->adj.points[direction].y;
334 grid_type gline = is->adj.points[direction].dx ? G_LINEH : G_LINEV;
335
336 if (GRID(is->state, x, y) & gline) return 1;
337 return 0;
338}
339
340static struct island *island_find_connection(struct island *is, int adjpt)
341{
342 struct island *is_r;
343
344 assert(adjpt < is->adj.npoints);
345 if (!is->adj.points[adjpt].off) return NULL;
346 if (!island_hasbridge(is, adjpt)) return NULL;
347
348 is_r = INDEX(is->state, gridi,
349 ISLAND_ORTHX(is, adjpt), ISLAND_ORTHY(is, adjpt));
350 assert(is_r);
351
352 return is_r;
353}
354
355static struct island *island_add(game_state *state, int x, int y, int count)
356{
357 struct island *is;
358 int realloced = 0;
359
360 assert(!(GRID(state,x,y) & G_ISLAND));
361 GRID(state,x,y) |= G_ISLAND;
362
363 state->n_islands++;
364 if (state->n_islands > state->n_islands_alloc) {
365 state->n_islands_alloc = state->n_islands * 2;
366 state->islands =
367 sresize(state->islands, state->n_islands_alloc, struct island);
368 realloced = 1;
369 }
370 is = &state->islands[state->n_islands-1];
371
372 memset(is, 0, sizeof(struct island));
373 is->state = state;
374 is->x = x;
375 is->y = y;
376 is->count = count;
377 island_set_surrounds(is);
378
379 if (realloced)
380 fixup_islands_for_realloc(state);
381 else
382 INDEX(state, gridi, x, y) = is;
383
384 return is;
385}
386
387
388/* n = -1 means 'flip NOLINE flags [and set line to 0].' */
389static void island_join(struct island *i1, struct island *i2, int n, int is_max)
390{
391 game_state *state = i1->state;
392 int s, e, x, y;
393
394 assert(i1->state == i2->state);
395 assert(n >= -1 && n <= i1->state->maxb);
396
397 if (i1->x == i2->x) {
398 x = i1->x;
399 if (i1->y < i2->y) {
400 s = i1->y+1; e = i2->y-1;
401 } else {
402 s = i2->y+1; e = i1->y-1;
403 }
404 for (y = s; y <= e; y++) {
405 if (is_max) {
406 INDEX(state,maxv,x,y) = n;
407 } else {
408 if (n < 0) {
409 GRID(state,x,y) ^= G_NOLINEV;
410 } else if (n == 0) {
411 GRID(state,x,y) &= ~G_LINEV;
412 } else {
413 GRID(state,x,y) |= G_LINEV;
414 INDEX(state,lines,x,y) = n;
415 }
416 }
417 }
418 } else if (i1->y == i2->y) {
419 y = i1->y;
420 if (i1->x < i2->x) {
421 s = i1->x+1; e = i2->x-1;
422 } else {
423 s = i2->x+1; e = i1->x-1;
424 }
425 for (x = s; x <= e; x++) {
426 if (is_max) {
427 INDEX(state,maxh,x,y) = n;
428 } else {
429 if (n < 0) {
430 GRID(state,x,y) ^= G_NOLINEH;
431 } else if (n == 0) {
432 GRID(state,x,y) &= ~G_LINEH;
433 } else {
434 GRID(state,x,y) |= G_LINEH;
435 INDEX(state,lines,x,y) = n;
436 }
437 }
438 }
439 } else {
440 assert(!"island_join: islands not orthogonal.");
441 }
442}
443
444/* Counts the number of bridges currently attached to the island. */
445static int island_countbridges(struct island *is)
446{
447 int i, c = 0;
448
449 for (i = 0; i < is->adj.npoints; i++) {
450 c += GRIDCOUNT(is->state,
451 is->adj.points[i].x, is->adj.points[i].y,
452 is->adj.points[i].dx ? G_LINEH : G_LINEV);
453 }
454 /*debug(("island count for (%d,%d) is %d.\n", is->x, is->y, c));*/
455 return c;
456}
457
458static int island_adjspace(struct island *is, int marks, int missing,
459 int direction)
460{
461 int x, y, poss, curr, dx;
462 grid_type gline, mline;
463
464 x = is->adj.points[direction].x;
465 y = is->adj.points[direction].y;
466 dx = is->adj.points[direction].dx;
467 gline = dx ? G_LINEH : G_LINEV;
468
469 if (marks) {
470 mline = dx ? G_MARKH : G_MARKV;
471 if (GRID(is->state,x,y) & mline) return 0;
472 }
473 poss = POSSIBLES(is->state, dx, x, y);
474 poss = min(poss, missing);
475
476 curr = GRIDCOUNT(is->state, x, y, gline);
477 poss = min(poss, MAXIMUM(is->state, dx, x, y) - curr);
478
479 return poss;
480}
481
482/* Counts the number of bridge spaces left around the island;
483 * expects the possibles to be up-to-date. */
484static int island_countspaces(struct island *is, int marks)
485{
486 int i, c = 0, missing;
487
488 missing = is->count - island_countbridges(is);
489 if (missing < 0) return 0;
490
491 for (i = 0; i < is->adj.npoints; i++) {
492 c += island_adjspace(is, marks, missing, i);
493 }
494 return c;
495}
496
497static int island_isadj(struct island *is, int direction)
498{
499 int x, y;
500 grid_type gline, mline;
501
502 x = is->adj.points[direction].x;
503 y = is->adj.points[direction].y;
504
505 mline = is->adj.points[direction].dx ? G_MARKH : G_MARKV;
506 gline = is->adj.points[direction].dx ? G_LINEH : G_LINEV;
507 if (GRID(is->state, x, y) & mline) {
508 /* If we're marked (i.e. the thing to attach to is complete)
509 * only count an adjacency if we're already attached. */
510 return GRIDCOUNT(is->state, x, y, gline);
511 } else {
512 /* If we're unmarked, count possible adjacency iff it's
513 * flagged as POSSIBLE. */
514 return POSSIBLES(is->state, is->adj.points[direction].dx, x, y);
515 }
516 return 0;
517}
518
519/* Counts the no. of possible adjacent islands (including islands
520 * we're already connected to). */
521static int island_countadj(struct island *is)
522{
523 int i, nadj = 0;
524
525 for (i = 0; i < is->adj.npoints; i++) {
526 if (island_isadj(is, i)) nadj++;
527 }
528 return nadj;
529}
530
531static void island_togglemark(struct island *is)
532{
533 int i, j, x, y, o;
534 struct island *is_loop;
535
536 /* mark the island... */
537 GRID(is->state, is->x, is->y) ^= G_MARK;
538
539 /* ...remove all marks on non-island squares... */
540 for (x = 0; x < is->state->w; x++) {
541 for (y = 0; y < is->state->h; y++) {
542 if (!(GRID(is->state, x, y) & G_ISLAND))
543 GRID(is->state, x, y) &= ~G_MARK;
544 }
545 }
546
547 /* ...and add marks to squares around marked islands. */
548 for (i = 0; i < is->state->n_islands; i++) {
549 is_loop = &is->state->islands[i];
550 if (!(GRID(is_loop->state, is_loop->x, is_loop->y) & G_MARK))
551 continue;
552
553 for (j = 0; j < is_loop->adj.npoints; j++) {
554 /* if this direction takes us to another island, mark all
555 * squares between the two islands. */
556 if (!is_loop->adj.points[j].off) continue;
557 assert(is_loop->adj.points[j].off > 1);
558 for (o = 1; o < is_loop->adj.points[j].off; o++) {
559 GRID(is_loop->state,
560 is_loop->x + is_loop->adj.points[j].dx*o,
561 is_loop->y + is_loop->adj.points[j].dy*o) |=
562 is_loop->adj.points[j].dy ? G_MARKV : G_MARKH;
563 }
564 }
565 }
566}
567
568static int island_impossible(struct island *is, int strict)
569{
570 int curr = island_countbridges(is), nspc = is->count - curr, nsurrspc;
571 int i, poss;
572 struct island *is_orth;
573
574 if (nspc < 0) {
575 debug(("island at (%d,%d) impossible because full.\n", is->x, is->y));
576 return 1; /* too many bridges */
577 } else if ((curr + island_countspaces(is, 0)) < is->count) {
578 debug(("island at (%d,%d) impossible because not enough spaces.\n", is->x, is->y));
579 return 1; /* impossible to create enough bridges */
580 } else if (strict && curr < is->count) {
581 debug(("island at (%d,%d) impossible because locked.\n", is->x, is->y));
582 return 1; /* not enough bridges and island is locked */
583 }
584
585 /* Count spaces in surrounding islands. */
586 nsurrspc = 0;
587 for (i = 0; i < is->adj.npoints; i++) {
588 int ifree, dx = is->adj.points[i].dx;
589
590 if (!is->adj.points[i].off) continue;
591 poss = POSSIBLES(is->state, dx,
592 is->adj.points[i].x, is->adj.points[i].y);
593 if (poss == 0) continue;
594 is_orth = INDEX(is->state, gridi,
595 ISLAND_ORTHX(is,i), ISLAND_ORTHY(is,i));
596 assert(is_orth);
597
598 ifree = is_orth->count - island_countbridges(is_orth);
599 if (ifree > 0) {
600 /*
601 * ifree is the number of bridges unfilled in the other
602 * island, which is clearly an upper bound on the number
603 * of extra bridges this island may run to it.
604 *
605 * Another upper bound is the number of bridges unfilled
606 * on the specific line between here and there. We must
607 * take the minimum of both.
608 */
609 int bmax = MAXIMUM(is->state, dx,
610 is->adj.points[i].x, is->adj.points[i].y);
611 int bcurr = GRIDCOUNT(is->state,
612 is->adj.points[i].x, is->adj.points[i].y,
613 dx ? G_LINEH : G_LINEV);
614 assert(bcurr <= bmax);
615 nsurrspc += min(ifree, bmax - bcurr);
616 }
617 }
618 if (nsurrspc < nspc) {
619 debug(("island at (%d,%d) impossible: surr. islands %d spc, need %d.\n",
620 is->x, is->y, nsurrspc, nspc));
621 return 1; /* not enough spaces around surrounding islands to fill this one. */
622 }
623
624 return 0;
625}
626
627/* --- Game parameter functions --- */
628
629#define DEFAULT_PRESET 0
630
631const struct game_params bridges_presets[] = {
632 { 7, 7, 2, 30, 10, 1, 0 },
633 { 7, 7, 2, 30, 10, 1, 1 },
634 { 7, 7, 2, 30, 10, 1, 2 },
635 { 10, 10, 2, 30, 10, 1, 0 },
636 { 10, 10, 2, 30, 10, 1, 1 },
637 { 10, 10, 2, 30, 10, 1, 2 },
638 { 15, 15, 2, 30, 10, 1, 0 },
639 { 15, 15, 2, 30, 10, 1, 1 },
640 { 15, 15, 2, 30, 10, 1, 2 },
641};
642
643static game_params *default_params(void)
644{
645 game_params *ret = snew(game_params);
646 *ret = bridges_presets[DEFAULT_PRESET];
647
648 return ret;
649}
650
651static int game_fetch_preset(int i, char **name, game_params **params)
652{
653 game_params *ret;
654 char buf[80];
655
656 if (i < 0 || i >= lenof(bridges_presets))
657 return FALSE;
658
659 ret = default_params();
660 *ret = bridges_presets[i];
661 *params = ret;
662
663 sprintf(buf, "%dx%d %s", ret->w, ret->h,
664 ret->difficulty == 0 ? "easy" :
665 ret->difficulty == 1 ? "medium" : "hard");
666 *name = dupstr(buf);
667
668 return TRUE;
669}
670
671static void free_params(game_params *params)
672{
673 sfree(params);
674}
675
676static game_params *dup_params(const game_params *params)
677{
678 game_params *ret = snew(game_params);
679 *ret = *params; /* structure copy */
680 return ret;
681}
682
683#define EATNUM(x) do { \
684 (x) = atoi(string); \
685 while (*string && isdigit((unsigned char)*string)) string++; \
686} while(0)
687
688static void decode_params(game_params *params, char const *string)
689{
690 EATNUM(params->w);
691 params->h = params->w;
692 if (*string == 'x') {
693 string++;
694 EATNUM(params->h);
695 }
696 if (*string == 'i') {
697 string++;
698 EATNUM(params->islands);
699 }
700 if (*string == 'e') {
701 string++;
702 EATNUM(params->expansion);
703 }
704 if (*string == 'm') {
705 string++;
706 EATNUM(params->maxb);
707 }
708 params->allowloops = 1;
709 if (*string == 'L') {
710 string++;
711 params->allowloops = 0;
712 }
713 if (*string == 'd') {
714 string++;
715 EATNUM(params->difficulty);
716 }
717}
718
719static char *encode_params(const game_params *params, int full)
720{
721 char buf[80];
722
723 if (full) {
724 sprintf(buf, "%dx%di%de%dm%d%sd%d",
725 params->w, params->h, params->islands, params->expansion,
726 params->maxb, params->allowloops ? "" : "L",
727 params->difficulty);
728 } else {
729 sprintf(buf, "%dx%dm%d%s", params->w, params->h,
730 params->maxb, params->allowloops ? "" : "L");
731 }
732 return dupstr(buf);
733}
734
735static config_item *game_configure(const game_params *params)
736{
737 config_item *ret;
738 char buf[80];
739
740 ret = snewn(8, config_item);
741
742 ret[0].name = "Width";
743 ret[0].type = C_STRING;
744 sprintf(buf, "%d", params->w);
745 ret[0].sval = dupstr(buf);
746 ret[0].ival = 0;
747
748 ret[1].name = "Height";
749 ret[1].type = C_STRING;
750 sprintf(buf, "%d", params->h);
751 ret[1].sval = dupstr(buf);
752 ret[1].ival = 0;
753
754 ret[2].name = "Difficulty";
755 ret[2].type = C_CHOICES;
756 ret[2].sval = ":Easy:Medium:Hard";
757 ret[2].ival = params->difficulty;
758
759 ret[3].name = "Allow loops";
760 ret[3].type = C_BOOLEAN;
761 ret[3].sval = NULL;
762 ret[3].ival = params->allowloops;
763
764 ret[4].name = "Max. bridges per direction";
765 ret[4].type = C_CHOICES;
766 ret[4].sval = ":1:2:3:4"; /* keep up-to-date with MAX_BRIDGES */
767 ret[4].ival = params->maxb - 1;
768
769 ret[5].name = "%age of island squares";
770 ret[5].type = C_CHOICES;
771 ret[5].sval = ":5%:10%:15%:20%:25%:30%";
772 ret[5].ival = (params->islands / 5)-1;
773
774 ret[6].name = "Expansion factor (%age)";
775 ret[6].type = C_CHOICES;
776 ret[6].sval = ":0%:10%:20%:30%:40%:50%:60%:70%:80%:90%:100%";
777 ret[6].ival = params->expansion / 10;
778
779 ret[7].name = NULL;
780 ret[7].type = C_END;
781 ret[7].sval = NULL;
782 ret[7].ival = 0;
783
784 return ret;
785}
786
787static game_params *custom_params(const config_item *cfg)
788{
789 game_params *ret = snew(game_params);
790
791 ret->w = atoi(cfg[0].sval);
792 ret->h = atoi(cfg[1].sval);
793 ret->difficulty = cfg[2].ival;
794 ret->allowloops = cfg[3].ival;
795 ret->maxb = cfg[4].ival + 1;
796 ret->islands = (cfg[5].ival + 1) * 5;
797 ret->expansion = cfg[6].ival * 10;
798
799 return ret;
800}
801
802static char *validate_params(const game_params *params, int full)
803{
804 if (params->w < 3 || params->h < 3)
805 return "Width and height must be at least 3";
806 if (params->maxb < 1 || params->maxb > MAX_BRIDGES)
807 return "Too many bridges.";
808 if (full) {
809 if (params->islands <= 0 || params->islands > 30)
810 return "%age of island squares must be between 1% and 30%";
811 if (params->expansion < 0 || params->expansion > 100)
812 return "Expansion factor must be between 0 and 100";
813 }
814 return NULL;
815}
816
817/* --- Game encoding and differences --- */
818
819static char *encode_game(game_state *state)
820{
821 char *ret, *p;
822 int wh = state->w*state->h, run, x, y;
823 struct island *is;
824
825 ret = snewn(wh + 1, char);
826 p = ret;
827 run = 0;
828 for (y = 0; y < state->h; y++) {
829 for (x = 0; x < state->w; x++) {
830 is = INDEX(state, gridi, x, y);
831 if (is) {
832 if (run) {
833 *p++ = ('a'-1) + run;
834 run = 0;
835 }
836 if (is->count < 10)
837 *p++ = '0' + is->count;
838 else
839 *p++ = 'A' + (is->count - 10);
840 } else {
841 if (run == 26) {
842 *p++ = ('a'-1) + run;
843 run = 0;
844 }
845 run++;
846 }
847 }
848 }
849 if (run) {
850 *p++ = ('a'-1) + run;
851 run = 0;
852 }
853 *p = '\0';
854 assert(p - ret <= wh);
855
856 return ret;
857}
858
859static char *game_state_diff(const game_state *src, const game_state *dest)
860{
861 int movesize = 256, movelen = 0;
862 char *move = snewn(movesize, char), buf[80];
863 int i, d, x, y, len;
864 grid_type gline, nline;
865 struct island *is_s, *is_d, *is_orth;
866
867#define APPEND do { \
868 if (movelen + len >= movesize) { \
869 movesize = movelen + len + 256; \
870 move = sresize(move, movesize, char); \
871 } \
872 strcpy(move + movelen, buf); \
873 movelen += len; \
874} while(0)
875
876 move[movelen++] = 'S';
877 move[movelen] = '\0';
878
879 assert(src->n_islands == dest->n_islands);
880
881 for (i = 0; i < src->n_islands; i++) {
882 is_s = &src->islands[i];
883 is_d = &dest->islands[i];
884 assert(is_s->x == is_d->x);
885 assert(is_s->y == is_d->y);
886 assert(is_s->adj.npoints == is_d->adj.npoints); /* more paranoia */
887
888 for (d = 0; d < is_s->adj.npoints; d++) {
889 if (is_s->adj.points[d].dx == -1 ||
890 is_s->adj.points[d].dy == -1) continue;
891
892 x = is_s->adj.points[d].x;
893 y = is_s->adj.points[d].y;
894 gline = is_s->adj.points[d].dx ? G_LINEH : G_LINEV;
895 nline = is_s->adj.points[d].dx ? G_NOLINEH : G_NOLINEV;
896 is_orth = INDEX(dest, gridi,
897 ISLAND_ORTHX(is_d, d), ISLAND_ORTHY(is_d, d));
898
899 if (GRIDCOUNT(src, x, y, gline) != GRIDCOUNT(dest, x, y, gline)) {
900 assert(is_orth);
901 len = sprintf(buf, ";L%d,%d,%d,%d,%d",
902 is_s->x, is_s->y, is_orth->x, is_orth->y,
903 GRIDCOUNT(dest, x, y, gline));
904 APPEND;
905 }
906 if ((GRID(src,x,y) & nline) != (GRID(dest, x, y) & nline)) {
907 assert(is_orth);
908 len = sprintf(buf, ";N%d,%d,%d,%d",
909 is_s->x, is_s->y, is_orth->x, is_orth->y);
910 APPEND;
911 }
912 }
913 if ((GRID(src, is_s->x, is_s->y) & G_MARK) !=
914 (GRID(dest, is_d->x, is_d->y) & G_MARK)) {
915 len = sprintf(buf, ";M%d,%d", is_s->x, is_s->y);
916 APPEND;
917 }
918 }
919 return move;
920}
921
922/* --- Game setup and solving utilities --- */
923
924/* This function is optimised; a Quantify showed that lots of grid-generation time
925 * (>50%) was spent in here. Hence the IDX() stuff. */
926
927static void map_update_possibles(game_state *state)
928{
929 int x, y, s, e, bl, i, np, maxb, w = state->w, idx;
930 struct island *is_s = NULL, *is_f = NULL;
931
932 /* Run down vertical stripes [un]setting possv... */
933 for (x = 0; x < state->w; x++) {
934 idx = x;
935 s = e = -1;
936 bl = 0;
937 maxb = state->params.maxb; /* placate optimiser */
938 /* Unset possible flags until we find an island. */
939 for (y = 0; y < state->h; y++) {
940 is_s = IDX(state, gridi, idx);
941 if (is_s) {
942 maxb = is_s->count;
943 break;
944 }
945
946 IDX(state, possv, idx) = 0;
947 idx += w;
948 }
949 for (; y < state->h; y++) {
950 maxb = min(maxb, IDX(state, maxv, idx));
951 is_f = IDX(state, gridi, idx);
952 if (is_f) {
953 assert(is_s);
954 np = min(maxb, is_f->count);
955
956 if (s != -1) {
957 for (i = s; i <= e; i++) {
958 INDEX(state, possv, x, i) = bl ? 0 : np;
959 }
960 }
961 s = y+1;
962 bl = 0;
963 is_s = is_f;
964 maxb = is_s->count;
965 } else {
966 e = y;
967 if (IDX(state,grid,idx) & (G_LINEH|G_NOLINEV)) bl = 1;
968 }
969 idx += w;
970 }
971 if (s != -1) {
972 for (i = s; i <= e; i++)
973 INDEX(state, possv, x, i) = 0;
974 }
975 }
976
977 /* ...and now do horizontal stripes [un]setting possh. */
978 /* can we lose this clone'n'hack? */
979 for (y = 0; y < state->h; y++) {
980 idx = y*w;
981 s = e = -1;
982 bl = 0;
983 maxb = state->params.maxb; /* placate optimiser */
984 for (x = 0; x < state->w; x++) {
985 is_s = IDX(state, gridi, idx);
986 if (is_s) {
987 maxb = is_s->count;
988 break;
989 }
990
991 IDX(state, possh, idx) = 0;
992 idx += 1;
993 }
994 for (; x < state->w; x++) {
995 maxb = min(maxb, IDX(state, maxh, idx));
996 is_f = IDX(state, gridi, idx);
997 if (is_f) {
998 assert(is_s);
999 np = min(maxb, is_f->count);
1000
1001 if (s != -1) {
1002 for (i = s; i <= e; i++) {
1003 INDEX(state, possh, i, y) = bl ? 0 : np;
1004 }
1005 }
1006 s = x+1;
1007 bl = 0;
1008 is_s = is_f;
1009 maxb = is_s->count;
1010 } else {
1011 e = x;
1012 if (IDX(state,grid,idx) & (G_LINEV|G_NOLINEH)) bl = 1;
1013 }
1014 idx += 1;
1015 }
1016 if (s != -1) {
1017 for (i = s; i <= e; i++)
1018 INDEX(state, possh, i, y) = 0;
1019 }
1020 }
1021}
1022
1023static void map_count(game_state *state)
1024{
1025 int i, n, ax, ay;
1026 grid_type flag, grid;
1027 struct island *is;
1028
1029 for (i = 0; i < state->n_islands; i++) {
1030 is = &state->islands[i];
1031 is->count = 0;
1032 for (n = 0; n < is->adj.npoints; n++) {
1033 ax = is->adj.points[n].x;
1034 ay = is->adj.points[n].y;
1035 flag = (ax == is->x) ? G_LINEV : G_LINEH;
1036 grid = GRID(state,ax,ay);
1037 if (grid & flag) {
1038 is->count += INDEX(state,lines,ax,ay);
1039 }
1040 }
1041 }
1042}
1043
1044static void map_find_orthogonal(game_state *state)
1045{
1046 int i;
1047
1048 for (i = 0; i < state->n_islands; i++) {
1049 island_find_orthogonal(&state->islands[i]);
1050 }
1051}
1052
1053struct bridges_neighbour_ctx {
1054 game_state *state;
1055 int i, n, neighbours[4];
1056};
1057static int bridges_neighbour(int vertex, void *vctx)
1058{
1059 struct bridges_neighbour_ctx *ctx = (struct bridges_neighbour_ctx *)vctx;
1060 if (vertex >= 0) {
1061 game_state *state = ctx->state;
1062 int w = state->w, x = vertex % w, y = vertex / w;
1063 grid_type grid = GRID(state, x, y), gline = grid & G_LINE;
1064 struct island *is;
1065 int x1, y1, x2, y2, i;
1066
1067 ctx->i = ctx->n = 0;
1068
1069 is = INDEX(state, gridi, x, y);
1070 if (is) {
1071 for (i = 0; i < is->adj.npoints; i++) {
1072 gline = is->adj.points[i].dx ? G_LINEH : G_LINEV;
1073 if (GRID(state, is->adj.points[i].x,
1074 is->adj.points[i].y) & gline) {
1075 ctx->neighbours[ctx->n++] =
1076 (is->adj.points[i].y * w + is->adj.points[i].x);
1077 }
1078 }
1079 } else if (gline) {
1080 if (gline & G_LINEV) {
1081 x1 = x2 = x;
1082 y1 = y-1; y2 = y+1;
1083 } else {
1084 x1 = x-1; x2 = x+1;
1085 y1 = y2 = y;
1086 }
1087 /* Non-island squares with edges in should never be
1088 * pointing off the edge of the grid. */
1089 assert(INGRID(state, x1, y1));
1090 assert(INGRID(state, x2, y2));
1091 if (GRID(state, x1, y1) & (gline | G_ISLAND))
1092 ctx->neighbours[ctx->n++] = y1 * w + x1;
1093 if (GRID(state, x2, y2) & (gline | G_ISLAND))
1094 ctx->neighbours[ctx->n++] = y2 * w + x2;
1095 }
1096 }
1097
1098 if (ctx->i < ctx->n)
1099 return ctx->neighbours[ctx->i++];
1100 else
1101 return -1;
1102}
1103
1104static int map_hasloops(game_state *state, int mark)
1105{
1106 int x, y;
1107 struct findloopstate *fls;
1108 struct bridges_neighbour_ctx ctx;
1109 int ret;
1110
1111 fls = findloop_new_state(state->w * state->h);
1112 ctx.state = state;
1113 ret = findloop_run(fls, state->w * state->h, bridges_neighbour, &ctx);
1114
1115 if (mark) {
1116 for (y = 0; y < state->h; y++) {
1117 for (x = 0; x < state->w; x++) {
1118 int u, v;
1119
1120 u = y * state->w + x;
1121 for (v = bridges_neighbour(u, &ctx); v >= 0;
1122 v = bridges_neighbour(-1, &ctx))
1123 if (findloop_is_loop_edge(fls, u, v))
1124 GRID(state,x,y) |= G_WARN;
1125 }
1126 }
1127 }
1128
1129 findloop_free_state(fls);
1130 return ret;
1131}
1132
1133static void map_group(game_state *state)
1134{
1135 int i, wh = state->w*state->h, d1, d2;
1136 int x, y, x2, y2;
1137 int *dsf = state->solver->dsf;
1138 struct island *is, *is_join;
1139
1140 /* Initialise dsf. */
1141 dsf_init(dsf, wh);
1142
1143 /* For each island, find connected islands right or down
1144 * and merge the dsf for the island squares as well as the
1145 * bridge squares. */
1146 for (x = 0; x < state->w; x++) {
1147 for (y = 0; y < state->h; y++) {
1148 GRID(state,x,y) &= ~(G_SWEEP|G_WARN); /* for group_full. */
1149
1150 is = INDEX(state, gridi, x, y);
1151 if (!is) continue;
1152 d1 = DINDEX(x,y);
1153 for (i = 0; i < is->adj.npoints; i++) {
1154 /* only want right/down */
1155 if (is->adj.points[i].dx == -1 ||
1156 is->adj.points[i].dy == -1) continue;
1157
1158 is_join = island_find_connection(is, i);
1159 if (!is_join) continue;
1160
1161 d2 = DINDEX(is_join->x, is_join->y);
1162 if (dsf_canonify(dsf,d1) == dsf_canonify(dsf,d2)) {
1163 ; /* we have a loop. See comment in map_hasloops. */
1164 /* However, we still want to merge all squares joining
1165 * this side-that-makes-a-loop. */
1166 }
1167 /* merge all squares between island 1 and island 2. */
1168 for (x2 = x; x2 <= is_join->x; x2++) {
1169 for (y2 = y; y2 <= is_join->y; y2++) {
1170 d2 = DINDEX(x2,y2);
1171 if (d1 != d2) dsf_merge(dsf,d1,d2);
1172 }
1173 }
1174 }
1175 }
1176 }
1177}
1178
1179static int map_group_check(game_state *state, int canon, int warn,
1180 int *nislands_r)
1181{
1182 int *dsf = state->solver->dsf, nislands = 0;
1183 int x, y, i, allfull = 1;
1184 struct island *is;
1185
1186 for (i = 0; i < state->n_islands; i++) {
1187 is = &state->islands[i];
1188 if (dsf_canonify(dsf, DINDEX(is->x,is->y)) != canon) continue;
1189
1190 GRID(state, is->x, is->y) |= G_SWEEP;
1191 nislands++;
1192 if (island_countbridges(is) != is->count)
1193 allfull = 0;
1194 }
1195 if (warn && allfull && nislands != state->n_islands) {
1196 /* we're full and this island group isn't the whole set.
1197 * Mark all squares with this dsf canon as ERR. */
1198 for (x = 0; x < state->w; x++) {
1199 for (y = 0; y < state->h; y++) {
1200 if (dsf_canonify(dsf, DINDEX(x,y)) == canon) {
1201 GRID(state,x,y) |= G_WARN;
1202 }
1203 }
1204 }
1205
1206 }
1207 if (nislands_r) *nislands_r = nislands;
1208 return allfull;
1209}
1210
1211static int map_group_full(game_state *state, int *ngroups_r)
1212{
1213 int *dsf = state->solver->dsf, ngroups = 0;
1214 int i, anyfull = 0;
1215 struct island *is;
1216
1217 /* NB this assumes map_group (or sth else) has cleared G_SWEEP. */
1218
1219 for (i = 0; i < state->n_islands; i++) {
1220 is = &state->islands[i];
1221 if (GRID(state,is->x,is->y) & G_SWEEP) continue;
1222
1223 ngroups++;
1224 if (map_group_check(state, dsf_canonify(dsf, DINDEX(is->x,is->y)),
1225 1, NULL))
1226 anyfull = 1;
1227 }
1228
1229 *ngroups_r = ngroups;
1230 return anyfull;
1231}
1232
1233static int map_check(game_state *state)
1234{
1235 int ngroups;
1236
1237 /* Check for loops, if necessary. */
1238 if (!state->allowloops) {
1239 if (map_hasloops(state, 1))
1240 return 0;
1241 }
1242
1243 /* Place islands into island groups and check for early
1244 * satisfied-groups. */
1245 map_group(state); /* clears WARN and SWEEP */
1246 if (map_group_full(state, &ngroups)) {
1247 if (ngroups == 1) return 1;
1248 }
1249 return 0;
1250}
1251
1252static void map_clear(game_state *state)
1253{
1254 int x, y;
1255
1256 for (x = 0; x < state->w; x++) {
1257 for (y = 0; y < state->h; y++) {
1258 /* clear most flags; might want to be slightly more careful here. */
1259 GRID(state,x,y) &= G_ISLAND;
1260 }
1261 }
1262}
1263
1264static void solve_join(struct island *is, int direction, int n, int is_max)
1265{
1266 struct island *is_orth;
1267 int d1, d2, *dsf = is->state->solver->dsf;
1268 game_state *state = is->state; /* for DINDEX */
1269
1270 is_orth = INDEX(is->state, gridi,
1271 ISLAND_ORTHX(is, direction),
1272 ISLAND_ORTHY(is, direction));
1273 assert(is_orth);
1274 /*debug(("...joining (%d,%d) to (%d,%d) with %d bridge(s).\n",
1275 is->x, is->y, is_orth->x, is_orth->y, n));*/
1276 island_join(is, is_orth, n, is_max);
1277
1278 if (n > 0 && !is_max) {
1279 d1 = DINDEX(is->x, is->y);
1280 d2 = DINDEX(is_orth->x, is_orth->y);
1281 if (dsf_canonify(dsf, d1) != dsf_canonify(dsf, d2))
1282 dsf_merge(dsf, d1, d2);
1283 }
1284}
1285
1286static int solve_fillone(struct island *is)
1287{
1288 int i, nadded = 0;
1289
1290 debug(("solve_fillone for island (%d,%d).\n", is->x, is->y));
1291
1292 for (i = 0; i < is->adj.npoints; i++) {
1293 if (island_isadj(is, i)) {
1294 if (island_hasbridge(is, i)) {
1295 /* already attached; do nothing. */;
1296 } else {
1297 solve_join(is, i, 1, 0);
1298 nadded++;
1299 }
1300 }
1301 }
1302 return nadded;
1303}
1304
1305static int solve_fill(struct island *is)
1306{
1307 /* for each unmarked adjacent, make sure we convert every possible bridge
1308 * to a real one, and then work out the possibles afresh. */
1309 int i, nnew, ncurr, nadded = 0, missing;
1310
1311 debug(("solve_fill for island (%d,%d).\n", is->x, is->y));
1312
1313 missing = is->count - island_countbridges(is);
1314 if (missing < 0) return 0;
1315
1316 /* very like island_countspaces. */
1317 for (i = 0; i < is->adj.npoints; i++) {
1318 nnew = island_adjspace(is, 1, missing, i);
1319 if (nnew) {
1320 ncurr = GRIDCOUNT(is->state,
1321 is->adj.points[i].x, is->adj.points[i].y,
1322 is->adj.points[i].dx ? G_LINEH : G_LINEV);
1323
1324 solve_join(is, i, nnew + ncurr, 0);
1325 nadded += nnew;
1326 }
1327 }
1328 return nadded;
1329}
1330
1331static int solve_island_stage1(struct island *is, int *didsth_r)
1332{
1333 int bridges = island_countbridges(is);
1334 int nspaces = island_countspaces(is, 1);
1335 int nadj = island_countadj(is);
1336 int didsth = 0;
1337
1338 assert(didsth_r);
1339
1340 /*debug(("island at (%d,%d) filled %d/%d (%d spc) nadj %d\n",
1341 is->x, is->y, bridges, is->count, nspaces, nadj));*/
1342 if (bridges > is->count) {
1343 /* We only ever add bridges when we're sure they fit, or that's
1344 * the only place they can go. If we've added bridges such that
1345 * another island has become wrong, the puzzle must not have had
1346 * a solution. */
1347 debug(("...island at (%d,%d) is overpopulated!\n", is->x, is->y));
1348 return 0;
1349 } else if (bridges == is->count) {
1350 /* This island is full. Make sure it's marked (and update
1351 * possibles if we did). */
1352 if (!(GRID(is->state, is->x, is->y) & G_MARK)) {
1353 debug(("...marking island (%d,%d) as full.\n", is->x, is->y));
1354 island_togglemark(is);
1355 didsth = 1;
1356 }
1357 } else if (GRID(is->state, is->x, is->y) & G_MARK) {
1358 debug(("...island (%d,%d) is marked but unfinished!\n",
1359 is->x, is->y));
1360 return 0; /* island has been marked unfinished; no solution from here. */
1361 } else {
1362 /* This is the interesting bit; we try and fill in more information
1363 * about this island. */
1364 if (is->count == bridges + nspaces) {
1365 if (solve_fill(is) > 0) didsth = 1;
1366 } else if (is->count > ((nadj-1) * is->state->maxb)) {
1367 /* must have at least one bridge in each possible direction. */
1368 if (solve_fillone(is) > 0) didsth = 1;
1369 }
1370 }
1371 if (didsth) {
1372 map_update_possibles(is->state);
1373 *didsth_r = 1;
1374 }
1375 return 1;
1376}
1377
1378/* returns non-zero if a new line here would cause a loop. */
1379static int solve_island_checkloop(struct island *is, int direction)
1380{
1381 struct island *is_orth;
1382 int *dsf = is->state->solver->dsf, d1, d2;
1383 game_state *state = is->state;
1384
1385 if (is->state->allowloops) return 0; /* don't care anyway */
1386 if (island_hasbridge(is, direction)) return 0; /* already has a bridge */
1387 if (island_isadj(is, direction) == 0) return 0; /* no adj island */
1388
1389 is_orth = INDEX(is->state, gridi,
1390 ISLAND_ORTHX(is,direction),
1391 ISLAND_ORTHY(is,direction));
1392 if (!is_orth) return 0;
1393
1394 d1 = DINDEX(is->x, is->y);
1395 d2 = DINDEX(is_orth->x, is_orth->y);
1396 if (dsf_canonify(dsf, d1) == dsf_canonify(dsf, d2)) {
1397 /* two islands are connected already; don't join them. */
1398 return 1;
1399 }
1400 return 0;
1401}
1402
1403static int solve_island_stage2(struct island *is, int *didsth_r)
1404{
1405 int added = 0, removed = 0, navail = 0, nadj, i;
1406
1407 assert(didsth_r);
1408
1409 for (i = 0; i < is->adj.npoints; i++) {
1410 if (solve_island_checkloop(is, i)) {
1411 debug(("removing possible loop at (%d,%d) direction %d.\n",
1412 is->x, is->y, i));
1413 solve_join(is, i, -1, 0);
1414 map_update_possibles(is->state);
1415 removed = 1;
1416 } else {
1417 navail += island_isadj(is, i);
1418 /*debug(("stage2: navail for (%d,%d) direction (%d,%d) is %d.\n",
1419 is->x, is->y,
1420 is->adj.points[i].dx, is->adj.points[i].dy,
1421 island_isadj(is, i)));*/
1422 }
1423 }
1424
1425 /*debug(("island at (%d,%d) navail %d: checking...\n", is->x, is->y, navail));*/
1426
1427 for (i = 0; i < is->adj.npoints; i++) {
1428 if (!island_hasbridge(is, i)) {
1429 nadj = island_isadj(is, i);
1430 if (nadj > 0 && (navail - nadj) < is->count) {
1431 /* we couldn't now complete the island without at
1432 * least one bridge here; put it in. */
1433 /*debug(("nadj %d, navail %d, is->count %d.\n",
1434 nadj, navail, is->count));*/
1435 debug(("island at (%d,%d) direction (%d,%d) must have 1 bridge\n",
1436 is->x, is->y,
1437 is->adj.points[i].dx, is->adj.points[i].dy));
1438 solve_join(is, i, 1, 0);
1439 added = 1;
1440 /*debug_state(is->state);
1441 debug_possibles(is->state);*/
1442 }
1443 }
1444 }
1445 if (added) map_update_possibles(is->state);
1446 if (added || removed) *didsth_r = 1;
1447 return 1;
1448}
1449
1450static int solve_island_subgroup(struct island *is, int direction)
1451{
1452 struct island *is_join;
1453 int nislands, *dsf = is->state->solver->dsf;
1454 game_state *state = is->state;
1455
1456 debug(("..checking subgroups.\n"));
1457
1458 /* if is isn't full, return 0. */
1459 if (island_countbridges(is) < is->count) {
1460 debug(("...orig island (%d,%d) not full.\n", is->x, is->y));
1461 return 0;
1462 }
1463
1464 if (direction >= 0) {
1465 is_join = INDEX(state, gridi,
1466 ISLAND_ORTHX(is, direction),
1467 ISLAND_ORTHY(is, direction));
1468 assert(is_join);
1469
1470 /* if is_join isn't full, return 0. */
1471 if (island_countbridges(is_join) < is_join->count) {
1472 debug(("...dest island (%d,%d) not full.\n",
1473 is_join->x, is_join->y));
1474 return 0;
1475 }
1476 }
1477
1478 /* Check group membership for is->dsf; if it's full return 1. */
1479 if (map_group_check(state, dsf_canonify(dsf, DINDEX(is->x,is->y)),
1480 0, &nislands)) {
1481 if (nislands < state->n_islands) {
1482 /* we have a full subgroup that isn't the whole set.
1483 * This isn't allowed. */
1484 debug(("island at (%d,%d) makes full subgroup, disallowing.\n",
1485 is->x, is->y));
1486 return 1;
1487 } else {
1488 debug(("...has finished puzzle.\n"));
1489 }
1490 }
1491 return 0;
1492}
1493
1494static int solve_island_impossible(game_state *state)
1495{
1496 struct island *is;
1497 int i;
1498
1499 /* If any islands are impossible, return 1. */
1500 for (i = 0; i < state->n_islands; i++) {
1501 is = &state->islands[i];
1502 if (island_impossible(is, 0)) {
1503 debug(("island at (%d,%d) has become impossible, disallowing.\n",
1504 is->x, is->y));
1505 return 1;
1506 }
1507 }
1508 return 0;
1509}
1510
1511/* Bear in mind that this function is really rather inefficient. */
1512static int solve_island_stage3(struct island *is, int *didsth_r)
1513{
1514 int i, n, x, y, missing, spc, curr, maxb, didsth = 0;
1515 int wh = is->state->w * is->state->h;
1516 struct solver_state *ss = is->state->solver;
1517
1518 assert(didsth_r);
1519
1520 missing = is->count - island_countbridges(is);
1521 if (missing <= 0) return 1;
1522
1523 for (i = 0; i < is->adj.npoints; i++) {
1524 x = is->adj.points[i].x;
1525 y = is->adj.points[i].y;
1526 spc = island_adjspace(is, 1, missing, i);
1527 if (spc == 0) continue;
1528
1529 curr = GRIDCOUNT(is->state, x, y,
1530 is->adj.points[i].dx ? G_LINEH : G_LINEV);
1531 debug(("island at (%d,%d) s3, trying %d - %d bridges.\n",
1532 is->x, is->y, curr+1, curr+spc));
1533
1534 /* Now we know that this island could have more bridges,
1535 * to bring the total from curr+1 to curr+spc. */
1536 maxb = -1;
1537 /* We have to squirrel the dsf away and restore it afterwards;
1538 * it is additive only, and can't be removed from. */
1539 memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int));
1540 for (n = curr+1; n <= curr+spc; n++) {
1541 solve_join(is, i, n, 0);
1542 map_update_possibles(is->state);
1543
1544 if (solve_island_subgroup(is, i) ||
1545 solve_island_impossible(is->state)) {
1546 maxb = n-1;
1547 debug(("island at (%d,%d) d(%d,%d) new max of %d bridges:\n",
1548 is->x, is->y,
1549 is->adj.points[i].dx, is->adj.points[i].dy,
1550 maxb));
1551 break;
1552 }
1553 }
1554 solve_join(is, i, curr, 0); /* put back to before. */
1555 memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int));
1556
1557 if (maxb != -1) {
1558 /*debug_state(is->state);*/
1559 if (maxb == 0) {
1560 debug(("...adding NOLINE.\n"));
1561 solve_join(is, i, -1, 0); /* we can't have any bridges here. */
1562 } else {
1563 debug(("...setting maximum\n"));
1564 solve_join(is, i, maxb, 1);
1565 }
1566 didsth = 1;
1567 }
1568 map_update_possibles(is->state);
1569 }
1570
1571 for (i = 0; i < is->adj.npoints; i++) {
1572 /*
1573 * Now check to see if any currently empty direction must have
1574 * at least one bridge in order to avoid forming an isolated
1575 * subgraph. This differs from the check above in that it
1576 * considers multiple target islands. For example:
1577 *
1578 * 2 2 4
1579 * 1 3 2
1580 * 3
1581 * 4
1582 *
1583 * The example on the left can be handled by the above loop:
1584 * it will observe that connecting the central 2 twice to the
1585 * left would form an isolated subgraph, and hence it will
1586 * restrict that 2 to at most one bridge in that direction.
1587 * But the example on the right won't be handled by that loop,
1588 * because the deduction requires us to imagine connecting the
1589 * 3 to _both_ the 1 and 2 at once to form an isolated
1590 * subgraph.
1591 *
1592 * This pass is necessary _as well_ as the above one, because
1593 * neither can do the other's job. In the left one,
1594 * restricting the direction which _would_ cause trouble can
1595 * be done even if it's not yet clear which of the remaining
1596 * directions has to have a compensatory bridge; whereas the
1597 * pass below that can handle the right-hand example does need
1598 * to know what direction to point the necessary bridge in.
1599 *
1600 * Neither pass can handle the most general case, in which we
1601 * observe that an arbitrary subset of an island's neighbours
1602 * would form an isolated subgraph with it if it connected
1603 * maximally to them, and hence that at least one bridge must
1604 * point to some neighbour outside that subset but we don't
1605 * know which neighbour. To handle that, we'd have to have a
1606 * richer data format for the solver, which could cope with
1607 * recording the idea that at least one of two edges must have
1608 * a bridge.
1609 */
1610 int got = 0;
1611 int before[4];
1612 int j;
1613
1614 spc = island_adjspace(is, 1, missing, i);
1615 if (spc == 0) continue;
1616
1617 for (j = 0; j < is->adj.npoints; j++)
1618 before[j] = GRIDCOUNT(is->state,
1619 is->adj.points[j].x,
1620 is->adj.points[j].y,
1621 is->adj.points[j].dx ? G_LINEH : G_LINEV);
1622 if (before[i] != 0) continue; /* this idea is pointless otherwise */
1623
1624 memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int));
1625
1626 for (j = 0; j < is->adj.npoints; j++) {
1627 spc = island_adjspace(is, 1, missing, j);
1628 if (spc == 0) continue;
1629 if (j == i) continue;
1630 solve_join(is, j, before[j] + spc, 0);
1631 }
1632 map_update_possibles(is->state);
1633
1634 if (solve_island_subgroup(is, -1))
1635 got = 1;
1636
1637 for (j = 0; j < is->adj.npoints; j++)
1638 solve_join(is, j, before[j], 0);
1639 memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int));
1640
1641 if (got) {
1642 debug(("island at (%d,%d) must connect in direction (%d,%d) to"
1643 " avoid full subgroup.\n",
1644 is->x, is->y, is->adj.points[i].dx, is->adj.points[i].dy));
1645 solve_join(is, i, 1, 0);
1646 didsth = 1;
1647 }
1648
1649 map_update_possibles(is->state);
1650 }
1651
1652 if (didsth) *didsth_r = didsth;
1653 return 1;
1654}
1655
1656#define CONTINUE_IF_FULL do { \
1657if (GRID(state, is->x, is->y) & G_MARK) { \
1658 /* island full, don't try fixing it */ \
1659 continue; \
1660} } while(0)
1661
1662static int solve_sub(game_state *state, int difficulty, int depth)
1663{
1664 struct island *is;
1665 int i, didsth;
1666
1667 while (1) {
1668 didsth = 0;
1669
1670 /* First island iteration: things we can work out by looking at
1671 * properties of the island as a whole. */
1672 for (i = 0; i < state->n_islands; i++) {
1673 is = &state->islands[i];
1674 if (!solve_island_stage1(is, &didsth)) return 0;
1675 }
1676 if (didsth) continue;
1677 else if (difficulty < 1) break;
1678
1679 /* Second island iteration: thing we can work out by looking at
1680 * properties of individual island connections. */
1681 for (i = 0; i < state->n_islands; i++) {
1682 is = &state->islands[i];
1683 CONTINUE_IF_FULL;
1684 if (!solve_island_stage2(is, &didsth)) return 0;
1685 }
1686 if (didsth) continue;
1687 else if (difficulty < 2) break;
1688
1689 /* Third island iteration: things we can only work out by looking
1690 * at groups of islands. */
1691 for (i = 0; i < state->n_islands; i++) {
1692 is = &state->islands[i];
1693 if (!solve_island_stage3(is, &didsth)) return 0;
1694 }
1695 if (didsth) continue;
1696 else if (difficulty < 3) break;
1697
1698 /* If we can be bothered, write a recursive solver to finish here. */
1699 break;
1700 }
1701 if (map_check(state)) return 1; /* solved it */
1702 return 0;
1703}
1704
1705static void solve_for_hint(game_state *state)
1706{
1707 map_group(state);
1708 solve_sub(state, 10, 0);
1709}
1710
1711static int solve_from_scratch(game_state *state, int difficulty)
1712{
1713 map_clear(state);
1714 map_group(state);
1715 map_update_possibles(state);
1716 return solve_sub(state, difficulty, 0);
1717}
1718
1719/* --- New game functions --- */
1720
1721static game_state *new_state(const game_params *params)
1722{
1723 game_state *ret = snew(game_state);
1724 int wh = params->w * params->h, i;
1725
1726 ret->w = params->w;
1727 ret->h = params->h;
1728 ret->allowloops = params->allowloops;
1729 ret->maxb = params->maxb;
1730 ret->params = *params;
1731
1732 ret->grid = snewn(wh, grid_type);
1733 memset(ret->grid, 0, GRIDSZ(ret));
1734
1735 ret->wha = snewn(wh*N_WH_ARRAYS, char);
1736 memset(ret->wha, 0, wh*N_WH_ARRAYS*sizeof(char));
1737
1738 ret->possv = ret->wha;
1739 ret->possh = ret->wha + wh;
1740 ret->lines = ret->wha + wh*2;
1741 ret->maxv = ret->wha + wh*3;
1742 ret->maxh = ret->wha + wh*4;
1743
1744 memset(ret->maxv, ret->maxb, wh*sizeof(char));
1745 memset(ret->maxh, ret->maxb, wh*sizeof(char));
1746
1747 ret->islands = NULL;
1748 ret->n_islands = 0;
1749 ret->n_islands_alloc = 0;
1750
1751 ret->gridi = snewn(wh, struct island *);
1752 for (i = 0; i < wh; i++) ret->gridi[i] = NULL;
1753
1754 ret->solved = ret->completed = 0;
1755
1756 ret->solver = snew(struct solver_state);
1757 ret->solver->dsf = snew_dsf(wh);
1758 ret->solver->tmpdsf = snewn(wh, int);
1759
1760 ret->solver->refcount = 1;
1761
1762 return ret;
1763}
1764
1765static game_state *dup_game(const game_state *state)
1766{
1767 game_state *ret = snew(game_state);
1768 int wh = state->w*state->h;
1769
1770 ret->w = state->w;
1771 ret->h = state->h;
1772 ret->allowloops = state->allowloops;
1773 ret->maxb = state->maxb;
1774 ret->params = state->params;
1775
1776 ret->grid = snewn(wh, grid_type);
1777 memcpy(ret->grid, state->grid, GRIDSZ(ret));
1778
1779 ret->wha = snewn(wh*N_WH_ARRAYS, char);
1780 memcpy(ret->wha, state->wha, wh*N_WH_ARRAYS*sizeof(char));
1781
1782 ret->possv = ret->wha;
1783 ret->possh = ret->wha + wh;
1784 ret->lines = ret->wha + wh*2;
1785 ret->maxv = ret->wha + wh*3;
1786 ret->maxh = ret->wha + wh*4;
1787
1788 ret->islands = snewn(state->n_islands, struct island);
1789 memcpy(ret->islands, state->islands, state->n_islands * sizeof(struct island));
1790 ret->n_islands = ret->n_islands_alloc = state->n_islands;
1791
1792 ret->gridi = snewn(wh, struct island *);
1793 fixup_islands_for_realloc(ret);
1794
1795 ret->solved = state->solved;
1796 ret->completed = state->completed;
1797
1798 ret->solver = state->solver;
1799 ret->solver->refcount++;
1800
1801 return ret;
1802}
1803
1804static void free_game(game_state *state)
1805{
1806 if (--state->solver->refcount <= 0) {
1807 sfree(state->solver->dsf);
1808 sfree(state->solver->tmpdsf);
1809 sfree(state->solver);
1810 }
1811
1812 sfree(state->islands);
1813 sfree(state->gridi);
1814
1815 sfree(state->wha);
1816
1817 sfree(state->grid);
1818 sfree(state);
1819}
1820
1821#define MAX_NEWISLAND_TRIES 50
1822#define MIN_SENSIBLE_ISLANDS 3
1823
1824#define ORDER(a,b) do { if (a < b) { int tmp=a; int a=b; int b=tmp; } } while(0)
1825
1826static char *new_game_desc(const game_params *params, random_state *rs,
1827 char **aux, int interactive)
1828{
1829 game_state *tobuild = NULL;
1830 int i, j, wh = params->w * params->h, x, y, dx, dy;
1831 int minx, miny, maxx, maxy, joinx, joiny, newx, newy, diffx, diffy;
1832 int ni_req = max((params->islands * wh) / 100, MIN_SENSIBLE_ISLANDS), ni_curr, ni_bad;
1833 struct island *is, *is2;
1834 char *ret;
1835 unsigned int echeck;
1836
1837 /* pick a first island position randomly. */
1838generate:
1839 if (tobuild) free_game(tobuild);
1840 tobuild = new_state(params);
1841
1842 x = random_upto(rs, params->w);
1843 y = random_upto(rs, params->h);
1844 island_add(tobuild, x, y, 0);
1845 ni_curr = 1;
1846 ni_bad = 0;
1847 debug(("Created initial island at (%d,%d).\n", x, y));
1848
1849 while (ni_curr < ni_req) {
1850 /* Pick a random island to try and extend from. */
1851 i = random_upto(rs, tobuild->n_islands);
1852 is = &tobuild->islands[i];
1853
1854 /* Pick a random direction to extend in. */
1855 j = random_upto(rs, is->adj.npoints);
1856 dx = is->adj.points[j].x - is->x;
1857 dy = is->adj.points[j].y - is->y;
1858
1859 /* Find out limits of where we could put a new island. */
1860 joinx = joiny = -1;
1861 minx = is->x + 2*dx; miny = is->y + 2*dy; /* closest is 2 units away. */
1862 x = is->x+dx; y = is->y+dy;
1863 if (GRID(tobuild,x,y) & (G_LINEV|G_LINEH)) {
1864 /* already a line next to the island, continue. */
1865 goto bad;
1866 }
1867 while (1) {
1868 if (x < 0 || x >= params->w || y < 0 || y >= params->h) {
1869 /* got past the edge; put a possible at the island
1870 * and exit. */
1871 maxx = x-dx; maxy = y-dy;
1872 goto foundmax;
1873 }
1874 if (GRID(tobuild,x,y) & G_ISLAND) {
1875 /* could join up to an existing island... */
1876 joinx = x; joiny = y;
1877 /* ... or make a new one 2 spaces away. */
1878 maxx = x - 2*dx; maxy = y - 2*dy;
1879 goto foundmax;
1880 } else if (GRID(tobuild,x,y) & (G_LINEV|G_LINEH)) {
1881 /* could make a new one 1 space away from the line. */
1882 maxx = x - dx; maxy = y - dy;
1883 goto foundmax;
1884 }
1885 x += dx; y += dy;
1886 }
1887
1888foundmax:
1889 debug(("Island at (%d,%d) with d(%d,%d) has new positions "
1890 "(%d,%d) -> (%d,%d), join (%d,%d).\n",
1891 is->x, is->y, dx, dy, minx, miny, maxx, maxy, joinx, joiny));
1892 /* Now we know where we could either put a new island
1893 * (between min and max), or (if loops are allowed) could join on
1894 * to an existing island (at join). */
1895 if (params->allowloops && joinx != -1 && joiny != -1) {
1896 if (random_upto(rs, 100) < (unsigned long)params->expansion) {
1897 is2 = INDEX(tobuild, gridi, joinx, joiny);
1898 debug(("Joining island at (%d,%d) to (%d,%d).\n",
1899 is->x, is->y, is2->x, is2->y));
1900 goto join;
1901 }
1902 }
1903 diffx = (maxx - minx) * dx;
1904 diffy = (maxy - miny) * dy;
1905 if (diffx < 0 || diffy < 0) goto bad;
1906 if (random_upto(rs,100) < (unsigned long)params->expansion) {
1907 newx = maxx; newy = maxy;
1908 debug(("Creating new island at (%d,%d) (expanded).\n", newx, newy));
1909 } else {
1910 newx = minx + random_upto(rs,diffx+1)*dx;
1911 newy = miny + random_upto(rs,diffy+1)*dy;
1912 debug(("Creating new island at (%d,%d).\n", newx, newy));
1913 }
1914 /* check we're not next to island in the other orthogonal direction. */
1915 if ((INGRID(tobuild,newx+dy,newy+dx) && (GRID(tobuild,newx+dy,newy+dx) & G_ISLAND)) ||
1916 (INGRID(tobuild,newx-dy,newy-dx) && (GRID(tobuild,newx-dy,newy-dx) & G_ISLAND))) {
1917 debug(("New location is adjacent to island, skipping.\n"));
1918 goto bad;
1919 }
1920 is2 = island_add(tobuild, newx, newy, 0);
1921 /* Must get is again at this point; the array might have
1922 * been realloced by island_add... */
1923 is = &tobuild->islands[i]; /* ...but order will not change. */
1924
1925 ni_curr++; ni_bad = 0;
1926join:
1927 island_join(is, is2, random_upto(rs, tobuild->maxb)+1, 0);
1928 debug_state(tobuild);
1929 continue;
1930
1931bad:
1932 ni_bad++;
1933 if (ni_bad > MAX_NEWISLAND_TRIES) {
1934 debug(("Unable to create any new islands after %d tries; "
1935 "created %d [%d%%] (instead of %d [%d%%] requested).\n",
1936 MAX_NEWISLAND_TRIES,
1937 ni_curr, ni_curr * 100 / wh,
1938 ni_req, ni_req * 100 / wh));
1939 goto generated;
1940 }
1941 }
1942
1943generated:
1944 if (ni_curr == 1) {
1945 debug(("Only generated one island (!), retrying.\n"));
1946 goto generate;
1947 }
1948 /* Check we have at least one island on each extremity of the grid. */
1949 echeck = 0;
1950 for (x = 0; x < params->w; x++) {
1951 if (INDEX(tobuild, gridi, x, 0)) echeck |= 1;
1952 if (INDEX(tobuild, gridi, x, params->h-1)) echeck |= 2;
1953 }
1954 for (y = 0; y < params->h; y++) {
1955 if (INDEX(tobuild, gridi, 0, y)) echeck |= 4;
1956 if (INDEX(tobuild, gridi, params->w-1, y)) echeck |= 8;
1957 }
1958 if (echeck != 15) {
1959 debug(("Generated grid doesn't fill to sides, retrying.\n"));
1960 goto generate;
1961 }
1962
1963 map_count(tobuild);
1964 map_find_orthogonal(tobuild);
1965
1966 if (params->difficulty > 0) {
1967 if ((ni_curr > MIN_SENSIBLE_ISLANDS) &&
1968 (solve_from_scratch(tobuild, params->difficulty-1) > 0)) {
1969 debug(("Grid is solvable at difficulty %d (too easy); retrying.\n",
1970 params->difficulty-1));
1971 goto generate;
1972 }
1973 }
1974
1975 if (solve_from_scratch(tobuild, params->difficulty) == 0) {
1976 debug(("Grid not solvable at difficulty %d, (too hard); retrying.\n",
1977 params->difficulty));
1978 goto generate;
1979 }
1980
1981 /* ... tobuild is now solved. We rely on this making the diff for aux. */
1982 debug_state(tobuild);
1983 ret = encode_game(tobuild);
1984 {
1985 game_state *clean = dup_game(tobuild);
1986 map_clear(clean);
1987 map_update_possibles(clean);
1988 *aux = game_state_diff(clean, tobuild);
1989 free_game(clean);
1990 }
1991 free_game(tobuild);
1992
1993 return ret;
1994}
1995
1996static char *validate_desc(const game_params *params, const char *desc)
1997{
1998 int i, wh = params->w * params->h;
1999
2000 for (i = 0; i < wh; i++) {
2001 if (*desc >= '1' && *desc <= '9')
2002 /* OK */;
2003 else if (*desc >= 'a' && *desc <= 'z')
2004 i += *desc - 'a'; /* plus the i++ */
2005 else if (*desc >= 'A' && *desc <= 'G')
2006 /* OK */;
2007 else if (*desc == 'V' || *desc == 'W' ||
2008 *desc == 'X' || *desc == 'Y' ||
2009 *desc == 'H' || *desc == 'I' ||
2010 *desc == 'J' || *desc == 'K')
2011 /* OK */;
2012 else if (!*desc)
2013 return "Game description shorter than expected";
2014 else
2015 return "Game description contains unexpected character";
2016 desc++;
2017 }
2018 if (*desc || i > wh)
2019 return "Game description longer than expected";
2020
2021 return NULL;
2022}
2023
2024static game_state *new_game_sub(const game_params *params, const char *desc)
2025{
2026 game_state *state = new_state(params);
2027 int x, y, run = 0;
2028
2029 debug(("new_game[_sub]: desc = '%s'.\n", desc));
2030
2031 for (y = 0; y < params->h; y++) {
2032 for (x = 0; x < params->w; x++) {
2033 char c = '\0';
2034
2035 if (run == 0) {
2036 c = *desc++;
2037 assert(c != 'S');
2038 if (c >= 'a' && c <= 'z')
2039 run = c - 'a' + 1;
2040 }
2041
2042 if (run > 0) {
2043 c = 'S';
2044 run--;
2045 }
2046
2047 switch (c) {
2048 case '1': case '2': case '3': case '4':
2049 case '5': case '6': case '7': case '8': case '9':
2050 island_add(state, x, y, (c - '0'));
2051 break;
2052
2053 case 'A': case 'B': case 'C': case 'D':
2054 case 'E': case 'F': case 'G':
2055 island_add(state, x, y, (c - 'A') + 10);
2056 break;
2057
2058 case 'S':
2059 /* empty square */
2060 break;
2061
2062 default:
2063 assert(!"Malformed desc.");
2064 break;
2065 }
2066 }
2067 }
2068 if (*desc) assert(!"Over-long desc.");
2069
2070 map_find_orthogonal(state);
2071 map_update_possibles(state);
2072
2073 return state;
2074}
2075
2076static game_state *new_game(midend *me, const game_params *params,
2077 const char *desc)
2078{
2079 return new_game_sub(params, desc);
2080}
2081
2082struct game_ui {
2083 int dragx_src, dragy_src; /* source; -1 means no drag */
2084 int dragx_dst, dragy_dst; /* src's closest orth island. */
2085 grid_type todraw;
2086 int dragging, drag_is_noline, nlines;
2087
2088 int cur_x, cur_y, cur_visible; /* cursor position */
2089 int show_hints;
2090};
2091
2092static char *ui_cancel_drag(game_ui *ui)
2093{
2094 ui->dragx_src = ui->dragy_src = -1;
2095 ui->dragx_dst = ui->dragy_dst = -1;
2096 ui->dragging = 0;
2097 return "";
2098}
2099
2100static game_ui *new_ui(const game_state *state)
2101{
2102 game_ui *ui = snew(game_ui);
2103 ui_cancel_drag(ui);
2104 ui->cur_x = state->islands[0].x;
2105 ui->cur_y = state->islands[0].y;
2106 ui->cur_visible = 0;
2107 ui->show_hints = 0;
2108 return ui;
2109}
2110
2111static void free_ui(game_ui *ui)
2112{
2113 sfree(ui);
2114}
2115
2116static char *encode_ui(const game_ui *ui)
2117{
2118 return NULL;
2119}
2120
2121static void decode_ui(game_ui *ui, const char *encoding)
2122{
2123}
2124
2125static void game_changed_state(game_ui *ui, const game_state *oldstate,
2126 const game_state *newstate)
2127{
2128}
2129
2130struct game_drawstate {
2131 int tilesize;
2132 int w, h;
2133 unsigned long *grid, *newgrid;
2134 int *lv, *lh;
2135 int started, dragging;
2136};
2137
2138/*
2139 * The contents of ds->grid are complicated, because of the circular
2140 * islands which overlap their own grid square into neighbouring
2141 * squares. An island square can contain pieces of the bridges in all
2142 * directions, and conversely a bridge square can be intruded on by
2143 * islands from any direction.
2144 *
2145 * So we define one group of flags describing what's important about
2146 * an island, and another describing a bridge. Island squares' entries
2147 * in ds->grid contain one of the former and four of the latter; bridge
2148 * squares, four of the former and _two_ of the latter - because a
2149 * horizontal and vertical 'bridge' can cross, when one of them is a
2150 * 'no bridge here' pencil mark.
2151 *
2152 * Bridge flags need to indicate 0-4 actual bridges (3 bits), a 'no
2153 * bridge' row of crosses, or a grey hint line; that's 7
2154 * possibilities, so 3 bits suffice. But then we also need to vary the
2155 * colours: the bridges can turn COL_WARNING if they're part of a loop
2156 * in no-loops mode, COL_HIGHLIGHT during a victory flash, or
2157 * COL_SELECTED if they're the bridge the user is currently dragging,
2158 * so that's 2 more bits for foreground colour. Also bridges can be
2159 * backed by COL_MARK if they're locked by the user, so that's one
2160 * more bit, making 6 bits per bridge direction.
2161 *
2162 * Island flags omit the actual island clue (it never changes during
2163 * the game, so doesn't have to be stored in ds->grid to check against
2164 * the previous version), so they just need to include 2 bits for
2165 * foreground colour (an island can be normal, COL_HIGHLIGHT during
2166 * victory, COL_WARNING if its clue is unsatisfiable, or COL_SELECTED
2167 * if it's part of the user's drag) and 2 bits for background (normal,
2168 * COL_MARK for a locked island, COL_CURSOR for the keyboard cursor).
2169 * That's 4 bits per island direction. We must also indicate whether
2170 * no island is present at all (in the case where the island is
2171 * potentially intruding into the side of a line square), which we do
2172 * using the unused 4th value of the background field.
2173 *
2174 * So an island square needs 4 + 4*6 = 28 bits, while a bridge square
2175 * needs 4*4 + 2*6 = 28 bits too. Both only just fit in 32 bits, which
2176 * is handy, because otherwise we'd have to faff around forever with
2177 * little structs!
2178 */
2179/* Flags for line data */
2180#define DL_COUNTMASK 0x07
2181#define DL_COUNT_CROSS 0x06
2182#define DL_COUNT_HINT 0x07
2183#define DL_COLMASK 0x18
2184#define DL_COL_NORMAL 0x00
2185#define DL_COL_WARNING 0x08
2186#define DL_COL_FLASH 0x10
2187#define DL_COL_SELECTED 0x18
2188#define DL_LOCK 0x20
2189#define DL_MASK 0x3F
2190/* Flags for island data */
2191#define DI_COLMASK 0x03
2192#define DI_COL_NORMAL 0x00
2193#define DI_COL_FLASH 0x01
2194#define DI_COL_WARNING 0x02
2195#define DI_COL_SELECTED 0x03
2196#define DI_BGMASK 0x0C
2197#define DI_BG_NO_ISLAND 0x00
2198#define DI_BG_NORMAL 0x04
2199#define DI_BG_MARK 0x08
2200#define DI_BG_CURSOR 0x0C
2201#define DI_MASK 0x0F
2202/* Shift counts for the format of a 32-bit word in an island square */
2203#define D_I_ISLAND_SHIFT 0
2204#define D_I_LINE_SHIFT_L 4
2205#define D_I_LINE_SHIFT_R 10
2206#define D_I_LINE_SHIFT_U 16
2207#define D_I_LINE_SHIFT_D 24
2208/* Shift counts for the format of a 32-bit word in a line square */
2209#define D_L_ISLAND_SHIFT_L 0
2210#define D_L_ISLAND_SHIFT_R 4
2211#define D_L_ISLAND_SHIFT_U 8
2212#define D_L_ISLAND_SHIFT_D 12
2213#define D_L_LINE_SHIFT_H 16
2214#define D_L_LINE_SHIFT_V 22
2215
2216static char *update_drag_dst(const game_state *state, game_ui *ui,
2217 const game_drawstate *ds, int nx, int ny)
2218{
2219 int ox, oy, dx, dy, i, currl, maxb;
2220 struct island *is;
2221 grid_type gtype, ntype, mtype, curr;
2222
2223 if (ui->dragx_src == -1 || ui->dragy_src == -1) return NULL;
2224
2225 ui->dragx_dst = -1;
2226 ui->dragy_dst = -1;
2227
2228 /* work out which of the four directions we're closest to... */
2229 ox = COORD(ui->dragx_src) + TILE_SIZE/2;
2230 oy = COORD(ui->dragy_src) + TILE_SIZE/2;
2231
2232 if (abs(nx-ox) < abs(ny-oy)) {
2233 dx = 0;
2234 dy = (ny-oy) < 0 ? -1 : 1;
2235 gtype = G_LINEV; ntype = G_NOLINEV; mtype = G_MARKV;
2236 maxb = INDEX(state, maxv, ui->dragx_src+dx, ui->dragy_src+dy);
2237 } else {
2238 dy = 0;
2239 dx = (nx-ox) < 0 ? -1 : 1;
2240 gtype = G_LINEH; ntype = G_NOLINEH; mtype = G_MARKH;
2241 maxb = INDEX(state, maxh, ui->dragx_src+dx, ui->dragy_src+dy);
2242 }
2243 if (ui->drag_is_noline) {
2244 ui->todraw = ntype;
2245 } else {
2246 curr = GRID(state, ui->dragx_src+dx, ui->dragy_src+dy);
2247 currl = INDEX(state, lines, ui->dragx_src+dx, ui->dragy_src+dy);
2248
2249 if (curr & gtype) {
2250 if (currl == maxb) {
2251 ui->todraw = 0;
2252 ui->nlines = 0;
2253 } else {
2254 ui->todraw = gtype;
2255 ui->nlines = currl + 1;
2256 }
2257 } else {
2258 ui->todraw = gtype;
2259 ui->nlines = 1;
2260 }
2261 }
2262
2263 /* ... and see if there's an island off in that direction. */
2264 is = INDEX(state, gridi, ui->dragx_src, ui->dragy_src);
2265 for (i = 0; i < is->adj.npoints; i++) {
2266 if (is->adj.points[i].off == 0) continue;
2267 curr = GRID(state, is->x+dx, is->y+dy);
2268 if (curr & mtype) continue; /* don't allow changes to marked lines. */
2269 if (ui->drag_is_noline) {
2270 if (curr & gtype) continue; /* no no-line where already a line */
2271 } else {
2272 if (POSSIBLES(state, dx, is->x+dx, is->y+dy) == 0) continue; /* no line if !possible. */
2273 if (curr & ntype) continue; /* can't have a bridge where there's a no-line. */
2274 }
2275
2276 if (is->adj.points[i].dx == dx &&
2277 is->adj.points[i].dy == dy) {
2278 ui->dragx_dst = ISLAND_ORTHX(is,i);
2279 ui->dragy_dst = ISLAND_ORTHY(is,i);
2280 }
2281 }
2282 /*debug(("update_drag src (%d,%d) d(%d,%d) dst (%d,%d)\n",
2283 ui->dragx_src, ui->dragy_src, dx, dy,
2284 ui->dragx_dst, ui->dragy_dst));*/
2285 return "";
2286}
2287
2288static char *finish_drag(const game_state *state, game_ui *ui)
2289{
2290 char buf[80];
2291
2292 if (ui->dragx_src == -1 || ui->dragy_src == -1)
2293 return NULL;
2294 if (ui->dragx_dst == -1 || ui->dragy_dst == -1)
2295 return ui_cancel_drag(ui);
2296
2297 if (ui->drag_is_noline) {
2298 sprintf(buf, "N%d,%d,%d,%d",
2299 ui->dragx_src, ui->dragy_src,
2300 ui->dragx_dst, ui->dragy_dst);
2301 } else {
2302 sprintf(buf, "L%d,%d,%d,%d,%d",
2303 ui->dragx_src, ui->dragy_src,
2304 ui->dragx_dst, ui->dragy_dst, ui->nlines);
2305 }
2306
2307 ui_cancel_drag(ui);
2308
2309 return dupstr(buf);
2310}
2311
2312static char *interpret_move(const game_state *state, game_ui *ui,
2313 const game_drawstate *ds,
2314 int x, int y, int button)
2315{
2316 int gx = FROMCOORD(x), gy = FROMCOORD(y);
2317 char buf[80], *ret;
2318 grid_type ggrid = INGRID(state,gx,gy) ? GRID(state,gx,gy) : 0;
2319 int shift = button & MOD_SHFT, control = button & MOD_CTRL;
2320 button &= ~MOD_MASK;
2321
2322 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2323 if (!INGRID(state, gx, gy)) return NULL;
2324 ui->cur_visible = 0;
2325 if (ggrid & G_ISLAND) {
2326 ui->dragx_src = gx;
2327 ui->dragy_src = gy;
2328 return "";
2329 } else
2330 return ui_cancel_drag(ui);
2331 } else if (button == LEFT_DRAG || button == RIGHT_DRAG) {
2332 if (INGRID(state, ui->dragx_src, ui->dragy_src)
2333 && (gx != ui->dragx_src || gy != ui->dragy_src)
2334 && !(GRID(state,ui->dragx_src,ui->dragy_src) & G_MARK)) {
2335 ui->dragging = 1;
2336 ui->drag_is_noline = (button == RIGHT_DRAG) ? 1 : 0;
2337 return update_drag_dst(state, ui, ds, x, y);
2338 } else {
2339 /* cancel a drag when we go back to the starting point */
2340 ui->dragx_dst = -1;
2341 ui->dragy_dst = -1;
2342 return "";
2343 }
2344 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) {
2345 if (ui->dragging) {
2346 return finish_drag(state, ui);
2347 } else {
2348 if (!INGRID(state, ui->dragx_src, ui->dragy_src)
2349 || gx != ui->dragx_src || gy != ui->dragy_src) {
2350 return ui_cancel_drag(ui);
2351 }
2352 ui_cancel_drag(ui);
2353 if (!INGRID(state, gx, gy)) return NULL;
2354 if (!(GRID(state, gx, gy) & G_ISLAND)) return NULL;
2355 sprintf(buf, "M%d,%d", gx, gy);
2356 return dupstr(buf);
2357 }
2358 } else if (button == 'h' || button == 'H') {
2359 game_state *solved = dup_game(state);
2360 solve_for_hint(solved);
2361 ret = game_state_diff(state, solved);
2362 free_game(solved);
2363 return ret;
2364 } else if (IS_CURSOR_MOVE(button)) {
2365 ui->cur_visible = 1;
2366 if (control || shift) {
2367 ui->dragx_src = ui->cur_x;
2368 ui->dragy_src = ui->cur_y;
2369 ui->dragging = TRUE;
2370 ui->drag_is_noline = !control;
2371 }
2372 if (ui->dragging) {
2373 int nx = ui->cur_x, ny = ui->cur_y;
2374
2375 move_cursor(button, &nx, &ny, state->w, state->h, 0);
2376 if (nx == ui->cur_x && ny == ui->cur_y)
2377 return NULL;
2378 update_drag_dst(state, ui, ds,
2379 COORD(nx)+TILE_SIZE/2,
2380 COORD(ny)+TILE_SIZE/2);
2381 return finish_drag(state, ui);
2382 } else {
2383 int dx = (button == CURSOR_RIGHT) ? +1 : (button == CURSOR_LEFT) ? -1 : 0;
2384 int dy = (button == CURSOR_DOWN) ? +1 : (button == CURSOR_UP) ? -1 : 0;
2385 int dorthx = 1 - abs(dx), dorthy = 1 - abs(dy);
2386 int dir, orth, nx = x, ny = y;
2387
2388 /* 'orthorder' is a tweak to ensure that if you press RIGHT and
2389 * happen to move upwards, when you press LEFT you then tend
2390 * downwards (rather than upwards again). */
2391 int orthorder = (button == CURSOR_LEFT || button == CURSOR_UP) ? 1 : -1;
2392
2393 /* This attempts to find an island in the direction you're
2394 * asking for, broadly speaking. If you ask to go right, for
2395 * example, it'll look for islands to the right and slightly
2396 * above or below your current horiz. position, allowing
2397 * further above/below the further away it searches. */
2398
2399 assert(GRID(state, ui->cur_x, ui->cur_y) & G_ISLAND);
2400 /* currently this is depth-first (so orthogonally-adjacent
2401 * islands across the other side of the grid will be moved to
2402 * before closer islands slightly offset). Swap the order of
2403 * these two loops to change to breadth-first search. */
2404 for (orth = 0; ; orth++) {
2405 int oingrid = 0;
2406 for (dir = 1; ; dir++) {
2407 int dingrid = 0;
2408
2409 if (orth > dir) continue; /* only search in cone outwards. */
2410
2411 nx = ui->cur_x + dir*dx + orth*dorthx*orthorder;
2412 ny = ui->cur_y + dir*dy + orth*dorthy*orthorder;
2413 if (INGRID(state, nx, ny)) {
2414 dingrid = oingrid = 1;
2415 if (GRID(state, nx, ny) & G_ISLAND) goto found;
2416 }
2417
2418 nx = ui->cur_x + dir*dx - orth*dorthx*orthorder;
2419 ny = ui->cur_y + dir*dy - orth*dorthy*orthorder;
2420 if (INGRID(state, nx, ny)) {
2421 dingrid = oingrid = 1;
2422 if (GRID(state, nx, ny) & G_ISLAND) goto found;
2423 }
2424
2425 if (!dingrid) break;
2426 }
2427 if (!oingrid) return "";
2428 }
2429 /* not reached */
2430
2431found:
2432 ui->cur_x = nx;
2433 ui->cur_y = ny;
2434 return "";
2435 }
2436 } else if (IS_CURSOR_SELECT(button)) {
2437 if (!ui->cur_visible) {
2438 ui->cur_visible = 1;
2439 return "";
2440 }
2441 if (ui->dragging || button == CURSOR_SELECT2) {
2442 ui_cancel_drag(ui);
2443 if (ui->dragx_dst == -1 && ui->dragy_dst == -1) {
2444 sprintf(buf, "M%d,%d", ui->cur_x, ui->cur_y);
2445 return dupstr(buf);
2446 } else
2447 return "";
2448 } else {
2449 grid_type v = GRID(state, ui->cur_x, ui->cur_y);
2450 if (v & G_ISLAND) {
2451 ui->dragging = 1;
2452 ui->dragx_src = ui->cur_x;
2453 ui->dragy_src = ui->cur_y;
2454 ui->dragx_dst = ui->dragy_dst = -1;
2455 ui->drag_is_noline = (button == CURSOR_SELECT2) ? 1 : 0;
2456 return "";
2457 }
2458 }
2459 } else if ((button >= '0' && button <= '9') ||
2460 (button >= 'a' && button <= 'f') ||
2461 (button >= 'A' && button <= 'F')) {
2462 /* jump to island with .count == number closest to cur_{x,y} */
2463 int best_x = -1, best_y = -1, best_sqdist = -1, number = -1, i;
2464
2465 if (button >= '0' && button <= '9')
2466 number = (button == '0' ? 16 : button - '0');
2467 else if (button >= 'a' && button <= 'f')
2468 number = 10 + button - 'a';
2469 else if (button >= 'A' && button <= 'F')
2470 number = 10 + button - 'A';
2471
2472 if (!ui->cur_visible) {
2473 ui->cur_visible = 1;
2474 return "";
2475 }
2476
2477 for (i = 0; i < state->n_islands; ++i) {
2478 int x = state->islands[i].x, y = state->islands[i].y;
2479 int dx = x - ui->cur_x, dy = y - ui->cur_y;
2480 int sqdist = dx*dx + dy*dy;
2481
2482 if (state->islands[i].count != number)
2483 continue;
2484 if (x == ui->cur_x && y == ui->cur_y)
2485 continue;
2486
2487 /* new_game() reads the islands in row-major order, so by
2488 * breaking ties in favor of `first in state->islands' we
2489 * also break ties by `lexicographically smallest (y, x)'.
2490 * Thus, there's a stable pattern to how ties are broken
2491 * which the user can learn and use to navigate faster. */
2492 if (best_sqdist == -1 || sqdist < best_sqdist) {
2493 best_x = x;
2494 best_y = y;
2495 best_sqdist = sqdist;
2496 }
2497 }
2498 if (best_x != -1 && best_y != -1) {
2499 ui->cur_x = best_x;
2500 ui->cur_y = best_y;
2501 return "";
2502 } else
2503 return NULL;
2504 } else if (button == 'g' || button == 'G') {
2505 ui->show_hints = 1 - ui->show_hints;
2506 return "";
2507 }
2508
2509 return NULL;
2510}
2511
2512static game_state *execute_move(const game_state *state, const char *move)
2513{
2514 game_state *ret = dup_game(state);
2515 int x1, y1, x2, y2, nl, n;
2516 struct island *is1, *is2;
2517 char c;
2518
2519 debug(("execute_move: %s\n", move));
2520
2521 if (!*move) goto badmove;
2522 while (*move) {
2523 c = *move++;
2524 if (c == 'S') {
2525 ret->solved = TRUE;
2526 n = 0;
2527 } else if (c == 'L') {
2528 if (sscanf(move, "%d,%d,%d,%d,%d%n",
2529 &x1, &y1, &x2, &y2, &nl, &n) != 5)
2530 goto badmove;
2531 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2532 goto badmove;
2533 is1 = INDEX(ret, gridi, x1, y1);
2534 is2 = INDEX(ret, gridi, x2, y2);
2535 if (!is1 || !is2) goto badmove;
2536 if (nl < 0 || nl > state->maxb) goto badmove;
2537 island_join(is1, is2, nl, 0);
2538 } else if (c == 'N') {
2539 if (sscanf(move, "%d,%d,%d,%d%n",
2540 &x1, &y1, &x2, &y2, &n) != 4)
2541 goto badmove;
2542 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2543 goto badmove;
2544 is1 = INDEX(ret, gridi, x1, y1);
2545 is2 = INDEX(ret, gridi, x2, y2);
2546 if (!is1 || !is2) goto badmove;
2547 island_join(is1, is2, -1, 0);
2548 } else if (c == 'M') {
2549 if (sscanf(move, "%d,%d%n",
2550 &x1, &y1, &n) != 2)
2551 goto badmove;
2552 if (!INGRID(ret, x1, y1))
2553 goto badmove;
2554 is1 = INDEX(ret, gridi, x1, y1);
2555 if (!is1) goto badmove;
2556 island_togglemark(is1);
2557 } else
2558 goto badmove;
2559
2560 move += n;
2561 if (*move == ';')
2562 move++;
2563 else if (*move) goto badmove;
2564 }
2565
2566 map_update_possibles(ret);
2567 if (map_check(ret)) {
2568 debug(("Game completed.\n"));
2569 ret->completed = 1;
2570 }
2571 return ret;
2572
2573badmove:
2574 debug(("%s: unrecognised move.\n", move));
2575 free_game(ret);
2576 return NULL;
2577}
2578
2579static char *solve_game(const game_state *state, const game_state *currstate,
2580 const char *aux, char **error)
2581{
2582 char *ret;
2583 game_state *solved;
2584
2585 if (aux) {
2586 debug(("solve_game: aux = %s\n", aux));
2587 solved = execute_move(state, aux);
2588 if (!solved) {
2589 *error = "Generated aux string is not a valid move (!).";
2590 return NULL;
2591 }
2592 } else {
2593 solved = dup_game(state);
2594 /* solve with max strength... */
2595 if (solve_from_scratch(solved, 10) == 0) {
2596 free_game(solved);
2597 *error = "Game does not have a (non-recursive) solution.";
2598 return NULL;
2599 }
2600 }
2601 ret = game_state_diff(currstate, solved);
2602 free_game(solved);
2603 debug(("solve_game: ret = %s\n", ret));
2604 return ret;
2605}
2606
2607/* ----------------------------------------------------------------------
2608 * Drawing routines.
2609 */
2610
2611static void game_compute_size(const game_params *params, int tilesize,
2612 int *x, int *y)
2613{
2614 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2615 struct { int tilesize; } ads, *ds = &ads;
2616 ads.tilesize = tilesize;
2617
2618 *x = TILE_SIZE * params->w + 2 * BORDER;
2619 *y = TILE_SIZE * params->h + 2 * BORDER;
2620}
2621
2622static void game_set_size(drawing *dr, game_drawstate *ds,
2623 const game_params *params, int tilesize)
2624{
2625 ds->tilesize = tilesize;
2626}
2627
2628static float *game_colours(frontend *fe, int *ncolours)
2629{
2630 float *ret = snewn(3 * NCOLOURS, float);
2631 int i;
2632
2633 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
2634
2635 for (i = 0; i < 3; i++) {
2636 ret[COL_FOREGROUND * 3 + i] = 0.0F;
2637 ret[COL_HINT * 3 + i] = ret[COL_LOWLIGHT * 3 + i];
2638 ret[COL_GRID * 3 + i] =
2639 (ret[COL_HINT * 3 + i] + ret[COL_BACKGROUND * 3 + i]) * 0.5F;
2640 ret[COL_MARK * 3 + i] = ret[COL_HIGHLIGHT * 3 + i];
2641 }
2642 ret[COL_WARNING * 3 + 0] = 1.0F;
2643 ret[COL_WARNING * 3 + 1] = 0.25F;
2644 ret[COL_WARNING * 3 + 2] = 0.25F;
2645
2646 ret[COL_SELECTED * 3 + 0] = 0.25F;
2647 ret[COL_SELECTED * 3 + 1] = 1.00F;
2648 ret[COL_SELECTED * 3 + 2] = 0.25F;
2649
2650 ret[COL_CURSOR * 3 + 0] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F);
2651 ret[COL_CURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.8F;
2652 ret[COL_CURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.8F;
2653
2654 *ncolours = NCOLOURS;
2655 return ret;
2656}
2657
2658static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2659{
2660 struct game_drawstate *ds = snew(struct game_drawstate);
2661 int wh = state->w*state->h;
2662 int i;
2663
2664 ds->tilesize = 0;
2665 ds->w = state->w;
2666 ds->h = state->h;
2667 ds->started = 0;
2668 ds->dragging = 0;
2669 ds->grid = snewn(wh, unsigned long);
2670 for (i = 0; i < wh; i++)
2671 ds->grid[i] = ~0UL;
2672 ds->newgrid = snewn(wh, unsigned long);
2673 ds->lv = snewn(wh, int);
2674 ds->lh = snewn(wh, int);
2675 memset(ds->lv, 0, wh*sizeof(int));
2676 memset(ds->lh, 0, wh*sizeof(int));
2677
2678 return ds;
2679}
2680
2681static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2682{
2683 sfree(ds->lv);
2684 sfree(ds->lh);
2685 sfree(ds->newgrid);
2686 sfree(ds->grid);
2687 sfree(ds);
2688}
2689
2690#define LINE_WIDTH (TILE_SIZE/8)
2691#define TS8(x) (((x)*TILE_SIZE)/8)
2692
2693#define OFFSET(thing) ((TILE_SIZE/2) - ((thing)/2))
2694
2695static int between_island(const game_state *state, int sx, int sy,
2696 int dx, int dy)
2697{
2698 int x = sx - dx, y = sy - dy;
2699
2700 while (INGRID(state, x, y)) {
2701 if (GRID(state, x, y) & G_ISLAND) goto found;
2702 x -= dx; y -= dy;
2703 }
2704 return 0;
2705found:
2706 x = sx + dx, y = sy + dy;
2707 while (INGRID(state, x, y)) {
2708 if (GRID(state, x, y) & G_ISLAND) return 1;
2709 x += dx; y += dy;
2710 }
2711 return 0;
2712}
2713
2714static void lines_lvlh(const game_state *state, const game_ui *ui,
2715 int x, int y, grid_type v, int *lv_r, int *lh_r)
2716{
2717 int lh = 0, lv = 0;
2718
2719 if (v & G_LINEV) lv = INDEX(state,lines,x,y);
2720 if (v & G_LINEH) lh = INDEX(state,lines,x,y);
2721
2722 if (ui->show_hints) {
2723 if (between_island(state, x, y, 0, 1) && !lv) lv = 1;
2724 if (between_island(state, x, y, 1, 0) && !lh) lh = 1;
2725 }
2726 /*debug(("lvlh: (%d,%d) v 0x%x lv %d lh %d.\n", x, y, v, lv, lh));*/
2727 *lv_r = lv; *lh_r = lh;
2728}
2729
2730static void draw_cross(drawing *dr, game_drawstate *ds,
2731 int ox, int oy, int col)
2732{
2733 int off = TS8(2);
2734 draw_line(dr, ox, oy, ox+off, oy+off, col);
2735 draw_line(dr, ox+off, oy, ox, oy+off, col);
2736}
2737
2738static void draw_general_line(drawing *dr, game_drawstate *ds,
2739 int ox, int oy, int fx, int fy, int ax, int ay,
2740 int len, unsigned long ldata, int which)
2741{
2742 /*
2743 * Draw one direction of lines in a square. To permit the same
2744 * code to handle horizontal and vertical lines, fx,fy are the
2745 * 'forward' direction (along the lines) and ax,ay are the
2746 * 'across' direction.
2747 *
2748 * We draw the white background for a locked bridge if (which &
2749 * 1), and draw the bridges themselves if (which & 2). This
2750 * permits us to get two overlapping locked bridges right without
2751 * one of them erasing part of the other.
2752 */
2753 int fg;
2754
2755 fg = ((ldata & DL_COUNTMASK) == DL_COUNT_HINT ? COL_HINT :
2756 (ldata & DL_COLMASK) == DL_COL_SELECTED ? COL_SELECTED :
2757 (ldata & DL_COLMASK) == DL_COL_FLASH ? COL_HIGHLIGHT :
2758 (ldata & DL_COLMASK) == DL_COL_WARNING ? COL_WARNING :
2759 COL_FOREGROUND);
2760
2761 if ((ldata & DL_COUNTMASK) == DL_COUNT_CROSS) {
2762 draw_cross(dr, ds,
2763 ox + TS8(1)*fx + TS8(3)*ax,
2764 oy + TS8(1)*fy + TS8(3)*ay, fg);
2765 draw_cross(dr, ds,
2766 ox + TS8(5)*fx + TS8(3)*ax,
2767 oy + TS8(5)*fy + TS8(3)*ay, fg);
2768 } else if ((ldata & DL_COUNTMASK) != 0) {
2769 int lh, lw, gw, bw, i, loff;
2770
2771 lh = (ldata & DL_COUNTMASK);
2772 if (lh == DL_COUNT_HINT)
2773 lh = 1;
2774
2775 lw = gw = LINE_WIDTH;
2776 while ((bw = lw * lh + gw * (lh+1)) > TILE_SIZE)
2777 gw--;
2778
2779 loff = OFFSET(bw);
2780
2781 if (which & 1) {
2782 if ((ldata & DL_LOCK) && fg != COL_HINT)
2783 draw_rect(dr, ox + loff*ax, oy + loff*ay,
2784 len*fx+bw*ax, len*fy+bw*ay, COL_MARK);
2785 }
2786 if (which & 2) {
2787 for (i = 0; i < lh; i++, loff += lw + gw)
2788 draw_rect(dr, ox + (loff+gw)*ax, oy + (loff+gw)*ay,
2789 len*fx+lw*ax, len*fy+lw*ay, fg);
2790 }
2791 }
2792}
2793
2794static void draw_hline(drawing *dr, game_drawstate *ds,
2795 int ox, int oy, int w, unsigned long vdata, int which)
2796{
2797 draw_general_line(dr, ds, ox, oy, 1, 0, 0, 1, w, vdata, which);
2798}
2799
2800static void draw_vline(drawing *dr, game_drawstate *ds,
2801 int ox, int oy, int h, unsigned long vdata, int which)
2802{
2803 draw_general_line(dr, ds, ox, oy, 0, 1, 1, 0, h, vdata, which);
2804}
2805
2806#define ISLAND_RADIUS ((TILE_SIZE*12)/20)
2807#define ISLAND_NUMSIZE(clue) \
2808 (((clue) < 10) ? (TILE_SIZE*7)/10 : (TILE_SIZE*5)/10)
2809
2810static void draw_island(drawing *dr, game_drawstate *ds,
2811 int ox, int oy, int clue, unsigned long idata)
2812{
2813 int half, orad, irad, fg, bg;
2814
2815 if ((idata & DI_BGMASK) == DI_BG_NO_ISLAND)
2816 return;
2817
2818 half = TILE_SIZE/2;
2819 orad = ISLAND_RADIUS;
2820 irad = orad - LINE_WIDTH;
2821 fg = ((idata & DI_COLMASK) == DI_COL_SELECTED ? COL_SELECTED :
2822 (idata & DI_COLMASK) == DI_COL_WARNING ? COL_WARNING :
2823 (idata & DI_COLMASK) == DI_COL_FLASH ? COL_HIGHLIGHT :
2824 COL_FOREGROUND);
2825 bg = ((idata & DI_BGMASK) == DI_BG_CURSOR ? COL_CURSOR :
2826 (idata & DI_BGMASK) == DI_BG_MARK ? COL_MARK :
2827 COL_BACKGROUND);
2828
2829 /* draw a thick circle */
2830 draw_circle(dr, ox+half, oy+half, orad, fg, fg);
2831 draw_circle(dr, ox+half, oy+half, irad, bg, bg);
2832
2833 if (clue > 0) {
2834 char str[32];
2835 int textcolour = (fg == COL_SELECTED ? COL_FOREGROUND : fg);
2836 sprintf(str, "%d", clue);
2837 draw_text(dr, ox+half, oy+half, FONT_VARIABLE, ISLAND_NUMSIZE(clue),
2838 ALIGN_VCENTRE | ALIGN_HCENTRE, textcolour, str);
2839 }
2840}
2841
2842static void draw_island_tile(drawing *dr, game_drawstate *ds,
2843 int x, int y, int clue, unsigned long data)
2844{
2845 int ox = COORD(x), oy = COORD(y);
2846 int which;
2847
2848 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2849 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
2850
2851 /*
2852 * Because of the possibility of incoming bridges just about
2853 * meeting at one corner, we must split the line-drawing into
2854 * background and foreground segments.
2855 */
2856 for (which = 1; which <= 2; which <<= 1) {
2857 draw_hline(dr, ds, ox, oy, TILE_SIZE/2,
2858 (data >> D_I_LINE_SHIFT_L) & DL_MASK, which);
2859 draw_hline(dr, ds, ox + TILE_SIZE - TILE_SIZE/2, oy, TILE_SIZE/2,
2860 (data >> D_I_LINE_SHIFT_R) & DL_MASK, which);
2861 draw_vline(dr, ds, ox, oy, TILE_SIZE/2,
2862 (data >> D_I_LINE_SHIFT_U) & DL_MASK, which);
2863 draw_vline(dr, ds, ox, oy + TILE_SIZE - TILE_SIZE/2, TILE_SIZE/2,
2864 (data >> D_I_LINE_SHIFT_D) & DL_MASK, which);
2865 }
2866 draw_island(dr, ds, ox, oy, clue, (data >> D_I_ISLAND_SHIFT) & DI_MASK);
2867
2868 unclip(dr);
2869 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2870}
2871
2872static void draw_line_tile(drawing *dr, game_drawstate *ds,
2873 int x, int y, unsigned long data)
2874{
2875 int ox = COORD(x), oy = COORD(y);
2876 unsigned long hdata, vdata;
2877
2878 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2879 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
2880
2881 /*
2882 * We have to think about which of the horizontal and vertical
2883 * line to draw first, if both exist.
2884 *
2885 * The rule is that hint lines are drawn at the bottom, then
2886 * NOLINE crosses, then actual bridges. The enumeration in the
2887 * DL_COUNTMASK field is set up so that this drops out of a
2888 * straight comparison between the two.
2889 *
2890 * Since lines crossing in this type of square cannot both be
2891 * actual bridges, there's no need to pass a nontrivial 'which'
2892 * parameter to draw_[hv]line.
2893 */
2894 hdata = (data >> D_L_LINE_SHIFT_H) & DL_MASK;
2895 vdata = (data >> D_L_LINE_SHIFT_V) & DL_MASK;
2896 if ((hdata & DL_COUNTMASK) > (vdata & DL_COUNTMASK)) {
2897 draw_hline(dr, ds, ox, oy, TILE_SIZE, hdata, 3);
2898 draw_vline(dr, ds, ox, oy, TILE_SIZE, vdata, 3);
2899 } else {
2900 draw_vline(dr, ds, ox, oy, TILE_SIZE, vdata, 3);
2901 draw_hline(dr, ds, ox, oy, TILE_SIZE, hdata, 3);
2902 }
2903
2904 /*
2905 * The islands drawn at the edges of a line tile don't need clue
2906 * numbers.
2907 */
2908 draw_island(dr, ds, ox - TILE_SIZE, oy, -1,
2909 (data >> D_L_ISLAND_SHIFT_L) & DI_MASK);
2910 draw_island(dr, ds, ox + TILE_SIZE, oy, -1,
2911 (data >> D_L_ISLAND_SHIFT_R) & DI_MASK);
2912 draw_island(dr, ds, ox, oy - TILE_SIZE, -1,
2913 (data >> D_L_ISLAND_SHIFT_U) & DI_MASK);
2914 draw_island(dr, ds, ox, oy + TILE_SIZE, -1,
2915 (data >> D_L_ISLAND_SHIFT_D) & DI_MASK);
2916
2917 unclip(dr);
2918 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2919}
2920
2921static void draw_edge_tile(drawing *dr, game_drawstate *ds,
2922 int x, int y, int dx, int dy, unsigned long data)
2923{
2924 int ox = COORD(x), oy = COORD(y);
2925 int cx = ox, cy = oy, cw = TILE_SIZE, ch = TILE_SIZE;
2926
2927 if (dy) {
2928 if (dy > 0)
2929 cy += TILE_SIZE/2;
2930 ch -= TILE_SIZE/2;
2931 } else {
2932 if (dx > 0)
2933 cx += TILE_SIZE/2;
2934 cw -= TILE_SIZE/2;
2935 }
2936 clip(dr, cx, cy, cw, ch);
2937 draw_rect(dr, cx, cy, cw, ch, COL_BACKGROUND);
2938
2939 draw_island(dr, ds, ox + TILE_SIZE*dx, oy + TILE_SIZE*dy, -1,
2940 (data >> D_I_ISLAND_SHIFT) & DI_MASK);
2941
2942 unclip(dr);
2943 draw_update(dr, cx, cy, cw, ch);
2944}
2945
2946static void game_redraw(drawing *dr, game_drawstate *ds,
2947 const game_state *oldstate, const game_state *state,
2948 int dir, const game_ui *ui,
2949 float animtime, float flashtime)
2950{
2951 int x, y, lv, lh;
2952 grid_type v, flash = 0;
2953 struct island *is, *is_drag_src = NULL, *is_drag_dst = NULL;
2954
2955 if (flashtime) {
2956 int f = (int)(flashtime * 5 / FLASH_TIME);
2957 if (f == 1 || f == 3) flash = TRUE;
2958 }
2959
2960 /* Clear screen, if required. */
2961 if (!ds->started) {
2962 draw_rect(dr, 0, 0,
2963 TILE_SIZE * ds->w + 2 * BORDER,
2964 TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND);
2965#ifdef DRAW_GRID
2966 draw_rect_outline(dr,
2967 COORD(0)-1, COORD(0)-1,
2968 TILE_SIZE * ds->w + 2, TILE_SIZE * ds->h + 2,
2969 COL_GRID);
2970#endif
2971 draw_update(dr, 0, 0,
2972 TILE_SIZE * ds->w + 2 * BORDER,
2973 TILE_SIZE * ds->h + 2 * BORDER);
2974 ds->started = 1;
2975 }
2976
2977 if (ui->dragx_src != -1 && ui->dragy_src != -1) {
2978 ds->dragging = 1;
2979 is_drag_src = INDEX(state, gridi, ui->dragx_src, ui->dragy_src);
2980 assert(is_drag_src);
2981 if (ui->dragx_dst != -1 && ui->dragy_dst != -1) {
2982 is_drag_dst = INDEX(state, gridi, ui->dragx_dst, ui->dragy_dst);
2983 assert(is_drag_dst);
2984 }
2985 } else
2986 ds->dragging = 0;
2987
2988 /*
2989 * Set up ds->newgrid with the current grid contents.
2990 */
2991 for (x = 0; x < ds->w; x++)
2992 for (y = 0; y < ds->h; y++)
2993 INDEX(ds,newgrid,x,y) = 0;
2994
2995 for (x = 0; x < ds->w; x++) {
2996 for (y = 0; y < ds->h; y++) {
2997 v = GRID(state, x, y);
2998
2999 if (v & G_ISLAND) {
3000 /*
3001 * An island square. Compute the drawing data for the
3002 * island, and put it in this square and surrounding
3003 * squares.
3004 */
3005 unsigned long idata = 0;
3006
3007 is = INDEX(state, gridi, x, y);
3008
3009 if (flash)
3010 idata |= DI_COL_FLASH;
3011 if (is_drag_src && (is == is_drag_src ||
3012 (is_drag_dst && is == is_drag_dst)))
3013 idata |= DI_COL_SELECTED;
3014 else if (island_impossible(is, v & G_MARK) || (v & G_WARN))
3015 idata |= DI_COL_WARNING;
3016 else
3017 idata |= DI_COL_NORMAL;
3018
3019 if (ui->cur_visible &&
3020 ui->cur_x == is->x && ui->cur_y == is->y)
3021 idata |= DI_BG_CURSOR;
3022 else if (v & G_MARK)
3023 idata |= DI_BG_MARK;
3024 else
3025 idata |= DI_BG_NORMAL;
3026
3027 INDEX(ds,newgrid,x,y) |= idata << D_I_ISLAND_SHIFT;
3028 if (x > 0 && !(GRID(state,x-1,y) & G_ISLAND))
3029 INDEX(ds,newgrid,x-1,y) |= idata << D_L_ISLAND_SHIFT_R;
3030 if (x+1 < state->w && !(GRID(state,x+1,y) & G_ISLAND))
3031 INDEX(ds,newgrid,x+1,y) |= idata << D_L_ISLAND_SHIFT_L;
3032 if (y > 0 && !(GRID(state,x,y-1) & G_ISLAND))
3033 INDEX(ds,newgrid,x,y-1) |= idata << D_L_ISLAND_SHIFT_D;
3034 if (y+1 < state->h && !(GRID(state,x,y+1) & G_ISLAND))
3035 INDEX(ds,newgrid,x,y+1) |= idata << D_L_ISLAND_SHIFT_U;
3036 } else {
3037 unsigned long hdata, vdata;
3038 int selh = FALSE, selv = FALSE;
3039
3040 /*
3041 * A line (non-island) square. Compute the drawing
3042 * data for any horizontal and vertical lines in the
3043 * square, and put them in this square's entry and
3044 * optionally those for neighbouring islands too.
3045 */
3046
3047 if (is_drag_dst &&
3048 WITHIN(x,is_drag_src->x, is_drag_dst->x) &&
3049 WITHIN(y,is_drag_src->y, is_drag_dst->y)) {
3050 if (is_drag_src->x != is_drag_dst->x)
3051 selh = TRUE;
3052 else
3053 selv = TRUE;
3054 }
3055 lines_lvlh(state, ui, x, y, v, &lv, &lh);
3056
3057 hdata = (v & G_NOLINEH ? DL_COUNT_CROSS :
3058 v & G_LINEH ? lh :
3059 (ui->show_hints &&
3060 between_island(state,x,y,1,0)) ? DL_COUNT_HINT : 0);
3061 vdata = (v & G_NOLINEV ? DL_COUNT_CROSS :
3062 v & G_LINEV ? lv :
3063 (ui->show_hints &&
3064 between_island(state,x,y,0,1)) ? DL_COUNT_HINT : 0);
3065
3066 hdata |= (flash ? DL_COL_FLASH :
3067 v & G_WARN ? DL_COL_WARNING :
3068 selh ? DL_COL_SELECTED :
3069 DL_COL_NORMAL);
3070 vdata |= (flash ? DL_COL_FLASH :
3071 v & G_WARN ? DL_COL_WARNING :
3072 selv ? DL_COL_SELECTED :
3073 DL_COL_NORMAL);
3074
3075 if (v & G_MARKH)
3076 hdata |= DL_LOCK;
3077 if (v & G_MARKV)
3078 vdata |= DL_LOCK;
3079
3080 INDEX(ds,newgrid,x,y) |= hdata << D_L_LINE_SHIFT_H;
3081 INDEX(ds,newgrid,x,y) |= vdata << D_L_LINE_SHIFT_V;
3082 if (x > 0 && (GRID(state,x-1,y) & G_ISLAND))
3083 INDEX(ds,newgrid,x-1,y) |= hdata << D_I_LINE_SHIFT_R;
3084 if (x+1 < state->w && (GRID(state,x+1,y) & G_ISLAND))
3085 INDEX(ds,newgrid,x+1,y) |= hdata << D_I_LINE_SHIFT_L;
3086 if (y > 0 && (GRID(state,x,y-1) & G_ISLAND))
3087 INDEX(ds,newgrid,x,y-1) |= vdata << D_I_LINE_SHIFT_D;
3088 if (y+1 < state->h && (GRID(state,x,y+1) & G_ISLAND))
3089 INDEX(ds,newgrid,x,y+1) |= vdata << D_I_LINE_SHIFT_U;
3090 }
3091 }
3092 }
3093
3094 /*
3095 * Now go through and draw any changed grid square.
3096 */
3097 for (x = 0; x < ds->w; x++) {
3098 for (y = 0; y < ds->h; y++) {
3099 unsigned long newval = INDEX(ds,newgrid,x,y);
3100 if (INDEX(ds,grid,x,y) != newval) {
3101 v = GRID(state, x, y);
3102 if (v & G_ISLAND) {
3103 is = INDEX(state, gridi, x, y);
3104 draw_island_tile(dr, ds, x, y, is->count, newval);
3105
3106 /*
3107 * If this tile is right at the edge of the grid,
3108 * we must also draw the part of the island that
3109 * goes completely out of bounds. We don't bother
3110 * keeping separate entries in ds->newgrid for
3111 * these tiles; it's easier just to redraw them
3112 * iff we redraw their parent island tile.
3113 */
3114 if (x == 0)
3115 draw_edge_tile(dr, ds, x-1, y, +1, 0, newval);
3116 if (y == 0)
3117 draw_edge_tile(dr, ds, x, y-1, 0, +1, newval);
3118 if (x == state->w-1)
3119 draw_edge_tile(dr, ds, x+1, y, -1, 0, newval);
3120 if (y == state->h-1)
3121 draw_edge_tile(dr, ds, x, y+1, 0, -1, newval);
3122 } else {
3123 draw_line_tile(dr, ds, x, y, newval);
3124 }
3125 INDEX(ds,grid,x,y) = newval;
3126 }
3127 }
3128 }
3129}
3130
3131static float game_anim_length(const game_state *oldstate,
3132 const game_state *newstate, int dir, game_ui *ui)
3133{
3134 return 0.0F;
3135}
3136
3137static float game_flash_length(const game_state *oldstate,
3138 const game_state *newstate, int dir, game_ui *ui)
3139{
3140 if (!oldstate->completed && newstate->completed &&
3141 !oldstate->solved && !newstate->solved)
3142 return FLASH_TIME;
3143
3144 return 0.0F;
3145}
3146
3147static int game_status(const game_state *state)
3148{
3149 return state->completed ? +1 : 0;
3150}
3151
3152static int game_timing_state(const game_state *state, game_ui *ui)
3153{
3154 return TRUE;
3155}
3156
3157static void game_print_size(const game_params *params, float *x, float *y)
3158{
3159 int pw, ph;
3160
3161 /* 10mm squares by default. */
3162 game_compute_size(params, 1000, &pw, &ph);
3163 *x = pw / 100.0F;
3164 *y = ph / 100.0F;
3165}
3166
3167static void game_print(drawing *dr, const game_state *state, int ts)
3168{
3169 int ink = print_mono_colour(dr, 0);
3170 int paper = print_mono_colour(dr, 1);
3171 int x, y, cx, cy, i, nl;
3172 int loff;
3173 grid_type grid;
3174
3175 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3176 game_drawstate ads, *ds = &ads;
3177 ads.tilesize = ts;
3178
3179 /* I don't think this wants a border. */
3180
3181 /* Bridges */
3182 loff = ts / (8 * sqrt((state->params.maxb - 1)));
3183 print_line_width(dr, ts / 12);
3184 for (x = 0; x < state->w; x++) {
3185 for (y = 0; y < state->h; y++) {
3186 cx = COORD(x); cy = COORD(y);
3187 grid = GRID(state,x,y);
3188 nl = INDEX(state,lines,x,y);
3189
3190 if (grid & G_ISLAND) continue;
3191 if (grid & G_LINEV) {
3192 for (i = 0; i < nl; i++)
3193 draw_line(dr, cx+ts/2+(2*i-nl+1)*loff, cy,
3194 cx+ts/2+(2*i-nl+1)*loff, cy+ts, ink);
3195 }
3196 if (grid & G_LINEH) {
3197 for (i = 0; i < nl; i++)
3198 draw_line(dr, cx, cy+ts/2+(2*i-nl+1)*loff,
3199 cx+ts, cy+ts/2+(2*i-nl+1)*loff, ink);
3200 }
3201 }
3202 }
3203
3204 /* Islands */
3205 for (i = 0; i < state->n_islands; i++) {
3206 char str[32];
3207 struct island *is = &state->islands[i];
3208 grid = GRID(state, is->x, is->y);
3209 cx = COORD(is->x) + ts/2;
3210 cy = COORD(is->y) + ts/2;
3211
3212 draw_circle(dr, cx, cy, ISLAND_RADIUS, paper, ink);
3213
3214 sprintf(str, "%d", is->count);
3215 draw_text(dr, cx, cy, FONT_VARIABLE, ISLAND_NUMSIZE(is->count),
3216 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
3217 }
3218}
3219
3220#ifdef COMBINED
3221#define thegame bridges
3222#endif
3223
3224const struct game thegame = {
3225 "Bridges", "games.bridges", "bridges",
3226 default_params,
3227 game_fetch_preset, NULL,
3228 decode_params,
3229 encode_params,
3230 free_params,
3231 dup_params,
3232 TRUE, game_configure, custom_params,
3233 validate_params,
3234 new_game_desc,
3235 validate_desc,
3236 new_game,
3237 dup_game,
3238 free_game,
3239 TRUE, solve_game,
3240 TRUE, game_can_format_as_text_now, game_text_format,
3241 new_ui,
3242 free_ui,
3243 encode_ui,
3244 decode_ui,
3245 game_changed_state,
3246 interpret_move,
3247 execute_move,
3248 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3249 game_colours,
3250 game_new_drawstate,
3251 game_free_drawstate,
3252 game_redraw,
3253 game_anim_length,
3254 game_flash_length,
3255 game_status,
3256 TRUE, FALSE, game_print_size, game_print,
3257 FALSE, /* wants_statusbar */
3258 FALSE, game_timing_state,
3259 REQUIRE_RBUTTON, /* flags */
3260};
3261
3262/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/bridges.html b/apps/plugins/puzzles/src/bridges.html
new file mode 100644
index 0000000000..74a8a130cd
--- /dev/null
+++ b/apps/plugins/puzzles/src/bridges.html
@@ -0,0 +1,135 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Bridges</title>
7<link rel="previous" href="tents.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="unequal.html">
12</head>
13<body>
14<p><a href="tents.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="unequal.html">Next</a></p>
15<h1><a name="C26"></a>Chapter 26: <a name="i0"></a>Bridges</h1>
16<p>
17You have a set of islands distributed across the playing area. Each island contains a number. Your aim is to connect the islands together with bridges, in such a way that:
18</p>
19<ul><li>
20Bridges run horizontally or vertically.
21</li>
22<li>
23The number of bridges terminating at any island is equal to the number written in that island.
24</li>
25<li>
26Two bridges may run in parallel between the same two islands, but no more than two may do so.
27</li>
28<li>
29No bridge crosses another bridge.
30</li>
31<li>
32All the islands are connected together.
33</li>
34</ul>
35<p>
36There are some configurable alternative modes, which involve changing the parallel-bridge limit to something other than 2, and introducing the additional constraint that no sequence of bridges may form a loop from one island back to the same island. The rules stated above are the default ones.
37</p>
38<p>
39Credit for this puzzle goes to <a name="i1"></a>Nikoli <a href="#p0">[12]</a>.
40</p>
41<p>
42Bridges was contributed to this collection by James Harvey.
43</p>
44<p><a name="p0"></a>
45[12] <a href="http://www.nikoli.co.jp/en/puzzles/hashiwokakero.html"><code>http://www.nikoli.co.jp/en/puzzles/hashiwokakero.html</code></a> (beware of Flash)
46</p>
47<h2><a name="S26.1"></a>26.1 <a name="i2"></a>Bridges controls</h2>
48<p>
49To place a bridge between two islands, click the mouse down on one island and drag it towards the other. You do not need to drag all the way to the other island; you only need to move the mouse far enough for the intended bridge direction to be unambiguous. (So you can keep the mouse near the starting island and conveniently throw bridges out from it in many directions.)
50</p>
51<p>
52Doing this again when a bridge is already present will add another parallel bridge. If there are already as many bridges between the two islands as permitted by the current game rules (i.e. two by default), the same dragging action will remove all of them.
53</p>
54<p>
55If you want to remind yourself that two islands definitely <em>do not</em> have a bridge between them, you can right-drag between them in the same way to draw a &#8216;non-bridge&#8217; marker.
56</p>
57<p>
58If you think you have finished with an island (i.e. you have placed all its bridges and are confident that they are in the right places), you can mark the island as finished by left-clicking on it. This will highlight it and all the bridges connected to it, and you will be prevented from accidentally modifying any of those bridges in future. Left-clicking again on a highlighted island will unmark it and restore your ability to modify it.
59</p>
60<p>
61You can also use the cursor keys to move around the grid: if possible the cursor will always move orthogonally, otherwise it will move towards the nearest island to the indicated direction. Holding Control and pressing a cursor key will lay a bridge in that direction (if available); Shift and a cursor key will lay a &#8216;non-bridge&#8217; marker. Pressing the return key followed by a cursor key will also lay a bridge in that direction.
62</p>
63<p>
64You can mark an island as finished by pressing the space bar or by pressing the return key twice.
65</p>
66<p>
67By pressing a number key, you can jump to the nearest island with that number. Letters &#8216;a&#8217;, ..., &#8216;f&#8217; count as 10, ..., 15 and &#8216;0&#8217; as 16.
68</p>
69<p>
70Violations of the puzzle rules will be marked in red:
71</p>
72<ul><li>
73An island with too many bridges will be highlighted in red.
74</li>
75<li>
76An island with too few bridges will be highlighted in red if it is definitely an error (as opposed to merely not being finished yet): if adding enough bridges would involve having to cross another bridge or remove a non-bridge marker, or if the island has been highlighted as complete.
77</li>
78<li>
79A group of islands and bridges may be highlighted in red if it is a closed subset of the puzzle with no way to connect it to the rest of the islands. For example, if you directly connect two 1s together with a bridge and they are not the only two islands on the grid, they will light up red to indicate that such a group cannot be contained in any valid solution.
80</li>
81<li>
82If you have selected the (non-default) option to disallow loops in the solution, a group of bridges which forms a loop will be highlighted.
83</li>
84</ul>
85<p>
86(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
87</p>
88<h2><a name="S26.2"></a>26.2 <a name="i3"></a>Bridges parameters</h2>
89<p>
90These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
91</p>
92<dl><dt>
93<em>Width</em>, <em>Height</em>
94</dt>
95<dd>
96Size of grid in squares.
97</dd>
98<dt>
99<em>Difficulty</em>
100</dt>
101<dd>
102Difficulty level of puzzle.
103</dd>
104<dt>
105<em>Allow loops</em>
106</dt>
107<dd>
108This is set by default. If cleared, puzzles will be generated in such a way that they are always soluble without creating a loop, and solutions which do involve a loop will be disallowed.
109</dd>
110<dt>
111<em>Max. bridges per direction</em>
112</dt>
113<dd>
114Maximum number of bridges in any particular direction. The default is 2, but you can change it to 1, 3 or 4. In general, fewer is easier.
115</dd>
116<dt>
117<em>%age of island squares</em>
118</dt>
119<dd>
120Gives a rough percentage of islands the generator will try and lay before finishing the puzzle. Certain layouts will not manage to lay enough islands; this is an upper bound.
121</dd>
122<dt>
123<em>Expansion factor (%age)</em>
124</dt>
125<dd>
126The grid generator works by picking an existing island at random (after first creating an initial island somewhere). It then decides on a direction (at random), and then works out how far it could extend before creating another island. This parameter determines how likely it is to extend as far as it can, rather than choosing somewhere closer.
127<p>
128High expansion factors usually mean easier puzzles with fewer possible islands; low expansion factors can create lots of tightly-packed islands.
129</p>
130
131</dd>
132</dl>
133
134<hr><address></address></body>
135</html>
diff --git a/apps/plugins/puzzles/src/chm.but b/apps/plugins/puzzles/src/chm.but
new file mode 100644
index 0000000000..e0237044e4
--- /dev/null
+++ b/apps/plugins/puzzles/src/chm.but
@@ -0,0 +1,21 @@
1\# File containing the magic HTML configuration directives to create
2\# an MS HTML Help project. We put this on the end of the Puzzles
3\# docs build command line to build the HHP and friends.
4
5\cfg{html-leaf-level}{infinite}
6\cfg{html-leaf-contains-contents}{false}
7\cfg{html-suppress-navlinks}{true}
8\cfg{html-suppress-address}{true}
9
10\cfg{html-contents-filename}{index.html}
11\cfg{html-template-filename}{%k.html}
12\cfg{html-template-fragment}{%k}
13
14\cfg{html-mshtmlhelp-chm}{puzzles.chm}
15\cfg{html-mshtmlhelp-project}{puzzles.hhp}
16\cfg{html-mshtmlhelp-contents}{puzzles.hhc}
17\cfg{html-mshtmlhelp-index}{puzzles.hhk}
18
19\cfg{html-body-end}{}
20
21\cfg{html-head-end}{<link rel="stylesheet" type="text/css" href="chm.css">}
diff --git a/apps/plugins/puzzles/src/chm.css b/apps/plugins/puzzles/src/chm.css
new file mode 100644
index 0000000000..d8c316bfc6
--- /dev/null
+++ b/apps/plugins/puzzles/src/chm.css
@@ -0,0 +1,7 @@
1/* Stylesheet for a Windows .CHM help file */
2
3body { font-size: 75%; font-family: Verdana, Arial, Helvetica, Sans-Serif; }
4
5h1 { font-weight: bold; font-size: 150%; }
6h2 { font-weight: bold; font-size: 130%; }
7h3 { font-weight: bold; font-size: 120%; }
diff --git a/apps/plugins/puzzles/src/combi.c b/apps/plugins/puzzles/src/combi.c
new file mode 100644
index 0000000000..4c5d1077aa
--- /dev/null
+++ b/apps/plugins/puzzles/src/combi.c
@@ -0,0 +1,110 @@
1#include <assert.h>
2#include <string.h>
3
4#include "puzzles.h"
5
6/* horrific and doesn't check overflow. */
7static long factx(long x, long y)
8{
9 long acc = 1, i;
10
11 for (i = y; i <= x; i++)
12 acc *= i;
13 return acc;
14}
15
16void reset_combi(combi_ctx *combi)
17{
18 int i;
19 combi->nleft = combi->total;
20 for (i = 0; i < combi->r; i++)
21 combi->a[i] = i;
22}
23
24combi_ctx *new_combi(int r, int n)
25{
26 long nfr, nrf;
27 combi_ctx *combi;
28
29 assert(r <= n);
30 assert(n >= 1);
31
32 combi = snew(combi_ctx);
33 memset(combi, 0, sizeof(combi_ctx));
34 combi->r = r;
35 combi->n = n;
36
37 combi->a = snewn(r, int);
38 memset(combi->a, 0, r * sizeof(int));
39
40 nfr = factx(n, r+1);
41 nrf = factx(n-r, 1);
42 combi->total = (int)(nfr / nrf);
43
44 reset_combi(combi);
45 return combi;
46}
47
48/* returns NULL when we're done otherwise returns input. */
49combi_ctx *next_combi(combi_ctx *combi)
50{
51 int i = combi->r - 1, j;
52
53 if (combi->nleft == combi->total)
54 goto done;
55 else if (combi->nleft <= 0)
56 return NULL;
57
58 while (combi->a[i] == combi->n - combi->r + i)
59 i--;
60 combi->a[i] += 1;
61 for (j = i+1; j < combi->r; j++)
62 combi->a[j] = combi->a[i] + j - i;
63
64 done:
65 combi->nleft--;
66 return combi;
67}
68
69void free_combi(combi_ctx *combi)
70{
71 sfree(combi->a);
72 sfree(combi);
73}
74
75/* compile this with:
76 * gcc -o combi.exe -DSTANDALONE_COMBI_TEST combi.c malloc.c
77 */
78#ifdef STANDALONE_COMBI_TEST
79
80#include <stdio.h>
81
82void fatal(char *fmt, ...)
83{
84 abort();
85}
86
87int main(int argc, char *argv[])
88{
89 combi_ctx *c;
90 int i, r, n;
91
92 if (argc < 3) {
93 fprintf(stderr, "Usage: combi R N\n");
94 exit(1);
95 }
96
97 r = atoi(argv[1]); n = atoi(argv[2]);
98 c = new_combi(r, n);
99 printf("combi %d of %d, %d elements.\n", c->r, c->n, c->total);
100
101 while (next_combi(c)) {
102 for (i = 0; i < c->r; i++) {
103 printf("%d ", c->a[i]);
104 }
105 printf("\n");
106 }
107 free_combi(c);
108}
109
110#endif
diff --git a/apps/plugins/puzzles/src/common.html b/apps/plugins/puzzles/src/common.html
new file mode 100644
index 0000000000..951429d806
--- /dev/null
+++ b/apps/plugins/puzzles/src/common.html
@@ -0,0 +1,285 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Common features</title>
7<link rel="previous" href="intro.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="net.html">
12</head>
13<body>
14<p><a href="intro.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="net.html">Next</a></p>
15
16<ul>
17<li><a href="#C2">Chapter 2: Common features</a>
18<ul>
19<li><a href="#S2.1">2.1 Common actions</a></li>
20<li><a href="#S2.2">2.2 Specifying games with the game ID</a></li>
21<li><a href="#S2.3">2.3 The &#8216;Type&#8217; menu</a></li>
22<li><a href="#S2.4">2.4 Specifying game parameters on the command line</a></li>
23<li><a href="#S2.5">2.5 Unix command-line options</a></li>
24</ul></li>
25</ul>
26<h1><a name="C2"></a>Chapter 2: <a name="i0"></a>Common features</h1>
27<p>
28This chapter describes features that are common to all the games.
29</p>
30<h2><a name="S2.1"></a>2.1 <a name="i1"></a>Common actions</h2>
31<p>
32These actions are all available from the <a name="i2"></a>&#8216;Game&#8217; menu and via <a name="i3"></a>keyboard shortcuts, in addition to any game-specific actions.
33</p>
34<p>
35(On <a name="i4"></a>Mac OS X, to conform with local user interface standards, these actions are situated on the <a name="i5"></a>&#8216;File&#8217; and <a name="i6"></a>&#8216;Edit&#8217; menus instead.)
36</p>
37<dl><dt>
38<a name="i7"></a><em>New game</em> (&#8216;N&#8217;, Ctrl+&#8216;N&#8217;)
39</dt>
40<dd>
41Starts a new game, with a random initial state.
42</dd>
43<dt>
44<a name="i8"></a><em>Restart game</em>
45</dt>
46<dd>
47Resets the current game to its initial state. (This can be undone.)
48</dd>
49<dt>
50<a name="i9"></a><em>Load</em>
51</dt>
52<dd>
53Loads a saved game from a file on disk.
54</dd>
55<dt>
56<a name="i10"></a><em>Save</em>
57</dt>
58<dd>
59Saves the current state of your game to a file on disk.
60<p>
61The Load and Save operations preserve your entire game history (so you can save, reload, and still Undo and Redo things you had done before saving).
62</p>
63
64</dd>
65<dt>
66<a name="i11"></a><em>Print</em>
67</dt>
68<dd>
69Where supported (currently only on Windows), brings up a dialog allowing you to print an arbitrary number of puzzles randomly generated from the current parameters, optionally including the current puzzle. (Only for puzzles which make sense to print, of course &#8211; it's hard to think of a sensible printable representation of Fifteen!)
70</dd>
71<dt>
72<a name="i12"></a><em>Undo</em> (&#8216;U&#8217;, Ctrl+&#8216;Z&#8217;, Ctrl+&#8216;_&#8217;)
73</dt>
74<dd>
75Undoes a single move. (You can undo moves back to the start of the session.)
76</dd>
77<dt>
78<a name="i13"></a><em>Redo</em> (&#8216;R&#8217;, Ctrl+&#8216;R&#8217;)
79</dt>
80<dd>
81Redoes a previously undone move.
82</dd>
83<dt>
84<a name="i14"></a><em>Copy</em>
85</dt>
86<dd>
87Copies the current state of your game to the clipboard in text format, so that you can paste it into (say) an e-mail client or a web message board if you're discussing the game with someone else. (Not all games support this feature.)
88</dd>
89<dt>
90<a name="i15"></a><em>Solve</em>
91</dt>
92<dd>
93Transforms the puzzle instantly into its solved state. For some games (Cube) this feature is not supported at all because it is of no particular use. For other games (such as Pattern), the solved state can be used to give you information, if you can't see how a solution can exist at all or you want to know where you made a mistake. For still other games (such as Sixteen), automatic solution tells you nothing about how to <em>get</em> to the solution, but it does provide a useful way to get there quickly so that you can experiment with set-piece moves and transformations.
94<p>
95Some games (such as Solo) are capable of solving a game ID you have typed in from elsewhere. Other games (such as Rectangles) cannot solve a game ID they didn't invent themself, but when they did invent the game ID they know what the solution is already. Still other games (Pattern) can solve <em>some</em> external game IDs, but only if they aren't too difficult.
96</p>
97<p>
98The &#8216;Solve&#8217; command adds the solved state to the end of the undo chain for the puzzle. In other words, if you want to go back to solving it yourself after seeing the answer, you can just press Undo.
99</p>
100
101</dd>
102<dt>
103<a name="i16"></a><a name="i17"></a><em>Quit</em> (&#8216;Q&#8217;, Ctrl+&#8216;Q&#8217;)
104</dt>
105<dd>
106Closes the application entirely.
107</dd>
108</dl>
109<h2><a name="S2.2"></a>2.2 Specifying games with the <a name="i18"></a>game ID</h2>
110<p>
111There are two ways to save a game specification out of a puzzle and recreate it later, or recreate it in somebody else's copy of the same puzzle.
112</p>
113<p>
114The &#8216;<a name="i19"></a>Specific&#8217; and &#8216;<a name="i20"></a>Random Seed&#8217; options from the <a name="i21"></a>&#8216;Game&#8217; menu (or the &#8216;File&#8217; menu, on <a name="i22"></a>Mac OS X) each show a piece of text (a &#8216;game ID&#8217;) which is sufficient to reconstruct precisely the same game at a later date.
115</p>
116<p>
117You can enter either of these pieces of text back into the program (via the same &#8216;Specific&#8217; or &#8216;Random Seed&#8217; menu options) at a later point, and it will recreate the same game. You can also use either one as a <a name="i23"></a>command line argument (on Windows or Unix); see <a href="#S2.4">section 2.4</a> for more detail.
118</p>
119<p>
120The difference between the two forms is that a descriptive game ID is a literal <em>description</em> of the <a name="i24"></a>initial state of the game, whereas a random seed is just a piece of arbitrary text which was provided as input to the random number generator used to create the puzzle. This means that:
121</p>
122<ul><li>
123Descriptive game IDs tend to be longer in many puzzles (although some, such as Cube (<a href="cube.html#C4">chapter 4</a>), only need very short descriptions). So a random seed is often a <em>quicker</em> way to note down the puzzle you're currently playing, or to tell it to somebody else so they can play the same one as you.
124</li>
125<li>
126Any text at all is a valid random seed. The automatically generated ones are fifteen-digit numbers, but anything will do; you can type in your full name, or a word you just made up, and a valid puzzle will be generated from it. This provides a way for two or more people to race to complete the same puzzle: you think of a random seed, then everybody types it in at the same time, and nobody has an advantage due to having seen the generated puzzle before anybody else.
127</li>
128<li>
129It is often possible to convert puzzles from other sources (such as &#8216;nonograms&#8217; or &#8216;sudoku&#8217; from newspapers) into descriptive game IDs suitable for use with these programs.
130</li>
131<li>
132Random seeds are not guaranteed to produce the same result if you use them with a different <a name="i25"></a><em>version</em> of the puzzle program. This is because the generation algorithm might have been improved or modified in later versions of the code, and will therefore produce a different result when given the same sequence of random numbers. Use a descriptive game ID if you aren't sure that it will be used on the same version of the program as yours.
133<p>
134(Use the &#8216;About&#8217; menu option to find out the version number of the program. Programs with the same version number running on different platforms should still be random-seed compatible.)
135</p>
136
137</li>
138</ul>
139<p>
140<a name="i26"></a>A descriptive game ID starts with a piece of text which encodes the <a name="i27"></a><em>parameters</em> of the current game (such as grid size). Then there is a colon, and after that is the description of the game's initial state. A random seed starts with a similar string of parameters, but then it contains a hash sign followed by arbitrary data.
141</p>
142<p>
143If you enter a descriptive game ID, the program will not be able to show you the random seed which generated it, since it wasn't generated <em>from</em> a random seed. If you <em>enter</em> a random seed, however, the program will be able to show you the descriptive game ID derived from that random seed.
144</p>
145<p>
146Note that the game parameter strings are not always identical between the two forms. For some games, there will be parameter data provided with the random seed which is not included in the descriptive game ID. This is because that parameter information is only relevant when <em>generating</em> puzzle grids, and is not important when playing them. Thus, for example, the difficulty level in Solo (<a href="solo.html#C11">chapter 11</a>) is not mentioned in the descriptive game ID.
147</p>
148<p>
149These additional parameters are also not set permanently if you type in a game ID. For example, suppose you have Solo set to &#8216;Advanced&#8217; difficulty level, and then a friend wants your help with a &#8216;Trivial&#8217; puzzle; so the friend reads out a random seed specifying &#8216;Trivial&#8217; difficulty, and you type it in. The program will generate you the same &#8216;Trivial&#8217; grid which your friend was having trouble with, but once you have finished playing it, when you ask for a new game it will automatically go back to the &#8216;Advanced&#8217; difficulty which it was previously set on.
150</p>
151<h2><a name="S2.3"></a>2.3 The &#8216;Type&#8217; menu</h2>
152<p>
153The <a name="i28"></a>&#8216;Type&#8217; menu, if present, may contain a list of <a name="i29"></a>preset game settings. Selecting one of these will start a new random game with the parameters specified.
154</p>
155<p>
156The &#8216;Type&#8217; menu may also contain a &#8216;<a name="i30"></a>Custom&#8217; option which allows you to fine-tune game <a name="i31"></a>parameters. The parameters available are specific to each game and are described in the following sections.
157</p>
158<h2><a name="S2.4"></a>2.4 Specifying game parameters on the <a name="i32"></a>command line</h2>
159<p>
160(This section does not apply to the <a name="i33"></a>Mac OS X version.)
161</p>
162<p>
163The games in this collection deliberately do not ever save information on to the computer they run on: they have no high score tables and no saved preferences. (This is because I expect at least some people to play them at work, and those people will probably appreciate leaving as little evidence as possible!)
164</p>
165<p>
166However, if you do want to arrange for one of these games to <a name="i34"></a>default to a particular set of parameters, you can specify them on the command line.
167</p>
168<p>
169The easiest way to do this is to set up the parameters you want using the &#8216;Type&#8217; menu (see <a href="#S2.3">section 2.3</a>), and then to select &#8216;Random Seed&#8217; from the &#8216;Game&#8217; or &#8216;File&#8217; menu (see <a href="#S2.2">section 2.2</a>). The text in the &#8216;Game ID&#8217; box will be composed of two parts, separated by a hash. The first of these parts represents the game parameters (the size of the playing area, for example, and anything else you set using the &#8216;Type&#8217; menu).
170</p>
171<p>
172If you run the game with just that parameter text on the command line, it will start up with the settings you specified.
173</p>
174<p>
175For example: if you run Cube (see <a href="cube.html#C4">chapter 4</a>), select &#8216;Octahedron&#8217; from the &#8216;Type&#8217; menu, and then go to the game ID selection, you will see a string of the form &#8216;<code>o2x2#338686542711620</code>&#8217;. Take only the part before the hash (&#8216;<code>o2x2</code>&#8217;), and start Cube with that text on the command line: &#8216;<code>PREFIX-cube o2x2</code>&#8217;.
176</p>
177<p>
178If you copy the <em>entire</em> game ID on to the command line, the game will start up in the specific game that was described. This is occasionally a more convenient way to start a particular game ID than by pasting it into the game ID selection box.
179</p>
180<p>
181(You could also retrieve the encoded game parameters using the &#8216;Specific&#8217; menu option instead of &#8216;Random Seed&#8217;, but if you do then some options, such as the difficulty level in Solo, will be missing. See <a href="#S2.2">section 2.2</a> for more details on this.)
182</p>
183<h2><a name="S2.5"></a>2.5 <a name="i35"></a>Unix <a name="i36"></a>command-line options</h2>
184<p>
185(This section only applies to the Unix port.)
186</p>
187<p>
188In addition to being able to specify game parameters on the command line (see <a href="#S2.4">section 2.4</a>), there are various other options:
189</p>
190<dl><dt>
191<code>--game</code>
192</dt>
193<dt>
194<code>--load</code>
195</dt>
196<dd>
197These options respectively determine whether the command-line argument is treated as specifying game parameters or a <a name="i37"></a>save file to <a name="i38"></a>load. Only one should be specified. If neither of these options is specified, a guess is made based on the format of the argument.
198</dd>
199<dt>
200<code>--generate </code><em>n</em>
201</dt>
202<dd>
203If this option is specified, instead of a puzzle being displayed, a number of descriptive game IDs will be <a name="i39"></a>invented and printed on standard output. This is useful for gaining access to the game generation algorithms without necessarily using the frontend.
204<p>
205If game parameters are specified on the command-line, they will be used to generate the game IDs; otherwise a default set of parameters will be used.
206</p>
207<p>
208The most common use of this option is in conjunction with <code>--print</code>, in which case its behaviour is slightly different; see below.
209</p>
210
211</dd>
212<dt>
213<a name="i40"></a><code>--print </code><em>w</em><code>x</code><em>h</em>
214</dt>
215<dd>
216If this option is specified, instead of a puzzle being displayed, a printed representation of one or more unsolved puzzles is sent to standard output, in <a name="i41"></a>PostScript format.
217<p>
218On each page of puzzles, there will be <em>w</em> across and <em>h</em> down. If there are more puzzles than <em>w</em>&#215;<em>h</em>, more than one page will be printed.
219</p>
220<p>
221If <code>--generate</code> has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see <a href="#S2.2">section 2.2</a>), in the same format produced by <code>--generate</code>.
222</p>
223<p>
224For example:
225</p>
226<pre><code>PREFIX-net --generate 12 --print 2x3 7x7w | lpr
227</code></pre>
228<p>
229will generate two pages of printed Net puzzles (each of which will have a 7&#215;7 wrapping grid), and pipe the output to the <code>lpr</code> command, which on many systems will send them to an actual printer.
230</p>
231<p>
232There are various other options which affect printing; see below.
233</p>
234
235</dd>
236<dt>
237<code>--save </code><em>file-prefix</em> [ <code>--save-suffix </code><em>file-suffix</em> ]
238</dt>
239<dd>
240If this option is specified, instead of a puzzle being displayed, saved-game files for one or more unsolved puzzles are written to files constructed from the supplied prefix and/or suffix.
241<p>
242If <code>--generate</code> has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see <a href="#S2.2">section 2.2</a>), in the same format produced by <code>--generate</code>.
243</p>
244<p>
245For example:
246</p>
247<pre><code>PREFIX-net --generate 12 --save game --save-suffix .sav
248</code></pre>
249<p>
250will generate twelve Net saved-game files with the names <code>game0.sav</code> to <code>game11.sav</code>.
251</p>
252
253</dd>
254<dt>
255<code>--version</code>
256</dt>
257<dd>
258Prints version information about the game, and then quits.
259</dd>
260</dl>
261<p>
262The following options are only meaningful if <code>--print</code> is also specified:
263</p>
264<dl><dt>
265<code>--with-solutions</code>
266</dt>
267<dd>
268The set of pages filled with unsolved puzzles will be followed by the solutions to those puzzles.
269</dd>
270<dt>
271<code>--scale </code><em>n</em>
272</dt>
273<dd>
274Adjusts how big each puzzle is when printed. Larger numbers make puzzles bigger; the default is 1.0.
275</dd>
276<dt>
277<code>--colour</code>
278</dt>
279<dd>
280Puzzles will be printed in colour, rather than in black and white (if supported by the puzzle).
281</dd>
282</dl>
283
284<hr><address></address></body>
285</html>
diff --git a/apps/plugins/puzzles/src/compile b/apps/plugins/puzzles/src/compile
new file mode 100755
index 0000000000..a85b723c7e
--- /dev/null
+++ b/apps/plugins/puzzles/src/compile
@@ -0,0 +1,347 @@
1#! /bin/sh
2# Wrapper for compilers which do not understand '-c -o'.
3
4scriptversion=2012-10-14.11; # UTC
5
6# Copyright (C) 1999-2014 Free Software Foundation, Inc.
7# Written by Tom Tromey <tromey@cygnus.com>.
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2, or (at your option)
12# any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22# As a special exception to the GNU General Public License, if you
23# distribute this file as part of a program that contains a
24# configuration script generated by Autoconf, you may include it under
25# the same distribution terms that you use for the rest of that program.
26
27# This file is maintained in Automake, please report
28# bugs to <bug-automake@gnu.org> or send patches to
29# <automake-patches@gnu.org>.
30
31nl='
32'
33
34# We need space, tab and new line, in precisely that order. Quoting is
35# there to prevent tools from complaining about whitespace usage.
36IFS=" "" $nl"
37
38file_conv=
39
40# func_file_conv build_file lazy
41# Convert a $build file to $host form and store it in $file
42# Currently only supports Windows hosts. If the determined conversion
43# type is listed in (the comma separated) LAZY, no conversion will
44# take place.
45func_file_conv ()
46{
47 file=$1
48 case $file in
49 / | /[!/]*) # absolute file, and not a UNC file
50 if test -z "$file_conv"; then
51 # lazily determine how to convert abs files
52 case `uname -s` in
53 MINGW*)
54 file_conv=mingw
55 ;;
56 CYGWIN*)
57 file_conv=cygwin
58 ;;
59 *)
60 file_conv=wine
61 ;;
62 esac
63 fi
64 case $file_conv/,$2, in
65 *,$file_conv,*)
66 ;;
67 mingw/*)
68 file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
69 ;;
70 cygwin/*)
71 file=`cygpath -m "$file" || echo "$file"`
72 ;;
73 wine/*)
74 file=`winepath -w "$file" || echo "$file"`
75 ;;
76 esac
77 ;;
78 esac
79}
80
81# func_cl_dashL linkdir
82# Make cl look for libraries in LINKDIR
83func_cl_dashL ()
84{
85 func_file_conv "$1"
86 if test -z "$lib_path"; then
87 lib_path=$file
88 else
89 lib_path="$lib_path;$file"
90 fi
91 linker_opts="$linker_opts -LIBPATH:$file"
92}
93
94# func_cl_dashl library
95# Do a library search-path lookup for cl
96func_cl_dashl ()
97{
98 lib=$1
99 found=no
100 save_IFS=$IFS
101 IFS=';'
102 for dir in $lib_path $LIB
103 do
104 IFS=$save_IFS
105 if $shared && test -f "$dir/$lib.dll.lib"; then
106 found=yes
107 lib=$dir/$lib.dll.lib
108 break
109 fi
110 if test -f "$dir/$lib.lib"; then
111 found=yes
112 lib=$dir/$lib.lib
113 break
114 fi
115 if test -f "$dir/lib$lib.a"; then
116 found=yes
117 lib=$dir/lib$lib.a
118 break
119 fi
120 done
121 IFS=$save_IFS
122
123 if test "$found" != yes; then
124 lib=$lib.lib
125 fi
126}
127
128# func_cl_wrapper cl arg...
129# Adjust compile command to suit cl
130func_cl_wrapper ()
131{
132 # Assume a capable shell
133 lib_path=
134 shared=:
135 linker_opts=
136 for arg
137 do
138 if test -n "$eat"; then
139 eat=
140 else
141 case $1 in
142 -o)
143 # configure might choose to run compile as 'compile cc -o foo foo.c'.
144 eat=1
145 case $2 in
146 *.o | *.[oO][bB][jJ])
147 func_file_conv "$2"
148 set x "$@" -Fo"$file"
149 shift
150 ;;
151 *)
152 func_file_conv "$2"
153 set x "$@" -Fe"$file"
154 shift
155 ;;
156 esac
157 ;;
158 -I)
159 eat=1
160 func_file_conv "$2" mingw
161 set x "$@" -I"$file"
162 shift
163 ;;
164 -I*)
165 func_file_conv "${1#-I}" mingw
166 set x "$@" -I"$file"
167 shift
168 ;;
169 -l)
170 eat=1
171 func_cl_dashl "$2"
172 set x "$@" "$lib"
173 shift
174 ;;
175 -l*)
176 func_cl_dashl "${1#-l}"
177 set x "$@" "$lib"
178 shift
179 ;;
180 -L)
181 eat=1
182 func_cl_dashL "$2"
183 ;;
184 -L*)
185 func_cl_dashL "${1#-L}"
186 ;;
187 -static)
188 shared=false
189 ;;
190 -Wl,*)
191 arg=${1#-Wl,}
192 save_ifs="$IFS"; IFS=','
193 for flag in $arg; do
194 IFS="$save_ifs"
195 linker_opts="$linker_opts $flag"
196 done
197 IFS="$save_ifs"
198 ;;
199 -Xlinker)
200 eat=1
201 linker_opts="$linker_opts $2"
202 ;;
203 -*)
204 set x "$@" "$1"
205 shift
206 ;;
207 *.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
208 func_file_conv "$1"
209 set x "$@" -Tp"$file"
210 shift
211 ;;
212 *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
213 func_file_conv "$1" mingw
214 set x "$@" "$file"
215 shift
216 ;;
217 *)
218 set x "$@" "$1"
219 shift
220 ;;
221 esac
222 fi
223 shift
224 done
225 if test -n "$linker_opts"; then
226 linker_opts="-link$linker_opts"
227 fi
228 exec "$@" $linker_opts
229 exit 1
230}
231
232eat=
233
234case $1 in
235 '')
236 echo "$0: No command. Try '$0 --help' for more information." 1>&2
237 exit 1;
238 ;;
239 -h | --h*)
240 cat <<\EOF
241Usage: compile [--help] [--version] PROGRAM [ARGS]
242
243Wrapper for compilers which do not understand '-c -o'.
244Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
245arguments, and rename the output as expected.
246
247If you are trying to build a whole package this is not the
248right script to run: please start by reading the file 'INSTALL'.
249
250Report bugs to <bug-automake@gnu.org>.
251EOF
252 exit $?
253 ;;
254 -v | --v*)
255 echo "compile $scriptversion"
256 exit $?
257 ;;
258 cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
259 func_cl_wrapper "$@" # Doesn't return...
260 ;;
261esac
262
263ofile=
264cfile=
265
266for arg
267do
268 if test -n "$eat"; then
269 eat=
270 else
271 case $1 in
272 -o)
273 # configure might choose to run compile as 'compile cc -o foo foo.c'.
274 # So we strip '-o arg' only if arg is an object.
275 eat=1
276 case $2 in
277 *.o | *.obj)
278 ofile=$2
279 ;;
280 *)
281 set x "$@" -o "$2"
282 shift
283 ;;
284 esac
285 ;;
286 *.c)
287 cfile=$1
288 set x "$@" "$1"
289 shift
290 ;;
291 *)
292 set x "$@" "$1"
293 shift
294 ;;
295 esac
296 fi
297 shift
298done
299
300if test -z "$ofile" || test -z "$cfile"; then
301 # If no '-o' option was seen then we might have been invoked from a
302 # pattern rule where we don't need one. That is ok -- this is a
303 # normal compilation that the losing compiler can handle. If no
304 # '.c' file was seen then we are probably linking. That is also
305 # ok.
306 exec "$@"
307fi
308
309# Name of file we expect compiler to create.
310cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
311
312# Create the lock directory.
313# Note: use '[/\\:.-]' here to ensure that we don't use the same name
314# that we are using for the .o file. Also, base the name on the expected
315# object file name, since that is what matters with a parallel build.
316lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
317while true; do
318 if mkdir "$lockdir" >/dev/null 2>&1; then
319 break
320 fi
321 sleep 1
322done
323# FIXME: race condition here if user kills between mkdir and trap.
324trap "rmdir '$lockdir'; exit 1" 1 2 15
325
326# Run the compile.
327"$@"
328ret=$?
329
330if test -f "$cofile"; then
331 test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
332elif test -f "${cofile}bj"; then
333 test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
334fi
335
336rmdir "$lockdir"
337exit $ret
338
339# Local Variables:
340# mode: shell-script
341# sh-indentation: 2
342# eval: (add-hook 'write-file-hooks 'time-stamp)
343# time-stamp-start: "scriptversion="
344# time-stamp-format: "%:y-%02m-%02d.%02H"
345# time-stamp-time-zone: "UTC"
346# time-stamp-end: "; # UTC"
347# End:
diff --git a/apps/plugins/puzzles/src/config.log b/apps/plugins/puzzles/src/config.log
new file mode 100644
index 0000000000..8bf125c515
--- /dev/null
+++ b/apps/plugins/puzzles/src/config.log
@@ -0,0 +1,135 @@
1This file contains any messages produced by compilers while
2running configure, to aid debugging if configure makes a mistake.
3
4It was created by puzzles configure 6.66, which was
5generated by GNU Autoconf 2.69. Invocation command line was
6
7 $ ./configure
8
9## --------- ##
10## Platform. ##
11## --------- ##
12
13hostname = alpha
14uname -m = x86_64
15uname -r = 4.8.4-1-ARCH
16uname -s = Linux
17uname -v = #1 SMP PREEMPT Sat Oct 22 18:26:57 CEST 2016
18
19/usr/bin/uname -p = unknown
20/bin/uname -X = unknown
21
22/bin/arch = unknown
23/usr/bin/arch -k = unknown
24/usr/convex/getsysinfo = unknown
25/usr/bin/hostinfo = unknown
26/bin/machine = unknown
27/usr/bin/oslevel = unknown
28/bin/universe = unknown
29
30PATH: /usr/local/sbin
31PATH: /usr/local/bin
32PATH: /usr/bin
33PATH: /opt/cuda/bin
34PATH: /usr/lib/jvm/default/bin
35PATH: /opt/kde/bin
36PATH: /usr/bin/site_perl
37PATH: /usr/bin/vendor_perl
38PATH: /usr/bin/core_perl
39
40
41## ----------- ##
42## Core tests. ##
43## ----------- ##
44
45
46## ---------------- ##
47## Cache variables. ##
48## ---------------- ##
49
50ac_cv_env_CC_set=
51ac_cv_env_CC_value=
52ac_cv_env_CFLAGS_set=
53ac_cv_env_CFLAGS_value=
54ac_cv_env_CPPFLAGS_set=
55ac_cv_env_CPPFLAGS_value=
56ac_cv_env_LDFLAGS_set=
57ac_cv_env_LDFLAGS_value=
58ac_cv_env_LIBS_set=
59ac_cv_env_LIBS_value=
60ac_cv_env_build_alias_set=
61ac_cv_env_build_alias_value=
62ac_cv_env_host_alias_set=
63ac_cv_env_host_alias_value=
64ac_cv_env_target_alias_set=
65ac_cv_env_target_alias_value=
66
67## ----------------- ##
68## Output variables. ##
69## ----------------- ##
70
71CC=''
72CFLAGS=''
73CPPFLAGS=''
74DEFS=''
75ECHO_C=''
76ECHO_N='-n'
77ECHO_T=''
78EXEEXT=''
79INSTALL_DATA=''
80INSTALL_PROGRAM=''
81INSTALL_SCRIPT=''
82LDFLAGS=''
83LIBOBJS=''
84LIBS=''
85LTLIBOBJS=''
86OBJEXT=''
87PACKAGE_BUGREPORT='anakin@pobox.com'
88PACKAGE_NAME='puzzles'
89PACKAGE_STRING='puzzles 6.66'
90PACKAGE_TARNAME='puzzles'
91PACKAGE_URL=''
92PACKAGE_VERSION='6.66'
93PATH_SEPARATOR=':'
94RANLIB=''
95SHELL='/bin/sh'
96ac_ct_CC=''
97bindir='${exec_prefix}/bin'
98build_alias=''
99datadir='${datarootdir}'
100datarootdir='${prefix}/share'
101docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
102dvidir='${docdir}'
103exec_prefix='NONE'
104host_alias=''
105htmldir='${docdir}'
106includedir='${prefix}/include'
107infodir='${datarootdir}/info'
108libdir='${exec_prefix}/lib'
109libexecdir='${exec_prefix}/libexec'
110localedir='${datarootdir}/locale'
111localstatedir='${prefix}/var'
112mandir='${datarootdir}/man'
113oldincludedir='/usr/include'
114pdfdir='${docdir}'
115prefix='NONE'
116program_transform_name='s,x,x,'
117psdir='${docdir}'
118sbindir='${exec_prefix}/sbin'
119sharedstatedir='${prefix}/com'
120sysconfdir='${prefix}/etc'
121target_alias=''
122
123## ----------- ##
124## confdefs.h. ##
125## ----------- ##
126
127/* confdefs.h */
128#define PACKAGE_NAME "puzzles"
129#define PACKAGE_TARNAME "puzzles"
130#define PACKAGE_VERSION "6.66"
131#define PACKAGE_STRING "puzzles 6.66"
132#define PACKAGE_BUGREPORT "anakin@pobox.com"
133#define PACKAGE_URL ""
134
135configure: exit 2
diff --git a/apps/plugins/puzzles/src/configure b/apps/plugins/puzzles/src/configure
new file mode 100755
index 0000000000..432ca273b3
--- /dev/null
+++ b/apps/plugins/puzzles/src/configure
@@ -0,0 +1,5739 @@
1#! /bin/sh
2# Guess values for system-dependent variables and create Makefiles.
3# Generated by GNU Autoconf 2.69 for puzzles 6.66.
4#
5# Report bugs to <anakin@pobox.com>.
6#
7#
8# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
9#
10#
11# This configure script is free software; the Free Software Foundation
12# gives unlimited permission to copy, distribute and modify it.
13## -------------------- ##
14## M4sh Initialization. ##
15## -------------------- ##
16
17# Be more Bourne compatible
18DUALCASE=1; export DUALCASE # for MKS sh
19if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
20 emulate sh
21 NULLCMD=:
22 # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
23 # is contrary to our usage. Disable this feature.
24 alias -g '${1+"$@"}'='"$@"'
25 setopt NO_GLOB_SUBST
26else
27 case `(set -o) 2>/dev/null` in #(
28 *posix*) :
29 set -o posix ;; #(
30 *) :
31 ;;
32esac
33fi
34
35
36as_nl='
37'
38export as_nl
39# Printing a long string crashes Solaris 7 /usr/bin/printf.
40as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
41as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
42as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
43# Prefer a ksh shell builtin over an external printf program on Solaris,
44# but without wasting forks for bash or zsh.
45if test -z "$BASH_VERSION$ZSH_VERSION" \
46 && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
47 as_echo='print -r --'
48 as_echo_n='print -rn --'
49elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
50 as_echo='printf %s\n'
51 as_echo_n='printf %s'
52else
53 if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
54 as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
55 as_echo_n='/usr/ucb/echo -n'
56 else
57 as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
58 as_echo_n_body='eval
59 arg=$1;
60 case $arg in #(
61 *"$as_nl"*)
62 expr "X$arg" : "X\\(.*\\)$as_nl";
63 arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
64 esac;
65 expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
66 '
67 export as_echo_n_body
68 as_echo_n='sh -c $as_echo_n_body as_echo'
69 fi
70 export as_echo_body
71 as_echo='sh -c $as_echo_body as_echo'
72fi
73
74# The user is always right.
75if test "${PATH_SEPARATOR+set}" != set; then
76 PATH_SEPARATOR=:
77 (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
78 (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
79 PATH_SEPARATOR=';'
80 }
81fi
82
83
84# IFS
85# We need space, tab and new line, in precisely that order. Quoting is
86# there to prevent editors from complaining about space-tab.
87# (If _AS_PATH_WALK were called with IFS unset, it would disable word
88# splitting by setting IFS to empty value.)
89IFS=" "" $as_nl"
90
91# Find who we are. Look in the path if we contain no directory separator.
92as_myself=
93case $0 in #((
94 *[\\/]* ) as_myself=$0 ;;
95 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
96for as_dir in $PATH
97do
98 IFS=$as_save_IFS
99 test -z "$as_dir" && as_dir=.
100 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
101 done
102IFS=$as_save_IFS
103
104 ;;
105esac
106# We did not find ourselves, most probably we were run as `sh COMMAND'
107# in which case we are not to be found in the path.
108if test "x$as_myself" = x; then
109 as_myself=$0
110fi
111if test ! -f "$as_myself"; then
112 $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
113 exit 1
114fi
115
116# Unset variables that we do not need and which cause bugs (e.g. in
117# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
118# suppresses any "Segmentation fault" message there. '((' could
119# trigger a bug in pdksh 5.2.14.
120for as_var in BASH_ENV ENV MAIL MAILPATH
121do eval test x\${$as_var+set} = xset \
122 && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
123done
124PS1='$ '
125PS2='> '
126PS4='+ '
127
128# NLS nuisances.
129LC_ALL=C
130export LC_ALL
131LANGUAGE=C
132export LANGUAGE
133
134# CDPATH.
135(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
136
137# Use a proper internal environment variable to ensure we don't fall
138 # into an infinite loop, continuously re-executing ourselves.
139 if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
140 _as_can_reexec=no; export _as_can_reexec;
141 # We cannot yet assume a decent shell, so we have to provide a
142# neutralization value for shells without unset; and this also
143# works around shells that cannot unset nonexistent variables.
144# Preserve -v and -x to the replacement shell.
145BASH_ENV=/dev/null
146ENV=/dev/null
147(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
148case $- in # ((((
149 *v*x* | *x*v* ) as_opts=-vx ;;
150 *v* ) as_opts=-v ;;
151 *x* ) as_opts=-x ;;
152 * ) as_opts= ;;
153esac
154exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
155# Admittedly, this is quite paranoid, since all the known shells bail
156# out after a failed `exec'.
157$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
158as_fn_exit 255
159 fi
160 # We don't want this to propagate to other subprocesses.
161 { _as_can_reexec=; unset _as_can_reexec;}
162if test "x$CONFIG_SHELL" = x; then
163 as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
164 emulate sh
165 NULLCMD=:
166 # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
167 # is contrary to our usage. Disable this feature.
168 alias -g '\${1+\"\$@\"}'='\"\$@\"'
169 setopt NO_GLOB_SUBST
170else
171 case \`(set -o) 2>/dev/null\` in #(
172 *posix*) :
173 set -o posix ;; #(
174 *) :
175 ;;
176esac
177fi
178"
179 as_required="as_fn_return () { (exit \$1); }
180as_fn_success () { as_fn_return 0; }
181as_fn_failure () { as_fn_return 1; }
182as_fn_ret_success () { return 0; }
183as_fn_ret_failure () { return 1; }
184
185exitcode=0
186as_fn_success || { exitcode=1; echo as_fn_success failed.; }
187as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
188as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
189as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
190if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
191
192else
193 exitcode=1; echo positional parameters were not saved.
194fi
195test x\$exitcode = x0 || exit 1
196test -x / || exit 1"
197 as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
198 as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
199 eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
200 test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
201 if (eval "$as_required") 2>/dev/null; then :
202 as_have_required=yes
203else
204 as_have_required=no
205fi
206 if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
207
208else
209 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
210as_found=false
211for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
212do
213 IFS=$as_save_IFS
214 test -z "$as_dir" && as_dir=.
215 as_found=:
216 case $as_dir in #(
217 /*)
218 for as_base in sh bash ksh sh5; do
219 # Try only shells that exist, to save several forks.
220 as_shell=$as_dir/$as_base
221 if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
222 { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
223 CONFIG_SHELL=$as_shell as_have_required=yes
224 if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
225 break 2
226fi
227fi
228 done;;
229 esac
230 as_found=false
231done
232$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
233 { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
234 CONFIG_SHELL=$SHELL as_have_required=yes
235fi; }
236IFS=$as_save_IFS
237
238
239 if test "x$CONFIG_SHELL" != x; then :
240 export CONFIG_SHELL
241 # We cannot yet assume a decent shell, so we have to provide a
242# neutralization value for shells without unset; and this also
243# works around shells that cannot unset nonexistent variables.
244# Preserve -v and -x to the replacement shell.
245BASH_ENV=/dev/null
246ENV=/dev/null
247(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
248case $- in # ((((
249 *v*x* | *x*v* ) as_opts=-vx ;;
250 *v* ) as_opts=-v ;;
251 *x* ) as_opts=-x ;;
252 * ) as_opts= ;;
253esac
254exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
255# Admittedly, this is quite paranoid, since all the known shells bail
256# out after a failed `exec'.
257$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
258exit 255
259fi
260
261 if test x$as_have_required = xno; then :
262 $as_echo "$0: This script requires a shell more modern than all"
263 $as_echo "$0: the shells that I found on your system."
264 if test x${ZSH_VERSION+set} = xset ; then
265 $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
266 $as_echo "$0: be upgraded to zsh 4.3.4 or later."
267 else
268 $as_echo "$0: Please tell bug-autoconf@gnu.org and anakin@pobox.com
269$0: about your system, including any error possibly output
270$0: before this message. Then install a modern shell, or
271$0: manually run the script under such a shell if you do
272$0: have one."
273 fi
274 exit 1
275fi
276fi
277fi
278SHELL=${CONFIG_SHELL-/bin/sh}
279export SHELL
280# Unset more variables known to interfere with behavior of common tools.
281CLICOLOR_FORCE= GREP_OPTIONS=
282unset CLICOLOR_FORCE GREP_OPTIONS
283
284## --------------------- ##
285## M4sh Shell Functions. ##
286## --------------------- ##
287# as_fn_unset VAR
288# ---------------
289# Portably unset VAR.
290as_fn_unset ()
291{
292 { eval $1=; unset $1;}
293}
294as_unset=as_fn_unset
295
296# as_fn_set_status STATUS
297# -----------------------
298# Set $? to STATUS, without forking.
299as_fn_set_status ()
300{
301 return $1
302} # as_fn_set_status
303
304# as_fn_exit STATUS
305# -----------------
306# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
307as_fn_exit ()
308{
309 set +e
310 as_fn_set_status $1
311 exit $1
312} # as_fn_exit
313
314# as_fn_mkdir_p
315# -------------
316# Create "$as_dir" as a directory, including parents if necessary.
317as_fn_mkdir_p ()
318{
319
320 case $as_dir in #(
321 -*) as_dir=./$as_dir;;
322 esac
323 test -d "$as_dir" || eval $as_mkdir_p || {
324 as_dirs=
325 while :; do
326 case $as_dir in #(
327 *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
328 *) as_qdir=$as_dir;;
329 esac
330 as_dirs="'$as_qdir' $as_dirs"
331 as_dir=`$as_dirname -- "$as_dir" ||
332$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
333 X"$as_dir" : 'X\(//\)[^/]' \| \
334 X"$as_dir" : 'X\(//\)$' \| \
335 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
336$as_echo X"$as_dir" |
337 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
338 s//\1/
339 q
340 }
341 /^X\(\/\/\)[^/].*/{
342 s//\1/
343 q
344 }
345 /^X\(\/\/\)$/{
346 s//\1/
347 q
348 }
349 /^X\(\/\).*/{
350 s//\1/
351 q
352 }
353 s/.*/./; q'`
354 test -d "$as_dir" && break
355 done
356 test -z "$as_dirs" || eval "mkdir $as_dirs"
357 } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
358
359
360} # as_fn_mkdir_p
361
362# as_fn_executable_p FILE
363# -----------------------
364# Test if FILE is an executable regular file.
365as_fn_executable_p ()
366{
367 test -f "$1" && test -x "$1"
368} # as_fn_executable_p
369# as_fn_append VAR VALUE
370# ----------------------
371# Append the text in VALUE to the end of the definition contained in VAR. Take
372# advantage of any shell optimizations that allow amortized linear growth over
373# repeated appends, instead of the typical quadratic growth present in naive
374# implementations.
375if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
376 eval 'as_fn_append ()
377 {
378 eval $1+=\$2
379 }'
380else
381 as_fn_append ()
382 {
383 eval $1=\$$1\$2
384 }
385fi # as_fn_append
386
387# as_fn_arith ARG...
388# ------------------
389# Perform arithmetic evaluation on the ARGs, and store the result in the
390# global $as_val. Take advantage of shells that can avoid forks. The arguments
391# must be portable across $(()) and expr.
392if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
393 eval 'as_fn_arith ()
394 {
395 as_val=$(( $* ))
396 }'
397else
398 as_fn_arith ()
399 {
400 as_val=`expr "$@" || test $? -eq 1`
401 }
402fi # as_fn_arith
403
404
405# as_fn_error STATUS ERROR [LINENO LOG_FD]
406# ----------------------------------------
407# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
408# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
409# script with STATUS, using 1 if that was 0.
410as_fn_error ()
411{
412 as_status=$1; test $as_status -eq 0 && as_status=1
413 if test "$4"; then
414 as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
415 $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
416 fi
417 $as_echo "$as_me: error: $2" >&2
418 as_fn_exit $as_status
419} # as_fn_error
420
421if expr a : '\(a\)' >/dev/null 2>&1 &&
422 test "X`expr 00001 : '.*\(...\)'`" = X001; then
423 as_expr=expr
424else
425 as_expr=false
426fi
427
428if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
429 as_basename=basename
430else
431 as_basename=false
432fi
433
434if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
435 as_dirname=dirname
436else
437 as_dirname=false
438fi
439
440as_me=`$as_basename -- "$0" ||
441$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
442 X"$0" : 'X\(//\)$' \| \
443 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
444$as_echo X/"$0" |
445 sed '/^.*\/\([^/][^/]*\)\/*$/{
446 s//\1/
447 q
448 }
449 /^X\/\(\/\/\)$/{
450 s//\1/
451 q
452 }
453 /^X\/\(\/\).*/{
454 s//\1/
455 q
456 }
457 s/.*/./; q'`
458
459# Avoid depending upon Character Ranges.
460as_cr_letters='abcdefghijklmnopqrstuvwxyz'
461as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
462as_cr_Letters=$as_cr_letters$as_cr_LETTERS
463as_cr_digits='0123456789'
464as_cr_alnum=$as_cr_Letters$as_cr_digits
465
466
467 as_lineno_1=$LINENO as_lineno_1a=$LINENO
468 as_lineno_2=$LINENO as_lineno_2a=$LINENO
469 eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
470 test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
471 # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
472 sed -n '
473 p
474 /[$]LINENO/=
475 ' <$as_myself |
476 sed '
477 s/[$]LINENO.*/&-/
478 t lineno
479 b
480 :lineno
481 N
482 :loop
483 s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
484 t loop
485 s/-\n.*//
486 ' >$as_me.lineno &&
487 chmod +x "$as_me.lineno" ||
488 { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
489
490 # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
491 # already done that, so ensure we don't try to do so again and fall
492 # in an infinite loop. This has already happened in practice.
493 _as_can_reexec=no; export _as_can_reexec
494 # Don't try to exec as it changes $[0], causing all sort of problems
495 # (the dirname of $[0] is not the place where we might find the
496 # original and so on. Autoconf is especially sensitive to this).
497 . "./$as_me.lineno"
498 # Exit status is that of the last command.
499 exit
500}
501
502ECHO_C= ECHO_N= ECHO_T=
503case `echo -n x` in #(((((
504-n*)
505 case `echo 'xy\c'` in
506 *c*) ECHO_T=' ';; # ECHO_T is single tab character.
507 xy) ECHO_C='\c';;
508 *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
509 ECHO_T=' ';;
510 esac;;
511*)
512 ECHO_N='-n';;
513esac
514
515rm -f conf$$ conf$$.exe conf$$.file
516if test -d conf$$.dir; then
517 rm -f conf$$.dir/conf$$.file
518else
519 rm -f conf$$.dir
520 mkdir conf$$.dir 2>/dev/null
521fi
522if (echo >conf$$.file) 2>/dev/null; then
523 if ln -s conf$$.file conf$$ 2>/dev/null; then
524 as_ln_s='ln -s'
525 # ... but there are two gotchas:
526 # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
527 # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
528 # In both cases, we have to default to `cp -pR'.
529 ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
530 as_ln_s='cp -pR'
531 elif ln conf$$.file conf$$ 2>/dev/null; then
532 as_ln_s=ln
533 else
534 as_ln_s='cp -pR'
535 fi
536else
537 as_ln_s='cp -pR'
538fi
539rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
540rmdir conf$$.dir 2>/dev/null
541
542if mkdir -p . 2>/dev/null; then
543 as_mkdir_p='mkdir -p "$as_dir"'
544else
545 test -d ./-p && rmdir ./-p
546 as_mkdir_p=false
547fi
548
549as_test_x='test -x'
550as_executable_p=as_fn_executable_p
551
552# Sed expression to map a string onto a valid CPP name.
553as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
554
555# Sed expression to map a string onto a valid variable name.
556as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
557
558
559test -n "$DJDIR" || exec 7<&0 </dev/null
560exec 6>&1
561
562# Name of the host.
563# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
564# so uname gets run too.
565ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
566
567#
568# Initializations.
569#
570ac_default_prefix=/usr/local
571ac_clean_files=
572ac_config_libobj_dir=.
573LIBOBJS=
574cross_compiling=no
575subdirs=
576MFLAGS=
577MAKEFLAGS=
578
579# Identity of this package.
580PACKAGE_NAME='puzzles'
581PACKAGE_TARNAME='puzzles'
582PACKAGE_VERSION='6.66'
583PACKAGE_STRING='puzzles 6.66'
584PACKAGE_BUGREPORT='anakin@pobox.com'
585PACKAGE_URL=''
586
587ac_unique_file="midend.c"
588ac_subst_vars='am__EXEEXT_FALSE
589am__EXEEXT_TRUE
590LTLIBOBJS
591LIBOBJS
592RANLIB
593PKG_CONFIG_LIBDIR
594PKG_CONFIG_PATH
595GTK_LIBS
596GTK_CFLAGS
597PKG_CONFIG
598am__fastdepCC_FALSE
599am__fastdepCC_TRUE
600CCDEPMODE
601am__nodep
602AMDEPBACKSLASH
603AMDEP_FALSE
604AMDEP_TRUE
605am__quote
606am__include
607DEPDIR
608OBJEXT
609EXEEXT
610ac_ct_CC
611CPPFLAGS
612LDFLAGS
613CFLAGS
614CC
615AM_BACKSLASH
616AM_DEFAULT_VERBOSITY
617AM_DEFAULT_V
618AM_V
619am__untar
620am__tar
621AMTAR
622am__leading_dot
623SET_MAKE
624AWK
625mkdir_p
626MKDIR_P
627INSTALL_STRIP_PROGRAM
628STRIP
629install_sh
630MAKEINFO
631AUTOHEADER
632AUTOMAKE
633AUTOCONF
634ACLOCAL
635VERSION
636PACKAGE
637CYGPATH_W
638am__isrc
639INSTALL_DATA
640INSTALL_SCRIPT
641INSTALL_PROGRAM
642target_alias
643host_alias
644build_alias
645LIBS
646ECHO_T
647ECHO_N
648ECHO_C
649DEFS
650mandir
651localedir
652libdir
653psdir
654pdfdir
655dvidir
656htmldir
657infodir
658docdir
659oldincludedir
660includedir
661localstatedir
662sharedstatedir
663sysconfdir
664datadir
665datarootdir
666libexecdir
667sbindir
668bindir
669program_transform_name
670prefix
671exec_prefix
672PACKAGE_URL
673PACKAGE_BUGREPORT
674PACKAGE_STRING
675PACKAGE_VERSION
676PACKAGE_TARNAME
677PACKAGE_NAME
678PATH_SEPARATOR
679SHELL'
680ac_subst_files=''
681ac_user_opts='
682enable_option_checking
683enable_silent_rules
684enable_dependency_tracking
685with_gtk
686enable_gtktest
687'
688 ac_precious_vars='build_alias
689host_alias
690target_alias
691CC
692CFLAGS
693LDFLAGS
694LIBS
695CPPFLAGS
696PKG_CONFIG
697PKG_CONFIG_PATH
698PKG_CONFIG_LIBDIR'
699
700
701# Initialize some variables set by options.
702ac_init_help=
703ac_init_version=false
704ac_unrecognized_opts=
705ac_unrecognized_sep=
706# The variables have the same names as the options, with
707# dashes changed to underlines.
708cache_file=/dev/null
709exec_prefix=NONE
710no_create=
711no_recursion=
712prefix=NONE
713program_prefix=NONE
714program_suffix=NONE
715program_transform_name=s,x,x,
716silent=
717site=
718srcdir=
719verbose=
720x_includes=NONE
721x_libraries=NONE
722
723# Installation directory options.
724# These are left unexpanded so users can "make install exec_prefix=/foo"
725# and all the variables that are supposed to be based on exec_prefix
726# by default will actually change.
727# Use braces instead of parens because sh, perl, etc. also accept them.
728# (The list follows the same order as the GNU Coding Standards.)
729bindir='${exec_prefix}/bin'
730sbindir='${exec_prefix}/sbin'
731libexecdir='${exec_prefix}/libexec'
732datarootdir='${prefix}/share'
733datadir='${datarootdir}'
734sysconfdir='${prefix}/etc'
735sharedstatedir='${prefix}/com'
736localstatedir='${prefix}/var'
737includedir='${prefix}/include'
738oldincludedir='/usr/include'
739docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
740infodir='${datarootdir}/info'
741htmldir='${docdir}'
742dvidir='${docdir}'
743pdfdir='${docdir}'
744psdir='${docdir}'
745libdir='${exec_prefix}/lib'
746localedir='${datarootdir}/locale'
747mandir='${datarootdir}/man'
748
749ac_prev=
750ac_dashdash=
751for ac_option
752do
753 # If the previous option needs an argument, assign it.
754 if test -n "$ac_prev"; then
755 eval $ac_prev=\$ac_option
756 ac_prev=
757 continue
758 fi
759
760 case $ac_option in
761 *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
762 *=) ac_optarg= ;;
763 *) ac_optarg=yes ;;
764 esac
765
766 # Accept the important Cygnus configure options, so we can diagnose typos.
767
768 case $ac_dashdash$ac_option in
769 --)
770 ac_dashdash=yes ;;
771
772 -bindir | --bindir | --bindi | --bind | --bin | --bi)
773 ac_prev=bindir ;;
774 -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
775 bindir=$ac_optarg ;;
776
777 -build | --build | --buil | --bui | --bu)
778 ac_prev=build_alias ;;
779 -build=* | --build=* | --buil=* | --bui=* | --bu=*)
780 build_alias=$ac_optarg ;;
781
782 -cache-file | --cache-file | --cache-fil | --cache-fi \
783 | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
784 ac_prev=cache_file ;;
785 -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
786 | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
787 cache_file=$ac_optarg ;;
788
789 --config-cache | -C)
790 cache_file=config.cache ;;
791
792 -datadir | --datadir | --datadi | --datad)
793 ac_prev=datadir ;;
794 -datadir=* | --datadir=* | --datadi=* | --datad=*)
795 datadir=$ac_optarg ;;
796
797 -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
798 | --dataroo | --dataro | --datar)
799 ac_prev=datarootdir ;;
800 -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
801 | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
802 datarootdir=$ac_optarg ;;
803
804 -disable-* | --disable-*)
805 ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
806 # Reject names that are not valid shell variable names.
807 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
808 as_fn_error $? "invalid feature name: $ac_useropt"
809 ac_useropt_orig=$ac_useropt
810 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
811 case $ac_user_opts in
812 *"
813"enable_$ac_useropt"
814"*) ;;
815 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
816 ac_unrecognized_sep=', ';;
817 esac
818 eval enable_$ac_useropt=no ;;
819
820 -docdir | --docdir | --docdi | --doc | --do)
821 ac_prev=docdir ;;
822 -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
823 docdir=$ac_optarg ;;
824
825 -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
826 ac_prev=dvidir ;;
827 -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
828 dvidir=$ac_optarg ;;
829
830 -enable-* | --enable-*)
831 ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
832 # Reject names that are not valid shell variable names.
833 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
834 as_fn_error $? "invalid feature name: $ac_useropt"
835 ac_useropt_orig=$ac_useropt
836 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
837 case $ac_user_opts in
838 *"
839"enable_$ac_useropt"
840"*) ;;
841 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
842 ac_unrecognized_sep=', ';;
843 esac
844 eval enable_$ac_useropt=\$ac_optarg ;;
845
846 -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
847 | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
848 | --exec | --exe | --ex)
849 ac_prev=exec_prefix ;;
850 -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
851 | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
852 | --exec=* | --exe=* | --ex=*)
853 exec_prefix=$ac_optarg ;;
854
855 -gas | --gas | --ga | --g)
856 # Obsolete; use --with-gas.
857 with_gas=yes ;;
858
859 -help | --help | --hel | --he | -h)
860 ac_init_help=long ;;
861 -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
862 ac_init_help=recursive ;;
863 -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
864 ac_init_help=short ;;
865
866 -host | --host | --hos | --ho)
867 ac_prev=host_alias ;;
868 -host=* | --host=* | --hos=* | --ho=*)
869 host_alias=$ac_optarg ;;
870
871 -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
872 ac_prev=htmldir ;;
873 -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
874 | --ht=*)
875 htmldir=$ac_optarg ;;
876
877 -includedir | --includedir | --includedi | --included | --include \
878 | --includ | --inclu | --incl | --inc)
879 ac_prev=includedir ;;
880 -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
881 | --includ=* | --inclu=* | --incl=* | --inc=*)
882 includedir=$ac_optarg ;;
883
884 -infodir | --infodir | --infodi | --infod | --info | --inf)
885 ac_prev=infodir ;;
886 -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
887 infodir=$ac_optarg ;;
888
889 -libdir | --libdir | --libdi | --libd)
890 ac_prev=libdir ;;
891 -libdir=* | --libdir=* | --libdi=* | --libd=*)
892 libdir=$ac_optarg ;;
893
894 -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
895 | --libexe | --libex | --libe)
896 ac_prev=libexecdir ;;
897 -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
898 | --libexe=* | --libex=* | --libe=*)
899 libexecdir=$ac_optarg ;;
900
901 -localedir | --localedir | --localedi | --localed | --locale)
902 ac_prev=localedir ;;
903 -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
904 localedir=$ac_optarg ;;
905
906 -localstatedir | --localstatedir | --localstatedi | --localstated \
907 | --localstate | --localstat | --localsta | --localst | --locals)
908 ac_prev=localstatedir ;;
909 -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
910 | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
911 localstatedir=$ac_optarg ;;
912
913 -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
914 ac_prev=mandir ;;
915 -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
916 mandir=$ac_optarg ;;
917
918 -nfp | --nfp | --nf)
919 # Obsolete; use --without-fp.
920 with_fp=no ;;
921
922 -no-create | --no-create | --no-creat | --no-crea | --no-cre \
923 | --no-cr | --no-c | -n)
924 no_create=yes ;;
925
926 -no-recursion | --no-recursion | --no-recursio | --no-recursi \
927 | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
928 no_recursion=yes ;;
929
930 -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
931 | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
932 | --oldin | --oldi | --old | --ol | --o)
933 ac_prev=oldincludedir ;;
934 -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
935 | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
936 | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
937 oldincludedir=$ac_optarg ;;
938
939 -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
940 ac_prev=prefix ;;
941 -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
942 prefix=$ac_optarg ;;
943
944 -program-prefix | --program-prefix | --program-prefi | --program-pref \
945 | --program-pre | --program-pr | --program-p)
946 ac_prev=program_prefix ;;
947 -program-prefix=* | --program-prefix=* | --program-prefi=* \
948 | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
949 program_prefix=$ac_optarg ;;
950
951 -program-suffix | --program-suffix | --program-suffi | --program-suff \
952 | --program-suf | --program-su | --program-s)
953 ac_prev=program_suffix ;;
954 -program-suffix=* | --program-suffix=* | --program-suffi=* \
955 | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
956 program_suffix=$ac_optarg ;;
957
958 -program-transform-name | --program-transform-name \
959 | --program-transform-nam | --program-transform-na \
960 | --program-transform-n | --program-transform- \
961 | --program-transform | --program-transfor \
962 | --program-transfo | --program-transf \
963 | --program-trans | --program-tran \
964 | --progr-tra | --program-tr | --program-t)
965 ac_prev=program_transform_name ;;
966 -program-transform-name=* | --program-transform-name=* \
967 | --program-transform-nam=* | --program-transform-na=* \
968 | --program-transform-n=* | --program-transform-=* \
969 | --program-transform=* | --program-transfor=* \
970 | --program-transfo=* | --program-transf=* \
971 | --program-trans=* | --program-tran=* \
972 | --progr-tra=* | --program-tr=* | --program-t=*)
973 program_transform_name=$ac_optarg ;;
974
975 -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
976 ac_prev=pdfdir ;;
977 -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
978 pdfdir=$ac_optarg ;;
979
980 -psdir | --psdir | --psdi | --psd | --ps)
981 ac_prev=psdir ;;
982 -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
983 psdir=$ac_optarg ;;
984
985 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
986 | -silent | --silent | --silen | --sile | --sil)
987 silent=yes ;;
988
989 -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
990 ac_prev=sbindir ;;
991 -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
992 | --sbi=* | --sb=*)
993 sbindir=$ac_optarg ;;
994
995 -sharedstatedir | --sharedstatedir | --sharedstatedi \
996 | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
997 | --sharedst | --shareds | --shared | --share | --shar \
998 | --sha | --sh)
999 ac_prev=sharedstatedir ;;
1000 -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
1001 | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
1002 | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
1003 | --sha=* | --sh=*)
1004 sharedstatedir=$ac_optarg ;;
1005
1006 -site | --site | --sit)
1007 ac_prev=site ;;
1008 -site=* | --site=* | --sit=*)
1009 site=$ac_optarg ;;
1010
1011 -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
1012 ac_prev=srcdir ;;
1013 -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
1014 srcdir=$ac_optarg ;;
1015
1016 -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
1017 | --syscon | --sysco | --sysc | --sys | --sy)
1018 ac_prev=sysconfdir ;;
1019 -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
1020 | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
1021 sysconfdir=$ac_optarg ;;
1022
1023 -target | --target | --targe | --targ | --tar | --ta | --t)
1024 ac_prev=target_alias ;;
1025 -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
1026 target_alias=$ac_optarg ;;
1027
1028 -v | -verbose | --verbose | --verbos | --verbo | --verb)
1029 verbose=yes ;;
1030
1031 -version | --version | --versio | --versi | --vers | -V)
1032 ac_init_version=: ;;
1033
1034 -with-* | --with-*)
1035 ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
1036 # Reject names that are not valid shell variable names.
1037 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
1038 as_fn_error $? "invalid package name: $ac_useropt"
1039 ac_useropt_orig=$ac_useropt
1040 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
1041 case $ac_user_opts in
1042 *"
1043"with_$ac_useropt"
1044"*) ;;
1045 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
1046 ac_unrecognized_sep=', ';;
1047 esac
1048 eval with_$ac_useropt=\$ac_optarg ;;
1049
1050 -without-* | --without-*)
1051 ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
1052 # Reject names that are not valid shell variable names.
1053 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
1054 as_fn_error $? "invalid package name: $ac_useropt"
1055 ac_useropt_orig=$ac_useropt
1056 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
1057 case $ac_user_opts in
1058 *"
1059"with_$ac_useropt"
1060"*) ;;
1061 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
1062 ac_unrecognized_sep=', ';;
1063 esac
1064 eval with_$ac_useropt=no ;;
1065
1066 --x)
1067 # Obsolete; use --with-x.
1068 with_x=yes ;;
1069
1070 -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
1071 | --x-incl | --x-inc | --x-in | --x-i)
1072 ac_prev=x_includes ;;
1073 -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
1074 | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
1075 x_includes=$ac_optarg ;;
1076
1077 -x-libraries | --x-libraries | --x-librarie | --x-librari \
1078 | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
1079 ac_prev=x_libraries ;;
1080 -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
1081 | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
1082 x_libraries=$ac_optarg ;;
1083
1084 -*) as_fn_error $? "unrecognized option: \`$ac_option'
1085Try \`$0 --help' for more information"
1086 ;;
1087
1088 *=*)
1089 ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
1090 # Reject names that are not valid shell variable names.
1091 case $ac_envvar in #(
1092 '' | [0-9]* | *[!_$as_cr_alnum]* )
1093 as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
1094 esac
1095 eval $ac_envvar=\$ac_optarg
1096 export $ac_envvar ;;
1097
1098 *)
1099 # FIXME: should be removed in autoconf 3.0.
1100 $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
1101 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
1102 $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
1103 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
1104 ;;
1105
1106 esac
1107done
1108
1109if test -n "$ac_prev"; then
1110 ac_option=--`echo $ac_prev | sed 's/_/-/g'`
1111 as_fn_error $? "missing argument to $ac_option"
1112fi
1113
1114if test -n "$ac_unrecognized_opts"; then
1115 case $enable_option_checking in
1116 no) ;;
1117 fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
1118 *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
1119 esac
1120fi
1121
1122# Check all directory arguments for consistency.
1123for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
1124 datadir sysconfdir sharedstatedir localstatedir includedir \
1125 oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
1126 libdir localedir mandir
1127do
1128 eval ac_val=\$$ac_var
1129 # Remove trailing slashes.
1130 case $ac_val in
1131 */ )
1132 ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
1133 eval $ac_var=\$ac_val;;
1134 esac
1135 # Be sure to have absolute directory names.
1136 case $ac_val in
1137 [\\/$]* | ?:[\\/]* ) continue;;
1138 NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
1139 esac
1140 as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
1141done
1142
1143# There might be people who depend on the old broken behavior: `$host'
1144# used to hold the argument of --host etc.
1145# FIXME: To remove some day.
1146build=$build_alias
1147host=$host_alias
1148target=$target_alias
1149
1150# FIXME: To remove some day.
1151if test "x$host_alias" != x; then
1152 if test "x$build_alias" = x; then
1153 cross_compiling=maybe
1154 elif test "x$build_alias" != "x$host_alias"; then
1155 cross_compiling=yes
1156 fi
1157fi
1158
1159ac_tool_prefix=
1160test -n "$host_alias" && ac_tool_prefix=$host_alias-
1161
1162test "$silent" = yes && exec 6>/dev/null
1163
1164
1165ac_pwd=`pwd` && test -n "$ac_pwd" &&
1166ac_ls_di=`ls -di .` &&
1167ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
1168 as_fn_error $? "working directory cannot be determined"
1169test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
1170 as_fn_error $? "pwd does not report name of working directory"
1171
1172
1173# Find the source files, if location was not specified.
1174if test -z "$srcdir"; then
1175 ac_srcdir_defaulted=yes
1176 # Try the directory containing this script, then the parent directory.
1177 ac_confdir=`$as_dirname -- "$as_myself" ||
1178$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
1179 X"$as_myself" : 'X\(//\)[^/]' \| \
1180 X"$as_myself" : 'X\(//\)$' \| \
1181 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
1182$as_echo X"$as_myself" |
1183 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
1184 s//\1/
1185 q
1186 }
1187 /^X\(\/\/\)[^/].*/{
1188 s//\1/
1189 q
1190 }
1191 /^X\(\/\/\)$/{
1192 s//\1/
1193 q
1194 }
1195 /^X\(\/\).*/{
1196 s//\1/
1197 q
1198 }
1199 s/.*/./; q'`
1200 srcdir=$ac_confdir
1201 if test ! -r "$srcdir/$ac_unique_file"; then
1202 srcdir=..
1203 fi
1204else
1205 ac_srcdir_defaulted=no
1206fi
1207if test ! -r "$srcdir/$ac_unique_file"; then
1208 test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
1209 as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
1210fi
1211ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
1212ac_abs_confdir=`(
1213 cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
1214 pwd)`
1215# When building in place, set srcdir=.
1216if test "$ac_abs_confdir" = "$ac_pwd"; then
1217 srcdir=.
1218fi
1219# Remove unnecessary trailing slashes from srcdir.
1220# Double slashes in file names in object file debugging info
1221# mess up M-x gdb in Emacs.
1222case $srcdir in
1223*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
1224esac
1225for ac_var in $ac_precious_vars; do
1226 eval ac_env_${ac_var}_set=\${${ac_var}+set}
1227 eval ac_env_${ac_var}_value=\$${ac_var}
1228 eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
1229 eval ac_cv_env_${ac_var}_value=\$${ac_var}
1230done
1231
1232#
1233# Report the --help message.
1234#
1235if test "$ac_init_help" = "long"; then
1236 # Omit some internal or obsolete options to make the list less imposing.
1237 # This message is too long to be a string in the A/UX 3.1 sh.
1238 cat <<_ACEOF
1239\`configure' configures puzzles 6.66 to adapt to many kinds of systems.
1240
1241Usage: $0 [OPTION]... [VAR=VALUE]...
1242
1243To assign environment variables (e.g., CC, CFLAGS...), specify them as
1244VAR=VALUE. See below for descriptions of some of the useful variables.
1245
1246Defaults for the options are specified in brackets.
1247
1248Configuration:
1249 -h, --help display this help and exit
1250 --help=short display options specific to this package
1251 --help=recursive display the short help of all the included packages
1252 -V, --version display version information and exit
1253 -q, --quiet, --silent do not print \`checking ...' messages
1254 --cache-file=FILE cache test results in FILE [disabled]
1255 -C, --config-cache alias for \`--cache-file=config.cache'
1256 -n, --no-create do not create output files
1257 --srcdir=DIR find the sources in DIR [configure dir or \`..']
1258
1259Installation directories:
1260 --prefix=PREFIX install architecture-independent files in PREFIX
1261 [$ac_default_prefix]
1262 --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
1263 [PREFIX]
1264
1265By default, \`make install' will install all the files in
1266\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
1267an installation prefix other than \`$ac_default_prefix' using \`--prefix',
1268for instance \`--prefix=\$HOME'.
1269
1270For better control, use the options below.
1271
1272Fine tuning of the installation directories:
1273 --bindir=DIR user executables [EPREFIX/bin]
1274 --sbindir=DIR system admin executables [EPREFIX/sbin]
1275 --libexecdir=DIR program executables [EPREFIX/libexec]
1276 --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
1277 --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
1278 --localstatedir=DIR modifiable single-machine data [PREFIX/var]
1279 --libdir=DIR object code libraries [EPREFIX/lib]
1280 --includedir=DIR C header files [PREFIX/include]
1281 --oldincludedir=DIR C header files for non-gcc [/usr/include]
1282 --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
1283 --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
1284 --infodir=DIR info documentation [DATAROOTDIR/info]
1285 --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
1286 --mandir=DIR man documentation [DATAROOTDIR/man]
1287 --docdir=DIR documentation root [DATAROOTDIR/doc/puzzles]
1288 --htmldir=DIR html documentation [DOCDIR]
1289 --dvidir=DIR dvi documentation [DOCDIR]
1290 --pdfdir=DIR pdf documentation [DOCDIR]
1291 --psdir=DIR ps documentation [DOCDIR]
1292_ACEOF
1293
1294 cat <<\_ACEOF
1295
1296Program names:
1297 --program-prefix=PREFIX prepend PREFIX to installed program names
1298 --program-suffix=SUFFIX append SUFFIX to installed program names
1299 --program-transform-name=PROGRAM run sed PROGRAM on installed program names
1300_ACEOF
1301fi
1302
1303if test -n "$ac_init_help"; then
1304 case $ac_init_help in
1305 short | recursive ) echo "Configuration of puzzles 6.66:";;
1306 esac
1307 cat <<\_ACEOF
1308
1309Optional Features:
1310 --disable-option-checking ignore unrecognized --enable/--with options
1311 --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
1312 --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
1313 --enable-silent-rules less verbose build output (undo: "make V=1")
1314 --disable-silent-rules verbose build output (undo: "make V=0")
1315 --enable-dependency-tracking
1316 do not reject slow dependency extractors
1317 --disable-dependency-tracking
1318 speeds up one-time build
1319 --disable-gtktest do not try to compile and run a test GTK+ program
1320
1321Optional Packages:
1322 --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
1323 --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
1324 --with-gtk=VER specify GTK version to use (`2' or `3')
1325
1326Some influential environment variables:
1327 CC C compiler command
1328 CFLAGS C compiler flags
1329 LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
1330 nonstandard directory <lib dir>
1331 LIBS libraries to pass to the linker, e.g. -l<library>
1332 CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
1333 you have headers in a nonstandard directory <include dir>
1334 PKG_CONFIG path to pkg-config utility
1335 PKG_CONFIG_PATH
1336 directories to add to pkg-config's search path
1337 PKG_CONFIG_LIBDIR
1338 path overriding pkg-config's built-in search path
1339
1340Use these variables to override the choices made by `configure' or to help
1341it to find libraries and programs with nonstandard names/locations.
1342
1343Report bugs to <anakin@pobox.com>.
1344_ACEOF
1345ac_status=$?
1346fi
1347
1348if test "$ac_init_help" = "recursive"; then
1349 # If there are subdirs, report their specific --help.
1350 for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
1351 test -d "$ac_dir" ||
1352 { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
1353 continue
1354 ac_builddir=.
1355
1356case "$ac_dir" in
1357.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
1358*)
1359 ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
1360 # A ".." for each directory in $ac_dir_suffix.
1361 ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
1362 case $ac_top_builddir_sub in
1363 "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
1364 *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
1365 esac ;;
1366esac
1367ac_abs_top_builddir=$ac_pwd
1368ac_abs_builddir=$ac_pwd$ac_dir_suffix
1369# for backward compatibility:
1370ac_top_builddir=$ac_top_build_prefix
1371
1372case $srcdir in
1373 .) # We are building in place.
1374 ac_srcdir=.
1375 ac_top_srcdir=$ac_top_builddir_sub
1376 ac_abs_top_srcdir=$ac_pwd ;;
1377 [\\/]* | ?:[\\/]* ) # Absolute name.
1378 ac_srcdir=$srcdir$ac_dir_suffix;
1379 ac_top_srcdir=$srcdir
1380 ac_abs_top_srcdir=$srcdir ;;
1381 *) # Relative name.
1382 ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
1383 ac_top_srcdir=$ac_top_build_prefix$srcdir
1384 ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
1385esac
1386ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
1387
1388 cd "$ac_dir" || { ac_status=$?; continue; }
1389 # Check for guested configure.
1390 if test -f "$ac_srcdir/configure.gnu"; then
1391 echo &&
1392 $SHELL "$ac_srcdir/configure.gnu" --help=recursive
1393 elif test -f "$ac_srcdir/configure"; then
1394 echo &&
1395 $SHELL "$ac_srcdir/configure" --help=recursive
1396 else
1397 $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
1398 fi || ac_status=$?
1399 cd "$ac_pwd" || { ac_status=$?; break; }
1400 done
1401fi
1402
1403test -n "$ac_init_help" && exit $ac_status
1404if $ac_init_version; then
1405 cat <<\_ACEOF
1406puzzles configure 6.66
1407generated by GNU Autoconf 2.69
1408
1409Copyright (C) 2012 Free Software Foundation, Inc.
1410This configure script is free software; the Free Software Foundation
1411gives unlimited permission to copy, distribute and modify it.
1412_ACEOF
1413 exit
1414fi
1415
1416## ------------------------ ##
1417## Autoconf initialization. ##
1418## ------------------------ ##
1419
1420# ac_fn_c_try_compile LINENO
1421# --------------------------
1422# Try to compile conftest.$ac_ext, and return whether this succeeded.
1423ac_fn_c_try_compile ()
1424{
1425 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1426 rm -f conftest.$ac_objext
1427 if { { ac_try="$ac_compile"
1428case "(($ac_try" in
1429 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1430 *) ac_try_echo=$ac_try;;
1431esac
1432eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1433$as_echo "$ac_try_echo"; } >&5
1434 (eval "$ac_compile") 2>conftest.err
1435 ac_status=$?
1436 if test -s conftest.err; then
1437 grep -v '^ *+' conftest.err >conftest.er1
1438 cat conftest.er1 >&5
1439 mv -f conftest.er1 conftest.err
1440 fi
1441 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1442 test $ac_status = 0; } && {
1443 test -z "$ac_c_werror_flag" ||
1444 test ! -s conftest.err
1445 } && test -s conftest.$ac_objext; then :
1446 ac_retval=0
1447else
1448 $as_echo "$as_me: failed program was:" >&5
1449sed 's/^/| /' conftest.$ac_ext >&5
1450
1451 ac_retval=1
1452fi
1453 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1454 as_fn_set_status $ac_retval
1455
1456} # ac_fn_c_try_compile
1457
1458# ac_fn_c_try_run LINENO
1459# ----------------------
1460# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
1461# that executables *can* be run.
1462ac_fn_c_try_run ()
1463{
1464 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1465 if { { ac_try="$ac_link"
1466case "(($ac_try" in
1467 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1468 *) ac_try_echo=$ac_try;;
1469esac
1470eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1471$as_echo "$ac_try_echo"; } >&5
1472 (eval "$ac_link") 2>&5
1473 ac_status=$?
1474 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1475 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
1476 { { case "(($ac_try" in
1477 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1478 *) ac_try_echo=$ac_try;;
1479esac
1480eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1481$as_echo "$ac_try_echo"; } >&5
1482 (eval "$ac_try") 2>&5
1483 ac_status=$?
1484 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1485 test $ac_status = 0; }; }; then :
1486 ac_retval=0
1487else
1488 $as_echo "$as_me: program exited with status $ac_status" >&5
1489 $as_echo "$as_me: failed program was:" >&5
1490sed 's/^/| /' conftest.$ac_ext >&5
1491
1492 ac_retval=$ac_status
1493fi
1494 rm -rf conftest.dSYM conftest_ipa8_conftest.oo
1495 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1496 as_fn_set_status $ac_retval
1497
1498} # ac_fn_c_try_run
1499
1500# ac_fn_c_try_link LINENO
1501# -----------------------
1502# Try to link conftest.$ac_ext, and return whether this succeeded.
1503ac_fn_c_try_link ()
1504{
1505 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1506 rm -f conftest.$ac_objext conftest$ac_exeext
1507 if { { ac_try="$ac_link"
1508case "(($ac_try" in
1509 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1510 *) ac_try_echo=$ac_try;;
1511esac
1512eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1513$as_echo "$ac_try_echo"; } >&5
1514 (eval "$ac_link") 2>conftest.err
1515 ac_status=$?
1516 if test -s conftest.err; then
1517 grep -v '^ *+' conftest.err >conftest.er1
1518 cat conftest.er1 >&5
1519 mv -f conftest.er1 conftest.err
1520 fi
1521 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1522 test $ac_status = 0; } && {
1523 test -z "$ac_c_werror_flag" ||
1524 test ! -s conftest.err
1525 } && test -s conftest$ac_exeext && {
1526 test "$cross_compiling" = yes ||
1527 test -x conftest$ac_exeext
1528 }; then :
1529 ac_retval=0
1530else
1531 $as_echo "$as_me: failed program was:" >&5
1532sed 's/^/| /' conftest.$ac_ext >&5
1533
1534 ac_retval=1
1535fi
1536 # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
1537 # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
1538 # interfere with the next link command; also delete a directory that is
1539 # left behind by Apple's compiler. We do this before executing the actions.
1540 rm -rf conftest.dSYM conftest_ipa8_conftest.oo
1541 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1542 as_fn_set_status $ac_retval
1543
1544} # ac_fn_c_try_link
1545cat >config.log <<_ACEOF
1546This file contains any messages produced by compilers while
1547running configure, to aid debugging if configure makes a mistake.
1548
1549It was created by puzzles $as_me 6.66, which was
1550generated by GNU Autoconf 2.69. Invocation command line was
1551
1552 $ $0 $@
1553
1554_ACEOF
1555exec 5>>config.log
1556{
1557cat <<_ASUNAME
1558## --------- ##
1559## Platform. ##
1560## --------- ##
1561
1562hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
1563uname -m = `(uname -m) 2>/dev/null || echo unknown`
1564uname -r = `(uname -r) 2>/dev/null || echo unknown`
1565uname -s = `(uname -s) 2>/dev/null || echo unknown`
1566uname -v = `(uname -v) 2>/dev/null || echo unknown`
1567
1568/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
1569/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
1570
1571/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
1572/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
1573/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
1574/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
1575/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
1576/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
1577/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
1578
1579_ASUNAME
1580
1581as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1582for as_dir in $PATH
1583do
1584 IFS=$as_save_IFS
1585 test -z "$as_dir" && as_dir=.
1586 $as_echo "PATH: $as_dir"
1587 done
1588IFS=$as_save_IFS
1589
1590} >&5
1591
1592cat >&5 <<_ACEOF
1593
1594
1595## ----------- ##
1596## Core tests. ##
1597## ----------- ##
1598
1599_ACEOF
1600
1601
1602# Keep a trace of the command line.
1603# Strip out --no-create and --no-recursion so they do not pile up.
1604# Strip out --silent because we don't want to record it for future runs.
1605# Also quote any args containing shell meta-characters.
1606# Make two passes to allow for proper duplicate-argument suppression.
1607ac_configure_args=
1608ac_configure_args0=
1609ac_configure_args1=
1610ac_must_keep_next=false
1611for ac_pass in 1 2
1612do
1613 for ac_arg
1614 do
1615 case $ac_arg in
1616 -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
1617 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
1618 | -silent | --silent | --silen | --sile | --sil)
1619 continue ;;
1620 *\'*)
1621 ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
1622 esac
1623 case $ac_pass in
1624 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
1625 2)
1626 as_fn_append ac_configure_args1 " '$ac_arg'"
1627 if test $ac_must_keep_next = true; then
1628 ac_must_keep_next=false # Got value, back to normal.
1629 else
1630 case $ac_arg in
1631 *=* | --config-cache | -C | -disable-* | --disable-* \
1632 | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
1633 | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
1634 | -with-* | --with-* | -without-* | --without-* | --x)
1635 case "$ac_configure_args0 " in
1636 "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
1637 esac
1638 ;;
1639 -* ) ac_must_keep_next=true ;;
1640 esac
1641 fi
1642 as_fn_append ac_configure_args " '$ac_arg'"
1643 ;;
1644 esac
1645 done
1646done
1647{ ac_configure_args0=; unset ac_configure_args0;}
1648{ ac_configure_args1=; unset ac_configure_args1;}
1649
1650# When interrupted or exit'd, cleanup temporary files, and complete
1651# config.log. We remove comments because anyway the quotes in there
1652# would cause problems or look ugly.
1653# WARNING: Use '\'' to represent an apostrophe within the trap.
1654# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
1655trap 'exit_status=$?
1656 # Save into config.log some information that might help in debugging.
1657 {
1658 echo
1659
1660 $as_echo "## ---------------- ##
1661## Cache variables. ##
1662## ---------------- ##"
1663 echo
1664 # The following way of writing the cache mishandles newlines in values,
1665(
1666 for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
1667 eval ac_val=\$$ac_var
1668 case $ac_val in #(
1669 *${as_nl}*)
1670 case $ac_var in #(
1671 *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
1672$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
1673 esac
1674 case $ac_var in #(
1675 _ | IFS | as_nl) ;; #(
1676 BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
1677 *) { eval $ac_var=; unset $ac_var;} ;;
1678 esac ;;
1679 esac
1680 done
1681 (set) 2>&1 |
1682 case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
1683 *${as_nl}ac_space=\ *)
1684 sed -n \
1685 "s/'\''/'\''\\\\'\'''\''/g;
1686 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
1687 ;; #(
1688 *)
1689 sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
1690 ;;
1691 esac |
1692 sort
1693)
1694 echo
1695
1696 $as_echo "## ----------------- ##
1697## Output variables. ##
1698## ----------------- ##"
1699 echo
1700 for ac_var in $ac_subst_vars
1701 do
1702 eval ac_val=\$$ac_var
1703 case $ac_val in
1704 *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
1705 esac
1706 $as_echo "$ac_var='\''$ac_val'\''"
1707 done | sort
1708 echo
1709
1710 if test -n "$ac_subst_files"; then
1711 $as_echo "## ------------------- ##
1712## File substitutions. ##
1713## ------------------- ##"
1714 echo
1715 for ac_var in $ac_subst_files
1716 do
1717 eval ac_val=\$$ac_var
1718 case $ac_val in
1719 *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
1720 esac
1721 $as_echo "$ac_var='\''$ac_val'\''"
1722 done | sort
1723 echo
1724 fi
1725
1726 if test -s confdefs.h; then
1727 $as_echo "## ----------- ##
1728## confdefs.h. ##
1729## ----------- ##"
1730 echo
1731 cat confdefs.h
1732 echo
1733 fi
1734 test "$ac_signal" != 0 &&
1735 $as_echo "$as_me: caught signal $ac_signal"
1736 $as_echo "$as_me: exit $exit_status"
1737 } >&5
1738 rm -f core *.core core.conftest.* &&
1739 rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
1740 exit $exit_status
1741' 0
1742for ac_signal in 1 2 13 15; do
1743 trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
1744done
1745ac_signal=0
1746
1747# confdefs.h avoids OS command line length limits that DEFS can exceed.
1748rm -f -r conftest* confdefs.h
1749
1750$as_echo "/* confdefs.h */" > confdefs.h
1751
1752# Predefined preprocessor variables.
1753
1754cat >>confdefs.h <<_ACEOF
1755#define PACKAGE_NAME "$PACKAGE_NAME"
1756_ACEOF
1757
1758cat >>confdefs.h <<_ACEOF
1759#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
1760_ACEOF
1761
1762cat >>confdefs.h <<_ACEOF
1763#define PACKAGE_VERSION "$PACKAGE_VERSION"
1764_ACEOF
1765
1766cat >>confdefs.h <<_ACEOF
1767#define PACKAGE_STRING "$PACKAGE_STRING"
1768_ACEOF
1769
1770cat >>confdefs.h <<_ACEOF
1771#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
1772_ACEOF
1773
1774cat >>confdefs.h <<_ACEOF
1775#define PACKAGE_URL "$PACKAGE_URL"
1776_ACEOF
1777
1778
1779# Let the site file select an alternate cache file if it wants to.
1780# Prefer an explicitly selected file to automatically selected ones.
1781ac_site_file1=NONE
1782ac_site_file2=NONE
1783if test -n "$CONFIG_SITE"; then
1784 # We do not want a PATH search for config.site.
1785 case $CONFIG_SITE in #((
1786 -*) ac_site_file1=./$CONFIG_SITE;;
1787 */*) ac_site_file1=$CONFIG_SITE;;
1788 *) ac_site_file1=./$CONFIG_SITE;;
1789 esac
1790elif test "x$prefix" != xNONE; then
1791 ac_site_file1=$prefix/share/config.site
1792 ac_site_file2=$prefix/etc/config.site
1793else
1794 ac_site_file1=$ac_default_prefix/share/config.site
1795 ac_site_file2=$ac_default_prefix/etc/config.site
1796fi
1797for ac_site_file in "$ac_site_file1" "$ac_site_file2"
1798do
1799 test "x$ac_site_file" = xNONE && continue
1800 if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
1801 { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
1802$as_echo "$as_me: loading site script $ac_site_file" >&6;}
1803 sed 's/^/| /' "$ac_site_file" >&5
1804 . "$ac_site_file" \
1805 || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
1806$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
1807as_fn_error $? "failed to load site script $ac_site_file
1808See \`config.log' for more details" "$LINENO" 5; }
1809 fi
1810done
1811
1812if test -r "$cache_file"; then
1813 # Some versions of bash will fail to source /dev/null (special files
1814 # actually), so we avoid doing that. DJGPP emulates it as a regular file.
1815 if test /dev/null != "$cache_file" && test -f "$cache_file"; then
1816 { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
1817$as_echo "$as_me: loading cache $cache_file" >&6;}
1818 case $cache_file in
1819 [\\/]* | ?:[\\/]* ) . "$cache_file";;
1820 *) . "./$cache_file";;
1821 esac
1822 fi
1823else
1824 { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
1825$as_echo "$as_me: creating cache $cache_file" >&6;}
1826 >$cache_file
1827fi
1828
1829# Check that the precious variables saved in the cache have kept the same
1830# value.
1831ac_cache_corrupted=false
1832for ac_var in $ac_precious_vars; do
1833 eval ac_old_set=\$ac_cv_env_${ac_var}_set
1834 eval ac_new_set=\$ac_env_${ac_var}_set
1835 eval ac_old_val=\$ac_cv_env_${ac_var}_value
1836 eval ac_new_val=\$ac_env_${ac_var}_value
1837 case $ac_old_set,$ac_new_set in
1838 set,)
1839 { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
1840$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
1841 ac_cache_corrupted=: ;;
1842 ,set)
1843 { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
1844$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
1845 ac_cache_corrupted=: ;;
1846 ,);;
1847 *)
1848 if test "x$ac_old_val" != "x$ac_new_val"; then
1849 # differences in whitespace do not lead to failure.
1850 ac_old_val_w=`echo x $ac_old_val`
1851 ac_new_val_w=`echo x $ac_new_val`
1852 if test "$ac_old_val_w" != "$ac_new_val_w"; then
1853 { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
1854$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
1855 ac_cache_corrupted=:
1856 else
1857 { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
1858$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
1859 eval $ac_var=\$ac_old_val
1860 fi
1861 { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
1862$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
1863 { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
1864$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
1865 fi;;
1866 esac
1867 # Pass precious variables to config.status.
1868 if test "$ac_new_set" = set; then
1869 case $ac_new_val in
1870 *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
1871 *) ac_arg=$ac_var=$ac_new_val ;;
1872 esac
1873 case " $ac_configure_args " in
1874 *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
1875 *) as_fn_append ac_configure_args " '$ac_arg'" ;;
1876 esac
1877 fi
1878done
1879if $ac_cache_corrupted; then
1880 { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
1881$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
1882 { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
1883$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
1884 as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
1885fi
1886## -------------------- ##
1887## Main body of script. ##
1888## -------------------- ##
1889
1890ac_ext=c
1891ac_cpp='$CPP $CPPFLAGS'
1892ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
1893ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
1894ac_compiler_gnu=$ac_cv_c_compiler_gnu
1895
1896
1897
1898am__api_version='1.15'
1899
1900ac_aux_dir=
1901for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
1902 if test -f "$ac_dir/install-sh"; then
1903 ac_aux_dir=$ac_dir
1904 ac_install_sh="$ac_aux_dir/install-sh -c"
1905 break
1906 elif test -f "$ac_dir/install.sh"; then
1907 ac_aux_dir=$ac_dir
1908 ac_install_sh="$ac_aux_dir/install.sh -c"
1909 break
1910 elif test -f "$ac_dir/shtool"; then
1911 ac_aux_dir=$ac_dir
1912 ac_install_sh="$ac_aux_dir/shtool install -c"
1913 break
1914 fi
1915done
1916if test -z "$ac_aux_dir"; then
1917 as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
1918fi
1919
1920# These three variables are undocumented and unsupported,
1921# and are intended to be withdrawn in a future Autoconf release.
1922# They can cause serious problems if a builder's source tree is in a directory
1923# whose full name contains unusual characters.
1924ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
1925ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
1926ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
1927
1928
1929# Find a good install program. We prefer a C program (faster),
1930# so one script is as good as another. But avoid the broken or
1931# incompatible versions:
1932# SysV /etc/install, /usr/sbin/install
1933# SunOS /usr/etc/install
1934# IRIX /sbin/install
1935# AIX /bin/install
1936# AmigaOS /C/install, which installs bootblocks on floppy discs
1937# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
1938# AFS /usr/afsws/bin/install, which mishandles nonexistent args
1939# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
1940# OS/2's system install, which has a completely different semantic
1941# ./install, which can be erroneously created by make from ./install.sh.
1942# Reject install programs that cannot install multiple files.
1943{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
1944$as_echo_n "checking for a BSD-compatible install... " >&6; }
1945if test -z "$INSTALL"; then
1946if ${ac_cv_path_install+:} false; then :
1947 $as_echo_n "(cached) " >&6
1948else
1949 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
1950for as_dir in $PATH
1951do
1952 IFS=$as_save_IFS
1953 test -z "$as_dir" && as_dir=.
1954 # Account for people who put trailing slashes in PATH elements.
1955case $as_dir/ in #((
1956 ./ | .// | /[cC]/* | \
1957 /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
1958 ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
1959 /usr/ucb/* ) ;;
1960 *)
1961 # OSF1 and SCO ODT 3.0 have their own names for install.
1962 # Don't use installbsd from OSF since it installs stuff as root
1963 # by default.
1964 for ac_prog in ginstall scoinst install; do
1965 for ac_exec_ext in '' $ac_executable_extensions; do
1966 if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
1967 if test $ac_prog = install &&
1968 grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
1969 # AIX install. It has an incompatible calling convention.
1970 :
1971 elif test $ac_prog = install &&
1972 grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
1973 # program-specific install script used by HP pwplus--don't use.
1974 :
1975 else
1976 rm -rf conftest.one conftest.two conftest.dir
1977 echo one > conftest.one
1978 echo two > conftest.two
1979 mkdir conftest.dir
1980 if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
1981 test -s conftest.one && test -s conftest.two &&
1982 test -s conftest.dir/conftest.one &&
1983 test -s conftest.dir/conftest.two
1984 then
1985 ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
1986 break 3
1987 fi
1988 fi
1989 fi
1990 done
1991 done
1992 ;;
1993esac
1994
1995 done
1996IFS=$as_save_IFS
1997
1998rm -rf conftest.one conftest.two conftest.dir
1999
2000fi
2001 if test "${ac_cv_path_install+set}" = set; then
2002 INSTALL=$ac_cv_path_install
2003 else
2004 # As a last resort, use the slow shell script. Don't cache a
2005 # value for INSTALL within a source directory, because that will
2006 # break other packages using the cache if that directory is
2007 # removed, or if the value is a relative name.
2008 INSTALL=$ac_install_sh
2009 fi
2010fi
2011{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
2012$as_echo "$INSTALL" >&6; }
2013
2014# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
2015# It thinks the first close brace ends the variable substitution.
2016test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
2017
2018test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
2019
2020test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
2021
2022{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
2023$as_echo_n "checking whether build environment is sane... " >&6; }
2024# Reject unsafe characters in $srcdir or the absolute working directory
2025# name. Accept space and tab only in the latter.
2026am_lf='
2027'
2028case `pwd` in
2029 *[\\\"\#\$\&\'\`$am_lf]*)
2030 as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
2031esac
2032case $srcdir in
2033 *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
2034 as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
2035esac
2036
2037# Do 'set' in a subshell so we don't clobber the current shell's
2038# arguments. Must try -L first in case configure is actually a
2039# symlink; some systems play weird games with the mod time of symlinks
2040# (eg FreeBSD returns the mod time of the symlink's containing
2041# directory).
2042if (
2043 am_has_slept=no
2044 for am_try in 1 2; do
2045 echo "timestamp, slept: $am_has_slept" > conftest.file
2046 set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
2047 if test "$*" = "X"; then
2048 # -L didn't work.
2049 set X `ls -t "$srcdir/configure" conftest.file`
2050 fi
2051 if test "$*" != "X $srcdir/configure conftest.file" \
2052 && test "$*" != "X conftest.file $srcdir/configure"; then
2053
2054 # If neither matched, then we have a broken ls. This can happen
2055 # if, for instance, CONFIG_SHELL is bash and it inherits a
2056 # broken ls alias from the environment. This has actually
2057 # happened. Such a system could not be considered "sane".
2058 as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
2059 alias in your environment" "$LINENO" 5
2060 fi
2061 if test "$2" = conftest.file || test $am_try -eq 2; then
2062 break
2063 fi
2064 # Just in case.
2065 sleep 1
2066 am_has_slept=yes
2067 done
2068 test "$2" = conftest.file
2069 )
2070then
2071 # Ok.
2072 :
2073else
2074 as_fn_error $? "newly created file is older than distributed files!
2075Check your system clock" "$LINENO" 5
2076fi
2077{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
2078$as_echo "yes" >&6; }
2079# If we didn't sleep, we still need to ensure time stamps of config.status and
2080# generated files are strictly newer.
2081am_sleep_pid=
2082if grep 'slept: no' conftest.file >/dev/null 2>&1; then
2083 ( sleep 1 ) &
2084 am_sleep_pid=$!
2085fi
2086
2087rm -f conftest.file
2088
2089test "$program_prefix" != NONE &&
2090 program_transform_name="s&^&$program_prefix&;$program_transform_name"
2091# Use a double $ so make ignores it.
2092test "$program_suffix" != NONE &&
2093 program_transform_name="s&\$&$program_suffix&;$program_transform_name"
2094# Double any \ or $.
2095# By default was `s,x,x', remove it if useless.
2096ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
2097program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
2098
2099# Expand $ac_aux_dir to an absolute path.
2100am_aux_dir=`cd "$ac_aux_dir" && pwd`
2101
2102if test x"${MISSING+set}" != xset; then
2103 case $am_aux_dir in
2104 *\ * | *\ *)
2105 MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
2106 *)
2107 MISSING="\${SHELL} $am_aux_dir/missing" ;;
2108 esac
2109fi
2110# Use eval to expand $SHELL
2111if eval "$MISSING --is-lightweight"; then
2112 am_missing_run="$MISSING "
2113else
2114 am_missing_run=
2115 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
2116$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
2117fi
2118
2119if test x"${install_sh+set}" != xset; then
2120 case $am_aux_dir in
2121 *\ * | *\ *)
2122 install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
2123 *)
2124 install_sh="\${SHELL} $am_aux_dir/install-sh"
2125 esac
2126fi
2127
2128# Installed binaries are usually stripped using 'strip' when the user
2129# run "make install-strip". However 'strip' might not be the right
2130# tool to use in cross-compilation environments, therefore Automake
2131# will honor the 'STRIP' environment variable to overrule this program.
2132if test "$cross_compiling" != no; then
2133 if test -n "$ac_tool_prefix"; then
2134 # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
2135set dummy ${ac_tool_prefix}strip; ac_word=$2
2136{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2137$as_echo_n "checking for $ac_word... " >&6; }
2138if ${ac_cv_prog_STRIP+:} false; then :
2139 $as_echo_n "(cached) " >&6
2140else
2141 if test -n "$STRIP"; then
2142 ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
2143else
2144as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2145for as_dir in $PATH
2146do
2147 IFS=$as_save_IFS
2148 test -z "$as_dir" && as_dir=.
2149 for ac_exec_ext in '' $ac_executable_extensions; do
2150 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2151 ac_cv_prog_STRIP="${ac_tool_prefix}strip"
2152 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2153 break 2
2154 fi
2155done
2156 done
2157IFS=$as_save_IFS
2158
2159fi
2160fi
2161STRIP=$ac_cv_prog_STRIP
2162if test -n "$STRIP"; then
2163 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
2164$as_echo "$STRIP" >&6; }
2165else
2166 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2167$as_echo "no" >&6; }
2168fi
2169
2170
2171fi
2172if test -z "$ac_cv_prog_STRIP"; then
2173 ac_ct_STRIP=$STRIP
2174 # Extract the first word of "strip", so it can be a program name with args.
2175set dummy strip; ac_word=$2
2176{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2177$as_echo_n "checking for $ac_word... " >&6; }
2178if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
2179 $as_echo_n "(cached) " >&6
2180else
2181 if test -n "$ac_ct_STRIP"; then
2182 ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
2183else
2184as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2185for as_dir in $PATH
2186do
2187 IFS=$as_save_IFS
2188 test -z "$as_dir" && as_dir=.
2189 for ac_exec_ext in '' $ac_executable_extensions; do
2190 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2191 ac_cv_prog_ac_ct_STRIP="strip"
2192 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2193 break 2
2194 fi
2195done
2196 done
2197IFS=$as_save_IFS
2198
2199fi
2200fi
2201ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
2202if test -n "$ac_ct_STRIP"; then
2203 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
2204$as_echo "$ac_ct_STRIP" >&6; }
2205else
2206 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2207$as_echo "no" >&6; }
2208fi
2209
2210 if test "x$ac_ct_STRIP" = x; then
2211 STRIP=":"
2212 else
2213 case $cross_compiling:$ac_tool_warned in
2214yes:)
2215{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
2216$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
2217ac_tool_warned=yes ;;
2218esac
2219 STRIP=$ac_ct_STRIP
2220 fi
2221else
2222 STRIP="$ac_cv_prog_STRIP"
2223fi
2224
2225fi
2226INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
2227
2228{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
2229$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
2230if test -z "$MKDIR_P"; then
2231 if ${ac_cv_path_mkdir+:} false; then :
2232 $as_echo_n "(cached) " >&6
2233else
2234 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2235for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
2236do
2237 IFS=$as_save_IFS
2238 test -z "$as_dir" && as_dir=.
2239 for ac_prog in mkdir gmkdir; do
2240 for ac_exec_ext in '' $ac_executable_extensions; do
2241 as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
2242 case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
2243 'mkdir (GNU coreutils) '* | \
2244 'mkdir (coreutils) '* | \
2245 'mkdir (fileutils) '4.1*)
2246 ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
2247 break 3;;
2248 esac
2249 done
2250 done
2251 done
2252IFS=$as_save_IFS
2253
2254fi
2255
2256 test -d ./--version && rmdir ./--version
2257 if test "${ac_cv_path_mkdir+set}" = set; then
2258 MKDIR_P="$ac_cv_path_mkdir -p"
2259 else
2260 # As a last resort, use the slow shell script. Don't cache a
2261 # value for MKDIR_P within a source directory, because that will
2262 # break other packages using the cache if that directory is
2263 # removed, or if the value is a relative name.
2264 MKDIR_P="$ac_install_sh -d"
2265 fi
2266fi
2267{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
2268$as_echo "$MKDIR_P" >&6; }
2269
2270for ac_prog in gawk mawk nawk awk
2271do
2272 # Extract the first word of "$ac_prog", so it can be a program name with args.
2273set dummy $ac_prog; ac_word=$2
2274{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2275$as_echo_n "checking for $ac_word... " >&6; }
2276if ${ac_cv_prog_AWK+:} false; then :
2277 $as_echo_n "(cached) " >&6
2278else
2279 if test -n "$AWK"; then
2280 ac_cv_prog_AWK="$AWK" # Let the user override the test.
2281else
2282as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2283for as_dir in $PATH
2284do
2285 IFS=$as_save_IFS
2286 test -z "$as_dir" && as_dir=.
2287 for ac_exec_ext in '' $ac_executable_extensions; do
2288 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2289 ac_cv_prog_AWK="$ac_prog"
2290 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2291 break 2
2292 fi
2293done
2294 done
2295IFS=$as_save_IFS
2296
2297fi
2298fi
2299AWK=$ac_cv_prog_AWK
2300if test -n "$AWK"; then
2301 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
2302$as_echo "$AWK" >&6; }
2303else
2304 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2305$as_echo "no" >&6; }
2306fi
2307
2308
2309 test -n "$AWK" && break
2310done
2311
2312{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
2313$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
2314set x ${MAKE-make}
2315ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
2316if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
2317 $as_echo_n "(cached) " >&6
2318else
2319 cat >conftest.make <<\_ACEOF
2320SHELL = /bin/sh
2321all:
2322 @echo '@@@%%%=$(MAKE)=@@@%%%'
2323_ACEOF
2324# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
2325case `${MAKE-make} -f conftest.make 2>/dev/null` in
2326 *@@@%%%=?*=@@@%%%*)
2327 eval ac_cv_prog_make_${ac_make}_set=yes;;
2328 *)
2329 eval ac_cv_prog_make_${ac_make}_set=no;;
2330esac
2331rm -f conftest.make
2332fi
2333if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
2334 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
2335$as_echo "yes" >&6; }
2336 SET_MAKE=
2337else
2338 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2339$as_echo "no" >&6; }
2340 SET_MAKE="MAKE=${MAKE-make}"
2341fi
2342
2343rm -rf .tst 2>/dev/null
2344mkdir .tst 2>/dev/null
2345if test -d .tst; then
2346 am__leading_dot=.
2347else
2348 am__leading_dot=_
2349fi
2350rmdir .tst 2>/dev/null
2351
2352# Check whether --enable-silent-rules was given.
2353if test "${enable_silent_rules+set}" = set; then :
2354 enableval=$enable_silent_rules;
2355fi
2356
2357case $enable_silent_rules in # (((
2358 yes) AM_DEFAULT_VERBOSITY=0;;
2359 no) AM_DEFAULT_VERBOSITY=1;;
2360 *) AM_DEFAULT_VERBOSITY=1;;
2361esac
2362am_make=${MAKE-make}
2363{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
2364$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
2365if ${am_cv_make_support_nested_variables+:} false; then :
2366 $as_echo_n "(cached) " >&6
2367else
2368 if $as_echo 'TRUE=$(BAR$(V))
2369BAR0=false
2370BAR1=true
2371V=1
2372am__doit:
2373 @$(TRUE)
2374.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
2375 am_cv_make_support_nested_variables=yes
2376else
2377 am_cv_make_support_nested_variables=no
2378fi
2379fi
2380{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
2381$as_echo "$am_cv_make_support_nested_variables" >&6; }
2382if test $am_cv_make_support_nested_variables = yes; then
2383 AM_V='$(V)'
2384 AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
2385else
2386 AM_V=$AM_DEFAULT_VERBOSITY
2387 AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
2388fi
2389AM_BACKSLASH='\'
2390
2391if test "`cd $srcdir && pwd`" != "`pwd`"; then
2392 # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
2393 # is not polluted with repeated "-I."
2394 am__isrc=' -I$(srcdir)'
2395 # test to see if srcdir already configured
2396 if test -f $srcdir/config.status; then
2397 as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
2398 fi
2399fi
2400
2401# test whether we have cygpath
2402if test -z "$CYGPATH_W"; then
2403 if (cygpath --version) >/dev/null 2>/dev/null; then
2404 CYGPATH_W='cygpath -w'
2405 else
2406 CYGPATH_W=echo
2407 fi
2408fi
2409
2410
2411# Define the identity of the package.
2412 PACKAGE='puzzles'
2413 VERSION='6.66'
2414
2415
2416cat >>confdefs.h <<_ACEOF
2417#define PACKAGE "$PACKAGE"
2418_ACEOF
2419
2420
2421cat >>confdefs.h <<_ACEOF
2422#define VERSION "$VERSION"
2423_ACEOF
2424
2425# Some tools Automake needs.
2426
2427ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
2428
2429
2430AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
2431
2432
2433AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
2434
2435
2436AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
2437
2438
2439MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
2440
2441# For better backward compatibility. To be removed once Automake 1.9.x
2442# dies out for good. For more background, see:
2443# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
2444# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
2445mkdir_p='$(MKDIR_P)'
2446
2447# We need awk for the "check" target (and possibly the TAP driver). The
2448# system "awk" is bad on some platforms.
2449# Always define AMTAR for backward compatibility. Yes, it's still used
2450# in the wild :-( We should find a proper way to deprecate it ...
2451AMTAR='$${TAR-tar}'
2452
2453
2454# We'll loop over all known methods to create a tar archive until one works.
2455_am_tools='gnutar pax cpio none'
2456
2457am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
2458
2459
2460
2461
2462
2463
2464# POSIX will say in a future version that running "rm -f" with no argument
2465# is OK; and we want to be able to make that assumption in our Makefile
2466# recipes. So use an aggressive probe to check that the usage we want is
2467# actually supported "in the wild" to an acceptable degree.
2468# See automake bug#10828.
2469# To make any issue more visible, cause the running configure to be aborted
2470# by default if the 'rm' program in use doesn't match our expectations; the
2471# user can still override this though.
2472if rm -f && rm -fr && rm -rf; then : OK; else
2473 cat >&2 <<'END'
2474Oops!
2475
2476Your 'rm' program seems unable to run without file operands specified
2477on the command line, even when the '-f' option is present. This is contrary
2478to the behaviour of most rm programs out there, and not conforming with
2479the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
2480
2481Please tell bug-automake@gnu.org about your system, including the value
2482of your $PATH and any error possibly output before this message. This
2483can help us improve future automake versions.
2484
2485END
2486 if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
2487 echo 'Configuration will proceed anyway, since you have set the' >&2
2488 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
2489 echo >&2
2490 else
2491 cat >&2 <<'END'
2492Aborting the configuration process, to ensure you take notice of the issue.
2493
2494You can download and install GNU coreutils to get an 'rm' implementation
2495that behaves properly: <http://www.gnu.org/software/coreutils/>.
2496
2497If you want to complete the configuration process using your problematic
2498'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
2499to "yes", and re-run configure.
2500
2501END
2502 as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
2503 fi
2504fi
2505
2506ac_ext=c
2507ac_cpp='$CPP $CPPFLAGS'
2508ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
2509ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
2510ac_compiler_gnu=$ac_cv_c_compiler_gnu
2511if test -n "$ac_tool_prefix"; then
2512 # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
2513set dummy ${ac_tool_prefix}gcc; ac_word=$2
2514{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2515$as_echo_n "checking for $ac_word... " >&6; }
2516if ${ac_cv_prog_CC+:} false; then :
2517 $as_echo_n "(cached) " >&6
2518else
2519 if test -n "$CC"; then
2520 ac_cv_prog_CC="$CC" # Let the user override the test.
2521else
2522as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2523for as_dir in $PATH
2524do
2525 IFS=$as_save_IFS
2526 test -z "$as_dir" && as_dir=.
2527 for ac_exec_ext in '' $ac_executable_extensions; do
2528 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2529 ac_cv_prog_CC="${ac_tool_prefix}gcc"
2530 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2531 break 2
2532 fi
2533done
2534 done
2535IFS=$as_save_IFS
2536
2537fi
2538fi
2539CC=$ac_cv_prog_CC
2540if test -n "$CC"; then
2541 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
2542$as_echo "$CC" >&6; }
2543else
2544 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2545$as_echo "no" >&6; }
2546fi
2547
2548
2549fi
2550if test -z "$ac_cv_prog_CC"; then
2551 ac_ct_CC=$CC
2552 # Extract the first word of "gcc", so it can be a program name with args.
2553set dummy gcc; ac_word=$2
2554{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2555$as_echo_n "checking for $ac_word... " >&6; }
2556if ${ac_cv_prog_ac_ct_CC+:} false; then :
2557 $as_echo_n "(cached) " >&6
2558else
2559 if test -n "$ac_ct_CC"; then
2560 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2561else
2562as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2563for as_dir in $PATH
2564do
2565 IFS=$as_save_IFS
2566 test -z "$as_dir" && as_dir=.
2567 for ac_exec_ext in '' $ac_executable_extensions; do
2568 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2569 ac_cv_prog_ac_ct_CC="gcc"
2570 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2571 break 2
2572 fi
2573done
2574 done
2575IFS=$as_save_IFS
2576
2577fi
2578fi
2579ac_ct_CC=$ac_cv_prog_ac_ct_CC
2580if test -n "$ac_ct_CC"; then
2581 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
2582$as_echo "$ac_ct_CC" >&6; }
2583else
2584 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2585$as_echo "no" >&6; }
2586fi
2587
2588 if test "x$ac_ct_CC" = x; then
2589 CC=""
2590 else
2591 case $cross_compiling:$ac_tool_warned in
2592yes:)
2593{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
2594$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
2595ac_tool_warned=yes ;;
2596esac
2597 CC=$ac_ct_CC
2598 fi
2599else
2600 CC="$ac_cv_prog_CC"
2601fi
2602
2603if test -z "$CC"; then
2604 if test -n "$ac_tool_prefix"; then
2605 # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
2606set dummy ${ac_tool_prefix}cc; ac_word=$2
2607{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2608$as_echo_n "checking for $ac_word... " >&6; }
2609if ${ac_cv_prog_CC+:} false; then :
2610 $as_echo_n "(cached) " >&6
2611else
2612 if test -n "$CC"; then
2613 ac_cv_prog_CC="$CC" # Let the user override the test.
2614else
2615as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2616for as_dir in $PATH
2617do
2618 IFS=$as_save_IFS
2619 test -z "$as_dir" && as_dir=.
2620 for ac_exec_ext in '' $ac_executable_extensions; do
2621 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2622 ac_cv_prog_CC="${ac_tool_prefix}cc"
2623 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2624 break 2
2625 fi
2626done
2627 done
2628IFS=$as_save_IFS
2629
2630fi
2631fi
2632CC=$ac_cv_prog_CC
2633if test -n "$CC"; then
2634 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
2635$as_echo "$CC" >&6; }
2636else
2637 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2638$as_echo "no" >&6; }
2639fi
2640
2641
2642 fi
2643fi
2644if test -z "$CC"; then
2645 # Extract the first word of "cc", so it can be a program name with args.
2646set dummy cc; ac_word=$2
2647{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2648$as_echo_n "checking for $ac_word... " >&6; }
2649if ${ac_cv_prog_CC+:} false; then :
2650 $as_echo_n "(cached) " >&6
2651else
2652 if test -n "$CC"; then
2653 ac_cv_prog_CC="$CC" # Let the user override the test.
2654else
2655 ac_prog_rejected=no
2656as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2657for as_dir in $PATH
2658do
2659 IFS=$as_save_IFS
2660 test -z "$as_dir" && as_dir=.
2661 for ac_exec_ext in '' $ac_executable_extensions; do
2662 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2663 if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
2664 ac_prog_rejected=yes
2665 continue
2666 fi
2667 ac_cv_prog_CC="cc"
2668 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2669 break 2
2670 fi
2671done
2672 done
2673IFS=$as_save_IFS
2674
2675if test $ac_prog_rejected = yes; then
2676 # We found a bogon in the path, so make sure we never use it.
2677 set dummy $ac_cv_prog_CC
2678 shift
2679 if test $# != 0; then
2680 # We chose a different compiler from the bogus one.
2681 # However, it has the same basename, so the bogon will be chosen
2682 # first if we set CC to just the basename; use the full file name.
2683 shift
2684 ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
2685 fi
2686fi
2687fi
2688fi
2689CC=$ac_cv_prog_CC
2690if test -n "$CC"; then
2691 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
2692$as_echo "$CC" >&6; }
2693else
2694 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2695$as_echo "no" >&6; }
2696fi
2697
2698
2699fi
2700if test -z "$CC"; then
2701 if test -n "$ac_tool_prefix"; then
2702 for ac_prog in cl.exe
2703 do
2704 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
2705set dummy $ac_tool_prefix$ac_prog; ac_word=$2
2706{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2707$as_echo_n "checking for $ac_word... " >&6; }
2708if ${ac_cv_prog_CC+:} false; then :
2709 $as_echo_n "(cached) " >&6
2710else
2711 if test -n "$CC"; then
2712 ac_cv_prog_CC="$CC" # Let the user override the test.
2713else
2714as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2715for as_dir in $PATH
2716do
2717 IFS=$as_save_IFS
2718 test -z "$as_dir" && as_dir=.
2719 for ac_exec_ext in '' $ac_executable_extensions; do
2720 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2721 ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
2722 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2723 break 2
2724 fi
2725done
2726 done
2727IFS=$as_save_IFS
2728
2729fi
2730fi
2731CC=$ac_cv_prog_CC
2732if test -n "$CC"; then
2733 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
2734$as_echo "$CC" >&6; }
2735else
2736 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2737$as_echo "no" >&6; }
2738fi
2739
2740
2741 test -n "$CC" && break
2742 done
2743fi
2744if test -z "$CC"; then
2745 ac_ct_CC=$CC
2746 for ac_prog in cl.exe
2747do
2748 # Extract the first word of "$ac_prog", so it can be a program name with args.
2749set dummy $ac_prog; ac_word=$2
2750{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2751$as_echo_n "checking for $ac_word... " >&6; }
2752if ${ac_cv_prog_ac_ct_CC+:} false; then :
2753 $as_echo_n "(cached) " >&6
2754else
2755 if test -n "$ac_ct_CC"; then
2756 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
2757else
2758as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2759for as_dir in $PATH
2760do
2761 IFS=$as_save_IFS
2762 test -z "$as_dir" && as_dir=.
2763 for ac_exec_ext in '' $ac_executable_extensions; do
2764 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2765 ac_cv_prog_ac_ct_CC="$ac_prog"
2766 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2767 break 2
2768 fi
2769done
2770 done
2771IFS=$as_save_IFS
2772
2773fi
2774fi
2775ac_ct_CC=$ac_cv_prog_ac_ct_CC
2776if test -n "$ac_ct_CC"; then
2777 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
2778$as_echo "$ac_ct_CC" >&6; }
2779else
2780 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2781$as_echo "no" >&6; }
2782fi
2783
2784
2785 test -n "$ac_ct_CC" && break
2786done
2787
2788 if test "x$ac_ct_CC" = x; then
2789 CC=""
2790 else
2791 case $cross_compiling:$ac_tool_warned in
2792yes:)
2793{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
2794$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
2795ac_tool_warned=yes ;;
2796esac
2797 CC=$ac_ct_CC
2798 fi
2799fi
2800
2801fi
2802
2803
2804test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
2805$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
2806as_fn_error $? "no acceptable C compiler found in \$PATH
2807See \`config.log' for more details" "$LINENO" 5; }
2808
2809# Provide some information about the compiler.
2810$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
2811set X $ac_compile
2812ac_compiler=$2
2813for ac_option in --version -v -V -qversion; do
2814 { { ac_try="$ac_compiler $ac_option >&5"
2815case "(($ac_try" in
2816 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
2817 *) ac_try_echo=$ac_try;;
2818esac
2819eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
2820$as_echo "$ac_try_echo"; } >&5
2821 (eval "$ac_compiler $ac_option >&5") 2>conftest.err
2822 ac_status=$?
2823 if test -s conftest.err; then
2824 sed '10a\
2825... rest of stderr output deleted ...
2826 10q' conftest.err >conftest.er1
2827 cat conftest.er1 >&5
2828 fi
2829 rm -f conftest.er1 conftest.err
2830 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
2831 test $ac_status = 0; }
2832done
2833
2834cat confdefs.h - <<_ACEOF >conftest.$ac_ext
2835/* end confdefs.h. */
2836
2837int
2838main ()
2839{
2840
2841 ;
2842 return 0;
2843}
2844_ACEOF
2845ac_clean_files_save=$ac_clean_files
2846ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
2847# Try to create an executable without -o first, disregard a.out.
2848# It will help us diagnose broken compilers, and finding out an intuition
2849# of exeext.
2850{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
2851$as_echo_n "checking whether the C compiler works... " >&6; }
2852ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
2853
2854# The possible output files:
2855ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
2856
2857ac_rmfiles=
2858for ac_file in $ac_files
2859do
2860 case $ac_file in
2861 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
2862 * ) ac_rmfiles="$ac_rmfiles $ac_file";;
2863 esac
2864done
2865rm -f $ac_rmfiles
2866
2867if { { ac_try="$ac_link_default"
2868case "(($ac_try" in
2869 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
2870 *) ac_try_echo=$ac_try;;
2871esac
2872eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
2873$as_echo "$ac_try_echo"; } >&5
2874 (eval "$ac_link_default") 2>&5
2875 ac_status=$?
2876 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
2877 test $ac_status = 0; }; then :
2878 # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
2879# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
2880# in a Makefile. We should not override ac_cv_exeext if it was cached,
2881# so that the user can short-circuit this test for compilers unknown to
2882# Autoconf.
2883for ac_file in $ac_files ''
2884do
2885 test -f "$ac_file" || continue
2886 case $ac_file in
2887 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
2888 ;;
2889 [ab].out )
2890 # We found the default executable, but exeext='' is most
2891 # certainly right.
2892 break;;
2893 *.* )
2894 if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
2895 then :; else
2896 ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
2897 fi
2898 # We set ac_cv_exeext here because the later test for it is not
2899 # safe: cross compilers may not add the suffix if given an `-o'
2900 # argument, so we may need to know it at that point already.
2901 # Even if this section looks crufty: it has the advantage of
2902 # actually working.
2903 break;;
2904 * )
2905 break;;
2906 esac
2907done
2908test "$ac_cv_exeext" = no && ac_cv_exeext=
2909
2910else
2911 ac_file=''
2912fi
2913if test -z "$ac_file"; then :
2914 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2915$as_echo "no" >&6; }
2916$as_echo "$as_me: failed program was:" >&5
2917sed 's/^/| /' conftest.$ac_ext >&5
2918
2919{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
2920$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
2921as_fn_error 77 "C compiler cannot create executables
2922See \`config.log' for more details" "$LINENO" 5; }
2923else
2924 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
2925$as_echo "yes" >&6; }
2926fi
2927{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
2928$as_echo_n "checking for C compiler default output file name... " >&6; }
2929{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
2930$as_echo "$ac_file" >&6; }
2931ac_exeext=$ac_cv_exeext
2932
2933rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
2934ac_clean_files=$ac_clean_files_save
2935{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
2936$as_echo_n "checking for suffix of executables... " >&6; }
2937if { { ac_try="$ac_link"
2938case "(($ac_try" in
2939 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
2940 *) ac_try_echo=$ac_try;;
2941esac
2942eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
2943$as_echo "$ac_try_echo"; } >&5
2944 (eval "$ac_link") 2>&5
2945 ac_status=$?
2946 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
2947 test $ac_status = 0; }; then :
2948 # If both `conftest.exe' and `conftest' are `present' (well, observable)
2949# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
2950# work properly (i.e., refer to `conftest.exe'), while it won't with
2951# `rm'.
2952for ac_file in conftest.exe conftest conftest.*; do
2953 test -f "$ac_file" || continue
2954 case $ac_file in
2955 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
2956 *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
2957 break;;
2958 * ) break;;
2959 esac
2960done
2961else
2962 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
2963$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
2964as_fn_error $? "cannot compute suffix of executables: cannot compile and link
2965See \`config.log' for more details" "$LINENO" 5; }
2966fi
2967rm -f conftest conftest$ac_cv_exeext
2968{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
2969$as_echo "$ac_cv_exeext" >&6; }
2970
2971rm -f conftest.$ac_ext
2972EXEEXT=$ac_cv_exeext
2973ac_exeext=$EXEEXT
2974cat confdefs.h - <<_ACEOF >conftest.$ac_ext
2975/* end confdefs.h. */
2976#include <stdio.h>
2977int
2978main ()
2979{
2980FILE *f = fopen ("conftest.out", "w");
2981 return ferror (f) || fclose (f) != 0;
2982
2983 ;
2984 return 0;
2985}
2986_ACEOF
2987ac_clean_files="$ac_clean_files conftest.out"
2988# Check that the compiler produces executables we can run. If not, either
2989# the compiler is broken, or we cross compile.
2990{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
2991$as_echo_n "checking whether we are cross compiling... " >&6; }
2992if test "$cross_compiling" != yes; then
2993 { { ac_try="$ac_link"
2994case "(($ac_try" in
2995 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
2996 *) ac_try_echo=$ac_try;;
2997esac
2998eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
2999$as_echo "$ac_try_echo"; } >&5
3000 (eval "$ac_link") 2>&5
3001 ac_status=$?
3002 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
3003 test $ac_status = 0; }
3004 if { ac_try='./conftest$ac_cv_exeext'
3005 { { case "(($ac_try" in
3006 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
3007 *) ac_try_echo=$ac_try;;
3008esac
3009eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
3010$as_echo "$ac_try_echo"; } >&5
3011 (eval "$ac_try") 2>&5
3012 ac_status=$?
3013 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
3014 test $ac_status = 0; }; }; then
3015 cross_compiling=no
3016 else
3017 if test "$cross_compiling" = maybe; then
3018 cross_compiling=yes
3019 else
3020 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
3021$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
3022as_fn_error $? "cannot run C compiled programs.
3023If you meant to cross compile, use \`--host'.
3024See \`config.log' for more details" "$LINENO" 5; }
3025 fi
3026 fi
3027fi
3028{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
3029$as_echo "$cross_compiling" >&6; }
3030
3031rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
3032ac_clean_files=$ac_clean_files_save
3033{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
3034$as_echo_n "checking for suffix of object files... " >&6; }
3035if ${ac_cv_objext+:} false; then :
3036 $as_echo_n "(cached) " >&6
3037else
3038 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3039/* end confdefs.h. */
3040
3041int
3042main ()
3043{
3044
3045 ;
3046 return 0;
3047}
3048_ACEOF
3049rm -f conftest.o conftest.obj
3050if { { ac_try="$ac_compile"
3051case "(($ac_try" in
3052 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
3053 *) ac_try_echo=$ac_try;;
3054esac
3055eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
3056$as_echo "$ac_try_echo"; } >&5
3057 (eval "$ac_compile") 2>&5
3058 ac_status=$?
3059 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
3060 test $ac_status = 0; }; then :
3061 for ac_file in conftest.o conftest.obj conftest.*; do
3062 test -f "$ac_file" || continue;
3063 case $ac_file in
3064 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
3065 *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
3066 break;;
3067 esac
3068done
3069else
3070 $as_echo "$as_me: failed program was:" >&5
3071sed 's/^/| /' conftest.$ac_ext >&5
3072
3073{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
3074$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
3075as_fn_error $? "cannot compute suffix of object files: cannot compile
3076See \`config.log' for more details" "$LINENO" 5; }
3077fi
3078rm -f conftest.$ac_cv_objext conftest.$ac_ext
3079fi
3080{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
3081$as_echo "$ac_cv_objext" >&6; }
3082OBJEXT=$ac_cv_objext
3083ac_objext=$OBJEXT
3084{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
3085$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
3086if ${ac_cv_c_compiler_gnu+:} false; then :
3087 $as_echo_n "(cached) " >&6
3088else
3089 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3090/* end confdefs.h. */
3091
3092int
3093main ()
3094{
3095#ifndef __GNUC__
3096 choke me
3097#endif
3098
3099 ;
3100 return 0;
3101}
3102_ACEOF
3103if ac_fn_c_try_compile "$LINENO"; then :
3104 ac_compiler_gnu=yes
3105else
3106 ac_compiler_gnu=no
3107fi
3108rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
3109ac_cv_c_compiler_gnu=$ac_compiler_gnu
3110
3111fi
3112{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
3113$as_echo "$ac_cv_c_compiler_gnu" >&6; }
3114if test $ac_compiler_gnu = yes; then
3115 GCC=yes
3116else
3117 GCC=
3118fi
3119ac_test_CFLAGS=${CFLAGS+set}
3120ac_save_CFLAGS=$CFLAGS
3121{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
3122$as_echo_n "checking whether $CC accepts -g... " >&6; }
3123if ${ac_cv_prog_cc_g+:} false; then :
3124 $as_echo_n "(cached) " >&6
3125else
3126 ac_save_c_werror_flag=$ac_c_werror_flag
3127 ac_c_werror_flag=yes
3128 ac_cv_prog_cc_g=no
3129 CFLAGS="-g"
3130 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3131/* end confdefs.h. */
3132
3133int
3134main ()
3135{
3136
3137 ;
3138 return 0;
3139}
3140_ACEOF
3141if ac_fn_c_try_compile "$LINENO"; then :
3142 ac_cv_prog_cc_g=yes
3143else
3144 CFLAGS=""
3145 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3146/* end confdefs.h. */
3147
3148int
3149main ()
3150{
3151
3152 ;
3153 return 0;
3154}
3155_ACEOF
3156if ac_fn_c_try_compile "$LINENO"; then :
3157
3158else
3159 ac_c_werror_flag=$ac_save_c_werror_flag
3160 CFLAGS="-g"
3161 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3162/* end confdefs.h. */
3163
3164int
3165main ()
3166{
3167
3168 ;
3169 return 0;
3170}
3171_ACEOF
3172if ac_fn_c_try_compile "$LINENO"; then :
3173 ac_cv_prog_cc_g=yes
3174fi
3175rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
3176fi
3177rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
3178fi
3179rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
3180 ac_c_werror_flag=$ac_save_c_werror_flag
3181fi
3182{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
3183$as_echo "$ac_cv_prog_cc_g" >&6; }
3184if test "$ac_test_CFLAGS" = set; then
3185 CFLAGS=$ac_save_CFLAGS
3186elif test $ac_cv_prog_cc_g = yes; then
3187 if test "$GCC" = yes; then
3188 CFLAGS="-g -O2"
3189 else
3190 CFLAGS="-g"
3191 fi
3192else
3193 if test "$GCC" = yes; then
3194 CFLAGS="-O2"
3195 else
3196 CFLAGS=
3197 fi
3198fi
3199{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
3200$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
3201if ${ac_cv_prog_cc_c89+:} false; then :
3202 $as_echo_n "(cached) " >&6
3203else
3204 ac_cv_prog_cc_c89=no
3205ac_save_CC=$CC
3206cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3207/* end confdefs.h. */
3208#include <stdarg.h>
3209#include <stdio.h>
3210struct stat;
3211/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
3212struct buf { int x; };
3213FILE * (*rcsopen) (struct buf *, struct stat *, int);
3214static char *e (p, i)
3215 char **p;
3216 int i;
3217{
3218 return p[i];
3219}
3220static char *f (char * (*g) (char **, int), char **p, ...)
3221{
3222 char *s;
3223 va_list v;
3224 va_start (v,p);
3225 s = g (p, va_arg (v,int));
3226 va_end (v);
3227 return s;
3228}
3229
3230/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
3231 function prototypes and stuff, but not '\xHH' hex character constants.
3232 These don't provoke an error unfortunately, instead are silently treated
3233 as 'x'. The following induces an error, until -std is added to get
3234 proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
3235 array size at least. It's necessary to write '\x00'==0 to get something
3236 that's true only with -std. */
3237int osf4_cc_array ['\x00' == 0 ? 1 : -1];
3238
3239/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
3240 inside strings and character constants. */
3241#define FOO(x) 'x'
3242int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
3243
3244int test (int i, double x);
3245struct s1 {int (*f) (int a);};
3246struct s2 {int (*f) (double a);};
3247int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
3248int argc;
3249char **argv;
3250int
3251main ()
3252{
3253return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
3254 ;
3255 return 0;
3256}
3257_ACEOF
3258for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
3259 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
3260do
3261 CC="$ac_save_CC $ac_arg"
3262 if ac_fn_c_try_compile "$LINENO"; then :
3263 ac_cv_prog_cc_c89=$ac_arg
3264fi
3265rm -f core conftest.err conftest.$ac_objext
3266 test "x$ac_cv_prog_cc_c89" != "xno" && break
3267done
3268rm -f conftest.$ac_ext
3269CC=$ac_save_CC
3270
3271fi
3272# AC_CACHE_VAL
3273case "x$ac_cv_prog_cc_c89" in
3274 x)
3275 { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
3276$as_echo "none needed" >&6; } ;;
3277 xno)
3278 { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
3279$as_echo "unsupported" >&6; } ;;
3280 *)
3281 CC="$CC $ac_cv_prog_cc_c89"
3282 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
3283$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
3284esac
3285if test "x$ac_cv_prog_cc_c89" != xno; then :
3286
3287fi
3288
3289ac_ext=c
3290ac_cpp='$CPP $CPPFLAGS'
3291ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
3292ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
3293ac_compiler_gnu=$ac_cv_c_compiler_gnu
3294
3295ac_ext=c
3296ac_cpp='$CPP $CPPFLAGS'
3297ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
3298ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
3299ac_compiler_gnu=$ac_cv_c_compiler_gnu
3300{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
3301$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
3302if ${am_cv_prog_cc_c_o+:} false; then :
3303 $as_echo_n "(cached) " >&6
3304else
3305 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3306/* end confdefs.h. */
3307
3308int
3309main ()
3310{
3311
3312 ;
3313 return 0;
3314}
3315_ACEOF
3316 # Make sure it works both with $CC and with simple cc.
3317 # Following AC_PROG_CC_C_O, we do the test twice because some
3318 # compilers refuse to overwrite an existing .o file with -o,
3319 # though they will create one.
3320 am_cv_prog_cc_c_o=yes
3321 for am_i in 1 2; do
3322 if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
3323 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
3324 ac_status=$?
3325 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3326 (exit $ac_status); } \
3327 && test -f conftest2.$ac_objext; then
3328 : OK
3329 else
3330 am_cv_prog_cc_c_o=no
3331 break
3332 fi
3333 done
3334 rm -f core conftest*
3335 unset am_i
3336fi
3337{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
3338$as_echo "$am_cv_prog_cc_c_o" >&6; }
3339if test "$am_cv_prog_cc_c_o" != yes; then
3340 # Losing compiler, so override with the script.
3341 # FIXME: It is wrong to rewrite CC.
3342 # But if we don't then we get into trouble of one sort or another.
3343 # A longer-term fix would be to have automake use am__CC in this case,
3344 # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
3345 CC="$am_aux_dir/compile $CC"
3346fi
3347ac_ext=c
3348ac_cpp='$CPP $CPPFLAGS'
3349ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
3350ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
3351ac_compiler_gnu=$ac_cv_c_compiler_gnu
3352
3353DEPDIR="${am__leading_dot}deps"
3354
3355ac_config_commands="$ac_config_commands depfiles"
3356
3357
3358am_make=${MAKE-make}
3359cat > confinc << 'END'
3360am__doit:
3361 @echo this is the am__doit target
3362.PHONY: am__doit
3363END
3364# If we don't find an include directive, just comment out the code.
3365{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
3366$as_echo_n "checking for style of include used by $am_make... " >&6; }
3367am__include="#"
3368am__quote=
3369_am_result=none
3370# First try GNU make style include.
3371echo "include confinc" > confmf
3372# Ignore all kinds of additional output from 'make'.
3373case `$am_make -s -f confmf 2> /dev/null` in #(
3374*the\ am__doit\ target*)
3375 am__include=include
3376 am__quote=
3377 _am_result=GNU
3378 ;;
3379esac
3380# Now try BSD make style include.
3381if test "$am__include" = "#"; then
3382 echo '.include "confinc"' > confmf
3383 case `$am_make -s -f confmf 2> /dev/null` in #(
3384 *the\ am__doit\ target*)
3385 am__include=.include
3386 am__quote="\""
3387 _am_result=BSD
3388 ;;
3389 esac
3390fi
3391
3392
3393{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
3394$as_echo "$_am_result" >&6; }
3395rm -f confinc confmf
3396
3397# Check whether --enable-dependency-tracking was given.
3398if test "${enable_dependency_tracking+set}" = set; then :
3399 enableval=$enable_dependency_tracking;
3400fi
3401
3402if test "x$enable_dependency_tracking" != xno; then
3403 am_depcomp="$ac_aux_dir/depcomp"
3404 AMDEPBACKSLASH='\'
3405 am__nodep='_no'
3406fi
3407 if test "x$enable_dependency_tracking" != xno; then
3408 AMDEP_TRUE=
3409 AMDEP_FALSE='#'
3410else
3411 AMDEP_TRUE='#'
3412 AMDEP_FALSE=
3413fi
3414
3415
3416
3417depcc="$CC" am_compiler_list=
3418
3419{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
3420$as_echo_n "checking dependency style of $depcc... " >&6; }
3421if ${am_cv_CC_dependencies_compiler_type+:} false; then :
3422 $as_echo_n "(cached) " >&6
3423else
3424 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
3425 # We make a subdir and do the tests there. Otherwise we can end up
3426 # making bogus files that we don't know about and never remove. For
3427 # instance it was reported that on HP-UX the gcc test will end up
3428 # making a dummy file named 'D' -- because '-MD' means "put the output
3429 # in D".
3430 rm -rf conftest.dir
3431 mkdir conftest.dir
3432 # Copy depcomp to subdir because otherwise we won't find it if we're
3433 # using a relative directory.
3434 cp "$am_depcomp" conftest.dir
3435 cd conftest.dir
3436 # We will build objects and dependencies in a subdirectory because
3437 # it helps to detect inapplicable dependency modes. For instance
3438 # both Tru64's cc and ICC support -MD to output dependencies as a
3439 # side effect of compilation, but ICC will put the dependencies in
3440 # the current directory while Tru64 will put them in the object
3441 # directory.
3442 mkdir sub
3443
3444 am_cv_CC_dependencies_compiler_type=none
3445 if test "$am_compiler_list" = ""; then
3446 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
3447 fi
3448 am__universal=false
3449 case " $depcc " in #(
3450 *\ -arch\ *\ -arch\ *) am__universal=true ;;
3451 esac
3452
3453 for depmode in $am_compiler_list; do
3454 # Setup a source with many dependencies, because some compilers
3455 # like to wrap large dependency lists on column 80 (with \), and
3456 # we should not choose a depcomp mode which is confused by this.
3457 #
3458 # We need to recreate these files for each test, as the compiler may
3459 # overwrite some of them when testing with obscure command lines.
3460 # This happens at least with the AIX C compiler.
3461 : > sub/conftest.c
3462 for i in 1 2 3 4 5 6; do
3463 echo '#include "conftst'$i'.h"' >> sub/conftest.c
3464 # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
3465 # Solaris 10 /bin/sh.
3466 echo '/* dummy */' > sub/conftst$i.h
3467 done
3468 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
3469
3470 # We check with '-c' and '-o' for the sake of the "dashmstdout"
3471 # mode. It turns out that the SunPro C++ compiler does not properly
3472 # handle '-M -o', and we need to detect this. Also, some Intel
3473 # versions had trouble with output in subdirs.
3474 am__obj=sub/conftest.${OBJEXT-o}
3475 am__minus_obj="-o $am__obj"
3476 case $depmode in
3477 gcc)
3478 # This depmode causes a compiler race in universal mode.
3479 test "$am__universal" = false || continue
3480 ;;
3481 nosideeffect)
3482 # After this tag, mechanisms are not by side-effect, so they'll
3483 # only be used when explicitly requested.
3484 if test "x$enable_dependency_tracking" = xyes; then
3485 continue
3486 else
3487 break
3488 fi
3489 ;;
3490 msvc7 | msvc7msys | msvisualcpp | msvcmsys)
3491 # This compiler won't grok '-c -o', but also, the minuso test has
3492 # not run yet. These depmodes are late enough in the game, and
3493 # so weak that their functioning should not be impacted.
3494 am__obj=conftest.${OBJEXT-o}
3495 am__minus_obj=
3496 ;;
3497 none) break ;;
3498 esac
3499 if depmode=$depmode \
3500 source=sub/conftest.c object=$am__obj \
3501 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
3502 $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
3503 >/dev/null 2>conftest.err &&
3504 grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
3505 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
3506 grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
3507 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
3508 # icc doesn't choke on unknown options, it will just issue warnings
3509 # or remarks (even with -Werror). So we grep stderr for any message
3510 # that says an option was ignored or not supported.
3511 # When given -MP, icc 7.0 and 7.1 complain thusly:
3512 # icc: Command line warning: ignoring option '-M'; no argument required
3513 # The diagnosis changed in icc 8.0:
3514 # icc: Command line remark: option '-MP' not supported
3515 if (grep 'ignoring option' conftest.err ||
3516 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
3517 am_cv_CC_dependencies_compiler_type=$depmode
3518 break
3519 fi
3520 fi
3521 done
3522
3523 cd ..
3524 rm -rf conftest.dir
3525else
3526 am_cv_CC_dependencies_compiler_type=none
3527fi
3528
3529fi
3530{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
3531$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
3532CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
3533
3534 if
3535 test "x$enable_dependency_tracking" != xno \
3536 && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
3537 am__fastdepCC_TRUE=
3538 am__fastdepCC_FALSE='#'
3539else
3540 am__fastdepCC_TRUE='#'
3541 am__fastdepCC_FALSE=
3542fi
3543
3544
3545
3546
3547# Check whether --with-gtk was given.
3548if test "${with_gtk+set}" = set; then :
3549 withval=$with_gtk; gtk_version_desired="$withval"
3550else
3551 gtk_version_desired="any"
3552fi
3553
3554
3555case "$gtk_version_desired" in
3556 2 | 3 | any) ;;
3557 yes) gtk_version_desired="any" ;;
3558 *) as_fn_error $? "Invalid GTK version specified" "$LINENO" 5
3559esac
3560
3561gtk=none
3562
3563case "$gtk_version_desired:$gtk" in
3564 3:none | any:none)
3565
3566
3567
3568# Check whether --enable-gtktest was given.
3569if test "${enable_gtktest+set}" = set; then :
3570 enableval=$enable_gtktest;
3571else
3572 enable_gtktest=yes
3573fi
3574
3575 min_gtk_version=3.0.0
3576
3577 pkg_config_args="gtk+-3.0 >= $min_gtk_version"
3578 for module in .
3579 do
3580 case "$module" in
3581 gthread)
3582 pkg_config_args="$pkg_config_args gthread-2.0"
3583 ;;
3584 esac
3585 done
3586
3587 no_gtk=""
3588
3589 # Extract the first word of "pkg-config", so it can be a program name with args.
3590set dummy pkg-config; ac_word=$2
3591{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3592$as_echo_n "checking for $ac_word... " >&6; }
3593if ${ac_cv_path_PKG_CONFIG+:} false; then :
3594 $as_echo_n "(cached) " >&6
3595else
3596 case $PKG_CONFIG in
3597 [\\/]* | ?:[\\/]*)
3598 ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
3599 ;;
3600 *)
3601 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3602for as_dir in $PATH
3603do
3604 IFS=$as_save_IFS
3605 test -z "$as_dir" && as_dir=.
3606 for ac_exec_ext in '' $ac_executable_extensions; do
3607 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3608 ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
3609 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3610 break 2
3611 fi
3612done
3613 done
3614IFS=$as_save_IFS
3615
3616 test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
3617 ;;
3618esac
3619fi
3620PKG_CONFIG=$ac_cv_path_PKG_CONFIG
3621if test -n "$PKG_CONFIG"; then
3622 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
3623$as_echo "$PKG_CONFIG" >&6; }
3624else
3625 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3626$as_echo "no" >&6; }
3627fi
3628
3629
3630
3631 if test x$PKG_CONFIG != xno ; then
3632 if $PKG_CONFIG --atleast-pkgconfig-version 0.7 ; then
3633 :
3634 else
3635 echo "*** pkg-config too old; version 0.7 or better required."
3636 no_gtk=yes
3637 PKG_CONFIG=no
3638 fi
3639 else
3640 no_gtk=yes
3641 fi
3642
3643 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK+ - version >= $min_gtk_version" >&5
3644$as_echo_n "checking for GTK+ - version >= $min_gtk_version... " >&6; }
3645
3646 if test x$PKG_CONFIG != xno ; then
3647 ## don't try to run the test against uninstalled libtool libs
3648 if $PKG_CONFIG --uninstalled $pkg_config_args; then
3649 echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH"
3650 enable_gtktest=no
3651 fi
3652
3653 if $PKG_CONFIG $pkg_config_args; then
3654 :
3655 else
3656 no_gtk=yes
3657 fi
3658 fi
3659
3660 if test x"$no_gtk" = x ; then
3661 GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags`
3662 GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs`
3663 gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
3664 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
3665 gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
3666 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
3667 gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
3668 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
3669 if test "x$enable_gtktest" = "xyes" ; then
3670 ac_save_CFLAGS="$CFLAGS"
3671 ac_save_LIBS="$LIBS"
3672 CFLAGS="$CFLAGS $GTK_CFLAGS"
3673 LIBS="$GTK_LIBS $LIBS"
3674 rm -f conf.gtktest
3675 if test "$cross_compiling" = yes; then :
3676 echo $ac_n "cross compiling; assumed OK... $ac_c"
3677else
3678 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3679/* end confdefs.h. */
3680
3681#include <gtk/gtk.h>
3682#include <stdio.h>
3683#include <stdlib.h>
3684
3685int
3686main ()
3687{
3688 unsigned int major, minor, micro;
3689
3690 fclose (fopen ("conf.gtktest", "w"));
3691
3692 if (sscanf("$min_gtk_version", "%u.%u.%u", &major, &minor, &micro) != 3) {
3693 printf("%s, bad version string\n", "$min_gtk_version");
3694 exit(1);
3695 }
3696
3697 if ((gtk_major_version != $gtk_config_major_version) ||
3698 (gtk_minor_version != $gtk_config_minor_version) ||
3699 (gtk_micro_version != $gtk_config_micro_version))
3700 {
3701 printf("\n*** 'pkg-config --modversion gtk+-3.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
3702 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
3703 gtk_major_version, gtk_minor_version, gtk_micro_version);
3704 printf ("*** was found! If pkg-config was correct, then it is best\n");
3705 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
3706 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
3707 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
3708 printf("*** required on your system.\n");
3709 printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
3710 printf("*** to point to the correct configuration files\n");
3711 }
3712 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
3713 (gtk_minor_version != GTK_MINOR_VERSION) ||
3714 (gtk_micro_version != GTK_MICRO_VERSION))
3715 {
3716 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
3717 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
3718 printf("*** library (version %d.%d.%d)\n",
3719 gtk_major_version, gtk_minor_version, gtk_micro_version);
3720 }
3721 else
3722 {
3723 if ((gtk_major_version > major) ||
3724 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
3725 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
3726 {
3727 return 0;
3728 }
3729 else
3730 {
3731 printf("\n*** An old version of GTK+ (%u.%u.%u) was found.\n",
3732 gtk_major_version, gtk_minor_version, gtk_micro_version);
3733 printf("*** You need a version of GTK+ newer than %u.%u.%u. The latest version of\n",
3734 major, minor, micro);
3735 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
3736 printf("***\n");
3737 printf("*** If you have already installed a sufficiently new version, this error\n");
3738 printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
3739 printf("*** being found. The easiest way to fix this is to remove the old version\n");
3740 printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n");
3741 printf("*** correct copy of pkg-config. (In this case, you will have to\n");
3742 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
3743 printf("*** so that the correct libraries are found at run-time))\n");
3744 }
3745 }
3746 return 1;
3747}
3748
3749_ACEOF
3750if ac_fn_c_try_run "$LINENO"; then :
3751
3752else
3753 no_gtk=yes
3754fi
3755rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
3756 conftest.$ac_objext conftest.beam conftest.$ac_ext
3757fi
3758
3759 CFLAGS="$ac_save_CFLAGS"
3760 LIBS="$ac_save_LIBS"
3761 fi
3762 fi
3763 if test "x$no_gtk" = x ; then
3764 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&5
3765$as_echo "yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&6; }
3766 gtk=3
3767 else
3768 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3769$as_echo "no" >&6; }
3770 if test "$PKG_CONFIG" = "no" ; then
3771 echo "*** A new enough version of pkg-config was not found."
3772 echo "*** See http://pkgconfig.sourceforge.net"
3773 else
3774 if test -f conf.gtktest ; then
3775 :
3776 else
3777 echo "*** Could not run GTK+ test program, checking why..."
3778 ac_save_CFLAGS="$CFLAGS"
3779 ac_save_LIBS="$LIBS"
3780 CFLAGS="$CFLAGS $GTK_CFLAGS"
3781 LIBS="$LIBS $GTK_LIBS"
3782 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
3783/* end confdefs.h. */
3784
3785#include <gtk/gtk.h>
3786#include <stdio.h>
3787
3788int
3789main ()
3790{
3791 return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version));
3792 ;
3793 return 0;
3794}
3795_ACEOF
3796if ac_fn_c_try_link "$LINENO"; then :
3797 echo "*** The test program compiled, but did not run. This usually means"
3798 echo "*** that the run-time linker is not finding GTK+ or finding the wrong"
3799 echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your"
3800 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
3801 echo "*** to the installed location Also, make sure you have run ldconfig if that"
3802 echo "*** is required on your system"
3803 echo "***"
3804 echo "*** If you have an old version installed, it is best to remove it, although"
3805 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
3806else
3807 echo "*** The test program failed to compile or link. See the file config.log for the"
3808 echo "*** exact error that occurred. This usually means GTK+ is incorrectly installed."
3809fi
3810rm -f core conftest.err conftest.$ac_objext \
3811 conftest$ac_exeext conftest.$ac_ext
3812 CFLAGS="$ac_save_CFLAGS"
3813 LIBS="$ac_save_LIBS"
3814 fi
3815 fi
3816 GTK_CFLAGS=""
3817 GTK_LIBS=""
3818 :
3819 fi
3820
3821
3822 rm -f conf.gtktest
3823
3824
3825 ;;
3826esac
3827
3828case "$gtk_version_desired:$gtk" in
3829 2:none | any:none)
3830
3831
3832
3833
3834
3835
3836
3837
3838if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
3839 if test -n "$ac_tool_prefix"; then
3840 # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
3841set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
3842{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3843$as_echo_n "checking for $ac_word... " >&6; }
3844if ${ac_cv_path_PKG_CONFIG+:} false; then :
3845 $as_echo_n "(cached) " >&6
3846else
3847 case $PKG_CONFIG in
3848 [\\/]* | ?:[\\/]*)
3849 ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
3850 ;;
3851 *)
3852 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3853for as_dir in $PATH
3854do
3855 IFS=$as_save_IFS
3856 test -z "$as_dir" && as_dir=.
3857 for ac_exec_ext in '' $ac_executable_extensions; do
3858 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3859 ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
3860 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3861 break 2
3862 fi
3863done
3864 done
3865IFS=$as_save_IFS
3866
3867 ;;
3868esac
3869fi
3870PKG_CONFIG=$ac_cv_path_PKG_CONFIG
3871if test -n "$PKG_CONFIG"; then
3872 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
3873$as_echo "$PKG_CONFIG" >&6; }
3874else
3875 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3876$as_echo "no" >&6; }
3877fi
3878
3879
3880fi
3881if test -z "$ac_cv_path_PKG_CONFIG"; then
3882 ac_pt_PKG_CONFIG=$PKG_CONFIG
3883 # Extract the first word of "pkg-config", so it can be a program name with args.
3884set dummy pkg-config; ac_word=$2
3885{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3886$as_echo_n "checking for $ac_word... " >&6; }
3887if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
3888 $as_echo_n "(cached) " >&6
3889else
3890 case $ac_pt_PKG_CONFIG in
3891 [\\/]* | ?:[\\/]*)
3892 ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
3893 ;;
3894 *)
3895 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3896for as_dir in $PATH
3897do
3898 IFS=$as_save_IFS
3899 test -z "$as_dir" && as_dir=.
3900 for ac_exec_ext in '' $ac_executable_extensions; do
3901 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3902 ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
3903 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3904 break 2
3905 fi
3906done
3907 done
3908IFS=$as_save_IFS
3909
3910 ;;
3911esac
3912fi
3913ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
3914if test -n "$ac_pt_PKG_CONFIG"; then
3915 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
3916$as_echo "$ac_pt_PKG_CONFIG" >&6; }
3917else
3918 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3919$as_echo "no" >&6; }
3920fi
3921
3922 if test "x$ac_pt_PKG_CONFIG" = x; then
3923 PKG_CONFIG=""
3924 else
3925 case $cross_compiling:$ac_tool_warned in
3926yes:)
3927{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
3928$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
3929ac_tool_warned=yes ;;
3930esac
3931 PKG_CONFIG=$ac_pt_PKG_CONFIG
3932 fi
3933else
3934 PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
3935fi
3936
3937fi
3938if test -n "$PKG_CONFIG"; then
3939 _pkg_min_version=0.9.0
3940 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
3941$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
3942 if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
3943 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
3944$as_echo "yes" >&6; }
3945 else
3946 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3947$as_echo "no" >&6; }
3948 PKG_CONFIG=""
3949 fi
3950fi
3951# Check whether --enable-gtktest was given.
3952if test "${enable_gtktest+set}" = set; then :
3953 enableval=$enable_gtktest;
3954else
3955 enable_gtktest=yes
3956fi
3957
3958
3959 pkg_config_args=gtk+-2.0
3960 for module in .
3961 do
3962 case "$module" in
3963 gthread)
3964 pkg_config_args="$pkg_config_args gthread-2.0"
3965 ;;
3966 esac
3967 done
3968
3969 no_gtk=""
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
3980 if test -n "$ac_tool_prefix"; then
3981 # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
3982set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
3983{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3984$as_echo_n "checking for $ac_word... " >&6; }
3985if ${ac_cv_path_PKG_CONFIG+:} false; then :
3986 $as_echo_n "(cached) " >&6
3987else
3988 case $PKG_CONFIG in
3989 [\\/]* | ?:[\\/]*)
3990 ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
3991 ;;
3992 *)
3993 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3994for as_dir in $PATH
3995do
3996 IFS=$as_save_IFS
3997 test -z "$as_dir" && as_dir=.
3998 for ac_exec_ext in '' $ac_executable_extensions; do
3999 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4000 ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
4001 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4002 break 2
4003 fi
4004done
4005 done
4006IFS=$as_save_IFS
4007
4008 ;;
4009esac
4010fi
4011PKG_CONFIG=$ac_cv_path_PKG_CONFIG
4012if test -n "$PKG_CONFIG"; then
4013 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
4014$as_echo "$PKG_CONFIG" >&6; }
4015else
4016 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4017$as_echo "no" >&6; }
4018fi
4019
4020
4021fi
4022if test -z "$ac_cv_path_PKG_CONFIG"; then
4023 ac_pt_PKG_CONFIG=$PKG_CONFIG
4024 # Extract the first word of "pkg-config", so it can be a program name with args.
4025set dummy pkg-config; ac_word=$2
4026{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4027$as_echo_n "checking for $ac_word... " >&6; }
4028if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
4029 $as_echo_n "(cached) " >&6
4030else
4031 case $ac_pt_PKG_CONFIG in
4032 [\\/]* | ?:[\\/]*)
4033 ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
4034 ;;
4035 *)
4036 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4037for as_dir in $PATH
4038do
4039 IFS=$as_save_IFS
4040 test -z "$as_dir" && as_dir=.
4041 for ac_exec_ext in '' $ac_executable_extensions; do
4042 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4043 ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
4044 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4045 break 2
4046 fi
4047done
4048 done
4049IFS=$as_save_IFS
4050
4051 ;;
4052esac
4053fi
4054ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
4055if test -n "$ac_pt_PKG_CONFIG"; then
4056 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
4057$as_echo "$ac_pt_PKG_CONFIG" >&6; }
4058else
4059 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4060$as_echo "no" >&6; }
4061fi
4062
4063 if test "x$ac_pt_PKG_CONFIG" = x; then
4064 PKG_CONFIG=""
4065 else
4066 case $cross_compiling:$ac_tool_warned in
4067yes:)
4068{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
4069$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
4070ac_tool_warned=yes ;;
4071esac
4072 PKG_CONFIG=$ac_pt_PKG_CONFIG
4073 fi
4074else
4075 PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
4076fi
4077
4078fi
4079if test -n "$PKG_CONFIG"; then
4080 _pkg_min_version=0.7
4081 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
4082$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
4083 if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
4084 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
4085$as_echo "yes" >&6; }
4086 else
4087 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4088$as_echo "no" >&6; }
4089 PKG_CONFIG=""
4090 fi
4091fi
4092
4093 min_gtk_version=2.0.0
4094 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK+ - version >= $min_gtk_version" >&5
4095$as_echo_n "checking for GTK+ - version >= $min_gtk_version... " >&6; }
4096
4097 if test x$PKG_CONFIG != xno ; then
4098 ## don't try to run the test against uninstalled libtool libs
4099 if $PKG_CONFIG --uninstalled $pkg_config_args; then
4100 echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH"
4101 enable_gtktest=no
4102 fi
4103
4104 if $PKG_CONFIG --atleast-version $min_gtk_version $pkg_config_args; then
4105 :
4106 else
4107 no_gtk=yes
4108 fi
4109 fi
4110
4111 if test x"$no_gtk" = x ; then
4112 GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags`
4113 GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs`
4114 gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
4115 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
4116 gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
4117 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
4118 gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
4119 sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
4120 if test "x$enable_gtktest" = "xyes" ; then
4121 ac_save_CFLAGS="$CFLAGS"
4122 ac_save_LIBS="$LIBS"
4123 CFLAGS="$CFLAGS $GTK_CFLAGS"
4124 LIBS="$GTK_LIBS $LIBS"
4125 rm -f conf.gtktest
4126 if test "$cross_compiling" = yes; then :
4127 echo $ac_n "cross compiling; assumed OK... $ac_c"
4128else
4129 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4130/* end confdefs.h. */
4131
4132#include <gtk/gtk.h>
4133#include <stdio.h>
4134#include <stdlib.h>
4135
4136int
4137main ()
4138{
4139 int major, minor, micro;
4140 char *tmp_version;
4141
4142 fclose (fopen ("conf.gtktest", "w"));
4143
4144 /* HP/UX 9 (%@#!) writes to sscanf strings */
4145 tmp_version = g_strdup("$min_gtk_version");
4146 if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
4147 printf("%s, bad version string\n", "$min_gtk_version");
4148 exit(1);
4149 }
4150
4151 if ((gtk_major_version != $gtk_config_major_version) ||
4152 (gtk_minor_version != $gtk_config_minor_version) ||
4153 (gtk_micro_version != $gtk_config_micro_version))
4154 {
4155 printf("\n*** 'pkg-config --modversion gtk+-2.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n",
4156 $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
4157 gtk_major_version, gtk_minor_version, gtk_micro_version);
4158 printf ("*** was found! If pkg-config was correct, then it is best\n");
4159 printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
4160 printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
4161 printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
4162 printf("*** required on your system.\n");
4163 printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
4164 printf("*** to point to the correct configuration files\n");
4165 }
4166 else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
4167 (gtk_minor_version != GTK_MINOR_VERSION) ||
4168 (gtk_micro_version != GTK_MICRO_VERSION))
4169 {
4170 printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
4171 GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
4172 printf("*** library (version %d.%d.%d)\n",
4173 gtk_major_version, gtk_minor_version, gtk_micro_version);
4174 }
4175 else
4176 {
4177 if ((gtk_major_version > major) ||
4178 ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
4179 ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
4180 {
4181 return 0;
4182 }
4183 else
4184 {
4185 printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
4186 gtk_major_version, gtk_minor_version, gtk_micro_version);
4187 printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
4188 major, minor, micro);
4189 printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
4190 printf("***\n");
4191 printf("*** If you have already installed a sufficiently new version, this error\n");
4192 printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
4193 printf("*** being found. The easiest way to fix this is to remove the old version\n");
4194 printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n");
4195 printf("*** correct copy of pkg-config. (In this case, you will have to\n");
4196 printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
4197 printf("*** so that the correct libraries are found at run-time))\n");
4198 }
4199 }
4200 return 1;
4201}
4202
4203_ACEOF
4204if ac_fn_c_try_run "$LINENO"; then :
4205
4206else
4207 no_gtk=yes
4208fi
4209rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
4210 conftest.$ac_objext conftest.beam conftest.$ac_ext
4211fi
4212
4213 CFLAGS="$ac_save_CFLAGS"
4214 LIBS="$ac_save_LIBS"
4215 fi
4216 fi
4217 if test "x$no_gtk" = x ; then
4218 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&5
4219$as_echo "yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&6; }
4220 gtk=2
4221 else
4222 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4223$as_echo "no" >&6; }
4224 if test "$PKG_CONFIG" = "no" ; then
4225 echo "*** A new enough version of pkg-config was not found."
4226 echo "*** See http://pkgconfig.sourceforge.net"
4227 else
4228 if test -f conf.gtktest ; then
4229 :
4230 else
4231 echo "*** Could not run GTK+ test program, checking why..."
4232 ac_save_CFLAGS="$CFLAGS"
4233 ac_save_LIBS="$LIBS"
4234 CFLAGS="$CFLAGS $GTK_CFLAGS"
4235 LIBS="$LIBS $GTK_LIBS"
4236 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4237/* end confdefs.h. */
4238
4239#include <gtk/gtk.h>
4240#include <stdio.h>
4241
4242int
4243main ()
4244{
4245 return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version));
4246 ;
4247 return 0;
4248}
4249_ACEOF
4250if ac_fn_c_try_link "$LINENO"; then :
4251 echo "*** The test program compiled, but did not run. This usually means"
4252 echo "*** that the run-time linker is not finding GTK+ or finding the wrong"
4253 echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your"
4254 echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
4255 echo "*** to the installed location Also, make sure you have run ldconfig if that"
4256 echo "*** is required on your system"
4257 echo "***"
4258 echo "*** If you have an old version installed, it is best to remove it, although"
4259 echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
4260else
4261 echo "*** The test program failed to compile or link. See the file config.log for the"
4262 echo "*** exact error that occured. This usually means GTK+ is incorrectly installed."
4263fi
4264rm -f core conftest.err conftest.$ac_objext \
4265 conftest$ac_exeext conftest.$ac_ext
4266 CFLAGS="$ac_save_CFLAGS"
4267 LIBS="$ac_save_LIBS"
4268 fi
4269 fi
4270 GTK_CFLAGS=""
4271 GTK_LIBS=""
4272 :
4273 fi
4274
4275
4276 rm -f conf.gtktest
4277
4278
4279 ;;
4280esac
4281
4282if test "$gtk" = "none"; then
4283 as_fn_error $? "cannot build without GTK 2 or GTK 3" "$LINENO" 5
4284fi
4285
4286if test "x$GCC" = "xyes"; then
4287 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for usable gcc warning flags" >&5
4288$as_echo_n "checking for usable gcc warning flags... " >&6; }
4289 gccwarningflags=
4290 for flag in -Wall -Werror -std=c89 -pedantic; do
4291 ac_save_CFLAGS="$CFLAGS"
4292 ac_save_LIBS="$LIBS"
4293 CFLAGS="$CFLAGS$gccwarningflags $flag $GTK_CFLAGS"
4294 LIBS="$GTK_LIBS $LIBS"
4295 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4296/* end confdefs.h. */
4297
4298 #include <stdio.h>
4299 #include <assert.h>
4300 #include <stdlib.h>
4301 #include <time.h>
4302 #include <stdarg.h>
4303 #include <string.h>
4304 #include <errno.h>
4305 #include <math.h>
4306
4307 #include <sys/time.h>
4308 #include <sys/resource.h>
4309
4310 #include <gtk/gtk.h>
4311 #include <gdk/gdkkeysyms.h>
4312
4313 #include <gdk-pixbuf/gdk-pixbuf.h>
4314
4315 #include <gdk/gdkx.h>
4316 #include <X11/Xlib.h>
4317 #include <X11/Xutil.h>
4318 #include <X11/Xatom.h>
4319
4320int
4321main ()
4322{
4323
4324 return 0;
4325
4326 ;
4327 return 0;
4328}
4329_ACEOF
4330if ac_fn_c_try_compile "$LINENO"; then :
4331 gccwarningflags="$gccwarningflags $flag"
4332fi
4333rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
4334 CFLAGS="$ac_save_CFLAGS"
4335 LIBS="$ac_save_LIBS"
4336 done
4337 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gccwarningflags" >&5
4338$as_echo "$gccwarningflags" >&6; }
4339 CFLAGS="$CFLAGS$gccwarningflags"
4340fi
4341
4342if test -n "$ac_tool_prefix"; then
4343 # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
4344set dummy ${ac_tool_prefix}ranlib; ac_word=$2
4345{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4346$as_echo_n "checking for $ac_word... " >&6; }
4347if ${ac_cv_prog_RANLIB+:} false; then :
4348 $as_echo_n "(cached) " >&6
4349else
4350 if test -n "$RANLIB"; then
4351 ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
4352else
4353as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4354for as_dir in $PATH
4355do
4356 IFS=$as_save_IFS
4357 test -z "$as_dir" && as_dir=.
4358 for ac_exec_ext in '' $ac_executable_extensions; do
4359 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4360 ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
4361 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4362 break 2
4363 fi
4364done
4365 done
4366IFS=$as_save_IFS
4367
4368fi
4369fi
4370RANLIB=$ac_cv_prog_RANLIB
4371if test -n "$RANLIB"; then
4372 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
4373$as_echo "$RANLIB" >&6; }
4374else
4375 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4376$as_echo "no" >&6; }
4377fi
4378
4379
4380fi
4381if test -z "$ac_cv_prog_RANLIB"; then
4382 ac_ct_RANLIB=$RANLIB
4383 # Extract the first word of "ranlib", so it can be a program name with args.
4384set dummy ranlib; ac_word=$2
4385{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4386$as_echo_n "checking for $ac_word... " >&6; }
4387if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
4388 $as_echo_n "(cached) " >&6
4389else
4390 if test -n "$ac_ct_RANLIB"; then
4391 ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
4392else
4393as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4394for as_dir in $PATH
4395do
4396 IFS=$as_save_IFS
4397 test -z "$as_dir" && as_dir=.
4398 for ac_exec_ext in '' $ac_executable_extensions; do
4399 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4400 ac_cv_prog_ac_ct_RANLIB="ranlib"
4401 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4402 break 2
4403 fi
4404done
4405 done
4406IFS=$as_save_IFS
4407
4408fi
4409fi
4410ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
4411if test -n "$ac_ct_RANLIB"; then
4412 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
4413$as_echo "$ac_ct_RANLIB" >&6; }
4414else
4415 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4416$as_echo "no" >&6; }
4417fi
4418
4419 if test "x$ac_ct_RANLIB" = x; then
4420 RANLIB=":"
4421 else
4422 case $cross_compiling:$ac_tool_warned in
4423yes:)
4424{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
4425$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
4426ac_tool_warned=yes ;;
4427esac
4428 RANLIB=$ac_ct_RANLIB
4429 fi
4430else
4431 RANLIB="$ac_cv_prog_RANLIB"
4432fi
4433
4434
4435ac_config_files="$ac_config_files Makefile"
4436
4437cat >confcache <<\_ACEOF
4438# This file is a shell script that caches the results of configure
4439# tests run on this system so they can be shared between configure
4440# scripts and configure runs, see configure's option --config-cache.
4441# It is not useful on other systems. If it contains results you don't
4442# want to keep, you may remove or edit it.
4443#
4444# config.status only pays attention to the cache file if you give it
4445# the --recheck option to rerun configure.
4446#
4447# `ac_cv_env_foo' variables (set or unset) will be overridden when
4448# loading this file, other *unset* `ac_cv_foo' will be assigned the
4449# following values.
4450
4451_ACEOF
4452
4453# The following way of writing the cache mishandles newlines in values,
4454# but we know of no workaround that is simple, portable, and efficient.
4455# So, we kill variables containing newlines.
4456# Ultrix sh set writes to stderr and can't be redirected directly,
4457# and sets the high bit in the cache file unless we assign to the vars.
4458(
4459 for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
4460 eval ac_val=\$$ac_var
4461 case $ac_val in #(
4462 *${as_nl}*)
4463 case $ac_var in #(
4464 *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
4465$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
4466 esac
4467 case $ac_var in #(
4468 _ | IFS | as_nl) ;; #(
4469 BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
4470 *) { eval $ac_var=; unset $ac_var;} ;;
4471 esac ;;
4472 esac
4473 done
4474
4475 (set) 2>&1 |
4476 case $as_nl`(ac_space=' '; set) 2>&1` in #(
4477 *${as_nl}ac_space=\ *)
4478 # `set' does not quote correctly, so add quotes: double-quote
4479 # substitution turns \\\\ into \\, and sed turns \\ into \.
4480 sed -n \
4481 "s/'/'\\\\''/g;
4482 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
4483 ;; #(
4484 *)
4485 # `set' quotes correctly as required by POSIX, so do not add quotes.
4486 sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
4487 ;;
4488 esac |
4489 sort
4490) |
4491 sed '
4492 /^ac_cv_env_/b end
4493 t clear
4494 :clear
4495 s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
4496 t end
4497 s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
4498 :end' >>confcache
4499if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
4500 if test -w "$cache_file"; then
4501 if test "x$cache_file" != "x/dev/null"; then
4502 { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
4503$as_echo "$as_me: updating cache $cache_file" >&6;}
4504 if test ! -f "$cache_file" || test -h "$cache_file"; then
4505 cat confcache >"$cache_file"
4506 else
4507 case $cache_file in #(
4508 */* | ?:*)
4509 mv -f confcache "$cache_file"$$ &&
4510 mv -f "$cache_file"$$ "$cache_file" ;; #(
4511 *)
4512 mv -f confcache "$cache_file" ;;
4513 esac
4514 fi
4515 fi
4516 else
4517 { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
4518$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
4519 fi
4520fi
4521rm -f confcache
4522
4523test "x$prefix" = xNONE && prefix=$ac_default_prefix
4524# Let make expand exec_prefix.
4525test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
4526
4527# Transform confdefs.h into DEFS.
4528# Protect against shell expansion while executing Makefile rules.
4529# Protect against Makefile macro expansion.
4530#
4531# If the first sed substitution is executed (which looks for macros that
4532# take arguments), then branch to the quote section. Otherwise,
4533# look for a macro that doesn't take arguments.
4534ac_script='
4535:mline
4536/\\$/{
4537 N
4538 s,\\\n,,
4539 b mline
4540}
4541t clear
4542:clear
4543s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g
4544t quote
4545s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g
4546t quote
4547b any
4548:quote
4549s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g
4550s/\[/\\&/g
4551s/\]/\\&/g
4552s/\$/$$/g
4553H
4554:any
4555${
4556 g
4557 s/^\n//
4558 s/\n/ /g
4559 p
4560}
4561'
4562DEFS=`sed -n "$ac_script" confdefs.h`
4563
4564
4565ac_libobjs=
4566ac_ltlibobjs=
4567U=
4568for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
4569 # 1. Remove the extension, and $U if already installed.
4570 ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
4571 ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
4572 # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
4573 # will be set to the directory where LIBOBJS objects are built.
4574 as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
4575 as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
4576done
4577LIBOBJS=$ac_libobjs
4578
4579LTLIBOBJS=$ac_ltlibobjs
4580
4581
4582{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
4583$as_echo_n "checking that generated files are newer than configure... " >&6; }
4584 if test -n "$am_sleep_pid"; then
4585 # Hide warnings about reused PIDs.
4586 wait $am_sleep_pid 2>/dev/null
4587 fi
4588 { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
4589$as_echo "done" >&6; }
4590 if test -n "$EXEEXT"; then
4591 am__EXEEXT_TRUE=
4592 am__EXEEXT_FALSE='#'
4593else
4594 am__EXEEXT_TRUE='#'
4595 am__EXEEXT_FALSE=
4596fi
4597
4598if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
4599 as_fn_error $? "conditional \"AMDEP\" was never defined.
4600Usually this means the macro was only invoked conditionally." "$LINENO" 5
4601fi
4602if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
4603 as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
4604Usually this means the macro was only invoked conditionally." "$LINENO" 5
4605fi
4606
4607: "${CONFIG_STATUS=./config.status}"
4608ac_write_fail=0
4609ac_clean_files_save=$ac_clean_files
4610ac_clean_files="$ac_clean_files $CONFIG_STATUS"
4611{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
4612$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
4613as_write_fail=0
4614cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
4615#! $SHELL
4616# Generated by $as_me.
4617# Run this file to recreate the current configuration.
4618# Compiler output produced by configure, useful for debugging
4619# configure, is in config.log if it exists.
4620
4621debug=false
4622ac_cs_recheck=false
4623ac_cs_silent=false
4624
4625SHELL=\${CONFIG_SHELL-$SHELL}
4626export SHELL
4627_ASEOF
4628cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
4629## -------------------- ##
4630## M4sh Initialization. ##
4631## -------------------- ##
4632
4633# Be more Bourne compatible
4634DUALCASE=1; export DUALCASE # for MKS sh
4635if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
4636 emulate sh
4637 NULLCMD=:
4638 # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
4639 # is contrary to our usage. Disable this feature.
4640 alias -g '${1+"$@"}'='"$@"'
4641 setopt NO_GLOB_SUBST
4642else
4643 case `(set -o) 2>/dev/null` in #(
4644 *posix*) :
4645 set -o posix ;; #(
4646 *) :
4647 ;;
4648esac
4649fi
4650
4651
4652as_nl='
4653'
4654export as_nl
4655# Printing a long string crashes Solaris 7 /usr/bin/printf.
4656as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
4657as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
4658as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
4659# Prefer a ksh shell builtin over an external printf program on Solaris,
4660# but without wasting forks for bash or zsh.
4661if test -z "$BASH_VERSION$ZSH_VERSION" \
4662 && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
4663 as_echo='print -r --'
4664 as_echo_n='print -rn --'
4665elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
4666 as_echo='printf %s\n'
4667 as_echo_n='printf %s'
4668else
4669 if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
4670 as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
4671 as_echo_n='/usr/ucb/echo -n'
4672 else
4673 as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
4674 as_echo_n_body='eval
4675 arg=$1;
4676 case $arg in #(
4677 *"$as_nl"*)
4678 expr "X$arg" : "X\\(.*\\)$as_nl";
4679 arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
4680 esac;
4681 expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
4682 '
4683 export as_echo_n_body
4684 as_echo_n='sh -c $as_echo_n_body as_echo'
4685 fi
4686 export as_echo_body
4687 as_echo='sh -c $as_echo_body as_echo'
4688fi
4689
4690# The user is always right.
4691if test "${PATH_SEPARATOR+set}" != set; then
4692 PATH_SEPARATOR=:
4693 (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
4694 (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
4695 PATH_SEPARATOR=';'
4696 }
4697fi
4698
4699
4700# IFS
4701# We need space, tab and new line, in precisely that order. Quoting is
4702# there to prevent editors from complaining about space-tab.
4703# (If _AS_PATH_WALK were called with IFS unset, it would disable word
4704# splitting by setting IFS to empty value.)
4705IFS=" "" $as_nl"
4706
4707# Find who we are. Look in the path if we contain no directory separator.
4708as_myself=
4709case $0 in #((
4710 *[\\/]* ) as_myself=$0 ;;
4711 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4712for as_dir in $PATH
4713do
4714 IFS=$as_save_IFS
4715 test -z "$as_dir" && as_dir=.
4716 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
4717 done
4718IFS=$as_save_IFS
4719
4720 ;;
4721esac
4722# We did not find ourselves, most probably we were run as `sh COMMAND'
4723# in which case we are not to be found in the path.
4724if test "x$as_myself" = x; then
4725 as_myself=$0
4726fi
4727if test ! -f "$as_myself"; then
4728 $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
4729 exit 1
4730fi
4731
4732# Unset variables that we do not need and which cause bugs (e.g. in
4733# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
4734# suppresses any "Segmentation fault" message there. '((' could
4735# trigger a bug in pdksh 5.2.14.
4736for as_var in BASH_ENV ENV MAIL MAILPATH
4737do eval test x\${$as_var+set} = xset \
4738 && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
4739done
4740PS1='$ '
4741PS2='> '
4742PS4='+ '
4743
4744# NLS nuisances.
4745LC_ALL=C
4746export LC_ALL
4747LANGUAGE=C
4748export LANGUAGE
4749
4750# CDPATH.
4751(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
4752
4753
4754# as_fn_error STATUS ERROR [LINENO LOG_FD]
4755# ----------------------------------------
4756# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
4757# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
4758# script with STATUS, using 1 if that was 0.
4759as_fn_error ()
4760{
4761 as_status=$1; test $as_status -eq 0 && as_status=1
4762 if test "$4"; then
4763 as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
4764 $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
4765 fi
4766 $as_echo "$as_me: error: $2" >&2
4767 as_fn_exit $as_status
4768} # as_fn_error
4769
4770
4771# as_fn_set_status STATUS
4772# -----------------------
4773# Set $? to STATUS, without forking.
4774as_fn_set_status ()
4775{
4776 return $1
4777} # as_fn_set_status
4778
4779# as_fn_exit STATUS
4780# -----------------
4781# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
4782as_fn_exit ()
4783{
4784 set +e
4785 as_fn_set_status $1
4786 exit $1
4787} # as_fn_exit
4788
4789# as_fn_unset VAR
4790# ---------------
4791# Portably unset VAR.
4792as_fn_unset ()
4793{
4794 { eval $1=; unset $1;}
4795}
4796as_unset=as_fn_unset
4797# as_fn_append VAR VALUE
4798# ----------------------
4799# Append the text in VALUE to the end of the definition contained in VAR. Take
4800# advantage of any shell optimizations that allow amortized linear growth over
4801# repeated appends, instead of the typical quadratic growth present in naive
4802# implementations.
4803if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
4804 eval 'as_fn_append ()
4805 {
4806 eval $1+=\$2
4807 }'
4808else
4809 as_fn_append ()
4810 {
4811 eval $1=\$$1\$2
4812 }
4813fi # as_fn_append
4814
4815# as_fn_arith ARG...
4816# ------------------
4817# Perform arithmetic evaluation on the ARGs, and store the result in the
4818# global $as_val. Take advantage of shells that can avoid forks. The arguments
4819# must be portable across $(()) and expr.
4820if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
4821 eval 'as_fn_arith ()
4822 {
4823 as_val=$(( $* ))
4824 }'
4825else
4826 as_fn_arith ()
4827 {
4828 as_val=`expr "$@" || test $? -eq 1`
4829 }
4830fi # as_fn_arith
4831
4832
4833if expr a : '\(a\)' >/dev/null 2>&1 &&
4834 test "X`expr 00001 : '.*\(...\)'`" = X001; then
4835 as_expr=expr
4836else
4837 as_expr=false
4838fi
4839
4840if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
4841 as_basename=basename
4842else
4843 as_basename=false
4844fi
4845
4846if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
4847 as_dirname=dirname
4848else
4849 as_dirname=false
4850fi
4851
4852as_me=`$as_basename -- "$0" ||
4853$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
4854 X"$0" : 'X\(//\)$' \| \
4855 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
4856$as_echo X/"$0" |
4857 sed '/^.*\/\([^/][^/]*\)\/*$/{
4858 s//\1/
4859 q
4860 }
4861 /^X\/\(\/\/\)$/{
4862 s//\1/
4863 q
4864 }
4865 /^X\/\(\/\).*/{
4866 s//\1/
4867 q
4868 }
4869 s/.*/./; q'`
4870
4871# Avoid depending upon Character Ranges.
4872as_cr_letters='abcdefghijklmnopqrstuvwxyz'
4873as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
4874as_cr_Letters=$as_cr_letters$as_cr_LETTERS
4875as_cr_digits='0123456789'
4876as_cr_alnum=$as_cr_Letters$as_cr_digits
4877
4878ECHO_C= ECHO_N= ECHO_T=
4879case `echo -n x` in #(((((
4880-n*)
4881 case `echo 'xy\c'` in
4882 *c*) ECHO_T=' ';; # ECHO_T is single tab character.
4883 xy) ECHO_C='\c';;
4884 *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
4885 ECHO_T=' ';;
4886 esac;;
4887*)
4888 ECHO_N='-n';;
4889esac
4890
4891rm -f conf$$ conf$$.exe conf$$.file
4892if test -d conf$$.dir; then
4893 rm -f conf$$.dir/conf$$.file
4894else
4895 rm -f conf$$.dir
4896 mkdir conf$$.dir 2>/dev/null
4897fi
4898if (echo >conf$$.file) 2>/dev/null; then
4899 if ln -s conf$$.file conf$$ 2>/dev/null; then
4900 as_ln_s='ln -s'
4901 # ... but there are two gotchas:
4902 # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
4903 # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
4904 # In both cases, we have to default to `cp -pR'.
4905 ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
4906 as_ln_s='cp -pR'
4907 elif ln conf$$.file conf$$ 2>/dev/null; then
4908 as_ln_s=ln
4909 else
4910 as_ln_s='cp -pR'
4911 fi
4912else
4913 as_ln_s='cp -pR'
4914fi
4915rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
4916rmdir conf$$.dir 2>/dev/null
4917
4918
4919# as_fn_mkdir_p
4920# -------------
4921# Create "$as_dir" as a directory, including parents if necessary.
4922as_fn_mkdir_p ()
4923{
4924
4925 case $as_dir in #(
4926 -*) as_dir=./$as_dir;;
4927 esac
4928 test -d "$as_dir" || eval $as_mkdir_p || {
4929 as_dirs=
4930 while :; do
4931 case $as_dir in #(
4932 *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
4933 *) as_qdir=$as_dir;;
4934 esac
4935 as_dirs="'$as_qdir' $as_dirs"
4936 as_dir=`$as_dirname -- "$as_dir" ||
4937$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
4938 X"$as_dir" : 'X\(//\)[^/]' \| \
4939 X"$as_dir" : 'X\(//\)$' \| \
4940 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
4941$as_echo X"$as_dir" |
4942 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
4943 s//\1/
4944 q
4945 }
4946 /^X\(\/\/\)[^/].*/{
4947 s//\1/
4948 q
4949 }
4950 /^X\(\/\/\)$/{
4951 s//\1/
4952 q
4953 }
4954 /^X\(\/\).*/{
4955 s//\1/
4956 q
4957 }
4958 s/.*/./; q'`
4959 test -d "$as_dir" && break
4960 done
4961 test -z "$as_dirs" || eval "mkdir $as_dirs"
4962 } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
4963
4964
4965} # as_fn_mkdir_p
4966if mkdir -p . 2>/dev/null; then
4967 as_mkdir_p='mkdir -p "$as_dir"'
4968else
4969 test -d ./-p && rmdir ./-p
4970 as_mkdir_p=false
4971fi
4972
4973
4974# as_fn_executable_p FILE
4975# -----------------------
4976# Test if FILE is an executable regular file.
4977as_fn_executable_p ()
4978{
4979 test -f "$1" && test -x "$1"
4980} # as_fn_executable_p
4981as_test_x='test -x'
4982as_executable_p=as_fn_executable_p
4983
4984# Sed expression to map a string onto a valid CPP name.
4985as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
4986
4987# Sed expression to map a string onto a valid variable name.
4988as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
4989
4990
4991exec 6>&1
4992## ----------------------------------- ##
4993## Main body of $CONFIG_STATUS script. ##
4994## ----------------------------------- ##
4995_ASEOF
4996test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
4997
4998cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
4999# Save the log message, to keep $0 and so on meaningful, and to
5000# report actual input values of CONFIG_FILES etc. instead of their
5001# values after options handling.
5002ac_log="
5003This file was extended by puzzles $as_me 6.66, which was
5004generated by GNU Autoconf 2.69. Invocation command line was
5005
5006 CONFIG_FILES = $CONFIG_FILES
5007 CONFIG_HEADERS = $CONFIG_HEADERS
5008 CONFIG_LINKS = $CONFIG_LINKS
5009 CONFIG_COMMANDS = $CONFIG_COMMANDS
5010 $ $0 $@
5011
5012on `(hostname || uname -n) 2>/dev/null | sed 1q`
5013"
5014
5015_ACEOF
5016
5017case $ac_config_files in *"
5018"*) set x $ac_config_files; shift; ac_config_files=$*;;
5019esac
5020
5021
5022
5023cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5024# Files that config.status was made for.
5025config_files="$ac_config_files"
5026config_commands="$ac_config_commands"
5027
5028_ACEOF
5029
5030cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5031ac_cs_usage="\
5032\`$as_me' instantiates files and other configuration actions
5033from templates according to the current configuration. Unless the files
5034and actions are specified as TAGs, all are instantiated by default.
5035
5036Usage: $0 [OPTION]... [TAG]...
5037
5038 -h, --help print this help, then exit
5039 -V, --version print version number and configuration settings, then exit
5040 --config print configuration, then exit
5041 -q, --quiet, --silent
5042 do not print progress messages
5043 -d, --debug don't remove temporary files
5044 --recheck update $as_me by reconfiguring in the same conditions
5045 --file=FILE[:TEMPLATE]
5046 instantiate the configuration file FILE
5047
5048Configuration files:
5049$config_files
5050
5051Configuration commands:
5052$config_commands
5053
5054Report bugs to <anakin@pobox.com>."
5055
5056_ACEOF
5057cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5058ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
5059ac_cs_version="\\
5060puzzles config.status 6.66
5061configured by $0, generated by GNU Autoconf 2.69,
5062 with options \\"\$ac_cs_config\\"
5063
5064Copyright (C) 2012 Free Software Foundation, Inc.
5065This config.status script is free software; the Free Software Foundation
5066gives unlimited permission to copy, distribute and modify it."
5067
5068ac_pwd='$ac_pwd'
5069srcdir='$srcdir'
5070INSTALL='$INSTALL'
5071MKDIR_P='$MKDIR_P'
5072AWK='$AWK'
5073test -n "\$AWK" || AWK=awk
5074_ACEOF
5075
5076cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5077# The default lists apply if the user does not specify any file.
5078ac_need_defaults=:
5079while test $# != 0
5080do
5081 case $1 in
5082 --*=?*)
5083 ac_option=`expr "X$1" : 'X\([^=]*\)='`
5084 ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
5085 ac_shift=:
5086 ;;
5087 --*=)
5088 ac_option=`expr "X$1" : 'X\([^=]*\)='`
5089 ac_optarg=
5090 ac_shift=:
5091 ;;
5092 *)
5093 ac_option=$1
5094 ac_optarg=$2
5095 ac_shift=shift
5096 ;;
5097 esac
5098
5099 case $ac_option in
5100 # Handling of the options.
5101 -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
5102 ac_cs_recheck=: ;;
5103 --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
5104 $as_echo "$ac_cs_version"; exit ;;
5105 --config | --confi | --conf | --con | --co | --c )
5106 $as_echo "$ac_cs_config"; exit ;;
5107 --debug | --debu | --deb | --de | --d | -d )
5108 debug=: ;;
5109 --file | --fil | --fi | --f )
5110 $ac_shift
5111 case $ac_optarg in
5112 *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
5113 '') as_fn_error $? "missing file argument" ;;
5114 esac
5115 as_fn_append CONFIG_FILES " '$ac_optarg'"
5116 ac_need_defaults=false;;
5117 --he | --h | --help | --hel | -h )
5118 $as_echo "$ac_cs_usage"; exit ;;
5119 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
5120 | -silent | --silent | --silen | --sile | --sil | --si | --s)
5121 ac_cs_silent=: ;;
5122
5123 # This is an error.
5124 -*) as_fn_error $? "unrecognized option: \`$1'
5125Try \`$0 --help' for more information." ;;
5126
5127 *) as_fn_append ac_config_targets " $1"
5128 ac_need_defaults=false ;;
5129
5130 esac
5131 shift
5132done
5133
5134ac_configure_extra_args=
5135
5136if $ac_cs_silent; then
5137 exec 6>/dev/null
5138 ac_configure_extra_args="$ac_configure_extra_args --silent"
5139fi
5140
5141_ACEOF
5142cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5143if \$ac_cs_recheck; then
5144 set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
5145 shift
5146 \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
5147 CONFIG_SHELL='$SHELL'
5148 export CONFIG_SHELL
5149 exec "\$@"
5150fi
5151
5152_ACEOF
5153cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5154exec 5>>config.log
5155{
5156 echo
5157 sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
5158## Running $as_me. ##
5159_ASBOX
5160 $as_echo "$ac_log"
5161} >&5
5162
5163_ACEOF
5164cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5165#
5166# INIT-COMMANDS
5167#
5168AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
5169
5170_ACEOF
5171
5172cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5173
5174# Handling of arguments.
5175for ac_config_target in $ac_config_targets
5176do
5177 case $ac_config_target in
5178 "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
5179 "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
5180
5181 *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
5182 esac
5183done
5184
5185
5186# If the user did not use the arguments to specify the items to instantiate,
5187# then the envvar interface is used. Set only those that are not.
5188# We use the long form for the default assignment because of an extremely
5189# bizarre bug on SunOS 4.1.3.
5190if $ac_need_defaults; then
5191 test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
5192 test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
5193fi
5194
5195# Have a temporary directory for convenience. Make it in the build tree
5196# simply because there is no reason against having it here, and in addition,
5197# creating and moving files from /tmp can sometimes cause problems.
5198# Hook for its removal unless debugging.
5199# Note that there is a small window in which the directory will not be cleaned:
5200# after its creation but before its name has been assigned to `$tmp'.
5201$debug ||
5202{
5203 tmp= ac_tmp=
5204 trap 'exit_status=$?
5205 : "${ac_tmp:=$tmp}"
5206 { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
5207' 0
5208 trap 'as_fn_exit 1' 1 2 13 15
5209}
5210# Create a (secure) tmp directory for tmp files.
5211
5212{
5213 tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
5214 test -d "$tmp"
5215} ||
5216{
5217 tmp=./conf$$-$RANDOM
5218 (umask 077 && mkdir "$tmp")
5219} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
5220ac_tmp=$tmp
5221
5222# Set up the scripts for CONFIG_FILES section.
5223# No need to generate them if there are no CONFIG_FILES.
5224# This happens for instance with `./config.status config.h'.
5225if test -n "$CONFIG_FILES"; then
5226
5227
5228ac_cr=`echo X | tr X '\015'`
5229# On cygwin, bash can eat \r inside `` if the user requested igncr.
5230# But we know of no other shell where ac_cr would be empty at this
5231# point, so we can use a bashism as a fallback.
5232if test "x$ac_cr" = x; then
5233 eval ac_cr=\$\'\\r\'
5234fi
5235ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
5236if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
5237 ac_cs_awk_cr='\\r'
5238else
5239 ac_cs_awk_cr=$ac_cr
5240fi
5241
5242echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
5243_ACEOF
5244
5245
5246{
5247 echo "cat >conf$$subs.awk <<_ACEOF" &&
5248 echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
5249 echo "_ACEOF"
5250} >conf$$subs.sh ||
5251 as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
5252ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
5253ac_delim='%!_!# '
5254for ac_last_try in false false false false false :; do
5255 . ./conf$$subs.sh ||
5256 as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
5257
5258 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
5259 if test $ac_delim_n = $ac_delim_num; then
5260 break
5261 elif $ac_last_try; then
5262 as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
5263 else
5264 ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
5265 fi
5266done
5267rm -f conf$$subs.sh
5268
5269cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5270cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
5271_ACEOF
5272sed -n '
5273h
5274s/^/S["/; s/!.*/"]=/
5275p
5276g
5277s/^[^!]*!//
5278:repl
5279t repl
5280s/'"$ac_delim"'$//
5281t delim
5282:nl
5283h
5284s/\(.\{148\}\)..*/\1/
5285t more1
5286s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
5287p
5288n
5289b repl
5290:more1
5291s/["\\]/\\&/g; s/^/"/; s/$/"\\/
5292p
5293g
5294s/.\{148\}//
5295t nl
5296:delim
5297h
5298s/\(.\{148\}\)..*/\1/
5299t more2
5300s/["\\]/\\&/g; s/^/"/; s/$/"/
5301p
5302b
5303:more2
5304s/["\\]/\\&/g; s/^/"/; s/$/"\\/
5305p
5306g
5307s/.\{148\}//
5308t delim
5309' <conf$$subs.awk | sed '
5310/^[^""]/{
5311 N
5312 s/\n//
5313}
5314' >>$CONFIG_STATUS || ac_write_fail=1
5315rm -f conf$$subs.awk
5316cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5317_ACAWK
5318cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
5319 for (key in S) S_is_set[key] = 1
5320 FS = ""
5321
5322}
5323{
5324 line = $ 0
5325 nfields = split(line, field, "@")
5326 substed = 0
5327 len = length(field[1])
5328 for (i = 2; i < nfields; i++) {
5329 key = field[i]
5330 keylen = length(key)
5331 if (S_is_set[key]) {
5332 value = S[key]
5333 line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
5334 len += length(value) + length(field[++i])
5335 substed = 1
5336 } else
5337 len += 1 + keylen
5338 }
5339
5340 print line
5341}
5342
5343_ACAWK
5344_ACEOF
5345cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5346if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
5347 sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
5348else
5349 cat
5350fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
5351 || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
5352_ACEOF
5353
5354# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
5355# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
5356# trailing colons and then remove the whole line if VPATH becomes empty
5357# (actually we leave an empty line to preserve line numbers).
5358if test "x$srcdir" = x.; then
5359 ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
5360h
5361s///
5362s/^/:/
5363s/[ ]*$/:/
5364s/:\$(srcdir):/:/g
5365s/:\${srcdir}:/:/g
5366s/:@srcdir@:/:/g
5367s/^:*//
5368s/:*$//
5369x
5370s/\(=[ ]*\).*/\1/
5371G
5372s/\n//
5373s/^[^=]*=[ ]*$//
5374}'
5375fi
5376
5377cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5378fi # test -n "$CONFIG_FILES"
5379
5380
5381eval set X " :F $CONFIG_FILES :C $CONFIG_COMMANDS"
5382shift
5383for ac_tag
5384do
5385 case $ac_tag in
5386 :[FHLC]) ac_mode=$ac_tag; continue;;
5387 esac
5388 case $ac_mode$ac_tag in
5389 :[FHL]*:*);;
5390 :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
5391 :[FH]-) ac_tag=-:-;;
5392 :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
5393 esac
5394 ac_save_IFS=$IFS
5395 IFS=:
5396 set x $ac_tag
5397 IFS=$ac_save_IFS
5398 shift
5399 ac_file=$1
5400 shift
5401
5402 case $ac_mode in
5403 :L) ac_source=$1;;
5404 :[FH])
5405 ac_file_inputs=
5406 for ac_f
5407 do
5408 case $ac_f in
5409 -) ac_f="$ac_tmp/stdin";;
5410 *) # Look for the file first in the build tree, then in the source tree
5411 # (if the path is not absolute). The absolute path cannot be DOS-style,
5412 # because $ac_f cannot contain `:'.
5413 test -f "$ac_f" ||
5414 case $ac_f in
5415 [\\/$]*) false;;
5416 *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
5417 esac ||
5418 as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
5419 esac
5420 case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
5421 as_fn_append ac_file_inputs " '$ac_f'"
5422 done
5423
5424 # Let's still pretend it is `configure' which instantiates (i.e., don't
5425 # use $as_me), people would be surprised to read:
5426 # /* config.h. Generated by config.status. */
5427 configure_input='Generated from '`
5428 $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
5429 `' by configure.'
5430 if test x"$ac_file" != x-; then
5431 configure_input="$ac_file. $configure_input"
5432 { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
5433$as_echo "$as_me: creating $ac_file" >&6;}
5434 fi
5435 # Neutralize special characters interpreted by sed in replacement strings.
5436 case $configure_input in #(
5437 *\&* | *\|* | *\\* )
5438 ac_sed_conf_input=`$as_echo "$configure_input" |
5439 sed 's/[\\\\&|]/\\\\&/g'`;; #(
5440 *) ac_sed_conf_input=$configure_input;;
5441 esac
5442
5443 case $ac_tag in
5444 *:-:* | *:-) cat >"$ac_tmp/stdin" \
5445 || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
5446 esac
5447 ;;
5448 esac
5449
5450 ac_dir=`$as_dirname -- "$ac_file" ||
5451$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
5452 X"$ac_file" : 'X\(//\)[^/]' \| \
5453 X"$ac_file" : 'X\(//\)$' \| \
5454 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
5455$as_echo X"$ac_file" |
5456 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
5457 s//\1/
5458 q
5459 }
5460 /^X\(\/\/\)[^/].*/{
5461 s//\1/
5462 q
5463 }
5464 /^X\(\/\/\)$/{
5465 s//\1/
5466 q
5467 }
5468 /^X\(\/\).*/{
5469 s//\1/
5470 q
5471 }
5472 s/.*/./; q'`
5473 as_dir="$ac_dir"; as_fn_mkdir_p
5474 ac_builddir=.
5475
5476case "$ac_dir" in
5477.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
5478*)
5479 ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
5480 # A ".." for each directory in $ac_dir_suffix.
5481 ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
5482 case $ac_top_builddir_sub in
5483 "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
5484 *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
5485 esac ;;
5486esac
5487ac_abs_top_builddir=$ac_pwd
5488ac_abs_builddir=$ac_pwd$ac_dir_suffix
5489# for backward compatibility:
5490ac_top_builddir=$ac_top_build_prefix
5491
5492case $srcdir in
5493 .) # We are building in place.
5494 ac_srcdir=.
5495 ac_top_srcdir=$ac_top_builddir_sub
5496 ac_abs_top_srcdir=$ac_pwd ;;
5497 [\\/]* | ?:[\\/]* ) # Absolute name.
5498 ac_srcdir=$srcdir$ac_dir_suffix;
5499 ac_top_srcdir=$srcdir
5500 ac_abs_top_srcdir=$srcdir ;;
5501 *) # Relative name.
5502 ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
5503 ac_top_srcdir=$ac_top_build_prefix$srcdir
5504 ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
5505esac
5506ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
5507
5508
5509 case $ac_mode in
5510 :F)
5511 #
5512 # CONFIG_FILE
5513 #
5514
5515 case $INSTALL in
5516 [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
5517 *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
5518 esac
5519 ac_MKDIR_P=$MKDIR_P
5520 case $MKDIR_P in
5521 [\\/$]* | ?:[\\/]* ) ;;
5522 */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
5523 esac
5524_ACEOF
5525
5526cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5527# If the template does not know about datarootdir, expand it.
5528# FIXME: This hack should be removed a few years after 2.60.
5529ac_datarootdir_hack=; ac_datarootdir_seen=
5530ac_sed_dataroot='
5531/datarootdir/ {
5532 p
5533 q
5534}
5535/@datadir@/p
5536/@docdir@/p
5537/@infodir@/p
5538/@localedir@/p
5539/@mandir@/p'
5540case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
5541*datarootdir*) ac_datarootdir_seen=yes;;
5542*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
5543 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
5544$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
5545_ACEOF
5546cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5547 ac_datarootdir_hack='
5548 s&@datadir@&$datadir&g
5549 s&@docdir@&$docdir&g
5550 s&@infodir@&$infodir&g
5551 s&@localedir@&$localedir&g
5552 s&@mandir@&$mandir&g
5553 s&\\\${datarootdir}&$datarootdir&g' ;;
5554esac
5555_ACEOF
5556
5557# Neutralize VPATH when `$srcdir' = `.'.
5558# Shell code in configure.ac might set extrasub.
5559# FIXME: do we really want to maintain this feature?
5560cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
5561ac_sed_extra="$ac_vpsub
5562$extrasub
5563_ACEOF
5564cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
5565:t
5566/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
5567s|@configure_input@|$ac_sed_conf_input|;t t
5568s&@top_builddir@&$ac_top_builddir_sub&;t t
5569s&@top_build_prefix@&$ac_top_build_prefix&;t t
5570s&@srcdir@&$ac_srcdir&;t t
5571s&@abs_srcdir@&$ac_abs_srcdir&;t t
5572s&@top_srcdir@&$ac_top_srcdir&;t t
5573s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
5574s&@builddir@&$ac_builddir&;t t
5575s&@abs_builddir@&$ac_abs_builddir&;t t
5576s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
5577s&@INSTALL@&$ac_INSTALL&;t t
5578s&@MKDIR_P@&$ac_MKDIR_P&;t t
5579$ac_datarootdir_hack
5580"
5581eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
5582 >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
5583
5584test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
5585 { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
5586 { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
5587 "$ac_tmp/out"`; test -z "$ac_out"; } &&
5588 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
5589which seems to be undefined. Please make sure it is defined" >&5
5590$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
5591which seems to be undefined. Please make sure it is defined" >&2;}
5592
5593 rm -f "$ac_tmp/stdin"
5594 case $ac_file in
5595 -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
5596 *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
5597 esac \
5598 || as_fn_error $? "could not create $ac_file" "$LINENO" 5
5599 ;;
5600
5601
5602 :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
5603$as_echo "$as_me: executing $ac_file commands" >&6;}
5604 ;;
5605 esac
5606
5607
5608 case $ac_file$ac_mode in
5609 "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
5610 # Older Autoconf quotes --file arguments for eval, but not when files
5611 # are listed without --file. Let's play safe and only enable the eval
5612 # if we detect the quoting.
5613 case $CONFIG_FILES in
5614 *\'*) eval set x "$CONFIG_FILES" ;;
5615 *) set x $CONFIG_FILES ;;
5616 esac
5617 shift
5618 for mf
5619 do
5620 # Strip MF so we end up with the name of the file.
5621 mf=`echo "$mf" | sed -e 's/:.*$//'`
5622 # Check whether this is an Automake generated Makefile or not.
5623 # We used to match only the files named 'Makefile.in', but
5624 # some people rename them; so instead we look at the file content.
5625 # Grep'ing the first line is not enough: some people post-process
5626 # each Makefile.in and add a new line on top of each file to say so.
5627 # Grep'ing the whole file is not good either: AIX grep has a line
5628 # limit of 2048, but all sed's we know have understand at least 4000.
5629 if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
5630 dirpart=`$as_dirname -- "$mf" ||
5631$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
5632 X"$mf" : 'X\(//\)[^/]' \| \
5633 X"$mf" : 'X\(//\)$' \| \
5634 X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
5635$as_echo X"$mf" |
5636 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
5637 s//\1/
5638 q
5639 }
5640 /^X\(\/\/\)[^/].*/{
5641 s//\1/
5642 q
5643 }
5644 /^X\(\/\/\)$/{
5645 s//\1/
5646 q
5647 }
5648 /^X\(\/\).*/{
5649 s//\1/
5650 q
5651 }
5652 s/.*/./; q'`
5653 else
5654 continue
5655 fi
5656 # Extract the definition of DEPDIR, am__include, and am__quote
5657 # from the Makefile without running 'make'.
5658 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
5659 test -z "$DEPDIR" && continue
5660 am__include=`sed -n 's/^am__include = //p' < "$mf"`
5661 test -z "$am__include" && continue
5662 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
5663 # Find all dependency output files, they are included files with
5664 # $(DEPDIR) in their names. We invoke sed twice because it is the
5665 # simplest approach to changing $(DEPDIR) to its actual value in the
5666 # expansion.
5667 for file in `sed -n "
5668 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
5669 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
5670 # Make sure the directory exists.
5671 test -f "$dirpart/$file" && continue
5672 fdir=`$as_dirname -- "$file" ||
5673$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
5674 X"$file" : 'X\(//\)[^/]' \| \
5675 X"$file" : 'X\(//\)$' \| \
5676 X"$file" : 'X\(/\)' \| . 2>/dev/null ||
5677$as_echo X"$file" |
5678 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
5679 s//\1/
5680 q
5681 }
5682 /^X\(\/\/\)[^/].*/{
5683 s//\1/
5684 q
5685 }
5686 /^X\(\/\/\)$/{
5687 s//\1/
5688 q
5689 }
5690 /^X\(\/\).*/{
5691 s//\1/
5692 q
5693 }
5694 s/.*/./; q'`
5695 as_dir=$dirpart/$fdir; as_fn_mkdir_p
5696 # echo "creating $dirpart/$file"
5697 echo '# dummy' > "$dirpart/$file"
5698 done
5699 done
5700}
5701 ;;
5702
5703 esac
5704done # for ac_tag
5705
5706
5707as_fn_exit 0
5708_ACEOF
5709ac_clean_files=$ac_clean_files_save
5710
5711test $ac_write_fail = 0 ||
5712 as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
5713
5714
5715# configure is writing to config.log, and then calls config.status.
5716# config.status does its own redirection, appending to config.log.
5717# Unfortunately, on DOS this fails, as config.log is still kept open
5718# by configure, so config.status won't be able to write to it; its
5719# output is simply discarded. So we exec the FD to /dev/null,
5720# effectively closing config.log, so it can be properly (re)opened and
5721# appended to by config.status. When coming back to configure, we
5722# need to make the FD available again.
5723if test "$no_create" != yes; then
5724 ac_cs_success=:
5725 ac_config_status_args=
5726 test "$silent" = yes &&
5727 ac_config_status_args="$ac_config_status_args --quiet"
5728 exec 5>/dev/null
5729 $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
5730 exec 5>>config.log
5731 # Use ||, not &&, to avoid exiting from the if with $? = 1, which
5732 # would make configure fail if this is the last instruction.
5733 $ac_cs_success || as_fn_exit 1
5734fi
5735if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
5736 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
5737$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
5738fi
5739
diff --git a/apps/plugins/puzzles/src/configure.ac b/apps/plugins/puzzles/src/configure.ac
new file mode 100644
index 0000000000..3a38c95602
--- /dev/null
+++ b/apps/plugins/puzzles/src/configure.ac
@@ -0,0 +1,85 @@
1dnl Configure script for the Unix GTK build of puzzles.
2
3AC_INIT([puzzles], [6.66], [anakin@pobox.com])
4AC_CONFIG_SRCDIR([midend.c])
5AM_INIT_AUTOMAKE([foreign])
6AC_PROG_CC
7
8AC_ARG_WITH([gtk],
9 [AS_HELP_STRING([--with-gtk=VER],
10 [specify GTK version to use (`2' or `3')])],
11 [gtk_version_desired="$withval"],
12 [gtk_version_desired="any"])
13
14case "$gtk_version_desired" in
15 2 | 3 | any) ;;
16 yes) gtk_version_desired="any" ;;
17 *) AC_ERROR([Invalid GTK version specified])
18esac
19
20gtk=none
21
22case "$gtk_version_desired:$gtk" in
23 3:none | any:none)
24 ifdef([AM_PATH_GTK_3_0],[
25 AM_PATH_GTK_3_0([3.0.0], [gtk=3], [])
26 ],[AC_WARNING([generating configure script without GTK 3 autodetection])])
27 ;;
28esac
29
30case "$gtk_version_desired:$gtk" in
31 2:none | any:none)
32 ifdef([AM_PATH_GTK_2_0],[
33 AM_PATH_GTK_2_0([2.0.0], [gtk=2], [])
34 ],[AC_WARNING([generating configure script without GTK 2 autodetection])])
35 ;;
36esac
37
38if test "$gtk" = "none"; then
39 AC_MSG_ERROR([cannot build without GTK 2 or GTK 3])
40fi
41
42if test "x$GCC" = "xyes"; then
43 AC_MSG_CHECKING([for usable gcc warning flags])
44 gccwarningflags=
45 for flag in -Wall -Werror -std=c89 -pedantic; do
46 ac_save_CFLAGS="$CFLAGS"
47 ac_save_LIBS="$LIBS"
48 CFLAGS="$CFLAGS$gccwarningflags $flag $GTK_CFLAGS"
49 LIBS="$GTK_LIBS $LIBS"
50 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
51 #include <stdio.h>
52 #include <assert.h>
53 #include <stdlib.h>
54 #include <time.h>
55 #include <stdarg.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <math.h>
59
60 #include <sys/time.h>
61 #include <sys/resource.h>
62
63 #include <gtk/gtk.h>
64 #include <gdk/gdkkeysyms.h>
65
66 #include <gdk-pixbuf/gdk-pixbuf.h>
67
68 #include <gdk/gdkx.h>
69 #include <X11/Xlib.h>
70 #include <X11/Xutil.h>
71 #include <X11/Xatom.h>
72 ],[
73 return 0;
74 ])], [gccwarningflags="$gccwarningflags $flag"], [])
75 CFLAGS="$ac_save_CFLAGS"
76 LIBS="$ac_save_LIBS"
77 done
78 AC_MSG_RESULT($gccwarningflags)
79 CFLAGS="$CFLAGS$gccwarningflags"
80fi
81
82AC_PROG_RANLIB
83AC_PROG_INSTALL
84AC_CONFIG_FILES([Makefile])
85AC_OUTPUT
diff --git a/apps/plugins/puzzles/src/cube.R b/apps/plugins/puzzles/src/cube.R
new file mode 100644
index 0000000000..85b081ec76
--- /dev/null
+++ b/apps/plugins/puzzles/src/cube.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3cube : [X] GTK COMMON cube cube-icon|no-icon
4
5cube : [G] WINDOWS COMMON cube cube.res|noicon.res
6
7ALL += cube[COMBINED]
8
9!begin am gtk
10GAMES += cube
11!end
12
13!begin >list.c
14 A(cube) \
15!end
16
17!begin >gamedesc.txt
18cube:cube.exe:Cube:Rolling cube puzzle:Pick up all the blue squares by rolling the cube over them.
19!end
diff --git a/apps/plugins/puzzles/src/cube.c b/apps/plugins/puzzles/src/cube.c
new file mode 100644
index 0000000000..a30dc10b3f
--- /dev/null
+++ b/apps/plugins/puzzles/src/cube.c
@@ -0,0 +1,1773 @@
1/*
2 * cube.c: Cube game.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13
14#define MAXVERTICES 20
15#define MAXFACES 20
16#define MAXORDER 4
17struct solid {
18 int nvertices;
19 float vertices[MAXVERTICES * 3]; /* 3*npoints coordinates */
20 int order;
21 int nfaces;
22 int faces[MAXFACES * MAXORDER]; /* order*nfaces point indices */
23 float normals[MAXFACES * 3]; /* 3*npoints vector components */
24 float shear; /* isometric shear for nice drawing */
25 float border; /* border required around arena */
26};
27
28static const struct solid s_tetrahedron = {
29 4,
30 {
31 0.0F, -0.57735026919F, -0.20412414523F,
32 -0.5F, 0.28867513459F, -0.20412414523F,
33 0.0F, -0.0F, 0.6123724357F,
34 0.5F, 0.28867513459F, -0.20412414523F,
35 },
36 3, 4,
37 {
38 0,2,1, 3,1,2, 2,0,3, 1,3,0
39 },
40 {
41 -0.816496580928F, -0.471404520791F, 0.333333333334F,
42 0.0F, 0.942809041583F, 0.333333333333F,
43 0.816496580928F, -0.471404520791F, 0.333333333334F,
44 0.0F, 0.0F, -1.0F,
45 },
46 0.0F, 0.3F
47};
48
49static const struct solid s_cube = {
50 8,
51 {
52 -0.5F,-0.5F,-0.5F, -0.5F,-0.5F,+0.5F,
53 -0.5F,+0.5F,-0.5F, -0.5F,+0.5F,+0.5F,
54 +0.5F,-0.5F,-0.5F, +0.5F,-0.5F,+0.5F,
55 +0.5F,+0.5F,-0.5F, +0.5F,+0.5F,+0.5F,
56 },
57 4, 6,
58 {
59 0,1,3,2, 1,5,7,3, 5,4,6,7, 4,0,2,6, 0,4,5,1, 3,7,6,2
60 },
61 {
62 -1.0F,0.0F,0.0F, 0.0F,0.0F,+1.0F,
63 +1.0F,0.0F,0.0F, 0.0F,0.0F,-1.0F,
64 0.0F,-1.0F,0.0F, 0.0F,+1.0F,0.0F
65 },
66 0.3F, 0.5F
67};
68
69static const struct solid s_octahedron = {
70 6,
71 {
72 -0.5F, -0.28867513459472505F, 0.4082482904638664F,
73 0.5F, 0.28867513459472505F, -0.4082482904638664F,
74 -0.5F, 0.28867513459472505F, -0.4082482904638664F,
75 0.5F, -0.28867513459472505F, 0.4082482904638664F,
76 0.0F, -0.57735026918945009F, -0.4082482904638664F,
77 0.0F, 0.57735026918945009F, 0.4082482904638664F,
78 },
79 3, 8,
80 {
81 4,0,2, 0,5,2, 0,4,3, 5,0,3, 1,4,2, 5,1,2, 4,1,3, 1,5,3
82 },
83 {
84 -0.816496580928F, -0.471404520791F, -0.333333333334F,
85 -0.816496580928F, 0.471404520791F, 0.333333333334F,
86 0.0F, -0.942809041583F, 0.333333333333F,
87 0.0F, 0.0F, 1.0F,
88 0.0F, 0.0F, -1.0F,
89 0.0F, 0.942809041583F, -0.333333333333F,
90 0.816496580928F, -0.471404520791F, -0.333333333334F,
91 0.816496580928F, 0.471404520791F, 0.333333333334F,
92 },
93 0.0F, 0.5F
94};
95
96static const struct solid s_icosahedron = {
97 12,
98 {
99 0.0F, 0.57735026919F, 0.75576131408F,
100 0.0F, -0.93417235896F, 0.17841104489F,
101 0.0F, 0.93417235896F, -0.17841104489F,
102 0.0F, -0.57735026919F, -0.75576131408F,
103 -0.5F, -0.28867513459F, 0.75576131408F,
104 -0.5F, 0.28867513459F, -0.75576131408F,
105 0.5F, -0.28867513459F, 0.75576131408F,
106 0.5F, 0.28867513459F, -0.75576131408F,
107 -0.80901699437F, 0.46708617948F, 0.17841104489F,
108 0.80901699437F, 0.46708617948F, 0.17841104489F,
109 -0.80901699437F, -0.46708617948F, -0.17841104489F,
110 0.80901699437F, -0.46708617948F, -0.17841104489F,
111 },
112 3, 20,
113 {
114 8,0,2, 0,9,2, 1,10,3, 11,1,3, 0,4,6,
115 4,1,6, 5,2,7, 3,5,7, 4,8,10, 8,5,10,
116 9,6,11, 7,9,11, 0,8,4, 9,0,6, 10,1,4,
117 1,11,6, 8,2,5, 2,9,7, 3,10,5, 11,3,7,
118 },
119 {
120 -0.356822089773F, 0.87267799625F, 0.333333333333F,
121 0.356822089773F, 0.87267799625F, 0.333333333333F,
122 -0.356822089773F, -0.87267799625F, -0.333333333333F,
123 0.356822089773F, -0.87267799625F, -0.333333333333F,
124 -0.0F, 0.0F, 1.0F,
125 0.0F, -0.666666666667F, 0.745355992501F,
126 0.0F, 0.666666666667F, -0.745355992501F,
127 0.0F, 0.0F, -1.0F,
128 -0.934172358963F, -0.12732200375F, 0.333333333333F,
129 -0.934172358963F, 0.12732200375F, -0.333333333333F,
130 0.934172358963F, -0.12732200375F, 0.333333333333F,
131 0.934172358963F, 0.12732200375F, -0.333333333333F,
132 -0.57735026919F, 0.333333333334F, 0.745355992501F,
133 0.57735026919F, 0.333333333334F, 0.745355992501F,
134 -0.57735026919F, -0.745355992501F, 0.333333333334F,
135 0.57735026919F, -0.745355992501F, 0.333333333334F,
136 -0.57735026919F, 0.745355992501F, -0.333333333334F,
137 0.57735026919F, 0.745355992501F, -0.333333333334F,
138 -0.57735026919F, -0.333333333334F, -0.745355992501F,
139 0.57735026919F, -0.333333333334F, -0.745355992501F,
140 },
141 0.0F, 0.8F
142};
143
144enum {
145 TETRAHEDRON, CUBE, OCTAHEDRON, ICOSAHEDRON
146};
147static const struct solid *solids[] = {
148 &s_tetrahedron, &s_cube, &s_octahedron, &s_icosahedron
149};
150
151enum {
152 COL_BACKGROUND,
153 COL_BORDER,
154 COL_BLUE,
155 NCOLOURS
156};
157
158enum { LEFT, RIGHT, UP, DOWN, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT };
159
160#define PREFERRED_GRID_SCALE 48
161#define GRID_SCALE (ds->gridscale)
162#define ROLLTIME 0.13F
163
164#define SQ(x) ( (x) * (x) )
165
166#define MATMUL(ra,m,a) do { \
167 float rx, ry, rz, xx = (a)[0], yy = (a)[1], zz = (a)[2], *mat = (m); \
168 rx = mat[0] * xx + mat[3] * yy + mat[6] * zz; \
169 ry = mat[1] * xx + mat[4] * yy + mat[7] * zz; \
170 rz = mat[2] * xx + mat[5] * yy + mat[8] * zz; \
171 (ra)[0] = rx; (ra)[1] = ry; (ra)[2] = rz; \
172} while (0)
173
174#define APPROXEQ(x,y) ( SQ(x-y) < 0.1 )
175
176struct grid_square {
177 float x, y;
178 int npoints;
179 float points[8]; /* maximum */
180 int directions[8]; /* bit masks showing point pairs */
181 int flip;
182 int tetra_class;
183};
184
185struct game_params {
186 int solid;
187 /*
188 * Grid dimensions. For a square grid these are width and
189 * height respectively; otherwise the grid is a hexagon, with
190 * the top side and the two lower diagonals having length d1
191 * and the remaining three sides having length d2 (so that
192 * d1==d2 gives a regular hexagon, and d2==0 gives a triangle).
193 */
194 int d1, d2;
195};
196
197typedef struct game_grid game_grid;
198struct game_grid {
199 int refcount;
200 struct grid_square *squares;
201 int nsquares;
202};
203
204#define SET_SQUARE(state, i, val) \
205 ((state)->bluemask[(i)/32] &= ~(1 << ((i)%32)), \
206 (state)->bluemask[(i)/32] |= ((!!val) << ((i)%32)))
207#define GET_SQUARE(state, i) \
208 (((state)->bluemask[(i)/32] >> ((i)%32)) & 1)
209
210struct game_state {
211 struct game_params params;
212 const struct solid *solid;
213 int *facecolours;
214 game_grid *grid;
215 unsigned long *bluemask;
216 int current; /* index of current grid square */
217 int sgkey[2]; /* key-point indices into grid sq */
218 int dgkey[2]; /* key-point indices into grid sq */
219 int spkey[2]; /* key-point indices into polyhedron */
220 int dpkey[2]; /* key-point indices into polyhedron */
221 int previous;
222 float angle;
223 int completed;
224 int movecount;
225};
226
227static game_params *default_params(void)
228{
229 game_params *ret = snew(game_params);
230
231 ret->solid = CUBE;
232 ret->d1 = 4;
233 ret->d2 = 4;
234
235 return ret;
236}
237
238static int game_fetch_preset(int i, char **name, game_params **params)
239{
240 game_params *ret = snew(game_params);
241 char *str;
242
243 switch (i) {
244 case 0:
245 str = "Cube";
246 ret->solid = CUBE;
247 ret->d1 = 4;
248 ret->d2 = 4;
249 break;
250 case 1:
251 str = "Tetrahedron";
252 ret->solid = TETRAHEDRON;
253 ret->d1 = 1;
254 ret->d2 = 2;
255 break;
256 case 2:
257 str = "Octahedron";
258 ret->solid = OCTAHEDRON;
259 ret->d1 = 2;
260 ret->d2 = 2;
261 break;
262 case 3:
263 str = "Icosahedron";
264 ret->solid = ICOSAHEDRON;
265 ret->d1 = 3;
266 ret->d2 = 3;
267 break;
268 default:
269 sfree(ret);
270 return FALSE;
271 }
272
273 *name = dupstr(str);
274 *params = ret;
275 return TRUE;
276}
277
278static void free_params(game_params *params)
279{
280 sfree(params);
281}
282
283static game_params *dup_params(const game_params *params)
284{
285 game_params *ret = snew(game_params);
286 *ret = *params; /* structure copy */
287 return ret;
288}
289
290static void decode_params(game_params *ret, char const *string)
291{
292 switch (*string) {
293 case 't': ret->solid = TETRAHEDRON; string++; break;
294 case 'c': ret->solid = CUBE; string++; break;
295 case 'o': ret->solid = OCTAHEDRON; string++; break;
296 case 'i': ret->solid = ICOSAHEDRON; string++; break;
297 default: break;
298 }
299 ret->d1 = ret->d2 = atoi(string);
300 while (*string && isdigit((unsigned char)*string)) string++;
301 if (*string == 'x') {
302 string++;
303 ret->d2 = atoi(string);
304 }
305}
306
307static char *encode_params(const game_params *params, int full)
308{
309 char data[256];
310
311 assert(params->solid >= 0 && params->solid < 4);
312 sprintf(data, "%c%dx%d", "tcoi"[params->solid], params->d1, params->d2);
313
314 return dupstr(data);
315}
316typedef void (*egc_callback)(void *, struct grid_square *);
317
318static void enum_grid_squares(const game_params *params, egc_callback callback,
319 void *ctx)
320{
321 const struct solid *solid = solids[params->solid];
322
323 if (solid->order == 4) {
324 int x, y;
325
326 for (y = 0; y < params->d2; y++)
327 for (x = 0; x < params->d1; x++) {
328 struct grid_square sq;
329
330 sq.x = (float)x;
331 sq.y = (float)y;
332 sq.points[0] = x - 0.5F;
333 sq.points[1] = y - 0.5F;
334 sq.points[2] = x - 0.5F;
335 sq.points[3] = y + 0.5F;
336 sq.points[4] = x + 0.5F;
337 sq.points[5] = y + 0.5F;
338 sq.points[6] = x + 0.5F;
339 sq.points[7] = y - 0.5F;
340 sq.npoints = 4;
341
342 sq.directions[LEFT] = 0x03; /* 0,1 */
343 sq.directions[RIGHT] = 0x0C; /* 2,3 */
344 sq.directions[UP] = 0x09; /* 0,3 */
345 sq.directions[DOWN] = 0x06; /* 1,2 */
346 sq.directions[UP_LEFT] = 0; /* no diagonals in a square */
347 sq.directions[UP_RIGHT] = 0; /* no diagonals in a square */
348 sq.directions[DOWN_LEFT] = 0; /* no diagonals in a square */
349 sq.directions[DOWN_RIGHT] = 0; /* no diagonals in a square */
350
351 sq.flip = FALSE;
352
353 /*
354 * This is supremely irrelevant, but just to avoid
355 * having any uninitialised structure members...
356 */
357 sq.tetra_class = 0;
358
359 callback(ctx, &sq);
360 }
361 } else {
362 int row, rowlen, other, i, firstix = -1;
363 float theight = (float)(sqrt(3) / 2.0);
364
365 for (row = 0; row < params->d1 + params->d2; row++) {
366 if (row < params->d2) {
367 other = +1;
368 rowlen = row + params->d1;
369 } else {
370 other = -1;
371 rowlen = 2*params->d2 + params->d1 - row;
372 }
373
374 /*
375 * There are `rowlen' down-pointing triangles.
376 */
377 for (i = 0; i < rowlen; i++) {
378 struct grid_square sq;
379 int ix;
380 float x, y;
381
382 ix = (2 * i - (rowlen-1));
383 x = ix * 0.5F;
384 y = theight * row;
385 sq.x = x;
386 sq.y = y + theight / 3;
387 sq.points[0] = x - 0.5F;
388 sq.points[1] = y;
389 sq.points[2] = x;
390 sq.points[3] = y + theight;
391 sq.points[4] = x + 0.5F;
392 sq.points[5] = y;
393 sq.npoints = 3;
394
395 sq.directions[LEFT] = 0x03; /* 0,1 */
396 sq.directions[RIGHT] = 0x06; /* 1,2 */
397 sq.directions[UP] = 0x05; /* 0,2 */
398 sq.directions[DOWN] = 0; /* invalid move */
399
400 /*
401 * Down-pointing triangle: both the up diagonals go
402 * up, and the down ones go left and right.
403 */
404 sq.directions[UP_LEFT] = sq.directions[UP_RIGHT] =
405 sq.directions[UP];
406 sq.directions[DOWN_LEFT] = sq.directions[LEFT];
407 sq.directions[DOWN_RIGHT] = sq.directions[RIGHT];
408
409 sq.flip = TRUE;
410
411 if (firstix < 0)
412 firstix = ix & 3;
413 ix -= firstix;
414 sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3);
415
416 callback(ctx, &sq);
417 }
418
419 /*
420 * There are `rowlen+other' up-pointing triangles.
421 */
422 for (i = 0; i < rowlen+other; i++) {
423 struct grid_square sq;
424 int ix;
425 float x, y;
426
427 ix = (2 * i - (rowlen+other-1));
428 x = ix * 0.5F;
429 y = theight * row;
430 sq.x = x;
431 sq.y = y + 2*theight / 3;
432 sq.points[0] = x + 0.5F;
433 sq.points[1] = y + theight;
434 sq.points[2] = x;
435 sq.points[3] = y;
436 sq.points[4] = x - 0.5F;
437 sq.points[5] = y + theight;
438 sq.npoints = 3;
439
440 sq.directions[LEFT] = 0x06; /* 1,2 */
441 sq.directions[RIGHT] = 0x03; /* 0,1 */
442 sq.directions[DOWN] = 0x05; /* 0,2 */
443 sq.directions[UP] = 0; /* invalid move */
444
445 /*
446 * Up-pointing triangle: both the down diagonals go
447 * down, and the up ones go left and right.
448 */
449 sq.directions[DOWN_LEFT] = sq.directions[DOWN_RIGHT] =
450 sq.directions[DOWN];
451 sq.directions[UP_LEFT] = sq.directions[LEFT];
452 sq.directions[UP_RIGHT] = sq.directions[RIGHT];
453
454 sq.flip = FALSE;
455
456 if (firstix < 0)
457 firstix = (ix - 1) & 3;
458 ix -= firstix;
459 sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3);
460
461 callback(ctx, &sq);
462 }
463 }
464 }
465}
466
467static int grid_area(int d1, int d2, int order)
468{
469 /*
470 * An NxM grid of squares has NM squares in it.
471 *
472 * A grid of triangles with dimensions A and B has a total of
473 * A^2 + B^2 + 4AB triangles in it. (You can divide it up into
474 * a side-A triangle containing A^2 subtriangles, a side-B
475 * triangle containing B^2, and two congruent parallelograms,
476 * each with side lengths A and B, each therefore containing AB
477 * two-triangle rhombuses.)
478 */
479 if (order == 4)
480 return d1 * d2;
481 else
482 return d1*d1 + d2*d2 + 4*d1*d2;
483}
484
485static config_item *game_configure(const game_params *params)
486{
487 config_item *ret = snewn(4, config_item);
488 char buf[80];
489
490 ret[0].name = "Type of solid";
491 ret[0].type = C_CHOICES;
492 ret[0].sval = ":Tetrahedron:Cube:Octahedron:Icosahedron";
493 ret[0].ival = params->solid;
494
495 ret[1].name = "Width / top";
496 ret[1].type = C_STRING;
497 sprintf(buf, "%d", params->d1);
498 ret[1].sval = dupstr(buf);
499 ret[1].ival = 0;
500
501 ret[2].name = "Height / bottom";
502 ret[2].type = C_STRING;
503 sprintf(buf, "%d", params->d2);
504 ret[2].sval = dupstr(buf);
505 ret[2].ival = 0;
506
507 ret[3].name = NULL;
508 ret[3].type = C_END;
509 ret[3].sval = NULL;
510 ret[3].ival = 0;
511
512 return ret;
513}
514
515static game_params *custom_params(const config_item *cfg)
516{
517 game_params *ret = snew(game_params);
518
519 ret->solid = cfg[0].ival;
520 ret->d1 = atoi(cfg[1].sval);
521 ret->d2 = atoi(cfg[2].sval);
522
523 return ret;
524}
525
526static void count_grid_square_callback(void *ctx, struct grid_square *sq)
527{
528 int *classes = (int *)ctx;
529 int thisclass;
530
531 if (classes[4] == 4)
532 thisclass = sq->tetra_class;
533 else if (classes[4] == 2)
534 thisclass = sq->flip;
535 else
536 thisclass = 0;
537
538 classes[thisclass]++;
539}
540
541static char *validate_params(const game_params *params, int full)
542{
543 int classes[5];
544 int i;
545
546 if (params->solid < 0 || params->solid >= lenof(solids))
547 return "Unrecognised solid type";
548
549 if (solids[params->solid]->order == 4) {
550 if (params->d1 <= 0 || params->d2 <= 0)
551 return "Both grid dimensions must be greater than zero";
552 } else {
553 if (params->d1 <= 0 && params->d2 <= 0)
554 return "At least one grid dimension must be greater than zero";
555 }
556
557 for (i = 0; i < 4; i++)
558 classes[i] = 0;
559 if (params->solid == TETRAHEDRON)
560 classes[4] = 4;
561 else if (params->solid == OCTAHEDRON)
562 classes[4] = 2;
563 else
564 classes[4] = 1;
565 enum_grid_squares(params, count_grid_square_callback, classes);
566
567 for (i = 0; i < classes[4]; i++)
568 if (classes[i] < solids[params->solid]->nfaces / classes[4])
569 return "Not enough grid space to place all blue faces";
570
571 if (grid_area(params->d1, params->d2, solids[params->solid]->order) <
572 solids[params->solid]->nfaces + 1)
573 return "Not enough space to place the solid on an empty square";
574
575 return NULL;
576}
577
578struct grid_data {
579 int *gridptrs[4];
580 int nsquares[4];
581 int nclasses;
582 int squareindex;
583};
584
585static void classify_grid_square_callback(void *ctx, struct grid_square *sq)
586{
587 struct grid_data *data = (struct grid_data *)ctx;
588 int thisclass;
589
590 if (data->nclasses == 4)
591 thisclass = sq->tetra_class;
592 else if (data->nclasses == 2)
593 thisclass = sq->flip;
594 else
595 thisclass = 0;
596
597 data->gridptrs[thisclass][data->nsquares[thisclass]++] =
598 data->squareindex++;
599}
600
601static char *new_game_desc(const game_params *params, random_state *rs,
602 char **aux, int interactive)
603{
604 struct grid_data data;
605 int i, j, k, m, area, facesperclass;
606 int *flags;
607 char *desc, *p;
608
609 /*
610 * Enumerate the grid squares, dividing them into equivalence
611 * classes as appropriate. (For the tetrahedron, there is one
612 * equivalence class for each face; for the octahedron there
613 * are two classes; for the other two solids there's only one.)
614 */
615
616 area = grid_area(params->d1, params->d2, solids[params->solid]->order);
617 if (params->solid == TETRAHEDRON)
618 data.nclasses = 4;
619 else if (params->solid == OCTAHEDRON)
620 data.nclasses = 2;
621 else
622 data.nclasses = 1;
623 data.gridptrs[0] = snewn(data.nclasses * area, int);
624 for (i = 0; i < data.nclasses; i++) {
625 data.gridptrs[i] = data.gridptrs[0] + i * area;
626 data.nsquares[i] = 0;
627 }
628 data.squareindex = 0;
629 enum_grid_squares(params, classify_grid_square_callback, &data);
630
631 facesperclass = solids[params->solid]->nfaces / data.nclasses;
632
633 for (i = 0; i < data.nclasses; i++)
634 assert(data.nsquares[i] >= facesperclass);
635 assert(data.squareindex == area);
636
637 /*
638 * So now we know how many faces to allocate in each class. Get
639 * on with it.
640 */
641 flags = snewn(area, int);
642 for (i = 0; i < area; i++)
643 flags[i] = FALSE;
644
645 for (i = 0; i < data.nclasses; i++) {
646 for (j = 0; j < facesperclass; j++) {
647 int n = random_upto(rs, data.nsquares[i]);
648
649 assert(!flags[data.gridptrs[i][n]]);
650 flags[data.gridptrs[i][n]] = TRUE;
651
652 /*
653 * Move everything else up the array. I ought to use a
654 * better data structure for this, but for such small
655 * numbers it hardly seems worth the effort.
656 */
657 while (n < data.nsquares[i]-1) {
658 data.gridptrs[i][n] = data.gridptrs[i][n+1];
659 n++;
660 }
661 data.nsquares[i]--;
662 }
663 }
664
665 /*
666 * Now we know precisely which squares are blue. Encode this
667 * information in hex. While we're looping over this, collect
668 * the non-blue squares into a list in the now-unused gridptrs
669 * array.
670 */
671 desc = snewn(area / 4 + 40, char);
672 p = desc;
673 j = 0;
674 k = 8;
675 m = 0;
676 for (i = 0; i < area; i++) {
677 if (flags[i]) {
678 j |= k;
679 } else {
680 data.gridptrs[0][m++] = i;
681 }
682 k >>= 1;
683 if (!k) {
684 *p++ = "0123456789ABCDEF"[j];
685 k = 8;
686 j = 0;
687 }
688 }
689 if (k != 8)
690 *p++ = "0123456789ABCDEF"[j];
691
692 /*
693 * Choose a non-blue square for the polyhedron.
694 */
695 sprintf(p, ",%d", data.gridptrs[0][random_upto(rs, m)]);
696
697 sfree(data.gridptrs[0]);
698 sfree(flags);
699
700 return desc;
701}
702
703static void add_grid_square_callback(void *ctx, struct grid_square *sq)
704{
705 game_grid *grid = (game_grid *)ctx;
706
707 grid->squares[grid->nsquares++] = *sq; /* structure copy */
708}
709
710static int lowest_face(const struct solid *solid)
711{
712 int i, j, best;
713 float zmin;
714
715 best = 0;
716 zmin = 0.0;
717 for (i = 0; i < solid->nfaces; i++) {
718 float z = 0;
719
720 for (j = 0; j < solid->order; j++) {
721 int f = solid->faces[i*solid->order + j];
722 z += solid->vertices[f*3+2];
723 }
724
725 if (i == 0 || zmin > z) {
726 zmin = z;
727 best = i;
728 }
729 }
730
731 return best;
732}
733
734static int align_poly(const struct solid *solid, struct grid_square *sq,
735 int *pkey)
736{
737 float zmin;
738 int i, j;
739 int flip = (sq->flip ? -1 : +1);
740
741 /*
742 * First, find the lowest z-coordinate present in the solid.
743 */
744 zmin = 0.0;
745 for (i = 0; i < solid->nvertices; i++)
746 if (zmin > solid->vertices[i*3+2])
747 zmin = solid->vertices[i*3+2];
748
749 /*
750 * Now go round the grid square. For each point in the grid
751 * square, we're looking for a point of the polyhedron with the
752 * same x- and y-coordinates (relative to the square's centre),
753 * and z-coordinate equal to zmin (near enough).
754 */
755 for (j = 0; j < sq->npoints; j++) {
756 int matches, index;
757
758 matches = 0;
759 index = -1;
760
761 for (i = 0; i < solid->nvertices; i++) {
762 float dist = 0;
763
764 dist += SQ(solid->vertices[i*3+0] * flip - sq->points[j*2+0] + sq->x);
765 dist += SQ(solid->vertices[i*3+1] * flip - sq->points[j*2+1] + sq->y);
766 dist += SQ(solid->vertices[i*3+2] - zmin);
767
768 if (dist < 0.1) {
769 matches++;
770 index = i;
771 }
772 }
773
774 if (matches != 1 || index < 0)
775 return FALSE;
776 pkey[j] = index;
777 }
778
779 return TRUE;
780}
781
782static void flip_poly(struct solid *solid, int flip)
783{
784 int i;
785
786 if (flip) {
787 for (i = 0; i < solid->nvertices; i++) {
788 solid->vertices[i*3+0] *= -1;
789 solid->vertices[i*3+1] *= -1;
790 }
791 for (i = 0; i < solid->nfaces; i++) {
792 solid->normals[i*3+0] *= -1;
793 solid->normals[i*3+1] *= -1;
794 }
795 }
796}
797
798static struct solid *transform_poly(const struct solid *solid, int flip,
799 int key0, int key1, float angle)
800{
801 struct solid *ret = snew(struct solid);
802 float vx, vy, ax, ay;
803 float vmatrix[9], amatrix[9], vmatrix2[9];
804 int i;
805
806 *ret = *solid; /* structure copy */
807
808 flip_poly(ret, flip);
809
810 /*
811 * Now rotate the polyhedron through the given angle. We must
812 * rotate about the Z-axis to bring the two vertices key0 and
813 * key1 into horizontal alignment, then rotate about the
814 * X-axis, then rotate back again.
815 */
816 vx = ret->vertices[key1*3+0] - ret->vertices[key0*3+0];
817 vy = ret->vertices[key1*3+1] - ret->vertices[key0*3+1];
818 assert(APPROXEQ(vx*vx + vy*vy, 1.0));
819
820 vmatrix[0] = vx; vmatrix[3] = vy; vmatrix[6] = 0;
821 vmatrix[1] = -vy; vmatrix[4] = vx; vmatrix[7] = 0;
822 vmatrix[2] = 0; vmatrix[5] = 0; vmatrix[8] = 1;
823
824 ax = (float)cos(angle);
825 ay = (float)sin(angle);
826
827 amatrix[0] = 1; amatrix[3] = 0; amatrix[6] = 0;
828 amatrix[1] = 0; amatrix[4] = ax; amatrix[7] = ay;
829 amatrix[2] = 0; amatrix[5] = -ay; amatrix[8] = ax;
830
831 memcpy(vmatrix2, vmatrix, sizeof(vmatrix));
832 vmatrix2[1] = vy;
833 vmatrix2[3] = -vy;
834
835 for (i = 0; i < ret->nvertices; i++) {
836 MATMUL(ret->vertices + 3*i, vmatrix, ret->vertices + 3*i);
837 MATMUL(ret->vertices + 3*i, amatrix, ret->vertices + 3*i);
838 MATMUL(ret->vertices + 3*i, vmatrix2, ret->vertices + 3*i);
839 }
840 for (i = 0; i < ret->nfaces; i++) {
841 MATMUL(ret->normals + 3*i, vmatrix, ret->normals + 3*i);
842 MATMUL(ret->normals + 3*i, amatrix, ret->normals + 3*i);
843 MATMUL(ret->normals + 3*i, vmatrix2, ret->normals + 3*i);
844 }
845
846 return ret;
847}
848
849static char *validate_desc(const game_params *params, const char *desc)
850{
851 int area = grid_area(params->d1, params->d2, solids[params->solid]->order);
852 int i, j;
853
854 i = (area + 3) / 4;
855 for (j = 0; j < i; j++) {
856 int c = desc[j];
857 if (c >= '0' && c <= '9') continue;
858 if (c >= 'A' && c <= 'F') continue;
859 if (c >= 'a' && c <= 'f') continue;
860 return "Not enough hex digits at start of string";
861 /* NB if desc[j]=='\0' that will also be caught here, so we're safe */
862 }
863
864 if (desc[i] != ',')
865 return "Expected ',' after hex digits";
866
867 i++;
868 do {
869 if (desc[i] < '0' || desc[i] > '9')
870 return "Expected decimal integer after ','";
871 i++;
872 } while (desc[i]);
873
874 return NULL;
875}
876
877static game_state *new_game(midend *me, const game_params *params,
878 const char *desc)
879{
880 game_grid *grid = snew(game_grid);
881 game_state *state = snew(game_state);
882 int area;
883
884 state->params = *params; /* structure copy */
885 state->solid = solids[params->solid];
886
887 area = grid_area(params->d1, params->d2, state->solid->order);
888 grid->squares = snewn(area, struct grid_square);
889 grid->nsquares = 0;
890 enum_grid_squares(params, add_grid_square_callback, grid);
891 assert(grid->nsquares == area);
892 state->grid = grid;
893 grid->refcount = 1;
894
895 state->facecolours = snewn(state->solid->nfaces, int);
896 memset(state->facecolours, 0, state->solid->nfaces * sizeof(int));
897
898 state->bluemask = snewn((state->grid->nsquares + 31) / 32, unsigned long);
899 memset(state->bluemask, 0, (state->grid->nsquares + 31) / 32 *
900 sizeof(unsigned long));
901
902 /*
903 * Set up the blue squares and polyhedron position according to
904 * the game description.
905 */
906 {
907 const char *p = desc;
908 int i, j, v;
909
910 j = 8;
911 v = 0;
912 for (i = 0; i < state->grid->nsquares; i++) {
913 if (j == 8) {
914 v = *p++;
915 if (v >= '0' && v <= '9')
916 v -= '0';
917 else if (v >= 'A' && v <= 'F')
918 v -= 'A' - 10;
919 else if (v >= 'a' && v <= 'f')
920 v -= 'a' - 10;
921 else
922 break;
923 }
924 if (v & j)
925 SET_SQUARE(state, i, TRUE);
926 j >>= 1;
927 if (j == 0)
928 j = 8;
929 }
930
931 if (*p == ',')
932 p++;
933
934 state->current = atoi(p);
935 if (state->current < 0 || state->current >= state->grid->nsquares)
936 state->current = 0; /* got to do _something_ */
937 }
938
939 /*
940 * Align the polyhedron with its grid square and determine
941 * initial key points.
942 */
943 {
944 int pkey[4];
945 int ret;
946
947 ret = align_poly(state->solid, &state->grid->squares[state->current], pkey);
948 assert(ret);
949
950 state->dpkey[0] = state->spkey[0] = pkey[0];
951 state->dpkey[1] = state->spkey[0] = pkey[1];
952 state->dgkey[0] = state->sgkey[0] = 0;
953 state->dgkey[1] = state->sgkey[0] = 1;
954 }
955
956 state->previous = state->current;
957 state->angle = 0.0;
958 state->completed = 0;
959 state->movecount = 0;
960
961 return state;
962}
963
964static game_state *dup_game(const game_state *state)
965{
966 game_state *ret = snew(game_state);
967
968 ret->params = state->params; /* structure copy */
969 ret->solid = state->solid;
970 ret->facecolours = snewn(ret->solid->nfaces, int);
971 memcpy(ret->facecolours, state->facecolours,
972 ret->solid->nfaces * sizeof(int));
973 ret->current = state->current;
974 ret->grid = state->grid;
975 ret->grid->refcount++;
976 ret->bluemask = snewn((ret->grid->nsquares + 31) / 32, unsigned long);
977 memcpy(ret->bluemask, state->bluemask, (ret->grid->nsquares + 31) / 32 *
978 sizeof(unsigned long));
979 ret->dpkey[0] = state->dpkey[0];
980 ret->dpkey[1] = state->dpkey[1];
981 ret->dgkey[0] = state->dgkey[0];
982 ret->dgkey[1] = state->dgkey[1];
983 ret->spkey[0] = state->spkey[0];
984 ret->spkey[1] = state->spkey[1];
985 ret->sgkey[0] = state->sgkey[0];
986 ret->sgkey[1] = state->sgkey[1];
987 ret->previous = state->previous;
988 ret->angle = state->angle;
989 ret->completed = state->completed;
990 ret->movecount = state->movecount;
991
992 return ret;
993}
994
995static void free_game(game_state *state)
996{
997 if (--state->grid->refcount <= 0) {
998 sfree(state->grid->squares);
999 sfree(state->grid);
1000 }
1001 sfree(state->bluemask);
1002 sfree(state->facecolours);
1003 sfree(state);
1004}
1005
1006static char *solve_game(const game_state *state, const game_state *currstate,
1007 const char *aux, char **error)
1008{
1009 return NULL;
1010}
1011
1012static int game_can_format_as_text_now(const game_params *params)
1013{
1014 return TRUE;
1015}
1016
1017static char *game_text_format(const game_state *state)
1018{
1019 return NULL;
1020}
1021
1022static game_ui *new_ui(const game_state *state)
1023{
1024 return NULL;
1025}
1026
1027static void free_ui(game_ui *ui)
1028{
1029}
1030
1031static char *encode_ui(const game_ui *ui)
1032{
1033 return NULL;
1034}
1035
1036static void decode_ui(game_ui *ui, const char *encoding)
1037{
1038}
1039
1040static void game_changed_state(game_ui *ui, const game_state *oldstate,
1041 const game_state *newstate)
1042{
1043}
1044
1045struct game_drawstate {
1046 float gridscale;
1047 int ox, oy; /* pixel position of float origin */
1048};
1049
1050/*
1051 * Code shared between interpret_move() and execute_move().
1052 */
1053static int find_move_dest(const game_state *from, int direction,
1054 int *skey, int *dkey)
1055{
1056 int mask, dest, i, j;
1057 float points[4];
1058
1059 /*
1060 * Find the two points in the current grid square which
1061 * correspond to this move.
1062 */
1063 mask = from->grid->squares[from->current].directions[direction];
1064 if (mask == 0)
1065 return -1;
1066 for (i = j = 0; i < from->grid->squares[from->current].npoints; i++)
1067 if (mask & (1 << i)) {
1068 points[j*2] = from->grid->squares[from->current].points[i*2];
1069 points[j*2+1] = from->grid->squares[from->current].points[i*2+1];
1070 skey[j] = i;
1071 j++;
1072 }
1073 assert(j == 2);
1074
1075 /*
1076 * Now find the other grid square which shares those points.
1077 * This is our move destination.
1078 */
1079 dest = -1;
1080 for (i = 0; i < from->grid->nsquares; i++)
1081 if (i != from->current) {
1082 int match = 0;
1083 float dist;
1084
1085 for (j = 0; j < from->grid->squares[i].npoints; j++) {
1086 dist = (SQ(from->grid->squares[i].points[j*2] - points[0]) +
1087 SQ(from->grid->squares[i].points[j*2+1] - points[1]));
1088 if (dist < 0.1)
1089 dkey[match++] = j;
1090 dist = (SQ(from->grid->squares[i].points[j*2] - points[2]) +
1091 SQ(from->grid->squares[i].points[j*2+1] - points[3]));
1092 if (dist < 0.1)
1093 dkey[match++] = j;
1094 }
1095
1096 if (match == 2) {
1097 dest = i;
1098 break;
1099 }
1100 }
1101
1102 return dest;
1103}
1104
1105static char *interpret_move(const game_state *state, game_ui *ui,
1106 const game_drawstate *ds,
1107 int x, int y, int button)
1108{
1109 int direction, mask, i;
1110 int skey[2], dkey[2];
1111
1112 button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
1113
1114 /*
1115 * Moves can be made with the cursor keys or numeric keypad, or
1116 * alternatively you can left-click and the polyhedron will
1117 * move in the general direction of the mouse pointer.
1118 */
1119 if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
1120 direction = UP;
1121 else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
1122 direction = DOWN;
1123 else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
1124 direction = LEFT;
1125 else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
1126 direction = RIGHT;
1127 else if (button == (MOD_NUM_KEYPAD | '7'))
1128 direction = UP_LEFT;
1129 else if (button == (MOD_NUM_KEYPAD | '1'))
1130 direction = DOWN_LEFT;
1131 else if (button == (MOD_NUM_KEYPAD | '9'))
1132 direction = UP_RIGHT;
1133 else if (button == (MOD_NUM_KEYPAD | '3'))
1134 direction = DOWN_RIGHT;
1135 else if (button == LEFT_BUTTON) {
1136 /*
1137 * Find the bearing of the click point from the current
1138 * square's centre.
1139 */
1140 int cx, cy;
1141 double angle;
1142
1143 cx = (int)(state->grid->squares[state->current].x * GRID_SCALE) + ds->ox;
1144 cy = (int)(state->grid->squares[state->current].y * GRID_SCALE) + ds->oy;
1145
1146 if (x == cx && y == cy)
1147 return NULL; /* clicked in exact centre! */
1148 angle = atan2(y - cy, x - cx);
1149
1150 /*
1151 * There are three possibilities.
1152 *
1153 * - This square is a square, so we choose between UP,
1154 * DOWN, LEFT and RIGHT by dividing the available angle
1155 * at the 45-degree points.
1156 *
1157 * - This square is an up-pointing triangle, so we choose
1158 * between DOWN, LEFT and RIGHT by dividing into
1159 * 120-degree arcs.
1160 *
1161 * - This square is a down-pointing triangle, so we choose
1162 * between UP, LEFT and RIGHT in the inverse manner.
1163 *
1164 * Don't forget that since our y-coordinates increase
1165 * downwards, `angle' is measured _clockwise_ from the
1166 * x-axis, not anticlockwise as most mathematicians would
1167 * instinctively assume.
1168 */
1169 if (state->grid->squares[state->current].npoints == 4) {
1170 /* Square. */
1171 if (fabs(angle) > 3*PI/4)
1172 direction = LEFT;
1173 else if (fabs(angle) < PI/4)
1174 direction = RIGHT;
1175 else if (angle > 0)
1176 direction = DOWN;
1177 else
1178 direction = UP;
1179 } else if (state->grid->squares[state->current].directions[UP] == 0) {
1180 /* Up-pointing triangle. */
1181 if (angle < -PI/2 || angle > 5*PI/6)
1182 direction = LEFT;
1183 else if (angle > PI/6)
1184 direction = DOWN;
1185 else
1186 direction = RIGHT;
1187 } else {
1188 /* Down-pointing triangle. */
1189 assert(state->grid->squares[state->current].directions[DOWN] == 0);
1190 if (angle > PI/2 || angle < -5*PI/6)
1191 direction = LEFT;
1192 else if (angle < -PI/6)
1193 direction = UP;
1194 else
1195 direction = RIGHT;
1196 }
1197 } else
1198 return NULL;
1199
1200 mask = state->grid->squares[state->current].directions[direction];
1201 if (mask == 0)
1202 return NULL;
1203
1204 /*
1205 * Translate diagonal directions into orthogonal ones.
1206 */
1207 if (direction > DOWN) {
1208 for (i = LEFT; i <= DOWN; i++)
1209 if (state->grid->squares[state->current].directions[i] == mask) {
1210 direction = i;
1211 break;
1212 }
1213 assert(direction <= DOWN);
1214 }
1215
1216 if (find_move_dest(state, direction, skey, dkey) < 0)
1217 return NULL;
1218
1219 if (direction == LEFT) return dupstr("L");
1220 if (direction == RIGHT) return dupstr("R");
1221 if (direction == UP) return dupstr("U");
1222 if (direction == DOWN) return dupstr("D");
1223
1224 return NULL; /* should never happen */
1225}
1226
1227static game_state *execute_move(const game_state *from, const char *move)
1228{
1229 game_state *ret;
1230 float angle;
1231 struct solid *poly;
1232 int pkey[2];
1233 int skey[2], dkey[2];
1234 int i, j, dest;
1235 int direction;
1236
1237 switch (*move) {
1238 case 'L': direction = LEFT; break;
1239 case 'R': direction = RIGHT; break;
1240 case 'U': direction = UP; break;
1241 case 'D': direction = DOWN; break;
1242 default: return NULL;
1243 }
1244
1245 dest = find_move_dest(from, direction, skey, dkey);
1246 if (dest < 0)
1247 return NULL;
1248
1249 ret = dup_game(from);
1250 ret->current = dest;
1251
1252 /*
1253 * So we know what grid square we're aiming for, and we also
1254 * know the two key points (as indices in both the source and
1255 * destination grid squares) which are invariant between source
1256 * and destination.
1257 *
1258 * Next we must roll the polyhedron on to that square. So we
1259 * find the indices of the key points within the polyhedron's
1260 * vertex array, then use those in a call to transform_poly,
1261 * and align the result on the new grid square.
1262 */
1263 {
1264 int all_pkey[4];
1265 align_poly(from->solid, &from->grid->squares[from->current], all_pkey);
1266 pkey[0] = all_pkey[skey[0]];
1267 pkey[1] = all_pkey[skey[1]];
1268 /*
1269 * Now pkey[0] corresponds to skey[0] and dkey[0], and
1270 * likewise [1].
1271 */
1272 }
1273
1274 /*
1275 * Now find the angle through which to rotate the polyhedron.
1276 * Do this by finding the two faces that share the two vertices
1277 * we've found, and taking the dot product of their normals.
1278 */
1279 {
1280 int f[2], nf = 0;
1281 float dp;
1282
1283 for (i = 0; i < from->solid->nfaces; i++) {
1284 int match = 0;
1285 for (j = 0; j < from->solid->order; j++)
1286 if (from->solid->faces[i*from->solid->order + j] == pkey[0] ||
1287 from->solid->faces[i*from->solid->order + j] == pkey[1])
1288 match++;
1289 if (match == 2) {
1290 assert(nf < 2);
1291 f[nf++] = i;
1292 }
1293 }
1294
1295 assert(nf == 2);
1296
1297 dp = 0;
1298 for (i = 0; i < 3; i++)
1299 dp += (from->solid->normals[f[0]*3+i] *
1300 from->solid->normals[f[1]*3+i]);
1301 angle = (float)acos(dp);
1302 }
1303
1304 /*
1305 * Now transform the polyhedron. We aren't entirely sure
1306 * whether we need to rotate through angle or -angle, and the
1307 * simplest way round this is to try both and see which one
1308 * aligns successfully!
1309 *
1310 * Unfortunately, _both_ will align successfully if this is a
1311 * cube, which won't tell us anything much. So for that
1312 * particular case, I resort to gross hackery: I simply negate
1313 * the angle before trying the alignment, depending on the
1314 * direction. Which directions work which way is determined by
1315 * pure trial and error. I said it was gross :-/
1316 */
1317 {
1318 int all_pkey[4];
1319 int success;
1320
1321 if (from->solid->order == 4 && direction == UP)
1322 angle = -angle; /* HACK */
1323
1324 poly = transform_poly(from->solid,
1325 from->grid->squares[from->current].flip,
1326 pkey[0], pkey[1], angle);
1327 flip_poly(poly, from->grid->squares[ret->current].flip);
1328 success = align_poly(poly, &from->grid->squares[ret->current], all_pkey);
1329
1330 if (!success) {
1331 sfree(poly);
1332 angle = -angle;
1333 poly = transform_poly(from->solid,
1334 from->grid->squares[from->current].flip,
1335 pkey[0], pkey[1], angle);
1336 flip_poly(poly, from->grid->squares[ret->current].flip);
1337 success = align_poly(poly, &from->grid->squares[ret->current], all_pkey);
1338 }
1339
1340 assert(success);
1341 }
1342
1343 /*
1344 * Now we have our rotated polyhedron, which we expect to be
1345 * exactly congruent to the one we started with - but with the
1346 * faces permuted. So we map that congruence and thereby figure
1347 * out how to permute the faces as a result of the polyhedron
1348 * having rolled.
1349 */
1350 {
1351 int *newcolours = snewn(from->solid->nfaces, int);
1352
1353 for (i = 0; i < from->solid->nfaces; i++)
1354 newcolours[i] = -1;
1355
1356 for (i = 0; i < from->solid->nfaces; i++) {
1357 int nmatch = 0;
1358
1359 /*
1360 * Now go through the transformed polyhedron's faces
1361 * and figure out which one's normal is approximately
1362 * equal to this one.
1363 */
1364 for (j = 0; j < poly->nfaces; j++) {
1365 float dist;
1366 int k;
1367
1368 dist = 0;
1369
1370 for (k = 0; k < 3; k++)
1371 dist += SQ(poly->normals[j*3+k] -
1372 from->solid->normals[i*3+k]);
1373
1374 if (APPROXEQ(dist, 0)) {
1375 nmatch++;
1376 newcolours[i] = ret->facecolours[j];
1377 }
1378 }
1379
1380 assert(nmatch == 1);
1381 }
1382
1383 for (i = 0; i < from->solid->nfaces; i++)
1384 assert(newcolours[i] != -1);
1385
1386 sfree(ret->facecolours);
1387 ret->facecolours = newcolours;
1388 }
1389
1390 ret->movecount++;
1391
1392 /*
1393 * And finally, swap the colour between the bottom face of the
1394 * polyhedron and the face we've just landed on.
1395 *
1396 * We don't do this if the game is already complete, since we
1397 * allow the user to roll the fully blue polyhedron around the
1398 * grid as a feeble reward.
1399 */
1400 if (!ret->completed) {
1401 i = lowest_face(from->solid);
1402 j = ret->facecolours[i];
1403 ret->facecolours[i] = GET_SQUARE(ret, ret->current);
1404 SET_SQUARE(ret, ret->current, j);
1405
1406 /*
1407 * Detect game completion.
1408 */
1409 j = 0;
1410 for (i = 0; i < ret->solid->nfaces; i++)
1411 if (ret->facecolours[i])
1412 j++;
1413 if (j == ret->solid->nfaces)
1414 ret->completed = ret->movecount;
1415 }
1416
1417 sfree(poly);
1418
1419 /*
1420 * Align the normal polyhedron with its grid square, to get key
1421 * points for non-animated display.
1422 */
1423 {
1424 int pkey[4];
1425 int success;
1426
1427 success = align_poly(ret->solid, &ret->grid->squares[ret->current], pkey);
1428 assert(success);
1429
1430 ret->dpkey[0] = pkey[0];
1431 ret->dpkey[1] = pkey[1];
1432 ret->dgkey[0] = 0;
1433 ret->dgkey[1] = 1;
1434 }
1435
1436
1437 ret->spkey[0] = pkey[0];
1438 ret->spkey[1] = pkey[1];
1439 ret->sgkey[0] = skey[0];
1440 ret->sgkey[1] = skey[1];
1441 ret->previous = from->current;
1442 ret->angle = angle;
1443
1444 return ret;
1445}
1446
1447/* ----------------------------------------------------------------------
1448 * Drawing routines.
1449 */
1450
1451struct bbox {
1452 float l, r, u, d;
1453};
1454
1455static void find_bbox_callback(void *ctx, struct grid_square *sq)
1456{
1457 struct bbox *bb = (struct bbox *)ctx;
1458 int i;
1459
1460 for (i = 0; i < sq->npoints; i++) {
1461 if (bb->l > sq->points[i*2]) bb->l = sq->points[i*2];
1462 if (bb->r < sq->points[i*2]) bb->r = sq->points[i*2];
1463 if (bb->u > sq->points[i*2+1]) bb->u = sq->points[i*2+1];
1464 if (bb->d < sq->points[i*2+1]) bb->d = sq->points[i*2+1];
1465 }
1466}
1467
1468static struct bbox find_bbox(const game_params *params)
1469{
1470 struct bbox bb;
1471
1472 /*
1473 * These should be hugely more than the real bounding box will
1474 * be.
1475 */
1476 bb.l = 2.0F * (params->d1 + params->d2);
1477 bb.r = -2.0F * (params->d1 + params->d2);
1478 bb.u = 2.0F * (params->d1 + params->d2);
1479 bb.d = -2.0F * (params->d1 + params->d2);
1480 enum_grid_squares(params, find_bbox_callback, &bb);
1481
1482 return bb;
1483}
1484
1485#define XSIZE(gs, bb, solid) \
1486 ((int)(((bb).r - (bb).l + 2*(solid)->border) * gs))
1487#define YSIZE(gs, bb, solid) \
1488 ((int)(((bb).d - (bb).u + 2*(solid)->border) * gs))
1489
1490static void game_compute_size(const game_params *params, int tilesize,
1491 int *x, int *y)
1492{
1493 struct bbox bb = find_bbox(params);
1494
1495 *x = XSIZE(tilesize, bb, solids[params->solid]);
1496 *y = YSIZE(tilesize, bb, solids[params->solid]);
1497}
1498
1499static void game_set_size(drawing *dr, game_drawstate *ds,
1500 const game_params *params, int tilesize)
1501{
1502 struct bbox bb = find_bbox(params);
1503
1504 ds->gridscale = (float)tilesize;
1505 ds->ox = (int)(-(bb.l - solids[params->solid]->border) * ds->gridscale);
1506 ds->oy = (int)(-(bb.u - solids[params->solid]->border) * ds->gridscale);
1507}
1508
1509static float *game_colours(frontend *fe, int *ncolours)
1510{
1511 float *ret = snewn(3 * NCOLOURS, float);
1512
1513 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1514
1515 ret[COL_BORDER * 3 + 0] = 0.0;
1516 ret[COL_BORDER * 3 + 1] = 0.0;
1517 ret[COL_BORDER * 3 + 2] = 0.0;
1518
1519 ret[COL_BLUE * 3 + 0] = 0.0;
1520 ret[COL_BLUE * 3 + 1] = 0.0;
1521 ret[COL_BLUE * 3 + 2] = 1.0;
1522
1523 *ncolours = NCOLOURS;
1524 return ret;
1525}
1526
1527static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1528{
1529 struct game_drawstate *ds = snew(struct game_drawstate);
1530
1531 ds->ox = ds->oy = 0;
1532 ds->gridscale = 0.0F; /* not decided yet */
1533
1534 return ds;
1535}
1536
1537static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1538{
1539 sfree(ds);
1540}
1541
1542static void game_redraw(drawing *dr, game_drawstate *ds,
1543 const game_state *oldstate, const game_state *state,
1544 int dir, const game_ui *ui,
1545 float animtime, float flashtime)
1546{
1547 int i, j;
1548 struct bbox bb = find_bbox(&state->params);
1549 struct solid *poly;
1550 const int *pkey, *gkey;
1551 float t[3];
1552 float angle;
1553 int square;
1554
1555 draw_rect(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid),
1556 YSIZE(GRID_SCALE, bb, state->solid), COL_BACKGROUND);
1557
1558 if (dir < 0) {
1559 const game_state *t;
1560
1561 /*
1562 * This is an Undo. So reverse the order of the states, and
1563 * run the roll timer backwards.
1564 */
1565 assert(oldstate);
1566
1567 t = oldstate;
1568 oldstate = state;
1569 state = t;
1570
1571 animtime = ROLLTIME - animtime;
1572 }
1573
1574 if (!oldstate) {
1575 oldstate = state;
1576 angle = 0.0;
1577 square = state->current;
1578 pkey = state->dpkey;
1579 gkey = state->dgkey;
1580 } else {
1581 angle = state->angle * animtime / ROLLTIME;
1582 square = state->previous;
1583 pkey = state->spkey;
1584 gkey = state->sgkey;
1585 }
1586 state = oldstate;
1587
1588 for (i = 0; i < state->grid->nsquares; i++) {
1589 int coords[8];
1590
1591 for (j = 0; j < state->grid->squares[i].npoints; j++) {
1592 coords[2*j] = ((int)(state->grid->squares[i].points[2*j] * GRID_SCALE)
1593 + ds->ox);
1594 coords[2*j+1] = ((int)(state->grid->squares[i].points[2*j+1]*GRID_SCALE)
1595 + ds->oy);
1596 }
1597
1598 draw_polygon(dr, coords, state->grid->squares[i].npoints,
1599 GET_SQUARE(state, i) ? COL_BLUE : COL_BACKGROUND,
1600 COL_BORDER);
1601 }
1602
1603 /*
1604 * Now compute and draw the polyhedron.
1605 */
1606 poly = transform_poly(state->solid, state->grid->squares[square].flip,
1607 pkey[0], pkey[1], angle);
1608
1609 /*
1610 * Compute the translation required to align the two key points
1611 * on the polyhedron with the same key points on the current
1612 * face.
1613 */
1614 for (i = 0; i < 3; i++) {
1615 float tc = 0.0;
1616
1617 for (j = 0; j < 2; j++) {
1618 float grid_coord;
1619
1620 if (i < 2) {
1621 grid_coord =
1622 state->grid->squares[square].points[gkey[j]*2+i];
1623 } else {
1624 grid_coord = 0.0;
1625 }
1626
1627 tc += (grid_coord - poly->vertices[pkey[j]*3+i]);
1628 }
1629
1630 t[i] = tc / 2;
1631 }
1632 for (i = 0; i < poly->nvertices; i++)
1633 for (j = 0; j < 3; j++)
1634 poly->vertices[i*3+j] += t[j];
1635
1636 /*
1637 * Now actually draw each face.
1638 */
1639 for (i = 0; i < poly->nfaces; i++) {
1640 float points[8];
1641 int coords[8];
1642
1643 for (j = 0; j < poly->order; j++) {
1644 int f = poly->faces[i*poly->order + j];
1645 points[j*2] = (poly->vertices[f*3+0] -
1646 poly->vertices[f*3+2] * poly->shear);
1647 points[j*2+1] = (poly->vertices[f*3+1] -
1648 poly->vertices[f*3+2] * poly->shear);
1649 }
1650
1651 for (j = 0; j < poly->order; j++) {
1652 coords[j*2] = (int)floor(points[j*2] * GRID_SCALE) + ds->ox;
1653 coords[j*2+1] = (int)floor(points[j*2+1] * GRID_SCALE) + ds->oy;
1654 }
1655
1656 /*
1657 * Find out whether these points are in a clockwise or
1658 * anticlockwise arrangement. If the latter, discard the
1659 * face because it's facing away from the viewer.
1660 *
1661 * This would involve fiddly winding-number stuff for a
1662 * general polygon, but for the simple parallelograms we'll
1663 * be seeing here, all we have to do is check whether the
1664 * corners turn right or left. So we'll take the vector
1665 * from point 0 to point 1, turn it right 90 degrees,
1666 * and check the sign of the dot product with that and the
1667 * next vector (point 1 to point 2).
1668 */
1669 {
1670 float v1x = points[2]-points[0];
1671 float v1y = points[3]-points[1];
1672 float v2x = points[4]-points[2];
1673 float v2y = points[5]-points[3];
1674 float dp = v1x * v2y - v1y * v2x;
1675
1676 if (dp <= 0)
1677 continue;
1678 }
1679
1680 draw_polygon(dr, coords, poly->order,
1681 state->facecolours[i] ? COL_BLUE : COL_BACKGROUND,
1682 COL_BORDER);
1683 }
1684 sfree(poly);
1685
1686 draw_update(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid),
1687 YSIZE(GRID_SCALE, bb, state->solid));
1688
1689 /*
1690 * Update the status bar.
1691 */
1692 {
1693 char statusbuf[256];
1694
1695 sprintf(statusbuf, "%sMoves: %d",
1696 (state->completed ? "COMPLETED! " : ""),
1697 (state->completed ? state->completed : state->movecount));
1698
1699 status_bar(dr, statusbuf);
1700 }
1701}
1702
1703static float game_anim_length(const game_state *oldstate,
1704 const game_state *newstate, int dir, game_ui *ui)
1705{
1706 return ROLLTIME;
1707}
1708
1709static float game_flash_length(const game_state *oldstate,
1710 const game_state *newstate, int dir, game_ui *ui)
1711{
1712 return 0.0F;
1713}
1714
1715static int game_status(const game_state *state)
1716{
1717 return state->completed ? +1 : 0;
1718}
1719
1720static int game_timing_state(const game_state *state, game_ui *ui)
1721{
1722 return TRUE;
1723}
1724
1725static void game_print_size(const game_params *params, float *x, float *y)
1726{
1727}
1728
1729static void game_print(drawing *dr, const game_state *state, int tilesize)
1730{
1731}
1732
1733#ifdef COMBINED
1734#define thegame cube
1735#endif
1736
1737const struct game thegame = {
1738 "Cube", "games.cube", "cube",
1739 default_params,
1740 game_fetch_preset, NULL,
1741 decode_params,
1742 encode_params,
1743 free_params,
1744 dup_params,
1745 TRUE, game_configure, custom_params,
1746 validate_params,
1747 new_game_desc,
1748 validate_desc,
1749 new_game,
1750 dup_game,
1751 free_game,
1752 FALSE, solve_game,
1753 FALSE, game_can_format_as_text_now, game_text_format,
1754 new_ui,
1755 free_ui,
1756 encode_ui,
1757 decode_ui,
1758 game_changed_state,
1759 interpret_move,
1760 execute_move,
1761 PREFERRED_GRID_SCALE, game_compute_size, game_set_size,
1762 game_colours,
1763 game_new_drawstate,
1764 game_free_drawstate,
1765 game_redraw,
1766 game_anim_length,
1767 game_flash_length,
1768 game_status,
1769 FALSE, FALSE, game_print_size, game_print,
1770 TRUE, /* wants_statusbar */
1771 FALSE, game_timing_state,
1772 0, /* flags */
1773};
diff --git a/apps/plugins/puzzles/src/cube.html b/apps/plugins/puzzles/src/cube.html
new file mode 100644
index 0000000000..0e32be600e
--- /dev/null
+++ b/apps/plugins/puzzles/src/cube.html
@@ -0,0 +1,57 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Cube</title>
7<link rel="previous" href="net.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="fifteen.html">
12</head>
13<body>
14<p><a href="net.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="fifteen.html">Next</a></p>
15<h1><a name="C4"></a>Chapter 4: <a name="i0"></a>Cube</h1>
16<p>
17This is another one I originally saw as a web game. This one was a Java game <a href="#p0">[2]</a>, by Paul Scott. You have a grid of 16 squares, six of which are blue; on one square rests a cube. Your move is to use the arrow keys to roll the cube through 90 degrees so that it moves to an adjacent square. If you roll the cube on to a blue square, the blue square is picked up on one face of the cube; if you roll a blue face of the cube on to a non-blue square, the blueness is put down again. (In general, whenever you roll the cube, the two faces that come into contact swap colours.) Your job is to get all six blue squares on to the six faces of the cube at the same time. Count your moves and try to do it in as few as possible.
18</p>
19<p>
20Unlike the original Java game, my version has an additional feature: once you've mastered the game with a cube rolling on a square grid, you can change to a triangular grid and roll any of a tetrahedron, an octahedron or an icosahedron.
21</p>
22<p><a name="p0"></a>
23[2] <a href="http://www3.sympatico.ca/paulscott/cube/cube.htm"><code>http://www3.sympatico.ca/paulscott/cube/cube.htm</code></a>
24</p>
25<h2><a name="S4.1"></a>4.1 <a name="i1"></a>Cube controls</h2>
26<p>
27This game can be played with either the keyboard or the mouse.
28</p>
29<p>
30Left-clicking anywhere on the window will move the cube (or other solid) towards the mouse pointer.
31</p>
32<p>
33The arrow keys can also used to roll the cube on its square grid in the four cardinal directions. On the triangular grids, the mapping of arrow keys to directions is more approximate. Vertical movement is disallowed where it doesn't make sense. The four keys surrounding the arrow keys on the numeric keypad (&#8216;7&#8217;, &#8216;9&#8217;, &#8216;1&#8217;, &#8216;3&#8217;) can be used for diagonal movement.
34</p>
35<p>
36(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
37</p>
38<h2><a name="S4.2"></a>4.2 <a name="i2"></a>Cube parameters</h2>
39<p>
40These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
41</p>
42<dl><dt>
43<em>Type of solid</em>
44</dt>
45<dd>
46Selects the solid to roll (and hence the shape of the grid): tetrahedron, cube, octahedron, or icosahedron.
47</dd>
48<dt>
49<em>Width / top</em>, <em>Height / bottom</em>
50</dt>
51<dd>
52On a square grid, horizontal and vertical dimensions. On a triangular grid, the number of triangles on the top and bottom rows respectively.
53</dd>
54</dl>
55
56<hr><address></address></body>
57</html>
diff --git a/apps/plugins/puzzles/src/depcomp b/apps/plugins/puzzles/src/depcomp
new file mode 100755
index 0000000000..fc98710e2a
--- /dev/null
+++ b/apps/plugins/puzzles/src/depcomp
@@ -0,0 +1,791 @@
1#! /bin/sh
2# depcomp - compile a program generating dependencies as side-effects
3
4scriptversion=2013-05-30.07; # UTC
5
6# Copyright (C) 1999-2014 Free Software Foundation, Inc.
7
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2, or (at your option)
11# any later version.
12
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17
18# You should have received a copy of the GNU General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21# As a special exception to the GNU General Public License, if you
22# distribute this file as part of a program that contains a
23# configuration script generated by Autoconf, you may include it under
24# the same distribution terms that you use for the rest of that program.
25
26# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
27
28case $1 in
29 '')
30 echo "$0: No command. Try '$0 --help' for more information." 1>&2
31 exit 1;
32 ;;
33 -h | --h*)
34 cat <<\EOF
35Usage: depcomp [--help] [--version] PROGRAM [ARGS]
36
37Run PROGRAMS ARGS to compile a file, generating dependencies
38as side-effects.
39
40Environment variables:
41 depmode Dependency tracking mode.
42 source Source file read by 'PROGRAMS ARGS'.
43 object Object file output by 'PROGRAMS ARGS'.
44 DEPDIR directory where to store dependencies.
45 depfile Dependency file to output.
46 tmpdepfile Temporary file to use when outputting dependencies.
47 libtool Whether libtool is used (yes/no).
48
49Report bugs to <bug-automake@gnu.org>.
50EOF
51 exit $?
52 ;;
53 -v | --v*)
54 echo "depcomp $scriptversion"
55 exit $?
56 ;;
57esac
58
59# Get the directory component of the given path, and save it in the
60# global variables '$dir'. Note that this directory component will
61# be either empty or ending with a '/' character. This is deliberate.
62set_dir_from ()
63{
64 case $1 in
65 */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
66 *) dir=;;
67 esac
68}
69
70# Get the suffix-stripped basename of the given path, and save it the
71# global variable '$base'.
72set_base_from ()
73{
74 base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
75}
76
77# If no dependency file was actually created by the compiler invocation,
78# we still have to create a dummy depfile, to avoid errors with the
79# Makefile "include basename.Plo" scheme.
80make_dummy_depfile ()
81{
82 echo "#dummy" > "$depfile"
83}
84
85# Factor out some common post-processing of the generated depfile.
86# Requires the auxiliary global variable '$tmpdepfile' to be set.
87aix_post_process_depfile ()
88{
89 # If the compiler actually managed to produce a dependency file,
90 # post-process it.
91 if test -f "$tmpdepfile"; then
92 # Each line is of the form 'foo.o: dependency.h'.
93 # Do two passes, one to just change these to
94 # $object: dependency.h
95 # and one to simply output
96 # dependency.h:
97 # which is needed to avoid the deleted-header problem.
98 { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
99 sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
100 } > "$depfile"
101 rm -f "$tmpdepfile"
102 else
103 make_dummy_depfile
104 fi
105}
106
107# A tabulation character.
108tab=' '
109# A newline character.
110nl='
111'
112# Character ranges might be problematic outside the C locale.
113# These definitions help.
114upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
115lower=abcdefghijklmnopqrstuvwxyz
116digits=0123456789
117alpha=${upper}${lower}
118
119if test -z "$depmode" || test -z "$source" || test -z "$object"; then
120 echo "depcomp: Variables source, object and depmode must be set" 1>&2
121 exit 1
122fi
123
124# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
125depfile=${depfile-`echo "$object" |
126 sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
127tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
128
129rm -f "$tmpdepfile"
130
131# Avoid interferences from the environment.
132gccflag= dashmflag=
133
134# Some modes work just like other modes, but use different flags. We
135# parameterize here, but still list the modes in the big case below,
136# to make depend.m4 easier to write. Note that we *cannot* use a case
137# here, because this file can only contain one case statement.
138if test "$depmode" = hp; then
139 # HP compiler uses -M and no extra arg.
140 gccflag=-M
141 depmode=gcc
142fi
143
144if test "$depmode" = dashXmstdout; then
145 # This is just like dashmstdout with a different argument.
146 dashmflag=-xM
147 depmode=dashmstdout
148fi
149
150cygpath_u="cygpath -u -f -"
151if test "$depmode" = msvcmsys; then
152 # This is just like msvisualcpp but w/o cygpath translation.
153 # Just convert the backslash-escaped backslashes to single forward
154 # slashes to satisfy depend.m4
155 cygpath_u='sed s,\\\\,/,g'
156 depmode=msvisualcpp
157fi
158
159if test "$depmode" = msvc7msys; then
160 # This is just like msvc7 but w/o cygpath translation.
161 # Just convert the backslash-escaped backslashes to single forward
162 # slashes to satisfy depend.m4
163 cygpath_u='sed s,\\\\,/,g'
164 depmode=msvc7
165fi
166
167if test "$depmode" = xlc; then
168 # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
169 gccflag=-qmakedep=gcc,-MF
170 depmode=gcc
171fi
172
173case "$depmode" in
174gcc3)
175## gcc 3 implements dependency tracking that does exactly what
176## we want. Yay! Note: for some reason libtool 1.4 doesn't like
177## it if -MD -MP comes after the -MF stuff. Hmm.
178## Unfortunately, FreeBSD c89 acceptance of flags depends upon
179## the command line argument order; so add the flags where they
180## appear in depend2.am. Note that the slowdown incurred here
181## affects only configure: in makefiles, %FASTDEP% shortcuts this.
182 for arg
183 do
184 case $arg in
185 -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
186 *) set fnord "$@" "$arg" ;;
187 esac
188 shift # fnord
189 shift # $arg
190 done
191 "$@"
192 stat=$?
193 if test $stat -ne 0; then
194 rm -f "$tmpdepfile"
195 exit $stat
196 fi
197 mv "$tmpdepfile" "$depfile"
198 ;;
199
200gcc)
201## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
202## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
203## (see the conditional assignment to $gccflag above).
204## There are various ways to get dependency output from gcc. Here's
205## why we pick this rather obscure method:
206## - Don't want to use -MD because we'd like the dependencies to end
207## up in a subdir. Having to rename by hand is ugly.
208## (We might end up doing this anyway to support other compilers.)
209## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
210## -MM, not -M (despite what the docs say). Also, it might not be
211## supported by the other compilers which use the 'gcc' depmode.
212## - Using -M directly means running the compiler twice (even worse
213## than renaming).
214 if test -z "$gccflag"; then
215 gccflag=-MD,
216 fi
217 "$@" -Wp,"$gccflag$tmpdepfile"
218 stat=$?
219 if test $stat -ne 0; then
220 rm -f "$tmpdepfile"
221 exit $stat
222 fi
223 rm -f "$depfile"
224 echo "$object : \\" > "$depfile"
225 # The second -e expression handles DOS-style file names with drive
226 # letters.
227 sed -e 's/^[^:]*: / /' \
228 -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
229## This next piece of magic avoids the "deleted header file" problem.
230## The problem is that when a header file which appears in a .P file
231## is deleted, the dependency causes make to die (because there is
232## typically no way to rebuild the header). We avoid this by adding
233## dummy dependencies for each header file. Too bad gcc doesn't do
234## this for us directly.
235## Some versions of gcc put a space before the ':'. On the theory
236## that the space means something, we add a space to the output as
237## well. hp depmode also adds that space, but also prefixes the VPATH
238## to the object. Take care to not repeat it in the output.
239## Some versions of the HPUX 10.20 sed can't process this invocation
240## correctly. Breaking it into two sed invocations is a workaround.
241 tr ' ' "$nl" < "$tmpdepfile" \
242 | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
243 | sed -e 's/$/ :/' >> "$depfile"
244 rm -f "$tmpdepfile"
245 ;;
246
247hp)
248 # This case exists only to let depend.m4 do its work. It works by
249 # looking at the text of this script. This case will never be run,
250 # since it is checked for above.
251 exit 1
252 ;;
253
254sgi)
255 if test "$libtool" = yes; then
256 "$@" "-Wp,-MDupdate,$tmpdepfile"
257 else
258 "$@" -MDupdate "$tmpdepfile"
259 fi
260 stat=$?
261 if test $stat -ne 0; then
262 rm -f "$tmpdepfile"
263 exit $stat
264 fi
265 rm -f "$depfile"
266
267 if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
268 echo "$object : \\" > "$depfile"
269 # Clip off the initial element (the dependent). Don't try to be
270 # clever and replace this with sed code, as IRIX sed won't handle
271 # lines with more than a fixed number of characters (4096 in
272 # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
273 # the IRIX cc adds comments like '#:fec' to the end of the
274 # dependency line.
275 tr ' ' "$nl" < "$tmpdepfile" \
276 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
277 | tr "$nl" ' ' >> "$depfile"
278 echo >> "$depfile"
279 # The second pass generates a dummy entry for each header file.
280 tr ' ' "$nl" < "$tmpdepfile" \
281 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
282 >> "$depfile"
283 else
284 make_dummy_depfile
285 fi
286 rm -f "$tmpdepfile"
287 ;;
288
289xlc)
290 # This case exists only to let depend.m4 do its work. It works by
291 # looking at the text of this script. This case will never be run,
292 # since it is checked for above.
293 exit 1
294 ;;
295
296aix)
297 # The C for AIX Compiler uses -M and outputs the dependencies
298 # in a .u file. In older versions, this file always lives in the
299 # current directory. Also, the AIX compiler puts '$object:' at the
300 # start of each line; $object doesn't have directory information.
301 # Version 6 uses the directory in both cases.
302 set_dir_from "$object"
303 set_base_from "$object"
304 if test "$libtool" = yes; then
305 tmpdepfile1=$dir$base.u
306 tmpdepfile2=$base.u
307 tmpdepfile3=$dir.libs/$base.u
308 "$@" -Wc,-M
309 else
310 tmpdepfile1=$dir$base.u
311 tmpdepfile2=$dir$base.u
312 tmpdepfile3=$dir$base.u
313 "$@" -M
314 fi
315 stat=$?
316 if test $stat -ne 0; then
317 rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
318 exit $stat
319 fi
320
321 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
322 do
323 test -f "$tmpdepfile" && break
324 done
325 aix_post_process_depfile
326 ;;
327
328tcc)
329 # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
330 # FIXME: That version still under development at the moment of writing.
331 # Make that this statement remains true also for stable, released
332 # versions.
333 # It will wrap lines (doesn't matter whether long or short) with a
334 # trailing '\', as in:
335 #
336 # foo.o : \
337 # foo.c \
338 # foo.h \
339 #
340 # It will put a trailing '\' even on the last line, and will use leading
341 # spaces rather than leading tabs (at least since its commit 0394caf7
342 # "Emit spaces for -MD").
343 "$@" -MD -MF "$tmpdepfile"
344 stat=$?
345 if test $stat -ne 0; then
346 rm -f "$tmpdepfile"
347 exit $stat
348 fi
349 rm -f "$depfile"
350 # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
351 # We have to change lines of the first kind to '$object: \'.
352 sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
353 # And for each line of the second kind, we have to emit a 'dep.h:'
354 # dummy dependency, to avoid the deleted-header problem.
355 sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
356 rm -f "$tmpdepfile"
357 ;;
358
359## The order of this option in the case statement is important, since the
360## shell code in configure will try each of these formats in the order
361## listed in this file. A plain '-MD' option would be understood by many
362## compilers, so we must ensure this comes after the gcc and icc options.
363pgcc)
364 # Portland's C compiler understands '-MD'.
365 # Will always output deps to 'file.d' where file is the root name of the
366 # source file under compilation, even if file resides in a subdirectory.
367 # The object file name does not affect the name of the '.d' file.
368 # pgcc 10.2 will output
369 # foo.o: sub/foo.c sub/foo.h
370 # and will wrap long lines using '\' :
371 # foo.o: sub/foo.c ... \
372 # sub/foo.h ... \
373 # ...
374 set_dir_from "$object"
375 # Use the source, not the object, to determine the base name, since
376 # that's sadly what pgcc will do too.
377 set_base_from "$source"
378 tmpdepfile=$base.d
379
380 # For projects that build the same source file twice into different object
381 # files, the pgcc approach of using the *source* file root name can cause
382 # problems in parallel builds. Use a locking strategy to avoid stomping on
383 # the same $tmpdepfile.
384 lockdir=$base.d-lock
385 trap "
386 echo '$0: caught signal, cleaning up...' >&2
387 rmdir '$lockdir'
388 exit 1
389 " 1 2 13 15
390 numtries=100
391 i=$numtries
392 while test $i -gt 0; do
393 # mkdir is a portable test-and-set.
394 if mkdir "$lockdir" 2>/dev/null; then
395 # This process acquired the lock.
396 "$@" -MD
397 stat=$?
398 # Release the lock.
399 rmdir "$lockdir"
400 break
401 else
402 # If the lock is being held by a different process, wait
403 # until the winning process is done or we timeout.
404 while test -d "$lockdir" && test $i -gt 0; do
405 sleep 1
406 i=`expr $i - 1`
407 done
408 fi
409 i=`expr $i - 1`
410 done
411 trap - 1 2 13 15
412 if test $i -le 0; then
413 echo "$0: failed to acquire lock after $numtries attempts" >&2
414 echo "$0: check lockdir '$lockdir'" >&2
415 exit 1
416 fi
417
418 if test $stat -ne 0; then
419 rm -f "$tmpdepfile"
420 exit $stat
421 fi
422 rm -f "$depfile"
423 # Each line is of the form `foo.o: dependent.h',
424 # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
425 # Do two passes, one to just change these to
426 # `$object: dependent.h' and one to simply `dependent.h:'.
427 sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
428 # Some versions of the HPUX 10.20 sed can't process this invocation
429 # correctly. Breaking it into two sed invocations is a workaround.
430 sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
431 | sed -e 's/$/ :/' >> "$depfile"
432 rm -f "$tmpdepfile"
433 ;;
434
435hp2)
436 # The "hp" stanza above does not work with aCC (C++) and HP's ia64
437 # compilers, which have integrated preprocessors. The correct option
438 # to use with these is +Maked; it writes dependencies to a file named
439 # 'foo.d', which lands next to the object file, wherever that
440 # happens to be.
441 # Much of this is similar to the tru64 case; see comments there.
442 set_dir_from "$object"
443 set_base_from "$object"
444 if test "$libtool" = yes; then
445 tmpdepfile1=$dir$base.d
446 tmpdepfile2=$dir.libs/$base.d
447 "$@" -Wc,+Maked
448 else
449 tmpdepfile1=$dir$base.d
450 tmpdepfile2=$dir$base.d
451 "$@" +Maked
452 fi
453 stat=$?
454 if test $stat -ne 0; then
455 rm -f "$tmpdepfile1" "$tmpdepfile2"
456 exit $stat
457 fi
458
459 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
460 do
461 test -f "$tmpdepfile" && break
462 done
463 if test -f "$tmpdepfile"; then
464 sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
465 # Add 'dependent.h:' lines.
466 sed -ne '2,${
467 s/^ *//
468 s/ \\*$//
469 s/$/:/
470 p
471 }' "$tmpdepfile" >> "$depfile"
472 else
473 make_dummy_depfile
474 fi
475 rm -f "$tmpdepfile" "$tmpdepfile2"
476 ;;
477
478tru64)
479 # The Tru64 compiler uses -MD to generate dependencies as a side
480 # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
481 # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
482 # dependencies in 'foo.d' instead, so we check for that too.
483 # Subdirectories are respected.
484 set_dir_from "$object"
485 set_base_from "$object"
486
487 if test "$libtool" = yes; then
488 # Libtool generates 2 separate objects for the 2 libraries. These
489 # two compilations output dependencies in $dir.libs/$base.o.d and
490 # in $dir$base.o.d. We have to check for both files, because
491 # one of the two compilations can be disabled. We should prefer
492 # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
493 # automatically cleaned when .libs/ is deleted, while ignoring
494 # the former would cause a distcleancheck panic.
495 tmpdepfile1=$dir$base.o.d # libtool 1.5
496 tmpdepfile2=$dir.libs/$base.o.d # Likewise.
497 tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
498 "$@" -Wc,-MD
499 else
500 tmpdepfile1=$dir$base.d
501 tmpdepfile2=$dir$base.d
502 tmpdepfile3=$dir$base.d
503 "$@" -MD
504 fi
505
506 stat=$?
507 if test $stat -ne 0; then
508 rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
509 exit $stat
510 fi
511
512 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
513 do
514 test -f "$tmpdepfile" && break
515 done
516 # Same post-processing that is required for AIX mode.
517 aix_post_process_depfile
518 ;;
519
520msvc7)
521 if test "$libtool" = yes; then
522 showIncludes=-Wc,-showIncludes
523 else
524 showIncludes=-showIncludes
525 fi
526 "$@" $showIncludes > "$tmpdepfile"
527 stat=$?
528 grep -v '^Note: including file: ' "$tmpdepfile"
529 if test $stat -ne 0; then
530 rm -f "$tmpdepfile"
531 exit $stat
532 fi
533 rm -f "$depfile"
534 echo "$object : \\" > "$depfile"
535 # The first sed program below extracts the file names and escapes
536 # backslashes for cygpath. The second sed program outputs the file
537 # name when reading, but also accumulates all include files in the
538 # hold buffer in order to output them again at the end. This only
539 # works with sed implementations that can handle large buffers.
540 sed < "$tmpdepfile" -n '
541/^Note: including file: *\(.*\)/ {
542 s//\1/
543 s/\\/\\\\/g
544 p
545}' | $cygpath_u | sort -u | sed -n '
546s/ /\\ /g
547s/\(.*\)/'"$tab"'\1 \\/p
548s/.\(.*\) \\/\1:/
549H
550$ {
551 s/.*/'"$tab"'/
552 G
553 p
554}' >> "$depfile"
555 echo >> "$depfile" # make sure the fragment doesn't end with a backslash
556 rm -f "$tmpdepfile"
557 ;;
558
559msvc7msys)
560 # This case exists only to let depend.m4 do its work. It works by
561 # looking at the text of this script. This case will never be run,
562 # since it is checked for above.
563 exit 1
564 ;;
565
566#nosideeffect)
567 # This comment above is used by automake to tell side-effect
568 # dependency tracking mechanisms from slower ones.
569
570dashmstdout)
571 # Important note: in order to support this mode, a compiler *must*
572 # always write the preprocessed file to stdout, regardless of -o.
573 "$@" || exit $?
574
575 # Remove the call to Libtool.
576 if test "$libtool" = yes; then
577 while test "X$1" != 'X--mode=compile'; do
578 shift
579 done
580 shift
581 fi
582
583 # Remove '-o $object'.
584 IFS=" "
585 for arg
586 do
587 case $arg in
588 -o)
589 shift
590 ;;
591 $object)
592 shift
593 ;;
594 *)
595 set fnord "$@" "$arg"
596 shift # fnord
597 shift # $arg
598 ;;
599 esac
600 done
601
602 test -z "$dashmflag" && dashmflag=-M
603 # Require at least two characters before searching for ':'
604 # in the target name. This is to cope with DOS-style filenames:
605 # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
606 "$@" $dashmflag |
607 sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
608 rm -f "$depfile"
609 cat < "$tmpdepfile" > "$depfile"
610 # Some versions of the HPUX 10.20 sed can't process this sed invocation
611 # correctly. Breaking it into two sed invocations is a workaround.
612 tr ' ' "$nl" < "$tmpdepfile" \
613 | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
614 | sed -e 's/$/ :/' >> "$depfile"
615 rm -f "$tmpdepfile"
616 ;;
617
618dashXmstdout)
619 # This case only exists to satisfy depend.m4. It is never actually
620 # run, as this mode is specially recognized in the preamble.
621 exit 1
622 ;;
623
624makedepend)
625 "$@" || exit $?
626 # Remove any Libtool call
627 if test "$libtool" = yes; then
628 while test "X$1" != 'X--mode=compile'; do
629 shift
630 done
631 shift
632 fi
633 # X makedepend
634 shift
635 cleared=no eat=no
636 for arg
637 do
638 case $cleared in
639 no)
640 set ""; shift
641 cleared=yes ;;
642 esac
643 if test $eat = yes; then
644 eat=no
645 continue
646 fi
647 case "$arg" in
648 -D*|-I*)
649 set fnord "$@" "$arg"; shift ;;
650 # Strip any option that makedepend may not understand. Remove
651 # the object too, otherwise makedepend will parse it as a source file.
652 -arch)
653 eat=yes ;;
654 -*|$object)
655 ;;
656 *)
657 set fnord "$@" "$arg"; shift ;;
658 esac
659 done
660 obj_suffix=`echo "$object" | sed 's/^.*\././'`
661 touch "$tmpdepfile"
662 ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
663 rm -f "$depfile"
664 # makedepend may prepend the VPATH from the source file name to the object.
665 # No need to regex-escape $object, excess matching of '.' is harmless.
666 sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
667 # Some versions of the HPUX 10.20 sed can't process the last invocation
668 # correctly. Breaking it into two sed invocations is a workaround.
669 sed '1,2d' "$tmpdepfile" \
670 | tr ' ' "$nl" \
671 | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
672 | sed -e 's/$/ :/' >> "$depfile"
673 rm -f "$tmpdepfile" "$tmpdepfile".bak
674 ;;
675
676cpp)
677 # Important note: in order to support this mode, a compiler *must*
678 # always write the preprocessed file to stdout.
679 "$@" || exit $?
680
681 # Remove the call to Libtool.
682 if test "$libtool" = yes; then
683 while test "X$1" != 'X--mode=compile'; do
684 shift
685 done
686 shift
687 fi
688
689 # Remove '-o $object'.
690 IFS=" "
691 for arg
692 do
693 case $arg in
694 -o)
695 shift
696 ;;
697 $object)
698 shift
699 ;;
700 *)
701 set fnord "$@" "$arg"
702 shift # fnord
703 shift # $arg
704 ;;
705 esac
706 done
707
708 "$@" -E \
709 | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
710 -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
711 | sed '$ s: \\$::' > "$tmpdepfile"
712 rm -f "$depfile"
713 echo "$object : \\" > "$depfile"
714 cat < "$tmpdepfile" >> "$depfile"
715 sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
716 rm -f "$tmpdepfile"
717 ;;
718
719msvisualcpp)
720 # Important note: in order to support this mode, a compiler *must*
721 # always write the preprocessed file to stdout.
722 "$@" || exit $?
723
724 # Remove the call to Libtool.
725 if test "$libtool" = yes; then
726 while test "X$1" != 'X--mode=compile'; do
727 shift
728 done
729 shift
730 fi
731
732 IFS=" "
733 for arg
734 do
735 case "$arg" in
736 -o)
737 shift
738 ;;
739 $object)
740 shift
741 ;;
742 "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
743 set fnord "$@"
744 shift
745 shift
746 ;;
747 *)
748 set fnord "$@" "$arg"
749 shift
750 shift
751 ;;
752 esac
753 done
754 "$@" -E 2>/dev/null |
755 sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
756 rm -f "$depfile"
757 echo "$object : \\" > "$depfile"
758 sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
759 echo "$tab" >> "$depfile"
760 sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
761 rm -f "$tmpdepfile"
762 ;;
763
764msvcmsys)
765 # This case exists only to let depend.m4 do its work. It works by
766 # looking at the text of this script. This case will never be run,
767 # since it is checked for above.
768 exit 1
769 ;;
770
771none)
772 exec "$@"
773 ;;
774
775*)
776 echo "Unknown depmode $depmode" 1>&2
777 exit 1
778 ;;
779esac
780
781exit 0
782
783# Local Variables:
784# mode: shell-script
785# sh-indentation: 2
786# eval: (add-hook 'write-file-hooks 'time-stamp)
787# time-stamp-start: "scriptversion="
788# time-stamp-format: "%:y-%02m-%02d.%02H"
789# time-stamp-time-zone: "UTC"
790# time-stamp-end: "; # UTC"
791# End:
diff --git a/apps/plugins/puzzles/src/desktop.pl b/apps/plugins/puzzles/src/desktop.pl
new file mode 100755
index 0000000000..204c0ce262
--- /dev/null
+++ b/apps/plugins/puzzles/src/desktop.pl
@@ -0,0 +1,52 @@
1#!/usr/bin/perl
2
3# Make .desktop files for the puzzles.
4#
5# At present, this script is intended for developer usage: if you're
6# working on the puzzles and want to play your bleeding-edge locally
7# modified and compiled versions, run this script and it will create a
8# collection of desktop files in ~/.local/share/applications where
9# XFCE can pick them up and add them to its main menu. (Be sure to run
10# 'xfdesktop --reload' after running this.)
11#
12# (If you don't use XFCE, patches to support other desktop
13# environments are welcome :-)
14
15use strict;
16use warnings;
17use Cwd 'abs_path';
18
19die "usage: desktop.pl [<outdir> [<bindir> <icondir>]]\n"
20 unless @ARGV == 0 or @ARGV == 1 or @ARGV == 3;
21
22my ($outdir, $bindir, $icondir) = @ARGV;
23$outdir = $ENV{'HOME'}."/.local/share/applications" unless defined $outdir;
24$bindir = "." unless defined $bindir;
25$icondir = "./icons" unless defined $icondir;
26$bindir = abs_path($bindir);
27$icondir = abs_path($icondir);
28
29open my $desc, "<", "gamedesc.txt"
30 or die "gamedesc.txt: open: $!\n";
31
32while (<$desc>) {
33 chomp;
34 my ($id, $win, $displayname, $description, $summary) = split /:/, $_;
35
36 open my $desktop, ">", "$outdir/$id.desktop"
37 or die "$outdir/$id.desktop: open: $!\n";
38
39 print $desktop "[Desktop Entry]\n";
40 print $desktop "Version=1.0\n";
41 print $desktop "Type=Application\n";
42 print $desktop "Name=$displayname\n";
43 print $desktop "Comment=$description\n";
44 print $desktop "Exec=$bindir/$id\n";
45 print $desktop "Icon=$icondir/$id-48d24.png\n";
46 print $desktop "StartupNotify=false\n";
47 print $desktop "Categories=Game;\n";
48 print $desktop "Terminal=false\n";
49
50 close $desktop
51 or die "$outdir/$id.desktop: close: $!\n";
52}
diff --git a/apps/plugins/puzzles/src/devel.but b/apps/plugins/puzzles/src/devel.but
new file mode 100644
index 0000000000..a38fdda5d0
--- /dev/null
+++ b/apps/plugins/puzzles/src/devel.but
@@ -0,0 +1,4895 @@
1\cfg{text-indent}{0}
2\cfg{text-width}{72}
3\cfg{text-title-align}{left}
4\cfg{text-chapter-align}{left}
5\cfg{text-chapter-numeric}{true}
6\cfg{text-chapter-suffix}{. }
7\cfg{text-chapter-underline}{-}
8\cfg{text-section-align}{0}{left}
9\cfg{text-section-numeric}{0}{true}
10\cfg{text-section-suffix}{0}{. }
11\cfg{text-section-underline}{0}{-}
12\cfg{text-section-align}{1}{left}
13\cfg{text-section-numeric}{1}{true}
14\cfg{text-section-suffix}{1}{. }
15\cfg{text-section-underline}{1}{-}
16\cfg{text-versionid}{0}
17
18\cfg{html-contents-filename}{index.html}
19\cfg{html-template-filename}{%k.html}
20\cfg{html-index-filename}{docindex.html}
21\cfg{html-leaf-level}{1}
22\cfg{html-contents-depth-0}{1}
23\cfg{html-contents-depth-1}{3}
24\cfg{html-leaf-contains-contents}{true}
25
26\define{dash} \u2013{-}
27
28\title Developer documentation for Simon Tatham's puzzle collection
29
30This is a guide to the internal structure of Simon Tatham's Portable
31Puzzle Collection (henceforth referred to simply as \q{Puzzles}),
32for use by anyone attempting to implement a new puzzle or port to a
33new platform.
34
35This guide is believed correct as of r6190. Hopefully it will be
36updated along with the code in future, but if not, I've at least
37left this version number in here so you can figure out what's
38changed by tracking commit comments from there onwards.
39
40\C{intro} Introduction
41
42The Puzzles code base is divided into four parts: a set of
43interchangeable front ends, a set of interchangeable back ends, a
44universal \q{middle end} which acts as a buffer between the two, and
45a bunch of miscellaneous utility functions. In the following
46sections I give some general discussion of each of these parts.
47
48\H{intro-frontend} Front end
49
50The front end is the non-portable part of the code: it's the bit
51that you replace completely when you port to a different platform.
52So it's responsible for all system calls, all GUI interaction, and
53anything else platform-specific.
54
55The current front ends in the main code base are for Windows, GTK
56and MacOS X; I also know of a third-party front end for PalmOS.
57
58The front end contains \cw{main()} or the local platform's
59equivalent. Top-level control over the application's execution flow
60belongs to the front end (it isn't, for example, a set of functions
61called by a universal \cw{main()} somewhere else).
62
63The front end has complete freedom to design the GUI for any given
64port of Puzzles. There is no centralised mechanism for maintaining
65the menu layout, for example. This has a cost in consistency (when I
66\e{do} want the same menu layout on more than one platform, I have
67to edit two pieces of code in parallel every time I make a change),
68but the advantage is that local GUI conventions can be conformed to
69and local constraints adapted to. For example, MacOS X has strict
70human interface guidelines which specify a different menu layout
71from the one I've used on Windows and GTK; there's nothing stopping
72the OS X front end from providing a menu layout consistent with
73those guidelines.
74
75Although the front end is mostly caller rather than the callee in
76its interactions with other parts of the code, it is required to
77implement a small API for other modules to call, mostly of drawing
78functions for games to use when drawing their graphics. The drawing
79API is documented in \k{drawing}; the other miscellaneous front end
80API functions are documented in \k{frontend-api}.
81
82\H{intro-backend} Back end
83
84A \q{back end}, in this collection, is synonymous with a \q{puzzle}.
85Each back end implements a different game.
86
87At the top level, a back end is simply a data structure, containing
88a few constants (flag words, preferred pixel size) and a large
89number of function pointers. Back ends are almost invariably callee
90rather than caller, which means there's a limitation on what a back
91end can do on its own initiative.
92
93The persistent state in a back end is divided into a number of data
94structures, which are used for different purposes and therefore
95likely to be switched around, changed without notice, and otherwise
96updated by the rest of the code. It is important when designing a
97back end to put the right pieces of data into the right structures,
98or standard midend-provided features (such as Undo) may fail to
99work.
100
101The functions and variables provided in the back end data structure
102are documented in \k{backend}.
103
104\H{intro-midend} Middle end
105
106Puzzles has a single and universal \q{middle end}. This code is
107common to all platforms and all games; it sits in between the front
108end and the back end and provides standard functionality everywhere.
109
110People adding new back ends or new front ends should generally not
111need to edit the middle end. On rare occasions there might be a
112change that can be made to the middle end to permit a new game to do
113something not currently anticipated by the middle end's present
114design; however, this is terribly easy to get wrong and should
115probably not be undertaken without consulting the primary maintainer
116(me). Patch submissions containing unannounced mid-end changes will
117be treated on their merits like any other patch; this is just a
118friendly warning that mid-end changes will need quite a lot of
119merits to make them acceptable.
120
121Functionality provided by the mid-end includes:
122
123\b Maintaining a list of game state structures and moving back and
124forth along that list to provide Undo and Redo.
125
126\b Handling timers (for move animations, flashes on completion, and
127in some cases actually timing the game).
128
129\b Handling the container format of game IDs: receiving them,
130picking them apart into parameters, description and/or random seed,
131and so on. The game back end need only handle the individual parts
132of a game ID (encoded parameters and encoded game description);
133everything else is handled centrally by the mid-end.
134
135\b Handling standard keystrokes and menu commands, such as \q{New
136Game}, \q{Restart Game} and \q{Quit}.
137
138\b Pre-processing mouse events so that the game back ends can rely
139on them arriving in a sensible order (no missing button-release
140events, no sudden changes of which button is currently pressed,
141etc).
142
143\b Handling the dialog boxes which ask the user for a game ID.
144
145\b Handling serialisation of entire games (for loading and saving a
146half-finished game to a disk file, or for handling application
147shutdown and restart on platforms such as PalmOS where state is
148expected to be saved).
149
150Thus, there's a lot of work done once by the mid-end so that
151individual back ends don't have to worry about it. All the back end
152has to do is cooperate in ensuring the mid-end can do its work
153properly.
154
155The API of functions provided by the mid-end to be called by the
156front end is documented in \k{midend}.
157
158\H{intro-utils} Miscellaneous utilities
159
160In addition to these three major structural components, the Puzzles
161code also contains a variety of utility modules usable by all of the
162above components. There is a set of functions to provide
163platform-independent random number generation; functions to make
164memory allocation easier; functions which implement a balanced tree
165structure to be used as necessary in complex algorithms; and a few
166other miscellaneous functions. All of these are documented in
167\k{utils}.
168
169\H{intro-structure} Structure of this guide
170
171There are a number of function call interfaces within Puzzles, and
172this guide will discuss each one in a chapter of its own. After
173that, \k{writing} discusses how to design new games, with some
174general design thoughts and tips.
175
176\C{backend} Interface to the back end
177
178This chapter gives a detailed discussion of the interface that each
179back end must implement.
180
181At the top level, each back end source file exports a single global
182symbol, which is a \c{const struct game} containing a large number
183of function pointers and a small amount of constant data. This
184structure is called by different names depending on what kind of
185platform the puzzle set is being compiled on:
186
187\b On platforms such as Windows and GTK, which build a separate
188binary for each puzzle, the game structure in every back end has the
189same name, \cq{thegame}; the front end refers directly to this name,
190so that compiling the same front end module against a different back
191end module builds a different puzzle.
192
193\b On platforms such as MacOS X and PalmOS, which build all the
194puzzles into a single monolithic binary, the game structure in each
195back end must have a different name, and there's a helper module
196\c{list.c} (constructed automatically by the same Perl script that
197builds the \cw{Makefile}s) which contains a complete list of those
198game structures.
199
200On the latter type of platform, source files may assume that the
201preprocessor symbol \c{COMBINED} has been defined. Thus, the usual
202code to declare the game structure looks something like this:
203
204\c #ifdef COMBINED
205\c #define thegame net /* or whatever this game is called */
206\e iii iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
207\c #endif
208\c
209\c const struct game thegame = {
210\c /* lots of structure initialisation in here */
211\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
212\c };
213
214Game back ends must also internally define a number of data
215structures, for storing their various persistent state. This chapter
216will first discuss the nature and use of those structures, and then
217go on to give details of every element of the game structure.
218
219\H{backend-structs} Data structures
220
221Each game is required to define four separate data structures. This
222section discusses each one and suggests what sorts of things need to
223be put in it.
224
225\S{backend-game-params} \c{game_params}
226
227The \c{game_params} structure contains anything which affects the
228automatic generation of new puzzles. So if puzzle generation is
229parametrised in any way, those parameters need to be stored in
230\c{game_params}.
231
232Most puzzles currently in this collection are played on a grid of
233squares, meaning that the most obvious parameter is the grid size.
234Many puzzles have additional parameters; for example, Mines allows
235you to control the number of mines in the grid independently of its
236size, Net can be wrapping or non-wrapping, Solo has difficulty
237levels and symmetry settings, and so on.
238
239A simple rule for deciding whether a data item needs to go in
240\c{game_params} is: would the user expect to be able to control this
241data item from either the preset-game-types menu or the \q{Custom}
242game type configuration? If so, it's part of \c{game_params}.
243
244\c{game_params} structures are permitted to contain pointers to
245subsidiary data if they need to. The back end is required to provide
246functions to create and destroy \c{game_params}, and those functions
247can allocate and free additional memory if necessary. (It has not
248yet been necessary to do this in any puzzle so far, but the
249capability is there just in case.)
250
251\c{game_params} is also the only structure which the game's
252\cw{compute_size()} function may refer to; this means that any
253aspect of the game which affects the size of the window it needs to
254be drawn in must be stored in \c{game_params}. In particular, this
255imposes the fundamental limitation that random game generation may
256not have a random effect on the window size: game generation
257algorithms are constrained to work by starting from the grid size
258rather than generating it as an emergent phenomenon. (Although this
259is a restriction in theory, it has not yet seemed to be a problem.)
260
261\S{backend-game-state} \c{game_state}
262
263While the user is actually playing a puzzle, the \c{game_state}
264structure stores all the data corresponding to the current state of
265play.
266
267The mid-end keeps \c{game_state}s in a list, and adds to the list
268every time the player makes a move; the Undo and Redo functions step
269back and forth through that list.
270
271Therefore, a good means of deciding whether a data item needs to go
272in \c{game_state} is: would a player expect that data item to be
273restored on undo? If so, put it in \c{game_state}, and this will
274automatically happen without you having to lift a finger. If not
275\dash for example, the deaths counter in Mines is precisely
276something that does \e{not} want to be reset to its previous state
277on an undo \dash then you might have found a data item that needs to
278go in \c{game_ui} instead.
279
280During play, \c{game_state}s are often passed around without an
281accompanying \c{game_params} structure. Therefore, any information
282in \c{game_params} which is important during play (such as the grid
283size) must be duplicated within the \c{game_state}. One simple
284method of doing this is to have the \c{game_state} structure
285\e{contain} a \c{game_params} structure as one of its members,
286although this isn't obligatory if you prefer to do it another way.
287
288\S{backend-game-drawstate} \c{game_drawstate}
289
290\c{game_drawstate} carries persistent state relating to the current
291graphical contents of the puzzle window. The same \c{game_drawstate}
292is passed to every call to the game redraw function, so that it can
293remember what it has already drawn and what needs redrawing.
294
295A typical use for a \c{game_drawstate} is to have an array mirroring
296the array of grid squares in the \c{game_state}; then every time the
297redraw function was passed a \c{game_state}, it would loop over all
298the squares, and physically redraw any whose description in the
299\c{game_state} (i.e. what the square needs to look like when the
300redraw is completed) did not match its description in the
301\c{game_drawstate} (i.e. what the square currently looks like).
302
303\c{game_drawstate} is occasionally completely torn down and
304reconstructed by the mid-end, if the user somehow forces a full
305redraw. Therefore, no data should be stored in \c{game_drawstate}
306which is \e{not} related to the state of the puzzle window, because
307it might be unexpectedly destroyed.
308
309The back end provides functions to create and destroy
310\c{game_drawstate}, which means it can contain pointers to
311subsidiary allocated data if it needs to. A common thing to want to
312allocate in a \c{game_drawstate} is a \c{blitter}; see
313\k{drawing-blitter} for more on this subject.
314
315\S{backend-game-ui} \c{game_ui}
316
317\c{game_ui} contains whatever doesn't fit into the above three
318structures!
319
320A new \c{game_ui} is created when the user begins playing a new
321instance of a puzzle (i.e. during \q{New Game} or after entering a
322game ID etc). It persists until the user finishes playing that game
323and begins another one (or closes the window); in particular,
324\q{Restart Game} does \e{not} destroy the \c{game_ui}.
325
326\c{game_ui} is useful for implementing user-interface state which is
327not part of \c{game_state}. Common examples are keyboard control
328(you wouldn't want to have to separately Undo through every cursor
329motion) and mouse dragging. See \k{writing-keyboard-cursor} and
330\k{writing-howto-dragging}, respectively, for more details.
331
332Another use for \c{game_ui} is to store highly persistent data such
333as the Mines death counter. This is conceptually rather different:
334where the Net cursor position was \e{not important enough} to
335preserve for the player to restore by Undo, the Mines death counter
336is \e{too important} to permit the player to revert by Undo!
337
338A final use for \c{game_ui} is to pass information to the redraw
339function about recent changes to the game state. This is used in
340Mines, for example, to indicate whether a requested \q{flash} should
341be a white flash for victory or a red flash for defeat; see
342\k{writing-flash-types}.
343
344\H{backend-simple} Simple data in the back end
345
346In this section I begin to discuss each individual element in the
347back end structure. To begin with, here are some simple
348self-contained data elements.
349
350\S{backend-name} \c{name}
351
352\c const char *name;
353
354This is a simple ASCII string giving the name of the puzzle. This
355name will be used in window titles, in game selection menus on
356monolithic platforms, and anywhere else that the front end needs to
357know the name of a game.
358
359\S{backend-winhelp} \c{winhelp_topic}
360
361\c const char *winhelp_topic;
362
363This member is used on Windows only, to provide online help.
364Although the Windows front end provides a separate binary for each
365puzzle, it has a single monolithic help file; so when a user selects
366\q{Help} from the menu, the program needs to open the help file and
367jump to the chapter describing that particular puzzle.
368
369Therefore, each chapter in \c{puzzles.but} is labelled with a
370\e{help topic} name, similar to this:
371
372\c \cfg{winhelp-topic}{games.net}
373
374And then the corresponding game back end encodes the topic string
375(here \cq{games.net}) in the \c{winhelp_topic} element of the game
376structure.
377
378\H{backend-params} Handling game parameter sets
379
380In this section I present the various functions which handle the
381\c{game_params} structure.
382
383\S{backend-default-params} \cw{default_params()}
384
385\c game_params *(*default_params)(void);
386
387This function allocates a new \c{game_params} structure, fills it
388with the default values, and returns a pointer to it.
389
390\S{backend-fetch-preset} \cw{fetch_preset()}
391
392\c int (*fetch_preset)(int i, char **name, game_params **params);
393
394This function is one of the two APIs a back end can provide to
395populate the \q{Type} menu, which provides a list of conveniently
396accessible preset parameters for most games.
397
398The function is called with \c{i} equal to the index of the preset
399required (numbering from zero). It returns \cw{FALSE} if that preset
400does not exist (if \c{i} is less than zero or greater than the
401largest preset index). Otherwise, it sets \c{*params} to point at a
402newly allocated \c{game_params} structure containing the preset
403information, sets \c{*name} to point at a newly allocated C string
404containing the preset title (to go on the \q{Type} menu), and
405returns \cw{TRUE}.
406
407If the game does not wish to support any presets at all, this
408function is permitted to return \cw{FALSE} always.
409
410If the game wants to return presets in the form of a hierarchical menu
411instead of a flat list (and, indeed, even if it doesn't), then it may
412set this function pointer to \cw{NULL}, and instead fill in the
413alternative function pointer \cw{preset_menu}
414(\k{backend-preset-menu}).
415
416\S{backend-preset-menu} \cw{preset_menu()}
417
418\c struct preset_menu *(*preset_menu)(void);
419
420This function is the more flexible of the two APIs by which a back end
421can define a collection of preset game parameters.
422
423This function simply returns a complete menu hierarchy, in the form of
424a \c{struct preset_menu} (see \k{midend-get-presets}) and further
425submenus (if it wishes) dangling off it. There are utility functions
426described in \k{utils-presets} to make it easy for the back end to
427construct this menu.
428
429If the game has no need to return a hierarchy of menus, it may instead
430opt to implement the \cw{fetch_preset()} function (see
431\k{backend-fetch-preset}).
432
433The game need not fill in the \c{id} fields in the preset menu
434structures. The mid-end will do that after it receives the structure
435from the game, and before passing it on to the front end.
436
437\S{backend-encode-params} \cw{encode_params()}
438
439\c char *(*encode_params)(const game_params *params, int full);
440
441The job of this function is to take a \c{game_params}, and encode it
442in a string form for use in game IDs. The return value must be a
443newly allocated C string, and \e{must} not contain a colon or a hash
444(since those characters are used to mark the end of the parameter
445section in a game ID).
446
447Ideally, it should also not contain any other potentially
448controversial punctuation; bear in mind when designing a string
449parameter format that it will probably be used on both Windows and
450Unix command lines under a variety of exciting shell quoting and
451metacharacter rules. Sticking entirely to alphanumerics is the
452safest thing; if you really need punctuation, you can probably get
453away with commas, periods or underscores without causing anybody any
454major inconvenience. If you venture far beyond that, you're likely
455to irritate \e{somebody}.
456
457(At the time of writing this, all existing games have purely
458alphanumeric string parameter formats. Usually these involve a
459letter denoting a parameter, followed optionally by a number giving
460the value of that parameter, with a few mandatory parts at the
461beginning such as numeric width and height separated by \cq{x}.)
462
463If the \c{full} parameter is \cw{TRUE}, this function should encode
464absolutely everything in the \c{game_params}, such that a subsequent
465call to \cw{decode_params()} (\k{backend-decode-params}) will yield
466an identical structure. If \c{full} is \cw{FALSE}, however, you
467should leave out anything which is not necessary to describe a
468\e{specific puzzle instance}, i.e. anything which only takes effect
469when a new puzzle is \e{generated}. For example, the Solo
470\c{game_params} includes a difficulty rating used when constructing
471new puzzles; but a Solo game ID need not explicitly include the
472difficulty, since to describe a puzzle once generated it's
473sufficient to give the grid dimensions and the location and contents
474of the clue squares. (Indeed, one might very easily type in a puzzle
475out of a newspaper without \e{knowing} what its difficulty level is
476in Solo's terminology.) Therefore, Solo's \cw{encode_params()} only
477encodes the difficulty level if \c{full} is set.
478
479\S{backend-decode-params} \cw{decode_params()}
480
481\c void (*decode_params)(game_params *params, char const *string);
482
483This function is the inverse of \cw{encode_params()}
484(\k{backend-encode-params}). It parses the supplied string and fills
485in the supplied \c{game_params} structure. Note that the structure
486will \e{already} have been allocated: this function is not expected
487to create a \e{new} \c{game_params}, but to modify an existing one.
488
489This function can receive a string which only encodes a subset of
490the parameters. The most obvious way in which this can happen is if
491the string was constructed by \cw{encode_params()} with its \c{full}
492parameter set to \cw{FALSE}; however, it could also happen if the
493user typed in a parameter set manually and missed something out. Be
494prepared to deal with a wide range of possibilities.
495
496When dealing with a parameter which is not specified in the input
497string, what to do requires a judgment call on the part of the
498programmer. Sometimes it makes sense to adjust other parameters to
499bring them into line with the new ones. In Mines, for example, you
500would probably not want to keep the same mine count if the user
501dropped the grid size and didn't specify one, since you might easily
502end up with more mines than would actually fit in the grid! On the
503other hand, sometimes it makes sense to leave the parameter alone: a
504Solo player might reasonably expect to be able to configure size and
505difficulty independently of one another.
506
507This function currently has no direct means of returning an error if
508the string cannot be parsed at all. However, the returned
509\c{game_params} is almost always subsequently passed to
510\cw{validate_params()} (\k{backend-validate-params}), so if you
511really want to signal parse errors, you could always have a \c{char
512*} in your parameters structure which stored an error message, and
513have \cw{validate_params()} return it if it is non-\cw{NULL}.
514
515\S{backend-free-params} \cw{free_params()}
516
517\c void (*free_params)(game_params *params);
518
519This function frees a \c{game_params} structure, and any subsidiary
520allocations contained within it.
521
522\S{backend-dup-params} \cw{dup_params()}
523
524\c game_params *(*dup_params)(const game_params *params);
525
526This function allocates a new \c{game_params} structure and
527initialises it with an exact copy of the information in the one
528provided as input. It returns a pointer to the new duplicate.
529
530\S{backend-can-configure} \c{can_configure}
531
532\c int can_configure;
533
534This boolean data element is set to \cw{TRUE} if the back end
535supports custom parameter configuration via a dialog box. If it is
536\cw{TRUE}, then the functions \cw{configure()} and
537\cw{custom_params()} are expected to work. See \k{backend-configure}
538and \k{backend-custom-params} for more details.
539
540\S{backend-configure} \cw{configure()}
541
542\c config_item *(*configure)(const game_params *params);
543
544This function is called when the user requests a dialog box for
545custom parameter configuration. It returns a newly allocated array
546of \cw{config_item} structures, describing the GUI elements required
547in the dialog box. The array should have one more element than the
548number of controls, since it is terminated with a \cw{C_END} marker
549(see below). Each array element describes the control together with
550its initial value; the front end will modify the value fields and
551return the updated array to \cw{custom_params()} (see
552\k{backend-custom-params}).
553
554The \cw{config_item} structure contains the following elements:
555
556\c char *name;
557\c int type;
558\c char *sval;
559\c int ival;
560
561\c{name} is an ASCII string giving the textual label for a GUI
562control. It is \e{not} expected to be dynamically allocated.
563
564\c{type} contains one of a small number of \c{enum} values defining
565what type of control is being described. The meaning of the \c{sval}
566and \c{ival} fields depends on the value in \c{type}. The valid
567values are:
568
569\dt \c{C_STRING}
570
571\dd Describes a text input box. (This is also used for numeric
572input. The back end does not bother informing the front end that the
573box is numeric rather than textual; some front ends do have the
574capacity to take this into account, but I decided it wasn't worth
575the extra complexity in the interface.) For this type, \c{ival} is
576unused, and \c{sval} contains a dynamically allocated string
577representing the contents of the input box.
578
579\dt \c{C_BOOLEAN}
580
581\dd Describes a simple checkbox. For this type, \c{sval} is unused,
582and \c{ival} is \cw{TRUE} or \cw{FALSE}.
583
584\dt \c{C_CHOICES}
585
586\dd Describes a drop-down list presenting one of a small number of
587fixed choices. For this type, \c{sval} contains a list of strings
588describing the choices; the very first character of \c{sval} is used
589as a delimiter when processing the rest (so that the strings
590\cq{:zero:one:two}, \cq{!zero!one!two} and \cq{xzeroxonextwo} all
591define a three-element list containing \cq{zero}, \cq{one} and
592\cq{two}). \c{ival} contains the index of the currently selected
593element, numbering from zero (so that in the above example, 0 would
594mean \cq{zero} and 2 would mean \cq{two}).
595
596\lcont{
597
598Note that for this control type, \c{sval} is \e{not} dynamically
599allocated, whereas it was for \c{C_STRING}.
600
601}
602
603\dt \c{C_END}
604
605\dd Marks the end of the array of \c{config_item}s. All other fields
606are unused.
607
608The array returned from this function is expected to have filled in
609the initial values of all the controls according to the input
610\c{game_params} structure.
611
612If the game's \c{can_configure} flag is set to \cw{FALSE}, this
613function is never called and need not do anything at all.
614
615\S{backend-custom-params} \cw{custom_params()}
616
617\c game_params *(*custom_params)(const config_item *cfg);
618
619This function is the counterpart to \cw{configure()}
620(\k{backend-configure}). It receives as input an array of
621\c{config_item}s which was originally created by \cw{configure()},
622but in which the control values have since been changed in
623accordance with user input. Its function is to read the new values
624out of the controls and return a newly allocated \c{game_params}
625structure representing the user's chosen parameter set.
626
627(The front end will have modified the controls' \e{values}, but
628there will still always be the same set of controls, in the same
629order, as provided by \cw{configure()}. It is not necessary to check
630the \c{name} and \c{type} fields, although you could use
631\cw{assert()} if you were feeling energetic.)
632
633This function is not expected to (and indeed \e{must not}) free the
634input \c{config_item} array. (If the parameters fail to validate,
635the dialog box will stay open.)
636
637If the game's \c{can_configure} flag is set to \cw{FALSE}, this
638function is never called and need not do anything at all.
639
640\S{backend-validate-params} \cw{validate_params()}
641
642\c char *(*validate_params)(const game_params *params, int full);
643
644This function takes a \c{game_params} structure as input, and checks
645that the parameters described in it fall within sensible limits. (At
646the very least, grid dimensions should almost certainly be strictly
647positive, for example.)
648
649Return value is \cw{NULL} if no problems were found, or
650alternatively a (non-dynamically-allocated) ASCII string describing
651the error in human-readable form.
652
653If the \c{full} parameter is set, full validation should be
654performed: any set of parameters which would not permit generation
655of a sensible puzzle should be faulted. If \c{full} is \e{not} set,
656the implication is that these parameters are not going to be used
657for \e{generating} a puzzle; so parameters which can't even sensibly
658\e{describe} a valid puzzle should still be faulted, but parameters
659which only affect puzzle generation should not be.
660
661(The \c{full} option makes a difference when parameter combinations
662are non-orthogonal. For example, Net has a boolean option
663controlling whether it enforces a unique solution; it turns out that
664it's impossible to generate a uniquely soluble puzzle with wrapping
665walls and width 2, so \cw{validate_params()} will complain if you
666ask for one. However, if the user had just been playing a unique
667wrapping puzzle of a more sensible width, and then pastes in a game
668ID acquired from somebody else which happens to describe a
669\e{non}-unique wrapping width-2 puzzle, then \cw{validate_params()}
670will be passed a \c{game_params} containing the width and wrapping
671settings from the new game ID and the uniqueness setting from the
672old one. This would be faulted, if it weren't for the fact that
673\c{full} is not set during this call, so Net ignores the
674inconsistency. The resulting \c{game_params} is never subsequently
675used to generate a puzzle; this is a promise made by the mid-end
676when it asks for a non-full validation.)
677
678\H{backend-descs} Handling game descriptions
679
680In this section I present the functions that deal with a textual
681description of a puzzle, i.e. the part that comes after the colon in
682a descriptive-format game ID.
683
684\S{backend-new-desc} \cw{new_desc()}
685
686\c char *(*new_desc)(const game_params *params, random_state *rs,
687\c char **aux, int interactive);
688
689This function is where all the really hard work gets done. This is
690the function whose job is to randomly generate a new puzzle,
691ensuring solubility and uniqueness as appropriate.
692
693As input it is given a \c{game_params} structure and a random state
694(see \k{utils-random} for the random number API). It must invent a
695puzzle instance, encode it in string form, and return a dynamically
696allocated C string containing that encoding.
697
698Additionally, it may return a second dynamically allocated string in
699\c{*aux}. (If it doesn't want to, then it can leave that parameter
700completely alone; it isn't required to set it to \cw{NULL}, although
701doing so is harmless.) That string, if present, will be passed to
702\cw{solve()} (\k{backend-solve}) later on; so if the puzzle is
703generated in such a way that a solution is known, then information
704about that solution can be saved in \c{*aux} for \cw{solve()} to
705use.
706
707The \c{interactive} parameter should be ignored by almost all
708puzzles. Its purpose is to distinguish between generating a puzzle
709within a GUI context for immediate play, and generating a puzzle in
710a command-line context for saving to be played later. The only
711puzzle that currently uses this distinction (and, I fervently hope,
712the only one which will \e{ever} need to use it) is Mines, which
713chooses a random first-click location when generating puzzles
714non-interactively, but which waits for the user to place the first
715click when interactive. If you think you have come up with another
716puzzle which needs to make use of this parameter, please think for
717at least ten minutes about whether there is \e{any} alternative!
718
719Note that game description strings are not required to contain an
720encoding of parameters such as grid size; a game description is
721never separated from the \c{game_params} it was generated with, so
722any information contained in that structure need not be encoded
723again in the game description.
724
725\S{backend-validate-desc} \cw{validate_desc()}
726
727\c char *(*validate_desc)(const game_params *params, const char *desc);
728
729This function is given a game description, and its job is to
730validate that it describes a puzzle which makes sense.
731
732To some extent it's up to the user exactly how far they take the
733phrase \q{makes sense}; there are no particularly strict rules about
734how hard the user is permitted to shoot themself in the foot when
735typing in a bogus game description by hand. (For example, Rectangles
736will not verify that the sum of all the numbers in the grid equals
737the grid's area. So a user could enter a puzzle which was provably
738not soluble, and the program wouldn't complain; there just wouldn't
739happen to be any sequence of moves which solved it.)
740
741The one non-negotiable criterion is that any game description which
742makes it through \cw{validate_desc()} \e{must not} subsequently
743cause a crash or an assertion failure when fed to \cw{new_game()}
744and thence to the rest of the back end.
745
746The return value is \cw{NULL} on success, or a
747non-dynamically-allocated C string containing an error message.
748
749\S{backend-new-game} \cw{new_game()}
750
751\c game_state *(*new_game)(midend *me, const game_params *params,
752\c const char *desc);
753
754This function takes a game description as input, together with its
755accompanying \c{game_params}, and constructs a \c{game_state}
756describing the initial state of the puzzle. It returns a newly
757allocated \c{game_state} structure.
758
759Almost all puzzles should ignore the \c{me} parameter. It is
760required by Mines, which needs it for later passing to
761\cw{midend_supersede_game_desc()} (see \k{backend-supersede}) once
762the user has placed the first click. I fervently hope that no other
763puzzle will be awkward enough to require it, so everybody else
764should ignore it. As with the \c{interactive} parameter in
765\cw{new_desc()} (\k{backend-new-desc}), if you think you have a
766reason to need this parameter, please try very hard to think of an
767alternative approach!
768
769\H{backend-states} Handling game states
770
771This section describes the functions which create and destroy
772\c{game_state} structures.
773
774(Well, except \cw{new_game()}, which is in \k{backend-new-game}
775instead of under here; but it deals with game descriptions \e{and}
776game states and it had to go in one section or the other.)
777
778\S{backend-dup-game} \cw{dup_game()}
779
780\c game_state *(*dup_game)(const game_state *state);
781
782This function allocates a new \c{game_state} structure and
783initialises it with an exact copy of the information in the one
784provided as input. It returns a pointer to the new duplicate.
785
786\S{backend-free-game} \cw{free_game()}
787
788\c void (*free_game)(game_state *state);
789
790This function frees a \c{game_state} structure, and any subsidiary
791allocations contained within it.
792
793\H{backend-ui} Handling \c{game_ui}
794
795\S{backend-new-ui} \cw{new_ui()}
796
797\c game_ui *(*new_ui)(const game_state *state);
798
799This function allocates and returns a new \c{game_ui} structure for
800playing a particular puzzle. It is passed a pointer to the initial
801\c{game_state}, in case it needs to refer to that when setting up
802the initial values for the new game.
803
804\S{backend-free-ui} \cw{free_ui()}
805
806\c void (*free_ui)(game_ui *ui);
807
808This function frees a \c{game_ui} structure, and any subsidiary
809allocations contained within it.
810
811\S{backend-encode-ui} \cw{encode_ui()}
812
813\c char *(*encode_ui)(const game_ui *ui);
814
815This function encodes any \e{important} data in a \c{game_ui}
816structure in string form. It is only called when saving a
817half-finished game to a file.
818
819It should be used sparingly. Almost all data in a \c{game_ui} is not
820important enough to save. The location of the keyboard-controlled
821cursor, for example, can be reset to a default position on reloading
822the game without impacting the user experience. If the user should
823somehow manage to save a game while a mouse drag was in progress,
824then discarding that mouse drag would be an outright \e{feature}.
825
826A typical thing that \e{would} be worth encoding in this function is
827the Mines death counter: it's in the \c{game_ui} rather than the
828\c{game_state} because it's too important to allow the user to
829revert it by using Undo, and therefore it's also too important to
830allow the user to revert it by saving and reloading. (Of course, the
831user could edit the save file by hand... But if the user is \e{that}
832determined to cheat, they could just as easily modify the game's
833source.)
834
835\S{backend-decode-ui} \cw{decode_ui()}
836
837\c void (*decode_ui)(game_ui *ui, const char *encoding);
838
839This function parses a string previously output by \cw{encode_ui()},
840and writes the decoded data back into the provided \c{game_ui}
841structure.
842
843\S{backend-changed-state} \cw{changed_state()}
844
845\c void (*changed_state)(game_ui *ui, const game_state *oldstate,
846\c const game_state *newstate);
847
848This function is called by the mid-end whenever the current game
849state changes, for any reason. Those reasons include:
850
851\b a fresh move being made by \cw{interpret_move()} and
852\cw{execute_move()}
853
854\b a solve operation being performed by \cw{solve()} and
855\cw{execute_move()}
856
857\b the user moving back and forth along the undo list by means of
858the Undo and Redo operations
859
860\b the user selecting Restart to go back to the initial game state.
861
862The job of \cw{changed_state()} is to update the \c{game_ui} for
863consistency with the new game state, if any update is necessary. For
864example, Same Game stores data about the currently selected tile
865group in its \c{game_ui}, and this data is intrinsically related to
866the game state it was derived from. So it's very likely to become
867invalid when the game state changes; thus, Same Game's
868\cw{changed_state()} function clears the current selection whenever
869it is called.
870
871When \cw{anim_length()} or \cw{flash_length()} are called, you can
872be sure that there has been a previous call to \cw{changed_state()}.
873So \cw{changed_state()} can set up data in the \c{game_ui} which will
874be read by \cw{anim_length()} and \cw{flash_length()}, and those
875functions will not have to worry about being called without the data
876having been initialised.
877
878\H{backend-moves} Making moves
879
880This section describes the functions which actually make moves in
881the game: that is, the functions which process user input and end up
882producing new \c{game_state}s.
883
884\S{backend-interpret-move} \cw{interpret_move()}
885
886\c char *(*interpret_move)(const game_state *state, game_ui *ui,
887\c const game_drawstate *ds,
888\c int x, int y, int button);
889
890This function receives user input and processes it. Its input
891parameters are the current \c{game_state}, the current \c{game_ui}
892and the current \c{game_drawstate}, plus details of the input event.
893\c{button} is either an ASCII value or a special code (listed below)
894indicating an arrow or function key or a mouse event; when
895\c{button} is a mouse event, \c{x} and \c{y} contain the pixel
896coordinates of the mouse pointer relative to the top left of the
897puzzle's drawing area.
898
899(The pointer to the \c{game_drawstate} is marked \c{const}, because
900\c{interpret_move} should not write to it. The normal use of that
901pointer will be to read the game's tile size parameter in order to
902divide mouse coordinates by it.)
903
904\cw{interpret_move()} may return in three different ways:
905
906\b Returning \cw{NULL} indicates that no action whatsoever occurred
907in response to the input event; the puzzle was not interested in it
908at all.
909
910\b Returning the empty string (\cw{""}) indicates that the input
911event has resulted in a change being made to the \c{game_ui} which
912will require a redraw of the game window, but that no actual
913\e{move} was made (i.e. no new \c{game_state} needs to be created).
914
915\b Returning anything else indicates that a move was made and that a
916new \c{game_state} must be created. However, instead of actually
917constructing a new \c{game_state} itself, this function is required
918to return a string description of the details of the move. This
919string will be passed to \cw{execute_move()}
920(\k{backend-execute-move}) to actually create the new
921\c{game_state}. (Encoding moves as strings in this way means that
922the mid-end can keep the strings as well as the game states, and the
923strings can be written to disk when saving the game and fed to
924\cw{execute_move()} again on reloading.)
925
926The return value from \cw{interpret_move()} is expected to be
927dynamically allocated if and only if it is not either \cw{NULL}
928\e{or} the empty string.
929
930After this function is called, the back end is permitted to rely on
931some subsequent operations happening in sequence:
932
933\b \cw{execute_move()} will be called to convert this move
934description into a new \c{game_state}
935
936\b \cw{changed_state()} will be called with the new \c{game_state}.
937
938This means that if \cw{interpret_move()} needs to do updates to the
939\c{game_ui} which are easier to perform by referring to the new
940\c{game_state}, it can safely leave them to be done in
941\cw{changed_state()} and not worry about them failing to happen.
942
943(Note, however, that \cw{execute_move()} may \e{also} be called in
944other circumstances. It is only \cw{interpret_move()} which can rely
945on a subsequent call to \cw{changed_state()}.)
946
947The special key codes supported by this function are:
948
949\dt \cw{LEFT_BUTTON}, \cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON}
950
951\dd Indicate that one of the mouse buttons was pressed down.
952
953\dt \cw{LEFT_DRAG}, \cw{MIDDLE_DRAG}, \cw{RIGHT_DRAG}
954
955\dd Indicate that the mouse was moved while one of the mouse buttons
956was still down. The mid-end guarantees that when one of these events
957is received, it will always have been preceded by a button-down
958event (and possibly other drag events) for the same mouse button,
959and no event involving another mouse button will have appeared in
960between.
961
962\dt \cw{LEFT_RELEASE}, \cw{MIDDLE_RELEASE}, \cw{RIGHT_RELEASE}
963
964\dd Indicate that a mouse button was released. The mid-end
965guarantees that when one of these events is received, it will always
966have been preceded by a button-down event (and possibly some drag
967events) for the same mouse button, and no event involving another
968mouse button will have appeared in between.
969
970\dt \cw{CURSOR_UP}, \cw{CURSOR_DOWN}, \cw{CURSOR_LEFT},
971\cw{CURSOR_RIGHT}
972
973\dd Indicate that an arrow key was pressed.
974
975\dt \cw{CURSOR_SELECT}
976
977\dd On platforms which have a prominent \q{select} button alongside
978their cursor keys, indicates that that button was pressed.
979
980In addition, there are some modifiers which can be bitwise-ORed into
981the \c{button} parameter:
982
983\dt \cw{MOD_CTRL}, \cw{MOD_SHFT}
984
985\dd These indicate that the Control or Shift key was pressed
986alongside the key. They only apply to the cursor keys, not to mouse
987buttons or anything else.
988
989\dt \cw{MOD_NUM_KEYPAD}
990
991\dd This applies to some ASCII values, and indicates that the key
992code was input via the numeric keypad rather than the main keyboard.
993Some puzzles may wish to treat this differently (for example, a
994puzzle might want to use the numeric keypad as an eight-way
995directional pad), whereas others might not (a game involving numeric
996input probably just wants to treat the numeric keypad as numbers).
997
998\dt \cw{MOD_MASK}
999
1000\dd This mask is the bitwise OR of all the available modifiers; you
1001can bitwise-AND with \cw{~MOD_MASK} to strip all the modifiers off
1002any input value.
1003
1004\S{backend-execute-move} \cw{execute_move()}
1005
1006\c game_state *(*execute_move)(const game_state *state, char *move);
1007
1008This function takes an input \c{game_state} and a move string as
1009output from \cw{interpret_move()}. It returns a newly allocated
1010\c{game_state} which contains the result of applying the specified
1011move to the input game state.
1012
1013This function may return \cw{NULL} if it cannot parse the move
1014string (and this is definitely preferable to crashing or failing an
1015assertion, since one way this can happen is if loading a corrupt
1016save file). However, it must not return \cw{NULL} for any move
1017string that really was output from \cw{interpret_move()}: this is
1018punishable by assertion failure in the mid-end.
1019
1020\S{backend-can-solve} \c{can_solve}
1021
1022\c int can_solve;
1023
1024This boolean field is set to \cw{TRUE} if the game's \cw{solve()}
1025function does something. If it's set to \cw{FALSE}, the game will
1026not even offer the \q{Solve} menu option.
1027
1028\S{backend-solve} \cw{solve()}
1029
1030\c char *(*solve)(const game_state *orig, const game_state *curr,
1031\c const char *aux, char **error);
1032
1033This function is called when the user selects the \q{Solve} option
1034from the menu.
1035
1036It is passed two input game states: \c{orig} is the game state from
1037the very start of the puzzle, and \c{curr} is the current one.
1038(Different games find one or other or both of these convenient.) It
1039is also passed the \c{aux} string saved by \cw{new_desc()}
1040(\k{backend-new-desc}), in case that encodes important information
1041needed to provide the solution.
1042
1043If this function is unable to produce a solution (perhaps, for
1044example, the game has no in-built solver so it can only solve
1045puzzles it invented internally and has an \c{aux} string for) then
1046it may return \cw{NULL}. If it does this, it must also set
1047\c{*error} to an error message to be presented to the user (such as
1048\q{Solution not known for this puzzle}); that error message is not
1049expected to be dynamically allocated.
1050
1051If this function \e{does} produce a solution, it returns a move string
1052suitable for feeding to \cw{execute_move()}
1053(\k{backend-execute-move}). Like a (non-empty) string returned from
1054\cw{interpret_move()}, the returned string should be dynamically
1055allocated.
1056
1057\H{backend-drawing} Drawing the game graphics
1058
1059This section discusses the back end functions that deal with
1060drawing.
1061
1062\S{backend-new-drawstate} \cw{new_drawstate()}
1063
1064\c game_drawstate *(*new_drawstate)(drawing *dr,
1065\c const game_state *state);
1066
1067This function allocates and returns a new \c{game_drawstate}
1068structure for drawing a particular puzzle. It is passed a pointer to
1069a \c{game_state}, in case it needs to refer to that when setting up
1070any initial data.
1071
1072This function may not rely on the puzzle having been newly started;
1073a new draw state can be constructed at any time if the front end
1074requests a forced redraw. For games like Pattern, in which initial
1075game states are much simpler than general ones, this might be
1076important to keep in mind.
1077
1078The parameter \c{dr} is a drawing object (see \k{drawing}) which the
1079function might need to use to allocate blitters. (However, this
1080isn't recommended; it's usually more sensible to wait to allocate a
1081blitter until \cw{set_size()} is called, because that way you can
1082tailor it to the scale at which the puzzle is being drawn.)
1083
1084\S{backend-free-drawstate} \cw{free_drawstate()}
1085
1086\c void (*free_drawstate)(drawing *dr, game_drawstate *ds);
1087
1088This function frees a \c{game_drawstate} structure, and any
1089subsidiary allocations contained within it.
1090
1091The parameter \c{dr} is a drawing object (see \k{drawing}), which
1092might be required if you are freeing a blitter.
1093
1094\S{backend-preferred-tilesize} \c{preferred_tilesize}
1095
1096\c int preferred_tilesize;
1097
1098Each game is required to define a single integer parameter which
1099expresses, in some sense, the scale at which it is drawn. This is
1100described in the APIs as \cq{tilesize}, since most puzzles are on a
1101square (or possibly triangular or hexagonal) grid and hence a
1102sensible interpretation of this parameter is to define it as the
1103size of one grid tile in pixels; however, there's no actual
1104requirement that the \q{tile size} be proportional to the game
1105window size. Window size is required to increase monotonically with
1106\q{tile size}, however.
1107
1108The data element \c{preferred_tilesize} indicates the tile size
1109which should be used in the absence of a good reason to do otherwise
1110(such as the screen being too small, or the user explicitly
1111requesting a resize if that ever gets implemented).
1112
1113\S{backend-compute-size} \cw{compute_size()}
1114
1115\c void (*compute_size)(const game_params *params, int tilesize,
1116\c int *x, int *y);
1117
1118This function is passed a \c{game_params} structure and a tile size.
1119It returns, in \c{*x} and \c{*y}, the size in pixels of the drawing
1120area that would be required to render a puzzle with those parameters
1121at that tile size.
1122
1123\S{backend-set-size} \cw{set_size()}
1124
1125\c void (*set_size)(drawing *dr, game_drawstate *ds,
1126\c const game_params *params, int tilesize);
1127
1128This function is responsible for setting up a \c{game_drawstate} to
1129draw at a given tile size. Typically this will simply involve
1130copying the supplied \c{tilesize} parameter into a \c{tilesize}
1131field inside the draw state; for some more complex games it might
1132also involve setting up other dimension fields, or possibly
1133allocating a blitter (see \k{drawing-blitter}).
1134
1135The parameter \c{dr} is a drawing object (see \k{drawing}), which is
1136required if a blitter needs to be allocated.
1137
1138Back ends may assume (and may enforce by assertion) that this
1139function will be called at most once for any \c{game_drawstate}. If
1140a puzzle needs to be redrawn at a different size, the mid-end will
1141create a fresh drawstate.
1142
1143\S{backend-colours} \cw{colours()}
1144
1145\c float *(*colours)(frontend *fe, int *ncolours);
1146
1147This function is responsible for telling the front end what colours
1148the puzzle will need to draw itself.
1149
1150It returns the number of colours required in \c{*ncolours}, and the
1151return value from the function itself is a dynamically allocated
1152array of three times that many \c{float}s, containing the red, green
1153and blue components of each colour respectively as numbers in the
1154range [0,1].
1155
1156The second parameter passed to this function is a front end handle.
1157The only things it is permitted to do with this handle are to call
1158the front-end function called \cw{frontend_default_colour()} (see
1159\k{frontend-default-colour}) or the utility function called
1160\cw{game_mkhighlight()} (see \k{utils-game-mkhighlight}). (The
1161latter is a wrapper on the former, so front end implementors only
1162need to provide \cw{frontend_default_colour()}.) This allows
1163\cw{colours()} to take local configuration into account when
1164deciding on its own colour allocations. Most games use the front
1165end's default colour as their background, apart from a few which
1166depend on drawing relief highlights so they adjust the background
1167colour if it's too light for highlights to show up against it.
1168
1169Note that the colours returned from this function are for
1170\e{drawing}, not for printing. Printing has an entirely different
1171colour allocation policy.
1172
1173\S{backend-anim-length} \cw{anim_length()}
1174
1175\c float (*anim_length)(const game_state *oldstate,
1176\c const game_state *newstate,
1177\c int dir, game_ui *ui);
1178
1179This function is called when a move is made, undone or redone. It is
1180given the old and the new \c{game_state}, and its job is to decide
1181whether the transition between the two needs to be animated or can
1182be instant.
1183
1184\c{oldstate} is the state that was current until this call;
1185\c{newstate} is the state that will be current after it. \c{dir}
1186specifies the chronological order of those states: if it is
1187positive, then the transition is the result of a move or a redo (and
1188so \c{newstate} is the later of the two moves), whereas if it is
1189negative then the transition is the result of an undo (so that
1190\c{newstate} is the \e{earlier} move).
1191
1192If this function decides the transition should be animated, it
1193returns the desired length of the animation in seconds. If not, it
1194returns zero.
1195
1196State changes as a result of a Restart operation are never animated;
1197the mid-end will handle them internally and never consult this
1198function at all. State changes as a result of Solve operations are
1199also not animated by default, although you can change this for a
1200particular game by setting a flag in \c{flags} (\k{backend-flags}).
1201
1202The function is also passed a pointer to the local \c{game_ui}. It
1203may refer to information in here to help with its decision (see
1204\k{writing-conditional-anim} for an example of this), and/or it may
1205\e{write} information about the nature of the animation which will
1206be read later by \cw{redraw()}.
1207
1208When this function is called, it may rely on \cw{changed_state()}
1209having been called previously, so if \cw{anim_length()} needs to
1210refer to information in the \c{game_ui}, then \cw{changed_state()}
1211is a reliable place to have set that information up.
1212
1213Move animations do not inhibit further input events. If the user
1214continues playing before a move animation is complete, the animation
1215will be abandoned and the display will jump straight to the final
1216state.
1217
1218\S{backend-flash-length} \cw{flash_length()}
1219
1220\c float (*flash_length)(const game_state *oldstate,
1221\c const game_state *newstate,
1222\c int dir, game_ui *ui);
1223
1224This function is called when a move is completed. (\q{Completed}
1225means that not only has the move been made, but any animation which
1226accompanied it has finished.) It decides whether the transition from
1227\c{oldstate} to \c{newstate} merits a \q{flash}.
1228
1229A flash is much like a move animation, but it is \e{not} interrupted
1230by further user interface activity; it runs to completion in
1231parallel with whatever else might be going on on the display. The
1232only thing which will rush a flash to completion is another flash.
1233
1234The purpose of flashes is to indicate that the game has been
1235completed. They were introduced as a separate concept from move
1236animations because of Net: the habit of most Net players (and
1237certainly me) is to rotate a tile into place and immediately lock
1238it, then move on to another tile. When you make your last move, at
1239the instant the final tile is rotated into place the screen starts
1240to flash to indicate victory \dash but if you then press the lock
1241button out of habit, then the move animation is cancelled, and the
1242victory flash does not complete. (And if you \e{don't} press the
1243lock button, the completed grid will look untidy because there will
1244be one unlocked square.) Therefore, I introduced a specific concept
1245of a \q{flash} which is separate from a move animation and can
1246proceed in parallel with move animations and any other display
1247activity, so that the victory flash in Net is not cancelled by that
1248final locking move.
1249
1250The input parameters to \cw{flash_length()} are exactly the same as
1251the ones to \cw{anim_length()}.
1252
1253Just like \cw{anim_length()}, when this function is called, it may
1254rely on \cw{changed_state()} having been called previously, so if it
1255needs to refer to information in the \c{game_ui} then
1256\cw{changed_state()} is a reliable place to have set that
1257information up.
1258
1259(Some games use flashes to indicate defeat as well as victory;
1260Mines, for example, flashes in a different colour when you tread on
1261a mine from the colour it uses when you complete the game. In order
1262to achieve this, its \cw{flash_length()} function has to store a
1263flag in the \c{game_ui} to indicate which flash type is required.)
1264
1265\S{backend-status} \cw{status()}
1266
1267\c int (*status)(const game_state *state);
1268
1269This function returns a status value indicating whether the current
1270game is still in play, or has been won, or has been conclusively lost.
1271The mid-end uses this to implement \cw{midend_status()}
1272(\k{midend-status}).
1273
1274The return value should be +1 if the game has been successfully
1275solved. If the game has been lost in a situation where further play is
1276unlikely, the return value should be -1. If neither is true (so play
1277is still ongoing), return zero.
1278
1279Front ends may wish to use a non-zero status as a cue to proactively
1280offer the option of starting a new game. Therefore, back ends should
1281not return -1 if the game has been \e{technically} lost but undoing
1282and continuing is still a realistic possibility.
1283
1284(For instance, games with hidden information such as Guess or Mines
1285might well return a non-zero status whenever they reveal the solution,
1286whether or not the player guessed it correctly, on the grounds that a
1287player would be unlikely to hide the solution and continue playing
1288after the answer was spoiled. On the other hand, games where you can
1289merely get into a dead end such as Same Game or Inertia might choose
1290to return 0 in that situation, on the grounds that the player would
1291quite likely press Undo and carry on playing.)
1292
1293\S{backend-redraw} \cw{redraw()}
1294
1295\c void (*redraw)(drawing *dr, game_drawstate *ds,
1296\c const game_state *oldstate,
1297\c const game_state *newstate,
1298\c int dir, const game_ui *ui,
1299\c float anim_time, float flash_time);
1300
1301This function is responsible for actually drawing the contents of
1302the game window, and for redrawing every time the game state or the
1303\c{game_ui} changes.
1304
1305The parameter \c{dr} is a drawing object which may be passed to the
1306drawing API functions (see \k{drawing} for documentation of the
1307drawing API). This function may not save \c{dr} and use it
1308elsewhere; it must only use it for calling back to the drawing API
1309functions within its own lifetime.
1310
1311\c{ds} is the local \c{game_drawstate}, of course, and \c{ui} is the
1312local \c{game_ui}.
1313
1314\c{newstate} is the semantically-current game state, and is always
1315non-\cw{NULL}. If \c{oldstate} is also non-\cw{NULL}, it means that
1316a move has recently been made and the game is still in the process
1317of displaying an animation linking the old and new states; in this
1318situation, \c{anim_time} will give the length of time (in seconds)
1319that the animation has already been running. If \c{oldstate} is
1320\cw{NULL}, then \c{anim_time} is unused (and will hopefully be set
1321to zero to avoid confusion).
1322
1323\c{flash_time}, if it is is non-zero, denotes that the game is in
1324the middle of a flash, and gives the time since the start of the
1325flash. See \k{backend-flash-length} for general discussion of
1326flashes.
1327
1328The very first time this function is called for a new
1329\c{game_drawstate}, it is expected to redraw the \e{entire} drawing
1330area. Since this often involves drawing visual furniture which is
1331never subsequently altered, it is often simplest to arrange this by
1332having a special \q{first time} flag in the draw state, and
1333resetting it after the first redraw.
1334
1335When this function (or any subfunction) calls the drawing API, it is
1336expected to pass colour indices which were previously defined by the
1337\cw{colours()} function.
1338
1339\H{backend-printing} Printing functions
1340
1341This section discusses the back end functions that deal with
1342printing puzzles out on paper.
1343
1344\S{backend-can-print} \c{can_print}
1345
1346\c int can_print;
1347
1348This flag is set to \cw{TRUE} if the puzzle is capable of printing
1349itself on paper. (This makes sense for some puzzles, such as Solo,
1350which can be filled in with a pencil. Other puzzles, such as
1351Twiddle, inherently involve moving things around and so would not
1352make sense to print.)
1353
1354If this flag is \cw{FALSE}, then the functions \cw{print_size()}
1355and \cw{print()} will never be called.
1356
1357\S{backend-can-print-in-colour} \c{can_print_in_colour}
1358
1359\c int can_print_in_colour;
1360
1361This flag is set to \cw{TRUE} if the puzzle is capable of printing
1362itself differently when colour is available. For example, Map can
1363actually print coloured regions in different \e{colours} rather than
1364resorting to cross-hatching.
1365
1366If the \c{can_print} flag is \cw{FALSE}, then this flag will be
1367ignored.
1368
1369\S{backend-print-size} \cw{print_size()}
1370
1371\c void (*print_size)(const game_params *params, float *x, float *y);
1372
1373This function is passed a \c{game_params} structure and a tile size.
1374It returns, in \c{*x} and \c{*y}, the preferred size in
1375\e{millimetres} of that puzzle if it were to be printed out on paper.
1376
1377If the \c{can_print} flag is \cw{FALSE}, this function will never be
1378called.
1379
1380\S{backend-print} \cw{print()}
1381
1382\c void (*print)(drawing *dr, const game_state *state, int tilesize);
1383
1384This function is called when a puzzle is to be printed out on paper.
1385It should use the drawing API functions (see \k{drawing}) to print
1386itself.
1387
1388This function is separate from \cw{redraw()} because it is often
1389very different:
1390
1391\b The printing function may not depend on pixel accuracy, since
1392printer resolution is variable. Draw as if your canvas had infinite
1393resolution.
1394
1395\b The printing function sometimes needs to display things in a
1396completely different style. Net, for example, is very different as
1397an on-screen puzzle and as a printed one.
1398
1399\b The printing function is often much simpler since it has no need
1400to deal with repeated partial redraws.
1401
1402However, there's no reason the printing and redraw functions can't
1403share some code if they want to.
1404
1405When this function (or any subfunction) calls the drawing API, the
1406colour indices it passes should be colours which have been allocated
1407by the \cw{print_*_colour()} functions within this execution of
1408\cw{print()}. This is very different from the fixed small number of
1409colours used in \cw{redraw()}, because printers do not have a
1410limitation on the total number of colours that may be used. Some
1411puzzles' printing functions might wish to allocate only one \q{ink}
1412colour and use it for all drawing; others might wish to allocate
1413\e{more} colours than are used on screen.
1414
1415One possible colour policy worth mentioning specifically is that a
1416puzzle's printing function might want to allocate the \e{same}
1417colour indices as are used by the redraw function, so that code
1418shared between drawing and printing does not have to keep switching
1419its colour indices. In order to do this, the simplest thing is to
1420make use of the fact that colour indices returned from
1421\cw{print_*_colour()} are guaranteed to be in increasing order from
1422zero. So if you have declared an \c{enum} defining three colours
1423\cw{COL_BACKGROUND}, \cw{COL_THIS} and \cw{COL_THAT}, you might then
1424write
1425
1426\c int c;
1427\c c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
1428\c c = print_mono_colour(dr, 0); assert(c == COL_THIS);
1429\c c = print_mono_colour(dr, 0); assert(c == COL_THAT);
1430
1431If the \c{can_print} flag is \cw{FALSE}, this function will never be
1432called.
1433
1434\H{backend-misc} Miscellaneous
1435
1436\S{backend-can-format-as-text-ever} \c{can_format_as_text_ever}
1437
1438\c int can_format_as_text_ever;
1439
1440This boolean field is \cw{TRUE} if the game supports formatting a
1441game state as ASCII text (typically ASCII art) for copying to the
1442clipboard and pasting into other applications. If it is \cw{FALSE},
1443front ends will not offer the \q{Copy} command at all.
1444
1445If this field is \cw{TRUE}, the game does not necessarily have to
1446support text formatting for \e{all} games: e.g. a game which can be
1447played on a square grid or a triangular one might only support copy
1448and paste for the former, because triangular grids in ASCII art are
1449just too difficult.
1450
1451If this field is \cw{FALSE}, the functions
1452\cw{can_format_as_text_now()} (\k{backend-can-format-as-text-now})
1453and \cw{text_format()} (\k{backend-text-format}) are never called.
1454
1455\S{backend-can-format-as-text-now} \c{can_format_as_text_now()}
1456
1457\c int (*can_format_as_text_now)(const game_params *params);
1458
1459This function is passed a \c{game_params} and returns a boolean,
1460which is \cw{TRUE} if the game can support ASCII text output for
1461this particular game type. If it returns \cw{FALSE}, front ends will
1462grey out or otherwise disable the \q{Copy} command.
1463
1464Games may enable and disable the copy-and-paste function for
1465different game \e{parameters}, but are currently constrained to
1466return the same answer from this function for all game \e{states}
1467sharing the same parameters. In other words, the \q{Copy} function
1468may enable or disable itself when the player changes game preset,
1469but will never change during play of a single game or when another
1470game of exactly the same type is generated.
1471
1472This function should not take into account aspects of the game
1473parameters which are not encoded by \cw{encode_params()}
1474(\k{backend-encode-params}) when the \c{full} parameter is set to
1475\cw{FALSE}. Such parameters will not necessarily match up between a
1476call to this function and a subsequent call to \cw{text_format()}
1477itself. (For instance, game \e{difficulty} should not affect whether
1478the game can be copied to the clipboard. Only the actual visible
1479\e{shape} of the game can affect that.)
1480
1481\S{backend-text-format} \cw{text_format()}
1482
1483\c char *(*text_format)(const game_state *state);
1484
1485This function is passed a \c{game_state}, and returns a newly
1486allocated C string containing an ASCII representation of that game
1487state. It is used to implement the \q{Copy} operation in many front
1488ends.
1489
1490This function will only ever be called if the back end field
1491\c{can_format_as_text_ever} (\k{backend-can-format-as-text-ever}) is
1492\cw{TRUE} \e{and} the function \cw{can_format_as_text_now()}
1493(\k{backend-can-format-as-text-now}) has returned \cw{TRUE} for the
1494currently selected game parameters.
1495
1496The returned string may contain line endings (and will probably want
1497to), using the normal C internal \cq{\\n} convention. For
1498consistency between puzzles, all multi-line textual puzzle
1499representations should \e{end} with a newline as well as containing
1500them internally. (There are currently no puzzles which have a
1501one-line ASCII representation, so there's no precedent yet for
1502whether that should come with a newline or not.)
1503
1504\S{backend-wants-statusbar} \cw{wants_statusbar}
1505
1506\c int wants_statusbar;
1507
1508This boolean field is set to \cw{TRUE} if the puzzle has a use for a
1509textual status line (to display score, completion status, currently
1510active tiles, etc).
1511
1512\S{backend-is-timed} \c{is_timed}
1513
1514\c int is_timed;
1515
1516This boolean field is \cw{TRUE} if the puzzle is time-critical. If
1517so, the mid-end will maintain a game timer while the user plays.
1518
1519If this field is \cw{FALSE}, then \cw{timing_state()} will never be
1520called and need not do anything.
1521
1522\S{backend-timing-state} \cw{timing_state()}
1523
1524\c int (*timing_state)(const game_state *state, game_ui *ui);
1525
1526This function is passed the current \c{game_state} and the local
1527\c{game_ui}; it returns \cw{TRUE} if the game timer should currently
1528be running.
1529
1530A typical use for the \c{game_ui} in this function is to note when
1531the game was first completed (by setting a flag in
1532\cw{changed_state()} \dash see \k{backend-changed-state}), and
1533freeze the timer thereafter so that the user can undo back through
1534their solution process without altering their time.
1535
1536\S{backend-flags} \c{flags}
1537
1538\c int flags;
1539
1540This field contains miscellaneous per-backend flags. It consists of
1541the bitwise OR of some combination of the following:
1542
1543\dt \cw{BUTTON_BEATS(x,y)}
1544
1545\dd Given any \cw{x} and \cw{y} from the set \{\cw{LEFT_BUTTON},
1546\cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON}\}, this macro evaluates to a
1547bit flag which indicates that when buttons \cw{x} and \cw{y} are
1548both pressed simultaneously, the mid-end should consider \cw{x} to
1549have priority. (In the absence of any such flags, the mid-end will
1550always consider the most recently pressed button to have priority.)
1551
1552\dt \cw{SOLVE_ANIMATES}
1553
1554\dd This flag indicates that moves generated by \cw{solve()}
1555(\k{backend-solve}) are candidates for animation just like any other
1556move. For most games, solve moves should not be animated, so the
1557mid-end doesn't even bother calling \cw{anim_length()}
1558(\k{backend-anim-length}), thus saving some special-case code in
1559each game. On the rare occasion that animated solve moves are
1560actually required, you can set this flag.
1561
1562\dt \cw{REQUIRE_RBUTTON}
1563
1564\dd This flag indicates that the puzzle cannot be usefully played
1565without the use of mouse buttons other than the left one. On some
1566PDA platforms, this flag is used by the front end to enable
1567right-button emulation through an appropriate gesture. Note that a
1568puzzle is not required to set this just because it \e{uses} the
1569right button, but only if its use of the right button is critical to
1570playing the game. (Slant, for example, uses the right button to
1571cycle through the three square states in the opposite order from the
1572left button, and hence can manage fine without it.)
1573
1574\dt \cw{REQUIRE_NUMPAD}
1575
1576\dd This flag indicates that the puzzle cannot be usefully played
1577without the use of number-key input. On some PDA platforms it causes
1578an emulated number pad to appear on the screen. Similarly to
1579\cw{REQUIRE_RBUTTON}, a puzzle need not specify this simply if its
1580use of the number keys is not critical.
1581
1582\H{backend-initiative} Things a back end may do on its own initiative
1583
1584This section describes a couple of things that a back end may choose
1585to do by calling functions elsewhere in the program, which would not
1586otherwise be obvious.
1587
1588\S{backend-newrs} Create a random state
1589
1590If a back end needs random numbers at some point during normal play,
1591it can create a fresh \c{random_state} by first calling
1592\c{get_random_seed} (\k{frontend-get-random-seed}) and then passing
1593the returned seed data to \cw{random_new()}.
1594
1595This is likely not to be what you want. If a puzzle needs randomness
1596in the middle of play, it's likely to be more sensible to store some
1597sort of random state within the \c{game_state}, so that the random
1598numbers are tied to the particular game state and hence the player
1599can't simply keep undoing their move until they get numbers they
1600like better.
1601
1602This facility is currently used only in Net, to implement the
1603\q{jumble} command, which sets every unlocked tile to a new random
1604orientation. This randomness \e{is} a reasonable use of the feature,
1605because it's non-adversarial \dash there's no advantage to the user
1606in getting different random numbers.
1607
1608\S{backend-supersede} Supersede its own game description
1609
1610In response to a move, a back end is (reluctantly) permitted to call
1611\cw{midend_supersede_game_desc()}:
1612
1613\c void midend_supersede_game_desc(midend *me,
1614\c char *desc, char *privdesc);
1615
1616When the user selects \q{New Game}, the mid-end calls
1617\cw{new_desc()} (\k{backend-new-desc}) to get a new game
1618description, and (as well as using that to generate an initial game
1619state) stores it for the save file and for telling to the user. The
1620function above overwrites that game description, and also splits it
1621in two. \c{desc} becomes the new game description which is provided
1622to the user on request, and is also the one used to construct a new
1623initial game state if the user selects \q{Restart}. \c{privdesc} is
1624a \q{private} game description, used to reconstruct the game's
1625initial state when reloading.
1626
1627The distinction between the two, as well as the need for this
1628function at all, comes from Mines. Mines begins with a blank grid
1629and no idea of where the mines actually are; \cw{new_desc()} does
1630almost no work in interactive mode, and simply returns a string
1631encoding the \c{random_state}. When the user first clicks to open a
1632tile, \e{then} Mines generates the mine positions, in such a way
1633that the game is soluble from that starting point. Then it uses this
1634function to supersede the random-state game description with a
1635proper one. But it needs two: one containing the initial click
1636location (because that's what you want to happen if you restart the
1637game, and also what you want to send to a friend so that they play
1638\e{the same game} as you), and one without the initial click
1639location (because when you save and reload the game, you expect to
1640see the same blank initial state as you had before saving).
1641
1642I should stress again that this function is a horrid hack. Nobody
1643should use it if they're not Mines; if you think you need to use it,
1644think again repeatedly in the hope of finding a better way to do
1645whatever it was you needed to do.
1646
1647\C{drawing} The drawing API
1648
1649The back end function \cw{redraw()} (\k{backend-redraw}) is required
1650to draw the puzzle's graphics on the window's drawing area, or on
1651paper if the puzzle is printable. To do this portably, it is
1652provided with a drawing API allowing it to talk directly to the
1653front end. In this chapter I document that API, both for the benefit
1654of back end authors trying to use it and for front end authors
1655trying to implement it.
1656
1657The drawing API as seen by the back end is a collection of global
1658functions, each of which takes a pointer to a \c{drawing} structure
1659(a \q{drawing object}). These objects are supplied as parameters to
1660the back end's \cw{redraw()} and \cw{print()} functions.
1661
1662In fact these global functions are not implemented directly by the
1663front end; instead, they are implemented centrally in \c{drawing.c}
1664and form a small piece of middleware. The drawing API as supplied by
1665the front end is a structure containing a set of function pointers,
1666plus a \cq{void *} handle which is passed to each of those
1667functions. This enables a single front end to switch between
1668multiple implementations of the drawing API if necessary. For
1669example, the Windows API supplies a printing mechanism integrated
1670into the same GDI which deals with drawing in windows, and therefore
1671the same API implementation can handle both drawing and printing;
1672but on Unix, the most common way for applications to print is by
1673producing PostScript output directly, and although it would be
1674\e{possible} to write a single (say) \cw{draw_rect()} function which
1675checked a global flag to decide whether to do GTK drawing operations
1676or output PostScript to a file, it's much nicer to have two separate
1677functions and switch between them as appropriate.
1678
1679When drawing, the puzzle window is indexed by pixel coordinates,
1680with the top left pixel defined as \cw{(0,0)} and the bottom right
1681pixel \cw{(w-1,h-1)}, where \c{w} and \c{h} are the width and height
1682values returned by the back end function \cw{compute_size()}
1683(\k{backend-compute-size}).
1684
1685When printing, the puzzle's print area is indexed in exactly the
1686same way (with an arbitrary tile size provided by the printing
1687module \c{printing.c}), to facilitate sharing of code between the
1688drawing and printing routines. However, when printing, puzzles may
1689no longer assume that the coordinate unit has any relationship to a
1690pixel; the printer's actual resolution might very well not even be
1691known at print time, so the coordinate unit might be smaller or
1692larger than a pixel. Puzzles' print functions should restrict
1693themselves to drawing geometric shapes rather than fiddly pixel
1694manipulation.
1695
1696\e{Puzzles' redraw functions may assume that the surface they draw
1697on is persistent}. It is the responsibility of every front end to
1698preserve the puzzle's window contents in the face of GUI window
1699expose issues and similar. It is not permissible to request that the
1700back end redraw any part of a window that it has already drawn,
1701unless something has actually changed as a result of making moves in
1702the puzzle.
1703
1704Most front ends accomplish this by having the drawing routines draw
1705on a stored bitmap rather than directly on the window, and copying
1706the bitmap to the window every time a part of the window needs to be
1707redrawn. Therefore, it is vitally important that whenever the back
1708end does any drawing it informs the front end of which parts of the
1709window it has accessed, and hence which parts need repainting. This
1710is done by calling \cw{draw_update()} (\k{drawing-draw-update}).
1711
1712Persistence of old drawing is convenient. However, a puzzle should
1713be very careful about how it updates its drawing area. The problem
1714is that some front ends do anti-aliased drawing: rather than simply
1715choosing between leaving each pixel untouched or painting it a
1716specified colour, an antialiased drawing function will \e{blend} the
1717original and new colours in pixels at a figure's boundary according
1718to the proportion of the pixel occupied by the figure (probably
1719modified by some heuristic fudge factors). All of this produces a
1720smoother appearance for curves and diagonal lines.
1721
1722An unfortunate effect of drawing an anti-aliased figure repeatedly
1723is that the pixels around the figure's boundary come steadily more
1724saturated with \q{ink} and the boundary appears to \q{spread out}.
1725Worse, redrawing a figure in a different colour won't fully paint
1726over the old boundary pixels, so the end result is a rather ugly
1727smudge.
1728
1729A good strategy to avoid unpleasant anti-aliasing artifacts is to
1730identify a number of rectangular areas which need to be redrawn,
1731clear them to the background colour, and then redraw their contents
1732from scratch, being careful all the while not to stray beyond the
1733boundaries of the original rectangles. The \cw{clip()} function
1734(\k{drawing-clip}) comes in very handy here. Games based on a square
1735grid can often do this fairly easily. Other games may need to be
1736somewhat more careful. For example, Loopy's redraw function first
1737identifies portions of the display which need to be updated. Then,
1738if the changes are fairly well localised, it clears and redraws a
1739rectangle containing each changed area. Otherwise, it gives up and
1740redraws the entire grid from scratch.
1741
1742It is possible to avoid clearing to background and redrawing from
1743scratch if one is very careful about which drawing functions one
1744uses: if a function is documented as not anti-aliasing under some
1745circumstances, you can rely on each pixel in a drawing either being
1746left entirely alone or being set to the requested colour, with no
1747blending being performed.
1748
1749In the following sections I first discuss the drawing API as seen by
1750the back end, and then the \e{almost} identical function-pointer
1751form seen by the front end.
1752
1753\H{drawing-backend} Drawing API as seen by the back end
1754
1755This section documents the back-end drawing API, in the form of
1756functions which take a \c{drawing} object as an argument.
1757
1758\S{drawing-draw-rect} \cw{draw_rect()}
1759
1760\c void draw_rect(drawing *dr, int x, int y, int w, int h,
1761\c int colour);
1762
1763Draws a filled rectangle in the puzzle window.
1764
1765\c{x} and \c{y} give the coordinates of the top left pixel of the
1766rectangle. \c{w} and \c{h} give its width and height. Thus, the
1767horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
1768inclusive, and the vertical extent from \c{y} to \c{y+h-1}
1769inclusive.
1770
1771\c{colour} is an integer index into the colours array returned by
1772the back end function \cw{colours()} (\k{backend-colours}).
1773
1774There is no separate pixel-plotting function. If you want to plot a
1775single pixel, the approved method is to use \cw{draw_rect()} with
1776width and height set to 1.
1777
1778Unlike many of the other drawing functions, this function is
1779guaranteed to be pixel-perfect: the rectangle will be sharply
1780defined and not anti-aliased or anything like that.
1781
1782This function may be used for both drawing and printing.
1783
1784\S{drawing-draw-rect-outline} \cw{draw_rect_outline()}
1785
1786\c void draw_rect_outline(drawing *dr, int x, int y, int w, int h,
1787\c int colour);
1788
1789Draws an outline rectangle in the puzzle window.
1790
1791\c{x} and \c{y} give the coordinates of the top left pixel of the
1792rectangle. \c{w} and \c{h} give its width and height. Thus, the
1793horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
1794inclusive, and the vertical extent from \c{y} to \c{y+h-1}
1795inclusive.
1796
1797\c{colour} is an integer index into the colours array returned by
1798the back end function \cw{colours()} (\k{backend-colours}).
1799
1800From a back end perspective, this function may be considered to be
1801part of the drawing API. However, front ends are not required to
1802implement it, since it is actually implemented centrally (in
1803\cw{misc.c}) as a wrapper on \cw{draw_polygon()}.
1804
1805This function may be used for both drawing and printing.
1806
1807\S{drawing-draw-line} \cw{draw_line()}
1808
1809\c void draw_line(drawing *dr, int x1, int y1, int x2, int y2,
1810\c int colour);
1811
1812Draws a straight line in the puzzle window.
1813
1814\c{x1} and \c{y1} give the coordinates of one end of the line.
1815\c{x2} and \c{y2} give the coordinates of the other end. The line
1816drawn includes both those points.
1817
1818\c{colour} is an integer index into the colours array returned by
1819the back end function \cw{colours()} (\k{backend-colours}).
1820
1821Some platforms may perform anti-aliasing on this function.
1822Therefore, do not assume that you can erase a line by drawing the
1823same line over it in the background colour; anti-aliasing might lead
1824to perceptible ghost artefacts around the vanished line. Horizontal
1825and vertical lines, however, are pixel-perfect and not anti-aliased.
1826
1827This function may be used for both drawing and printing.
1828
1829\S{drawing-draw-polygon} \cw{draw_polygon()}
1830
1831\c void draw_polygon(drawing *dr, int *coords, int npoints,
1832\c int fillcolour, int outlinecolour);
1833
1834Draws an outlined or filled polygon in the puzzle window.
1835
1836\c{coords} is an array of \cw{(2*npoints)} integers, containing the
1837\c{x} and \c{y} coordinates of \c{npoints} vertices.
1838
1839\c{fillcolour} and \c{outlinecolour} are integer indices into the
1840colours array returned by the back end function \cw{colours()}
1841(\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to
1842indicate that the polygon should be outlined only.
1843
1844The polygon defined by the specified list of vertices is first
1845filled in \c{fillcolour}, if specified, and then outlined in
1846\c{outlinecolour}.
1847
1848\c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour
1849(and front ends are permitted to enforce this by assertion). This is
1850because different platforms disagree on whether a filled polygon
1851should include its boundary line or not, so drawing \e{only} a
1852filled polygon would have non-portable effects. If you want your
1853filled polygon not to have a visible outline, you must set
1854\c{outlinecolour} to the same as \c{fillcolour}.
1855
1856Some platforms may perform anti-aliasing on this function.
1857Therefore, do not assume that you can erase a polygon by drawing the
1858same polygon over it in the background colour. Also, be prepared for
1859the polygon to extend a pixel beyond its obvious bounding box as a
1860result of this; if you really need it not to do this to avoid
1861interfering with other delicate graphics, you should probably use
1862\cw{clip()} (\k{drawing-clip}). You can rely on horizontal and
1863vertical lines not being anti-aliased.
1864
1865This function may be used for both drawing and printing.
1866
1867\S{drawing-draw-circle} \cw{draw_circle()}
1868
1869\c void draw_circle(drawing *dr, int cx, int cy, int radius,
1870\c int fillcolour, int outlinecolour);
1871
1872Draws an outlined or filled circle in the puzzle window.
1873
1874\c{cx} and \c{cy} give the coordinates of the centre of the circle.
1875\c{radius} gives its radius. The total horizontal pixel extent of
1876the circle is from \c{cx-radius+1} to \c{cx+radius-1} inclusive, and
1877the vertical extent similarly around \c{cy}.
1878
1879\c{fillcolour} and \c{outlinecolour} are integer indices into the
1880colours array returned by the back end function \cw{colours()}
1881(\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to
1882indicate that the circle should be outlined only.
1883
1884The circle is first filled in \c{fillcolour}, if specified, and then
1885outlined in \c{outlinecolour}.
1886
1887\c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour
1888(and front ends are permitted to enforce this by assertion). This is
1889because different platforms disagree on whether a filled circle
1890should include its boundary line or not, so drawing \e{only} a
1891filled circle would have non-portable effects. If you want your
1892filled circle not to have a visible outline, you must set
1893\c{outlinecolour} to the same as \c{fillcolour}.
1894
1895Some platforms may perform anti-aliasing on this function.
1896Therefore, do not assume that you can erase a circle by drawing the
1897same circle over it in the background colour. Also, be prepared for
1898the circle to extend a pixel beyond its obvious bounding box as a
1899result of this; if you really need it not to do this to avoid
1900interfering with other delicate graphics, you should probably use
1901\cw{clip()} (\k{drawing-clip}).
1902
1903This function may be used for both drawing and printing.
1904
1905\S{drawing-draw-thick-line} \cw{draw_thick_line()}
1906
1907\c void draw_thick_line(drawing *dr, float thickness,
1908\c float x1, float y1, float x2, float y2,
1909\c int colour)
1910
1911Draws a line in the puzzle window, giving control over the line's
1912thickness.
1913
1914\c{x1} and \c{y1} give the coordinates of one end of the line.
1915\c{x2} and \c{y2} give the coordinates of the other end.
1916\c{thickness} gives the thickness of the line, in pixels.
1917
1918Note that the coordinates and thickness are floating-point: the
1919continuous coordinate system is in effect here. It's important to
1920be able to address points with better-than-pixel precision in this
1921case, because one can't otherwise properly express the endpoints of
1922lines with both odd and even thicknesses.
1923
1924Some platforms may perform anti-aliasing on this function. The
1925precise pixels affected by a thick-line drawing operation may vary
1926between platforms, and no particular guarantees are provided.
1927Indeed, even horizontal or vertical lines may be anti-aliased.
1928
1929This function may be used for both drawing and printing.
1930
1931\S{drawing-draw-text} \cw{draw_text()}
1932
1933\c void draw_text(drawing *dr, int x, int y, int fonttype,
1934\c int fontsize, int align, int colour, char *text);
1935
1936Draws text in the puzzle window.
1937
1938\c{x} and \c{y} give the coordinates of a point. The relation of
1939this point to the location of the text is specified by \c{align},
1940which is a bitwise OR of horizontal and vertical alignment flags:
1941
1942\dt \cw{ALIGN_VNORMAL}
1943
1944\dd Indicates that \c{y} is aligned with the baseline of the text.
1945
1946\dt \cw{ALIGN_VCENTRE}
1947
1948\dd Indicates that \c{y} is aligned with the vertical centre of the
1949text. (In fact, it's aligned with the vertical centre of normal
1950\e{capitalised} text: displaying two pieces of text with
1951\cw{ALIGN_VCENTRE} at the same \cw{y}-coordinate will cause their
1952baselines to be aligned with one another, even if one is an ascender
1953and the other a descender.)
1954
1955\dt \cw{ALIGN_HLEFT}
1956
1957\dd Indicates that \c{x} is aligned with the left-hand end of the
1958text.
1959
1960\dt \cw{ALIGN_HCENTRE}
1961
1962\dd Indicates that \c{x} is aligned with the horizontal centre of
1963the text.
1964
1965\dt \cw{ALIGN_HRIGHT}
1966
1967\dd Indicates that \c{x} is aligned with the right-hand end of the
1968text.
1969
1970\c{fonttype} is either \cw{FONT_FIXED} or \cw{FONT_VARIABLE}, for a
1971monospaced or proportional font respectively. (No more detail than
1972that may be specified; it would only lead to portability issues
1973between different platforms.)
1974
1975\c{fontsize} is the desired size, in pixels, of the text. This size
1976corresponds to the overall point size of the text, not to any
1977internal dimension such as the cap-height.
1978
1979\c{colour} is an integer index into the colours array returned by
1980the back end function \cw{colours()} (\k{backend-colours}).
1981
1982This function may be used for both drawing and printing.
1983
1984The character set used to encode the text passed to this function is
1985specified \e{by the drawing object}, although it must be a superset
1986of ASCII. If a puzzle wants to display text that is not contained in
1987ASCII, it should use the \cw{text_fallback()} function
1988(\k{drawing-text-fallback}) to query the drawing object for an
1989appropriate representation of the characters it wants.
1990
1991\S{drawing-text-fallback} \cw{text_fallback()}
1992
1993\c char *text_fallback(drawing *dr, const char *const *strings,
1994\c int nstrings);
1995
1996This function is used to request a translation of UTF-8 text into
1997whatever character encoding is expected by the drawing object's
1998implementation of \cw{draw_text()}.
1999
2000The input is a list of strings encoded in UTF-8: \cw{nstrings} gives
2001the number of strings in the list, and \cw{strings[0]},
2002\cw{strings[1]}, ..., \cw{strings[nstrings-1]} are the strings
2003themselves.
2004
2005The returned string (which is dynamically allocated and must be
2006freed when finished with) is derived from the first string in the
2007list that the drawing object expects to be able to display reliably;
2008it will consist of that string translated into the character set
2009expected by \cw{draw_text()}.
2010
2011Drawing implementations are not required to handle anything outside
2012ASCII, but are permitted to assume that \e{some} string will be
2013successfully translated. So every call to this function must include
2014a string somewhere in the list (presumably the last element) which
2015consists of nothing but ASCII, to be used by any front end which
2016cannot handle anything else.
2017
2018For example, if a puzzle wished to display a string including a
2019multiplication sign (U+00D7 in Unicode, represented by the bytes C3
202097 in UTF-8), it might do something like this:
2021
2022\c static const char *const times_signs[] = { "\xC3\x97", "x" };
2023\c char *times_sign = text_fallback(dr, times_signs, 2);
2024\c sprintf(buffer, "%d%s%d", width, times_sign, height);
2025\c draw_text(dr, x, y, font, size, align, colour, buffer);
2026\c sfree(buffer);
2027
2028which would draw a string with a times sign in the middle on
2029platforms that support it, and fall back to a simple ASCII \cq{x}
2030where there was no alternative.
2031
2032\S{drawing-clip} \cw{clip()}
2033
2034\c void clip(drawing *dr, int x, int y, int w, int h);
2035
2036Establishes a clipping rectangle in the puzzle window.
2037
2038\c{x} and \c{y} give the coordinates of the top left pixel of the
2039clipping rectangle. \c{w} and \c{h} give its width and height. Thus,
2040the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
2041inclusive, and the vertical extent from \c{y} to \c{y+h-1}
2042inclusive. (These are exactly the same semantics as
2043\cw{draw_rect()}.)
2044
2045After this call, no drawing operation will affect anything outside
2046the specified rectangle. The effect can be reversed by calling
2047\cw{unclip()} (\k{drawing-unclip}). The clipping rectangle is
2048pixel-perfect: pixels within the rectangle are affected as usual by
2049drawing functions; pixels outside are completely untouched.
2050
2051Back ends should not assume that a clipping rectangle will be
2052automatically cleared up by the front end if it's left lying around;
2053that might work on current front ends, but shouldn't be relied upon.
2054Always explicitly call \cw{unclip()}.
2055
2056This function may be used for both drawing and printing.
2057
2058\S{drawing-unclip} \cw{unclip()}
2059
2060\c void unclip(drawing *dr);
2061
2062Reverts the effect of a previous call to \cw{clip()}. After this
2063call, all drawing operations will be able to affect the entire
2064puzzle window again.
2065
2066This function may be used for both drawing and printing.
2067
2068\S{drawing-draw-update} \cw{draw_update()}
2069
2070\c void draw_update(drawing *dr, int x, int y, int w, int h);
2071
2072Informs the front end that a rectangular portion of the puzzle
2073window has been drawn on and needs to be updated.
2074
2075\c{x} and \c{y} give the coordinates of the top left pixel of the
2076update rectangle. \c{w} and \c{h} give its width and height. Thus,
2077the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
2078inclusive, and the vertical extent from \c{y} to \c{y+h-1}
2079inclusive. (These are exactly the same semantics as
2080\cw{draw_rect()}.)
2081
2082The back end redraw function \e{must} call this function to report
2083any changes it has made to the window. Otherwise, those changes may
2084not become immediately visible, and may then appear at an
2085unpredictable subsequent time such as the next time the window is
2086covered and re-exposed.
2087
2088This function is only important when drawing. It may be called when
2089printing as well, but doing so is not compulsory, and has no effect.
2090(So if you have a shared piece of code between the drawing and
2091printing routines, that code may safely call \cw{draw_update()}.)
2092
2093\S{drawing-status-bar} \cw{status_bar()}
2094
2095\c void status_bar(drawing *dr, char *text);
2096
2097Sets the text in the game's status bar to \c{text}. The text is copied
2098from the supplied buffer, so the caller is free to deallocate or
2099modify the buffer after use.
2100
2101(This function is not exactly a \e{drawing} function, but it shares
2102with the drawing API the property that it may only be called from
2103within the back end redraw function, so this is as good a place as
2104any to document it.)
2105
2106The supplied text is filtered through the mid-end for optional
2107rewriting before being passed on to the front end; the mid-end will
2108prepend the current game time if the game is timed (and may in
2109future perform other rewriting if it seems like a good idea).
2110
2111This function is for drawing only; it must never be called during
2112printing.
2113
2114\S{drawing-blitter} Blitter functions
2115
2116This section describes a group of related functions which save and
2117restore a section of the puzzle window. This is most commonly used
2118to implement user interfaces involving dragging a puzzle element
2119around the window: at the end of each call to \cw{redraw()}, if an
2120object is currently being dragged, the back end saves the window
2121contents under that location and then draws the dragged object, and
2122at the start of the next \cw{redraw()} the first thing it does is to
2123restore the background.
2124
2125The front end defines an opaque type called a \c{blitter}, which is
2126capable of storing a rectangular area of a specified size.
2127
2128Blitter functions are for drawing only; they must never be called
2129during printing.
2130
2131\S2{drawing-blitter-new} \cw{blitter_new()}
2132
2133\c blitter *blitter_new(drawing *dr, int w, int h);
2134
2135Creates a new blitter object which stores a rectangle of size \c{w}
2136by \c{h} pixels. Returns a pointer to the blitter object.
2137
2138Blitter objects are best stored in the \c{game_drawstate}. A good
2139time to create them is in the \cw{set_size()} function
2140(\k{backend-set-size}), since it is at this point that you first
2141know how big a rectangle they will need to save.
2142
2143\S2{drawing-blitter-free} \cw{blitter_free()}
2144
2145\c void blitter_free(drawing *dr, blitter *bl);
2146
2147Disposes of a blitter object. Best called in \cw{free_drawstate()}.
2148(However, check that the blitter object is not \cw{NULL} before
2149attempting to free it; it is possible that a draw state might be
2150created and freed without ever having \cw{set_size()} called on it
2151in between.)
2152
2153\S2{drawing-blitter-save} \cw{blitter_save()}
2154
2155\c void blitter_save(drawing *dr, blitter *bl, int x, int y);
2156
2157This is a true drawing API function, in that it may only be called
2158from within the game redraw routine. It saves a rectangular portion
2159of the puzzle window into the specified blitter object.
2160
2161\c{x} and \c{y} give the coordinates of the top left corner of the
2162saved rectangle. The rectangle's width and height are the ones
2163specified when the blitter object was created.
2164
2165This function is required to cope and do the right thing if \c{x}
2166and \c{y} are out of range. (The right thing probably means saving
2167whatever part of the blitter rectangle overlaps with the visible
2168area of the puzzle window.)
2169
2170\S2{drawing-blitter-load} \cw{blitter_load()}
2171
2172\c void blitter_load(drawing *dr, blitter *bl, int x, int y);
2173
2174This is a true drawing API function, in that it may only be called
2175from within the game redraw routine. It restores a rectangular
2176portion of the puzzle window from the specified blitter object.
2177
2178\c{x} and \c{y} give the coordinates of the top left corner of the
2179rectangle to be restored. The rectangle's width and height are the
2180ones specified when the blitter object was created.
2181
2182Alternatively, you can specify both \c{x} and \c{y} as the special
2183value \cw{BLITTER_FROMSAVED}, in which case the rectangle will be
2184restored to exactly where it was saved from. (This is probably what
2185you want to do almost all the time, if you're using blitters to
2186implement draggable puzzle elements.)
2187
2188This function is required to cope and do the right thing if \c{x}
2189and \c{y} (or the equivalent ones saved in the blitter) are out of
2190range. (The right thing probably means restoring whatever part of
2191the blitter rectangle overlaps with the visible area of the puzzle
2192window.)
2193
2194If this function is called on a blitter which had previously been
2195saved from a partially out-of-range rectangle, then the parts of the
2196saved bitmap which were not visible at save time are undefined. If
2197the blitter is restored to a different position so as to make those
2198parts visible, the effect on the drawing area is undefined.
2199
2200\S{print-mono-colour} \cw{print_mono_colour()}
2201
2202\c int print_mono_colour(drawing *dr, int grey);
2203
2204This function allocates a colour index for a simple monochrome
2205colour during printing.
2206
2207\c{grey} must be 0 or 1. If \c{grey} is 0, the colour returned is
2208black; if \c{grey} is 1, the colour is white.
2209
2210\S{print-grey-colour} \cw{print_grey_colour()}
2211
2212\c int print_grey_colour(drawing *dr, float grey);
2213
2214This function allocates a colour index for a grey-scale colour
2215during printing.
2216
2217\c{grey} may be any number between 0 (black) and 1 (white); for
2218example, 0.5 indicates a medium grey.
2219
2220The chosen colour will be rendered to the limits of the printer's
2221halftoning capability.
2222
2223\S{print-hatched-colour} \cw{print_hatched_colour()}
2224
2225\c int print_hatched_colour(drawing *dr, int hatch);
2226
2227This function allocates a colour index which does not represent a
2228literal \e{colour}. Instead, regions shaded in this colour will be
2229hatched with parallel lines. The \c{hatch} parameter defines what
2230type of hatching should be used in place of this colour:
2231
2232\dt \cw{HATCH_SLASH}
2233
2234\dd This colour will be hatched by lines slanting to the right at 45
2235degrees.
2236
2237\dt \cw{HATCH_BACKSLASH}
2238
2239\dd This colour will be hatched by lines slanting to the left at 45
2240degrees.
2241
2242\dt \cw{HATCH_HORIZ}
2243
2244\dd This colour will be hatched by horizontal lines.
2245
2246\dt \cw{HATCH_VERT}
2247
2248\dd This colour will be hatched by vertical lines.
2249
2250\dt \cw{HATCH_PLUS}
2251
2252\dd This colour will be hatched by criss-crossing horizontal and
2253vertical lines.
2254
2255\dt \cw{HATCH_X}
2256
2257\dd This colour will be hatched by criss-crossing diagonal lines.
2258
2259Colours defined to use hatching may not be used for drawing lines or
2260text; they may only be used for filling areas. That is, they may be
2261used as the \c{fillcolour} parameter to \cw{draw_circle()} and
2262\cw{draw_polygon()}, and as the colour parameter to
2263\cw{draw_rect()}, but may not be used as the \c{outlinecolour}
2264parameter to \cw{draw_circle()} or \cw{draw_polygon()}, or with
2265\cw{draw_line()} or \cw{draw_text()}.
2266
2267\S{print-rgb-mono-colour} \cw{print_rgb_mono_colour()}
2268
2269\c int print_rgb_mono_colour(drawing *dr, float r, float g,
2270\c float b, float grey);
2271
2272This function allocates a colour index for a fully specified RGB
2273colour during printing.
2274
2275\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2276
2277If printing in black and white only, these values will be ignored,
2278and either pure black or pure white will be used instead, according
2279to the \q{grey} parameter. (The fallback colour is the same as the
2280one which would be allocated by \cw{print_mono_colour(grey)}.)
2281
2282\S{print-rgb-grey-colour} \cw{print_rgb_grey_colour()}
2283
2284\c int print_rgb_grey_colour(drawing *dr, float r, float g,
2285\c float b, float grey);
2286
2287This function allocates a colour index for a fully specified RGB
2288colour during printing.
2289
2290\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2291
2292If printing in black and white only, these values will be ignored,
2293and a shade of grey given by the \c{grey} parameter will be used
2294instead. (The fallback colour is the same as the one which would be
2295allocated by \cw{print_grey_colour(grey)}.)
2296
2297\S{print-rgb-hatched-colour} \cw{print_rgb_hatched_colour()}
2298
2299\c int print_rgb_hatched_colour(drawing *dr, float r, float g,
2300\c float b, float hatched);
2301
2302This function allocates a colour index for a fully specified RGB
2303colour during printing.
2304
2305\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2306
2307If printing in black and white only, these values will be ignored,
2308and a form of cross-hatching given by the \c{hatch} parameter will
2309be used instead; see \k{print-hatched-colour} for the possible
2310values of this parameter. (The fallback colour is the same as the
2311one which would be allocated by \cw{print_hatched_colour(hatch)}.)
2312
2313\S{print-line-width} \cw{print_line_width()}
2314
2315\c void print_line_width(drawing *dr, int width);
2316
2317This function is called to set the thickness of lines drawn during
2318printing. It is meaningless in drawing: all lines drawn by
2319\cw{draw_line()}, \cw{draw_circle} and \cw{draw_polygon()} are one
2320pixel in thickness. However, in printing there is no clear
2321definition of a pixel and so line widths must be explicitly
2322specified.
2323
2324The line width is specified in the usual coordinate system. Note,
2325however, that it is a hint only: the central printing system may
2326choose to vary line thicknesses at user request or due to printer
2327capabilities.
2328
2329\S{print-line-dotted} \cw{print_line_dotted()}
2330
2331\c void print_line_dotted(drawing *dr, int dotted);
2332
2333This function is called to toggle the drawing of dotted lines during
2334printing. It is not supported during drawing.
2335
2336The parameter \cq{dotted} is a boolean; \cw{TRUE} means that future
2337lines drawn by \cw{draw_line()}, \cw{draw_circle} and
2338\cw{draw_polygon()} will be dotted, and \cw{FALSE} means that they
2339will be solid.
2340
2341Some front ends may impose restrictions on the width of dotted
2342lines. Asking for a dotted line via this front end will override any
2343line width request if the front end requires it.
2344
2345\H{drawing-frontend} The drawing API as implemented by the front end
2346
2347This section describes the drawing API in the function-pointer form
2348in which it is implemented by a front end.
2349
2350(It isn't only platform-specific front ends which implement this
2351API; the platform-independent module \c{ps.c} also provides an
2352implementation of it which outputs PostScript. Thus, any platform
2353which wants to do PS printing can do so with minimum fuss.)
2354
2355The following entries all describe function pointer fields in a
2356structure called \c{drawing_api}. Each of the functions takes a
2357\cq{void *} context pointer, which it should internally cast back to
2358a more useful type. Thus, a drawing \e{object} (\c{drawing *)}
2359suitable for passing to the back end redraw or printing functions
2360is constructed by passing a \c{drawing_api} and a \cq{void *} to the
2361function \cw{drawing_new()} (see \k{drawing-new}).
2362
2363\S{drawingapi-draw-text} \cw{draw_text()}
2364
2365\c void (*draw_text)(void *handle, int x, int y, int fonttype,
2366\c int fontsize, int align, int colour, char *text);
2367
2368This function behaves exactly like the back end \cw{draw_text()}
2369function; see \k{drawing-draw-text}.
2370
2371\S{drawingapi-draw-rect} \cw{draw_rect()}
2372
2373\c void (*draw_rect)(void *handle, int x, int y, int w, int h,
2374\c int colour);
2375
2376This function behaves exactly like the back end \cw{draw_rect()}
2377function; see \k{drawing-draw-rect}.
2378
2379\S{drawingapi-draw-line} \cw{draw_line()}
2380
2381\c void (*draw_line)(void *handle, int x1, int y1, int x2, int y2,
2382\c int colour);
2383
2384This function behaves exactly like the back end \cw{draw_line()}
2385function; see \k{drawing-draw-line}.
2386
2387\S{drawingapi-draw-polygon} \cw{draw_polygon()}
2388
2389\c void (*draw_polygon)(void *handle, int *coords, int npoints,
2390\c int fillcolour, int outlinecolour);
2391
2392This function behaves exactly like the back end \cw{draw_polygon()}
2393function; see \k{drawing-draw-polygon}.
2394
2395\S{drawingapi-draw-circle} \cw{draw_circle()}
2396
2397\c void (*draw_circle)(void *handle, int cx, int cy, int radius,
2398\c int fillcolour, int outlinecolour);
2399
2400This function behaves exactly like the back end \cw{draw_circle()}
2401function; see \k{drawing-draw-circle}.
2402
2403\S{drawingapi-draw-thick-line} \cw{draw_thick_line()}
2404
2405\c void draw_thick_line(drawing *dr, float thickness,
2406\c float x1, float y1, float x2, float y2,
2407\c int colour)
2408
2409This function behaves exactly like the back end
2410\cw{draw_thick_line()} function; see \k{drawing-draw-thick-line}.
2411
2412An implementation of this API which doesn't provide high-quality
2413rendering of thick lines is permitted to define this function
2414pointer to be \cw{NULL}. The middleware in \cw{drawing.c} will notice
2415and provide a low-quality alternative using \cw{draw_polygon()}.
2416
2417\S{drawingapi-draw-update} \cw{draw_update()}
2418
2419\c void (*draw_update)(void *handle, int x, int y, int w, int h);
2420
2421This function behaves exactly like the back end \cw{draw_update()}
2422function; see \k{drawing-draw-update}.
2423
2424An implementation of this API which only supports printing is
2425permitted to define this function pointer to be \cw{NULL} rather
2426than bothering to define an empty function. The middleware in
2427\cw{drawing.c} will notice and avoid calling it.
2428
2429\S{drawingapi-clip} \cw{clip()}
2430
2431\c void (*clip)(void *handle, int x, int y, int w, int h);
2432
2433This function behaves exactly like the back end \cw{clip()}
2434function; see \k{drawing-clip}.
2435
2436\S{drawingapi-unclip} \cw{unclip()}
2437
2438\c void (*unclip)(void *handle);
2439
2440This function behaves exactly like the back end \cw{unclip()}
2441function; see \k{drawing-unclip}.
2442
2443\S{drawingapi-start-draw} \cw{start_draw()}
2444
2445\c void (*start_draw)(void *handle);
2446
2447This function is called at the start of drawing. It allows the front
2448end to initialise any temporary data required to draw with, such as
2449device contexts.
2450
2451Implementations of this API which do not provide drawing services
2452may define this function pointer to be \cw{NULL}; it will never be
2453called unless drawing is attempted.
2454
2455\S{drawingapi-end-draw} \cw{end_draw()}
2456
2457\c void (*end_draw)(void *handle);
2458
2459This function is called at the end of drawing. It allows the front
2460end to do cleanup tasks such as deallocating device contexts and
2461scheduling appropriate GUI redraw events.
2462
2463Implementations of this API which do not provide drawing services
2464may define this function pointer to be \cw{NULL}; it will never be
2465called unless drawing is attempted.
2466
2467\S{drawingapi-status-bar} \cw{status_bar()}
2468
2469\c void (*status_bar)(void *handle, char *text);
2470
2471This function behaves exactly like the back end \cw{status_bar()}
2472function; see \k{drawing-status-bar}.
2473
2474Front ends implementing this function need not worry about it being
2475called repeatedly with the same text; the middleware code in
2476\cw{status_bar()} will take care of this.
2477
2478Implementations of this API which do not provide drawing services
2479may define this function pointer to be \cw{NULL}; it will never be
2480called unless drawing is attempted.
2481
2482\S{drawingapi-blitter-new} \cw{blitter_new()}
2483
2484\c blitter *(*blitter_new)(void *handle, int w, int h);
2485
2486This function behaves exactly like the back end \cw{blitter_new()}
2487function; see \k{drawing-blitter-new}.
2488
2489Implementations of this API which do not provide drawing services
2490may define this function pointer to be \cw{NULL}; it will never be
2491called unless drawing is attempted.
2492
2493\S{drawingapi-blitter-free} \cw{blitter_free()}
2494
2495\c void (*blitter_free)(void *handle, blitter *bl);
2496
2497This function behaves exactly like the back end \cw{blitter_free()}
2498function; see \k{drawing-blitter-free}.
2499
2500Implementations of this API which do not provide drawing services
2501may define this function pointer to be \cw{NULL}; it will never be
2502called unless drawing is attempted.
2503
2504\S{drawingapi-blitter-save} \cw{blitter_save()}
2505
2506\c void (*blitter_save)(void *handle, blitter *bl, int x, int y);
2507
2508This function behaves exactly like the back end \cw{blitter_save()}
2509function; see \k{drawing-blitter-save}.
2510
2511Implementations of this API which do not provide drawing services
2512may define this function pointer to be \cw{NULL}; it will never be
2513called unless drawing is attempted.
2514
2515\S{drawingapi-blitter-load} \cw{blitter_load()}
2516
2517\c void (*blitter_load)(void *handle, blitter *bl, int x, int y);
2518
2519This function behaves exactly like the back end \cw{blitter_load()}
2520function; see \k{drawing-blitter-load}.
2521
2522Implementations of this API which do not provide drawing services
2523may define this function pointer to be \cw{NULL}; it will never be
2524called unless drawing is attempted.
2525
2526\S{drawingapi-begin-doc} \cw{begin_doc()}
2527
2528\c void (*begin_doc)(void *handle, int pages);
2529
2530This function is called at the beginning of a printing run. It gives
2531the front end an opportunity to initialise any required printing
2532subsystem. It also provides the number of pages in advance.
2533
2534Implementations of this API which do not provide printing services
2535may define this function pointer to be \cw{NULL}; it will never be
2536called unless printing is attempted.
2537
2538\S{drawingapi-begin-page} \cw{begin_page()}
2539
2540\c void (*begin_page)(void *handle, int number);
2541
2542This function is called during printing, at the beginning of each
2543page. It gives the page number (numbered from 1 rather than 0, so
2544suitable for use in user-visible contexts).
2545
2546Implementations of this API which do not provide printing services
2547may define this function pointer to be \cw{NULL}; it will never be
2548called unless printing is attempted.
2549
2550\S{drawingapi-begin-puzzle} \cw{begin_puzzle()}
2551
2552\c void (*begin_puzzle)(void *handle, float xm, float xc,
2553\c float ym, float yc, int pw, int ph, float wmm);
2554
2555This function is called during printing, just before printing a
2556single puzzle on a page. It specifies the size and location of the
2557puzzle on the page.
2558
2559\c{xm} and \c{xc} specify the horizontal position of the puzzle on
2560the page, as a linear function of the page width. The front end is
2561expected to multiply the page width by \c{xm}, add \c{xc} (measured
2562in millimetres), and use the resulting x-coordinate as the left edge
2563of the puzzle.
2564
2565Similarly, \c{ym} and \c{yc} specify the vertical position of the
2566puzzle as a function of the page height: the page height times
2567\c{ym}, plus \c{yc} millimetres, equals the desired distance from
2568the top of the page to the top of the puzzle.
2569
2570(This unwieldy mechanism is required because not all printing
2571systems can communicate the page size back to the software. The
2572PostScript back end, for example, writes out PS which determines the
2573page size at print time by means of calling \cq{clippath}, and
2574centres the puzzles within that. Thus, exactly the same PS file
2575works on A4 or on US Letter paper without needing local
2576configuration, which simplifies matters.)
2577
2578\cw{pw} and \cw{ph} give the size of the puzzle in drawing API
2579coordinates. The printing system will subsequently call the puzzle's
2580own print function, which will in turn call drawing API functions in
2581the expectation that an area \cw{pw} by \cw{ph} units is available
2582to draw the puzzle on.
2583
2584Finally, \cw{wmm} gives the desired width of the puzzle in
2585millimetres. (The aspect ratio is expected to be preserved, so if
2586the desired puzzle height is also needed then it can be computed as
2587\cw{wmm*ph/pw}.)
2588
2589Implementations of this API which do not provide printing services
2590may define this function pointer to be \cw{NULL}; it will never be
2591called unless printing is attempted.
2592
2593\S{drawingapi-end-puzzle} \cw{end_puzzle()}
2594
2595\c void (*end_puzzle)(void *handle);
2596
2597This function is called after the printing of a specific puzzle is
2598complete.
2599
2600Implementations of this API which do not provide printing services
2601may define this function pointer to be \cw{NULL}; it will never be
2602called unless printing is attempted.
2603
2604\S{drawingapi-end-page} \cw{end_page()}
2605
2606\c void (*end_page)(void *handle, int number);
2607
2608This function is called after the printing of a page is finished.
2609
2610Implementations of this API which do not provide printing services
2611may define this function pointer to be \cw{NULL}; it will never be
2612called unless printing is attempted.
2613
2614\S{drawingapi-end-doc} \cw{end_doc()}
2615
2616\c void (*end_doc)(void *handle);
2617
2618This function is called after the printing of the entire document is
2619finished. This is the moment to close files, send things to the
2620print spooler, or whatever the local convention is.
2621
2622Implementations of this API which do not provide printing services
2623may define this function pointer to be \cw{NULL}; it will never be
2624called unless printing is attempted.
2625
2626\S{drawingapi-line-width} \cw{line_width()}
2627
2628\c void (*line_width)(void *handle, float width);
2629
2630This function is called to set the line thickness, during printing
2631only. Note that the width is a \cw{float} here, where it was an
2632\cw{int} as seen by the back end. This is because \cw{drawing.c} may
2633have scaled it on the way past.
2634
2635However, the width is still specified in the same coordinate system
2636as the rest of the drawing.
2637
2638Implementations of this API which do not provide printing services
2639may define this function pointer to be \cw{NULL}; it will never be
2640called unless printing is attempted.
2641
2642\S{drawingapi-text-fallback} \cw{text_fallback()}
2643
2644\c char *(*text_fallback)(void *handle, const char *const *strings,
2645\c int nstrings);
2646
2647This function behaves exactly like the back end \cw{text_fallback()}
2648function; see \k{drawing-text-fallback}.
2649
2650Implementations of this API which do not support any characters
2651outside ASCII may define this function pointer to be \cw{NULL}, in
2652which case the central code in \cw{drawing.c} will provide a default
2653implementation.
2654
2655\H{drawingapi-frontend} The drawing API as called by the front end
2656
2657There are a small number of functions provided in \cw{drawing.c}
2658which the front end needs to \e{call}, rather than helping to
2659implement. They are described in this section.
2660
2661\S{drawing-new} \cw{drawing_new()}
2662
2663\c drawing *drawing_new(const drawing_api *api, midend *me,
2664\c void *handle);
2665
2666This function creates a drawing object. It is passed a
2667\c{drawing_api}, which is a structure containing nothing but
2668function pointers; and also a \cq{void *} handle. The handle is
2669passed back to each function pointer when it is called.
2670
2671The \c{midend} parameter is used for rewriting the status bar
2672contents: \cw{status_bar()} (see \k{drawing-status-bar}) has to call
2673a function in the mid-end which might rewrite the status bar text.
2674If the drawing object is to be used only for printing, or if the
2675game is known not to call \cw{status_bar()}, this parameter may be
2676\cw{NULL}.
2677
2678\S{drawing-free} \cw{drawing_free()}
2679
2680\c void drawing_free(drawing *dr);
2681
2682This function frees a drawing object. Note that the \cq{void *}
2683handle is not freed; if that needs cleaning up it must be done by
2684the front end.
2685
2686\S{drawing-print-get-colour} \cw{print_get_colour()}
2687
2688\c void print_get_colour(drawing *dr, int colour, int printincolour,
2689\c int *hatch, float *r, float *g, float *b)
2690
2691This function is called by the implementations of the drawing API
2692functions when they are called in a printing context. It takes a
2693colour index as input, and returns the description of the colour as
2694requested by the back end.
2695
2696\c{printincolour} is \cw{TRUE} iff the implementation is printing in
2697colour. This will alter the results returned if the colour in
2698question was specified with a black-and-white fallback value.
2699
2700If the colour should be rendered by hatching, \c{*hatch} is filled
2701with the type of hatching desired. See \k{print-grey-colour} for
2702details of the values this integer can take.
2703
2704If the colour should be rendered as solid colour, \c{*hatch} is
2705given a negative value, and \c{*r}, \c{*g} and \c{*b} are filled
2706with the RGB values of the desired colour (if printing in colour),
2707or all filled with the grey-scale value (if printing in black and
2708white).
2709
2710\C{midend} The API provided by the mid-end
2711
2712This chapter documents the API provided by the mid-end to be called
2713by the front end. You probably only need to read this if you are a
2714front end implementor, i.e. you are porting Puzzles to a new
2715platform. If you're only interested in writing new puzzles, you can
2716safely skip this chapter.
2717
2718All the persistent state in the mid-end is encapsulated within a
2719\c{midend} structure, to facilitate having multiple mid-ends in any
2720port which supports multiple puzzle windows open simultaneously.
2721Each \c{midend} is intended to handle the contents of a single
2722puzzle window.
2723
2724\H{midend-new} \cw{midend_new()}
2725
2726\c midend *midend_new(frontend *fe, const game *ourgame,
2727\c const drawing_api *drapi, void *drhandle)
2728
2729Allocates and returns a new mid-end structure.
2730
2731The \c{fe} argument is stored in the mid-end. It will be used when
2732calling back to functions such as \cw{activate_timer()}
2733(\k{frontend-activate-timer}), and will be passed on to the back end
2734function \cw{colours()} (\k{backend-colours}).
2735
2736The parameters \c{drapi} and \c{drhandle} are passed to
2737\cw{drawing_new()} (\k{drawing-new}) to construct a drawing object
2738which will be passed to the back end function \cw{redraw()}
2739(\k{backend-redraw}). Hence, all drawing-related function pointers
2740defined in \c{drapi} can expect to be called with \c{drhandle} as
2741their first argument.
2742
2743The \c{ourgame} argument points to a container structure describing
2744a game back end. The mid-end thus created will only be capable of
2745handling that one game. (So even in a monolithic front end
2746containing all the games, this imposes the constraint that any
2747individual puzzle window is tied to a single game. Unless, of
2748course, you feel brave enough to change the mid-end for the window
2749without closing the window...)
2750
2751\H{midend-free} \cw{midend_free()}
2752
2753\c void midend_free(midend *me);
2754
2755Frees a mid-end structure and all its associated data.
2756
2757\H{midend-tilesize} \cw{midend_tilesize()}
2758
2759\c int midend_tilesize(midend *me);
2760
2761Returns the \cq{tilesize} parameter being used to display the
2762current puzzle (\k{backend-preferred-tilesize}).
2763
2764\H{midend-set-params} \cw{midend_set_params()}
2765
2766\c void midend_set_params(midend *me, game_params *params);
2767
2768Sets the current game parameters for a mid-end. Subsequent games
2769generated by \cw{midend_new_game()} (\k{midend-new-game}) will use
2770these parameters until further notice.
2771
2772The usual way in which the front end will have an actual
2773\c{game_params} structure to pass to this function is if it had
2774previously got it from \cw{midend_get_presets()}
2775(\k{midend-get-presets}). Thus, this function is usually called in
2776response to the user making a selection from the presets menu.
2777
2778\H{midend-get-params} \cw{midend_get_params()}
2779
2780\c game_params *midend_get_params(midend *me);
2781
2782Returns the current game parameters stored in this mid-end.
2783
2784The returned value is dynamically allocated, and should be freed
2785when finished with by passing it to the game's own
2786\cw{free_params()} function (see \k{backend-free-params}).
2787
2788\H{midend-size} \cw{midend_size()}
2789
2790\c void midend_size(midend *me, int *x, int *y, int user_size);
2791
2792Tells the mid-end to figure out its window size.
2793
2794On input, \c{*x} and \c{*y} should contain the maximum or requested
2795size for the window. (Typically this will be the size of the screen
2796that the window has to fit on, or similar.) The mid-end will
2797repeatedly call the back end function \cw{compute_size()}
2798(\k{backend-compute-size}), searching for a tile size that best
2799satisfies the requirements. On exit, \c{*x} and \c{*y} will contain
2800the size needed for the puzzle window's drawing area. (It is of
2801course up to the front end to adjust this for any additional window
2802furniture such as menu bars and window borders, if necessary. The
2803status bar is also not included in this size.)
2804
2805Use \c{user_size} to indicate whether \c{*x} and \c{*y} are a
2806requested size, or just a maximum size.
2807
2808If \c{user_size} is set to \cw{TRUE}, the mid-end will treat the
2809input size as a request, and will pick a tile size which
2810approximates it \e{as closely as possible}, going over the game's
2811preferred tile size if necessary to achieve this. The mid-end will
2812also use the resulting tile size as its preferred one until further
2813notice, on the assumption that this size was explicitly requested
2814by the user. Use this option if you want your front end to support
2815dynamic resizing of the puzzle window with automatic scaling of the
2816puzzle to fit.
2817
2818If \c{user_size} is set to \cw{FALSE}, then the game's tile size
2819will never go over its preferred one, although it may go under in
2820order to fit within the maximum bounds specified by \c{*x} and
2821\c{*y}. This is the recommended approach when opening a new window
2822at default size: the game will use its preferred size unless it has
2823to use a smaller one to fit on the screen. If the tile size is
2824shrunk for this reason, the change will not persist; if a smaller
2825grid is subsequently chosen, the tile size will recover.
2826
2827The mid-end will try as hard as it can to return a size which is
2828less than or equal to the input size, in both dimensions. In extreme
2829circumstances it may fail (if even the lowest possible tile size
2830gives window dimensions greater than the input), in which case it
2831will return a size greater than the input size. Front ends should be
2832prepared for this to happen (i.e. don't crash or fail an assertion),
2833but may handle it in any way they see fit: by rejecting the game
2834parameters which caused the problem, by opening a window larger than
2835the screen regardless of inconvenience, by introducing scroll bars
2836on the window, by drawing on a large bitmap and scaling it into a
2837smaller window, or by any other means you can think of. It is likely
2838that when the tile size is that small the game will be unplayable
2839anyway, so don't put \e{too} much effort into handling it
2840creatively.
2841
2842If your platform has no limit on window size (or if you're planning
2843to use scroll bars for large puzzles), you can pass dimensions of
2844\cw{INT_MAX} as input to this function. You should probably not do
2845that \e{and} set the \c{user_size} flag, though!
2846
2847The midend relies on the frontend calling \cw{midend_new_game()}
2848(\k{midend-new-game}) before calling \cw{midend_size()}.
2849
2850\H{midend-reset-tilesize} \cw{midend_reset_tilesize()}
2851
2852\c void midend_reset_tilesize(midend *me);
2853
2854This function resets the midend's preferred tile size to that of the
2855standard puzzle.
2856
2857As discussed in \k{midend-size}, puzzle resizes are typically
2858'sticky', in that once the user has dragged the puzzle to a different
2859window size, the resulting tile size will be remembered and used when
2860the puzzle configuration changes. If you \e{don't} want that, e.g. if
2861you want to provide a command to explicitly reset the puzzle size back
2862to its default, then you can call this just before calling
2863\cw{midend_size()} (which, in turn, you would probably call with
2864\c{user_size} set to \cw{FALSE}).
2865
2866\H{midend-new-game} \cw{midend_new_game()}
2867
2868\c void midend_new_game(midend *me);
2869
2870Causes the mid-end to begin a new game. Normally the game will be a
2871new randomly generated puzzle. However, if you have previously
2872called \cw{midend_game_id()} or \cw{midend_set_config()}, the game
2873generated might be dictated by the results of those functions. (In
2874particular, you \e{must} call \cw{midend_new_game()} after calling
2875either of those functions, or else no immediate effect will be
2876visible.)
2877
2878You will probably need to call \cw{midend_size()} after calling this
2879function, because if the game parameters have been changed since the
2880last new game then the window size might need to change. (If you
2881know the parameters \e{haven't} changed, you don't need to do this.)
2882
2883This function will create a new \c{game_drawstate}, but does not
2884actually perform a redraw (since you often need to call
2885\cw{midend_size()} before the redraw can be done). So after calling
2886this function and after calling \cw{midend_size()}, you should then
2887call \cw{midend_redraw()}. (It is not necessary to call
2888\cw{midend_force_redraw()}; that will discard the draw state and
2889create a fresh one, which is unnecessary in this case since there's
2890a fresh one already. It would work, but it's usually excessive.)
2891
2892\H{midend-restart-game} \cw{midend_restart_game()}
2893
2894\c void midend_restart_game(midend *me);
2895
2896This function causes the current game to be restarted. This is done
2897by placing a new copy of the original game state on the end of the
2898undo list (so that an accidental restart can be undone).
2899
2900This function automatically causes a redraw, i.e. the front end can
2901expect its drawing API to be called from \e{within} a call to this
2902function. Some back ends require that \cw{midend_size()}
2903(\k{midend-size}) is called before \cw{midend_restart_game()}.
2904
2905\H{midend-force-redraw} \cw{midend_force_redraw()}
2906
2907\c void midend_force_redraw(midend *me);
2908
2909Forces a complete redraw of the puzzle window, by means of
2910discarding the current \c{game_drawstate} and creating a new one
2911from scratch before calling the game's \cw{redraw()} function.
2912
2913The front end can expect its drawing API to be called from within a
2914call to this function. Some back ends require that \cw{midend_size()}
2915(\k{midend-size}) is called before \cw{midend_force_redraw()}.
2916
2917\H{midend-redraw} \cw{midend_redraw()}
2918
2919\c void midend_redraw(midend *me);
2920
2921Causes a partial redraw of the puzzle window, by means of simply
2922calling the game's \cw{redraw()} function. (That is, the only things
2923redrawn will be things that have changed since the last redraw.)
2924
2925The front end can expect its drawing API to be called from within a
2926call to this function. Some back ends require that \cw{midend_size()}
2927(\k{midend-size}) is called before \cw{midend_redraw()}.
2928
2929\H{midend-process-key} \cw{midend_process_key()}
2930
2931\c int midend_process_key(midend *me, int x, int y, int button);
2932
2933The front end calls this function to report a mouse or keyboard
2934event. The parameters \c{x}, \c{y} and \c{button} are almost
2935identical to the ones passed to the back end function
2936\cw{interpret_move()} (\k{backend-interpret-move}), except that the
2937front end is \e{not} required to provide the guarantees about mouse
2938event ordering. The mid-end will sort out multiple simultaneous
2939button presses and changes of button; the front end's responsibility
2940is simply to pass on the mouse events it receives as accurately as
2941possible.
2942
2943(Some platforms may need to emulate absent mouse buttons by means of
2944using a modifier key such as Shift with another mouse button. This
2945tends to mean that if Shift is pressed or released in the middle of
2946a mouse drag, the mid-end will suddenly stop receiving, say,
2947\cw{LEFT_DRAG} events and start receiving \cw{RIGHT_DRAG}s, with no
2948intervening button release or press events. This too is something
2949which the mid-end will sort out for you; the front end has no
2950obligation to maintain sanity in this area.)
2951
2952The front end \e{should}, however, always eventually send some kind
2953of button release. On some platforms this requires special effort:
2954Windows, for example, requires a call to the system API function
2955\cw{SetCapture()} in order to ensure that your window receives a
2956mouse-up event even if the pointer has left the window by the time
2957the mouse button is released. On any platform that requires this
2958sort of thing, the front end \e{is} responsible for doing it.
2959
2960Calling this function is very likely to result in calls back to the
2961front end's drawing API and/or \cw{activate_timer()}
2962(\k{frontend-activate-timer}).
2963
2964The return value from \cw{midend_process_key()} is non-zero, unless
2965the effect of the keypress was to request termination of the
2966program. A front end should shut down the puzzle in response to a
2967zero return.
2968
2969\H{midend-colours} \cw{midend_colours()}
2970
2971\c float *midend_colours(midend *me, int *ncolours);
2972
2973Returns an array of the colours required by the game, in exactly the
2974same format as that returned by the back end function \cw{colours()}
2975(\k{backend-colours}). Front ends should call this function rather
2976than calling the back end's version directly, since the mid-end adds
2977standard customisation facilities. (At the time of writing, those
2978customisation facilities are implemented hackily by means of
2979environment variables, but it's not impossible that they may become
2980more full and formal in future.)
2981
2982\H{midend-timer} \cw{midend_timer()}
2983
2984\c void midend_timer(midend *me, float tplus);
2985
2986If the mid-end has called \cw{activate_timer()}
2987(\k{frontend-activate-timer}) to request regular callbacks for
2988purposes of animation or timing, this is the function the front end
2989should call on a regular basis. The argument \c{tplus} gives the
2990time, in seconds, since the last time either this function was
2991called or \cw{activate_timer()} was invoked.
2992
2993One of the major purposes of timing in the mid-end is to perform
2994move animation. Therefore, calling this function is very likely to
2995result in calls back to the front end's drawing API.
2996
2997\H{midend-get-presets} \cw{midend_get_presets()}
2998
2999\c struct preset_menu *midend_get_presets(midend *me, int *id_limit);
3000
3001Returns a data structure describing this game's collection of preset
3002game parameters, organised into a hierarchical structure of menus and
3003submenus.
3004
3005The return value is a pointer to a data structure containing the
3006following fields (among others, which are not intended for front end
3007use):
3008
3009\c struct preset_menu {
3010\c int n_entries;
3011\c struct preset_menu_entry *entries;
3012\c /* and other things */
3013\e iiiiiiiiiiiiiiiiiiiiii
3014\c };
3015
3016Those fields describe the intended contents of one particular menu in
3017the hierarchy. \cq{entries} points to an array of \cq{n_entries}
3018items, each of which is a structure containing the following fields:
3019
3020\c struct preset_menu_entry {
3021\c char *title;
3022\c game_params *params;
3023\c struct preset_menu *submenu;
3024\c int id;
3025\c };
3026
3027Of these fields, \cq{title} and \cq{id} are present in every entry,
3028giving (respectively) the textual name of the menu item and an integer
3029identifier for it. The integer id will correspond to the one returned
3030by \c{midend_which_preset} (\k{midend-which-preset}), when that preset
3031is the one selected.
3032
3033The other two fields are mutually exclusive. Each \c{struct
3034preset_menu_entry} will have one of those fields \cw{NULL} and the
3035other one non-null. If the menu item is an actual preset, then
3036\cq{params} will point to the set of game parameters that go with the
3037name; if it's a submenu, then \cq{submenu} instead will be non-null,
3038and will point at a subsidiary \c{struct preset_menu}.
3039
3040The complete hierarchy of these structures is owned by the mid-end,
3041and will be freed when the mid-end is freed. The front end should not
3042attempt to free any of it.
3043
3044The integer identifiers will be allocated densely from 0 upwards, so
3045that it's reasonable for the front end to allocate an array which uses
3046them as indices, if it needs to store information per preset menu
3047item. For this purpose, the front end may pass the second parameter
3048\cq{id_limit} to \cw{midend_get_presets} as the address of an \c{int}
3049variable, into which \cw{midend_get_presets} will write an integer one
3050larger than the largest id number actually used (i.e. the number of
3051elements the front end would need in the array).
3052
3053Submenu-type entries also have integer identifiers.
3054
3055\H{midend-which-preset} \cw{midend_which_preset()}
3056
3057\c int midend_which_preset(midend *me);
3058
3059Returns the numeric index of the preset game parameter structure
3060which matches the current game parameters, or a negative number if
3061no preset matches. Front ends could use this to maintain a tick
3062beside one of the items in the menu (or tick the \q{Custom} option
3063if the return value is less than zero).
3064
3065The returned index value (if non-negative) will match the \c{id} field
3066of the corresponding \cw{struct preset_menu_entry} returned by
3067\c{midend_get_presets()} (\k{midend-get-presets}).
3068
3069\H{midend-wants-statusbar} \cw{midend_wants_statusbar()}
3070
3071\c int midend_wants_statusbar(midend *me);
3072
3073This function returns \cw{TRUE} if the puzzle has a use for a
3074textual status line (to display score, completion status, currently
3075active tiles, time, or anything else).
3076
3077Front ends should call this function rather than talking directly to
3078the back end.
3079
3080\H{midend-get-config} \cw{midend_get_config()}
3081
3082\c config_item *midend_get_config(midend *me, int which,
3083\c char **wintitle);
3084
3085Returns a dialog box description for user configuration.
3086
3087On input, \cw{which} should be set to one of three values, which
3088select which of the various dialog box descriptions is returned:
3089
3090\dt \cw{CFG_SETTINGS}
3091
3092\dd Requests the GUI parameter configuration box generated by the
3093puzzle itself. This should be used when the user selects \q{Custom}
3094from the game types menu (or equivalent). The mid-end passes this
3095request on to the back end function \cw{configure()}
3096(\k{backend-configure}).
3097
3098\dt \cw{CFG_DESC}
3099
3100\dd Requests a box suitable for entering a descriptive game ID (and
3101viewing the existing one). The mid-end generates this dialog box
3102description itself. This should be used when the user selects
3103\q{Specific} from the game menu (or equivalent).
3104
3105\dt \cw{CFG_SEED}
3106
3107\dd Requests a box suitable for entering a random-seed game ID (and
3108viewing the existing one). The mid-end generates this dialog box
3109description itself. This should be used when the user selects
3110\q{Random Seed} from the game menu (or equivalent).
3111
3112The returned value is an array of \cw{config_item}s, exactly as
3113described in \k{backend-configure}. Another returned value is an
3114ASCII string giving a suitable title for the configuration window,
3115in \c{*wintitle}.
3116
3117Both returned values are dynamically allocated and will need to be
3118freed. The window title can be freed in the obvious way; the
3119\cw{config_item} array is a slightly complex structure, so a utility
3120function \cw{free_cfg()} is provided to free it for you. See
3121\k{utils-free-cfg}.
3122
3123(Of course, you will probably not want to free the \cw{config_item}
3124array until the dialog box is dismissed, because before then you
3125will probably need to pass it to \cw{midend_set_config}.)
3126
3127\H{midend-set-config} \cw{midend_set_config()}
3128
3129\c char *midend_set_config(midend *me, int which,
3130\c config_item *cfg);
3131
3132Passes the mid-end the results of a configuration dialog box.
3133\c{which} should have the same value which it had when
3134\cw{midend_get_config()} was called; \c{cfg} should be the array of
3135\c{config_item}s returned from \cw{midend_get_config()}, modified to
3136contain the results of the user's editing operations.
3137
3138This function returns \cw{NULL} on success, or otherwise (if the
3139configuration data was in some way invalid) an ASCII string
3140containing an error message suitable for showing to the user.
3141
3142If the function succeeds, it is likely that the game parameters will
3143have been changed and it is certain that a new game will be
3144requested. The front end should therefore call
3145\cw{midend_new_game()}, and probably also re-think the window size
3146using \cw{midend_size()} and eventually perform a refresh using
3147\cw{midend_redraw()}.
3148
3149\H{midend-game-id} \cw{midend_game_id()}
3150
3151\c char *midend_game_id(midend *me, char *id);
3152
3153Passes the mid-end a string game ID (of any of the valid forms
3154\cq{params}, \cq{params:description} or \cq{params#seed}) which the
3155mid-end will process and use for the next generated game.
3156
3157This function returns \cw{NULL} on success, or otherwise (if the
3158configuration data was in some way invalid) an ASCII string
3159containing an error message (not dynamically allocated) suitable for
3160showing to the user. In the event of an error, the mid-end's
3161internal state will be left exactly as it was before the call.
3162
3163If the function succeeds, it is likely that the game parameters will
3164have been changed and it is certain that a new game will be
3165requested. The front end should therefore call
3166\cw{midend_new_game()}, and probably also re-think the window size
3167using \cw{midend_size()} and eventually case a refresh using
3168\cw{midend_redraw()}.
3169
3170\H{midend-get-game-id} \cw{midend_get_game_id()}
3171
3172\c char *midend_get_game_id(midend *me)
3173
3174Returns a descriptive game ID (i.e. one in the form
3175\cq{params:description}) describing the game currently active in the
3176mid-end. The returned string is dynamically allocated.
3177
3178\H{midend-get-random-seed} \cw{midend_get_random_seed()}
3179
3180\c char *midend_get_random_seed(midend *me)
3181
3182Returns a random game ID (i.e. one in the form \cq{params#seedstring})
3183describing the game currently active in the mid-end, if there is one.
3184If the game was created by entering a description, no random seed will
3185currently exist and this function will return \cw{NULL}.
3186
3187The returned string, if it is non-\cw{NULL}, is dynamically allocated.
3188
3189\H{midend-can-format-as-text-now} \cw{midend_can_format_as_text_now()}
3190
3191\c int midend_can_format_as_text_now(midend *me);
3192
3193Returns \cw{TRUE} if the game code is capable of formatting puzzles
3194of the currently selected game type as ASCII.
3195
3196If this returns \cw{FALSE}, then \cw{midend_text_format()}
3197(\k{midend-text-format}) will return \cw{NULL}.
3198
3199\H{midend-text-format} \cw{midend_text_format()}
3200
3201\c char *midend_text_format(midend *me);
3202
3203Formats the current game's current state as ASCII text suitable for
3204copying to the clipboard. The returned string is dynamically
3205allocated.
3206
3207If the game's \c{can_format_as_text_ever} flag is \cw{FALSE}, or if
3208its \cw{can_format_as_text_now()} function returns \cw{FALSE}, then
3209this function will return \cw{NULL}.
3210
3211If the returned string contains multiple lines (which is likely), it
3212will use the normal C line ending convention (\cw{\\n} only). On
3213platforms which use a different line ending convention for data in
3214the clipboard, it is the front end's responsibility to perform the
3215conversion.
3216
3217\H{midend-solve} \cw{midend_solve()}
3218
3219\c char *midend_solve(midend *me);
3220
3221Requests the mid-end to perform a Solve operation.
3222
3223On success, \cw{NULL} is returned. On failure, an error message (not
3224dynamically allocated) is returned, suitable for showing to the
3225user.
3226
3227The front end can expect its drawing API and/or
3228\cw{activate_timer()} to be called from within a call to this
3229function. Some back ends require that \cw{midend_size()}
3230(\k{midend-size}) is called before \cw{midend_solve()}.
3231
3232\H{midend-status} \cw{midend_status()}
3233
3234\c int midend_status(midend *me);
3235
3236This function returns +1 if the midend is currently displaying a game
3237in a solved state, -1 if the game is in a permanently lost state, or 0
3238otherwise. This function just calls the back end's \cw{status()}
3239function. Front ends may wish to use this as a cue to proactively
3240offer the option of starting a new game.
3241
3242(See \k{backend-status} for more detail about the back end's
3243\cw{status()} function and discussion of what should count as which
3244status code.)
3245
3246\H{midend-can-undo} \cw{midend_can_undo()}
3247
3248\c int midend_can_undo(midend *me);
3249
3250Returns \cw{TRUE} if the midend is currently in a state where the undo
3251operation is meaningful (i.e. at least one position exists on the undo
3252chain before the present one). Front ends may wish to use this to
3253visually activate and deactivate an undo button.
3254
3255\H{midend-can-redo} \cw{midend_can_redo()}
3256
3257\c int midend_can_redo(midend *me);
3258
3259Returns \cw{TRUE} if the midend is currently in a state where the redo
3260operation is meaningful (i.e. at least one position exists on the redo
3261chain after the present one). Front ends may wish to use this to
3262visually activate and deactivate a redo button.
3263
3264\H{midend-serialise} \cw{midend_serialise()}
3265
3266\c void midend_serialise(midend *me,
3267\c void (*write)(void *ctx, void *buf, int len),
3268\c void *wctx);
3269
3270Calling this function causes the mid-end to convert its entire
3271internal state into a long ASCII text string, and to pass that
3272string (piece by piece) to the supplied \c{write} function.
3273
3274Desktop implementations can use this function to save a game in any
3275state (including half-finished) to a disk file, by supplying a
3276\c{write} function which is a wrapper on \cw{fwrite()} (or local
3277equivalent). Other implementations may find other uses for it, such
3278as compressing the large and sprawling mid-end state into a
3279manageable amount of memory when a palmtop application is suspended
3280so that another one can run; in this case \cw{write} might want to
3281write to a memory buffer rather than a file. There may be other uses
3282for it as well.
3283
3284This function will call back to the supplied \c{write} function a
3285number of times, with the first parameter (\c{ctx}) equal to
3286\c{wctx}, and the other two parameters pointing at a piece of the
3287output string.
3288
3289\H{midend-deserialise} \cw{midend_deserialise()}
3290
3291\c char *midend_deserialise(midend *me,
3292\c int (*read)(void *ctx, void *buf, int len),
3293\c void *rctx);
3294
3295This function is the counterpart to \cw{midend_serialise()}. It
3296calls the supplied \cw{read} function repeatedly to read a quantity
3297of data, and attempts to interpret that data as a serialised mid-end
3298as output by \cw{midend_serialise()}.
3299
3300The \cw{read} function is called with the first parameter (\c{ctx})
3301equal to \c{rctx}, and should attempt to read \c{len} bytes of data
3302into the buffer pointed to by \c{buf}. It should return \cw{FALSE}
3303on failure or \cw{TRUE} on success. It should not report success
3304unless it has filled the entire buffer; on platforms which might be
3305reading from a pipe or other blocking data source, \c{read} is
3306responsible for looping until the whole buffer has been filled.
3307
3308If the de-serialisation operation is successful, the mid-end's
3309internal data structures will be replaced by the results of the
3310load, and \cw{NULL} will be returned. Otherwise, the mid-end's state
3311will be completely unchanged and an error message (typically some
3312variation on \q{save file is corrupt}) will be returned. As usual,
3313the error message string is not dynamically allocated.
3314
3315If this function succeeds, it is likely that the game parameters
3316will have been changed. The front end should therefore probably
3317re-think the window size using \cw{midend_size()}, and probably
3318cause a refresh using \cw{midend_redraw()}.
3319
3320Because each mid-end is tied to a specific game back end, this
3321function will fail if you attempt to read in a save file generated by
3322a different game from the one configured in this mid-end, even if your
3323application is a monolithic one containing all the puzzles. See
3324\k{identify-game} for a helper function which will allow you to
3325identify a save file before you instantiate your mid-end in the first
3326place.
3327
3328\H{identify-game} \cw{identify_game()}
3329
3330\c char *identify_game(char **name,
3331\c int (*read)(void *ctx, void *buf, int len),
3332\c void *rctx);
3333
3334This function examines a serialised midend stream, of the same kind
3335used by \cw{midend_serialise()} and \cw{midend_deserialise()}, and
3336returns the \cw{name} field of the game back end from which it was
3337saved.
3338
3339You might want this if your front end was a monolithic one containing
3340all the puzzles, and you wanted to be able to load an arbitrary save
3341file and automatically switch to the right game. Probably your next
3342step would be to iterate through \cw{gamelist} (\k{frontend-backend})
3343looking for a game structure whose \cw{name} field matched the
3344returned string, and give an error if you didn't find one.
3345
3346On success, the return value of this function is \cw{NULL}, and the
3347game name string is written into \cw{*name}. The caller should free
3348that string after using it.
3349
3350On failure, \cw{*name} is \cw{NULL}, and the return value is an error
3351message (which does not need freeing at all).
3352
3353(This isn't strictly speaking a midend function, since it doesn't
3354accept or return a pointer to a midend. You'd probably call it just
3355\e{before} deciding what kind of midend you wanted to instantiate.)
3356
3357\H{midend-request-id-changes} \cw{midend_request_id_changes()}
3358
3359\c void midend_request_id_changes(midend *me,
3360\c void (*notify)(void *), void *ctx);
3361
3362This function is called by the front end to request notification by
3363the mid-end when the current game IDs (either descriptive or
3364random-seed) change. This can occur as a result of keypresses ('n' for
3365New Game, for example) or when a puzzle supersedes its game
3366description (see \k{backend-supersede}). After this function is
3367called, any change of the game ids will cause the mid-end to call
3368\cw{notify(ctx)} after the change.
3369
3370This is for use by puzzles which want to present the game description
3371to the user constantly (e.g. as an HTML hyperlink) instead of only
3372showing it when the user explicitly requests it.
3373
3374This is a function I anticipate few front ends needing to implement,
3375so I make it a callback rather than a static function in order to
3376relieve most front ends of the need to provide an empty
3377implementation.
3378
3379\H{frontend-backend} Direct reference to the back end structure by
3380the front end
3381
3382Although \e{most} things the front end needs done should be done by
3383calling the mid-end, there are a few situations in which the front
3384end needs to refer directly to the game back end structure.
3385
3386The most obvious of these is
3387
3388\b passing the game back end as a parameter to \cw{midend_new()}.
3389
3390There are a few other back end features which are not wrapped by the
3391mid-end because there didn't seem much point in doing so:
3392
3393\b fetching the \c{name} field to use in window titles and similar
3394
3395\b reading the \c{can_configure}, \c{can_solve} and
3396\c{can_format_as_text_ever} fields to decide whether to add those
3397items to the menu bar or equivalent
3398
3399\b reading the \c{winhelp_topic} field (Windows only)
3400
3401\b the GTK front end provides a \cq{--generate} command-line option
3402which directly calls the back end to do most of its work. This is
3403not really part of the main front end code, though, and I'm not sure
3404it counts.
3405
3406In order to find the game back end structure, the front end does one
3407of two things:
3408
3409\b If the particular front end is compiling a separate binary per
3410game, then the back end structure is a global variable with the
3411standard name \cq{thegame}:
3412
3413\lcont{
3414
3415\c extern const game thegame;
3416
3417}
3418
3419\b If the front end is compiled as a monolithic application
3420containing all the puzzles together (in which case the preprocessor
3421symbol \cw{COMBINED} must be defined when compiling most of the code
3422base), then there will be two global variables defined:
3423
3424\lcont{
3425
3426\c extern const game *gamelist[];
3427\c extern const int gamecount;
3428
3429\c{gamelist} will be an array of \c{gamecount} game structures,
3430declared in the automatically constructed source module \c{list.c}.
3431The application should search that array for the game it wants,
3432probably by reaching into each game structure and looking at its
3433\c{name} field.
3434
3435}
3436
3437\H{frontend-api} Mid-end to front-end calls
3438
3439This section describes the small number of functions which a front
3440end must provide to be called by the mid-end or other standard
3441utility modules.
3442
3443\H{frontend-get-random-seed} \cw{get_random_seed()}
3444
3445\c void get_random_seed(void **randseed, int *randseedsize);
3446
3447This function is called by a new mid-end, and also occasionally by
3448game back ends. Its job is to return a piece of data suitable for
3449using as a seed for initialisation of a new \c{random_state}.
3450
3451On exit, \c{*randseed} should be set to point at a newly allocated
3452piece of memory containing some seed data, and \c{*randseedsize}
3453should be set to the length of that data.
3454
3455A simple and entirely adequate implementation is to return a piece
3456of data containing the current system time at the highest
3457conveniently available resolution.
3458
3459\H{frontend-activate-timer} \cw{activate_timer()}
3460
3461\c void activate_timer(frontend *fe);
3462
3463This is called by the mid-end to request that the front end begin
3464calling it back at regular intervals.
3465
3466The timeout interval is left up to the front end; the finer it is,
3467the smoother move animations will be, but the more CPU time will be
3468used. Current front ends use values around 20ms (i.e. 50Hz).
3469
3470After this function is called, the mid-end will expect to receive
3471calls to \cw{midend_timer()} on a regular basis.
3472
3473\H{frontend-deactivate-timer} \cw{deactivate_timer()}
3474
3475\c void deactivate_timer(frontend *fe);
3476
3477This is called by the mid-end to request that the front end stop
3478calling \cw{midend_timer()}.
3479
3480\H{frontend-fatal} \cw{fatal()}
3481
3482\c void fatal(char *fmt, ...);
3483
3484This is called by some utility functions if they encounter a
3485genuinely fatal error such as running out of memory. It is a
3486variadic function in the style of \cw{printf()}, and is expected to
3487show the formatted error message to the user any way it can and then
3488terminate the application. It must not return.
3489
3490\H{frontend-default-colour} \cw{frontend_default_colour()}
3491
3492\c void frontend_default_colour(frontend *fe, float *output);
3493
3494This function expects to be passed a pointer to an array of three
3495\cw{float}s. It returns the platform's local preferred background
3496colour in those three floats, as red, green and blue values (in that
3497order) ranging from \cw{0.0} to \cw{1.0}.
3498
3499This function should only ever be called by the back end function
3500\cw{colours()} (\k{backend-colours}). (Thus, it isn't a
3501\e{midend}-to-frontend function as such, but there didn't seem to be
3502anywhere else particularly good to put it. Sorry.)
3503
3504\C{utils} Utility APIs
3505
3506This chapter documents a variety of utility APIs provided for the
3507general use of the rest of the Puzzles code.
3508
3509\H{utils-random} Random number generation
3510
3511Platforms' local random number generators vary widely in quality and
3512seed size. Puzzles therefore supplies its own high-quality random
3513number generator, with the additional advantage of giving the same
3514results if fed the same seed data on different platforms. This
3515allows game random seeds to be exchanged between different ports of
3516Puzzles and still generate the same games.
3517
3518Unlike the ANSI C \cw{rand()} function, the Puzzles random number
3519generator has an \e{explicit} state object called a
3520\c{random_state}. One of these is managed by each mid-end, for
3521example, and passed to the back end to generate a game with.
3522
3523\S{utils-random-init} \cw{random_new()}
3524
3525\c random_state *random_new(char *seed, int len);
3526
3527Allocates, initialises and returns a new \c{random_state}. The input
3528data is used as the seed for the random number stream (i.e. using
3529the same seed at a later time will generate the same stream).
3530
3531The seed data can be any data at all; there is no requirement to use
3532printable ASCII, or NUL-terminated strings, or anything like that.
3533
3534\S{utils-random-copy} \cw{random_copy()}
3535
3536\c random_state *random_copy(random_state *tocopy);
3537
3538Allocates a new \c{random_state}, copies the contents of another
3539\c{random_state} into it, and returns the new state. If exactly the
3540same sequence of functions is subseqently called on both the copy and
3541the original, the results will be identical. This may be useful for
3542speculatively performing some operation using a given random state,
3543and later replaying that operation precisely.
3544
3545\S{utils-random-free} \cw{random_free()}
3546
3547\c void random_free(random_state *state);
3548
3549Frees a \c{random_state}.
3550
3551\S{utils-random-bits} \cw{random_bits()}
3552
3553\c unsigned long random_bits(random_state *state, int bits);
3554
3555Returns a random number from 0 to \cw{2^bits-1} inclusive. \c{bits}
3556should be between 1 and 32 inclusive.
3557
3558\S{utils-random-upto} \cw{random_upto()}
3559
3560\c unsigned long random_upto(random_state *state, unsigned long limit);
3561
3562Returns a random number from 0 to \cw{limit-1} inclusive.
3563
3564\S{utils-random-state-encode} \cw{random_state_encode()}
3565
3566\c char *random_state_encode(random_state *state);
3567
3568Encodes the entire contents of a \c{random_state} in printable
3569ASCII. Returns a dynamically allocated string containing that
3570encoding. This can subsequently be passed to
3571\cw{random_state_decode()} to reconstruct the same \c{random_state}.
3572
3573\S{utils-random-state-decode} \cw{random_state_decode()}
3574
3575\c random_state *random_state_decode(char *input);
3576
3577Decodes a string generated by \cw{random_state_encode()} and
3578reconstructs an equivalent \c{random_state} to the one encoded, i.e.
3579it should produce the same stream of random numbers.
3580
3581This function has no error reporting; if you pass it an invalid
3582string it will simply generate an arbitrary random state, which may
3583turn out to be noticeably non-random.
3584
3585\S{utils-shuffle} \cw{shuffle()}
3586
3587\c void shuffle(void *array, int nelts, int eltsize, random_state *rs);
3588
3589Shuffles an array into a random order. The interface is much like
3590ANSI C \cw{qsort()}, except that there's no need for a compare
3591function.
3592
3593\c{array} is a pointer to the first element of the array. \c{nelts}
3594is the number of elements in the array; \c{eltsize} is the size of a
3595single element (typically measured using \c{sizeof}). \c{rs} is a
3596\c{random_state} used to generate all the random numbers for the
3597shuffling process.
3598
3599\H{utils-presets} Presets menu management
3600
3601The function \c{midend_get_presets()} (\k{midend-get-presets}) returns
3602a data structure describing a menu hierarchy. Back ends can also
3603choose to provide such a structure to the mid-end, if they want to
3604group their presets hierarchically. To make this easy, there are a few
3605utility functions to construct preset menu structures, and also one
3606intended for front-end use.
3607
3608\S{utils-preset-menu-new} \cw{preset_menu_new()}
3609
3610\c struct preset_menu *preset_menu_new(void);
3611
3612Allocates a new \c{struct preset_menu}, and initialises it to hold no
3613menu items.
3614
3615\S{utils-preset-menu-add_submenu} \cw{preset_menu_add_submenu()}
3616
3617\c struct preset_menu *preset_menu_add_submenu
3618\c (struct preset_menu *parent, char *title);
3619
3620Adds a new submenu to the end of an existing preset menu, and returns
3621a pointer to a newly allocated \c{struct preset_menu} describing the
3622submenu.
3623
3624The string parameter \cq{title} must be dynamically allocated by the
3625caller. The preset-menu structure will take ownership of it, so the
3626caller must not free it.
3627
3628\S{utils-preset-menu-add-preset} \cw{preset_menu_add_preset()}
3629
3630\c void preset_menu_add_preset
3631\c (struct preset_menu *menu, char *title, game_params *params);
3632
3633Adds a preset game configuration to the end of a preset menu.
3634
3635Both the string parameter \cq{title} and the game parameter structure
3636\cq{params} itself must be dynamically allocated by the caller. The
3637preset-menu structure will take ownership of it, so the caller must
3638not free it.
3639
3640\S{utils-preset-menu-lookup-by-id} \cw{preset_menu_lookup_by_id()}
3641
3642\c game_params *preset_menu_lookup_by_id
3643\c (struct preset_menu *menu, int id);
3644
3645Given a numeric index, searches recursively through a preset menu
3646hierarchy to find the corresponding menu entry, and returns a pointer
3647to its existing \c{game_params} structure.
3648
3649This function is intended for front end use (but front ends need not
3650use it if they prefer to do things another way). If a front end finds
3651it inconvenient to store anything more than a numeric index alongside
3652each menu item, then this function provides an easy way for the front
3653end to get back the actual game parameters corresponding to a menu
3654item that the user has selected.
3655
3656\H{utils-alloc} Memory allocation
3657
3658Puzzles has some central wrappers on the standard memory allocation
3659functions, which provide compile-time type checking, and run-time
3660error checking by means of quitting the application if it runs out
3661of memory. This doesn't provide the best possible recovery from
3662memory shortage, but on the other hand it greatly simplifies the
3663rest of the code, because nothing else anywhere needs to worry about
3664\cw{NULL} returns from allocation.
3665
3666\S{utils-snew} \cw{snew()}
3667
3668\c var = snew(type);
3669\e iii iiii
3670
3671This macro takes a single argument which is a \e{type name}. It
3672allocates space for one object of that type. If allocation fails it
3673will call \cw{fatal()} and not return; so if it does return, you can
3674be confident that its return value is non-\cw{NULL}.
3675
3676The return value is cast to the specified type, so that the compiler
3677will type-check it against the variable you assign it into. Thus,
3678this ensures you don't accidentally allocate memory the size of the
3679wrong type and assign it into a variable of the right one (or vice
3680versa!).
3681
3682\S{utils-snewn} \cw{snewn()}
3683
3684\c var = snewn(n, type);
3685\e iii i iiii
3686
3687This macro is the array form of \cw{snew()}. It takes two arguments;
3688the first is a number, and the second is a type name. It allocates
3689space for that many objects of that type, and returns a type-checked
3690non-\cw{NULL} pointer just as \cw{snew()} does.
3691
3692\S{utils-sresize} \cw{sresize()}
3693
3694\c var = sresize(var, n, type);
3695\e iii iii i iiii
3696
3697This macro is a type-checked form of \cw{realloc()}. It takes three
3698arguments: an input memory block, a new size in elements, and a
3699type. It re-sizes the input memory block to a size sufficient to
3700contain that many elements of that type. It returns a type-checked
3701non-\cw{NULL} pointer, like \cw{snew()} and \cw{snewn()}.
3702
3703The input memory block can be \cw{NULL}, in which case this function
3704will behave exactly like \cw{snewn()}. (In principle any
3705ANSI-compliant \cw{realloc()} implementation ought to cope with
3706this, but I've never quite trusted it to work everywhere.)
3707
3708\S{utils-sfree} \cw{sfree()}
3709
3710\c void sfree(void *p);
3711
3712This function is pretty much equivalent to \cw{free()}. It is
3713provided with a dynamically allocated block, and frees it.
3714
3715The input memory block can be \cw{NULL}, in which case this function
3716will do nothing. (In principle any ANSI-compliant \cw{free()}
3717implementation ought to cope with this, but I've never quite trusted
3718it to work everywhere.)
3719
3720\S{utils-dupstr} \cw{dupstr()}
3721
3722\c char *dupstr(const char *s);
3723
3724This function dynamically allocates a duplicate of a C string. Like
3725the \cw{snew()} functions, it guarantees to return non-\cw{NULL} or
3726not return at all.
3727
3728(Many platforms provide the function \cw{strdup()}. As well as
3729guaranteeing never to return \cw{NULL}, my version has the advantage
3730of being defined \e{everywhere}, rather than inconveniently not
3731quite everywhere.)
3732
3733\S{utils-free-cfg} \cw{free_cfg()}
3734
3735\c void free_cfg(config_item *cfg);
3736
3737This function correctly frees an array of \c{config_item}s,
3738including walking the array until it gets to the end and freeing
3739precisely those \c{sval} fields which are expected to be dynamically
3740allocated.
3741
3742(See \k{backend-configure} for details of the \c{config_item}
3743structure.)
3744
3745\H{utils-tree234} Sorted and counted tree functions
3746
3747Many games require complex algorithms for generating random puzzles,
3748and some require moderately complex algorithms even during play. A
3749common requirement during these algorithms is for a means of
3750maintaining sorted or unsorted lists of items, such that items can
3751be removed and added conveniently.
3752
3753For general use, Puzzles provides the following set of functions
3754which maintain 2-3-4 trees in memory. (A 2-3-4 tree is a balanced
3755tree structure, with the property that all lookups, insertions,
3756deletions, splits and joins can be done in \cw{O(log N)} time.)
3757
3758All these functions expect you to be storing a tree of \c{void *}
3759pointers. You can put anything you like in those pointers.
3760
3761By the use of per-node element counts, these tree structures have
3762the slightly unusual ability to look elements up by their numeric
3763index within the list represented by the tree. This means that they
3764can be used to store an unsorted list (in which case, every time you
3765insert a new element, you must explicitly specify the position where
3766you wish to insert it). They can also do numeric lookups in a sorted
3767tree, which might be useful for (for example) tracking the median of
3768a changing data set.
3769
3770As well as storing sorted lists, these functions can be used for
3771storing \q{maps} (associative arrays), by defining each element of a
3772tree to be a (key, value) pair.
3773
3774\S{utils-newtree234} \cw{newtree234()}
3775
3776\c tree234 *newtree234(cmpfn234 cmp);
3777
3778Creates a new empty tree, and returns a pointer to it.
3779
3780The parameter \c{cmp} determines the sorting criterion on the tree.
3781Its prototype is
3782
3783\c typedef int (*cmpfn234)(void *, void *);
3784
3785If you want a sorted tree, you should provide a function matching
3786this prototype, which returns like \cw{strcmp()} does (negative if
3787the first argument is smaller than the second, positive if it is
3788bigger, zero if they compare equal). In this case, the function
3789\cw{addpos234()} will not be usable on your tree (because all
3790insertions must respect the sorting order).
3791
3792If you want an unsorted tree, pass \cw{NULL}. In this case you will
3793not be able to use either \cw{add234()} or \cw{del234()}, or any
3794other function such as \cw{find234()} which depends on a sorting
3795order. Your tree will become something more like an array, except
3796that it will efficiently support insertion and deletion as well as
3797lookups by numeric index.
3798
3799\S{utils-freetree234} \cw{freetree234()}
3800
3801\c void freetree234(tree234 *t);
3802
3803Frees a tree. This function will not free the \e{elements} of the
3804tree (because they might not be dynamically allocated, or you might
3805be storing the same set of elements in more than one tree); it will
3806just free the tree structure itself. If you want to free all the
3807elements of a tree, you should empty it before passing it to
3808\cw{freetree234()}, by means of code along the lines of
3809
3810\c while ((element = delpos234(tree, 0)) != NULL)
3811\c sfree(element); /* or some more complicated free function */
3812\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
3813
3814\S{utils-add234} \cw{add234()}
3815
3816\c void *add234(tree234 *t, void *e);
3817
3818Inserts a new element \c{e} into the tree \c{t}. This function
3819expects the tree to be sorted; the new element is inserted according
3820to the sort order.
3821
3822If an element comparing equal to \c{e} is already in the tree, then
3823the insertion will fail, and the return value will be the existing
3824element. Otherwise, the insertion succeeds, and \c{e} is returned.
3825
3826\S{utils-addpos234} \cw{addpos234()}
3827
3828\c void *addpos234(tree234 *t, void *e, int index);
3829
3830Inserts a new element into an unsorted tree. Since there is no
3831sorting order to dictate where the new element goes, you must
3832specify where you want it to go. Setting \c{index} to zero puts the
3833new element right at the start of the list; setting \c{index} to the
3834current number of elements in the tree puts the new element at the
3835end.
3836
3837Return value is \c{e}, in line with \cw{add234()} (although this
3838function cannot fail except by running out of memory, in which case
3839it will bomb out and die rather than returning an error indication).
3840
3841\S{utils-index234} \cw{index234()}
3842
3843\c void *index234(tree234 *t, int index);
3844
3845Returns a pointer to the \c{index}th element of the tree, or
3846\cw{NULL} if \c{index} is out of range. Elements of the tree are
3847numbered from zero.
3848
3849\S{utils-find234} \cw{find234()}
3850
3851\c void *find234(tree234 *t, void *e, cmpfn234 cmp);
3852
3853Searches for an element comparing equal to \c{e} in a sorted tree.
3854
3855If \c{cmp} is \cw{NULL}, the tree's ordinary comparison function
3856will be used to perform the search. However, sometimes you don't
3857want that; suppose, for example, each of your elements is a big
3858structure containing a \c{char *} name field, and you want to find
3859the element with a given name. You \e{could} achieve this by
3860constructing a fake element structure, setting its name field
3861appropriately, and passing it to \cw{find234()}, but you might find
3862it more convenient to pass \e{just} a name string to \cw{find234()},
3863supplying an alternative comparison function which expects one of
3864its arguments to be a bare name and the other to be a large
3865structure containing a name field.
3866
3867Therefore, if \c{cmp} is not \cw{NULL}, then it will be used to
3868compare \c{e} to elements of the tree. The first argument passed to
3869\c{cmp} will always be \c{e}; the second will be an element of the
3870tree.
3871
3872(See \k{utils-newtree234} for the definition of the \c{cmpfn234}
3873function pointer type.)
3874
3875The returned value is the element found, or \cw{NULL} if the search
3876is unsuccessful.
3877
3878\S{utils-findrel234} \cw{findrel234()}
3879
3880\c void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation);
3881
3882This function is like \cw{find234()}, but has the additional ability
3883to do a \e{relative} search. The additional parameter \c{relation}
3884can be one of the following values:
3885
3886\dt \cw{REL234_EQ}
3887
3888\dd Find only an element that compares equal to \c{e}. This is
3889exactly the behaviour of \cw{find234()}.
3890
3891\dt \cw{REL234_LT}
3892
3893\dd Find the greatest element that compares strictly less than
3894\c{e}. \c{e} may be \cw{NULL}, in which case it finds the greatest
3895element in the whole tree (which could also be done by
3896\cw{index234(t, count234(t)-1)}).
3897
3898\dt \cw{REL234_LE}
3899
3900\dd Find the greatest element that compares less than or equal to
3901\c{e}. (That is, find an element that compares equal to \c{e} if
3902possible, but failing that settle for something just less than it.)
3903
3904\dt \cw{REL234_GT}
3905
3906\dd Find the smallest element that compares strictly greater than
3907\c{e}. \c{e} may be \cw{NULL}, in which case it finds the smallest
3908element in the whole tree (which could also be done by
3909\cw{index234(t, 0)}).
3910
3911\dt \cw{REL234_GE}
3912
3913\dd Find the smallest element that compares greater than or equal to
3914\c{e}. (That is, find an element that compares equal to \c{e} if
3915possible, but failing that settle for something just bigger than
3916it.)
3917
3918Return value, as before, is the element found or \cw{NULL} if no
3919element satisfied the search criterion.
3920
3921\S{utils-findpos234} \cw{findpos234()}
3922
3923\c void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index);
3924
3925This function is like \cw{find234()}, but has the additional feature
3926of returning the index of the element found in the tree; that index
3927is written to \c{*index} in the event of a successful search (a
3928non-\cw{NULL} return value).
3929
3930\c{index} may be \cw{NULL}, in which case this function behaves
3931exactly like \cw{find234()}.
3932
3933\S{utils-findrelpos234} \cw{findrelpos234()}
3934
3935\c void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation,
3936\c int *index);
3937
3938This function combines all the features of \cw{findrel234()} and
3939\cw{findpos234()}.
3940
3941\S{utils-del234} \cw{del234()}
3942
3943\c void *del234(tree234 *t, void *e);
3944
3945Finds an element comparing equal to \c{e} in the tree, deletes it,
3946and returns it.
3947
3948The input tree must be sorted.
3949
3950The element found might be \c{e} itself, or might merely compare
3951equal to it.
3952
3953Return value is \cw{NULL} if no such element is found.
3954
3955\S{utils-delpos234} \cw{delpos234()}
3956
3957\c void *delpos234(tree234 *t, int index);
3958
3959Deletes the element at position \c{index} in the tree, and returns
3960it.
3961
3962Return value is \cw{NULL} if the index is out of range.
3963
3964\S{utils-count234} \cw{count234()}
3965
3966\c int count234(tree234 *t);
3967
3968Returns the number of elements currently in the tree.
3969
3970\S{utils-splitpos234} \cw{splitpos234()}
3971
3972\c tree234 *splitpos234(tree234 *t, int index, int before);
3973
3974Splits the input tree into two pieces at a given position, and
3975creates a new tree containing all the elements on one side of that
3976position.
3977
3978If \c{before} is \cw{TRUE}, then all the items at or after position
3979\c{index} are left in the input tree, and the items before that
3980point are returned in the new tree. Otherwise, the reverse happens:
3981all the items at or after \c{index} are moved into the new tree, and
3982those before that point are left in the old one.
3983
3984If \c{index} is equal to 0 or to the number of elements in the input
3985tree, then one of the two trees will end up empty (and this is not
3986an error condition). If \c{index} is further out of range in either
3987direction, the operation will fail completely and return \cw{NULL}.
3988
3989This operation completes in \cw{O(log N)} time, no matter how large
3990the tree or how balanced or unbalanced the split.
3991
3992\S{utils-split234} \cw{split234()}
3993
3994\c tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel);
3995
3996Splits a sorted tree according to its sort order.
3997
3998\c{rel} can be any of the relation constants described in
3999\k{utils-findrel234}, \e{except} for \cw{REL234_EQ}. All the
4000elements having that relation to \c{e} will be transferred into the
4001new tree; the rest will be left in the old one.
4002
4003The parameter \c{cmp} has the same semantics as it does in
4004\cw{find234()}: if it is not \cw{NULL}, it will be used in place of
4005the tree's own comparison function when comparing elements to \c{e},
4006in such a way that \c{e} itself is always the first of its two
4007operands.
4008
4009Again, this operation completes in \cw{O(log N)} time, no matter how
4010large the tree or how balanced or unbalanced the split.
4011
4012\S{utils-join234} \cw{join234()}
4013
4014\c tree234 *join234(tree234 *t1, tree234 *t2);
4015
4016Joins two trees together by concatenating the lists they represent.
4017All the elements of \c{t2} are moved into \c{t1}, in such a way that
4018they appear \e{after} the elements of \c{t1}. The tree \c{t2} is
4019freed; the return value is \c{t1}.
4020
4021If you apply this function to a sorted tree and it violates the sort
4022order (i.e. the smallest element in \c{t2} is smaller than or equal
4023to the largest element in \c{t1}), the operation will fail and
4024return \cw{NULL}.
4025
4026This operation completes in \cw{O(log N)} time, no matter how large
4027the trees being joined together.
4028
4029\S{utils-join234r} \cw{join234r()}
4030
4031\c tree234 *join234r(tree234 *t1, tree234 *t2);
4032
4033Joins two trees together in exactly the same way as \cw{join234()},
4034but this time the combined tree is returned in \c{t2}, and \c{t1} is
4035destroyed. The elements in \c{t1} still appear before those in
4036\c{t2}.
4037
4038Again, this operation completes in \cw{O(log N)} time, no matter how
4039large the trees being joined together.
4040
4041\S{utils-copytree234} \cw{copytree234()}
4042
4043\c tree234 *copytree234(tree234 *t, copyfn234 copyfn,
4044\c void *copyfnstate);
4045
4046Makes a copy of an entire tree.
4047
4048If \c{copyfn} is \cw{NULL}, the tree will be copied but the elements
4049will not be; i.e. the new tree will contain pointers to exactly the
4050same physical elements as the old one.
4051
4052If you want to copy each actual element during the operation, you
4053can instead pass a function in \c{copyfn} which makes a copy of each
4054element. That function has the prototype
4055
4056\c typedef void *(*copyfn234)(void *state, void *element);
4057
4058and every time it is called, the \c{state} parameter will be set to
4059the value you passed in as \c{copyfnstate}.
4060
4061\H{utils-misc} Miscellaneous utility functions and macros
4062
4063This section contains all the utility functions which didn't
4064sensibly fit anywhere else.
4065
4066\S{utils-truefalse} \cw{TRUE} and \cw{FALSE}
4067
4068The main Puzzles header file defines the macros \cw{TRUE} and
4069\cw{FALSE}, which are used throughout the code in place of 1 and 0
4070(respectively) to indicate that the values are in a boolean context.
4071For code base consistency, I'd prefer it if submissions of new code
4072followed this convention as well.
4073
4074\S{utils-maxmin} \cw{max()} and \cw{min()}
4075
4076The main Puzzles header file defines the pretty standard macros
4077\cw{max()} and \cw{min()}, each of which is given two arguments and
4078returns the one which compares greater or less respectively.
4079
4080These macros may evaluate their arguments multiple times. Avoid side
4081effects.
4082
4083\S{utils-pi} \cw{PI}
4084
4085The main Puzzles header file defines a macro \cw{PI} which expands
4086to a floating-point constant representing pi.
4087
4088(I've never understood why ANSI's \cw{<math.h>} doesn't define this.
4089It'd be so useful!)
4090
4091\S{utils-obfuscate-bitmap} \cw{obfuscate_bitmap()}
4092
4093\c void obfuscate_bitmap(unsigned char *bmp, int bits, int decode);
4094
4095This function obscures the contents of a piece of data, by
4096cryptographic methods. It is useful for games of hidden information
4097(such as Mines, Guess or Black Box), in which the game ID
4098theoretically reveals all the information the player is supposed to
4099be trying to guess. So in order that players should be able to send
4100game IDs to one another without accidentally spoiling the resulting
4101game by looking at them, these games obfuscate their game IDs using
4102this function.
4103
4104Although the obfuscation function is cryptographic, it cannot
4105properly be called encryption because it has no key. Therefore,
4106anybody motivated enough can re-implement it, or hack it out of the
4107Puzzles source, and strip the obfuscation off one of these game IDs
4108to see what lies beneath. (Indeed, they could usually do it much
4109more easily than that, by entering the game ID into their own copy
4110of the puzzle and hitting Solve.) The aim is not to protect against
4111a determined attacker; the aim is simply to protect people who
4112wanted to play the game honestly from \e{accidentally} spoiling
4113their own fun.
4114
4115The input argument \c{bmp} points at a piece of memory to be
4116obfuscated. \c{bits} gives the length of the data. Note that that
4117length is in \e{bits} rather than bytes: if you ask for obfuscation
4118of a partial number of bytes, then you will get it. Bytes are
4119considered to be used from the top down: thus, for example, setting
4120\c{bits} to 10 will cover the whole of \cw{bmp[0]} and the \e{top
4121two} bits of \cw{bmp[1]}. The remainder of a partially used byte is
4122undefined (i.e. it may be corrupted by the function).
4123
4124The parameter \c{decode} is \cw{FALSE} for an encoding operation,
4125and \cw{TRUE} for a decoding operation. Each is the inverse of the
4126other. (There's no particular reason you shouldn't obfuscate by
4127decoding and restore cleartext by encoding, if you really wanted to;
4128it should still work.)
4129
4130The input bitmap is processed in place.
4131
4132\S{utils-bin2hex} \cw{bin2hex()}
4133
4134\c char *bin2hex(const unsigned char *in, int inlen);
4135
4136This function takes an input byte array and converts it into an
4137ASCII string encoding those bytes in (lower-case) hex. It returns a
4138dynamically allocated string containing that encoding.
4139
4140This function is useful for encoding the result of
4141\cw{obfuscate_bitmap()} in printable ASCII for use in game IDs.
4142
4143\S{utils-hex2bin} \cw{hex2bin()}
4144
4145\c unsigned char *hex2bin(const char *in, int outlen);
4146
4147This function takes an ASCII string containing hex digits, and
4148converts it back into a byte array of length \c{outlen}. If there
4149aren't enough hex digits in the string, the contents of the
4150resulting array will be undefined.
4151
4152This function is the inverse of \cw{bin2hex()}.
4153
4154\S{utils-game-mkhighlight} \cw{game_mkhighlight()}
4155
4156\c void game_mkhighlight(frontend *fe, float *ret,
4157\c int background, int highlight, int lowlight);
4158
4159It's reasonably common for a puzzle game's graphics to use
4160highlights and lowlights to indicate \q{raised} or \q{lowered}
4161sections. Fifteen, Sixteen and Twiddle are good examples of this.
4162
4163Puzzles using this graphical style are running a risk if they just
4164use whatever background colour is supplied to them by the front end,
4165because that background colour might be too light to see any
4166highlights on at all. (In particular, it's not unheard of for the
4167front end to specify a default background colour of white.)
4168
4169Therefore, such puzzles can call this utility function from their
4170\cw{colours()} routine (\k{backend-colours}). You pass it your front
4171end handle, a pointer to the start of your return array, and three
4172colour indices. It will:
4173
4174\b call \cw{frontend_default_colour()} (\k{frontend-default-colour})
4175to fetch the front end's default background colour
4176
4177\b alter the brightness of that colour if it's unsuitable
4178
4179\b define brighter and darker variants of the colour to be used as
4180highlights and lowlights
4181
4182\b write those results into the relevant positions in the \c{ret}
4183array.
4184
4185Thus, \cw{ret[background*3]} to \cw{ret[background*3+2]} will be set
4186to RGB values defining a sensible background colour, and similary
4187\c{highlight} and \c{lowlight} will be set to sensible colours.
4188
4189\C{writing} How to write a new puzzle
4190
4191This chapter gives a guide to how to actually write a new puzzle:
4192where to start, what to do first, how to solve common problems.
4193
4194The previous chapters have been largely composed of facts. This one
4195is mostly advice.
4196
4197\H{writing-editorial} Choosing a puzzle
4198
4199Before you start writing a puzzle, you have to choose one. Your
4200taste in puzzle games is up to you, of course; and, in fact, you're
4201probably reading this guide because you've \e{already} thought of a
4202game you want to write. But if you want to get it accepted into the
4203official Puzzles distribution, then there's a criterion it has to
4204meet.
4205
4206The current Puzzles editorial policy is that all games should be
4207\e{fair}. A fair game is one which a player can only fail to
4208complete through demonstrable lack of skill \dash that is, such that
4209a better player in the same situation would have \e{known} to do
4210something different.
4211
4212For a start, that means every game presented to the user must have
4213\e{at least one solution}. Giving the unsuspecting user a puzzle
4214which is actually impossible is not acceptable. (There is an
4215exception: if the user has selected some non-default option which is
4216clearly labelled as potentially unfair, \e{then} you're allowed to
4217generate possibly insoluble puzzles, because the user isn't
4218unsuspecting any more. Same Game and Mines both have options of this
4219type.)
4220
4221Also, this actually \e{rules out} games such as Klondike, or the
4222normal form of Mahjong Solitaire. Those games have the property that
4223even if there is a solution (i.e. some sequence of moves which will
4224get from the start state to the solved state), the player doesn't
4225necessarily have enough information to \e{find} that solution. In
4226both games, it is possible to reach a dead end because you had an
4227arbitrary choice to make and made it the wrong way. This violates
4228the fairness criterion, because a better player couldn't have known
4229they needed to make the other choice.
4230
4231(GNOME has a variant on Mahjong Solitaire which makes it fair: there
4232is a Shuffle operation which randomly permutes all the remaining
4233tiles without changing their positions, which allows you to get out
4234of a sticky situation. Using this operation adds a 60-second penalty
4235to your solution time, so it's to the player's advantage to try to
4236minimise the chance of having to use it. It's still possible to
4237render the game uncompletable if you end up with only two tiles
4238vertically stacked, but that's easy to foresee and avoid using a
4239shuffle operation. This form of the game \e{is} fair. Implementing
4240it in Puzzles would require an infrastructure change so that the
4241back end could communicate time penalties to the mid-end, but that
4242would be easy enough.)
4243
4244Providing a \e{unique} solution is a little more negotiable; it
4245depends on the puzzle. Solo would have been of unacceptably low
4246quality if it didn't always have a unique solution, whereas Twiddle
4247inherently has multiple solutions by its very nature and it would
4248have been meaningless to even \e{suggest} making it uniquely
4249soluble. Somewhere in between, Flip could reasonably be made to have
4250unique solutions (by enforcing a zero-dimension kernel in every
4251generated matrix) but it doesn't seem like a serious quality problem
4252that it doesn't.
4253
4254Of course, you don't \e{have} to care about all this. There's
4255nothing stopping you implementing any puzzle you want to if you're
4256happy to maintain your puzzle yourself, distribute it from your own
4257web site, fork the Puzzles code completely, or anything like that.
4258It's free software; you can do what you like with it. But any game
4259that you want to be accepted into \e{my} Puzzles code base has to
4260satisfy the fairness criterion, which means all randomly generated
4261puzzles must have a solution (unless the user has deliberately
4262chosen otherwise) and it must be possible \e{in theory} to find that
4263solution without having to guess.
4264
4265\H{writing-gs} Getting started
4266
4267The simplest way to start writing a new puzzle is to copy
4268\c{nullgame.c}. This is a template puzzle source file which does
4269almost nothing, but which contains all the back end function
4270prototypes and declares the back end data structure correctly. It is
4271built every time the rest of Puzzles is built, to ensure that it
4272doesn't get out of sync with the code and remains buildable.
4273
4274So start by copying \c{nullgame.c} into your new source file. Then
4275you'll gradually add functionality until the very boring Null Game
4276turns into your real game.
4277
4278Next you'll need to add your puzzle to the Makefiles, in order to
4279compile it conveniently. \e{Do not edit the Makefiles}: they are
4280created automatically by the script \c{mkfiles.pl}, from the file
4281called \c{Recipe}. Edit \c{Recipe}, and then re-run \c{mkfiles.pl}.
4282
4283Also, don't forget to add your puzzle to \c{list.c}: if you don't,
4284then it will still run fine on platforms which build each puzzle
4285separately, but Mac OS X and other monolithic platforms will not
4286include your new puzzle in their single binary.
4287
4288Once your source file is building, you can move on to the fun bit.
4289
4290\S{writing-generation} Puzzle generation
4291
4292Randomly generating instances of your puzzle is almost certain to be
4293the most difficult part of the code, and also the task with the
4294highest chance of turning out to be completely infeasible. Therefore
4295I strongly recommend doing it \e{first}, so that if it all goes
4296horribly wrong you haven't wasted any more time than you absolutely
4297had to. What I usually do is to take an unmodified \c{nullgame.c},
4298and start adding code to \cw{new_game_desc()} which tries to
4299generate a puzzle instance and print it out using \cw{printf()}.
4300Once that's working, \e{then} I start connecting it up to the return
4301value of \cw{new_game_desc()}, populating other structures like
4302\c{game_params}, and generally writing the rest of the source file.
4303
4304There are many ways to generate a puzzle which is known to be
4305soluble. In this section I list all the methods I currently know of,
4306in case any of them can be applied to your puzzle. (Not all of these
4307methods will work, or in some cases even make sense, for all
4308puzzles.)
4309
4310Some puzzles are mathematically tractable, meaning you can work out
4311in advance which instances are soluble. Sixteen, for example, has a
4312parity constraint in some settings which renders exactly half the
4313game space unreachable, but it can be mathematically proved that any
4314position not in that half \e{is} reachable. Therefore, Sixteen's
4315grid generation simply consists of selecting at random from a well
4316defined subset of the game space. Cube in its default state is even
4317easier: \e{every} possible arrangement of the blue squares and the
4318cube's starting position is soluble!
4319
4320Another option is to redefine what you mean by \q{soluble}. Black
4321Box takes this approach. There are layouts of balls in the box which
4322are completely indistinguishable from one another no matter how many
4323beams you fire into the box from which angles, which would normally
4324be grounds for declaring those layouts unfair; but fortunately,
4325detecting that indistinguishability is computationally easy. So
4326Black Box doesn't demand that your ball placements match its own; it
4327merely demands that your ball placements be \e{indistinguishable}
4328from the ones it was thinking of. If you have an ambiguous puzzle,
4329then any of the possible answers is considered to be a solution.
4330Having redefined the rules in that way, any puzzle is soluble again.
4331
4332Those are the simple techniques. If they don't work, you have to get
4333cleverer.
4334
4335One way to generate a soluble puzzle is to start from the solved
4336state and make inverse moves until you reach a starting state. Then
4337you know there's a solution, because you can just list the inverse
4338moves you made and make them in the opposite order to return to the
4339solved state.
4340
4341This method can be simple and effective for puzzles where you get to
4342decide what's a starting state and what's not. In Pegs, for example,
4343the generator begins with one peg in the centre of the board and
4344makes inverse moves until it gets bored; in this puzzle, valid
4345inverse moves are easy to detect, and \e{any} state that's reachable
4346from the solved state by inverse moves is a reasonable starting
4347position. So Pegs just continues making inverse moves until the
4348board satisfies some criteria about extent and density, and then
4349stops and declares itself done.
4350
4351For other puzzles, it can be a lot more difficult. Same Game uses
4352this strategy too, and it's lucky to get away with it at all: valid
4353inverse moves aren't easy to find (because although it's easy to
4354insert additional squares in a Same Game position, it's difficult to
4355arrange that \e{after} the insertion they aren't adjacent to any
4356other squares of the same colour), so you're constantly at risk of
4357running out of options and having to backtrack or start again. Also,
4358Same Game grids never start off half-empty, which means you can't
4359just stop when you run out of moves \dash you have to find a way to
4360fill the grid up \e{completely}.
4361
4362The other way to generate a puzzle that's soluble is to start from
4363the other end, and actually write a \e{solver}. This tends to ensure
4364that a puzzle has a \e{unique} solution over and above having a
4365solution at all, so it's a good technique to apply to puzzles for
4366which that's important.
4367
4368One theoretical drawback of generating soluble puzzles by using a
4369solver is that your puzzles are restricted in difficulty to those
4370which the solver can handle. (Most solvers are not fully general:
4371many sets of puzzle rules are NP-complete or otherwise nasty, so
4372most solvers can only handle a subset of the theoretically soluble
4373puzzles.) It's been my experience in practice, however, that this
4374usually isn't a problem; computers are good at very different things
4375from humans, and what the computer thinks is nice and easy might
4376still be pleasantly challenging for a human. For example, when
4377solving Dominosa puzzles I frequently find myself using a variety of
4378reasoning techniques that my solver doesn't know about; in
4379principle, therefore, I should be able to solve the puzzle using
4380only those techniques it \e{does} know about, but this would involve
4381repeatedly searching the entire grid for the one simple deduction I
4382can make. Computers are good at this sort of exhaustive search, but
4383it's been my experience that human solvers prefer to do more complex
4384deductions than to spend ages searching for simple ones. So in many
4385cases I don't find my own playing experience to be limited by the
4386restrictions on the solver.
4387
4388(This isn't \e{always} the case. Solo is a counter-example;
4389generating Solo puzzles using a simple solver does lead to
4390qualitatively easier puzzles. Therefore I had to make the Solo
4391solver rather more advanced than most of them.)
4392
4393There are several different ways to apply a solver to the problem of
4394generating a soluble puzzle. I list a few of them below.
4395
4396The simplest approach is brute force: randomly generate a puzzle,
4397use the solver to see if it's soluble, and if not, throw it away and
4398try again until you get lucky. This is often a viable technique if
4399all else fails, but it tends not to scale well: for many puzzle
4400types, the probability of finding a uniquely soluble instance
4401decreases sharply as puzzle size goes up, so this technique might
4402work reasonably fast for small puzzles but take (almost) forever at
4403larger sizes. Still, if there's no other alternative it can be
4404usable: Pattern and Dominosa both use this technique. (However,
4405Dominosa has a means of tweaking the randomly generated grids to
4406increase the \e{probability} of them being soluble, by ruling out
4407one of the most common ambiguous cases. This improved generation
4408speed by over a factor of 10 on the highest preset!)
4409
4410An approach which can be more scalable involves generating a grid
4411and then tweaking it to make it soluble. This is the technique used
4412by Mines and also by Net: first a random puzzle is generated, and
4413then the solver is run to see how far it gets. Sometimes the solver
4414will get stuck; when that happens, examine the area it's having
4415trouble with, and make a small random change in that area to allow
4416it to make more progress. Continue solving (possibly even without
4417restarting the solver), tweaking as necessary, until the solver
4418finishes. Then restart the solver from the beginning to ensure that
4419the tweaks haven't caused new problems in the process of solving old
4420ones (which can sometimes happen).
4421
4422This strategy works well in situations where the usual solver
4423failure mode is to get stuck in an easily localised spot. Thus it
4424works well for Net and Mines, whose most common failure mode tends
4425to be that most of the grid is fine but there are a few widely
4426separated ambiguous sections; but it would work less well for
4427Dominosa, in which the way you get stuck is to have scoured the
4428whole grid and not found anything you can deduce \e{anywhere}. Also,
4429it relies on there being a low probability that tweaking the grid
4430introduces a new problem at the same time as solving the old one;
4431Mines and Net also have the property that most of their deductions
4432are local, so that it's very unlikely for a tweak to affect
4433something half way across the grid from the location where it was
4434applied. In Dominosa, by contrast, a lot of deductions use
4435information about half the grid (\q{out of all the sixes, only one
4436is next to a three}, which can depend on the values of up to 32 of
4437the 56 squares in the default setting!), so this tweaking strategy
4438would be rather less likely to work well.
4439
4440A more specialised strategy is that used in Solo and Slant. These
4441puzzles have the property that they derive their difficulty from not
4442presenting all the available clues. (In Solo's case, if all the
4443possible clues were provided then the puzzle would already be
4444solved; in Slant it would still require user action to fill in the
4445lines, but it would present no challenge at all). Therefore, a
4446simple generation technique is to leave the decision of which clues
4447to provide until the last minute. In other words, first generate a
4448random \e{filled} grid with all possible clues present, and then
4449gradually remove clues for as long as the solver reports that it's
4450still soluble. Unlike the methods described above, this technique
4451\e{cannot} fail \dash once you've got a filled grid, nothing can
4452stop you from being able to convert it into a viable puzzle.
4453However, it wouldn't even be meaningful to apply this technique to
4454(say) Pattern, in which clues can never be left out, so the only way
4455to affect the set of clues is by altering the solution.
4456
4457(Unfortunately, Solo is complicated by the need to provide puzzles
4458at varying difficulty levels. It's easy enough to generate a puzzle
4459of \e{at most} a given level of difficulty; you just have a solver
4460with configurable intelligence, and you set it to a given level and
4461apply the above technique, thus guaranteeing that the resulting grid
4462is solvable by someone with at most that much intelligence. However,
4463generating a puzzle of \e{at least} a given level of difficulty is
4464rather harder; if you go for \e{at most} Intermediate level, you're
4465likely to find that you've accidentally generated a Trivial grid a
4466lot of the time, because removing just one number is sufficient to
4467take the puzzle from Trivial straight to Ambiguous. In that
4468situation Solo has no remaining options but to throw the puzzle away
4469and start again.)
4470
4471A final strategy is to use the solver \e{during} puzzle
4472construction: lay out a bit of the grid, run the solver to see what
4473it allows you to deduce, and then lay out a bit more to allow the
4474solver to make more progress. There are articles on the web that
4475recommend constructing Sudoku puzzles by this method (which is
4476completely the opposite way round to how Solo does it); for Sudoku
4477it has the advantage that you get to specify your clue squares in
4478advance (so you can have them make pretty patterns).
4479
4480Rectangles uses a strategy along these lines. First it generates a
4481grid by placing the actual rectangles; then it has to decide where
4482in each rectangle to place a number. It uses a solver to help it
4483place the numbers in such a way as to ensure a unique solution. It
4484does this by means of running a test solver, but it runs the solver
4485\e{before} it's placed any of the numbers \dash which means the
4486solver must be capable of coping with uncertainty about exactly
4487where the numbers are! It runs the solver as far as it can until it
4488gets stuck; then it narrows down the possible positions of a number
4489in order to allow the solver to make more progress, and so on. Most
4490of the time this process terminates with the grid fully solved, at
4491which point any remaining number-placement decisions can be made at
4492random from the options not so far ruled out. Note that unlike the
4493Net/Mines tweaking strategy described above, this algorithm does not
4494require a checking run after it completes: if it finishes
4495successfully at all, then it has definitely produced a uniquely
4496soluble puzzle.
4497
4498Most of the strategies described above are not 100% reliable. Each
4499one has a failure rate: every so often it has to throw out the whole
4500grid and generate a fresh one from scratch. (Solo's strategy would
4501be the exception, if it weren't for the need to provide configurable
4502difficulty levels.) Occasional failures are not a fundamental
4503problem in this sort of work, however: it's just a question of
4504dividing the grid generation time by the success rate (if it takes
450510ms to generate a candidate grid and 1/5 of them work, then it will
4506take 50ms on average to generate a viable one), and seeing whether
4507the expected time taken to \e{successfully} generate a puzzle is
4508unacceptably slow. Dominosa's generator has a very low success rate
4509(about 1 out of 20 candidate grids turn out to be usable, and if you
4510think \e{that's} bad then go and look at the source code and find
4511the comment showing what the figures were before the generation-time
4512tweaks!), but the generator itself is very fast so this doesn't
4513matter. Rectangles has a slower generator, but fails well under 50%
4514of the time.
4515
4516So don't be discouraged if you have an algorithm that doesn't always
4517work: if it \e{nearly} always works, that's probably good enough.
4518The one place where reliability is important is that your algorithm
4519must never produce false positives: it must not claim a puzzle is
4520soluble when it isn't. It can produce false negatives (failing to
4521notice that a puzzle is soluble), and it can fail to generate a
4522puzzle at all, provided it doesn't do either so often as to become
4523slow.
4524
4525One last piece of advice: for grid-based puzzles, when writing and
4526testing your generation algorithm, it's almost always a good idea
4527\e{not} to test it initially on a grid that's square (i.e.
4528\cw{w==h}), because if the grid is square then you won't notice if
4529you mistakenly write \c{h} instead of \c{w} (or vice versa)
4530somewhere in the code. Use a rectangular grid for testing, and any
4531size of grid will be likely to work after that.
4532
4533\S{writing-textformats} Designing textual description formats
4534
4535Another aspect of writing a puzzle which is worth putting some
4536thought into is the design of the various text description formats:
4537the format of the game parameter encoding, the game description
4538encoding, and the move encoding.
4539
4540The first two of these should be reasonably intuitive for a user to
4541type in; so provide some flexibility where possible. Suppose, for
4542example, your parameter format consists of two numbers separated by
4543an \c{x} to specify the grid dimensions (\c{10x10} or \c{20x15}),
4544and then has some suffixes to specify other aspects of the game
4545type. It's almost always a good idea in this situation to arrange
4546that \cw{decode_params()} can handle the suffixes appearing in any
4547order, even if \cw{encode_params()} only ever generates them in one
4548order.
4549
4550These formats will also be expected to be reasonably stable: users
4551will expect to be able to exchange game IDs with other users who
4552aren't running exactly the same version of your game. So make them
4553robust and stable: don't build too many assumptions into the game ID
4554format which will have to be changed every time something subtle
4555changes in the puzzle code.
4556
4557\H{writing-howto} Common how-to questions
4558
4559This section lists some common things people want to do when writing
4560a puzzle, and describes how to achieve them within the Puzzles
4561framework.
4562
4563\S{writing-howto-cursor} Drawing objects at only one position
4564
4565A common phenomenon is to have an object described in the
4566\c{game_state} or the \c{game_ui} which can only be at one position.
4567A cursor \dash probably specified in the \c{game_ui} \dash is a good
4568example.
4569
4570In the \c{game_ui}, it would \e{obviously} be silly to have an array
4571covering the whole game grid with a boolean flag stating whether the
4572cursor was at each position. Doing that would waste space, would
4573make it difficult to find the cursor in order to do anything with
4574it, and would introduce the potential for synchronisation bugs in
4575which you ended up with two cursors or none. The obviously sensible
4576way to store a cursor in the \c{game_ui} is to have fields directly
4577encoding the cursor's coordinates.
4578
4579However, it is a mistake to assume that the same logic applies to
4580the \c{game_drawstate}. If you replicate the cursor position fields
4581in the draw state, the redraw code will get very complicated. In the
4582draw state, in fact, it \e{is} probably the right thing to have a
4583cursor flag for every position in the grid. You probably have an
4584array for the whole grid in the drawstate already (stating what is
4585currently displayed in the window at each position); the sensible
4586approach is to add a \q{cursor} flag to each element of that array.
4587Then the main redraw loop will look something like this
4588(pseudo-code):
4589
4590\c for (y = 0; y < h; y++) {
4591\c for (x = 0; x < w; x++) {
4592\c int value = state->symbol_at_position[y][x];
4593\c if (x == ui->cursor_x && y == ui->cursor_y)
4594\c value |= CURSOR;
4595\c if (ds->symbol_at_position[y][x] != value) {
4596\c symbol_drawing_subroutine(dr, ds, x, y, value);
4597\c ds->symbol_at_position[y][x] = value;
4598\c }
4599\c }
4600\c }
4601
4602This loop is very simple, pretty hard to get wrong, and
4603\e{automatically} deals both with erasing the previous cursor and
4604drawing the new one, with no special case code required.
4605
4606This type of loop is generally a sensible way to write a redraw
4607function, in fact. The best thing is to ensure that the information
4608stored in the draw state for each position tells you \e{everything}
4609about what was drawn there. A good way to ensure that is to pass
4610precisely the same information, and \e{only} that information, to a
4611subroutine that does the actual drawing; then you know there's no
4612additional information which affects the drawing but which you don't
4613notice changes in.
4614
4615\S{writing-keyboard-cursor} Implementing a keyboard-controlled cursor
4616
4617It is often useful to provide a keyboard control method in a
4618basically mouse-controlled game. A keyboard-controlled cursor is
4619best implemented by storing its location in the \c{game_ui} (since
4620if it were in the \c{game_state} then the user would have to
4621separately undo every cursor move operation). So the procedure would
4622be:
4623
4624\b Put cursor position fields in the \c{game_ui}.
4625
4626\b \cw{interpret_move()} responds to arrow keys by modifying the
4627cursor position fields and returning \cw{""}.
4628
4629\b \cw{interpret_move()} responds to some sort of fire button by
4630actually performing a move based on the current cursor location.
4631
4632\b You might want an additional \c{game_ui} field stating whether
4633the cursor is currently visible, and having it disappear when a
4634mouse action occurs (so that it doesn't clutter the display when not
4635actually in use).
4636
4637\b You might also want to automatically hide the cursor in
4638\cw{changed_state()} when the current game state changes to one in
4639which there is no move to make (which is the case in some types of
4640completed game).
4641
4642\b \cw{redraw()} draws the cursor using the technique described in
4643\k{writing-howto-cursor}.
4644
4645\S{writing-howto-dragging} Implementing draggable sprites
4646
4647Some games have a user interface which involves dragging some sort
4648of game element around using the mouse. If you need to show a
4649graphic moving smoothly over the top of other graphics, use a
4650blitter (see \k{drawing-blitter} for the blitter API) to save the
4651background underneath it. The typical scenario goes:
4652
4653\b Have a blitter field in the \c{game_drawstate}.
4654
4655\b Set the blitter field to \cw{NULL} in the game's
4656\cw{new_drawstate()} function, since you don't yet know how big the
4657piece of saved background needs to be.
4658
4659\b In the game's \cw{set_size()} function, once you know the size of
4660the object you'll be dragging around the display and hence the
4661required size of the blitter, actually allocate the blitter.
4662
4663\b In \cw{free_drawstate()}, free the blitter if it's not \cw{NULL}.
4664
4665\b In \cw{interpret_move()}, respond to mouse-down and mouse-drag
4666events by updating some fields in the \cw{game_ui} which indicate
4667that a drag is in progress.
4668
4669\b At the \e{very end} of \cw{redraw()}, after all other drawing has
4670been done, draw the moving object if there is one. First save the
4671background under the object in the blitter; then set a clip
4672rectangle covering precisely the area you just saved (just in case
4673anti-aliasing or some other error causes your drawing to go beyond
4674the area you saved). Then draw the object, and call \cw{unclip()}.
4675Finally, set a flag in the \cw{game_drawstate} that indicates that
4676the blitter needs restoring.
4677
4678\b At the very start of \cw{redraw()}, before doing anything else at
4679all, check the flag in the \cw{game_drawstate}, and if it says the
4680blitter needs restoring then restore it. (Then clear the flag, so
4681that this won't happen again in the next redraw if no moving object
4682is drawn this time.)
4683
4684This way, you will be able to write the rest of the redraw function
4685completely ignoring the dragged object, as if it were floating above
4686your bitmap and being completely separate.
4687
4688\S{writing-ref-counting} Sharing large invariant data between all
4689game states
4690
4691In some puzzles, there is a large amount of data which never changes
4692between game states. The array of numbers in Dominosa is a good
4693example.
4694
4695You \e{could} dynamically allocate a copy of that array in every
4696\c{game_state}, and have \cw{dup_game()} make a fresh copy of it for
4697every new \c{game_state}; but it would waste memory and time. A
4698more efficient way is to use a reference-counted structure.
4699
4700\b Define a structure type containing the data in question, and also
4701containing an integer reference count.
4702
4703\b Have a field in \c{game_state} which is a pointer to this
4704structure.
4705
4706\b In \cw{new_game()}, when creating a fresh game state at the start
4707of a new game, create an instance of this structure, initialise it
4708with the invariant data, and set its reference count to 1.
4709
4710\b In \cw{dup_game()}, rather than making a copy of the structure
4711for the new game state, simply set the new game state to point at
4712the same copy of the structure, and increment its reference count.
4713
4714\b In \cw{free_game()}, decrement the reference count in the
4715structure pointed to by the game state; if the count reaches zero,
4716free the structure.
4717
4718This way, the invariant data will persist for only as long as it's
4719genuinely needed; \e{as soon} as the last game state for a
4720particular puzzle instance is freed, the invariant data for that
4721puzzle will vanish as well. Reference counting is a very efficient
4722form of garbage collection, when it works at all. (Which it does in
4723this instance, of course, because there's no possibility of circular
4724references.)
4725
4726\S{writing-flash-types} Implementing multiple types of flash
4727
4728In some games you need to flash in more than one different way.
4729Mines, for example, flashes white when you win, and flashes red when
4730you tread on a mine and die.
4731
4732The simple way to do this is:
4733
4734\b Have a field in the \c{game_ui} which describes the type of flash.
4735
4736\b In \cw{flash_length()}, examine the old and new game states to
4737decide whether a flash is required and what type. Write the type of
4738flash to the \c{game_ui} field whenever you return non-zero.
4739
4740\b In \cw{redraw()}, when you detect that \c{flash_time} is
4741non-zero, examine the field in \c{game_ui} to decide which type of
4742flash to draw.
4743
4744\cw{redraw()} will never be called with \c{flash_time} non-zero
4745unless \cw{flash_length()} was first called to tell the mid-end that
4746a flash was required; so whenever \cw{redraw()} notices that
4747\c{flash_time} is non-zero, you can be sure that the field in
4748\c{game_ui} is correctly set.
4749
4750\S{writing-move-anim} Animating game moves
4751
4752A number of puzzle types benefit from a quick animation of each move
4753you make.
4754
4755For some games, such as Fifteen, this is particularly easy. Whenever
4756\cw{redraw()} is called with \c{oldstate} non-\cw{NULL}, Fifteen
4757simply compares the position of each tile in the two game states,
4758and if the tile is not in the same place then it draws it some
4759fraction of the way from its old position to its new position. This
4760method copes automatically with undo.
4761
4762Other games are less obvious. In Sixteen, for example, you can't
4763just draw each tile a fraction of the way from its old to its new
4764position: if you did that, the end tile would zip very rapidly past
4765all the others to get to the other end and that would look silly.
4766(Worse, it would look inconsistent if the end tile was drawn on top
4767going one way and on the bottom going the other way.)
4768
4769A useful trick here is to define a field or two in the game state
4770that indicates what the last move was.
4771
4772\b Add a \q{last move} field to the \c{game_state} (or two or more
4773fields if the move is complex enough to need them).
4774
4775\b \cw{new_game()} initialises this field to a null value for a new
4776game state.
4777
4778\b \cw{execute_move()} sets up the field to reflect the move it just
4779performed.
4780
4781\b \cw{redraw()} now needs to examine its \c{dir} parameter. If
4782\c{dir} is positive, it determines the move being animated by
4783looking at the last-move field in \c{newstate}; but if \c{dir} is
4784negative, it has to look at the last-move field in \c{oldstate}, and
4785invert whatever move it finds there.
4786
4787Note also that Sixteen needs to store the \e{direction} of the move,
4788because you can't quite determine it by examining the row or column
4789in question. You can in almost all cases, but when the row is
4790precisely two squares long it doesn't work since a move in either
4791direction looks the same. (You could argue that since moving a
47922-element row left and right has the same effect, it doesn't matter
4793which one you animate; but in fact it's very disorienting to click
4794the arrow left and find the row moving right, and almost as bad to
4795undo a move to the right and find the game animating \e{another}
4796move to the right.)
4797
4798\S{writing-conditional-anim} Animating drag operations
4799
4800In Untangle, moves are made by dragging a node from an old position
4801to a new position. Therefore, at the time when the move is initially
4802made, it should not be animated, because the node has already been
4803dragged to the right place and doesn't need moving there. However,
4804it's nice to animate the same move if it's later undone or redone.
4805This requires a bit of fiddling.
4806
4807The obvious approach is to have a flag in the \c{game_ui} which
4808inhibits move animation, and to set that flag in
4809\cw{interpret_move()}. The question is, when would the flag be reset
4810again? The obvious place to do so is \cw{changed_state()}, which
4811will be called once per move. But it will be called \e{before}
4812\cw{anim_length()}, so if it resets the flag then \cw{anim_length()}
4813will never see the flag set at all.
4814
4815The solution is to have \e{two} flags in a queue.
4816
4817\b Define two flags in \c{game_ui}; let's call them \q{current} and
4818\q{next}.
4819
4820\b Set both to \cw{FALSE} in \c{new_ui()}.
4821
4822\b When a drag operation completes in \cw{interpret_move()}, set the
4823\q{next} flag to \cw{TRUE}.
4824
4825\b Every time \cw{changed_state()} is called, set the value of
4826\q{current} to the value in \q{next}, and then set the value of
4827\q{next} to \cw{FALSE}.
4828
4829\b That way, \q{current} will be \cw{TRUE} \e{after} a call to
4830\cw{changed_state()} if and only if that call to
4831\cw{changed_state()} was the result of a drag operation processed by
4832\cw{interpret_move()}. Any other call to \cw{changed_state()}, due
4833to an Undo or a Redo or a Restart or a Solve, will leave \q{current}
4834\cw{FALSE}.
4835
4836\b So now \cw{anim_length()} can request a move animation if and
4837only if the \q{current} flag is \e{not} set.
4838
4839\S{writing-cheating} Inhibiting the victory flash when Solve is used
4840
4841Many games flash when you complete them, as a visual congratulation
4842for having got to the end of the puzzle. It often seems like a good
4843idea to disable that flash when the puzzle is brought to a solved
4844state by means of the Solve operation.
4845
4846This is easily done:
4847
4848\b Add a \q{cheated} flag to the \c{game_state}.
4849
4850\b Set this flag to \cw{FALSE} in \cw{new_game()}.
4851
4852\b Have \cw{solve()} return a move description string which clearly
4853identifies the move as a solve operation.
4854
4855\b Have \cw{execute_move()} respond to that clear identification by
4856setting the \q{cheated} flag in the returned \c{game_state}. The
4857flag will then be propagated to all subsequent game states, even if
4858the user continues fiddling with the game after it is solved.
4859
4860\b \cw{flash_length()} now returns non-zero if \c{oldstate} is not
4861completed and \c{newstate} is, \e{and} neither state has the
4862\q{cheated} flag set.
4863
4864\H{writing-testing} Things to test once your puzzle is written
4865
4866Puzzle implementations written in this framework are self-testing as
4867far as I could make them.
4868
4869Textual game and move descriptions, for example, are generated and
4870parsed as part of the normal process of play. Therefore, if you can
4871make moves in the game \e{at all} you can be reasonably confident
4872that the mid-end serialisation interface will function correctly and
4873you will be able to save your game. (By contrast, if I'd stuck with
4874a single \cw{make_move()} function performing the jobs of both
4875\cw{interpret_move()} and \cw{execute_move()}, and had separate
4876functions to encode and decode a game state in string form, then
4877those functions would not be used during normal play; so they could
4878have been completely broken, and you'd never know it until you tried
4879to save the game \dash which would have meant you'd have to test
4880game saving \e{extensively} and make sure to test every possible
4881type of game state. As an added bonus, doing it the way I did leads
4882to smaller save files.)
4883
4884There is one exception to this, which is the string encoding of the
4885\c{game_ui}. Most games do not store anything permanent in the
4886\c{game_ui}, and hence do not need to put anything in its encode and
4887decode functions; but if there is anything in there, you do need to
4888test game loading and saving to ensure those functions work
4889properly.
4890
4891It's also worth testing undo and redo of all operations, to ensure
4892that the redraw and the animations (if any) work properly. Failing
4893to animate undo properly seems to be a common error.
4894
4895Other than that, just use your common sense.
diff --git a/apps/plugins/puzzles/src/divvy.c b/apps/plugins/puzzles/src/divvy.c
new file mode 100644
index 0000000000..517e3ddbb0
--- /dev/null
+++ b/apps/plugins/puzzles/src/divvy.c
@@ -0,0 +1,781 @@
1/*
2 * Library code to divide up a rectangle into a number of equally
3 * sized ominoes, in a random fashion.
4 *
5 * Could use this for generating solved grids of
6 * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/
7 * or for generating the playfield for Jigsaw Sudoku.
8 */
9
10/*
11 * This code is restricted to simply connected solutions: that is,
12 * no single polyomino may completely surround another (not even
13 * with a corner visible to the outside world, in the sense that a
14 * 7-omino can `surround' a single square).
15 *
16 * It's tempting to think that this is a natural consequence of
17 * all the ominoes being the same size - after all, a division of
18 * anything into 7-ominoes must necessarily have all of them
19 * simply connected, because if one was not then the 1-square
20 * space in the middle could not be part of any 7-omino - but in
21 * fact, for sufficiently large k, it is perfectly possible for a
22 * k-omino to completely surround another k-omino. A simple
23 * example is this one with two 25-ominoes:
24 *
25 * +--+--+--+--+--+--+--+
26 * | |
27 * + +--+--+--+--+--+ +
28 * | | | |
29 * + + + +
30 * | | | |
31 * + + + +--+
32 * | | | |
33 * + + + +--+
34 * | | | |
35 * + + + +
36 * | | | |
37 * + +--+--+--+--+--+ +
38 * | |
39 * +--+--+--+--+--+--+--+
40 *
41 * I claim the smallest k which can manage this is 23. More
42 * formally:
43 *
44 * If a k-omino P is completely surrounded by another k-omino Q,
45 * such that every edge of P borders on Q, then k >= 23.
46 *
47 * Proof:
48 *
49 * It's relatively simple to find the largest _rectangle_ a
50 * k-omino can enclose. So I'll construct my proof in two parts:
51 * firstly, show that no 22-omino or smaller can enclose a
52 * rectangle as large as itself, and secondly, show that no
53 * polyomino can enclose a larger non-rectangle than a rectangle.
54 *
55 * The first of those claims:
56 *
57 * To surround an m x n rectangle, a polyomino must have 2m
58 * squares along the two m-sides of the rectangle, 2n squares
59 * along the two n-sides, and must fill in at least three of the
60 * corners in order to be connected. Thus, 2(m+n)+3 <= k. We wish
61 * to find the largest value of mn subject to that constraint, and
62 * it's clear that this is achieved when m and n are as close to
63 * equal as possible. (If they aren't, WLOG suppose m < n; then
64 * (m+1)(n-1) = mn + n - m - 1 >= mn, with equality only when
65 * m=n-1.)
66 *
67 * So the area of the largest rectangle which can be enclosed by a
68 * k-omino is given by floor(k'/2) * ceil(k'/2), where k' =
69 * (k-3)/2. This is a monotonic function in k, so there will be a
70 * unique point at which it goes from being smaller than k to
71 * being larger than k. That point is between 22 (maximum area 20)
72 * and 23 (maximum area 25).
73 *
74 * The second claim:
75 *
76 * Suppose we have an inner polyomino P surrounded by an outer
77 * polyomino Q. I seek to show that if P is non-rectangular, then
78 * P is also non-maximal, in the sense that we can transform P and
79 * Q into a new pair of polyominoes in which P is larger and Q is
80 * at most the same size.
81 *
82 * Consider walking along the boundary of P in a clockwise
83 * direction. (We may assume, of course, that there is only _one_
84 * boundary of P, i.e. P has no hole in the middle. If it does
85 * have a hole in the middle, it's _trivially_ non-maximal because
86 * we can just fill the hole in!) Our walk will take us along many
87 * edges between squares; sometimes we might turn left, and
88 * certainly sometimes we will turn right. Always there will be a
89 * square of P on our right, and a square of Q on our left.
90 *
91 * The net angle through which we turn during the entire walk must
92 * add up to 360 degrees rightwards. So if there are no left
93 * turns, then we must turn right exactly four times, meaning we
94 * have described a rectangle. Hence, if P is _not_ rectangular,
95 * then there must have been a left turn at some point. A left
96 * turn must mean we walk along two edges of the same square of Q.
97 *
98 * Thus, there is some square X in Q which is adjacent to two
99 * diagonally separated squares in P. Let us call those two
100 * squares N and E; let us refer to the other two neighbours of X
101 * as S and W; let us refer to the other mutual neighbour of S and
102 * W as D; and let us refer to the other mutual neighbour of S and
103 * E as Y. In other words, we have named seven squares, arranged
104 * thus:
105 *
106 * N
107 * W X E
108 * D S Y
109 *
110 * where N and E are in P, and X is in Q.
111 *
112 * Clearly at least one of W and S must be in Q (because otherwise
113 * X would not be connected to any other square in Q, and would
114 * hence have to be the whole of Q; and evidently if Q were a
115 * 1-omino it could not enclose _anything_). So we divide into
116 * cases:
117 *
118 * If both W and S are in Q, then we take X out of Q and put it in
119 * P, which does not expose any edge of P. If this disconnects Q,
120 * then we can reconnect it by adding D to Q.
121 *
122 * If only one of W and S is in Q, then wlog let it be W. If S is
123 * in _P_, then we have a particularly easy case: we can simply
124 * take X out of Q and add it to P, and this cannot disconnect X
125 * since X was a leaf square of Q.
126 *
127 * Our remaining case is that W is in Q and S is in neither P nor
128 * Q. Again we take X out of Q and put it in P; we also add S to
129 * Q. This ensures we do not expose an edge of P, but we must now
130 * prove that S is adjacent to some other existing square of Q so
131 * that we haven't disconnected Q by adding it.
132 *
133 * To do this, we recall that we walked along the edge XE, and
134 * then turned left to walk along XN. So just before doing all
135 * that, we must have reached the corner XSE, and we must have
136 * done it by walking along one of the three edges meeting at that
137 * corner which are _not_ XE. It can't have been SY, since S would
138 * then have been on our left and it isn't in Q; and it can't have
139 * been XS, since S would then have been on our right and it isn't
140 * in P. So it must have been YE, in which case Y was on our left,
141 * and hence is in Q.
142 *
143 * So in all cases we have shown that we can take X out of Q and
144 * add it to P, and add at most one square to Q to restore the
145 * containment and connectedness properties. Hence, we can keep
146 * doing this until we run out of left turns and P becomes
147 * rectangular. []
148 *
149 * ------------
150 *
151 * Anyway, that entire proof was a bit of a sidetrack. The point
152 * is, although constructions of this type are possible for
153 * sufficiently large k, divvy_rectangle() will never generate
154 * them. This could be considered a weakness for some purposes, in
155 * the sense that we can't generate all possible divisions.
156 * However, there are many divisions which we are highly unlikely
157 * to generate anyway, so in practice it probably isn't _too_ bad.
158 *
159 * If I wanted to fix this issue, I would have to make the rules
160 * more complicated for determining when a square can safely be
161 * _removed_ from a polyomino. Adding one becomes easier (a square
162 * may be added to a polyomino iff it is 4-adjacent to any square
163 * currently part of the polyomino, and the current test for loop
164 * formation may be dispensed with), but to determine which
165 * squares may be removed we must now resort to analysis of the
166 * overall structure of the polyomino rather than the simple local
167 * properties we can currently get away with measuring.
168 */
169
170/*
171 * Possible improvements which might cut the fail rate:
172 *
173 * - instead of picking one omino to extend in an iteration, try
174 * them all in succession (in a randomised order)
175 *
176 * - (for real rigour) instead of bfsing over ominoes, bfs over
177 * the space of possible _removed squares_. That way we aren't
178 * limited to randomly choosing a single square to remove from
179 * an omino and failing if that particular square doesn't
180 * happen to work.
181 *
182 * However, I don't currently think it's necessary to do either of
183 * these, because the failure rate is already low enough to be
184 * easily tolerable, under all circumstances I've been able to
185 * think of.
186 */
187
188#include <assert.h>
189#include <stdio.h>
190#include <stdlib.h>
191#include <stddef.h>
192
193#include "puzzles.h"
194
195/*
196 * Subroutine which implements a function used in computing both
197 * whether a square can safely be added to an omino, and whether
198 * it can safely be removed.
199 *
200 * We enumerate the eight squares 8-adjacent to this one, in
201 * cyclic order. We go round that loop and count the number of
202 * times we find a square owned by the target omino next to one
203 * not owned by it. We then return success iff that count is 2.
204 *
205 * When adding a square to an omino, this is precisely the
206 * criterion which tells us that adding the square won't leave a
207 * hole in the middle of the omino. (If it did, then things get
208 * more complicated; see above.)
209 *
210 * When removing a square from an omino, the _same_ criterion
211 * tells us that removing the square won't disconnect the omino.
212 * (This only works _because_ we've ensured the omino is simply
213 * connected.)
214 */
215static int addremcommon(int w, int h, int x, int y, int *own, int val)
216{
217 int neighbours[8];
218 int dir, count;
219
220 for (dir = 0; dir < 8; dir++) {
221 int dx = ((dir & 3) == 2 ? 0 : dir > 2 && dir < 6 ? +1 : -1);
222 int dy = ((dir & 3) == 0 ? 0 : dir < 4 ? -1 : +1);
223 int sx = x+dx, sy = y+dy;
224
225 if (sx < 0 || sx >= w || sy < 0 || sy >= h)
226 neighbours[dir] = -1; /* outside the grid */
227 else
228 neighbours[dir] = own[sy*w+sx];
229 }
230
231 /*
232 * To begin with, check 4-adjacency.
233 */
234 if (neighbours[0] != val && neighbours[2] != val &&
235 neighbours[4] != val && neighbours[6] != val)
236 return FALSE;
237
238 count = 0;
239
240 for (dir = 0; dir < 8; dir++) {
241 int next = (dir + 1) & 7;
242 int gotthis = (neighbours[dir] == val);
243 int gotnext = (neighbours[next] == val);
244
245 if (gotthis != gotnext)
246 count++;
247 }
248
249 return (count == 2);
250}
251
252/*
253 * w and h are the dimensions of the rectangle.
254 *
255 * k is the size of the required ominoes. (So k must divide w*h,
256 * of course.)
257 *
258 * The returned result is a w*h-sized dsf.
259 *
260 * In both of the above suggested use cases, the user would
261 * probably want w==h==k, but that isn't a requirement.
262 */
263static int *divvy_internal(int w, int h, int k, random_state *rs)
264{
265 int *order, *queue, *tmp, *own, *sizes, *addable, *removable, *retdsf;
266 int wh = w*h;
267 int i, j, n, x, y, qhead, qtail;
268
269 n = wh / k;
270 assert(wh == k*n);
271
272 order = snewn(wh, int);
273 tmp = snewn(wh, int);
274 own = snewn(wh, int);
275 sizes = snewn(n, int);
276 queue = snewn(n, int);
277 addable = snewn(wh*4, int);
278 removable = snewn(wh, int);
279
280 /*
281 * Permute the grid squares into a random order, which will be
282 * used for iterating over the grid whenever we need to search
283 * for something. This prevents directional bias and arranges
284 * for the answer to be non-deterministic.
285 */
286 for (i = 0; i < wh; i++)
287 order[i] = i;
288 shuffle(order, wh, sizeof(*order), rs);
289
290 /*
291 * Begin by choosing a starting square at random for each
292 * omino.
293 */
294 for (i = 0; i < wh; i++) {
295 own[i] = -1;
296 }
297 for (i = 0; i < n; i++) {
298 own[order[i]] = i;
299 sizes[i] = 1;
300 }
301
302 /*
303 * Now repeatedly pick a random omino which isn't already at
304 * the target size, and find a way to expand it by one. This
305 * may involve stealing a square from another omino, in which
306 * case we then re-expand that omino, forming a chain of
307 * square-stealing which terminates in an as yet unclaimed
308 * square. Hence every successful iteration around this loop
309 * causes the number of unclaimed squares to drop by one, and
310 * so the process is bounded in duration.
311 */
312 while (1) {
313
314#ifdef DIVVY_DIAGNOSTICS
315 {
316 int x, y;
317 printf("Top of loop. Current grid:\n");
318 for (y = 0; y < h; y++) {
319 for (x = 0; x < w; x++)
320 printf("%3d", own[y*w+x]);
321 printf("\n");
322 }
323 }
324#endif
325
326 /*
327 * Go over the grid and figure out which squares can
328 * safely be added to, or removed from, each omino. We
329 * don't take account of other ominoes in this process, so
330 * we will often end up knowing that a square can be
331 * poached from one omino by another.
332 *
333 * For each square, there may be up to four ominoes to
334 * which it can be added (those to which it is
335 * 4-adjacent).
336 */
337 for (y = 0; y < h; y++) {
338 for (x = 0; x < w; x++) {
339 int yx = y*w+x;
340 int curr = own[yx];
341 int dir;
342
343 if (curr < 0) {
344 removable[yx] = FALSE; /* can't remove if not owned! */
345 } else if (sizes[curr] == 1) {
346 removable[yx] = TRUE; /* can always remove a singleton */
347 } else {
348 /*
349 * See if this square can be removed from its
350 * omino without disconnecting it.
351 */
352 removable[yx] = addremcommon(w, h, x, y, own, curr);
353 }
354
355 for (dir = 0; dir < 4; dir++) {
356 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
357 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
358 int sx = x + dx, sy = y + dy;
359 int syx = sy*w+sx;
360
361 addable[yx*4+dir] = -1;
362
363 if (sx < 0 || sx >= w || sy < 0 || sy >= h)
364 continue; /* no omino here! */
365 if (own[syx] < 0)
366 continue; /* also no omino here */
367 if (own[syx] == own[yx])
368 continue; /* we already got one */
369 if (!addremcommon(w, h, x, y, own, own[syx]))
370 continue; /* would non-simply connect the omino */
371
372 addable[yx*4+dir] = own[syx];
373 }
374 }
375 }
376
377 for (i = j = 0; i < n; i++)
378 if (sizes[i] < k)
379 tmp[j++] = i;
380 if (j == 0)
381 break; /* all ominoes are complete! */
382 j = tmp[random_upto(rs, j)];
383#ifdef DIVVY_DIAGNOSTICS
384 printf("Trying to extend %d\n", j);
385#endif
386
387 /*
388 * So we're trying to expand omino j. We breadth-first
389 * search out from j across the space of ominoes.
390 *
391 * For bfs purposes, we use two elements of tmp per omino:
392 * tmp[2*i+0] tells us which omino we got to i from, and
393 * tmp[2*i+1] numbers the grid square that omino stole
394 * from us.
395 *
396 * This requires that wh (the size of tmp) is at least 2n,
397 * i.e. k is at least 2. There would have been nothing to
398 * stop a user calling this function with k=1, but if they
399 * did then we wouldn't have got to _here_ in the code -
400 * we would have noticed above that all ominoes were
401 * already at their target sizes, and terminated :-)
402 */
403 assert(wh >= 2*n);
404 for (i = 0; i < n; i++)
405 tmp[2*i] = tmp[2*i+1] = -1;
406 qhead = qtail = 0;
407 queue[qtail++] = j;
408 tmp[2*j] = tmp[2*j+1] = -2; /* special value: `starting point' */
409
410 while (qhead < qtail) {
411 int tmpsq;
412
413 j = queue[qhead];
414
415 /*
416 * We wish to expand omino j. However, we might have
417 * got here by omino j having a square stolen from it,
418 * so first of all we must temporarily mark that
419 * square as not belonging to j, so that our adjacency
420 * calculations don't assume j _does_ belong to us.
421 */
422 tmpsq = tmp[2*j+1];
423 if (tmpsq >= 0) {
424 assert(own[tmpsq] == j);
425 own[tmpsq] = -3;
426 }
427
428 /*
429 * OK. Now begin by seeing if we can find any
430 * unclaimed square into which we can expand omino j.
431 * If we find one, the entire bfs terminates.
432 */
433 for (i = 0; i < wh; i++) {
434 int dir;
435
436 if (own[order[i]] != -1)
437 continue; /* this square is claimed */
438
439 /*
440 * Special case: if our current omino was size 1
441 * and then had a square stolen from it, it's now
442 * size zero, which means it's valid to `expand'
443 * it into _any_ unclaimed square.
444 */
445 if (sizes[j] == 1 && tmpsq >= 0)
446 break; /* got one */
447
448 /*
449 * Failing that, we must do the full test for
450 * addability.
451 */
452 for (dir = 0; dir < 4; dir++)
453 if (addable[order[i]*4+dir] == j) {
454 /*
455 * We know this square is addable to this
456 * omino with the grid in the state it had
457 * at the top of the loop. However, we
458 * must now check that it's _still_
459 * addable to this omino when the omino is
460 * missing a square. To do this it's only
461 * necessary to re-check addremcommon.
462 */
463 if (!addremcommon(w, h, order[i]%w, order[i]/w,
464 own, j))
465 continue;
466 break;
467 }
468 if (dir == 4)
469 continue; /* we can't add this square to j */
470
471 break; /* got one! */
472 }
473 if (i < wh) {
474 i = order[i];
475
476 /*
477 * Restore the temporarily removed square _before_
478 * we start shifting ownerships about.
479 */
480 if (tmpsq >= 0)
481 own[tmpsq] = j;
482
483 /*
484 * We are done. We can add square i to omino j,
485 * and then backtrack along the trail in tmp
486 * moving squares between ominoes, ending up
487 * expanding our starting omino by one.
488 */
489#ifdef DIVVY_DIAGNOSTICS
490 printf("(%d,%d)", i%w, i/w);
491#endif
492 while (1) {
493 own[i] = j;
494#ifdef DIVVY_DIAGNOSTICS
495 printf(" -> %d", j);
496#endif
497 if (tmp[2*j] == -2)
498 break;
499 i = tmp[2*j+1];
500 j = tmp[2*j];
501#ifdef DIVVY_DIAGNOSTICS
502 printf("; (%d,%d)", i%w, i/w);
503#endif
504 }
505#ifdef DIVVY_DIAGNOSTICS
506 printf("\n");
507#endif
508
509 /*
510 * Increment the size of the starting omino.
511 */
512 sizes[j]++;
513
514 /*
515 * Terminate the bfs loop.
516 */
517 break;
518 }
519
520 /*
521 * If we get here, we haven't been able to expand
522 * omino j into an unclaimed square. So now we begin
523 * to investigate expanding it into squares which are
524 * claimed by ominoes the bfs has not yet visited.
525 */
526 for (i = 0; i < wh; i++) {
527 int dir, nj;
528
529 nj = own[order[i]];
530 if (nj < 0 || tmp[2*nj] != -1)
531 continue; /* unclaimed, or owned by wrong omino */
532 if (!removable[order[i]])
533 continue; /* its omino won't let it go */
534
535 for (dir = 0; dir < 4; dir++)
536 if (addable[order[i]*4+dir] == j) {
537 /*
538 * As above, re-check addremcommon.
539 */
540 if (!addremcommon(w, h, order[i]%w, order[i]/w,
541 own, j))
542 continue;
543
544 /*
545 * We have found a square we can use to
546 * expand omino j, at the expense of the
547 * as-yet unvisited omino nj. So add this
548 * to the bfs queue.
549 */
550 assert(qtail < n);
551 queue[qtail++] = nj;
552 tmp[2*nj] = j;
553 tmp[2*nj+1] = order[i];
554
555 /*
556 * Now terminate the loop over dir, to
557 * ensure we don't accidentally add the
558 * same omino twice to the queue.
559 */
560 break;
561 }
562 }
563
564 /*
565 * Restore the temporarily removed square.
566 */
567 if (tmpsq >= 0)
568 own[tmpsq] = j;
569
570 /*
571 * Advance the queue head.
572 */
573 qhead++;
574 }
575
576 if (qhead == qtail) {
577 /*
578 * We have finished the bfs and not found any way to
579 * expand omino j. Panic, and return failure.
580 *
581 * FIXME: or should we loop over all ominoes before we
582 * give up?
583 */
584#ifdef DIVVY_DIAGNOSTICS
585 printf("FAIL!\n");
586#endif
587 retdsf = NULL;
588 goto cleanup;
589 }
590 }
591
592#ifdef DIVVY_DIAGNOSTICS
593 {
594 int x, y;
595 printf("SUCCESS! Final grid:\n");
596 for (y = 0; y < h; y++) {
597 for (x = 0; x < w; x++)
598 printf("%3d", own[y*w+x]);
599 printf("\n");
600 }
601 }
602#endif
603
604 /*
605 * Construct the output dsf.
606 */
607 for (i = 0; i < wh; i++) {
608 assert(own[i] >= 0 && own[i] < n);
609 tmp[own[i]] = i;
610 }
611 retdsf = snew_dsf(wh);
612 for (i = 0; i < wh; i++) {
613 dsf_merge(retdsf, i, tmp[own[i]]);
614 }
615
616 /*
617 * Construct the output dsf a different way, to verify that
618 * the ominoes really are k-ominoes and we haven't
619 * accidentally split one into two disconnected pieces.
620 */
621 dsf_init(tmp, wh);
622 for (y = 0; y < h; y++)
623 for (x = 0; x+1 < w; x++)
624 if (own[y*w+x] == own[y*w+(x+1)])
625 dsf_merge(tmp, y*w+x, y*w+(x+1));
626 for (x = 0; x < w; x++)
627 for (y = 0; y+1 < h; y++)
628 if (own[y*w+x] == own[(y+1)*w+x])
629 dsf_merge(tmp, y*w+x, (y+1)*w+x);
630 for (i = 0; i < wh; i++) {
631 j = dsf_canonify(retdsf, i);
632 assert(dsf_canonify(tmp, j) == dsf_canonify(tmp, i));
633 }
634
635 cleanup:
636
637 /*
638 * Free our temporary working space.
639 */
640 sfree(order);
641 sfree(tmp);
642 sfree(own);
643 sfree(sizes);
644 sfree(queue);
645 sfree(addable);
646 sfree(removable);
647
648 /*
649 * And we're done.
650 */
651 return retdsf;
652}
653
654#ifdef TESTMODE
655static int fail_counter = 0;
656#endif
657
658int *divvy_rectangle(int w, int h, int k, random_state *rs)
659{
660 int *ret;
661
662 do {
663 ret = divvy_internal(w, h, k, rs);
664
665#ifdef TESTMODE
666 if (!ret)
667 fail_counter++;
668#endif
669
670 } while (!ret);
671
672 return ret;
673}
674
675#ifdef TESTMODE
676
677/*
678 * gcc -g -O0 -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
679 *
680 * or to debug
681 *
682 * gcc -g -O0 -DDIVVY_DIAGNOSTICS -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
683 */
684
685int main(int argc, char **argv)
686{
687 int *dsf;
688 int i;
689 int w = 9, h = 4, k = 6, tries = 100;
690 random_state *rs;
691
692 rs = random_new("123456", 6);
693
694 if (argc > 1)
695 w = atoi(argv[1]);
696 if (argc > 2)
697 h = atoi(argv[2]);
698 if (argc > 3)
699 k = atoi(argv[3]);
700 if (argc > 4)
701 tries = atoi(argv[4]);
702
703 for (i = 0; i < tries; i++) {
704 int x, y;
705
706 dsf = divvy_rectangle(w, h, k, rs);
707 assert(dsf);
708
709 for (y = 0; y <= 2*h; y++) {
710 for (x = 0; x <= 2*w; x++) {
711 int miny = y/2 - 1, maxy = y/2;
712 int minx = x/2 - 1, maxx = x/2;
713 int classes[4], tx, ty;
714 for (ty = 0; ty < 2; ty++)
715 for (tx = 0; tx < 2; tx++) {
716 int cx = minx+tx, cy = miny+ty;
717 if (cx < 0 || cx >= w || cy < 0 || cy >= h)
718 classes[ty*2+tx] = -1;
719 else
720 classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx);
721 }
722 switch (y%2 * 2 + x%2) {
723 case 0: /* corner */
724 /*
725 * Cases for the corner:
726 *
727 * - if all four surrounding squares belong
728 * to the same omino, we print a space.
729 *
730 * - if the top two are the same and the
731 * bottom two are the same, we print a
732 * horizontal line.
733 *
734 * - if the left two are the same and the
735 * right two are the same, we print a
736 * vertical line.
737 *
738 * - otherwise, we print a cross.
739 */
740 if (classes[0] == classes[1] &&
741 classes[1] == classes[2] &&
742 classes[2] == classes[3])
743 printf(" ");
744 else if (classes[0] == classes[1] &&
745 classes[2] == classes[3])
746 printf("-");
747 else if (classes[0] == classes[2] &&
748 classes[1] == classes[3])
749 printf("|");
750 else
751 printf("+");
752 break;
753 case 1: /* horiz edge */
754 if (classes[1] == classes[3])
755 printf(" ");
756 else
757 printf("--");
758 break;
759 case 2: /* vert edge */
760 if (classes[2] == classes[3])
761 printf(" ");
762 else
763 printf("|");
764 break;
765 case 3: /* square centre */
766 printf(" ");
767 break;
768 }
769 }
770 printf("\n");
771 }
772 printf("\n");
773 sfree(dsf);
774 }
775
776 printf("%d retries needed for %d successes\n", fail_counter, tries);
777
778 return 0;
779}
780
781#endif
diff --git a/apps/plugins/puzzles/src/docindex.html b/apps/plugins/puzzles/src/docindex.html
new file mode 100644
index 0000000000..8ed483bbe1
--- /dev/null
+++ b/apps/plugins/puzzles/src/docindex.html
@@ -0,0 +1,217 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title></title>
7<link rel="previous" href="licence.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10</head>
11<body>
12<p><a href="licence.html">Previous</a> | <a href="index.html">Contents</a> | Index | Next</p>
13<h1><a name="Index"></a>Index</h1><p>
14Black Box: <a href="blackbox.html#i0">Chapter 19</a><br>
15Bridges: <a href="bridges.html#i0">Chapter 26</a><br>
16bugs: <a href="intro.html#i7">Chapter 1</a><br>
17command line: <a href="common.html#i23">Section 2.2</a>, <a href="common.html#i32">Section 2.4</a>, <a href="common.html#i36">Section 2.5</a><br>
18common features: <a href="common.html#i0">Chapter 2</a><br>
19controls: <a href="common.html#i1">Section 2.1</a><br>
20controls, for Black Box: <a href="blackbox.html#i1">Section 19.1</a><br>
21controls, for Bridges: <a href="bridges.html#i2">Section 26.1</a><br>
22controls, for Cube: <a href="cube.html#i1">Section 4.1</a><br>
23controls, for Dominosa: <a href="dominosa.html#i1">Section 17.1</a><br>
24controls, for Fifteen: <a href="fifteen.html#i2">Section 5.1</a><br>
25controls, for Filling: <a href="filling.html#i2">Section 29.1</a><br>
26controls, for Flip: <a href="flip.html#i1">Section 14.1</a><br>
27controls, for Flood: <a href="flood.html#i1">Section 39.1</a><br>
28controls, for Galaxies: <a href="galaxies.html#i2">Section 28.1</a><br>
29controls, for Guess: <a href="guess.html#i2">Section 15.1</a><br>
30controls, for Inertia: <a href="inertia.html#i1">Section 24.1</a><br>
31controls, for Keen: <a href="keen.html#i2">Section 30.1</a><br>
32controls, for Light Up: <a href="lightup.html#i2">Section 21.1</a><br>
33controls, for Loopy: <a href="loopy.html#i2">Section 23.1</a><br>
34controls, for Magnets: <a href="magnets.html#i2">Section 33.1</a><br>
35controls, for Map: <a href="map.html#i2">Section 22.1</a><br>
36controls, for Mines: <a href="mines.html#i1">Section 12.1</a><br>
37controls, for Net: <a href="net.html#i5">Section 3.1</a><br>
38controls, for Netslide: <a href="netslide.html#i1">Chapter 9</a><br>
39controls, for Palisade: <a href="palisade.html#i2">Section 41.1</a><br>
40controls, for Pattern: <a href="pattern.html#i2">Section 10.1</a><br>
41controls, for Pearl: <a href="pearl.html#i2">Section 36.1</a><br>
42controls, for Pegs: <a href="pegs.html#i2">Section 16.1</a><br>
43controls, for Range: <a href="range.html#i2">Section 35.1</a><br>
44controls, for Rectangles: <a href="rect.html#i3">Section 8.1</a><br>
45controls, for Same Game: <a href="samegame.html#i1">Section 13.1</a><br>
46controls, for Signpost: <a href="signpost.html#i2">Section 34.1</a><br>
47controls, for Singles: <a href="singles.html#i3">Section 32.1</a><br>
48controls, for Sixteen: <a href="sixteen.html#i1">Section 6.1</a><br>
49controls, for Slant: <a href="slant.html#i2">Section 20.1</a><br>
50controls, for Solo: <a href="solo.html#i2">Section 11.1</a><br>
51controls, for Tents: <a href="tents.html#i1">Section 25.1</a><br>
52controls, for Towers: <a href="towers.html#i2">Section 31.1</a><br>
53controls, for Tracks: <a href="tracks.html#i1">Section 40.1</a><br>
54controls, for Twiddle: <a href="twiddle.html#i1">Section 7.1</a><br>
55controls, for Undead: <a href="undead.html#i1">Section 37.1</a><br>
56controls, for Unequal: <a href="unequal.html#i3">Section 27.1</a><br>
57controls, for Unruly: <a href="unruly.html#i1">Section 38.1</a><br>
58controls, for Untangle: <a href="untangle.html#i2">Section 18.1</a><br>
59copy: <a href="common.html#i14">Section 2.1</a><br>
60copyright: <a href="licence.html#i2">Appendix A</a><br>
61Cube: <a href="cube.html#i0">Chapter 4</a><br>
62&#8216;Custom&#8217;, menu option: <a href="common.html#i30">Section 2.3</a><br>
63default parameters, specifying: <a href="common.html#i34">Section 2.4</a><br>
64Dominosa: <a href="dominosa.html#i0">Chapter 17</a><br>
65Edit menu: <a href="common.html#i6">Section 2.1</a><br>
66exit: <a href="common.html#i16">Section 2.1</a><br>
67feedback: <a href="intro.html#i6">Chapter 1</a><br>
68Fifteen: <a href="fifteen.html#i0">Chapter 5</a><br>
69File menu: <a href="common.html#i5">Section 2.1</a><br>
70Filling: <a href="filling.html#i0">Chapter 29</a><br>
71Flip: <a href="flip.html#i0">Chapter 14</a><br>
72Flood: <a href="flood.html#i0">Chapter 39</a><br>
73format, ID: <a href="common.html#i26">Section 2.2</a><br>
74four-colouring: <a href="map.html#i1">Chapter 22</a><br>
75FreeNet: <a href="net.html#i3">Chapter 3</a><br>
76Futoshiki: <a href="unequal.html#i2">Chapter 27</a><br>
77Galaxies: <a href="galaxies.html#i0">Chapter 28</a><br>
78game ID: <a href="common.html#i18">Section 2.2</a><br>
79game ID, format: <a href="common.html#i26">Section 2.2</a><br>
80game ID, generating: <a href="common.html#i39">Section 2.5</a><br>
81Game menu: <a href="common.html#i2">Section 2.1</a>, <a href="common.html#i21">Section 2.2</a><br>
82generating game IDs: <a href="common.html#i39">Section 2.5</a><br>
83Guess: <a href="guess.html#i0">Chapter 15</a><br>
84Hitori: <a href="singles.html#i2">Chapter 32</a><br>
85ID format: <a href="common.html#i26">Section 2.2</a><br>
86ID, game: <a href="common.html#i18">Section 2.2</a><br>
87Inertia: <a href="inertia.html#i0">Chapter 24</a><br>
88initial state: <a href="common.html#i24">Section 2.2</a><br>
89Janko: <a href="magnets.html#i1">Chapter 33</a>, <a href="signpost.html#i1">Chapter 34</a><br>
90Keen: <a href="keen.html#i0">Chapter 30</a><br>
91KenKen: <a href="keen.html#i1">Chapter 30</a><br>
92keys: <a href="common.html#i3">Section 2.1</a><br>
93keys, for Black Box: <a href="blackbox.html#i1">Section 19.1</a><br>
94keys, for Cube: <a href="cube.html#i1">Section 4.1</a><br>
95keys, for Fifteen: <a href="fifteen.html#i2">Section 5.1</a><br>
96keys, for Flip: <a href="flip.html#i1">Section 14.1</a><br>
97keys, for Guess: <a href="guess.html#i2">Section 15.1</a><br>
98keys, for Inertia: <a href="inertia.html#i1">Section 24.1</a><br>
99keys, for Net: <a href="net.html#i5">Section 3.1</a><br>
100keys, for Same Game: <a href="samegame.html#i1">Section 13.1</a><br>
101Latin square: <a href="unequal.html#i1">Chapter 27</a><br>
102licence: <a href="licence.html#i1">Appendix A</a><br>
103licence, MIT: <a href="intro.html#i3">Chapter 1</a>, <a href="licence.html#i0">Appendix A</a><br>
104Light Up: <a href="lightup.html#i0">Chapter 21</a><br>
105Linux: <a href="intro.html#i0">Chapter 1</a>, <a href="common.html#i35">Section 2.5</a><br>
106load: <a href="common.html#i9">Section 2.1</a>, <a href="common.html#i38">Section 2.5</a><br>
107Loopy: <a href="loopy.html#i0">Chapter 23</a><br>
108Mac OS X: <a href="intro.html#i2">Chapter 1</a>, <a href="common.html#i4">Section 2.1</a>, <a href="common.html#i22">Section 2.2</a>, <a href="common.html#i33">Section 2.4</a><br>
109Magnets: <a href="magnets.html#i0">Chapter 33</a><br>
110Map: <a href="map.html#i0">Chapter 22</a><br>
111Mastermind: <a href="guess.html#i1">Chapter 15</a><br>
112Mines: <a href="mines.html#i0">Chapter 12</a><br>
113MIT licence: <a href="intro.html#i3">Chapter 1</a>, <a href="licence.html#i0">Appendix A</a><br>
114Net: <a href="net.html#i0">Chapter 3</a><br>
115<code>NETGAME.EXE</code>: <a href="net.html#i2">Chapter 3</a><br>
116Netslide: <a href="netslide.html#i0">Chapter 9</a><br>
117NetWalk: <a href="net.html#i4">Chapter 3</a><br>
118new game: <a href="common.html#i7">Section 2.1</a><br>
119Nikoli: <a href="rect.html#i1">Chapter 8</a>, <a href="solo.html#i1">Chapter 11</a>, <a href="slant.html#i1">Chapter 20</a>, <a href="lightup.html#i1">Chapter 21</a>, <a href="loopy.html#i1">Chapter 23</a>, <a href="bridges.html#i1">Chapter 26</a>, <a href="galaxies.html#i1">Chapter 28</a>, <a href="filling.html#i1">Chapter 29</a>, <a href="singles.html#i1">Chapter 32</a>, <a href="range.html#i1">Chapter 35</a>, <a href="pearl.html#i1">Chapter 36</a>, <a href="palisade.html#i1">Chapter 41</a><br>
120nonograms: <a href="pattern.html#i1">Chapter 10</a><br>
121Palisade: <a href="palisade.html#i0">Chapter 41</a><br>
122parameters: <a href="common.html#i27">Section 2.2</a>, <a href="common.html#i31">Section 2.3</a><br>
123parameters, for Black Box: <a href="blackbox.html#i2">Section 19.2</a><br>
124parameters, for Bridges: <a href="bridges.html#i3">Section 26.2</a><br>
125parameters, for Cube: <a href="cube.html#i2">Section 4.2</a><br>
126parameters, for Dominosa: <a href="dominosa.html#i2">Section 17.2</a><br>
127parameters, for Fifteen: <a href="fifteen.html#i3">Section 5.2</a><br>
128parameters, for Filling: <a href="filling.html#i3">Section 29.2</a><br>
129parameters, for flip: <a href="flip.html#i2">Section 14.2</a><br>
130parameters, for Flood: <a href="flood.html#i2">Section 39.2</a><br>
131parameters, for Galaxies: <a href="galaxies.html#i3">Section 28.2</a><br>
132parameters, for Guess: <a href="guess.html#i3">Section 15.2</a><br>
133parameters, for Inertia: <a href="inertia.html#i2">Section 24.2</a><br>
134parameters, for Keen: <a href="keen.html#i3">Section 30.2</a><br>
135parameters, for Light Up: <a href="lightup.html#i3">Section 21.2</a><br>
136parameters, for Loopy: <a href="loopy.html#i3">Section 23.2</a><br>
137parameters, for Magnets: <a href="magnets.html#i3">Section 33.2</a><br>
138parameters, for Map: <a href="map.html#i3">Section 22.2</a><br>
139parameters, for Mines: <a href="mines.html#i2">Section 12.2</a><br>
140parameters, for Net: <a href="net.html#i6">Section 3.2</a><br>
141parameters, for Netslide: <a href="netslide.html#i2">Chapter 9</a><br>
142parameters, for Palisade: <a href="palisade.html#i3">Section 41.2</a><br>
143parameters, for Pattern: <a href="pattern.html#i3">Section 10.2</a><br>
144parameters, for Pearl: <a href="pearl.html#i3">Section 36.2</a><br>
145parameters, for Pegs: <a href="pegs.html#i3">Section 16.2</a><br>
146parameters, for Range: <a href="range.html#i3">Section 35.2</a><br>
147parameters, for Rectangles: <a href="rect.html#i4">Section 8.2</a><br>
148parameters, for Same Game: <a href="samegame.html#i2">Section 13.2</a><br>
149parameters, for Signpost: <a href="signpost.html#i3">Section 34.2</a><br>
150parameters, for Singles: <a href="singles.html#i4">Section 32.2</a><br>
151parameters, for Sixteen: <a href="sixteen.html#i2">Section 6.2</a><br>
152parameters, for Slant: <a href="slant.html#i3">Section 20.2</a><br>
153parameters, for Solo: <a href="solo.html#i3">Section 11.2</a><br>
154parameters, for Tents: <a href="tents.html#i2">Section 25.2</a><br>
155parameters, for Towers: <a href="towers.html#i3">Section 31.2</a><br>
156parameters, for Tracks: <a href="tracks.html#i2">Section 40.2</a><br>
157parameters, for Twiddle: <a href="twiddle.html#i2">Section 7.2</a><br>
158parameters, for Undead: <a href="undead.html#i2">Section 37.2</a><br>
159parameters, for Unequal: <a href="unequal.html#i4">Section 27.2</a><br>
160parameters, for Unruly: <a href="unruly.html#i2">Section 38.2</a><br>
161parameters, for Untangle: <a href="untangle.html#i3">Section 18.2</a><br>
162patches: <a href="intro.html#i8">Chapter 1</a><br>
163Pattern: <a href="pattern.html#i0">Chapter 10</a><br>
164Pearl: <a href="pearl.html#i0">Chapter 36</a><br>
165Pegs: <a href="pegs.html#i0">Chapter 16</a><br>
166Planarity: <a href="untangle.html#i1">Chapter 18</a><br>
167PostScript: <a href="common.html#i41">Section 2.5</a><br>
168preferences, specifying default: <a href="common.html#i34">Section 2.4</a><br>
169preset: <a href="common.html#i29">Section 2.3</a><br>
170printing, on Unix: <a href="common.html#i40">Section 2.5</a><br>
171printing, on Windows: <a href="common.html#i11">Section 2.1</a><br>
17215-puzzle: <a href="fifteen.html#i1">Chapter 5</a><br>
173Puzzle Palace: <a href="rect.html#i2">Chapter 8</a><br>
174quit: <a href="common.html#i17">Section 2.1</a><br>
175Random Seed: <a href="common.html#i20">Section 2.2</a><br>
176Range: <a href="range.html#i0">Chapter 35</a><br>
177Rectangles: <a href="rect.html#i0">Chapter 8</a><br>
178redo: <a href="common.html#i13">Section 2.1</a><br>
179restart game: <a href="common.html#i8">Section 2.1</a><br>
180Same Game: <a href="samegame.html#i0">Chapter 13</a><br>
181save: <a href="common.html#i10">Section 2.1</a>, <a href="common.html#i37">Section 2.5</a><br>
182shortcuts (keyboard): <a href="common.html#i3">Section 2.1</a><br>
183shortcuts (keyboard), for Black Box: <a href="blackbox.html#i1">Section 19.1</a><br>
184shortcuts (keyboard), for Cube: <a href="cube.html#i1">Section 4.1</a><br>
185shortcuts (keyboard), for Fifteen: <a href="fifteen.html#i2">Section 5.1</a><br>
186shortcuts (keyboard), for Flip: <a href="flip.html#i1">Section 14.1</a><br>
187shortcuts (keyboard), for Guess: <a href="guess.html#i2">Section 15.1</a><br>
188shortcuts (keyboard), for Inertia: <a href="inertia.html#i1">Section 24.1</a><br>
189shortcuts (keyboard), for Net: <a href="net.html#i5">Section 3.1</a><br>
190shortcuts (keyboard), for Same Game: <a href="samegame.html#i1">Section 13.1</a><br>
191Signpost: <a href="signpost.html#i0">Chapter 34</a><br>
192Singles: <a href="singles.html#i0">Chapter 32</a><br>
193Sixteen: <a href="sixteen.html#i0">Chapter 6</a><br>
194Skyscrapers: <a href="towers.html#i1">Chapter 31</a><br>
195Slant: <a href="slant.html#i0">Chapter 20</a><br>
196Solitaire, Peg: <a href="pegs.html#i1">Chapter 16</a><br>
197Solo: <a href="solo.html#i0">Chapter 11</a><br>
198solve: <a href="common.html#i15">Section 2.1</a><br>
199source code: <a href="intro.html#i4">Chapter 1</a><br>
200&#8216;Specific&#8217;, menu option: <a href="common.html#i19">Section 2.2</a><br>
201state, initial: <a href="common.html#i24">Section 2.2</a><br>
202Tents: <a href="tents.html#i0">Chapter 25</a><br>
203Towers: <a href="towers.html#i0">Chapter 31</a><br>
204Tracks: <a href="tracks.html#i0">Chapter 40</a><br>
205Twiddle: <a href="twiddle.html#i0">Chapter 7</a><br>
206Type menu: <a href="common.html#i28">Section 2.3</a><br>
207Undead: <a href="undead.html#i0">Chapter 37</a><br>
208undo: <a href="common.html#i12">Section 2.1</a><br>
209Unequal: <a href="unequal.html#i0">Chapter 27</a><br>
210Unix: <a href="intro.html#i0">Chapter 1</a>, <a href="common.html#i35">Section 2.5</a><br>
211Unruly: <a href="unruly.html#i0">Chapter 38</a><br>
212Untangle: <a href="untangle.html#i0">Chapter 18</a><br>
213version: <a href="common.html#i25">Section 2.2</a><br>
214website: <a href="intro.html#i5">Chapter 1</a><br>
215Windows: <a href="intro.html#i1">Chapter 1</a>, <a href="net.html#i1">Chapter 3</a></p>
216<hr><address></address></body>
217</html>
diff --git a/apps/plugins/puzzles/src/dominosa.R b/apps/plugins/puzzles/src/dominosa.R
new file mode 100644
index 0000000000..99218366e6
--- /dev/null
+++ b/apps/plugins/puzzles/src/dominosa.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3DOMINOSA_EXTRA = laydomino
4
5dominosa : [X] GTK COMMON dominosa DOMINOSA_EXTRA dominosa-icon|no-icon
6
7dominosa : [G] WINDOWS COMMON dominosa DOMINOSA_EXTRA dominosa.res|noicon.res
8
9ALL += dominosa[COMBINED] DOMINOSA_EXTRA
10
11!begin am gtk
12GAMES += dominosa
13!end
14
15!begin >list.c
16 A(dominosa) \
17!end
18
19!begin >gamedesc.txt
20dominosa:dominosa.exe:Dominosa:Domino tiling puzzle:Tile the rectangle with a full set of dominoes.
21!end
diff --git a/apps/plugins/puzzles/src/dominosa.c b/apps/plugins/puzzles/src/dominosa.c
new file mode 100644
index 0000000000..c86ba19dfa
--- /dev/null
+++ b/apps/plugins/puzzles/src/dominosa.c
@@ -0,0 +1,1748 @@
1/*
2 * dominosa.c: Domino jigsaw puzzle. Aim to place one of every
3 * possible domino within a rectangle in such a way that the number
4 * on each square matches the provided clue.
5 */
6
7/*
8 * TODO:
9 *
10 * - improve solver so as to use more interesting forms of
11 * deduction
12 *
13 * * rule out a domino placement if it would divide an unfilled
14 * region such that at least one resulting region had an odd
15 * area
16 * + use b.f.s. to determine the area of an unfilled region
17 * + a square is unfilled iff it has at least two possible
18 * placements, and two adjacent unfilled squares are part
19 * of the same region iff the domino placement joining
20 * them is possible
21 *
22 * * perhaps set analysis
23 * + look at all unclaimed squares containing a given number
24 * + for each one, find the set of possible numbers that it
25 * can connect to (i.e. each neighbouring tile such that
26 * the placement between it and that neighbour has not yet
27 * been ruled out)
28 * + now proceed similarly to Solo set analysis: try to find
29 * a subset of the squares such that the union of their
30 * possible numbers is the same size as the subset. If so,
31 * rule out those possible numbers for all other squares.
32 * * important wrinkle: the double dominoes complicate
33 * matters. Connecting a number to itself uses up _two_
34 * of the unclaimed squares containing a number. Thus,
35 * when finding the initial subset we must never
36 * include two adjacent squares; and also, when ruling
37 * things out after finding the subset, we must be
38 * careful that we don't rule out precisely the domino
39 * placement that was _included_ in our set!
40 */
41
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <assert.h>
46#include <ctype.h>
47#include <math.h>
48
49#include "puzzles.h"
50
51/* nth triangular number */
52#define TRI(n) ( (n) * ((n) + 1) / 2 )
53/* number of dominoes for value n */
54#define DCOUNT(n) TRI((n)+1)
55/* map a pair of numbers to a unique domino index from 0 upwards. */
56#define DINDEX(n1,n2) ( TRI(max(n1,n2)) + min(n1,n2) )
57
58#define FLASH_TIME 0.13F
59
60enum {
61 COL_BACKGROUND,
62 COL_TEXT,
63 COL_DOMINO,
64 COL_DOMINOCLASH,
65 COL_DOMINOTEXT,
66 COL_EDGE,
67 COL_HIGHLIGHT_1,
68 COL_HIGHLIGHT_2,
69 NCOLOURS
70};
71
72struct game_params {
73 int n;
74 int unique;
75};
76
77struct game_numbers {
78 int refcount;
79 int *numbers; /* h x w */
80};
81
82#define EDGE_L 0x100
83#define EDGE_R 0x200
84#define EDGE_T 0x400
85#define EDGE_B 0x800
86
87struct game_state {
88 game_params params;
89 int w, h;
90 struct game_numbers *numbers;
91 int *grid;
92 unsigned short *edges; /* h x w */
93 int completed, cheated;
94};
95
96static game_params *default_params(void)
97{
98 game_params *ret = snew(game_params);
99
100 ret->n = 6;
101 ret->unique = TRUE;
102
103 return ret;
104}
105
106static int game_fetch_preset(int i, char **name, game_params **params)
107{
108 game_params *ret;
109 int n;
110 char buf[80];
111
112 switch (i) {
113 case 0: n = 3; break;
114 case 1: n = 4; break;
115 case 2: n = 5; break;
116 case 3: n = 6; break;
117 case 4: n = 7; break;
118 case 5: n = 8; break;
119 case 6: n = 9; break;
120 default: return FALSE;
121 }
122
123 sprintf(buf, "Up to double-%d", n);
124 *name = dupstr(buf);
125
126 *params = ret = snew(game_params);
127 ret->n = n;
128 ret->unique = TRUE;
129
130 return TRUE;
131}
132
133static void free_params(game_params *params)
134{
135 sfree(params);
136}
137
138static game_params *dup_params(const game_params *params)
139{
140 game_params *ret = snew(game_params);
141 *ret = *params; /* structure copy */
142 return ret;
143}
144
145static void decode_params(game_params *params, char const *string)
146{
147 params->n = atoi(string);
148 while (*string && isdigit((unsigned char)*string)) string++;
149 if (*string == 'a')
150 params->unique = FALSE;
151}
152
153static char *encode_params(const game_params *params, int full)
154{
155 char buf[80];
156 sprintf(buf, "%d", params->n);
157 if (full && !params->unique)
158 strcat(buf, "a");
159 return dupstr(buf);
160}
161
162static config_item *game_configure(const game_params *params)
163{
164 config_item *ret;
165 char buf[80];
166
167 ret = snewn(3, config_item);
168
169 ret[0].name = "Maximum number on dominoes";
170 ret[0].type = C_STRING;
171 sprintf(buf, "%d", params->n);
172 ret[0].sval = dupstr(buf);
173 ret[0].ival = 0;
174
175 ret[1].name = "Ensure unique solution";
176 ret[1].type = C_BOOLEAN;
177 ret[1].sval = NULL;
178 ret[1].ival = params->unique;
179
180 ret[2].name = NULL;
181 ret[2].type = C_END;
182 ret[2].sval = NULL;
183 ret[2].ival = 0;
184
185 return ret;
186}
187
188static game_params *custom_params(const config_item *cfg)
189{
190 game_params *ret = snew(game_params);
191
192 ret->n = atoi(cfg[0].sval);
193 ret->unique = cfg[1].ival;
194
195 return ret;
196}
197
198static char *validate_params(const game_params *params, int full)
199{
200 if (params->n < 1)
201 return "Maximum face number must be at least one";
202 return NULL;
203}
204
205/* ----------------------------------------------------------------------
206 * Solver.
207 */
208
209static int find_overlaps(int w, int h, int placement, int *set)
210{
211 int x, y, n;
212
213 n = 0; /* number of returned placements */
214
215 x = placement / 2;
216 y = x / w;
217 x %= w;
218
219 if (placement & 1) {
220 /*
221 * Horizontal domino, indexed by its left end.
222 */
223 if (x > 0)
224 set[n++] = placement-2; /* horizontal domino to the left */
225 if (y > 0)
226 set[n++] = placement-2*w-1;/* vertical domino above left side */
227 if (y+1 < h)
228 set[n++] = placement-1; /* vertical domino below left side */
229 if (x+2 < w)
230 set[n++] = placement+2; /* horizontal domino to the right */
231 if (y > 0)
232 set[n++] = placement-2*w+2-1;/* vertical domino above right side */
233 if (y+1 < h)
234 set[n++] = placement+2-1; /* vertical domino below right side */
235 } else {
236 /*
237 * Vertical domino, indexed by its top end.
238 */
239 if (y > 0)
240 set[n++] = placement-2*w; /* vertical domino above */
241 if (x > 0)
242 set[n++] = placement-2+1; /* horizontal domino left of top */
243 if (x+1 < w)
244 set[n++] = placement+1; /* horizontal domino right of top */
245 if (y+2 < h)
246 set[n++] = placement+2*w; /* vertical domino below */
247 if (x > 0)
248 set[n++] = placement-2+2*w+1;/* horizontal domino left of bottom */
249 if (x+1 < w)
250 set[n++] = placement+2*w+1;/* horizontal domino right of bottom */
251 }
252
253 return n;
254}
255
256/*
257 * Returns 0, 1 or 2 for number of solutions. 2 means `any number
258 * more than one', or more accurately `we were unable to prove
259 * there was only one'.
260 *
261 * Outputs in a `placements' array, indexed the same way as the one
262 * within this function (see below); entries in there are <0 for a
263 * placement ruled out, 0 for an uncertain placement, and 1 for a
264 * definite one.
265 */
266static int solver(int w, int h, int n, int *grid, int *output)
267{
268 int wh = w*h, dc = DCOUNT(n);
269 int *placements, *heads;
270 int i, j, x, y, ret;
271
272 /*
273 * This array has one entry for every possible domino
274 * placement. Vertical placements are indexed by their top
275 * half, at (y*w+x)*2; horizontal placements are indexed by
276 * their left half at (y*w+x)*2+1.
277 *
278 * This array is used to link domino placements together into
279 * linked lists, so that we can track all the possible
280 * placements of each different domino. It's also used as a
281 * quick means of looking up an individual placement to see
282 * whether we still think it's possible. Actual values stored
283 * in this array are -2 (placement not possible at all), -1
284 * (end of list), or the array index of the next item.
285 *
286 * Oh, and -3 for `not even valid', used for array indices
287 * which don't even represent a plausible placement.
288 */
289 placements = snewn(2*wh, int);
290 for (i = 0; i < 2*wh; i++)
291 placements[i] = -3; /* not even valid */
292
293 /*
294 * This array has one entry for every domino, and it is an
295 * index into `placements' denoting the head of the placement
296 * list for that domino.
297 */
298 heads = snewn(dc, int);
299 for (i = 0; i < dc; i++)
300 heads[i] = -1;
301
302 /*
303 * Set up the initial possibility lists by scanning the grid.
304 */
305 for (y = 0; y < h-1; y++)
306 for (x = 0; x < w; x++) {
307 int di = DINDEX(grid[y*w+x], grid[(y+1)*w+x]);
308 placements[(y*w+x)*2] = heads[di];
309 heads[di] = (y*w+x)*2;
310 }
311 for (y = 0; y < h; y++)
312 for (x = 0; x < w-1; x++) {
313 int di = DINDEX(grid[y*w+x], grid[y*w+(x+1)]);
314 placements[(y*w+x)*2+1] = heads[di];
315 heads[di] = (y*w+x)*2+1;
316 }
317
318#ifdef SOLVER_DIAGNOSTICS
319 printf("before solver:\n");
320 for (i = 0; i <= n; i++)
321 for (j = 0; j <= i; j++) {
322 int k, m;
323 m = 0;
324 printf("%2d [%d %d]:", DINDEX(i, j), i, j);
325 for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k])
326 printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v');
327 printf("\n");
328 }
329#endif
330
331 while (1) {
332 int done_something = FALSE;
333
334 /*
335 * For each domino, look at its possible placements, and
336 * for each placement consider the placements (of any
337 * domino) it overlaps. Any placement overlapped by all
338 * placements of this domino can be ruled out.
339 *
340 * Each domino placement overlaps only six others, so we
341 * need not do serious set theory to work this out.
342 */
343 for (i = 0; i < dc; i++) {
344 int permset[6], permlen = 0, p;
345
346
347 if (heads[i] == -1) { /* no placement for this domino */
348 ret = 0; /* therefore puzzle is impossible */
349 goto done;
350 }
351 for (j = heads[i]; j >= 0; j = placements[j]) {
352 assert(placements[j] != -2);
353
354 if (j == heads[i]) {
355 permlen = find_overlaps(w, h, j, permset);
356 } else {
357 int tempset[6], templen, m, n, k;
358
359 templen = find_overlaps(w, h, j, tempset);
360
361 /*
362 * Pathetically primitive set intersection
363 * algorithm, which I'm only getting away with
364 * because I know my sets are bounded by a very
365 * small size.
366 */
367 for (m = n = 0; m < permlen; m++) {
368 for (k = 0; k < templen; k++)
369 if (tempset[k] == permset[m])
370 break;
371 if (k < templen)
372 permset[n++] = permset[m];
373 }
374 permlen = n;
375 }
376 }
377 for (p = 0; p < permlen; p++) {
378 j = permset[p];
379 if (placements[j] != -2) {
380 int p1, p2, di;
381
382 done_something = TRUE;
383
384 /*
385 * Rule out this placement. First find what
386 * domino it is...
387 */
388 p1 = j / 2;
389 p2 = (j & 1) ? p1 + 1 : p1 + w;
390 di = DINDEX(grid[p1], grid[p2]);
391#ifdef SOLVER_DIAGNOSTICS
392 printf("considering domino %d: ruling out placement %d"
393 " for %d\n", i, j, di);
394#endif
395
396 /*
397 * ... then walk that domino's placement list,
398 * removing this placement when we find it.
399 */
400 if (heads[di] == j)
401 heads[di] = placements[j];
402 else {
403 int k = heads[di];
404 while (placements[k] != -1 && placements[k] != j)
405 k = placements[k];
406 assert(placements[k] == j);
407 placements[k] = placements[j];
408 }
409 placements[j] = -2;
410 }
411 }
412 }
413
414 /*
415 * For each square, look at the available placements
416 * involving that square. If all of them are for the same
417 * domino, then rule out any placements for that domino
418 * _not_ involving this square.
419 */
420 for (i = 0; i < wh; i++) {
421 int list[4], k, n, adi;
422
423 x = i % w;
424 y = i / w;
425
426 j = 0;
427 if (x > 0)
428 list[j++] = 2*(i-1)+1;
429 if (x+1 < w)
430 list[j++] = 2*i+1;
431 if (y > 0)
432 list[j++] = 2*(i-w);
433 if (y+1 < h)
434 list[j++] = 2*i;
435
436 for (n = k = 0; k < j; k++)
437 if (placements[list[k]] >= -1)
438 list[n++] = list[k];
439
440 adi = -1;
441
442 for (j = 0; j < n; j++) {
443 int p1, p2, di;
444 k = list[j];
445
446 p1 = k / 2;
447 p2 = (k & 1) ? p1 + 1 : p1 + w;
448 di = DINDEX(grid[p1], grid[p2]);
449
450 if (adi == -1)
451 adi = di;
452 if (adi != di)
453 break;
454 }
455
456 if (j == n) {
457 int nn;
458
459 assert(adi >= 0);
460 /*
461 * We've found something. All viable placements
462 * involving this square are for domino `adi'. If
463 * the current placement list for that domino is
464 * longer than n, reduce it to precisely this
465 * placement list and we've done something.
466 */
467 nn = 0;
468 for (k = heads[adi]; k >= 0; k = placements[k])
469 nn++;
470 if (nn > n) {
471 done_something = TRUE;
472#ifdef SOLVER_DIAGNOSTICS
473 printf("considering square %d,%d: reducing placements "
474 "of domino %d\n", x, y, adi);
475#endif
476 /*
477 * Set all other placements on the list to
478 * impossible.
479 */
480 k = heads[adi];
481 while (k >= 0) {
482 int tmp = placements[k];
483 placements[k] = -2;
484 k = tmp;
485 }
486 /*
487 * Set up the new list.
488 */
489 heads[adi] = list[0];
490 for (k = 0; k < n; k++)
491 placements[list[k]] = (k+1 == n ? -1 : list[k+1]);
492 }
493 }
494 }
495
496 if (!done_something)
497 break;
498 }
499
500#ifdef SOLVER_DIAGNOSTICS
501 printf("after solver:\n");
502 for (i = 0; i <= n; i++)
503 for (j = 0; j <= i; j++) {
504 int k, m;
505 m = 0;
506 printf("%2d [%d %d]:", DINDEX(i, j), i, j);
507 for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k])
508 printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v');
509 printf("\n");
510 }
511#endif
512
513 ret = 1;
514 for (i = 0; i < wh*2; i++) {
515 if (placements[i] == -2) {
516 if (output)
517 output[i] = -1; /* ruled out */
518 } else if (placements[i] != -3) {
519 int p1, p2, di;
520
521 p1 = i / 2;
522 p2 = (i & 1) ? p1 + 1 : p1 + w;
523 di = DINDEX(grid[p1], grid[p2]);
524
525 if (i == heads[di] && placements[i] == -1) {
526 if (output)
527 output[i] = 1; /* certain */
528 } else {
529 if (output)
530 output[i] = 0; /* uncertain */
531 ret = 2;
532 }
533 }
534 }
535
536 done:
537 /*
538 * Free working data.
539 */
540 sfree(placements);
541 sfree(heads);
542
543 return ret;
544}
545
546/* ----------------------------------------------------------------------
547 * End of solver code.
548 */
549
550static char *new_game_desc(const game_params *params, random_state *rs,
551 char **aux, int interactive)
552{
553 int n = params->n, w = n+2, h = n+1, wh = w*h;
554 int *grid, *grid2, *list;
555 int i, j, k, len;
556 char *ret;
557
558 /*
559 * Allocate space in which to lay the grid out.
560 */
561 grid = snewn(wh, int);
562 grid2 = snewn(wh, int);
563 list = snewn(2*wh, int);
564
565 /*
566 * I haven't been able to think of any particularly clever
567 * techniques for generating instances of Dominosa with a
568 * unique solution. Many of the deductions used in this puzzle
569 * are based on information involving half the grid at a time
570 * (`of all the 6s, exactly one is next to a 3'), so a strategy
571 * of partially solving the grid and then perturbing the place
572 * where the solver got stuck seems particularly likely to
573 * accidentally destroy the information which the solver had
574 * used in getting that far. (Contrast with, say, Mines, in
575 * which most deductions are local so this is an excellent
576 * strategy.)
577 *
578 * Therefore I resort to the basest of brute force methods:
579 * generate a random grid, see if it's solvable, throw it away
580 * and try again if not. My only concession to sophistication
581 * and cleverness is to at least _try_ not to generate obvious
582 * 2x2 ambiguous sections (see comment below in the domino-
583 * flipping section).
584 *
585 * During tests performed on 2005-07-15, I found that the brute
586 * force approach without that tweak had to throw away about 87
587 * grids on average (at the default n=6) before finding a
588 * unique one, or a staggering 379 at n=9; good job the
589 * generator and solver are fast! When I added the
590 * ambiguous-section avoidance, those numbers came down to 19
591 * and 26 respectively, which is a lot more sensible.
592 */
593
594 do {
595 domino_layout_prealloc(w, h, rs, grid, grid2, list);
596
597 /*
598 * Now we have a complete layout covering the whole
599 * rectangle with dominoes. So shuffle the actual domino
600 * values and fill the rectangle with numbers.
601 */
602 k = 0;
603 for (i = 0; i <= params->n; i++)
604 for (j = 0; j <= i; j++) {
605 list[k++] = i;
606 list[k++] = j;
607 }
608 shuffle(list, k/2, 2*sizeof(*list), rs);
609 j = 0;
610 for (i = 0; i < wh; i++)
611 if (grid[i] > i) {
612 /* Optionally flip the domino round. */
613 int flip = -1;
614
615 if (params->unique) {
616 int t1, t2;
617 /*
618 * If we're after a unique solution, we can do
619 * something here to improve the chances. If
620 * we're placing a domino so that it forms a
621 * 2x2 rectangle with one we've already placed,
622 * and if that domino and this one share a
623 * number, we can try not to put them so that
624 * the identical numbers are diagonally
625 * separated, because that automatically causes
626 * non-uniqueness:
627 *
628 * +---+ +-+-+
629 * |2 3| |2|3|
630 * +---+ -> | | |
631 * |4 2| |4|2|
632 * +---+ +-+-+
633 */
634 t1 = i;
635 t2 = grid[i];
636 if (t2 == t1 + w) { /* this domino is vertical */
637 if (t1 % w > 0 &&/* and not on the left hand edge */
638 grid[t1-1] == t2-1 &&/* alongside one to left */
639 (grid2[t1-1] == list[j] || /* and has a number */
640 grid2[t1-1] == list[j+1] || /* in common */
641 grid2[t2-1] == list[j] ||
642 grid2[t2-1] == list[j+1])) {
643 if (grid2[t1-1] == list[j] ||
644 grid2[t2-1] == list[j+1])
645 flip = 0;
646 else
647 flip = 1;
648 }
649 } else { /* this domino is horizontal */
650 if (t1 / w > 0 &&/* and not on the top edge */
651 grid[t1-w] == t2-w &&/* alongside one above */
652 (grid2[t1-w] == list[j] || /* and has a number */
653 grid2[t1-w] == list[j+1] || /* in common */
654 grid2[t2-w] == list[j] ||
655 grid2[t2-w] == list[j+1])) {
656 if (grid2[t1-w] == list[j] ||
657 grid2[t2-w] == list[j+1])
658 flip = 0;
659 else
660 flip = 1;
661 }
662 }
663 }
664
665 if (flip < 0)
666 flip = random_upto(rs, 2);
667
668 grid2[i] = list[j + flip];
669 grid2[grid[i]] = list[j + 1 - flip];
670 j += 2;
671 }
672 assert(j == k);
673 } while (params->unique && solver(w, h, n, grid2, NULL) > 1);
674
675#ifdef GENERATION_DIAGNOSTICS
676 for (j = 0; j < h; j++) {
677 for (i = 0; i < w; i++) {
678 putchar('0' + grid2[j*w+i]);
679 }
680 putchar('\n');
681 }
682 putchar('\n');
683#endif
684
685 /*
686 * Encode the resulting game state.
687 *
688 * Our encoding is a string of digits. Any number greater than
689 * 9 is represented by a decimal integer within square
690 * brackets. We know there are n+2 of every number (it's paired
691 * with each number from 0 to n inclusive, and one of those is
692 * itself so that adds another occurrence), so we can work out
693 * the string length in advance.
694 */
695
696 /*
697 * To work out the total length of the decimal encodings of all
698 * the numbers from 0 to n inclusive:
699 * - every number has a units digit; total is n+1.
700 * - all numbers above 9 have a tens digit; total is max(n+1-10,0).
701 * - all numbers above 99 have a hundreds digit; total is max(n+1-100,0).
702 * - and so on.
703 */
704 len = n+1;
705 for (i = 10; i <= n; i *= 10)
706 len += max(n + 1 - i, 0);
707 /* Now add two square brackets for each number above 9. */
708 len += 2 * max(n + 1 - 10, 0);
709 /* And multiply by n+2 for the repeated occurrences of each number. */
710 len *= n+2;
711
712 /*
713 * Now actually encode the string.
714 */
715 ret = snewn(len+1, char);
716 j = 0;
717 for (i = 0; i < wh; i++) {
718 k = grid2[i];
719 if (k < 10)
720 ret[j++] = '0' + k;
721 else
722 j += sprintf(ret+j, "[%d]", k);
723 assert(j <= len);
724 }
725 assert(j == len);
726 ret[j] = '\0';
727
728 /*
729 * Encode the solved state as an aux_info.
730 */
731 {
732 char *auxinfo = snewn(wh+1, char);
733
734 for (i = 0; i < wh; i++) {
735 int v = grid[i];
736 auxinfo[i] = (v == i+1 ? 'L' : v == i-1 ? 'R' :
737 v == i+w ? 'T' : v == i-w ? 'B' : '.');
738 }
739 auxinfo[wh] = '\0';
740
741 *aux = auxinfo;
742 }
743
744 sfree(list);
745 sfree(grid2);
746 sfree(grid);
747
748 return ret;
749}
750
751static char *validate_desc(const game_params *params, const char *desc)
752{
753 int n = params->n, w = n+2, h = n+1, wh = w*h;
754 int *occurrences;
755 int i, j;
756 char *ret;
757
758 ret = NULL;
759 occurrences = snewn(n+1, int);
760 for (i = 0; i <= n; i++)
761 occurrences[i] = 0;
762
763 for (i = 0; i < wh; i++) {
764 if (!*desc) {
765 ret = ret ? ret : "Game description is too short";
766 } else {
767 if (*desc >= '0' && *desc <= '9')
768 j = *desc++ - '0';
769 else if (*desc == '[') {
770 desc++;
771 j = atoi(desc);
772 while (*desc && isdigit((unsigned char)*desc)) desc++;
773 if (*desc != ']')
774 ret = ret ? ret : "Missing ']' in game description";
775 else
776 desc++;
777 } else {
778 j = -1;
779 ret = ret ? ret : "Invalid syntax in game description";
780 }
781 if (j < 0 || j > n)
782 ret = ret ? ret : "Number out of range in game description";
783 else
784 occurrences[j]++;
785 }
786 }
787
788 if (*desc)
789 ret = ret ? ret : "Game description is too long";
790
791 if (!ret) {
792 for (i = 0; i <= n; i++)
793 if (occurrences[i] != n+2)
794 ret = "Incorrect number balance in game description";
795 }
796
797 sfree(occurrences);
798
799 return ret;
800}
801
802static game_state *new_game(midend *me, const game_params *params,
803 const char *desc)
804{
805 int n = params->n, w = n+2, h = n+1, wh = w*h;
806 game_state *state = snew(game_state);
807 int i, j;
808
809 state->params = *params;
810 state->w = w;
811 state->h = h;
812
813 state->grid = snewn(wh, int);
814 for (i = 0; i < wh; i++)
815 state->grid[i] = i;
816
817 state->edges = snewn(wh, unsigned short);
818 for (i = 0; i < wh; i++)
819 state->edges[i] = 0;
820
821 state->numbers = snew(struct game_numbers);
822 state->numbers->refcount = 1;
823 state->numbers->numbers = snewn(wh, int);
824
825 for (i = 0; i < wh; i++) {
826 assert(*desc);
827 if (*desc >= '0' && *desc <= '9')
828 j = *desc++ - '0';
829 else {
830 assert(*desc == '[');
831 desc++;
832 j = atoi(desc);
833 while (*desc && isdigit((unsigned char)*desc)) desc++;
834 assert(*desc == ']');
835 desc++;
836 }
837 assert(j >= 0 && j <= n);
838 state->numbers->numbers[i] = j;
839 }
840
841 state->completed = state->cheated = FALSE;
842
843 return state;
844}
845
846static game_state *dup_game(const game_state *state)
847{
848 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
849 game_state *ret = snew(game_state);
850
851 ret->params = state->params;
852 ret->w = state->w;
853 ret->h = state->h;
854 ret->grid = snewn(wh, int);
855 memcpy(ret->grid, state->grid, wh * sizeof(int));
856 ret->edges = snewn(wh, unsigned short);
857 memcpy(ret->edges, state->edges, wh * sizeof(unsigned short));
858 ret->numbers = state->numbers;
859 ret->numbers->refcount++;
860 ret->completed = state->completed;
861 ret->cheated = state->cheated;
862
863 return ret;
864}
865
866static void free_game(game_state *state)
867{
868 sfree(state->grid);
869 sfree(state->edges);
870 if (--state->numbers->refcount <= 0) {
871 sfree(state->numbers->numbers);
872 sfree(state->numbers);
873 }
874 sfree(state);
875}
876
877static char *solve_game(const game_state *state, const game_state *currstate,
878 const char *aux, char **error)
879{
880 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
881 int *placements;
882 char *ret;
883 int retlen, retsize;
884 int i, v;
885 char buf[80];
886 int extra;
887
888 if (aux) {
889 retsize = 256;
890 ret = snewn(retsize, char);
891 retlen = sprintf(ret, "S");
892
893 for (i = 0; i < wh; i++) {
894 if (aux[i] == 'L')
895 extra = sprintf(buf, ";D%d,%d", i, i+1);
896 else if (aux[i] == 'T')
897 extra = sprintf(buf, ";D%d,%d", i, i+w);
898 else
899 continue;
900
901 if (retlen + extra + 1 >= retsize) {
902 retsize = retlen + extra + 256;
903 ret = sresize(ret, retsize, char);
904 }
905 strcpy(ret + retlen, buf);
906 retlen += extra;
907 }
908
909 } else {
910
911 placements = snewn(wh*2, int);
912 for (i = 0; i < wh*2; i++)
913 placements[i] = -3;
914 solver(w, h, n, state->numbers->numbers, placements);
915
916 /*
917 * First make a pass putting in edges for -1, then make a pass
918 * putting in dominoes for +1.
919 */
920 retsize = 256;
921 ret = snewn(retsize, char);
922 retlen = sprintf(ret, "S");
923
924 for (v = -1; v <= +1; v += 2)
925 for (i = 0; i < wh*2; i++)
926 if (placements[i] == v) {
927 int p1 = i / 2;
928 int p2 = (i & 1) ? p1+1 : p1+w;
929
930 extra = sprintf(buf, ";%c%d,%d",
931 (int)(v==-1 ? 'E' : 'D'), p1, p2);
932
933 if (retlen + extra + 1 >= retsize) {
934 retsize = retlen + extra + 256;
935 ret = sresize(ret, retsize, char);
936 }
937 strcpy(ret + retlen, buf);
938 retlen += extra;
939 }
940
941 sfree(placements);
942 }
943
944 return ret;
945}
946
947static int game_can_format_as_text_now(const game_params *params)
948{
949 return params->n < 1000;
950}
951
952static void draw_domino(char *board, int start, char corner,
953 int dshort, int nshort, char cshort,
954 int dlong, int nlong, char clong)
955{
956 int go_short = nshort*dshort, go_long = nlong*dlong, i;
957
958 board[start] = corner;
959 board[start + go_short] = corner;
960 board[start + go_long] = corner;
961 board[start + go_short + go_long] = corner;
962
963 for (i = 1; i < nshort; ++i) {
964 int j = start + i*dshort, k = start + i*dshort + go_long;
965 if (board[j] != corner) board[j] = cshort;
966 if (board[k] != corner) board[k] = cshort;
967 }
968
969 for (i = 1; i < nlong; ++i) {
970 int j = start + i*dlong, k = start + i*dlong + go_short;
971 if (board[j] != corner) board[j] = clong;
972 if (board[k] != corner) board[k] = clong;
973 }
974}
975
976static char *game_text_format(const game_state *state)
977{
978 int w = state->w, h = state->h, r, c;
979 int cw = 4, ch = 2, gw = cw*w + 2, gh = ch * h + 1, len = gw * gh;
980 char *board = snewn(len + 1, char);
981
982 memset(board, ' ', len);
983
984 for (r = 0; r < h; ++r) {
985 for (c = 0; c < w; ++c) {
986 int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2;
987 int i = r*w + c, num = state->numbers->numbers[i];
988
989 if (num < 100) {
990 board[center] = '0' + num % 10;
991 if (num >= 10) board[center - 1] = '0' + num / 10;
992 } else {
993 board[center+1] = '0' + num % 10;
994 board[center] = '0' + num / 10 % 10;
995 board[center-1] = '0' + num / 100;
996 }
997
998 if (state->edges[i] & EDGE_L) board[center - cw/2] = '|';
999 if (state->edges[i] & EDGE_R) board[center + cw/2] = '|';
1000 if (state->edges[i] & EDGE_T) board[center - gw] = '-';
1001 if (state->edges[i] & EDGE_B) board[center + gw] = '-';
1002
1003 if (state->grid[i] == i) continue; /* no domino pairing */
1004 if (state->grid[i] < i) continue; /* already done */
1005 assert (state->grid[i] == i + 1 || state->grid[i] == i + w);
1006 if (state->grid[i] == i + 1)
1007 draw_domino(board, cell, '+', gw, ch, '|', +1, 2*cw, '-');
1008 else if (state->grid[i] == i + w)
1009 draw_domino(board, cell, '+', +1, cw, '-', gw, 2*ch, '|');
1010 }
1011 board[r*ch*gw + gw - 1] = '\n';
1012 board[r*ch*gw + gw + gw - 1] = '\n';
1013 }
1014 board[len - 1] = '\n';
1015 board[len] = '\0';
1016 return board;
1017}
1018
1019struct game_ui {
1020 int cur_x, cur_y, cur_visible, highlight_1, highlight_2;
1021};
1022
1023static game_ui *new_ui(const game_state *state)
1024{
1025 game_ui *ui = snew(game_ui);
1026 ui->cur_x = ui->cur_y = 0;
1027 ui->cur_visible = 0;
1028 ui->highlight_1 = ui->highlight_2 = -1;
1029 return ui;
1030}
1031
1032static void free_ui(game_ui *ui)
1033{
1034 sfree(ui);
1035}
1036
1037static char *encode_ui(const game_ui *ui)
1038{
1039 return NULL;
1040}
1041
1042static void decode_ui(game_ui *ui, const char *encoding)
1043{
1044}
1045
1046static void game_changed_state(game_ui *ui, const game_state *oldstate,
1047 const game_state *newstate)
1048{
1049 if (!oldstate->completed && newstate->completed)
1050 ui->cur_visible = 0;
1051}
1052
1053#define PREFERRED_TILESIZE 32
1054#define TILESIZE (ds->tilesize)
1055#define BORDER (TILESIZE * 3 / 4)
1056#define DOMINO_GUTTER (TILESIZE / 16)
1057#define DOMINO_RADIUS (TILESIZE / 8)
1058#define DOMINO_COFFSET (DOMINO_GUTTER + DOMINO_RADIUS)
1059#define CURSOR_RADIUS (TILESIZE / 4)
1060
1061#define COORD(x) ( (x) * TILESIZE + BORDER )
1062#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1063
1064struct game_drawstate {
1065 int started;
1066 int w, h, tilesize;
1067 unsigned long *visible;
1068};
1069
1070static char *interpret_move(const game_state *state, game_ui *ui,
1071 const game_drawstate *ds,
1072 int x, int y, int button)
1073{
1074 int w = state->w, h = state->h;
1075 char buf[80];
1076
1077 /*
1078 * A left-click between two numbers toggles a domino covering
1079 * them. A right-click toggles an edge.
1080 */
1081 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1082 int tx = FROMCOORD(x), ty = FROMCOORD(y), t = ty*w+tx;
1083 int dx, dy;
1084 int d1, d2;
1085
1086 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
1087 return NULL;
1088
1089 /*
1090 * Now we know which square the click was in, decide which
1091 * edge of the square it was closest to.
1092 */
1093 dx = 2 * (x - COORD(tx)) - TILESIZE;
1094 dy = 2 * (y - COORD(ty)) - TILESIZE;
1095
1096 if (abs(dx) > abs(dy) && dx < 0 && tx > 0)
1097 d1 = t - 1, d2 = t; /* clicked in right side of domino */
1098 else if (abs(dx) > abs(dy) && dx > 0 && tx+1 < w)
1099 d1 = t, d2 = t + 1; /* clicked in left side of domino */
1100 else if (abs(dy) > abs(dx) && dy < 0 && ty > 0)
1101 d1 = t - w, d2 = t; /* clicked in bottom half of domino */
1102 else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h)
1103 d1 = t, d2 = t + w; /* clicked in top half of domino */
1104 else
1105 return NULL;
1106
1107 /*
1108 * We can't mark an edge next to any domino.
1109 */
1110 if (button == RIGHT_BUTTON &&
1111 (state->grid[d1] != d1 || state->grid[d2] != d2))
1112 return NULL;
1113
1114 ui->cur_visible = 0;
1115 sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2);
1116 return dupstr(buf);
1117 } else if (IS_CURSOR_MOVE(button)) {
1118 ui->cur_visible = 1;
1119
1120 move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, 0);
1121
1122 return "";
1123 } else if (IS_CURSOR_SELECT(button)) {
1124 int d1, d2;
1125
1126 if (!((ui->cur_x ^ ui->cur_y) & 1))
1127 return NULL; /* must have exactly one dimension odd */
1128 d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2);
1129 d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2);
1130
1131 /*
1132 * We can't mark an edge next to any domino.
1133 */
1134 if (button == CURSOR_SELECT2 &&
1135 (state->grid[d1] != d1 || state->grid[d2] != d2))
1136 return NULL;
1137
1138 sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2);
1139 return dupstr(buf);
1140 } else if (isdigit(button)) {
1141 int n = state->params.n, num = button - '0';
1142 if (num > n) {
1143 return NULL;
1144 } else if (ui->highlight_1 == num) {
1145 ui->highlight_1 = -1;
1146 } else if (ui->highlight_2 == num) {
1147 ui->highlight_2 = -1;
1148 } else if (ui->highlight_1 == -1) {
1149 ui->highlight_1 = num;
1150 } else if (ui->highlight_2 == -1) {
1151 ui->highlight_2 = num;
1152 } else {
1153 return NULL;
1154 }
1155 return "";
1156 }
1157
1158 return NULL;
1159}
1160
1161static game_state *execute_move(const game_state *state, const char *move)
1162{
1163 int n = state->params.n, w = n+2, h = n+1, wh = w*h;
1164 int d1, d2, d3, p;
1165 game_state *ret = dup_game(state);
1166
1167 while (*move) {
1168 if (move[0] == 'S') {
1169 int i;
1170
1171 ret->cheated = TRUE;
1172
1173 /*
1174 * Clear the existing edges and domino placements. We
1175 * expect the S to be followed by other commands.
1176 */
1177 for (i = 0; i < wh; i++) {
1178 ret->grid[i] = i;
1179 ret->edges[i] = 0;
1180 }
1181 move++;
1182 } else if (move[0] == 'D' &&
1183 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
1184 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) {
1185
1186 /*
1187 * Toggle domino presence between d1 and d2.
1188 */
1189 if (ret->grid[d1] == d2) {
1190 assert(ret->grid[d2] == d1);
1191 ret->grid[d1] = d1;
1192 ret->grid[d2] = d2;
1193 } else {
1194 /*
1195 * Erase any dominoes that might overlap the new one.
1196 */
1197 d3 = ret->grid[d1];
1198 if (d3 != d1)
1199 ret->grid[d3] = d3;
1200 d3 = ret->grid[d2];
1201 if (d3 != d2)
1202 ret->grid[d3] = d3;
1203 /*
1204 * Place the new one.
1205 */
1206 ret->grid[d1] = d2;
1207 ret->grid[d2] = d1;
1208
1209 /*
1210 * Destroy any edges lurking around it.
1211 */
1212 if (ret->edges[d1] & EDGE_L) {
1213 assert(d1 - 1 >= 0);
1214 ret->edges[d1 - 1] &= ~EDGE_R;
1215 }
1216 if (ret->edges[d1] & EDGE_R) {
1217 assert(d1 + 1 < wh);
1218 ret->edges[d1 + 1] &= ~EDGE_L;
1219 }
1220 if (ret->edges[d1] & EDGE_T) {
1221 assert(d1 - w >= 0);
1222 ret->edges[d1 - w] &= ~EDGE_B;
1223 }
1224 if (ret->edges[d1] & EDGE_B) {
1225 assert(d1 + 1 < wh);
1226 ret->edges[d1 + w] &= ~EDGE_T;
1227 }
1228 ret->edges[d1] = 0;
1229 if (ret->edges[d2] & EDGE_L) {
1230 assert(d2 - 1 >= 0);
1231 ret->edges[d2 - 1] &= ~EDGE_R;
1232 }
1233 if (ret->edges[d2] & EDGE_R) {
1234 assert(d2 + 1 < wh);
1235 ret->edges[d2 + 1] &= ~EDGE_L;
1236 }
1237 if (ret->edges[d2] & EDGE_T) {
1238 assert(d2 - w >= 0);
1239 ret->edges[d2 - w] &= ~EDGE_B;
1240 }
1241 if (ret->edges[d2] & EDGE_B) {
1242 assert(d2 + 1 < wh);
1243 ret->edges[d2 + w] &= ~EDGE_T;
1244 }
1245 ret->edges[d2] = 0;
1246 }
1247
1248 move += p+1;
1249 } else if (move[0] == 'E' &&
1250 sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 &&
1251 d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 &&
1252 ret->grid[d1] == d1 && ret->grid[d2] == d2) {
1253
1254 /*
1255 * Toggle edge presence between d1 and d2.
1256 */
1257 if (d2 == d1 + 1) {
1258 ret->edges[d1] ^= EDGE_R;
1259 ret->edges[d2] ^= EDGE_L;
1260 } else {
1261 ret->edges[d1] ^= EDGE_B;
1262 ret->edges[d2] ^= EDGE_T;
1263 }
1264
1265 move += p+1;
1266 } else {
1267 free_game(ret);
1268 return NULL;
1269 }
1270
1271 if (*move) {
1272 if (*move != ';') {
1273 free_game(ret);
1274 return NULL;
1275 }
1276 move++;
1277 }
1278 }
1279
1280 /*
1281 * After modifying the grid, check completion.
1282 */
1283 if (!ret->completed) {
1284 int i, ok = 0;
1285 unsigned char *used = snewn(TRI(n+1), unsigned char);
1286
1287 memset(used, 0, TRI(n+1));
1288 for (i = 0; i < wh; i++)
1289 if (ret->grid[i] > i) {
1290 int n1, n2, di;
1291
1292 n1 = ret->numbers->numbers[i];
1293 n2 = ret->numbers->numbers[ret->grid[i]];
1294
1295 di = DINDEX(n1, n2);
1296 assert(di >= 0 && di < TRI(n+1));
1297
1298 if (!used[di]) {
1299 used[di] = 1;
1300 ok++;
1301 }
1302 }
1303
1304 sfree(used);
1305 if (ok == DCOUNT(n))
1306 ret->completed = TRUE;
1307 }
1308
1309 return ret;
1310}
1311
1312/* ----------------------------------------------------------------------
1313 * Drawing routines.
1314 */
1315
1316static void game_compute_size(const game_params *params, int tilesize,
1317 int *x, int *y)
1318{
1319 int n = params->n, w = n+2, h = n+1;
1320
1321 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1322 struct { int tilesize; } ads, *ds = &ads;
1323 ads.tilesize = tilesize;
1324
1325 *x = w * TILESIZE + 2*BORDER;
1326 *y = h * TILESIZE + 2*BORDER;
1327}
1328
1329static void game_set_size(drawing *dr, game_drawstate *ds,
1330 const game_params *params, int tilesize)
1331{
1332 ds->tilesize = tilesize;
1333}
1334
1335static float *game_colours(frontend *fe, int *ncolours)
1336{
1337 float *ret = snewn(3 * NCOLOURS, float);
1338
1339 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1340
1341 ret[COL_TEXT * 3 + 0] = 0.0F;
1342 ret[COL_TEXT * 3 + 1] = 0.0F;
1343 ret[COL_TEXT * 3 + 2] = 0.0F;
1344
1345 ret[COL_DOMINO * 3 + 0] = 0.0F;
1346 ret[COL_DOMINO * 3 + 1] = 0.0F;
1347 ret[COL_DOMINO * 3 + 2] = 0.0F;
1348
1349 ret[COL_DOMINOCLASH * 3 + 0] = 0.5F;
1350 ret[COL_DOMINOCLASH * 3 + 1] = 0.0F;
1351 ret[COL_DOMINOCLASH * 3 + 2] = 0.0F;
1352
1353 ret[COL_DOMINOTEXT * 3 + 0] = 1.0F;
1354 ret[COL_DOMINOTEXT * 3 + 1] = 1.0F;
1355 ret[COL_DOMINOTEXT * 3 + 2] = 1.0F;
1356
1357 ret[COL_EDGE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2 / 3;
1358 ret[COL_EDGE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2 / 3;
1359 ret[COL_EDGE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2 / 3;
1360
1361 ret[COL_HIGHLIGHT_1 * 3 + 0] = 0.85;
1362 ret[COL_HIGHLIGHT_1 * 3 + 1] = 0.20;
1363 ret[COL_HIGHLIGHT_1 * 3 + 2] = 0.20;
1364
1365 ret[COL_HIGHLIGHT_2 * 3 + 0] = 0.30;
1366 ret[COL_HIGHLIGHT_2 * 3 + 1] = 0.85;
1367 ret[COL_HIGHLIGHT_2 * 3 + 2] = 0.20;
1368
1369 *ncolours = NCOLOURS;
1370 return ret;
1371}
1372
1373static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1374{
1375 struct game_drawstate *ds = snew(struct game_drawstate);
1376 int i;
1377
1378 ds->started = FALSE;
1379 ds->w = state->w;
1380 ds->h = state->h;
1381 ds->visible = snewn(ds->w * ds->h, unsigned long);
1382 ds->tilesize = 0; /* not decided yet */
1383 for (i = 0; i < ds->w * ds->h; i++)
1384 ds->visible[i] = 0xFFFF;
1385
1386 return ds;
1387}
1388
1389static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1390{
1391 sfree(ds->visible);
1392 sfree(ds);
1393}
1394
1395enum {
1396 TYPE_L,
1397 TYPE_R,
1398 TYPE_T,
1399 TYPE_B,
1400 TYPE_BLANK,
1401 TYPE_MASK = 0x0F
1402};
1403
1404/* These flags must be disjoint with:
1405 * the above enum (TYPE_*) [0x000 -- 0x00F]
1406 * EDGE_* [0x100 -- 0xF00]
1407 * and must fit into an unsigned long (32 bits).
1408 */
1409#define DF_HIGHLIGHT_1 0x10
1410#define DF_HIGHLIGHT_2 0x20
1411#define DF_FLASH 0x40
1412#define DF_CLASH 0x80
1413
1414#define DF_CURSOR 0x01000
1415#define DF_CURSOR_USEFUL 0x02000
1416#define DF_CURSOR_XBASE 0x10000
1417#define DF_CURSOR_XMASK 0x30000
1418#define DF_CURSOR_YBASE 0x40000
1419#define DF_CURSOR_YMASK 0xC0000
1420
1421#define CEDGE_OFF (TILESIZE / 8)
1422#define IS_EMPTY(s,x,y) ((s)->grid[(y)*(s)->w+(x)] == ((y)*(s)->w+(x)))
1423
1424static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
1425 int x, int y, int type, int highlight_1, int highlight_2)
1426{
1427 int w = state->w /*, h = state->h */;
1428 int cx = COORD(x), cy = COORD(y);
1429 int nc;
1430 char str[80];
1431 int flags;
1432
1433 clip(dr, cx, cy, TILESIZE, TILESIZE);
1434 draw_rect(dr, cx, cy, TILESIZE, TILESIZE, COL_BACKGROUND);
1435
1436 flags = type &~ TYPE_MASK;
1437 type &= TYPE_MASK;
1438
1439 if (type != TYPE_BLANK) {
1440 int i, bg;
1441
1442 /*
1443 * Draw one end of a domino. This is composed of:
1444 *
1445 * - two filled circles (rounded corners)
1446 * - two rectangles
1447 * - a slight shift in the number
1448 */
1449
1450 if (flags & DF_CLASH)
1451 bg = COL_DOMINOCLASH;
1452 else
1453 bg = COL_DOMINO;
1454 nc = COL_DOMINOTEXT;
1455
1456 if (flags & DF_FLASH) {
1457 int tmp = nc;
1458 nc = bg;
1459 bg = tmp;
1460 }
1461
1462 if (type == TYPE_L || type == TYPE_T)
1463 draw_circle(dr, cx+DOMINO_COFFSET, cy+DOMINO_COFFSET,
1464 DOMINO_RADIUS, bg, bg);
1465 if (type == TYPE_R || type == TYPE_T)
1466 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, cy+DOMINO_COFFSET,
1467 DOMINO_RADIUS, bg, bg);
1468 if (type == TYPE_L || type == TYPE_B)
1469 draw_circle(dr, cx+DOMINO_COFFSET, cy+TILESIZE-1-DOMINO_COFFSET,
1470 DOMINO_RADIUS, bg, bg);
1471 if (type == TYPE_R || type == TYPE_B)
1472 draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET,
1473 cy+TILESIZE-1-DOMINO_COFFSET,
1474 DOMINO_RADIUS, bg, bg);
1475
1476 for (i = 0; i < 2; i++) {
1477 int x1, y1, x2, y2;
1478
1479 x1 = cx + (i ? DOMINO_GUTTER : DOMINO_COFFSET);
1480 y1 = cy + (i ? DOMINO_COFFSET : DOMINO_GUTTER);
1481 x2 = cx + TILESIZE-1 - (i ? DOMINO_GUTTER : DOMINO_COFFSET);
1482 y2 = cy + TILESIZE-1 - (i ? DOMINO_COFFSET : DOMINO_GUTTER);
1483 if (type == TYPE_L)
1484 x2 = cx + TILESIZE + TILESIZE/16;
1485 else if (type == TYPE_R)
1486 x1 = cx - TILESIZE/16;
1487 else if (type == TYPE_T)
1488 y2 = cy + TILESIZE + TILESIZE/16;
1489 else if (type == TYPE_B)
1490 y1 = cy - TILESIZE/16;
1491
1492 draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg);
1493 }
1494 } else {
1495 if (flags & EDGE_T)
1496 draw_rect(dr, cx+DOMINO_GUTTER, cy,
1497 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
1498 if (flags & EDGE_B)
1499 draw_rect(dr, cx+DOMINO_GUTTER, cy+TILESIZE-1,
1500 TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE);
1501 if (flags & EDGE_L)
1502 draw_rect(dr, cx, cy+DOMINO_GUTTER,
1503 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
1504 if (flags & EDGE_R)
1505 draw_rect(dr, cx+TILESIZE-1, cy+DOMINO_GUTTER,
1506 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE);
1507 nc = COL_TEXT;
1508 }
1509
1510 if (flags & DF_CURSOR) {
1511 int curx = ((flags & DF_CURSOR_XMASK) / DF_CURSOR_XBASE) & 3;
1512 int cury = ((flags & DF_CURSOR_YMASK) / DF_CURSOR_YBASE) & 3;
1513 int ox = cx + curx*TILESIZE/2;
1514 int oy = cy + cury*TILESIZE/2;
1515
1516 draw_rect_corners(dr, ox, oy, CURSOR_RADIUS, nc);
1517 if (flags & DF_CURSOR_USEFUL)
1518 draw_rect_corners(dr, ox, oy, CURSOR_RADIUS+1, nc);
1519 }
1520
1521 if (flags & DF_HIGHLIGHT_1) {
1522 nc = COL_HIGHLIGHT_1;
1523 } else if (flags & DF_HIGHLIGHT_2) {
1524 nc = COL_HIGHLIGHT_2;
1525 }
1526
1527 sprintf(str, "%d", state->numbers->numbers[y*w+x]);
1528 draw_text(dr, cx+TILESIZE/2, cy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2,
1529 ALIGN_HCENTRE | ALIGN_VCENTRE, nc, str);
1530
1531 draw_update(dr, cx, cy, TILESIZE, TILESIZE);
1532 unclip(dr);
1533}
1534
1535static void game_redraw(drawing *dr, game_drawstate *ds,
1536 const game_state *oldstate, const game_state *state,
1537 int dir, const game_ui *ui,
1538 float animtime, float flashtime)
1539{
1540 int n = state->params.n, w = state->w, h = state->h, wh = w*h;
1541 int x, y, i;
1542 unsigned char *used;
1543
1544 if (!ds->started) {
1545 int pw, ph;
1546 game_compute_size(&state->params, TILESIZE, &pw, &ph);
1547 draw_rect(dr, 0, 0, pw, ph, COL_BACKGROUND);
1548 draw_update(dr, 0, 0, pw, ph);
1549 ds->started = TRUE;
1550 }
1551
1552 /*
1553 * See how many dominoes of each type there are, so we can
1554 * highlight clashes in red.
1555 */
1556 used = snewn(TRI(n+1), unsigned char);
1557 memset(used, 0, TRI(n+1));
1558 for (i = 0; i < wh; i++)
1559 if (state->grid[i] > i) {
1560 int n1, n2, di;
1561
1562 n1 = state->numbers->numbers[i];
1563 n2 = state->numbers->numbers[state->grid[i]];
1564
1565 di = DINDEX(n1, n2);
1566 assert(di >= 0 && di < TRI(n+1));
1567
1568 if (used[di] < 2)
1569 used[di]++;
1570 }
1571
1572 for (y = 0; y < h; y++)
1573 for (x = 0; x < w; x++) {
1574 int n = y*w+x;
1575 int n1, n2, di;
1576 unsigned long c;
1577
1578 if (state->grid[n] == n-1)
1579 c = TYPE_R;
1580 else if (state->grid[n] == n+1)
1581 c = TYPE_L;
1582 else if (state->grid[n] == n-w)
1583 c = TYPE_B;
1584 else if (state->grid[n] == n+w)
1585 c = TYPE_T;
1586 else
1587 c = TYPE_BLANK;
1588
1589 n1 = state->numbers->numbers[n];
1590 if (c != TYPE_BLANK) {
1591 n2 = state->numbers->numbers[state->grid[n]];
1592 di = DINDEX(n1, n2);
1593 if (used[di] > 1)
1594 c |= DF_CLASH; /* highlight a clash */
1595 } else {
1596 c |= state->edges[n];
1597 }
1598
1599 if (n1 == ui->highlight_1)
1600 c |= DF_HIGHLIGHT_1;
1601 if (n1 == ui->highlight_2)
1602 c |= DF_HIGHLIGHT_2;
1603
1604 if (flashtime != 0)
1605 c |= DF_FLASH; /* we're flashing */
1606
1607 if (ui->cur_visible) {
1608 unsigned curx = (unsigned)(ui->cur_x - (2*x-1));
1609 unsigned cury = (unsigned)(ui->cur_y - (2*y-1));
1610 if (curx < 3 && cury < 3) {
1611 c |= (DF_CURSOR |
1612 (curx * DF_CURSOR_XBASE) |
1613 (cury * DF_CURSOR_YBASE));
1614 if ((ui->cur_x ^ ui->cur_y) & 1)
1615 c |= DF_CURSOR_USEFUL;
1616 }
1617 }
1618
1619 if (ds->visible[n] != c) {
1620 draw_tile(dr, ds, state, x, y, c,
1621 ui->highlight_1, ui->highlight_2);
1622 ds->visible[n] = c;
1623 }
1624 }
1625
1626 sfree(used);
1627}
1628
1629static float game_anim_length(const game_state *oldstate,
1630 const game_state *newstate, int dir, game_ui *ui)
1631{
1632 return 0.0F;
1633}
1634
1635static float game_flash_length(const game_state *oldstate,
1636 const game_state *newstate, int dir, game_ui *ui)
1637{
1638 if (!oldstate->completed && newstate->completed &&
1639 !oldstate->cheated && !newstate->cheated)
1640 {
1641 ui->highlight_1 = ui->highlight_2 = -1;
1642 return FLASH_TIME;
1643 }
1644 return 0.0F;
1645}
1646
1647static int game_status(const game_state *state)
1648{
1649 return state->completed ? +1 : 0;
1650}
1651
1652static int game_timing_state(const game_state *state, game_ui *ui)
1653{
1654 return TRUE;
1655}
1656
1657static void game_print_size(const game_params *params, float *x, float *y)
1658{
1659 int pw, ph;
1660
1661 /*
1662 * I'll use 6mm squares by default.
1663 */
1664 game_compute_size(params, 600, &pw, &ph);
1665 *x = pw / 100.0F;
1666 *y = ph / 100.0F;
1667}
1668
1669static void game_print(drawing *dr, const game_state *state, int tilesize)
1670{
1671 int w = state->w, h = state->h;
1672 int c, x, y;
1673
1674 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1675 game_drawstate ads, *ds = &ads;
1676 game_set_size(dr, ds, NULL, tilesize);
1677
1678 c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
1679 c = print_mono_colour(dr, 0); assert(c == COL_TEXT);
1680 c = print_mono_colour(dr, 0); assert(c == COL_DOMINO);
1681 c = print_mono_colour(dr, 0); assert(c == COL_DOMINOCLASH);
1682 c = print_mono_colour(dr, 1); assert(c == COL_DOMINOTEXT);
1683 c = print_mono_colour(dr, 0); assert(c == COL_EDGE);
1684
1685 for (y = 0; y < h; y++)
1686 for (x = 0; x < w; x++) {
1687 int n = y*w+x;
1688 unsigned long c;
1689
1690 if (state->grid[n] == n-1)
1691 c = TYPE_R;
1692 else if (state->grid[n] == n+1)
1693 c = TYPE_L;
1694 else if (state->grid[n] == n-w)
1695 c = TYPE_B;
1696 else if (state->grid[n] == n+w)
1697 c = TYPE_T;
1698 else
1699 c = TYPE_BLANK;
1700
1701 draw_tile(dr, ds, state, x, y, c, -1, -1);
1702 }
1703}
1704
1705#ifdef COMBINED
1706#define thegame dominosa
1707#endif
1708
1709const struct game thegame = {
1710 "Dominosa", "games.dominosa", "dominosa",
1711 default_params,
1712 game_fetch_preset, NULL,
1713 decode_params,
1714 encode_params,
1715 free_params,
1716 dup_params,
1717 TRUE, game_configure, custom_params,
1718 validate_params,
1719 new_game_desc,
1720 validate_desc,
1721 new_game,
1722 dup_game,
1723 free_game,
1724 TRUE, solve_game,
1725 TRUE, game_can_format_as_text_now, game_text_format,
1726 new_ui,
1727 free_ui,
1728 encode_ui,
1729 decode_ui,
1730 game_changed_state,
1731 interpret_move,
1732 execute_move,
1733 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1734 game_colours,
1735 game_new_drawstate,
1736 game_free_drawstate,
1737 game_redraw,
1738 game_anim_length,
1739 game_flash_length,
1740 game_status,
1741 TRUE, FALSE, game_print_size, game_print,
1742 FALSE, /* wants_statusbar */
1743 FALSE, game_timing_state,
1744 0, /* flags */
1745};
1746
1747/* vim: set shiftwidth=4 :set textwidth=80: */
1748
diff --git a/apps/plugins/puzzles/src/dominosa.html b/apps/plugins/puzzles/src/dominosa.html
new file mode 100644
index 0000000000..67659571ea
--- /dev/null
+++ b/apps/plugins/puzzles/src/dominosa.html
@@ -0,0 +1,57 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Dominosa</title>
7<link rel="previous" href="pegs.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="untangle.html">
12</head>
13<body>
14<p><a href="pegs.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="untangle.html">Next</a></p>
15<h1><a name="C17"></a>Chapter 17: <a name="i0"></a>Dominosa</h1>
16<p>
17A normal set of dominoes &#8211; that is, one instance of every (unordered) pair of numbers from 0 to 6 &#8211; has been arranged irregularly into a rectangle; then the number in each square has been written down and the dominoes themselves removed. Your task is to reconstruct the pattern by arranging the set of dominoes to match the provided array of numbers.
18</p>
19<p>
20This puzzle is widely credited to O. S. Adler, and takes part of its name from those initials.
21</p>
22<h2><a name="S17.1"></a>17.1 <a name="i1"></a>Dominosa controls</h2>
23<p>
24Left-clicking between any two adjacent numbers places a domino covering them, or removes one if it is already present. Trying to place a domino which overlaps existing dominoes will remove the ones it overlaps.
25</p>
26<p>
27Right-clicking between two adjacent numbers draws a line between them, which you can use to remind yourself that you know those two numbers are <em>not</em> covered by a single domino. Right-clicking again removes the line.
28</p>
29<p>
30You can also use the cursor keys to move a cursor around the grid. When the cursor is half way between two adjacent numbers, pressing the return key will place a domino covering those numbers, or pressing the space bar will lay a line between the two squares. Repeating either action removes the domino or line.
31</p>
32<p>
33Pressing a number key will highlight all occurrences of that number. Pressing that number again will clear the highlighting. Up to two different numbers can be highlighted at any given time.
34</p>
35<p>
36(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
37</p>
38<h2><a name="S17.2"></a>17.2 <a name="i2"></a>Dominosa parameters</h2>
39<p>
40These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
41</p>
42<dl><dt>
43<em>Maximum number on dominoes</em>
44</dt>
45<dd>
46Controls the size of the puzzle, by controlling the size of the set of dominoes used to make it. Dominoes with numbers going up to N will give rise to an (N+2) &#215; (N+1) rectangle; so, in particular, the default value of 6 gives an 8&#215;7 grid.
47</dd>
48<dt>
49<em>Ensure unique solution</em>
50</dt>
51<dd>
52Normally, Dominosa will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and sometimes more subtle, so if you like you can turn off this feature. Also, finding <em>all</em> the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation.
53</dd>
54</dl>
55
56<hr><address></address></body>
57</html>
diff --git a/apps/plugins/puzzles/src/drawing.c b/apps/plugins/puzzles/src/drawing.c
new file mode 100644
index 0000000000..7f4a6cf674
--- /dev/null
+++ b/apps/plugins/puzzles/src/drawing.c
@@ -0,0 +1,351 @@
1/*
2 * drawing.c: Intermediary between the drawing interface as
3 * presented to the back end, and that implemented by the front
4 * end.
5 *
6 * Mostly just looks up calls in a vtable and passes them through
7 * unchanged. However, on the printing side it tracks print colours
8 * so the front end API doesn't have to.
9 *
10 * FIXME:
11 *
12 * - I'd _like_ to do automatic draw_updates, but it's a pain for
13 * draw_text in particular. I'd have to invent a front end API
14 * which retrieved the text bounds.
15 * + that might allow me to do the alignment centrally as well?
16 * * perhaps not, because PS can't return this information,
17 * so there would have to be a special case for it.
18 * + however, that at least doesn't stand in the way of using
19 * the text bounds for draw_update, because PS doesn't need
20 * draw_update since it's printing-only. Any _interactive_
21 * drawing API couldn't get away with refusing to tell you
22 * what parts of the screen a text draw had covered, because
23 * you would inevitably need to erase it later on.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <assert.h>
30#include <math.h>
31
32#include "puzzles.h"
33
34struct print_colour {
35 int hatch;
36 int hatch_when; /* 0=never 1=only-in-b&w 2=always */
37 float r, g, b;
38 float grey;
39};
40
41struct drawing {
42 const drawing_api *api;
43 void *handle;
44 struct print_colour *colours;
45 int ncolours, coloursize;
46 float scale;
47 /* `me' is only used in status_bar(), so print-oriented instances of
48 * this may set it to NULL. */
49 midend *me;
50 char *laststatus;
51};
52
53drawing *drawing_new(const drawing_api *api, midend *me, void *handle)
54{
55 drawing *dr = snew(drawing);
56 dr->api = api;
57 dr->handle = handle;
58 dr->colours = NULL;
59 dr->ncolours = dr->coloursize = 0;
60 dr->scale = 1.0F;
61 dr->me = me;
62 dr->laststatus = NULL;
63 return dr;
64}
65
66void drawing_free(drawing *dr)
67{
68 sfree(dr->laststatus);
69 sfree(dr->colours);
70 sfree(dr);
71}
72
73void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
74 int align, int colour, char *text)
75{
76 dr->api->draw_text(dr->handle, x, y, fonttype, fontsize, align,
77 colour, text);
78}
79
80void draw_rect(drawing *dr, int x, int y, int w, int h, int colour)
81{
82 dr->api->draw_rect(dr->handle, x, y, w, h, colour);
83}
84
85void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
86{
87 dr->api->draw_line(dr->handle, x1, y1, x2, y2, colour);
88}
89
90void draw_thick_line(drawing *dr, float thickness,
91 float x1, float y1, float x2, float y2, int colour)
92{
93 if (dr->api->draw_thick_line) {
94 dr->api->draw_thick_line(dr->handle, thickness,
95 x1, y1, x2, y2, colour);
96 } else {
97 /* We'll fake it up with a filled polygon. The tweak to the
98 * thickness empirically compensates for rounding errors, because
99 * polygon rendering uses integer coordinates.
100 */
101 float len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
102 float tvhatx = (x2 - x1)/len * (thickness/2 - 0.2);
103 float tvhaty = (y2 - y1)/len * (thickness/2 - 0.2);
104 int p[8];
105
106 p[0] = x1 - tvhaty;
107 p[1] = y1 + tvhatx;
108 p[2] = x2 - tvhaty;
109 p[3] = y2 + tvhatx;
110 p[4] = x2 + tvhaty;
111 p[5] = y2 - tvhatx;
112 p[6] = x1 + tvhaty;
113 p[7] = y1 - tvhatx;
114 dr->api->draw_polygon(dr->handle, p, 4, colour, colour);
115 }
116}
117
118void draw_polygon(drawing *dr, int *coords, int npoints,
119 int fillcolour, int outlinecolour)
120{
121 dr->api->draw_polygon(dr->handle, coords, npoints, fillcolour,
122 outlinecolour);
123}
124
125void draw_circle(drawing *dr, int cx, int cy, int radius,
126 int fillcolour, int outlinecolour)
127{
128 dr->api->draw_circle(dr->handle, cx, cy, radius, fillcolour,
129 outlinecolour);
130}
131
132void draw_update(drawing *dr, int x, int y, int w, int h)
133{
134 if (dr->api->draw_update)
135 dr->api->draw_update(dr->handle, x, y, w, h);
136}
137
138void clip(drawing *dr, int x, int y, int w, int h)
139{
140 dr->api->clip(dr->handle, x, y, w, h);
141}
142
143void unclip(drawing *dr)
144{
145 dr->api->unclip(dr->handle);
146}
147
148void start_draw(drawing *dr)
149{
150 dr->api->start_draw(dr->handle);
151}
152
153void end_draw(drawing *dr)
154{
155 dr->api->end_draw(dr->handle);
156}
157
158char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
159{
160 int i;
161
162 /*
163 * If the drawing implementation provides one of these, use it.
164 */
165 if (dr && dr->api->text_fallback)
166 return dr->api->text_fallback(dr->handle, strings, nstrings);
167
168 /*
169 * Otherwise, do the simple thing and just pick the first string
170 * that fits in plain ASCII. It will then need no translation
171 * out of UTF-8.
172 */
173 for (i = 0; i < nstrings; i++) {
174 const char *p;
175
176 for (p = strings[i]; *p; p++)
177 if (*p & 0x80)
178 break;
179 if (!*p)
180 return dupstr(strings[i]);
181 }
182
183 /*
184 * The caller was responsible for making sure _some_ string in
185 * the list was in plain ASCII.
186 */
187 assert(!"Should never get here");
188 return NULL; /* placate optimiser */
189}
190
191void status_bar(drawing *dr, char *text)
192{
193 char *rewritten;
194
195 if (!dr->api->status_bar)
196 return;
197
198 assert(dr->me);
199
200 rewritten = midend_rewrite_statusbar(dr->me, text);
201 if (!dr->laststatus || strcmp(rewritten, dr->laststatus)) {
202 dr->api->status_bar(dr->handle, rewritten);
203 sfree(dr->laststatus);
204 dr->laststatus = rewritten;
205 } else {
206 sfree(rewritten);
207 }
208}
209
210blitter *blitter_new(drawing *dr, int w, int h)
211{
212 return dr->api->blitter_new(dr->handle, w, h);
213}
214
215void blitter_free(drawing *dr, blitter *bl)
216{
217 dr->api->blitter_free(dr->handle, bl);
218}
219
220void blitter_save(drawing *dr, blitter *bl, int x, int y)
221{
222 dr->api->blitter_save(dr->handle, bl, x, y);
223}
224
225void blitter_load(drawing *dr, blitter *bl, int x, int y)
226{
227 dr->api->blitter_load(dr->handle, bl, x, y);
228}
229
230void print_begin_doc(drawing *dr, int pages)
231{
232 dr->api->begin_doc(dr->handle, pages);
233}
234
235void print_begin_page(drawing *dr, int number)
236{
237 dr->api->begin_page(dr->handle, number);
238}
239
240void print_begin_puzzle(drawing *dr, float xm, float xc,
241 float ym, float yc, int pw, int ph, float wmm,
242 float scale)
243{
244 dr->scale = scale;
245 dr->ncolours = 0;
246 dr->api->begin_puzzle(dr->handle, xm, xc, ym, yc, pw, ph, wmm);
247}
248
249void print_end_puzzle(drawing *dr)
250{
251 dr->api->end_puzzle(dr->handle);
252 dr->scale = 1.0F;
253}
254
255void print_end_page(drawing *dr, int number)
256{
257 dr->api->end_page(dr->handle, number);
258}
259
260void print_end_doc(drawing *dr)
261{
262 dr->api->end_doc(dr->handle);
263}
264
265void print_get_colour(drawing *dr, int colour, int printing_in_colour,
266 int *hatch, float *r, float *g, float *b)
267{
268 assert(colour >= 0 && colour < dr->ncolours);
269 if (dr->colours[colour].hatch_when == 2 ||
270 (dr->colours[colour].hatch_when == 1 && !printing_in_colour)) {
271 *hatch = dr->colours[colour].hatch;
272 } else {
273 *hatch = -1;
274 if (printing_in_colour) {
275 *r = dr->colours[colour].r;
276 *g = dr->colours[colour].g;
277 *b = dr->colours[colour].b;
278 } else {
279 *r = *g = *b = dr->colours[colour].grey;
280 }
281 }
282}
283
284static int print_generic_colour(drawing *dr, float r, float g, float b,
285 float grey, int hatch, int hatch_when)
286{
287 if (dr->ncolours >= dr->coloursize) {
288 dr->coloursize = dr->ncolours + 16;
289 dr->colours = sresize(dr->colours, dr->coloursize,
290 struct print_colour);
291 }
292 dr->colours[dr->ncolours].hatch = hatch;
293 dr->colours[dr->ncolours].hatch_when = hatch_when;
294 dr->colours[dr->ncolours].r = r;
295 dr->colours[dr->ncolours].g = g;
296 dr->colours[dr->ncolours].b = b;
297 dr->colours[dr->ncolours].grey = grey;
298 return dr->ncolours++;
299}
300
301int print_mono_colour(drawing *dr, int grey)
302{
303 return print_generic_colour(dr, grey, grey, grey, grey, -1, 0);
304}
305
306int print_grey_colour(drawing *dr, float grey)
307{
308 return print_generic_colour(dr, grey, grey, grey, grey, -1, 0);
309}
310
311int print_hatched_colour(drawing *dr, int hatch)
312{
313 return print_generic_colour(dr, 0, 0, 0, 0, hatch, 2);
314}
315
316int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int grey)
317{
318 return print_generic_colour(dr, r, g, b, grey, -1, 0);
319}
320
321int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey)
322{
323 return print_generic_colour(dr, r, g, b, grey, -1, 0);
324}
325
326int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch)
327{
328 return print_generic_colour(dr, r, g, b, 0, hatch, 1);
329}
330
331void print_line_width(drawing *dr, int width)
332{
333 /*
334 * I don't think it's entirely sensible to have line widths be
335 * entirely relative to the puzzle size; there is a point
336 * beyond which lines are just _stupidly_ thick. On the other
337 * hand, absolute line widths aren't particularly nice either
338 * because they start to feel a bit feeble at really large
339 * scales.
340 *
341 * My experimental answer is to scale line widths as the
342 * _square root_ of the main puzzle scale. Double the puzzle
343 * size, and the line width multiplies by 1.4.
344 */
345 dr->api->line_width(dr->handle, (float)sqrt(dr->scale) * width);
346}
347
348void print_line_dotted(drawing *dr, int dotted)
349{
350 dr->api->line_dotted(dr->handle, dotted);
351}
diff --git a/apps/plugins/puzzles/src/dsf.c b/apps/plugins/puzzles/src/dsf.c
new file mode 100644
index 0000000000..aa22392661
--- /dev/null
+++ b/apps/plugins/puzzles/src/dsf.c
@@ -0,0 +1,192 @@
1/*
2 * dsf.c: some functions to handle a disjoint set forest,
3 * which is a data structure useful in any solver which has to
4 * worry about avoiding closed loops.
5 */
6
7#include <assert.h>
8#include <string.h>
9
10#include "puzzles.h"
11
12/*void print_dsf(int *dsf, int size)
13{
14 int *printed_elements = snewn(size, int);
15 int *equal_elements = snewn(size, int);
16 int *inverse_elements = snewn(size, int);
17 int printed_count = 0, equal_count, inverse_count;
18 int i, n, inverse;
19
20 memset(printed_elements, -1, sizeof(int) * size);
21
22 while (1) {
23 equal_count = 0;
24 inverse_count = 0;
25 for (i = 0; i < size; ++i) {
26 if (!memchr(printed_elements, i, sizeof(int) * size))
27 break;
28 }
29 if (i == size)
30 goto done;
31
32 i = dsf_canonify(dsf, i);
33
34 for (n = 0; n < size; ++n) {
35 if (edsf_canonify(dsf, n, &inverse) == i) {
36 if (inverse)
37 inverse_elements[inverse_count++] = n;
38 else
39 equal_elements[equal_count++] = n;
40 }
41 }
42
43 for (n = 0; n < equal_count; ++n) {
44 fprintf(stderr, "%d ", equal_elements[n]);
45 printed_elements[printed_count++] = equal_elements[n];
46 }
47 if (inverse_count) {
48 fprintf(stderr, "!= ");
49 for (n = 0; n < inverse_count; ++n) {
50 fprintf(stderr, "%d ", inverse_elements[n]);
51 printed_elements[printed_count++] = inverse_elements[n];
52 }
53 }
54 fprintf(stderr, "\n");
55 }
56done:
57
58 sfree(printed_elements);
59 sfree(equal_elements);
60 sfree(inverse_elements);
61}*/
62
63void dsf_init(int *dsf, int size)
64{
65 int i;
66
67 for (i = 0; i < size; i++) dsf[i] = 6;
68 /* Bottom bit of each element of this array stores whether that
69 * element is opposite to its parent, which starts off as
70 * false. Second bit of each element stores whether that element
71 * is the root of its tree or not. If it's not the root, the
72 * remaining 30 bits are the parent, otherwise the remaining 30
73 * bits are the number of elements in the tree. */
74}
75
76int *snew_dsf(int size)
77{
78 int *ret;
79
80 ret = snewn(size, int);
81 dsf_init(ret, size);
82
83 /*print_dsf(ret, size); */
84
85 return ret;
86}
87
88int dsf_canonify(int *dsf, int index)
89{
90 return edsf_canonify(dsf, index, NULL);
91}
92
93void dsf_merge(int *dsf, int v1, int v2)
94{
95 edsf_merge(dsf, v1, v2, FALSE);
96}
97
98int dsf_size(int *dsf, int index) {
99 return dsf[dsf_canonify(dsf, index)] >> 2;
100}
101
102int edsf_canonify(int *dsf, int index, int *inverse_return)
103{
104 int start_index = index, canonical_index;
105 int inverse = 0;
106
107/* fprintf(stderr, "dsf = %p\n", dsf); */
108/* fprintf(stderr, "Canonify %2d\n", index); */
109
110 assert(index >= 0);
111
112 /* Find the index of the canonical element of the 'equivalence class' of
113 * which start_index is a member, and figure out whether start_index is the
114 * same as or inverse to that. */
115 while ((dsf[index] & 2) == 0) {
116 inverse ^= (dsf[index] & 1);
117 index = dsf[index] >> 2;
118/* fprintf(stderr, "index = %2d, ", index); */
119/* fprintf(stderr, "inverse = %d\n", inverse); */
120 }
121 canonical_index = index;
122
123 if (inverse_return)
124 *inverse_return = inverse;
125
126 /* Update every member of this 'equivalence class' to point directly at the
127 * canonical member. */
128 index = start_index;
129 while (index != canonical_index) {
130 int nextindex = dsf[index] >> 2;
131 int nextinverse = inverse ^ (dsf[index] & 1);
132 dsf[index] = (canonical_index << 2) | inverse;
133 inverse = nextinverse;
134 index = nextindex;
135 }
136
137 assert(inverse == 0);
138
139/* fprintf(stderr, "Return %2d\n", index); */
140
141 return index;
142}
143
144void edsf_merge(int *dsf, int v1, int v2, int inverse)
145{
146 int i1, i2;
147
148/* fprintf(stderr, "dsf = %p\n", dsf); */
149/* fprintf(stderr, "Merge [%2d,%2d], %d\n", v1, v2, inverse); */
150
151 v1 = edsf_canonify(dsf, v1, &i1);
152 assert(dsf[v1] & 2);
153 inverse ^= i1;
154 v2 = edsf_canonify(dsf, v2, &i2);
155 assert(dsf[v2] & 2);
156 inverse ^= i2;
157
158/* fprintf(stderr, "Doing [%2d,%2d], %d\n", v1, v2, inverse); */
159
160 if (v1 == v2)
161 assert(!inverse);
162 else {
163 assert(inverse == 0 || inverse == 1);
164 /*
165 * We always make the smaller of v1 and v2 the new canonical
166 * element. This ensures that the canonical element of any
167 * class in this structure is always the first element in
168 * it. 'Keen' depends critically on this property.
169 *
170 * (Jonas Koelker previously had this code choosing which
171 * way round to connect the trees by examining the sizes of
172 * the classes being merged, so that the root of the
173 * larger-sized class became the new root. This gives better
174 * asymptotic performance, but I've changed it to do it this
175 * way because I like having a deterministic canonical
176 * element.)
177 */
178 if (v1 > v2) {
179 int v3 = v1;
180 v1 = v2;
181 v2 = v3;
182 }
183 dsf[v1] += (dsf[v2] >> 2) << 2;
184 dsf[v2] = (v1 << 2) | !!inverse;
185 }
186
187 v2 = edsf_canonify(dsf, v2, &i2);
188 assert(v2 == v1);
189 assert(i2 == inverse);
190
191/* fprintf(stderr, "dsf[%2d] = %2d\n", v2, dsf[v2]); */
192}
diff --git a/apps/plugins/puzzles/src/emcc.c b/apps/plugins/puzzles/src/emcc.c
new file mode 100644
index 0000000000..ca033cbd47
--- /dev/null
+++ b/apps/plugins/puzzles/src/emcc.c
@@ -0,0 +1,876 @@
1/*
2 * emcc.c: the C component of an Emscripten-based web/Javascript front
3 * end for Puzzles.
4 *
5 * The Javascript parts of this system live in emcclib.js and
6 * emccpre.js. It also depends on being run in the context of a web
7 * page containing an appropriate collection of bits and pieces (a
8 * canvas, some buttons and links etc), which is generated for each
9 * puzzle by the script html/jspage.pl.
10 */
11
12/*
13 * Further thoughts on possible enhancements:
14 *
15 * - I think it might be feasible to have these JS puzzles permit
16 * loading and saving games in disk files. Saving would be done by
17 * constructing a data: URI encapsulating the save file, and then
18 * telling the browser to visit that URI with the effect that it
19 * would naturally pop up a 'where would you like to save this'
20 * dialog box. Loading, more or less similarly, might be feasible
21 * by using the DOM File API to ask the user to select a file and
22 * permit us to see its contents.
23 *
24 * - I should think about whether these webified puzzles can support
25 * touchscreen-based tablet browsers (assuming there are any that
26 * can cope with the reasonably modern JS and run it fast enough to
27 * be worthwhile).
28 *
29 * - think about making use of localStorage. It might be useful to
30 * let the user save games into there as an alternative to disk
31 * files - disk files are all very well for getting the save right
32 * out of your browser to (e.g.) email to me as a bug report, but
33 * for just resuming a game you were in the middle of, you'd
34 * probably rather have a nice simple 'quick save' and 'quick load'
35 * button pair. Also, that might be a useful place to store
36 * preferences, if I ever get round to writing a preferences UI.
37 *
38 * - some CSS to make the button bar and configuration dialogs a
39 * little less ugly would probably not go amiss.
40 *
41 * - this is a downright silly idea, but it does occur to me that if
42 * I were to write a PDF output driver for the Puzzles printing
43 * API, then I might be able to implement a sort of 'printing'
44 * feature in this front end, using data: URIs again. (Ask the user
45 * exactly what they want printed, then construct an appropriate
46 * PDF and embed it in a gigantic data: URI. Then they can print
47 * that using whatever they normally use to print PDFs!)
48 */
49
50#include <assert.h>
51#include <stdio.h>
52#include <string.h>
53#include <stdarg.h>
54
55#include "puzzles.h"
56
57/*
58 * Extern references to Javascript functions provided in emcclib.js.
59 */
60extern void js_debug(const char *);
61extern void js_error_box(const char *message);
62extern void js_remove_type_dropdown(void);
63extern void js_remove_solve_button(void);
64extern void js_add_preset(int menuid, const char *name, int value);
65extern int js_add_preset_submenu(int menuid, const char *name);
66extern int js_get_selected_preset(void);
67extern void js_select_preset(int n);
68extern void js_get_date_64(unsigned *p);
69extern void js_update_permalinks(const char *desc, const char *seed);
70extern void js_enable_undo_redo(int undo, int redo);
71extern void js_activate_timer();
72extern void js_deactivate_timer();
73extern void js_canvas_start_draw(void);
74extern void js_canvas_draw_update(int x, int y, int w, int h);
75extern void js_canvas_end_draw(void);
76extern void js_canvas_draw_rect(int x, int y, int w, int h,
77 const char *colour);
78extern void js_canvas_clip_rect(int x, int y, int w, int h);
79extern void js_canvas_unclip(void);
80extern void js_canvas_draw_line(float x1, float y1, float x2, float y2,
81 int width, const char *colour);
82extern void js_canvas_draw_poly(int *points, int npoints,
83 const char *fillcolour,
84 const char *outlinecolour);
85extern void js_canvas_draw_circle(int x, int y, int r,
86 const char *fillcolour,
87 const char *outlinecolour);
88extern int js_canvas_find_font_midpoint(int height, const char *fontptr);
89extern void js_canvas_draw_text(int x, int y, int halign,
90 const char *colptr, const char *fontptr,
91 const char *text);
92extern int js_canvas_new_blitter(int w, int h);
93extern void js_canvas_free_blitter(int id);
94extern void js_canvas_copy_to_blitter(int id, int x, int y, int w, int h);
95extern void js_canvas_copy_from_blitter(int id, int x, int y, int w, int h);
96extern void js_canvas_make_statusbar(void);
97extern void js_canvas_set_statusbar(const char *text);
98extern void js_canvas_set_size(int w, int h);
99
100extern void js_dialog_init(const char *title);
101extern void js_dialog_string(int i, const char *title, const char *initvalue);
102extern void js_dialog_choices(int i, const char *title, const char *choicelist,
103 int initvalue);
104extern void js_dialog_boolean(int i, const char *title, int initvalue);
105extern void js_dialog_launch(void);
106extern void js_dialog_cleanup(void);
107extern void js_focus_canvas(void);
108
109/*
110 * Call JS to get the date, and use that to initialise our random
111 * number generator to invent the first game seed.
112 */
113void get_random_seed(void **randseed, int *randseedsize)
114{
115 unsigned *ret = snewn(2, unsigned);
116 js_get_date_64(ret);
117 *randseed = ret;
118 *randseedsize = 2*sizeof(unsigned);
119}
120
121/*
122 * Fatal error, called in cases of complete despair such as when
123 * malloc() has returned NULL.
124 */
125void fatal(char *fmt, ...)
126{
127 char buf[512];
128 va_list ap;
129
130 strcpy(buf, "puzzle fatal error: ");
131
132 va_start(ap, fmt);
133 vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, ap);
134 va_end(ap);
135
136 js_error_box(buf);
137}
138
139void debug_printf(char *fmt, ...)
140{
141 char buf[512];
142 va_list ap;
143 va_start(ap, fmt);
144 vsnprintf(buf, sizeof(buf), fmt, ap);
145 va_end(ap);
146 js_debug(buf);
147}
148
149/*
150 * Helper function that makes it easy to test strings that might be
151 * NULL.
152 */
153int strnullcmp(const char *a, const char *b)
154{
155 if (a == NULL || b == NULL)
156 return a != NULL ? +1 : b != NULL ? -1 : 0;
157 return strcmp(a, b);
158}
159
160/*
161 * HTMLish names for the colours allocated by the puzzle.
162 */
163char **colour_strings;
164int ncolours;
165
166/*
167 * The global midend object.
168 */
169midend *me;
170
171/* ----------------------------------------------------------------------
172 * Timing functions.
173 */
174int timer_active = FALSE;
175void deactivate_timer(frontend *fe)
176{
177 js_deactivate_timer();
178 timer_active = FALSE;
179}
180void activate_timer(frontend *fe)
181{
182 if (!timer_active) {
183 js_activate_timer();
184 timer_active = TRUE;
185 }
186}
187void timer_callback(double tplus)
188{
189 if (timer_active)
190 midend_timer(me, tplus);
191}
192
193/* ----------------------------------------------------------------------
194 * Helper functions to resize the canvas, and variables to remember
195 * its size for other functions (e.g. trimming blitter rectangles).
196 */
197static int canvas_w, canvas_h;
198
199/* Called when we resize as a result of changing puzzle settings */
200static void resize(void)
201{
202 int w, h;
203 w = h = INT_MAX;
204 midend_size(me, &w, &h, FALSE);
205 js_canvas_set_size(w, h);
206 canvas_w = w;
207 canvas_h = h;
208}
209
210/* Called from JS when the user uses the resize handle */
211void resize_puzzle(int w, int h)
212{
213 midend_size(me, &w, &h, TRUE);
214 if (canvas_w != w || canvas_h != h) {
215 js_canvas_set_size(w, h);
216 canvas_w = w;
217 canvas_h = h;
218 midend_force_redraw(me);
219 }
220}
221
222/* Called from JS when the user uses the restore button */
223void restore_puzzle_size(int w, int h)
224{
225 midend_reset_tilesize(me);
226 resize();
227 midend_force_redraw(me);
228}
229
230/*
231 * HTML doesn't give us a default frontend colour of its own, so we
232 * just make up a lightish grey ourselves.
233 */
234void frontend_default_colour(frontend *fe, float *output)
235{
236 output[0] = output[1] = output[2] = 0.9F;
237}
238
239/*
240 * Helper function called from all over the place to ensure the undo
241 * and redo buttons get properly enabled and disabled after every move
242 * or undo or new-game event.
243 */
244static void update_undo_redo(void)
245{
246 js_enable_undo_redo(midend_can_undo(me), midend_can_redo(me));
247}
248
249/*
250 * Mouse event handlers called from JS.
251 */
252void mousedown(int x, int y, int button)
253{
254 button = (button == 0 ? LEFT_BUTTON :
255 button == 1 ? MIDDLE_BUTTON : RIGHT_BUTTON);
256 midend_process_key(me, x, y, button);
257 update_undo_redo();
258}
259
260void mouseup(int x, int y, int button)
261{
262 button = (button == 0 ? LEFT_RELEASE :
263 button == 1 ? MIDDLE_RELEASE : RIGHT_RELEASE);
264 midend_process_key(me, x, y, button);
265 update_undo_redo();
266}
267
268void mousemove(int x, int y, int buttons)
269{
270 int button = (buttons & 2 ? MIDDLE_DRAG :
271 buttons & 4 ? RIGHT_DRAG : LEFT_DRAG);
272 midend_process_key(me, x, y, button);
273 update_undo_redo();
274}
275
276/*
277 * Keyboard handler called from JS.
278 */
279void key(int keycode, int charcode, const char *key, const char *chr,
280 int shift, int ctrl)
281{
282 int keyevent = -1;
283
284 if (!strnullcmp(key, "Backspace") || !strnullcmp(key, "Del") ||
285 keycode == 8 || keycode == 46) {
286 keyevent = 127; /* Backspace / Delete */
287 } else if (!strnullcmp(key, "Enter") || keycode == 13) {
288 keyevent = 13; /* return */
289 } else if (!strnullcmp(key, "Left") || keycode == 37) {
290 keyevent = CURSOR_LEFT;
291 } else if (!strnullcmp(key, "Up") || keycode == 38) {
292 keyevent = CURSOR_UP;
293 } else if (!strnullcmp(key, "Right") || keycode == 39) {
294 keyevent = CURSOR_RIGHT;
295 } else if (!strnullcmp(key, "Down") || keycode == 40) {
296 keyevent = CURSOR_DOWN;
297 } else if (!strnullcmp(key, "End") || keycode == 35) {
298 /*
299 * We interpret Home, End, PgUp and PgDn as numeric keypad
300 * controls regardless of whether they're the ones on the
301 * numeric keypad (since we can't tell). The effect of
302 * this should only be that the non-numeric-pad versions
303 * of those keys generate directions in 8-way movement
304 * puzzles like Cube and Inertia.
305 */
306 keyevent = MOD_NUM_KEYPAD | '1';
307 } else if (!strnullcmp(key, "PageDown") || keycode==34) {
308 keyevent = MOD_NUM_KEYPAD | '3';
309 } else if (!strnullcmp(key, "Home") || keycode==36) {
310 keyevent = MOD_NUM_KEYPAD | '7';
311 } else if (!strnullcmp(key, "PageUp") || keycode==33) {
312 keyevent = MOD_NUM_KEYPAD | '9';
313 } else if (chr && chr[0] && !chr[1]) {
314 keyevent = chr[0] & 0xFF;
315 } else if (keycode >= 96 && keycode < 106) {
316 keyevent = MOD_NUM_KEYPAD | ('0' + keycode - 96);
317 } else if (keycode >= 65 && keycode <= 90) {
318 keyevent = keycode + (shift ? 0 : 32);
319 } else if (keycode >= 48 && keycode <= 57) {
320 keyevent = keycode;
321 } else if (keycode == 32) { /* space / CURSOR_SELECT2 */
322 keyevent = keycode;
323 }
324
325 if (keyevent >= 0) {
326 if (shift && keyevent >= 0x100)
327 keyevent |= MOD_SHFT;
328
329 if (ctrl) {
330 if (keyevent >= 0x100)
331 keyevent |= MOD_CTRL;
332 else
333 keyevent &= 0x1F;
334 }
335
336 midend_process_key(me, 0, 0, keyevent);
337 update_undo_redo();
338 }
339}
340
341/*
342 * Helper function called from several places to update the permalinks
343 * whenever a new game is created.
344 */
345static void update_permalinks(void)
346{
347 char *desc, *seed;
348 desc = midend_get_game_id(me);
349 seed = midend_get_random_seed(me);
350 js_update_permalinks(desc, seed);
351 sfree(desc);
352 sfree(seed);
353}
354
355/*
356 * Callback from the midend when the game ids change, so we can update
357 * the permalinks.
358 */
359static void ids_changed(void *ignored)
360{
361 update_permalinks();
362}
363
364/* ----------------------------------------------------------------------
365 * Implementation of the drawing API by calling Javascript canvas
366 * drawing functions. (Well, half of it; the other half is on the JS
367 * side.)
368 */
369static void js_start_draw(void *handle)
370{
371 js_canvas_start_draw();
372}
373
374static void js_clip(void *handle, int x, int y, int w, int h)
375{
376 js_canvas_clip_rect(x, y, w, h);
377}
378
379static void js_unclip(void *handle)
380{
381 js_canvas_unclip();
382}
383
384static void js_draw_text(void *handle, int x, int y, int fonttype,
385 int fontsize, int align, int colour, char *text)
386{
387 char fontstyle[80];
388 int halign;
389
390 sprintf(fontstyle, "%dpx %s", fontsize,
391 fonttype == FONT_FIXED ? "monospace" : "sans-serif");
392
393 if (align & ALIGN_VCENTRE)
394 y += js_canvas_find_font_midpoint(fontsize, fontstyle);
395
396 if (align & ALIGN_HCENTRE)
397 halign = 1;
398 else if (align & ALIGN_HRIGHT)
399 halign = 2;
400 else
401 halign = 0;
402
403 js_canvas_draw_text(x, y, halign, colour_strings[colour], fontstyle, text);
404}
405
406static void js_draw_rect(void *handle, int x, int y, int w, int h, int colour)
407{
408 js_canvas_draw_rect(x, y, w, h, colour_strings[colour]);
409}
410
411static void js_draw_line(void *handle, int x1, int y1, int x2, int y2,
412 int colour)
413{
414 js_canvas_draw_line(x1, y1, x2, y2, 1, colour_strings[colour]);
415}
416
417static void js_draw_thick_line(void *handle, float thickness,
418 float x1, float y1, float x2, float y2,
419 int colour)
420{
421 js_canvas_draw_line(x1, y1, x2, y2, thickness, colour_strings[colour]);
422}
423
424static void js_draw_poly(void *handle, int *coords, int npoints,
425 int fillcolour, int outlinecolour)
426{
427 js_canvas_draw_poly(coords, npoints,
428 fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
429 colour_strings[outlinecolour]);
430}
431
432static void js_draw_circle(void *handle, int cx, int cy, int radius,
433 int fillcolour, int outlinecolour)
434{
435 js_canvas_draw_circle(cx, cy, radius,
436 fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
437 colour_strings[outlinecolour]);
438}
439
440struct blitter {
441 int id; /* allocated on the js side */
442 int w, h; /* easier to retain here */
443};
444
445static blitter *js_blitter_new(void *handle, int w, int h)
446{
447 blitter *bl = snew(blitter);
448 bl->w = w;
449 bl->h = h;
450 bl->id = js_canvas_new_blitter(w, h);
451 return bl;
452}
453
454static void js_blitter_free(void *handle, blitter *bl)
455{
456 js_canvas_free_blitter(bl->id);
457 sfree(bl);
458}
459
460static void trim_rect(int *x, int *y, int *w, int *h)
461{
462 int x0, x1, y0, y1;
463
464 /*
465 * Reduce the size of the copied rectangle to stop it going
466 * outside the bounds of the canvas.
467 */
468
469 /* Transform from x,y,w,h form into coordinates of all edges */
470 x0 = *x;
471 y0 = *y;
472 x1 = *x + *w;
473 y1 = *y + *h;
474
475 /* Clip each coordinate at both extremes of the canvas */
476 x0 = (x0 < 0 ? 0 : x0 > canvas_w ? canvas_w : x0);
477 x1 = (x1 < 0 ? 0 : x1 > canvas_w ? canvas_w : x1);
478 y0 = (y0 < 0 ? 0 : y0 > canvas_h ? canvas_h : y0);
479 y1 = (y1 < 0 ? 0 : y1 > canvas_h ? canvas_h : y1);
480
481 /* Transform back into x,y,w,h to return */
482 *x = x0;
483 *y = y0;
484 *w = x1 - x0;
485 *h = y1 - y0;
486}
487
488static void js_blitter_save(void *handle, blitter *bl, int x, int y)
489{
490 int w = bl->w, h = bl->h;
491 trim_rect(&x, &y, &w, &h);
492 if (w > 0 && h > 0)
493 js_canvas_copy_to_blitter(bl->id, x, y, w, h);
494}
495
496static void js_blitter_load(void *handle, blitter *bl, int x, int y)
497{
498 int w = bl->w, h = bl->h;
499 trim_rect(&x, &y, &w, &h);
500 if (w > 0 && h > 0)
501 js_canvas_copy_from_blitter(bl->id, x, y, w, h);
502}
503
504static void js_draw_update(void *handle, int x, int y, int w, int h)
505{
506 trim_rect(&x, &y, &w, &h);
507 if (w > 0 && h > 0)
508 js_canvas_draw_update(x, y, w, h);
509}
510
511static void js_end_draw(void *handle)
512{
513 js_canvas_end_draw();
514}
515
516static void js_status_bar(void *handle, char *text)
517{
518 js_canvas_set_statusbar(text);
519}
520
521static char *js_text_fallback(void *handle, const char *const *strings,
522 int nstrings)
523{
524 return dupstr(strings[0]); /* Emscripten has no trouble with UTF-8 */
525}
526
527const struct drawing_api js_drawing = {
528 js_draw_text,
529 js_draw_rect,
530 js_draw_line,
531 js_draw_poly,
532 js_draw_circle,
533 js_draw_update,
534 js_clip,
535 js_unclip,
536 js_start_draw,
537 js_end_draw,
538 js_status_bar,
539 js_blitter_new,
540 js_blitter_free,
541 js_blitter_save,
542 js_blitter_load,
543 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
544 NULL, NULL, /* line_width, line_dotted */
545 js_text_fallback,
546 js_draw_thick_line,
547};
548
549/* ----------------------------------------------------------------------
550 * Presets and game-configuration dialog support.
551 */
552static game_params **presets;
553static int npresets;
554int have_presets_dropdown;
555
556void populate_js_preset_menu(int menuid, struct preset_menu *menu)
557{
558 int i;
559 for (i = 0; i < menu->n_entries; i++) {
560 struct preset_menu_entry *entry = &menu->entries[i];
561 if (entry->params) {
562 presets[entry->id] = entry->params;
563 js_add_preset(menuid, entry->title, entry->id);
564 } else {
565 int js_submenu = js_add_preset_submenu(menuid, entry->title);
566 populate_js_preset_menu(js_submenu, entry->submenu);
567 }
568 }
569}
570
571void select_appropriate_preset(void)
572{
573 if (have_presets_dropdown) {
574 int preset = midend_which_preset(me);
575 js_select_preset(preset < 0 ? -1 : preset);
576 }
577}
578
579static config_item *cfg = NULL;
580static int cfg_which;
581
582/*
583 * Set up a dialog box. This is pretty easy on the C side; most of the
584 * work is done in JS.
585 */
586static void cfg_start(int which)
587{
588 char *title;
589 int i;
590
591 cfg = midend_get_config(me, which, &title);
592 cfg_which = which;
593
594 js_dialog_init(title);
595 sfree(title);
596
597 for (i = 0; cfg[i].type != C_END; i++) {
598 switch (cfg[i].type) {
599 case C_STRING:
600 js_dialog_string(i, cfg[i].name, cfg[i].sval);
601 break;
602 case C_BOOLEAN:
603 js_dialog_boolean(i, cfg[i].name, cfg[i].ival);
604 break;
605 case C_CHOICES:
606 js_dialog_choices(i, cfg[i].name, cfg[i].sval, cfg[i].ival);
607 break;
608 }
609 }
610
611 js_dialog_launch();
612}
613
614/*
615 * Callbacks from JS when the OK button is clicked, to return the
616 * final state of each control.
617 */
618void dlg_return_sval(int index, const char *val)
619{
620 sfree(cfg[index].sval);
621 cfg[index].sval = dupstr(val);
622}
623void dlg_return_ival(int index, int val)
624{
625 cfg[index].ival = val;
626}
627
628/*
629 * Called when the user clicks OK or Cancel. use_results will be TRUE
630 * or FALSE respectively, in those cases. We terminate the dialog box,
631 * unless the user selected an invalid combination of parameters.
632 */
633static void cfg_end(int use_results)
634{
635 if (use_results) {
636 /*
637 * User hit OK.
638 */
639 char *err = midend_set_config(me, cfg_which, cfg);
640
641 if (err) {
642 /*
643 * The settings were unacceptable, so leave the config box
644 * open for the user to adjust them and try again.
645 */
646 js_error_box(err);
647 } else {
648 /*
649 * New settings are fine; start a new game and close the
650 * dialog.
651 */
652 select_appropriate_preset();
653 midend_new_game(me);
654 resize();
655 midend_redraw(me);
656 free_cfg(cfg);
657 js_dialog_cleanup();
658 }
659 } else {
660 /*
661 * User hit Cancel. Close the dialog, but also we must still
662 * reselect the right element of the dropdown list.
663 *
664 * (Because: imagine you have a preset selected, and then you
665 * select Custom from the list, but change your mind and hit
666 * Esc. The Custom option will now still be selected in the
667 * list, whereas obviously it should show the preset you still
668 * _actually_ have selected. Worse still, it'll be the visible
669 * rather than invisible Custom option - see the comment in
670 * js_add_preset in emcclib.js - so you won't even be able to
671 * select Custom without a faffy workaround.)
672 */
673 select_appropriate_preset();
674
675 free_cfg(cfg);
676 js_dialog_cleanup();
677 }
678}
679
680/* ----------------------------------------------------------------------
681 * Called from JS when a command is given to the puzzle by clicking a
682 * button or control of some sort.
683 */
684void command(int n)
685{
686 switch (n) {
687 case 0: /* specific game ID */
688 cfg_start(CFG_DESC);
689 break;
690 case 1: /* random game seed */
691 cfg_start(CFG_SEED);
692 break;
693 case 2: /* game parameter dropdown changed */
694 {
695 int i = js_get_selected_preset();
696 if (i < 0) {
697 /*
698 * The user selected 'Custom', so launch the config
699 * box.
700 */
701 if (thegame.can_configure) /* (double-check just in case) */
702 cfg_start(CFG_SETTINGS);
703 } else {
704 /*
705 * The user selected a preset, so just switch straight
706 * to that.
707 */
708 assert(i < npresets);
709 midend_set_params(me, presets[i]);
710 midend_new_game(me);
711 resize();
712 midend_redraw(me);
713 update_undo_redo();
714 js_focus_canvas();
715 select_appropriate_preset();
716 }
717 }
718 break;
719 case 3: /* OK clicked in a config box */
720 cfg_end(TRUE);
721 update_undo_redo();
722 break;
723 case 4: /* Cancel clicked in a config box */
724 cfg_end(FALSE);
725 update_undo_redo();
726 break;
727 case 5: /* New Game */
728 midend_process_key(me, 0, 0, 'n');
729 update_undo_redo();
730 js_focus_canvas();
731 break;
732 case 6: /* Restart */
733 midend_restart_game(me);
734 update_undo_redo();
735 js_focus_canvas();
736 break;
737 case 7: /* Undo */
738 midend_process_key(me, 0, 0, 'u');
739 update_undo_redo();
740 js_focus_canvas();
741 break;
742 case 8: /* Redo */
743 midend_process_key(me, 0, 0, 'r');
744 update_undo_redo();
745 js_focus_canvas();
746 break;
747 case 9: /* Solve */
748 if (thegame.can_solve) {
749 char *msg = midend_solve(me);
750 if (msg)
751 js_error_box(msg);
752 }
753 update_undo_redo();
754 js_focus_canvas();
755 break;
756 }
757}
758
759/* ----------------------------------------------------------------------
760 * Setup function called at page load time. It's called main() because
761 * that's the most convenient thing in Emscripten, but it's not main()
762 * in the usual sense of bounding the program's entire execution.
763 * Instead, this function returns once the initial puzzle is set up
764 * and working, and everything thereafter happens by means of JS event
765 * handlers sending us callbacks.
766 */
767int main(int argc, char **argv)
768{
769 char *param_err;
770 float *colours;
771 int i;
772
773 /*
774 * Instantiate a midend.
775 */
776 me = midend_new(NULL, &thegame, &js_drawing, NULL);
777
778 /*
779 * Chuck in the HTML fragment ID if we have one (trimming the
780 * leading # off the front first). If that's invalid, we retain
781 * the error message and will display it at the end, after setting
782 * up a random puzzle as usual.
783 */
784 if (argc > 1 && argv[1][0] == '#' && argv[1][1] != '\0')
785 param_err = midend_game_id(me, argv[1] + 1);
786 else
787 param_err = NULL;
788
789 /*
790 * Create either a random game or the specified one, and set the
791 * canvas size appropriately.
792 */
793 midend_new_game(me);
794 resize();
795
796 /*
797 * Create a status bar, if needed.
798 */
799 if (midend_wants_statusbar(me))
800 js_canvas_make_statusbar();
801
802 /*
803 * Set up the game-type dropdown with presets and/or the Custom
804 * option.
805 */
806 {
807 struct preset_menu *menu = midend_get_presets(me, &npresets);
808 presets = snewn(npresets, game_params *);
809 for (i = 0; i < npresets; i++)
810 presets[i] = NULL;
811
812 populate_js_preset_menu(0, menu);
813
814 if (thegame.can_configure)
815 js_add_preset(0, "Custom", -1);
816
817 have_presets_dropdown = TRUE;
818
819 /*
820 * Now ensure the appropriate element of the presets menu
821 * starts off selected, in case it isn't the first one in the
822 * list (e.g. Slant).
823 */
824 select_appropriate_preset();
825 }
826
827 /*
828 * Remove the Solve button if the game doesn't support it.
829 */
830 if (!thegame.can_solve)
831 js_remove_solve_button();
832
833 /*
834 * Retrieve the game's colours, and convert them into #abcdef type
835 * hex ID strings.
836 */
837 colours = midend_colours(me, &ncolours);
838 colour_strings = snewn(ncolours, char *);
839 for (i = 0; i < ncolours; i++) {
840 char col[40];
841 sprintf(col, "#%02x%02x%02x",
842 (unsigned)(0.5 + 255 * colours[i*3+0]),
843 (unsigned)(0.5 + 255 * colours[i*3+1]),
844 (unsigned)(0.5 + 255 * colours[i*3+2]));
845 colour_strings[i] = dupstr(col);
846 }
847
848 /*
849 * Request notification when the game ids change (e.g. if the user
850 * presses 'n', and also when Mines supersedes its game
851 * description), so that we can proactively update the permalink.
852 */
853 midend_request_id_changes(me, ids_changed, NULL);
854
855 /*
856 * Draw the puzzle's initial state, and set up the permalinks and
857 * undo/redo greying out.
858 */
859 midend_redraw(me);
860 update_permalinks();
861 update_undo_redo();
862
863 /*
864 * If we were given an erroneous game ID in argv[1], now's the
865 * time to put up the error box about it, after we've fully set up
866 * a random puzzle. Then when the user clicks 'ok', we have a
867 * puzzle for them.
868 */
869 if (param_err)
870 js_error_box(param_err);
871
872 /*
873 * Done. Return to JS, and await callbacks!
874 */
875 return 0;
876}
diff --git a/apps/plugins/puzzles/src/emcclib.js b/apps/plugins/puzzles/src/emcclib.js
new file mode 100644
index 0000000000..cd8876e76d
--- /dev/null
+++ b/apps/plugins/puzzles/src/emcclib.js
@@ -0,0 +1,752 @@
1/*
2 * emcclib.js: one of the Javascript components of an Emscripten-based
3 * web/Javascript front end for Puzzles.
4 *
5 * The other parts of this system live in emcc.c and emccpre.js. It
6 * also depends on being run in the context of a web page containing
7 * an appropriate collection of bits and pieces (a canvas, some
8 * buttons and links etc), which is generated for each puzzle by the
9 * script html/jspage.pl.
10 *
11 * This file contains a set of Javascript functions which we insert
12 * into Emscripten's library object via the --js-library option; this
13 * allows us to provide JS code which can be called from the
14 * Emscripten-compiled C, mostly dealing with UI interaction of
15 * various kinds.
16 */
17
18mergeInto(LibraryManager.library, {
19 /*
20 * void js_debug(const char *message);
21 *
22 * A function to write a diagnostic to the Javascript console.
23 * Unused in production, but handy in development.
24 */
25 js_debug: function(ptr) {
26 console.log(Pointer_stringify(ptr));
27 },
28
29 /*
30 * void js_error_box(const char *message);
31 *
32 * A wrapper around Javascript's alert(), so the C code can print
33 * simple error message boxes (e.g. when invalid data is entered
34 * in a configuration dialog).
35 */
36 js_error_box: function(ptr) {
37 alert(Pointer_stringify(ptr));
38 },
39
40 /*
41 * void js_remove_type_dropdown(void);
42 *
43 * Get rid of the drop-down list on the web page for selecting
44 * game presets. Called at setup time if the game back end
45 * provides neither presets nor configurability.
46 */
47 js_remove_type_dropdown: function() {
48 gametypelist.style.display = "none";
49 },
50
51 /*
52 * void js_remove_solve_button(void);
53 *
54 * Get rid of the Solve button on the web page. Called at setup
55 * time if the game doesn't support an in-game solve function.
56 */
57 js_remove_solve_button: function() {
58 document.getElementById("solve").style.display = "none";
59 },
60
61 /*
62 * void js_add_preset(int menuid, const char *name, int value);
63 *
64 * Add a preset to the drop-down types menu, or to a submenu of
65 * it. 'menuid' specifies an index into our array of submenus
66 * where the item might be placed; 'value' specifies the number
67 * that js_get_selected_preset() will return when this item is
68 * clicked.
69 */
70 js_add_preset: function(menuid, ptr, value) {
71 var name = Pointer_stringify(ptr);
72 var item = document.createElement("li");
73 item.setAttribute("data-index", value);
74 var tick = document.createElement("span");
75 tick.appendChild(document.createTextNode("\u2713"));
76 tick.style.color = "transparent";
77 tick.style.paddingRight = "0.5em";
78 item.appendChild(tick);
79 item.appendChild(document.createTextNode(name));
80 gametypesubmenus[menuid].appendChild(item);
81 gametypeitems.push(item);
82
83 item.onclick = function(event) {
84 if (dlg_dimmer === null) {
85 gametypeselectedindex = value;
86 command(2);
87 }
88 }
89 },
90
91 /*
92 * int js_add_preset_submenu(int menuid, const char *name);
93 *
94 * Add a submenu in the presets menu hierarchy. Returns its index,
95 * for passing as the 'menuid' argument in further calls to
96 * js_add_preset or this function.
97 */
98 js_add_preset_submenu: function(menuid, ptr, value) {
99 var name = Pointer_stringify(ptr);
100 var item = document.createElement("li");
101 // We still create a transparent tick element, even though it
102 // won't ever be selected, to make submenu titles line up
103 // nicely with their neighbours.
104 var tick = document.createElement("span");
105 tick.appendChild(document.createTextNode("\u2713"));
106 tick.style.color = "transparent";
107 tick.style.paddingRight = "0.5em";
108 item.appendChild(tick);
109 item.appendChild(document.createTextNode(name));
110 var submenu = document.createElement("ul");
111 submenu.className = "left";
112 item.appendChild(submenu);
113 gametypesubmenus[menuid].appendChild(item);
114 var toret = gametypesubmenus.length;
115 gametypesubmenus.push(submenu);
116 return toret;
117 },
118
119 /*
120 * int js_get_selected_preset(void);
121 *
122 * Return the index of the currently selected value in the type
123 * dropdown.
124 */
125 js_get_selected_preset: function() {
126 return gametypeselectedindex;
127 },
128
129 /*
130 * void js_select_preset(int n);
131 *
132 * Cause a different value to be selected in the type dropdown
133 * (for when the user selects values from the Custom configurer
134 * which turn out to exactly match a preset).
135 */
136 js_select_preset: function(n) {
137 gametypeselectedindex = n;
138 for (var i in gametypeitems) {
139 var item = gametypeitems[i];
140 var tick = item.firstChild;
141 if (item.getAttribute("data-index") == n) {
142 tick.style.color = "inherit";
143 } else {
144 tick.style.color = "transparent";
145 }
146 }
147 },
148
149 /*
150 * void js_get_date_64(unsigned *p);
151 *
152 * Return the current date, in milliseconds since the epoch
153 * (Javascript's native format), as a 64-bit integer. Used to
154 * invent an initial random seed for puzzle generation.
155 */
156 js_get_date_64: function(ptr) {
157 var d = (new Date()).valueOf();
158 setValue(ptr, d, 'i64');
159 },
160
161 /*
162 * void js_update_permalinks(const char *desc, const char *seed);
163 *
164 * Update the permalinks on the web page for a new game
165 * description and optional random seed. desc can never be NULL,
166 * but seed might be (if the game was generated by entering a
167 * descriptive id by hand), in which case we suppress display of
168 * the random seed permalink.
169 */
170 js_update_permalinks: function(desc, seed) {
171 desc = Pointer_stringify(desc);
172 permalink_desc.href = "#" + desc;
173
174 if (seed == 0) {
175 permalink_seed.style.display = "none";
176 } else {
177 seed = Pointer_stringify(seed);
178 permalink_seed.href = "#" + seed;
179 permalink_seed.style.display = "inline";
180 }
181 },
182
183 /*
184 * void js_enable_undo_redo(int undo, int redo);
185 *
186 * Set the enabled/disabled states of the undo and redo buttons,
187 * after a move.
188 */
189 js_enable_undo_redo: function(undo, redo) {
190 disable_menu_item(undo_button, (undo == 0));
191 disable_menu_item(redo_button, (redo == 0));
192 },
193
194 /*
195 * void js_activate_timer();
196 *
197 * Start calling the C timer_callback() function every 20ms.
198 */
199 js_activate_timer: function() {
200 if (timer === null) {
201 timer_reference_date = (new Date()).valueOf();
202 timer = setInterval(function() {
203 var now = (new Date()).valueOf();
204 timer_callback((now - timer_reference_date) / 1000.0);
205 timer_reference_date = now;
206 return true;
207 }, 20);
208 }
209 },
210
211 /*
212 * void js_deactivate_timer();
213 *
214 * Stop calling the C timer_callback() function every 20ms.
215 */
216 js_deactivate_timer: function() {
217 if (timer !== null) {
218 clearInterval(timer);
219 timer = null;
220 }
221 },
222
223 /*
224 * void js_canvas_start_draw(void);
225 *
226 * Prepare to do some drawing on the canvas.
227 */
228 js_canvas_start_draw: function() {
229 ctx = offscreen_canvas.getContext('2d');
230 update_xmin = update_xmax = update_ymin = update_ymax = undefined;
231 },
232
233 /*
234 * void js_canvas_draw_update(int x, int y, int w, int h);
235 *
236 * Mark a rectangle of the off-screen canvas as needing to be
237 * copied to the on-screen one.
238 */
239 js_canvas_draw_update: function(x, y, w, h) {
240 /*
241 * Currently we do this in a really simple way, just by taking
242 * the smallest rectangle containing all updates so far. We
243 * could instead keep the data in a richer form (e.g. retain
244 * multiple smaller rectangles needing update, and only redraw
245 * the whole thing beyond a certain threshold) but this will
246 * do for now.
247 */
248 if (update_xmin === undefined || update_xmin > x) update_xmin = x;
249 if (update_ymin === undefined || update_ymin > y) update_ymin = y;
250 if (update_xmax === undefined || update_xmax < x+w) update_xmax = x+w;
251 if (update_ymax === undefined || update_ymax < y+h) update_ymax = y+h;
252 },
253
254 /*
255 * void js_canvas_end_draw(void);
256 *
257 * Finish the drawing, by actually copying the newly drawn stuff
258 * to the on-screen canvas.
259 */
260 js_canvas_end_draw: function() {
261 if (update_xmin !== undefined) {
262 var onscreen_ctx = onscreen_canvas.getContext('2d');
263 onscreen_ctx.drawImage(offscreen_canvas,
264 update_xmin, update_ymin,
265 update_xmax - update_xmin,
266 update_ymax - update_ymin,
267 update_xmin, update_ymin,
268 update_xmax - update_xmin,
269 update_ymax - update_ymin);
270 }
271 ctx = null;
272 },
273
274 /*
275 * void js_canvas_draw_rect(int x, int y, int w, int h,
276 * const char *colour);
277 *
278 * Draw a rectangle.
279 */
280 js_canvas_draw_rect: function(x, y, w, h, colptr) {
281 ctx.fillStyle = Pointer_stringify(colptr);
282 ctx.fillRect(x, y, w, h);
283 },
284
285 /*
286 * void js_canvas_clip_rect(int x, int y, int w, int h);
287 *
288 * Set a clipping rectangle.
289 */
290 js_canvas_clip_rect: function(x, y, w, h) {
291 ctx.save();
292 ctx.beginPath();
293 ctx.rect(x, y, w, h);
294 ctx.clip();
295 },
296
297 /*
298 * void js_canvas_unclip(void);
299 *
300 * Reset to no clipping.
301 */
302 js_canvas_unclip: function() {
303 ctx.restore();
304 },
305
306 /*
307 * void js_canvas_draw_line(float x1, float y1, float x2, float y2,
308 * int width, const char *colour);
309 *
310 * Draw a line. We must adjust the coordinates by 0.5 because
311 * Javascript's canvas coordinates appear to be pixel corners,
312 * whereas we want pixel centres. Also, we manually draw the pixel
313 * at each end of the line, which our clients will expect but
314 * Javascript won't reliably do by default (in common with other
315 * Postscriptish drawing frameworks).
316 */
317 js_canvas_draw_line: function(x1, y1, x2, y2, width, colour) {
318 colour = Pointer_stringify(colour);
319
320 ctx.beginPath();
321 ctx.moveTo(x1 + 0.5, y1 + 0.5);
322 ctx.lineTo(x2 + 0.5, y2 + 0.5);
323 ctx.lineWidth = width;
324 ctx.lineCap = 'round';
325 ctx.lineJoin = 'round';
326 ctx.strokeStyle = colour;
327 ctx.stroke();
328 ctx.fillStyle = colour;
329 ctx.fillRect(x1, y1, 1, 1);
330 ctx.fillRect(x2, y2, 1, 1);
331 },
332
333 /*
334 * void js_canvas_draw_poly(int *points, int npoints,
335 * const char *fillcolour,
336 * const char *outlinecolour);
337 *
338 * Draw a polygon.
339 */
340 js_canvas_draw_poly: function(pointptr, npoints, fill, outline) {
341 ctx.beginPath();
342 ctx.moveTo(getValue(pointptr , 'i32') + 0.5,
343 getValue(pointptr+4, 'i32') + 0.5);
344 for (var i = 1; i < npoints; i++)
345 ctx.lineTo(getValue(pointptr+8*i , 'i32') + 0.5,
346 getValue(pointptr+8*i+4, 'i32') + 0.5);
347 ctx.closePath();
348 if (fill != 0) {
349 ctx.fillStyle = Pointer_stringify(fill);
350 ctx.fill();
351 }
352 ctx.lineWidth = '1';
353 ctx.lineCap = 'round';
354 ctx.lineJoin = 'round';
355 ctx.strokeStyle = Pointer_stringify(outline);
356 ctx.stroke();
357 },
358
359 /*
360 * void js_canvas_draw_circle(int x, int y, int r,
361 * const char *fillcolour,
362 * const char *outlinecolour);
363 *
364 * Draw a circle.
365 */
366 js_canvas_draw_circle: function(x, y, r, fill, outline) {
367 ctx.beginPath();
368 ctx.arc(x + 0.5, y + 0.5, r, 0, 2*Math.PI);
369 if (fill != 0) {
370 ctx.fillStyle = Pointer_stringify(fill);
371 ctx.fill();
372 }
373 ctx.lineWidth = '1';
374 ctx.lineCap = 'round';
375 ctx.lineJoin = 'round';
376 ctx.strokeStyle = Pointer_stringify(outline);
377 ctx.stroke();
378 },
379
380 /*
381 * int js_canvas_find_font_midpoint(int height, const char *fontptr);
382 *
383 * Return the adjustment required for text displayed using
384 * ALIGN_VCENTRE. We want to place the midpoint between the
385 * baseline and the cap-height at the specified position; so this
386 * function returns the adjustment which, when added to the
387 * desired centre point, returns the y-coordinate at which you
388 * should put the baseline.
389 *
390 * There is no sensible method of querying this kind of font
391 * metric in Javascript, so instead we render a piece of test text
392 * to a throwaway offscreen canvas and then read the pixel data
393 * back out to find the highest and lowest pixels. That's good
394 * _enough_ (in that we only needed the answer to the nearest
395 * pixel anyway), but rather disgusting!
396 *
397 * Since this is a very expensive operation, we cache the results
398 * per (font,height) pair.
399 */
400 js_canvas_find_font_midpoint: function(height, font) {
401 font = Pointer_stringify(font);
402
403 // Reuse cached value if possible
404 if (midpoint_cache[font] !== undefined)
405 return midpoint_cache[font];
406
407 // Find the width of the string
408 var ctx1 = onscreen_canvas.getContext('2d');
409 ctx1.font = font;
410 var width = (ctx1.measureText(midpoint_test_str).width + 1) | 0;
411
412 // Construct a test canvas of appropriate size, initialise it to
413 // black, and draw the string on it in white
414 var measure_canvas = document.createElement('canvas');
415 var ctx2 = measure_canvas.getContext('2d');
416 ctx2.canvas.width = width;
417 ctx2.canvas.height = 2*height;
418 ctx2.fillStyle = "#000000";
419 ctx2.fillRect(0, 0, width, 2*height);
420 var baseline = (1.5*height) | 0;
421 ctx2.fillStyle = "#ffffff";
422 ctx2.font = font;
423 ctx2.fillText(midpoint_test_str, 0, baseline);
424
425 // Scan the contents of the test canvas to find the top and bottom
426 // set pixels.
427 var pixels = ctx2.getImageData(0, 0, width, 2*height).data;
428 var ymin = 2*height, ymax = -1;
429 for (var y = 0; y < 2*height; y++) {
430 for (var x = 0; x < width; x++) {
431 if (pixels[4*(y*width+x)] != 0) {
432 if (ymin > y) ymin = y;
433 if (ymax < y) ymax = y;
434 break;
435 }
436 }
437 }
438
439 var ret = (baseline - (ymin + ymax) / 2) | 0;
440 midpoint_cache[font] = ret;
441 return ret;
442 },
443
444 /*
445 * void js_canvas_draw_text(int x, int y, int halign,
446 * const char *colptr, const char *fontptr,
447 * const char *text);
448 *
449 * Draw text. Vertical alignment has been taken care of on the C
450 * side, by optionally calling the above function. Horizontal
451 * alignment is handled here, since we can get the canvas draw
452 * function to do it for us with almost no extra effort.
453 */
454 js_canvas_draw_text: function(x, y, halign, colptr, fontptr, text) {
455 ctx.font = Pointer_stringify(fontptr);
456 ctx.fillStyle = Pointer_stringify(colptr);
457 ctx.textAlign = (halign == 0 ? 'left' :
458 halign == 1 ? 'center' : 'right');
459 ctx.textBaseline = 'alphabetic';
460 ctx.fillText(Pointer_stringify(text), x, y);
461 },
462
463 /*
464 * int js_canvas_new_blitter(int w, int h);
465 *
466 * Create a new blitter object, which is just an offscreen canvas
467 * of the specified size.
468 */
469 js_canvas_new_blitter: function(w, h) {
470 var id = blittercount++;
471 blitters[id] = document.createElement("canvas");
472 blitters[id].width = w;
473 blitters[id].height = h;
474 return id;
475 },
476
477 /*
478 * void js_canvas_free_blitter(int id);
479 *
480 * Free a blitter (or rather, destroy our reference to it so JS
481 * can garbage-collect it, and also enforce that we don't
482 * accidentally use it again afterwards).
483 */
484 js_canvas_free_blitter: function(id) {
485 blitters[id] = null;
486 },
487
488 /*
489 * void js_canvas_copy_to_blitter(int id, int x, int y, int w, int h);
490 *
491 * Copy from the puzzle image to a blitter. The size is passed to
492 * us, partly so we don't have to remember the size of each
493 * blitter, but mostly so that the C side can adjust the copy
494 * rectangle in the case where it partially overlaps the edge of
495 * the screen.
496 */
497 js_canvas_copy_to_blitter: function(id, x, y, w, h) {
498 var blitter_ctx = blitters[id].getContext('2d');
499 blitter_ctx.drawImage(offscreen_canvas,
500 x, y, w, h,
501 0, 0, w, h);
502 },
503
504 /*
505 * void js_canvas_copy_from_blitter(int id, int x, int y, int w, int h);
506 *
507 * Copy from a blitter back to the puzzle image. As above, the
508 * size of the copied rectangle is passed to us from the C side
509 * and may already have been modified.
510 */
511 js_canvas_copy_from_blitter: function(id, x, y, w, h) {
512 ctx.drawImage(blitters[id],
513 0, 0, w, h,
514 x, y, w, h);
515 },
516
517 /*
518 * void js_canvas_make_statusbar(void);
519 *
520 * Cause a status bar to exist. Called at setup time if the puzzle
521 * back end turns out to want one.
522 */
523 js_canvas_make_statusbar: function() {
524 var statusholder = document.getElementById("statusbarholder");
525 statusbar = document.createElement("div");
526 statusbar.style.overflow = "hidden";
527 statusbar.style.width = (onscreen_canvas.width - 4) + "px";
528 statusholder.style.width = onscreen_canvas.width + "px";
529 statusbar.style.height = "1.2em";
530 statusbar.style.textAlign = "left";
531 statusbar.style.background = "#d8d8d8";
532 statusbar.style.borderLeft = '2px solid #c8c8c8';
533 statusbar.style.borderTop = '2px solid #c8c8c8';
534 statusbar.style.borderRight = '2px solid #e8e8e8';
535 statusbar.style.borderBottom = '2px solid #e8e8e8';
536 statusbar.appendChild(document.createTextNode(" "));
537 statusholder.appendChild(statusbar);
538 },
539
540 /*
541 * void js_canvas_set_statusbar(const char *text);
542 *
543 * Set the text in the status bar.
544 */
545 js_canvas_set_statusbar: function(ptr) {
546 var text = Pointer_stringify(ptr);
547 statusbar.replaceChild(document.createTextNode(text),
548 statusbar.lastChild);
549 },
550
551 /*
552 * void js_canvas_set_size(int w, int h);
553 *
554 * Set the size of the puzzle canvas. Called at setup, and every
555 * time the user picks new puzzle settings requiring a different
556 * size.
557 */
558 js_canvas_set_size: function(w, h) {
559 onscreen_canvas.width = w;
560 offscreen_canvas.width = w;
561 if (statusbar !== null) {
562 statusbar.style.width = (w - 4) + "px";
563 document.getElementById("statusbarholder").style.width = w + "px";
564 }
565 resizable_div.style.width = w + "px";
566
567 onscreen_canvas.height = h;
568 offscreen_canvas.height = h;
569 },
570
571 /*
572 * void js_dialog_init(const char *title);
573 *
574 * Begin constructing a 'dialog box' which will be popped up in an
575 * overlay on top of the rest of the puzzle web page.
576 */
577 js_dialog_init: function(titletext) {
578 // Create an overlay on the page which darkens everything
579 // beneath it.
580 dlg_dimmer = document.createElement("div");
581 dlg_dimmer.style.width = "100%";
582 dlg_dimmer.style.height = "100%";
583 dlg_dimmer.style.background = '#000000';
584 dlg_dimmer.style.position = 'fixed';
585 dlg_dimmer.style.opacity = 0.3;
586 dlg_dimmer.style.top = dlg_dimmer.style.left = 0;
587 dlg_dimmer.style["z-index"] = 99;
588
589 // Now create a form which sits on top of that in turn.
590 dlg_form = document.createElement("form");
591 dlg_form.style.width = (window.innerWidth * 2 / 3) + "px";
592 dlg_form.style.opacity = 1;
593 dlg_form.style.background = '#ffffff';
594 dlg_form.style.color = '#000000';
595 dlg_form.style.position = 'absolute';
596 dlg_form.style.border = "2px solid black";
597 dlg_form.style.padding = "20px";
598 dlg_form.style.top = (window.innerHeight / 10) + "px";
599 dlg_form.style.left = (window.innerWidth / 6) + "px";
600 dlg_form.style["z-index"] = 100;
601
602 var title = document.createElement("p");
603 title.style.marginTop = "0px";
604 title.appendChild(document.createTextNode
605 (Pointer_stringify(titletext)));
606 dlg_form.appendChild(title);
607
608 dlg_return_funcs = [];
609 dlg_next_id = 0;
610 },
611
612 /*
613 * void js_dialog_string(int i, const char *title, const char *initvalue);
614 *
615 * Add a string control (that is, an edit box) to the dialog under
616 * construction.
617 */
618 js_dialog_string: function(index, title, initialtext) {
619 dlg_form.appendChild(document.createTextNode(Pointer_stringify(title)));
620 var editbox = document.createElement("input");
621 editbox.type = "text";
622 editbox.value = Pointer_stringify(initialtext);
623 dlg_form.appendChild(editbox);
624 dlg_form.appendChild(document.createElement("br"));
625
626 dlg_return_funcs.push(function() {
627 dlg_return_sval(index, editbox.value);
628 });
629 },
630
631 /*
632 * void js_dialog_choices(int i, const char *title, const char *choicelist,
633 * int initvalue);
634 *
635 * Add a choices control (i.e. a drop-down list) to the dialog
636 * under construction. The 'choicelist' parameter is unchanged
637 * from the way the puzzle back end will have supplied it: i.e.
638 * it's still encoded as a single string whose first character
639 * gives the separator.
640 */
641 js_dialog_choices: function(index, title, choicelist, initvalue) {
642 dlg_form.appendChild(document.createTextNode(Pointer_stringify(title)));
643 var dropdown = document.createElement("select");
644 var choicestr = Pointer_stringify(choicelist);
645 var items = choicestr.slice(1).split(choicestr[0]);
646 var options = [];
647 for (var i in items) {
648 var option = document.createElement("option");
649 option.value = i;
650 option.appendChild(document.createTextNode(items[i]));
651 if (i == initvalue) option.selected = true;
652 dropdown.appendChild(option);
653 options.push(option);
654 }
655 dlg_form.appendChild(dropdown);
656 dlg_form.appendChild(document.createElement("br"));
657
658 dlg_return_funcs.push(function() {
659 var val = 0;
660 for (var i in options) {
661 if (options[i].selected) {
662 val = options[i].value;
663 break;
664 }
665 }
666 dlg_return_ival(index, val);
667 });
668 },
669
670 /*
671 * void js_dialog_boolean(int i, const char *title, int initvalue);
672 *
673 * Add a boolean control (a checkbox) to the dialog under
674 * construction. Checkboxes are generally expected to be sensitive
675 * on their label text as well as the box itself, so for this
676 * control we create an actual label rather than merely a text
677 * node (and hence we must allocate an id to the checkbox so that
678 * the label can refer to it).
679 */
680 js_dialog_boolean: function(index, title, initvalue) {
681 var checkbox = document.createElement("input");
682 checkbox.type = "checkbox";
683 checkbox.id = "cb" + String(dlg_next_id++);
684 checkbox.checked = (initvalue != 0);
685 dlg_form.appendChild(checkbox);
686 var checkboxlabel = document.createElement("label");
687 checkboxlabel.setAttribute("for", checkbox.id);
688 checkboxlabel.textContent = Pointer_stringify(title);
689 dlg_form.appendChild(checkboxlabel);
690 dlg_form.appendChild(document.createElement("br"));
691
692 dlg_return_funcs.push(function() {
693 dlg_return_ival(index, checkbox.checked ? 1 : 0);
694 });
695 },
696
697 /*
698 * void js_dialog_launch(void);
699 *
700 * Finish constructing a dialog, and actually display it, dimming
701 * everything else on the page.
702 */
703 js_dialog_launch: function() {
704 // Put in the OK and Cancel buttons at the bottom.
705 var button;
706
707 button = document.createElement("input");
708 button.type = "button";
709 button.value = "OK";
710 button.onclick = function(event) {
711 for (var i in dlg_return_funcs)
712 dlg_return_funcs[i]();
713 command(3);
714 }
715 dlg_form.appendChild(button);
716
717 button = document.createElement("input");
718 button.type = "button";
719 button.value = "Cancel";
720 button.onclick = function(event) {
721 command(4);
722 }
723 dlg_form.appendChild(button);
724
725 document.body.appendChild(dlg_dimmer);
726 document.body.appendChild(dlg_form);
727 },
728
729 /*
730 * void js_dialog_cleanup(void);
731 *
732 * Stop displaying a dialog, and clean up the internal state
733 * associated with it.
734 */
735 js_dialog_cleanup: function() {
736 document.body.removeChild(dlg_dimmer);
737 document.body.removeChild(dlg_form);
738 dlg_dimmer = dlg_form = null;
739 onscreen_canvas.focus();
740 },
741
742 /*
743 * void js_focus_canvas(void);
744 *
745 * Return keyboard focus to the puzzle canvas. Called after a
746 * puzzle-control button is pressed, which tends to have the side
747 * effect of taking focus away from the canvas.
748 */
749 js_focus_canvas: function() {
750 onscreen_canvas.focus();
751 }
752});
diff --git a/apps/plugins/puzzles/src/emccpre.js b/apps/plugins/puzzles/src/emccpre.js
new file mode 100644
index 0000000000..d715858883
--- /dev/null
+++ b/apps/plugins/puzzles/src/emccpre.js
@@ -0,0 +1,359 @@
1/*
2 * emccpre.js: one of the Javascript components of an Emscripten-based
3 * web/Javascript front end for Puzzles.
4 *
5 * The other parts of this system live in emcc.c and emcclib.js. It
6 * also depends on being run in the context of a web page containing
7 * an appropriate collection of bits and pieces (a canvas, some
8 * buttons and links etc), which is generated for each puzzle by the
9 * script html/jspage.pl.
10 *
11 * This file contains the Javascript code which is prefixed unmodified
12 * to Emscripten's output via the --pre-js option. It declares all our
13 * global variables, and provides the puzzle init function and a
14 * couple of other helper functions.
15 */
16
17// To avoid flicker while doing complicated drawing, we use two
18// canvases, the same size. One is actually on the web page, and the
19// other is off-screen. We do all our drawing on the off-screen one
20// first, and then copy rectangles of it to the on-screen canvas in
21// response to draw_update() calls by the game backend.
22var onscreen_canvas, offscreen_canvas;
23
24// A persistent drawing context for the offscreen canvas, to save
25// constructing one per individual graphics operation.
26var ctx;
27
28// Bounding rectangle for the copy to the onscreen canvas that will be
29// done at drawing end time. Updated by js_canvas_draw_update and used
30// by js_canvas_end_draw.
31var update_xmin, update_xmax, update_ymin, update_ymax;
32
33// Module object for Emscripten. We fill in these parameters to ensure
34// that Module.run() won't be called until we're ready (we want to do
35// our own init stuff first), and that when main() returns nothing
36// will get cleaned up so we remain able to call the puzzle's various
37// callbacks.
38var Module = {
39 'noInitialRun': true,
40 'noExitRuntime': true
41};
42
43// Variables used by js_canvas_find_font_midpoint().
44var midpoint_test_str = "ABCDEFGHIKLMNOPRSTUVWXYZ0123456789";
45var midpoint_cache = [];
46
47// Variables used by js_activate_timer() and js_deactivate_timer().
48var timer = null;
49var timer_reference_date;
50
51// void timer_callback(double tplus);
52//
53// Called every 20ms while timing is active.
54var timer_callback;
55
56// The status bar object, if we create one.
57var statusbar = null;
58
59// Currently live blitters. We keep an integer id for each one on the
60// JS side; the C side, which expects a blitter to look like a struct,
61// simply defines the struct to contain that integer id.
62var blittercount = 0;
63var blitters = [];
64
65// State for the dialog-box mechanism. dlg_dimmer and dlg_form are the
66// page-darkening overlay and the actual dialog box respectively;
67// dlg_next_id is used to allocate each checkbox a unique id to use
68// for linking its label to it (see js_dialog_boolean);
69// dlg_return_funcs is a list of JS functions to be called when the OK
70// button is pressed, to pass the results back to C.
71var dlg_dimmer = null, dlg_form = null;
72var dlg_next_id = 0;
73var dlg_return_funcs = null;
74
75// void dlg_return_sval(int index, const char *val);
76// void dlg_return_ival(int index, int val);
77//
78// C-side entry points called by functions in dlg_return_funcs, to
79// pass back the final value in each dialog control.
80var dlg_return_sval, dlg_return_ival;
81
82// The <ul> object implementing the game-type drop-down, and a list of
83// the <li> objects inside it. Used by js_add_preset(),
84// js_get_selected_preset() and js_select_preset().
85var gametypelist = null, gametypeitems = [];
86var gametypeselectedindex = null;
87var gametypesubmenus = [];
88
89// The two anchors used to give permalinks to the current puzzle. Used
90// by js_update_permalinks().
91var permalink_seed, permalink_desc;
92
93// The undo and redo buttons. Used by js_enable_undo_redo().
94var undo_button, redo_button;
95
96// A div element enclosing both the puzzle and its status bar, used
97// for positioning the resize handle.
98var resizable_div;
99
100// Helper function to find the absolute position of a given DOM
101// element on a page, by iterating upwards through the DOM finding
102// each element's offset from its parent, and thus calculating the
103// page-relative position of the target element.
104function element_coords(element) {
105 var ex = 0, ey = 0;
106 while (element.offsetParent) {
107 ex += element.offsetLeft;
108 ey += element.offsetTop;
109 element = element.offsetParent;
110 }
111 return {x: ex, y:ey};
112}
113
114// Helper function which is passed a mouse event object and a DOM
115// element, and returns the coordinates of the mouse event relative to
116// the top left corner of the element by subtracting element_coords
117// from event.page{X,Y}.
118function relative_mouse_coords(event, element) {
119 var ecoords = element_coords(element);
120 return {x: event.pageX - ecoords.x,
121 y: event.pageY - ecoords.y};
122}
123
124// Enable and disable items in the CSS menus.
125function disable_menu_item(item, disabledFlag) {
126 if (disabledFlag)
127 item.className = "disabled";
128 else
129 item.className = "";
130}
131
132// Init function called from body.onload.
133function initPuzzle() {
134 // Construct the off-screen canvas used for double buffering.
135 onscreen_canvas = document.getElementById("puzzlecanvas");
136 offscreen_canvas = document.createElement("canvas");
137 offscreen_canvas.width = onscreen_canvas.width;
138 offscreen_canvas.height = onscreen_canvas.height;
139
140 // Stop right-clicks on the puzzle from popping up a context menu.
141 // We need those right-clicks!
142 onscreen_canvas.oncontextmenu = function(event) { return false; }
143
144 // Set up mouse handlers. We do a bit of tracking of the currently
145 // pressed mouse buttons, to avoid sending mousemoves with no
146 // button down (our puzzles don't want those events).
147 mousedown = Module.cwrap('mousedown', 'void',
148 ['number', 'number', 'number']);
149 buttons_down = 0;
150 onscreen_canvas.onmousedown = function(event) {
151 var xy = relative_mouse_coords(event, onscreen_canvas);
152 mousedown(xy.x, xy.y, event.button);
153 buttons_down |= 1 << event.button;
154 onscreen_canvas.setCapture(true);
155 };
156 mousemove = Module.cwrap('mousemove', 'void',
157 ['number', 'number', 'number']);
158 onscreen_canvas.onmousemove = function(event) {
159 if (buttons_down) {
160 var xy = relative_mouse_coords(event, onscreen_canvas);
161 mousemove(xy.x, xy.y, buttons_down);
162 }
163 };
164 mouseup = Module.cwrap('mouseup', 'void',
165 ['number', 'number', 'number']);
166 onscreen_canvas.onmouseup = function(event) {
167 if (buttons_down & (1 << event.button)) {
168 buttons_down ^= 1 << event.button;
169 var xy = relative_mouse_coords(event, onscreen_canvas);
170 mouseup(xy.x, xy.y, event.button);
171 }
172 };
173
174 // Set up keyboard handlers. We do all the actual keyboard
175 // handling in onkeydown; but we also call event.preventDefault()
176 // in both the keydown and keypress handlers. This means that
177 // while the canvas itself has focus, _all_ keypresses go only to
178 // the puzzle - so users of this puzzle collection in other media
179 // can indulge their instinct to press ^R for redo, for example,
180 // without accidentally reloading the page.
181 key = Module.cwrap('key', 'void', ['number', 'number', 'string',
182 'string', 'number', 'number']);
183 onscreen_canvas.onkeydown = function(event) {
184 key(event.keyCode, event.charCode, event.key, event.char,
185 event.shiftKey ? 1 : 0, event.ctrlKey ? 1 : 0);
186 event.preventDefault();
187 };
188 onscreen_canvas.onkeypress = function(event) {
189 event.preventDefault();
190 };
191
192 // command() is a C function called to pass back events which
193 // don't fall into other categories like mouse and key events.
194 // Mostly those are button presses, but there's also one for the
195 // game-type dropdown having been changed.
196 command = Module.cwrap('command', 'void', ['number']);
197
198 // Event handlers for buttons and things, which call command().
199 document.getElementById("specific").onclick = function(event) {
200 // Ensure we don't accidentally process these events when a
201 // dialog is actually active, e.g. because the button still
202 // has keyboard focus
203 if (dlg_dimmer === null)
204 command(0);
205 };
206 document.getElementById("random").onclick = function(event) {
207 if (dlg_dimmer === null)
208 command(1);
209 };
210 document.getElementById("new").onclick = function(event) {
211 if (dlg_dimmer === null)
212 command(5);
213 };
214 document.getElementById("restart").onclick = function(event) {
215 if (dlg_dimmer === null)
216 command(6);
217 };
218 undo_button = document.getElementById("undo");
219 undo_button.onclick = function(event) {
220 if (dlg_dimmer === null)
221 command(7);
222 };
223 redo_button = document.getElementById("redo");
224 redo_button.onclick = function(event) {
225 if (dlg_dimmer === null)
226 command(8);
227 };
228 document.getElementById("solve").onclick = function(event) {
229 if (dlg_dimmer === null)
230 command(9);
231 };
232
233 gametypelist = document.getElementById("gametype");
234 gametypesubmenus.push(gametypelist);
235
236 // In IE, the canvas doesn't automatically gain focus on a mouse
237 // click, so make sure it does
238 onscreen_canvas.addEventListener("mousedown", function(event) {
239 onscreen_canvas.focus();
240 });
241
242 // In our dialog boxes, Return and Escape should be like pressing
243 // OK and Cancel respectively
244 document.addEventListener("keydown", function(event) {
245
246 if (dlg_dimmer !== null && event.keyCode == 13) {
247 for (var i in dlg_return_funcs)
248 dlg_return_funcs[i]();
249 command(3);
250 }
251
252 if (dlg_dimmer !== null && event.keyCode == 27)
253 command(4);
254 });
255
256 // Set up the function pointers we haven't already grabbed.
257 dlg_return_sval = Module.cwrap('dlg_return_sval', 'void',
258 ['number','string']);
259 dlg_return_ival = Module.cwrap('dlg_return_ival', 'void',
260 ['number','number']);
261 timer_callback = Module.cwrap('timer_callback', 'void', ['number']);
262
263 // Save references to the two permalinks.
264 permalink_desc = document.getElementById("permalink-desc");
265 permalink_seed = document.getElementById("permalink-seed");
266
267 // Default to giving keyboard focus to the puzzle.
268 onscreen_canvas.focus();
269
270 // Create the resize handle.
271 var resize_handle = document.createElement("canvas");
272 resize_handle.width = 10;
273 resize_handle.height = 10;
274 {
275 var ctx = resize_handle.getContext("2d");
276 ctx.beginPath();
277 for (var i = 1; i <= 7; i += 3) {
278 ctx.moveTo(8.5, i + 0.5);
279 ctx.lineTo(i + 0.5, 8.5);
280 }
281 ctx.lineWidth = '1px';
282 ctx.lineCap = 'round';
283 ctx.lineJoin = 'round';
284 ctx.strokeStyle = '#000000';
285 ctx.stroke();
286 }
287 resizable_div = document.getElementById("resizable");
288 resizable_div.appendChild(resize_handle);
289 resize_handle.style.position = 'absolute';
290 resize_handle.style.zIndex = 98;
291 resize_handle.style.bottom = "0";
292 resize_handle.style.right = "0";
293 resize_handle.style.cursor = "se-resize";
294 resize_handle.title = "Drag to resize the puzzle. Right-click to restore the default size.";
295 var resize_xbase = null, resize_ybase = null, restore_pending = false;
296 var resize_xoffset = null, resize_yoffset = null;
297 var resize_puzzle = Module.cwrap('resize_puzzle',
298 'void', ['number', 'number']);
299 var restore_puzzle_size = Module.cwrap('restore_puzzle_size', 'void', []);
300 resize_handle.oncontextmenu = function(event) { return false; }
301 resize_handle.onmousedown = function(event) {
302 if (event.button == 0) {
303 var xy = element_coords(onscreen_canvas);
304 resize_xbase = xy.x + onscreen_canvas.width / 2;
305 resize_ybase = xy.y;
306 resize_xoffset = xy.x + onscreen_canvas.width - event.pageX;
307 resize_yoffset = xy.y + onscreen_canvas.height - event.pageY;
308 } else {
309 restore_pending = true;
310 }
311 resize_handle.setCapture(true);
312 event.preventDefault();
313 };
314 window.addEventListener("mousemove", function(event) {
315 if (resize_xbase !== null && resize_ybase !== null) {
316 resize_puzzle((event.pageX + resize_xoffset - resize_xbase) * 2,
317 (event.pageY + resize_yoffset - resize_ybase));
318 event.preventDefault();
319 // Chrome insists on selecting text during a resize drag
320 // no matter what I do
321 if (window.getSelection)
322 window.getSelection().removeAllRanges();
323 else
324 document.selection.empty(); }
325 });
326 window.addEventListener("mouseup", function(event) {
327 if (resize_xbase !== null && resize_ybase !== null) {
328 resize_xbase = null;
329 resize_ybase = null;
330 onscreen_canvas.focus(); // return focus to the puzzle
331 event.preventDefault();
332 } else if (restore_pending) {
333 // If you have the puzzle at larger than normal size and
334 // then right-click to restore, I haven't found any way to
335 // stop Chrome and IE popping up a context menu on the
336 // revealed piece of document when you release the button
337 // except by putting the actual restore into a setTimeout.
338 // Gah.
339 setTimeout(function() {
340 restore_pending = false;
341 restore_puzzle_size();
342 onscreen_canvas.focus();
343 }, 20);
344 event.preventDefault();
345 }
346 });
347
348 // Run the C setup function, passing argv[1] as the fragment
349 // identifier (so that permalinks of the form puzzle.html#game-id
350 // can launch the specified id).
351 Module.callMain([location.hash]);
352
353 // And if we get here with everything having gone smoothly, i.e.
354 // we haven't crashed for one reason or another during setup, then
355 // it's probably safe to hide the 'sorry, no puzzle here' div and
356 // show the div containing the actual puzzle.
357 document.getElementById("apology").style.display = "none";
358 document.getElementById("puzzle").style.display = "inline";
359}
diff --git a/apps/plugins/puzzles/src/emccx.json b/apps/plugins/puzzles/src/emccx.json
new file mode 100644
index 0000000000..e03f7e25c7
--- /dev/null
+++ b/apps/plugins/puzzles/src/emccx.json
@@ -0,0 +1,29 @@
1// -*- js -*-
2//
3// List of entry points exported by the C side of the Emscripten
4// puzzle builds. Passed in to emcc via the option '-s
5// EXPORTED_FUNCTIONS=[list]'.
6//
7// This file isn't actually a valid list in its current state, since
8// emcc doesn't like comments or newlines. However, it's a nicer
9// source form to keep the comments and newlines in, so we sed them
10// away at compile time.
11[
12 // Event handlers for mouse and keyboard input
13 '_mouseup',
14 '_mousedown',
15 '_mousemove',
16 '_key',
17 // Callback when the program activates timing
18 '_timer_callback',
19 // Callback from button presses in the UI outside the canvas
20 '_command',
21 // Callbacks to return values from dialog boxes
22 '_dlg_return_sval',
23 '_dlg_return_ival',
24 // Callbacks when the resizing controls are used
25 '_resize_puzzle',
26 '_restore_puzzle_size',
27 // Main program, run at initialisation time
28 '_main'
29]
diff --git a/apps/plugins/puzzles/src/fifteen.R b/apps/plugins/puzzles/src/fifteen.R
new file mode 100644
index 0000000000..b2292acc69
--- /dev/null
+++ b/apps/plugins/puzzles/src/fifteen.R
@@ -0,0 +1,22 @@
1# -*- makefile -*-
2
3fifteen : [X] GTK COMMON fifteen fifteen-icon|no-icon
4
5fifteen : [G] WINDOWS COMMON fifteen fifteen.res|noicon.res
6
7fifteensolver : [U] fifteen[STANDALONE_SOLVER] STANDALONE
8fifteensolver : [C] fifteen[STANDALONE_SOLVER] STANDALONE
9
10ALL += fifteen[COMBINED]
11
12!begin am gtk
13GAMES += fifteen
14!end
15
16!begin >list.c
17 A(fifteen) \
18!end
19
20!begin >gamedesc.txt
21fifteen:fifteen.exe:Fifteen:Sliding block puzzle:Slide the tiles around to arrange them into order.
22!end
diff --git a/apps/plugins/puzzles/src/fifteen.c b/apps/plugins/puzzles/src/fifteen.c
new file mode 100644
index 0000000000..aee89071ca
--- /dev/null
+++ b/apps/plugins/puzzles/src/fifteen.c
@@ -0,0 +1,1215 @@
1/*
2 * fifteen.c: standard 15-puzzle.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13
14#define PREFERRED_TILE_SIZE 48
15#define TILE_SIZE (ds->tilesize)
16#define BORDER (TILE_SIZE / 2)
17#define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
18#define COORD(x) ( (x) * TILE_SIZE + BORDER )
19#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
20
21#define ANIM_TIME 0.13F
22#define FLASH_FRAME 0.13F
23
24#define X(state, i) ( (i) % (state)->w )
25#define Y(state, i) ( (i) / (state)->w )
26#define C(state, x, y) ( (y) * (state)->w + (x) )
27
28#define PARITY_P(params, gap) (((X((params), (gap)) - ((params)->w - 1)) ^ \
29 (Y((params), (gap)) - ((params)->h - 1)) ^ \
30 (((params)->w * (params)->h) + 1)) & 1)
31#define PARITY_S(state) PARITY_P((state), ((state)->gap_pos))
32
33enum {
34 COL_BACKGROUND,
35 COL_TEXT,
36 COL_HIGHLIGHT,
37 COL_LOWLIGHT,
38 NCOLOURS
39};
40
41struct game_params {
42 int w, h;
43};
44
45struct game_state {
46 int w, h, n;
47 int *tiles;
48 int gap_pos;
49 int completed;
50 int used_solve; /* used to suppress completion flash */
51 int movecount;
52};
53
54static game_params *default_params(void)
55{
56 game_params *ret = snew(game_params);
57
58 ret->w = ret->h = 4;
59
60 return ret;
61}
62
63static int game_fetch_preset(int i, char **name, game_params **params)
64{
65 if (i == 0) {
66 *params = default_params();
67 *name = dupstr("4x4");
68 return TRUE;
69 }
70 return FALSE;
71}
72
73static void free_params(game_params *params)
74{
75 sfree(params);
76}
77
78static game_params *dup_params(const game_params *params)
79{
80 game_params *ret = snew(game_params);
81 *ret = *params; /* structure copy */
82 return ret;
83}
84
85static void decode_params(game_params *ret, char const *string)
86{
87 ret->w = ret->h = atoi(string);
88 while (*string && isdigit((unsigned char)*string)) string++;
89 if (*string == 'x') {
90 string++;
91 ret->h = atoi(string);
92 }
93}
94
95static char *encode_params(const game_params *params, int full)
96{
97 char data[256];
98
99 sprintf(data, "%dx%d", params->w, params->h);
100
101 return dupstr(data);
102}
103
104static config_item *game_configure(const game_params *params)
105{
106 config_item *ret;
107 char buf[80];
108
109 ret = snewn(3, config_item);
110
111 ret[0].name = "Width";
112 ret[0].type = C_STRING;
113 sprintf(buf, "%d", params->w);
114 ret[0].sval = dupstr(buf);
115 ret[0].ival = 0;
116
117 ret[1].name = "Height";
118 ret[1].type = C_STRING;
119 sprintf(buf, "%d", params->h);
120 ret[1].sval = dupstr(buf);
121 ret[1].ival = 0;
122
123 ret[2].name = NULL;
124 ret[2].type = C_END;
125 ret[2].sval = NULL;
126 ret[2].ival = 0;
127
128 return ret;
129}
130
131static game_params *custom_params(const config_item *cfg)
132{
133 game_params *ret = snew(game_params);
134
135 ret->w = atoi(cfg[0].sval);
136 ret->h = atoi(cfg[1].sval);
137
138 return ret;
139}
140
141static char *validate_params(const game_params *params, int full)
142{
143 if (params->w < 2 || params->h < 2)
144 return "Width and height must both be at least two";
145
146 return NULL;
147}
148
149static int perm_parity(int *perm, int n)
150{
151 int i, j, ret;
152
153 ret = 0;
154
155 for (i = 0; i < n-1; i++)
156 for (j = i+1; j < n; j++)
157 if (perm[i] > perm[j])
158 ret = !ret;
159
160 return ret;
161}
162
163static char *new_game_desc(const game_params *params, random_state *rs,
164 char **aux, int interactive)
165{
166 int gap, n, i, x;
167 int x1, x2, p1, p2, parity;
168 int *tiles, *used;
169 char *ret;
170 int retlen;
171
172 n = params->w * params->h;
173
174 tiles = snewn(n, int);
175 used = snewn(n, int);
176
177 for (i = 0; i < n; i++) {
178 tiles[i] = -1;
179 used[i] = FALSE;
180 }
181
182 gap = random_upto(rs, n);
183 tiles[gap] = 0;
184 used[0] = TRUE;
185
186 /*
187 * Place everything else except the last two tiles.
188 */
189 for (x = 0, i = n-1; i > 2; i--) {
190 int k = random_upto(rs, i);
191 int j;
192
193 for (j = 0; j < n; j++)
194 if (!used[j] && (k-- == 0))
195 break;
196
197 assert(j < n && !used[j]);
198 used[j] = TRUE;
199
200 while (tiles[x] >= 0)
201 x++;
202 assert(x < n);
203 tiles[x] = j;
204 }
205
206 /*
207 * Find the last two locations, and the last two pieces.
208 */
209 while (tiles[x] >= 0)
210 x++;
211 assert(x < n);
212 x1 = x;
213 x++;
214 while (tiles[x] >= 0)
215 x++;
216 assert(x < n);
217 x2 = x;
218
219 for (i = 0; i < n; i++)
220 if (!used[i])
221 break;
222 p1 = i;
223 for (i = p1+1; i < n; i++)
224 if (!used[i])
225 break;
226 p2 = i;
227
228 /*
229 * Determine the required parity of the overall permutation.
230 * This is the XOR of:
231 *
232 * - The chessboard parity ((x^y)&1) of the gap square. The
233 * bottom right counts as even.
234 *
235 * - The parity of n. (The target permutation is 1,...,n-1,0
236 * rather than 0,...,n-1; this is a cyclic permutation of
237 * the starting point and hence is odd iff n is even.)
238 */
239 parity = PARITY_P(params, gap);
240
241 /*
242 * Try the last two tiles one way round. If that fails, swap
243 * them.
244 */
245 tiles[x1] = p1;
246 tiles[x2] = p2;
247 if (perm_parity(tiles, n) != parity) {
248 tiles[x1] = p2;
249 tiles[x2] = p1;
250 assert(perm_parity(tiles, n) == parity);
251 }
252
253 /*
254 * Now construct the game description, by describing the tile
255 * array as a simple sequence of comma-separated integers.
256 */
257 ret = NULL;
258 retlen = 0;
259 for (i = 0; i < n; i++) {
260 char buf[80];
261 int k;
262
263 k = sprintf(buf, "%d,", tiles[i]);
264
265 ret = sresize(ret, retlen + k + 1, char);
266 strcpy(ret + retlen, buf);
267 retlen += k;
268 }
269 ret[retlen-1] = '\0'; /* delete last comma */
270
271 sfree(tiles);
272 sfree(used);
273
274 return ret;
275}
276
277static char *validate_desc(const game_params *params, const char *desc)
278{
279 const char *p;
280 char *err;
281 int i, area;
282 int *used;
283
284 area = params->w * params->h;
285 p = desc;
286 err = NULL;
287
288 used = snewn(area, int);
289 for (i = 0; i < area; i++)
290 used[i] = FALSE;
291
292 for (i = 0; i < area; i++) {
293 const char *q = p;
294 int n;
295
296 if (*p < '0' || *p > '9') {
297 err = "Not enough numbers in string";
298 goto leave;
299 }
300 while (*p >= '0' && *p <= '9')
301 p++;
302 if (i < area-1 && *p != ',') {
303 err = "Expected comma after number";
304 goto leave;
305 }
306 else if (i == area-1 && *p) {
307 err = "Excess junk at end of string";
308 goto leave;
309 }
310 n = atoi(q);
311 if (n < 0 || n >= area) {
312 err = "Number out of range";
313 goto leave;
314 }
315 if (used[n]) {
316 err = "Number used twice";
317 goto leave;
318 }
319 used[n] = TRUE;
320
321 if (*p) p++; /* eat comma */
322 }
323
324 leave:
325 sfree(used);
326 return err;
327}
328
329static game_state *new_game(midend *me, const game_params *params,
330 const char *desc)
331{
332 game_state *state = snew(game_state);
333 int i;
334 const char *p;
335
336 state->w = params->w;
337 state->h = params->h;
338 state->n = params->w * params->h;
339 state->tiles = snewn(state->n, int);
340
341 state->gap_pos = 0;
342 p = desc;
343 i = 0;
344 for (i = 0; i < state->n; i++) {
345 assert(*p);
346 state->tiles[i] = atoi(p);
347 if (state->tiles[i] == 0)
348 state->gap_pos = i;
349 while (*p && *p != ',')
350 p++;
351 if (*p) p++; /* eat comma */
352 }
353 assert(!*p);
354 assert(state->tiles[state->gap_pos] == 0);
355
356 state->completed = state->movecount = 0;
357 state->used_solve = FALSE;
358
359 return state;
360}
361
362static game_state *dup_game(const game_state *state)
363{
364 game_state *ret = snew(game_state);
365
366 ret->w = state->w;
367 ret->h = state->h;
368 ret->n = state->n;
369 ret->tiles = snewn(state->w * state->h, int);
370 memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int));
371 ret->gap_pos = state->gap_pos;
372 ret->completed = state->completed;
373 ret->movecount = state->movecount;
374 ret->used_solve = state->used_solve;
375
376 return ret;
377}
378
379static void free_game(game_state *state)
380{
381 sfree(state->tiles);
382 sfree(state);
383}
384
385static char *solve_game(const game_state *state, const game_state *currstate,
386 const char *aux, char **error)
387{
388 return dupstr("S");
389}
390
391static int game_can_format_as_text_now(const game_params *params)
392{
393 return TRUE;
394}
395
396static char *game_text_format(const game_state *state)
397{
398 char *ret, *p, buf[80];
399 int x, y, col, maxlen;
400
401 /*
402 * First work out how many characters we need to display each
403 * number.
404 */
405 col = sprintf(buf, "%d", state->n-1);
406
407 /*
408 * Now we know the exact total size of the grid we're going to
409 * produce: it's got h rows, each containing w lots of col, w-1
410 * spaces and a trailing newline.
411 */
412 maxlen = state->h * state->w * (col+1);
413
414 ret = snewn(maxlen+1, char);
415 p = ret;
416
417 for (y = 0; y < state->h; y++) {
418 for (x = 0; x < state->w; x++) {
419 int v = state->tiles[state->w*y+x];
420 if (v == 0)
421 sprintf(buf, "%*s", col, "");
422 else
423 sprintf(buf, "%*d", col, v);
424 memcpy(p, buf, col);
425 p += col;
426 if (x+1 == state->w)
427 *p++ = '\n';
428 else
429 *p++ = ' ';
430 }
431 }
432
433 assert(p - ret == maxlen);
434 *p = '\0';
435 return ret;
436}
437
438static game_ui *new_ui(const game_state *state)
439{
440 return NULL;
441}
442
443static void free_ui(game_ui *ui)
444{
445}
446
447static char *encode_ui(const game_ui *ui)
448{
449 return NULL;
450}
451
452static void decode_ui(game_ui *ui, const char *encoding)
453{
454}
455
456static void game_changed_state(game_ui *ui, const game_state *oldstate,
457 const game_state *newstate)
458{
459}
460
461struct game_drawstate {
462 int started;
463 int w, h, bgcolour;
464 int *tiles;
465 int tilesize;
466};
467
468static int flip_cursor(int button)
469{
470 switch (button) {
471 case CURSOR_UP: return CURSOR_DOWN;
472 case CURSOR_DOWN: return CURSOR_UP;
473 case CURSOR_LEFT: return CURSOR_RIGHT;
474 case CURSOR_RIGHT: return CURSOR_LEFT;
475 }
476 return 0;
477}
478
479static void next_move_3x2(int ax, int ay, int bx, int by,
480 int gx, int gy, int *dx, int *dy)
481{
482 /* When w = 3 and h = 2 and the tile going in the top left corner
483 * is at (ax, ay) and the tile going in the bottom left corner is
484 * at (bx, by) and the blank tile is at (gx, gy), how do you move? */
485
486 /* Hard-coded shortest solutions. Sorry. */
487 static const unsigned char move[120] = {
488 1,2,0,1,2,2,
489 2,0,0,2,0,0,
490 0,0,2,0,2,0,
491 0,0,0,2,0,2,
492 2,0,0,0,2,0,
493
494 0,3,0,1,1,1,
495 3,0,3,2,1,2,
496 2,1,1,0,1,0,
497 2,1,2,1,0,1,
498 1,2,0,2,1,2,
499
500 0,1,3,1,3,0,
501 1,3,1,3,0,3,
502 0,0,3,3,0,0,
503 0,0,0,1,2,1,
504 3,0,0,1,1,1,
505
506 3,1,1,1,3,0,
507 1,1,1,1,1,1,
508 1,3,1,1,3,0,
509 1,1,3,3,1,3,
510 1,3,0,0,0,0
511 };
512 static const struct { int dx, dy; } d[4] = {{+1,0},{-1,0},{0,+1},{0,-1}};
513
514 int ea = 3*ay + ax, eb = 3*by + bx, eg = 3*gy + gx, v;
515 if (eb > ea) --eb;
516 if (eg > ea) --eg;
517 if (eg > eb) --eg;
518 v = move[ea + eb*6 + eg*5*6];
519 *dx = d[v].dx;
520 *dy = d[v].dy;
521}
522
523static void next_move(int nx, int ny, int ox, int oy, int gx, int gy,
524 int tx, int ty, int w, int *dx, int *dy)
525{
526 const int to_tile_x = (gx < nx ? +1 : -1);
527 const int to_goal_x = (gx < tx ? +1 : -1);
528 const int gap_x_on_goal_side = ((nx-tx) * (nx-gx) > 0);
529
530 assert (nx != tx || ny != ty); /* not already in place */
531 assert (nx != gx || ny != gy); /* not placing the gap */
532 assert (ty <= ny); /* because we're greedy (and flipping) */
533 assert (ty <= gy); /* because we're greedy (and flipping) */
534
535 /* TODO: define a termination function. Idea: 0 if solved, or
536 * the number of moves to solve the next piece plus the number of
537 * further unsolved pieces times an upper bound on the number of
538 * moves required to solve any piece. If such a function can be
539 * found, we have (termination && (termination => correctness)).
540 * The catch is our temporary disturbance of 2x3 corners. */
541
542 /* handles end-of-row, when 3 and 4 are in the top right 2x3 box */
543 if (tx == w - 2 &&
544 ny <= ty + 2 && (nx == tx || nx == tx + 1) &&
545 oy <= ty + 2 && (ox == tx || ox == tx + 1) &&
546 gy <= ty + 2 && (gx == tx || gx == tx + 1))
547 {
548 next_move_3x2(oy - ty, tx + 1 - ox,
549 ny - ty, tx + 1 - nx,
550 gy - ty, tx + 1 - gx, dy, dx);
551 *dx *= -1;
552 return;
553 }
554
555 if (tx == w - 1) {
556 if (ny <= ty + 2 && (nx == tx || nx == tx - 1) &&
557 gy <= ty + 2 && (gx == tx || gx == tx - 1)) {
558 next_move_3x2(ny - ty, tx - nx, 0, 1, gy - ty, tx - gx, dy, dx);
559 *dx *= -1;
560 } else if (gy == ty)
561 *dy = +1;
562 else if (nx != tx || ny != ty + 1) {
563 next_move((w - 1) - nx, ny, -1, -1, (w - 1) - gx, gy,
564 0, ty + 1, -1, dx, dy);
565 *dx *= -1;
566 } else if (gx == nx)
567 *dy = -1;
568 else
569 *dx = +1;
570 return;
571 }
572
573 /* note that *dy = -1 is unsafe when gy = ty + 1 and gx < tx */
574 if (gy < ny)
575 if (nx == gx || (gy == ty && gx == tx))
576 *dy = +1;
577 else if (!gap_x_on_goal_side)
578 *dx = to_tile_x;
579 else if (ny - ty > abs(nx - tx))
580 *dx = to_tile_x;
581 else *dy = +1;
582
583 else if (gy == ny)
584 if (nx == tx) /* then we know ny > ty */
585 if (gx > nx || ny > ty + 1)
586 *dy = -1; /* ... so this is safe */
587 else
588 *dy = +1;
589 else if (gap_x_on_goal_side)
590 *dx = to_tile_x;
591 else if (gy == ty || (gy == ty + 1 && gx < tx))
592 *dy = +1;
593 else
594 *dy = -1;
595
596 else if (nx == tx) /* gy > ny */
597 if (gx > nx)
598 *dy = -1;
599 else
600 *dx = +1;
601 else if (gx == nx)
602 *dx = to_goal_x;
603 else if (gap_x_on_goal_side)
604 if (gy == ty + 1 && gx < tx)
605 *dx = to_tile_x;
606 else
607 *dy = -1;
608
609 else if (ny - ty > abs(nx - tx))
610 *dy = -1;
611 else
612 *dx = to_tile_x;
613}
614
615static int compute_hint(const game_state *state, int *out_x, int *out_y)
616{
617 /* The overall solving process is this:
618 * 1. Find the next piece to be put in its place
619 * 2. Move it diagonally towards its place
620 * 3. Move it horizontally or vertically towards its place
621 * (Modulo the last two tiles at the end of each row/column)
622 */
623
624 int gx = X(state, state->gap_pos);
625 int gy = Y(state, state->gap_pos);
626
627 int tx, ty, nx, ny, ox, oy, /* {target,next,next2}_{x,y} */ i;
628 int dx = 0, dy = 0;
629
630 /* 1. Find the next piece
631 * if (there are no more unfinished columns than rows) {
632 * fill the top-most row, left to right
633 * } else { fill the left-most column, top to bottom }
634 */
635 const int w = state->w, h = state->h, n = w*h;
636 int next_piece = 0, next_piece_2 = 0, solr = 0, solc = 0;
637 int unsolved_rows = h, unsolved_cols = w;
638
639 assert(out_x);
640 assert(out_y);
641
642 while (solr < h && solc < w) {
643 int start, step, stop;
644 if (unsolved_cols <= unsolved_rows)
645 start = solr*w + solc, step = 1, stop = unsolved_cols;
646 else
647 start = solr*w + solc, step = w, stop = unsolved_rows;
648 for (i = 0; i < stop; ++i) {
649 const int j = start + i*step;
650 if (state->tiles[j] != j + 1) {
651 next_piece = j + 1;
652 next_piece_2 = next_piece + step;
653 break;
654 }
655 }
656 if (i < stop) break;
657
658 (unsolved_cols <= unsolved_rows)
659 ? (++solr, --unsolved_rows)
660 : (++solc, --unsolved_cols);
661 }
662
663 if (next_piece == n)
664 return FALSE;
665
666 /* 2, 3. Move the next piece towards its place */
667
668 /* gx, gy already set */
669 tx = X(state, next_piece - 1); /* where we're going */
670 ty = Y(state, next_piece - 1);
671 for (i = 0; i < n && state->tiles[i] != next_piece; ++i);
672 nx = X(state, i); /* where we're at */
673 ny = Y(state, i);
674 for (i = 0; i < n && state->tiles[i] != next_piece_2; ++i);
675 ox = X(state, i);
676 oy = Y(state, i);
677
678 if (unsolved_cols <= unsolved_rows)
679 next_move(nx, ny, ox, oy, gx, gy, tx, ty, w, &dx, &dy);
680 else
681 next_move(ny, nx, oy, ox, gy, gx, ty, tx, h, &dy, &dx);
682
683 assert (dx || dy);
684
685 *out_x = gx + dx;
686 *out_y = gy + dy;
687 return TRUE;
688}
689
690static char *interpret_move(const game_state *state, game_ui *ui,
691 const game_drawstate *ds,
692 int x, int y, int button)
693{
694 int cx = X(state, state->gap_pos), nx = cx;
695 int cy = Y(state, state->gap_pos), ny = cy;
696 char buf[80];
697
698 button &= ~MOD_MASK;
699
700 if (button == LEFT_BUTTON) {
701 nx = FROMCOORD(x);
702 ny = FROMCOORD(y);
703 if (nx < 0 || nx >= state->w || ny < 0 || ny >= state->h)
704 return NULL; /* out of bounds */
705 } else if (IS_CURSOR_MOVE(button)) {
706 static int invert_cursor = -1;
707 if (invert_cursor == -1) {
708 char *env = getenv("FIFTEEN_INVERT_CURSOR");
709 invert_cursor = (env && (env[0] == 'y' || env[0] == 'Y'));
710 }
711 button = flip_cursor(button); /* the default */
712 if (invert_cursor)
713 button = flip_cursor(button); /* undoes the first flip */
714 move_cursor(button, &nx, &ny, state->w, state->h, FALSE);
715 } else if ((button == 'h' || button == 'H') && !state->completed) {
716 if (!compute_hint(state, &nx, &ny))
717 return NULL; /* shouldn't happen, since ^^we^^checked^^ */
718 } else
719 return NULL; /* no move */
720
721 /*
722 * Any click location should be equal to the gap location
723 * in _precisely_ one coordinate.
724 */
725 if ((cx == nx) ^ (cy == ny)) {
726 sprintf(buf, "M%d,%d", nx, ny);
727 return dupstr(buf);
728 }
729
730 return NULL;
731}
732
733static game_state *execute_move(const game_state *from, const char *move)
734{
735 int gx, gy, dx, dy, ux, uy, up, p;
736 game_state *ret;
737
738 if (!strcmp(move, "S")) {
739 int i;
740
741 ret = dup_game(from);
742
743 /*
744 * Simply replace the grid with a solved one. For this game,
745 * this isn't a useful operation for actually telling the user
746 * what they should have done, but it is useful for
747 * conveniently being able to get hold of a clean state from
748 * which to practise manoeuvres.
749 */
750 for (i = 0; i < ret->n; i++)
751 ret->tiles[i] = (i+1) % ret->n;
752 ret->gap_pos = ret->n-1;
753 ret->used_solve = TRUE;
754 ret->completed = ret->movecount = 1;
755
756 return ret;
757 }
758
759 gx = X(from, from->gap_pos);
760 gy = Y(from, from->gap_pos);
761
762 if (move[0] != 'M' ||
763 sscanf(move+1, "%d,%d", &dx, &dy) != 2 ||
764 (dx == gx && dy == gy) || (dx != gx && dy != gy) ||
765 dx < 0 || dx >= from->w || dy < 0 || dy >= from->h)
766 return NULL;
767
768 /*
769 * Find the unit displacement from the original gap
770 * position towards this one.
771 */
772 ux = (dx < gx ? -1 : dx > gx ? +1 : 0);
773 uy = (dy < gy ? -1 : dy > gy ? +1 : 0);
774 up = C(from, ux, uy);
775
776 ret = dup_game(from);
777
778 ret->gap_pos = C(from, dx, dy);
779 assert(ret->gap_pos >= 0 && ret->gap_pos < ret->n);
780
781 ret->tiles[ret->gap_pos] = 0;
782
783 for (p = from->gap_pos; p != ret->gap_pos; p += up) {
784 assert(p >= 0 && p < from->n);
785 ret->tiles[p] = from->tiles[p + up];
786 ret->movecount++;
787 }
788
789 /*
790 * See if the game has been completed.
791 */
792 if (!ret->completed) {
793 ret->completed = ret->movecount;
794 for (p = 0; p < ret->n; p++)
795 if (ret->tiles[p] != (p < ret->n-1 ? p+1 : 0))
796 ret->completed = 0;
797 }
798
799 return ret;
800}
801
802/* ----------------------------------------------------------------------
803 * Drawing routines.
804 */
805
806static void game_compute_size(const game_params *params, int tilesize,
807 int *x, int *y)
808{
809 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
810 struct { int tilesize; } ads, *ds = &ads;
811 ads.tilesize = tilesize;
812
813 *x = TILE_SIZE * params->w + 2 * BORDER;
814 *y = TILE_SIZE * params->h + 2 * BORDER;
815}
816
817static void game_set_size(drawing *dr, game_drawstate *ds,
818 const game_params *params, int tilesize)
819{
820 ds->tilesize = tilesize;
821}
822
823static float *game_colours(frontend *fe, int *ncolours)
824{
825 float *ret = snewn(3 * NCOLOURS, float);
826 int i;
827
828 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
829
830 for (i = 0; i < 3; i++)
831 ret[COL_TEXT * 3 + i] = 0.0;
832
833 *ncolours = NCOLOURS;
834 return ret;
835}
836
837static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
838{
839 struct game_drawstate *ds = snew(struct game_drawstate);
840 int i;
841
842 ds->started = FALSE;
843 ds->w = state->w;
844 ds->h = state->h;
845 ds->bgcolour = COL_BACKGROUND;
846 ds->tiles = snewn(ds->w*ds->h, int);
847 ds->tilesize = 0; /* haven't decided yet */
848 for (i = 0; i < ds->w*ds->h; i++)
849 ds->tiles[i] = -1;
850
851 return ds;
852}
853
854static void game_free_drawstate(drawing *dr, game_drawstate *ds)
855{
856 sfree(ds->tiles);
857 sfree(ds);
858}
859
860static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
861 int x, int y, int tile, int flash_colour)
862{
863 if (tile == 0) {
864 draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
865 flash_colour);
866 } else {
867 int coords[6];
868 char str[40];
869
870 coords[0] = x + TILE_SIZE - 1;
871 coords[1] = y + TILE_SIZE - 1;
872 coords[2] = x + TILE_SIZE - 1;
873 coords[3] = y;
874 coords[4] = x;
875 coords[5] = y + TILE_SIZE - 1;
876 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
877
878 coords[0] = x;
879 coords[1] = y;
880 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
881
882 draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
883 TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
884 flash_colour);
885
886 sprintf(str, "%d", tile);
887 draw_text(dr, x + TILE_SIZE/2, y + TILE_SIZE/2,
888 FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE,
889 COL_TEXT, str);
890 }
891 draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
892}
893
894static void game_redraw(drawing *dr, game_drawstate *ds,
895 const game_state *oldstate, const game_state *state,
896 int dir, const game_ui *ui,
897 float animtime, float flashtime)
898{
899 int i, pass, bgcolour;
900
901 if (flashtime > 0) {
902 int frame = (int)(flashtime / FLASH_FRAME);
903 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
904 } else
905 bgcolour = COL_BACKGROUND;
906
907 if (!ds->started) {
908 int coords[10];
909
910 draw_rect(dr, 0, 0,
911 TILE_SIZE * state->w + 2 * BORDER,
912 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
913 draw_update(dr, 0, 0,
914 TILE_SIZE * state->w + 2 * BORDER,
915 TILE_SIZE * state->h + 2 * BORDER);
916
917 /*
918 * Recessed area containing the whole puzzle.
919 */
920 coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
921 coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
922 coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
923 coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
924 coords[4] = coords[2] - TILE_SIZE;
925 coords[5] = coords[3] + TILE_SIZE;
926 coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
927 coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
928 coords[6] = coords[8] + TILE_SIZE;
929 coords[7] = coords[9] - TILE_SIZE;
930 draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
931
932 coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
933 coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
934 draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
935
936 ds->started = TRUE;
937 }
938
939 /*
940 * Now draw each tile. We do this in two passes to make
941 * animation easy.
942 */
943 for (pass = 0; pass < 2; pass++) {
944 for (i = 0; i < state->n; i++) {
945 int t, t0;
946 /*
947 * Figure out what should be displayed at this
948 * location. It's either a simple tile, or it's a
949 * transition between two tiles (in which case we say
950 * -1 because it must always be drawn).
951 */
952
953 if (oldstate && oldstate->tiles[i] != state->tiles[i])
954 t = -1;
955 else
956 t = state->tiles[i];
957
958 t0 = t;
959
960 if (ds->bgcolour != bgcolour || /* always redraw when flashing */
961 ds->tiles[i] != t || ds->tiles[i] == -1 || t == -1) {
962 int x, y;
963
964 /*
965 * Figure out what to _actually_ draw, and where to
966 * draw it.
967 */
968 if (t == -1) {
969 int x0, y0, x1, y1;
970 int j;
971
972 /*
973 * On the first pass, just blank the tile.
974 */
975 if (pass == 0) {
976 x = COORD(X(state, i));
977 y = COORD(Y(state, i));
978 t = 0;
979 } else {
980 float c;
981
982 t = state->tiles[i];
983
984 /*
985 * Don't bother moving the gap; just don't
986 * draw it.
987 */
988 if (t == 0)
989 continue;
990
991 /*
992 * Find the coordinates of this tile in the old and
993 * new states.
994 */
995 x1 = COORD(X(state, i));
996 y1 = COORD(Y(state, i));
997 for (j = 0; j < oldstate->n; j++)
998 if (oldstate->tiles[j] == state->tiles[i])
999 break;
1000 assert(j < oldstate->n);
1001 x0 = COORD(X(state, j));
1002 y0 = COORD(Y(state, j));
1003
1004 c = (animtime / ANIM_TIME);
1005 if (c < 0.0F) c = 0.0F;
1006 if (c > 1.0F) c = 1.0F;
1007
1008 x = x0 + (int)(c * (x1 - x0));
1009 y = y0 + (int)(c * (y1 - y0));
1010 }
1011
1012 } else {
1013 if (pass == 0)
1014 continue;
1015 x = COORD(X(state, i));
1016 y = COORD(Y(state, i));
1017 }
1018
1019 draw_tile(dr, ds, state, x, y, t, bgcolour);
1020 }
1021 ds->tiles[i] = t0;
1022 }
1023 }
1024 ds->bgcolour = bgcolour;
1025
1026 /*
1027 * Update the status bar.
1028 */
1029 {
1030 char statusbuf[256];
1031
1032 /*
1033 * Don't show the new status until we're also showing the
1034 * new _state_ - after the game animation is complete.
1035 */
1036 if (oldstate)
1037 state = oldstate;
1038
1039 if (state->used_solve)
1040 sprintf(statusbuf, "Moves since auto-solve: %d",
1041 state->movecount - state->completed);
1042 else
1043 sprintf(statusbuf, "%sMoves: %d",
1044 (state->completed ? "COMPLETED! " : ""),
1045 (state->completed ? state->completed : state->movecount));
1046
1047 status_bar(dr, statusbuf);
1048 }
1049}
1050
1051static float game_anim_length(const game_state *oldstate,
1052 const game_state *newstate, int dir, game_ui *ui)
1053{
1054 return ANIM_TIME;
1055}
1056
1057static float game_flash_length(const game_state *oldstate,
1058 const game_state *newstate, int dir, game_ui *ui)
1059{
1060 if (!oldstate->completed && newstate->completed &&
1061 !oldstate->used_solve && !newstate->used_solve)
1062 return 2 * FLASH_FRAME;
1063 else
1064 return 0.0F;
1065}
1066
1067static int game_status(const game_state *state)
1068{
1069 return state->completed ? +1 : 0;
1070}
1071
1072static int game_timing_state(const game_state *state, game_ui *ui)
1073{
1074 return TRUE;
1075}
1076
1077static void game_print_size(const game_params *params, float *x, float *y)
1078{
1079}
1080
1081static void game_print(drawing *dr, const game_state *state, int tilesize)
1082{
1083}
1084
1085#ifdef COMBINED
1086#define thegame fifteen
1087#endif
1088
1089const struct game thegame = {
1090 "Fifteen", "games.fifteen", "fifteen",
1091 default_params,
1092 game_fetch_preset, NULL,
1093 decode_params,
1094 encode_params,
1095 free_params,
1096 dup_params,
1097 TRUE, game_configure, custom_params,
1098 validate_params,
1099 new_game_desc,
1100 validate_desc,
1101 new_game,
1102 dup_game,
1103 free_game,
1104 TRUE, solve_game,
1105 TRUE, game_can_format_as_text_now, game_text_format,
1106 new_ui,
1107 free_ui,
1108 encode_ui,
1109 decode_ui,
1110 game_changed_state,
1111 interpret_move,
1112 execute_move,
1113 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1114 game_colours,
1115 game_new_drawstate,
1116 game_free_drawstate,
1117 game_redraw,
1118 game_anim_length,
1119 game_flash_length,
1120 game_status,
1121 FALSE, FALSE, game_print_size, game_print,
1122 TRUE, /* wants_statusbar */
1123 FALSE, game_timing_state,
1124 0, /* flags */
1125};
1126
1127#ifdef STANDALONE_SOLVER
1128
1129int main(int argc, char **argv)
1130{
1131 game_params *params;
1132 game_state *state;
1133 char *id = NULL, *desc, *err;
1134 int grade = FALSE;
1135 char *progname = argv[0];
1136
1137 char buf[80];
1138 int limit, x, y, solvable;
1139
1140 while (--argc > 0) {
1141 char *p = *++argv;
1142 if (!strcmp(p, "-v")) {
1143 /* solver_show_working = TRUE; */
1144 } else if (!strcmp(p, "-g")) {
1145 grade = TRUE;
1146 } else if (*p == '-') {
1147 fprintf(stderr, "%s: unrecognised option `%s'\n", progname, p);
1148 return 1;
1149 } else {
1150 id = p;
1151 }
1152 }
1153
1154 if (!id) {
1155 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
1156 return 1;
1157 }
1158
1159 desc = strchr(id, ':');
1160 if (!desc) {
1161 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
1162 return 1;
1163 }
1164 *desc++ = '\0';
1165
1166 params = default_params();
1167 decode_params(params, id);
1168 err = validate_desc(params, desc);
1169 if (err) {
1170 free_params(params);
1171 fprintf(stderr, "%s: %s\n", argv[0], err);
1172 return 1;
1173 }
1174
1175 state = new_game(NULL, params, desc);
1176 free_params(params);
1177
1178 solvable = (PARITY_S(state) == perm_parity(state->tiles, state->n));
1179 if (grade || !solvable) {
1180 free_game(state);
1181 fputs(solvable ? "Game is solvable" : "Game is unsolvable",
1182 grade ? stdout : stderr);
1183 return !grade;
1184 }
1185
1186 for (limit = 5 * state->n * state->n * state->n; limit; --limit) {
1187 game_state *next_state;
1188 if (!compute_hint(state, &x, &y)) {
1189 fprintf(stderr, "couldn't compute next move while solving %s:%s",
1190 id, desc);
1191 return 1;
1192 }
1193 printf("Move the space to (%d, %d), moving %d into the space\n",
1194 x + 1, y + 1, state->tiles[C(state, x, y)]);
1195 sprintf(buf, "M%d,%d", x, y);
1196 next_state = execute_move(state, buf);
1197
1198 free_game(state);
1199 if (!next_state) {
1200 fprintf(stderr, "invalid move when solving %s:%s\n", id, desc);
1201 return 1;
1202 }
1203 state = next_state;
1204 if (next_state->completed) {
1205 free_game(state);
1206 return 0;
1207 }
1208 }
1209
1210 free_game(state);
1211 fprintf(stderr, "ran out of moves for %s:%s\n", id, desc);
1212 return 1;
1213}
1214
1215#endif
diff --git a/apps/plugins/puzzles/src/fifteen.html b/apps/plugins/puzzles/src/fifteen.html
new file mode 100644
index 0000000000..05d3661dad
--- /dev/null
+++ b/apps/plugins/puzzles/src/fifteen.html
@@ -0,0 +1,41 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Fifteen</title>
7<link rel="previous" href="cube.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="sixteen.html">
12</head>
13<body>
14<p><a href="cube.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="sixteen.html">Next</a></p>
15<h1><a name="C5"></a>Chapter 5: <a name="i0"></a>Fifteen</h1>
16<p>
17The old ones are the best: this is the good old &#8216;<a name="i1"></a>15-puzzle&#8217; with sliding tiles. You have a 4&#215;4 square grid; 15 squares contain numbered tiles, and the sixteenth is empty. Your move is to choose a tile next to the empty space, and slide it into the space. The aim is to end up with the tiles in numerical order, with the space in the bottom right (so that the top row reads 1,2,3,4 and the bottom row reads 13,14,15,<em>space</em>).
18</p>
19<h2><a name="S5.1"></a>5.1 <a name="i2"></a>Fifteen controls</h2>
20<p>
21This game can be controlled with the mouse or the keyboard.
22</p>
23<p>
24A left-click with the mouse in the row or column containing the empty space will move as many tiles as necessary to move the space to the mouse pointer.
25</p>
26<p>
27The arrow keys will move a tile adjacent to the space in the direction indicated (moving the space in the <em>opposite</em> direction).
28</p>
29<p>
30Pressing &#8216;h&#8217; will make a suggested move. Pressing &#8216;h&#8217; enough times will solve the game, but it may scramble your progress while doing so.
31</p>
32<p>
33(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
34</p>
35<h2><a name="S5.2"></a>5.2 <a name="i3"></a>Fifteen parameters</h2>
36<p>
37The only options available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu are <em>Width</em> and <em>Height</em>, which are self-explanatory. (Once you've changed these, it's not a &#8216;15-puzzle&#8217; any more, of course!)
38</p>
39
40<hr><address></address></body>
41</html>
diff --git a/apps/plugins/puzzles/src/filling.R b/apps/plugins/puzzles/src/filling.R
new file mode 100644
index 0000000000..cffbafaa0a
--- /dev/null
+++ b/apps/plugins/puzzles/src/filling.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3FILLING_EXTRA = dsf
4
5fillingsolver : [U] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE
6fillingsolver : [C] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE
7
8filling : [X] GTK COMMON filling FILLING_EXTRA filling-icon|no-icon
9
10filling : [G] WINDOWS COMMON filling FILLING_EXTRA filling.res|noicon.res
11
12ALL += filling[COMBINED] FILLING_EXTRA
13
14!begin am gtk
15GAMES += filling
16!end
17
18!begin >list.c
19 A(filling) \
20!end
21
22!begin >gamedesc.txt
23filling:filling.exe:Filling:Polyomino puzzle:Mark every square with the area of its containing region.
24!end
diff --git a/apps/plugins/puzzles/src/filling.c b/apps/plugins/puzzles/src/filling.c
new file mode 100644
index 0000000000..d8d0c8cbb0
--- /dev/null
+++ b/apps/plugins/puzzles/src/filling.c
@@ -0,0 +1,2179 @@
1/* -*- tab-width: 8; indent-tabs-mode: t -*-
2 * filling.c: An implementation of the Nikoli game fillomino.
3 * Copyright (C) 2007 Jonas Kölker. See LICENSE for the license.
4 */
5
6/* TODO:
7 *
8 * - use a typedef instead of int for numbers on the board
9 * + replace int with something else (signed short?)
10 * - the type should be signed (for -board[i] and -SENTINEL)
11 * - the type should be somewhat big: board[i] = i
12 * - Using shorts gives us 181x181 puzzles as upper bound.
13 *
14 * - in board generation, after having merged regions such that no
15 * more merges are necessary, try splitting (big) regions.
16 * + it seems that smaller regions make for better puzzles; see
17 * for instance the 7x7 puzzle in this file (grep for 7x7:).
18 *
19 * - symmetric hints (solo-style)
20 * + right now that means including _many_ hints, and the puzzles
21 * won't look any nicer. Not worth it (at the moment).
22 *
23 * - make the solver do recursion/backtracking.
24 * + This is for user-submitted puzzles, not for puzzle
25 * generation (on the other hand, never say never).
26 *
27 * - prove that only w=h=2 needs a special case
28 *
29 * - solo-like pencil marks?
30 *
31 * - a user says that the difficulty is unevenly distributed.
32 * + partition into levels? Will they be non-crap?
33 *
34 * - Allow square contents > 9?
35 * + I could use letters for digits (solo does this), but
36 * letters don't have numeric significance (normal people hate
37 * base36), which is relevant here (much more than in solo).
38 * + [click, 1, 0, enter] => [10 in clicked square]?
39 * + How much information is needed to solve? Does one need to
40 * know the algorithm by which the largest number is set?
41 *
42 * - eliminate puzzle instances with done chunks (1's in particular)?
43 * + that's what the qsort call is all about.
44 * + the 1's don't bother me that much.
45 * + but this takes a LONG time (not always possible)?
46 * - this may be affected by solver (lack of) quality.
47 * - weed them out by construction instead of post-cons check
48 * + but that interleaves make_board and new_game_desc: you
49 * have to alternate between changing the board and
50 * changing the hint set (instead of just creating the
51 * board once, then changing the hint set once -> done).
52 *
53 * - use binary search when discovering the minimal sovable point
54 * + profile to show a need (but when the solver gets slower...)
55 * + 7x9 @ .011s, 9x13 @ .075s, 17x13 @ .661s (all avg with n=100)
56 * + but the hints are independent, not linear, so... what?
57 */
58
59#include <assert.h>
60#include <ctype.h>
61#include <math.h>
62#include <stdarg.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66
67#include "puzzles.h"
68
69static unsigned char verbose;
70
71static void printv(char *fmt, ...) {
72#ifndef PALM
73 if (verbose) {
74 va_list va;
75 va_start(va, fmt);
76 vprintf(fmt, va);
77 va_end(va);
78 }
79#endif
80}
81
82/*****************************************************************************
83 * GAME CONFIGURATION AND PARAMETERS *
84 *****************************************************************************/
85
86struct game_params {
87 int w, h;
88};
89
90struct shared_state {
91 struct game_params params;
92 int *clues;
93 int refcnt;
94};
95
96struct game_state {
97 int *board;
98 struct shared_state *shared;
99 int completed, cheated;
100};
101
102static const struct game_params filling_defaults[3] = {
103 {9, 7}, {13, 9}, {17, 13}
104};
105
106static game_params *default_params(void)
107{
108 game_params *ret = snew(game_params);
109
110 *ret = filling_defaults[1]; /* struct copy */
111
112 return ret;
113}
114
115static int game_fetch_preset(int i, char **name, game_params **params)
116{
117 char buf[64];
118
119 if (i < 0 || i >= lenof(filling_defaults)) return FALSE;
120 *params = snew(game_params);
121 **params = filling_defaults[i]; /* struct copy */
122 sprintf(buf, "%dx%d", filling_defaults[i].w, filling_defaults[i].h);
123 *name = dupstr(buf);
124
125 return TRUE;
126}
127
128static void free_params(game_params *params)
129{
130 sfree(params);
131}
132
133static game_params *dup_params(const game_params *params)
134{
135 game_params *ret = snew(game_params);
136 *ret = *params; /* struct copy */
137 return ret;
138}
139
140static void decode_params(game_params *ret, char const *string)
141{
142 ret->w = ret->h = atoi(string);
143 while (*string && isdigit((unsigned char) *string)) ++string;
144 if (*string == 'x') ret->h = atoi(++string);
145}
146
147static char *encode_params(const game_params *params, int full)
148{
149 char buf[64];
150 sprintf(buf, "%dx%d", params->w, params->h);
151 return dupstr(buf);
152}
153
154static config_item *game_configure(const game_params *params)
155{
156 config_item *ret;
157 char buf[64];
158
159 ret = snewn(3, config_item);
160
161 ret[0].name = "Width";
162 ret[0].type = C_STRING;
163 sprintf(buf, "%d", params->w);
164 ret[0].sval = dupstr(buf);
165 ret[0].ival = 0;
166
167 ret[1].name = "Height";
168 ret[1].type = C_STRING;
169 sprintf(buf, "%d", params->h);
170 ret[1].sval = dupstr(buf);
171 ret[1].ival = 0;
172
173 ret[2].name = NULL;
174 ret[2].type = C_END;
175 ret[2].sval = NULL;
176 ret[2].ival = 0;
177
178 return ret;
179}
180
181static game_params *custom_params(const config_item *cfg)
182{
183 game_params *ret = snew(game_params);
184
185 ret->w = atoi(cfg[0].sval);
186 ret->h = atoi(cfg[1].sval);
187
188 return ret;
189}
190
191static char *validate_params(const game_params *params, int full)
192{
193 if (params->w < 1) return "Width must be at least one";
194 if (params->h < 1) return "Height must be at least one";
195
196 return NULL;
197}
198
199/*****************************************************************************
200 * STRINGIFICATION OF GAME STATE *
201 *****************************************************************************/
202
203#define EMPTY 0
204
205/* Example of plaintext rendering:
206 * +---+---+---+---+---+---+---+
207 * | 6 | | | 2 | | | 2 |
208 * +---+---+---+---+---+---+---+
209 * | | 3 | | 6 | | 3 | |
210 * +---+---+---+---+---+---+---+
211 * | 3 | | | | | | 1 |
212 * +---+---+---+---+---+---+---+
213 * | | 2 | 3 | | 4 | 2 | |
214 * +---+---+---+---+---+---+---+
215 * | 2 | | | | | | 3 |
216 * +---+---+---+---+---+---+---+
217 * | | 5 | | 1 | | 4 | |
218 * +---+---+---+---+---+---+---+
219 * | 4 | | | 3 | | | 3 |
220 * +---+---+---+---+---+---+---+
221 *
222 * This puzzle instance is taken from the nikoli website
223 * Encoded (unsolved and solved), the strings are these:
224 * 7x7:6002002030603030000010230420200000305010404003003
225 * 7x7:6662232336663232331311235422255544325413434443313
226 */
227static char *board_to_string(int *board, int w, int h) {
228 const int sz = w * h;
229 const int chw = (4*w + 2); /* +2 for trailing '+' and '\n' */
230 const int chh = (2*h + 1); /* +1: n fence segments, n+1 posts */
231 const int chlen = chw * chh;
232 char *repr = snewn(chlen + 1, char);
233 int i;
234
235 assert(board);
236
237 /* build the first line ("^(\+---){n}\+$") */
238 for (i = 0; i < w; ++i) {
239 repr[4*i + 0] = '+';
240 repr[4*i + 1] = '-';
241 repr[4*i + 2] = '-';
242 repr[4*i + 3] = '-';
243 }
244 repr[4*i + 0] = '+';
245 repr[4*i + 1] = '\n';
246
247 /* ... and copy it onto the odd-numbered lines */
248 for (i = 0; i < h; ++i) memcpy(repr + (2*i + 2) * chw, repr, chw);
249
250 /* build the second line ("^(\|\t){n}\|$") */
251 for (i = 0; i < w; ++i) {
252 repr[chw + 4*i + 0] = '|';
253 repr[chw + 4*i + 1] = ' ';
254 repr[chw + 4*i + 2] = ' ';
255 repr[chw + 4*i + 3] = ' ';
256 }
257 repr[chw + 4*i + 0] = '|';
258 repr[chw + 4*i + 1] = '\n';
259
260 /* ... and copy it onto the even-numbered lines */
261 for (i = 1; i < h; ++i) memcpy(repr + (2*i + 1) * chw, repr + chw, chw);
262
263 /* fill in the numbers */
264 for (i = 0; i < sz; ++i) {
265 const int x = i % w;
266 const int y = i / w;
267 if (board[i] == EMPTY) continue;
268 repr[chw*(2*y + 1) + (4*x + 2)] = board[i] + '0';
269 }
270
271 repr[chlen] = '\0';
272 return repr;
273}
274
275static int game_can_format_as_text_now(const game_params *params)
276{
277 return TRUE;
278}
279
280static char *game_text_format(const game_state *state)
281{
282 const int w = state->shared->params.w;
283 const int h = state->shared->params.h;
284 return board_to_string(state->board, w, h);
285}
286
287/*****************************************************************************
288 * GAME GENERATION AND SOLVER *
289 *****************************************************************************/
290
291static const int dx[4] = {-1, 1, 0, 0};
292static const int dy[4] = {0, 0, -1, 1};
293
294struct solver_state
295{
296 int *dsf;
297 int *board;
298 int *connected;
299 int nempty;
300
301 /* Used internally by learn_bitmap_deductions; kept here to avoid
302 * mallocing/freeing them every time that function is called. */
303 int *bm, *bmdsf, *bmminsize;
304};
305
306static void print_board(int *board, int w, int h) {
307 if (verbose) {
308 char *repr = board_to_string(board, w, h);
309 printv("%s\n", repr);
310 free(repr);
311 }
312}
313
314static game_state *new_game(midend *, const game_params *, const char *);
315static void free_game(game_state *);
316
317#define SENTINEL sz
318
319static int mark_region(int *board, int w, int h, int i, int n, int m) {
320 int j;
321
322 board[i] = -1;
323
324 for (j = 0; j < 4; ++j) {
325 const int x = (i % w) + dx[j], y = (i / w) + dy[j], ii = w*y + x;
326 if (x < 0 || x >= w || y < 0 || y >= h) continue;
327 if (board[ii] == m) return FALSE;
328 if (board[ii] != n) continue;
329 if (!mark_region(board, w, h, ii, n, m)) return FALSE;
330 }
331 return TRUE;
332}
333
334static int region_size(int *board, int w, int h, int i) {
335 const int sz = w * h;
336 int j, size, copy;
337 if (board[i] == 0) return 0;
338 copy = board[i];
339 mark_region(board, w, h, i, board[i], SENTINEL);
340 for (size = j = 0; j < sz; ++j) {
341 if (board[j] != -1) continue;
342 ++size;
343 board[j] = copy;
344 }
345 return size;
346}
347
348static void merge_ones(int *board, int w, int h)
349{
350 const int sz = w * h;
351 const int maxsize = min(max(max(w, h), 3), 9);
352 int i, j, k, change;
353 do {
354 change = FALSE;
355 for (i = 0; i < sz; ++i) {
356 if (board[i] != 1) continue;
357
358 for (j = 0; j < 4; ++j, board[i] = 1) {
359 const int x = (i % w) + dx[j], y = (i / w) + dy[j];
360 int oldsize, newsize, ok, ii = w*y + x;
361 if (x < 0 || x >= w || y < 0 || y >= h) continue;
362 if (board[ii] == maxsize) continue;
363
364 oldsize = board[ii];
365 board[i] = oldsize;
366 newsize = region_size(board, w, h, i);
367
368 if (newsize > maxsize) continue;
369
370 ok = mark_region(board, w, h, i, oldsize, newsize);
371
372 for (k = 0; k < sz; ++k)
373 if (board[k] == -1)
374 board[k] = ok ? newsize : oldsize;
375
376 if (ok) break;
377 }
378 if (j < 4) change = TRUE;
379 }
380 } while (change);
381}
382
383/* generate a random valid board; uses validate_board. */
384static void make_board(int *board, int w, int h, random_state *rs) {
385 const int sz = w * h;
386
387 /* w=h=2 is a special case which requires a number > max(w, h) */
388 /* TODO prove that this is the case ONLY for w=h=2. */
389 const int maxsize = min(max(max(w, h), 3), 9);
390
391 /* Note that if 1 in {w, h} then it's impossible to have a region
392 * of size > w*h, so the special case only affects w=h=2. */
393
394 int i, change, *dsf;
395
396 assert(w >= 1);
397 assert(h >= 1);
398 assert(board);
399
400 /* I abuse the board variable: when generating the puzzle, it
401 * contains a shuffled list of numbers {0, ..., sz-1}. */
402 for (i = 0; i < sz; ++i) board[i] = i;
403
404 dsf = snewn(sz, int);
405retry:
406 dsf_init(dsf, sz);
407 shuffle(board, sz, sizeof (int), rs);
408
409 do {
410 change = FALSE; /* as long as the board potentially has errors */
411 for (i = 0; i < sz; ++i) {
412 const int square = dsf_canonify(dsf, board[i]);
413 const int size = dsf_size(dsf, square);
414 int merge = SENTINEL, min = maxsize - size + 1, error = FALSE;
415 int neighbour, neighbour_size, j;
416
417 for (j = 0; j < 4; ++j) {
418 const int x = (board[i] % w) + dx[j];
419 const int y = (board[i] / w) + dy[j];
420 if (x < 0 || x >= w || y < 0 || y >= h) continue;
421
422 neighbour = dsf_canonify(dsf, w*y + x);
423 if (square == neighbour) continue;
424
425 neighbour_size = dsf_size(dsf, neighbour);
426 if (size == neighbour_size) error = TRUE;
427
428 /* find the smallest neighbour to merge with, which
429 * wouldn't make the region too large. (This is
430 * guaranteed by the initial value of `min'.) */
431 if (neighbour_size < min) {
432 min = neighbour_size;
433 merge = neighbour;
434 }
435 }
436
437 /* if this square is not in error, leave it be */
438 if (!error) continue;
439
440 /* if it is, but we can't fix it, retry the whole board.
441 * Maybe we could fix it by merging the conflicting
442 * neighbouring region(s) into some of their neighbours,
443 * but just restarting works out fine. */
444 if (merge == SENTINEL) goto retry;
445
446 /* merge with the smallest neighbouring workable region. */
447 dsf_merge(dsf, square, merge);
448 change = TRUE;
449 }
450 } while (change);
451
452 for (i = 0; i < sz; ++i) board[i] = dsf_size(dsf, i);
453 merge_ones(board, w, h);
454
455 sfree(dsf);
456}
457
458static void merge(int *dsf, int *connected, int a, int b) {
459 int c;
460 assert(dsf);
461 assert(connected);
462 a = dsf_canonify(dsf, a);
463 b = dsf_canonify(dsf, b);
464 if (a == b) return;
465 dsf_merge(dsf, a, b);
466 c = connected[a];
467 connected[a] = connected[b];
468 connected[b] = c;
469}
470
471static void *memdup(const void *ptr, size_t len, size_t esz) {
472 void *dup = smalloc(len * esz);
473 assert(ptr);
474 memcpy(dup, ptr, len * esz);
475 return dup;
476}
477
478static void expand(struct solver_state *s, int w, int h, int t, int f) {
479 int j;
480 assert(s);
481 assert(s->board[t] == EMPTY); /* expand to empty square */
482 assert(s->board[f] != EMPTY); /* expand from non-empty square */
483 printv(
484 "learn: expanding %d from (%d, %d) into (%d, %d)\n",
485 s->board[f], f % w, f / w, t % w, t / w);
486 s->board[t] = s->board[f];
487 for (j = 0; j < 4; ++j) {
488 const int x = (t % w) + dx[j];
489 const int y = (t / w) + dy[j];
490 const int idx = w*y + x;
491 if (x < 0 || x >= w || y < 0 || y >= h) continue;
492 if (s->board[idx] != s->board[t]) continue;
493 merge(s->dsf, s->connected, t, idx);
494 }
495 --s->nempty;
496}
497
498static void clear_count(int *board, int sz) {
499 int i;
500 for (i = 0; i < sz; ++i) {
501 if (board[i] >= 0) continue;
502 else if (board[i] == -SENTINEL) board[i] = EMPTY;
503 else board[i] = -board[i];
504 }
505}
506
507static void flood_count(int *board, int w, int h, int i, int n, int *c) {
508 const int sz = w * h;
509 int k;
510
511 if (board[i] == EMPTY) board[i] = -SENTINEL;
512 else if (board[i] == n) board[i] = -board[i];
513 else return;
514
515 if (--*c == 0) return;
516
517 for (k = 0; k < 4; ++k) {
518 const int x = (i % w) + dx[k];
519 const int y = (i / w) + dy[k];
520 const int idx = w*y + x;
521 if (x < 0 || x >= w || y < 0 || y >= h) continue;
522 flood_count(board, w, h, idx, n, c);
523 if (*c == 0) return;
524 }
525}
526
527static int check_capacity(int *board, int w, int h, int i) {
528 int n = board[i];
529 flood_count(board, w, h, i, board[i], &n);
530 clear_count(board, w * h);
531 return n == 0;
532}
533
534static int expandsize(const int *board, int *dsf, int w, int h, int i, int n) {
535 int j;
536 int nhits = 0;
537 int hits[4];
538 int size = 1;
539 for (j = 0; j < 4; ++j) {
540 const int x = (i % w) + dx[j];
541 const int y = (i / w) + dy[j];
542 const int idx = w*y + x;
543 int root;
544 int m;
545 if (x < 0 || x >= w || y < 0 || y >= h) continue;
546 if (board[idx] != n) continue;
547 root = dsf_canonify(dsf, idx);
548 for (m = 0; m < nhits && root != hits[m]; ++m);
549 if (m < nhits) continue;
550 printv("\t (%d, %d) contrib %d to size\n", x, y, dsf[root] >> 2);
551 size += dsf_size(dsf, root);
552 assert(dsf_size(dsf, root) >= 1);
553 hits[nhits++] = root;
554 }
555 return size;
556}
557
558/*
559 * +---+---+---+---+---+---+---+
560 * | 6 | | | 2 | | | 2 |
561 * +---+---+---+---+---+---+---+
562 * | | 3 | | 6 | | 3 | |
563 * +---+---+---+---+---+---+---+
564 * | 3 | | | | | | 1 |
565 * +---+---+---+---+---+---+---+
566 * | | 2 | 3 | | 4 | 2 | |
567 * +---+---+---+---+---+---+---+
568 * | 2 | | | | | | 3 |
569 * +---+---+---+---+---+---+---+
570 * | | 5 | | 1 | | 4 | |
571 * +---+---+---+---+---+---+---+
572 * | 4 | | | 3 | | | 3 |
573 * +---+---+---+---+---+---+---+
574 */
575
576/* Solving techniques:
577 *
578 * CONNECTED COMPONENT FORCED EXPANSION (too big):
579 * When a CC can only be expanded in one direction, because all the
580 * other ones would make the CC too big.
581 * +---+---+---+---+---+
582 * | 2 | 2 | | 2 | _ |
583 * +---+---+---+---+---+
584 *
585 * CONNECTED COMPONENT FORCED EXPANSION (too small):
586 * When a CC must include a particular square, because otherwise there
587 * would not be enough room to complete it. This includes squares not
588 * adjacent to the CC through learn_critical_square.
589 * +---+---+
590 * | 2 | _ |
591 * +---+---+
592 *
593 * DROPPING IN A ONE:
594 * When an empty square has no neighbouring empty squares and only a 1
595 * will go into the square (or other CCs would be too big).
596 * +---+---+---+
597 * | 2 | 2 | _ |
598 * +---+---+---+
599 *
600 * TODO: generalise DROPPING IN A ONE: find the size of the CC of
601 * empty squares and a list of all adjacent numbers. See if only one
602 * number in {1, ..., size} u {all adjacent numbers} is possible.
603 * Probably this is only effective for a CC size < n for some n (4?)
604 *
605 * TODO: backtracking.
606 */
607
608static void filled_square(struct solver_state *s, int w, int h, int i) {
609 int j;
610 for (j = 0; j < 4; ++j) {
611 const int x = (i % w) + dx[j];
612 const int y = (i / w) + dy[j];
613 const int idx = w*y + x;
614 if (x < 0 || x >= w || y < 0 || y >= h) continue;
615 if (s->board[i] == s->board[idx])
616 merge(s->dsf, s->connected, i, idx);
617 }
618}
619
620static void init_solver_state(struct solver_state *s, int w, int h) {
621 const int sz = w * h;
622 int i;
623 assert(s);
624
625 s->nempty = 0;
626 for (i = 0; i < sz; ++i) s->connected[i] = i;
627 for (i = 0; i < sz; ++i)
628 if (s->board[i] == EMPTY) ++s->nempty;
629 else filled_square(s, w, h, i);
630}
631
632static int learn_expand_or_one(struct solver_state *s, int w, int h) {
633 const int sz = w * h;
634 int i;
635 int learn = FALSE;
636
637 assert(s);
638
639 for (i = 0; i < sz; ++i) {
640 int j;
641 int one = TRUE;
642
643 if (s->board[i] != EMPTY) continue;
644
645 for (j = 0; j < 4; ++j) {
646 const int x = (i % w) + dx[j];
647 const int y = (i / w) + dy[j];
648 const int idx = w*y + x;
649 if (x < 0 || x >= w || y < 0 || y >= h) continue;
650 if (s->board[idx] == EMPTY) {
651 one = FALSE;
652 continue;
653 }
654 if (one &&
655 (s->board[idx] == 1 ||
656 (s->board[idx] >= expandsize(s->board, s->dsf, w, h,
657 i, s->board[idx]))))
658 one = FALSE;
659 if (dsf_size(s->dsf, idx) == s->board[idx]) continue;
660 assert(s->board[i] == EMPTY);
661 s->board[i] = -SENTINEL;
662 if (check_capacity(s->board, w, h, idx)) continue;
663 assert(s->board[i] == EMPTY);
664 printv("learn: expanding in one\n");
665 expand(s, w, h, i, idx);
666 learn = TRUE;
667 break;
668 }
669
670 if (j == 4 && one) {
671 printv("learn: one at (%d, %d)\n", i % w, i / w);
672 assert(s->board[i] == EMPTY);
673 s->board[i] = 1;
674 assert(s->nempty);
675 --s->nempty;
676 learn = TRUE;
677 }
678 }
679 return learn;
680}
681
682static int learn_blocked_expansion(struct solver_state *s, int w, int h) {
683 const int sz = w * h;
684 int i;
685 int learn = FALSE;
686
687 assert(s);
688 /* for every connected component */
689 for (i = 0; i < sz; ++i) {
690 int exp = SENTINEL;
691 int j;
692
693 if (s->board[i] == EMPTY) continue;
694 j = dsf_canonify(s->dsf, i);
695
696 /* (but only for each connected component) */
697 if (i != j) continue;
698
699 /* (and not if it's already complete) */
700 if (dsf_size(s->dsf, j) == s->board[j]) continue;
701
702 /* for each square j _in_ the connected component */
703 do {
704 int k;
705 printv(" looking at (%d, %d)\n", j % w, j / w);
706
707 /* for each neighbouring square (idx) */
708 for (k = 0; k < 4; ++k) {
709 const int x = (j % w) + dx[k];
710 const int y = (j / w) + dy[k];
711 const int idx = w*y + x;
712 int size;
713 /* int l;
714 int nhits = 0;
715 int hits[4]; */
716 if (x < 0 || x >= w || y < 0 || y >= h) continue;
717 if (s->board[idx] != EMPTY) continue;
718 if (exp == idx) continue;
719 printv("\ttrying to expand onto (%d, %d)\n", x, y);
720
721 /* find out the would-be size of the new connected
722 * component if we actually expanded into idx */
723 /*
724 size = 1;
725 for (l = 0; l < 4; ++l) {
726 const int lx = x + dx[l];
727 const int ly = y + dy[l];
728 const int idxl = w*ly + lx;
729 int root;
730 int m;
731 if (lx < 0 || lx >= w || ly < 0 || ly >= h) continue;
732 if (board[idxl] != board[j]) continue;
733 root = dsf_canonify(dsf, idxl);
734 for (m = 0; m < nhits && root != hits[m]; ++m);
735 if (m != nhits) continue;
736 // printv("\t (%d, %d) contributed %d to size\n", lx, ly, dsf[root] >> 2);
737 size += dsf_size(dsf, root);
738 assert(dsf_size(dsf, root) >= 1);
739 hits[nhits++] = root;
740 }
741 */
742
743 size = expandsize(s->board, s->dsf, w, h, idx, s->board[j]);
744
745 /* ... and see if that size is too big, or if we
746 * have other expansion candidates. Otherwise
747 * remember the (so far) only candidate. */
748
749 printv("\tthat would give a size of %d\n", size);
750 if (size > s->board[j]) continue;
751 /* printv("\tnow knowing %d expansions\n", nexpand + 1); */
752 if (exp != SENTINEL) goto next_i;
753 assert(exp != idx);
754 exp = idx;
755 }
756
757 j = s->connected[j]; /* next square in the same CC */
758 assert(s->board[i] == s->board[j]);
759 } while (j != i);
760 /* end: for each square j _in_ the connected component */
761
762 if (exp == SENTINEL) continue;
763 printv("learning to expand\n");
764 expand(s, w, h, exp, i);
765 learn = TRUE;
766
767 next_i:
768 ;
769 }
770 /* end: for each connected component */
771 return learn;
772}
773
774static int learn_critical_square(struct solver_state *s, int w, int h) {
775 const int sz = w * h;
776 int i;
777 int learn = FALSE;
778 assert(s);
779
780 /* for each connected component */
781 for (i = 0; i < sz; ++i) {
782 int j, slack;
783 if (s->board[i] == EMPTY) continue;
784 if (i != dsf_canonify(s->dsf, i)) continue;
785 slack = s->board[i] - dsf_size(s->dsf, i);
786 if (slack == 0) continue;
787 assert(s->board[i] != 1);
788 /* for each empty square */
789 for (j = 0; j < sz; ++j) {
790 if (s->board[j] == EMPTY) {
791 /* if it's too far away from the CC, don't bother */
792 int k = i, jx = j % w, jy = j / w;
793 do {
794 int kx = k % w, ky = k / w;
795 if (abs(kx - jx) + abs(ky - jy) <= slack) break;
796 k = s->connected[k];
797 } while (i != k);
798 if (i == k) continue; /* not within range */
799 } else continue;
800 s->board[j] = -SENTINEL;
801 if (check_capacity(s->board, w, h, i)) continue;
802 /* if not expanding s->board[i] to s->board[j] implies
803 * that s->board[i] can't reach its full size, ... */
804 assert(s->nempty);
805 printv(
806 "learn: ds %d at (%d, %d) blocking (%d, %d)\n",
807 s->board[i], j % w, j / w, i % w, i / w);
808 --s->nempty;
809 s->board[j] = s->board[i];
810 filled_square(s, w, h, j);
811 learn = TRUE;
812 }
813 }
814 return learn;
815}
816
817#if 0
818static void print_bitmap(int *bitmap, int w, int h) {
819 if (verbose) {
820 int x, y;
821 for (y = 0; y < h; y++) {
822 for (x = 0; x < w; x++) {
823 printv(" %03x", bm[y*w+x]);
824 }
825 printv("\n");
826 }
827 }
828}
829#endif
830
831static int learn_bitmap_deductions(struct solver_state *s, int w, int h)
832{
833 const int sz = w * h;
834 int *bm = s->bm;
835 int *dsf = s->bmdsf;
836 int *minsize = s->bmminsize;
837 int x, y, i, j, n;
838 int learn = FALSE;
839
840 /*
841 * This function does deductions based on building up a bitmap
842 * which indicates the possible numbers that can appear in each
843 * grid square. If we can rule out all but one possibility for a
844 * particular square, then we've found out the value of that
845 * square. In particular, this is one of the few forms of
846 * deduction capable of inferring the existence of a 'ghost
847 * region', i.e. a region which has none of its squares filled in
848 * at all.
849 *
850 * The reasoning goes like this. A currently unfilled square S can
851 * turn out to contain digit n in exactly two ways: either S is
852 * part of an n-region which also includes some currently known
853 * connected component of squares with n in, or S is part of an
854 * n-region separate from _all_ currently known connected
855 * components. If we can rule out both possibilities, then square
856 * S can't contain digit n at all.
857 *
858 * The former possibility: if there's a region of size n
859 * containing both S and some existing component C, then that
860 * means the distance from S to C must be small enough that C
861 * could be extended to include S without becoming too big. So we
862 * can do a breadth-first search out from all existing components
863 * with n in them, to identify all the squares which could be
864 * joined to any of them.
865 *
866 * The latter possibility: if there's a region of size n that
867 * doesn't contain _any_ existing component, then it also can't
868 * contain any square adjacent to an existing component either. So
869 * we can identify all the EMPTY squares not adjacent to any
870 * existing square with n in, and group them into connected
871 * components; then any component of size less than n is ruled
872 * out, because there wouldn't be room to create a completely new
873 * n-region in it.
874 *
875 * In fact we process these possibilities in the other order.
876 * First we find all the squares not adjacent to an existing
877 * square with n in; then we winnow those by removing too-small
878 * connected components, to get the set of squares which could
879 * possibly be part of a brand new n-region; and finally we do the
880 * breadth-first search to add in the set of squares which could
881 * possibly be added to some existing n-region.
882 */
883
884 /*
885 * Start by initialising our bitmap to 'all numbers possible in
886 * all squares'.
887 */
888 for (y = 0; y < h; y++)
889 for (x = 0; x < w; x++)
890 bm[y*w+x] = (1 << 10) - (1 << 1); /* bits 1,2,...,9 now set */
891#if 0
892 printv("initial bitmap:\n");
893 print_bitmap(bm, w, h);
894#endif
895
896 /*
897 * Now completely zero out the bitmap for squares that are already
898 * filled in (we aren't interested in those anyway). Also, for any
899 * filled square, eliminate its number from all its neighbours
900 * (because, as discussed above, the neighbours couldn't be part
901 * of a _new_ region with that number in it, and that's the case
902 * we consider first).
903 */
904 for (y = 0; y < h; y++) {
905 for (x = 0; x < w; x++) {
906 i = y*w+x;
907 n = s->board[i];
908
909 if (n != EMPTY) {
910 bm[i] = 0;
911
912 if (x > 0)
913 bm[i-1] &= ~(1 << n);
914 if (x+1 < w)
915 bm[i+1] &= ~(1 << n);
916 if (y > 0)
917 bm[i-w] &= ~(1 << n);
918 if (y+1 < h)
919 bm[i+w] &= ~(1 << n);
920 }
921 }
922 }
923#if 0
924 printv("bitmap after filled squares:\n");
925 print_bitmap(bm, w, h);
926#endif
927
928 /*
929 * Now, for each n, we separately find the connected components of
930 * squares for which n is still a possibility. Then discard any
931 * component of size < n, because that component is too small to
932 * have a completely new n-region in it.
933 */
934 for (n = 1; n <= 9; n++) {
935 dsf_init(dsf, sz);
936
937 /* Build the dsf */
938 for (y = 0; y < h; y++)
939 for (x = 0; x+1 < w; x++)
940 if (bm[y*w+x] & bm[y*w+(x+1)] & (1 << n))
941 dsf_merge(dsf, y*w+x, y*w+(x+1));
942 for (y = 0; y+1 < h; y++)
943 for (x = 0; x < w; x++)
944 if (bm[y*w+x] & bm[(y+1)*w+x] & (1 << n))
945 dsf_merge(dsf, y*w+x, (y+1)*w+x);
946
947 /* Query the dsf */
948 for (i = 0; i < sz; i++)
949 if ((bm[i] & (1 << n)) && dsf_size(dsf, i) < n)
950 bm[i] &= ~(1 << n);
951 }
952#if 0
953 printv("bitmap after winnowing small components:\n");
954 print_bitmap(bm, w, h);
955#endif
956
957 /*
958 * Now our bitmap includes every square which could be part of a
959 * completely new region, of any size. Extend it to include
960 * squares which could be part of an existing region.
961 */
962 for (n = 1; n <= 9; n++) {
963 /*
964 * We're going to do a breadth-first search starting from
965 * existing connected components with cell value n, to find
966 * all cells they might possibly extend into.
967 *
968 * The quantity we compute, for each square, is 'minimum size
969 * that any existing CC would have to have if extended to
970 * include this square'. So squares already _in_ an existing
971 * CC are initialised to the size of that CC; then we search
972 * outwards using the rule that if a square's score is j, then
973 * its neighbours can't score more than j+1.
974 *
975 * Scores are capped at n+1, because if a square scores more
976 * than n then that's enough to know it can't possibly be
977 * reached by extending an existing region - we don't need to
978 * know exactly _how far_ out of reach it is.
979 */
980 for (i = 0; i < sz; i++) {
981 if (s->board[i] == n) {
982 /* Square is part of an existing CC. */
983 minsize[i] = dsf_size(s->dsf, i);
984 } else {
985 /* Otherwise, initialise to the maximum score n+1;
986 * we'll reduce this later if we find a neighbouring
987 * square with a lower score. */
988 minsize[i] = n+1;
989 }
990 }
991
992 for (j = 1; j < n; j++) {
993 /*
994 * Find neighbours of cells scoring j, and set their score
995 * to at most j+1.
996 *
997 * Doing the BFS this way means we need n passes over the
998 * grid, which isn't entirely optimal but it seems to be
999 * fast enough for the moment. This could probably be
1000 * improved by keeping a linked-list queue of cells in
1001 * some way, but I think you'd have to be a bit careful to
1002 * insert things into the right place in the queue; this
1003 * way is easier not to get wrong.
1004 */
1005 for (y = 0; y < h; y++) {
1006 for (x = 0; x < w; x++) {
1007 i = y*w+x;
1008 if (minsize[i] == j) {
1009 if (x > 0 && minsize[i-1] > j+1)
1010 minsize[i-1] = j+1;
1011 if (x+1 < w && minsize[i+1] > j+1)
1012 minsize[i+1] = j+1;
1013 if (y > 0 && minsize[i-w] > j+1)
1014 minsize[i-w] = j+1;
1015 if (y+1 < h && minsize[i+w] > j+1)
1016 minsize[i+w] = j+1;
1017 }
1018 }
1019 }
1020 }
1021
1022 /*
1023 * Now, every cell scoring at most n should have its 1<<n bit
1024 * in the bitmap reinstated, because we've found that it's
1025 * potentially reachable by extending an existing CC.
1026 */
1027 for (i = 0; i < sz; i++)
1028 if (minsize[i] <= n)
1029 bm[i] |= 1<<n;
1030 }
1031#if 0
1032 printv("bitmap after bfs:\n");
1033 print_bitmap(bm, w, h);
1034#endif
1035
1036 /*
1037 * Now our bitmap is complete. Look for entries with only one bit
1038 * set; those are squares with only one possible number, in which
1039 * case we can fill that number in.
1040 */
1041 for (i = 0; i < sz; i++) {
1042 if (bm[i] && !(bm[i] & (bm[i]-1))) { /* is bm[i] a power of two? */
1043 int val = bm[i];
1044
1045 /* Integer log2, by simple binary search. */
1046 n = 0;
1047 if (val >> 8) { val >>= 8; n += 8; }
1048 if (val >> 4) { val >>= 4; n += 4; }
1049 if (val >> 2) { val >>= 2; n += 2; }
1050 if (val >> 1) { val >>= 1; n += 1; }
1051
1052 /* Double-check that we ended up with a sensible
1053 * answer. */
1054 assert(1 <= n);
1055 assert(n <= 9);
1056 assert(bm[i] == (1 << n));
1057
1058 if (s->board[i] == EMPTY) {
1059 printv("learn: %d is only possibility at (%d, %d)\n",
1060 n, i % w, i / w);
1061 s->board[i] = n;
1062 filled_square(s, w, h, i);
1063 assert(s->nempty);
1064 --s->nempty;
1065 learn = TRUE;
1066 }
1067 }
1068 }
1069
1070 return learn;
1071}
1072
1073static int solver(const int *orig, int w, int h, char **solution) {
1074 const int sz = w * h;
1075
1076 struct solver_state ss;
1077 ss.board = memdup(orig, sz, sizeof (int));
1078 ss.dsf = snew_dsf(sz); /* eqv classes: connected components */
1079 ss.connected = snewn(sz, int); /* connected[n] := n.next; */
1080 /* cyclic disjoint singly linked lists, same partitioning as dsf.
1081 * The lists lets you iterate over a partition given any member */
1082 ss.bm = snewn(sz, int);
1083 ss.bmdsf = snew_dsf(sz);
1084 ss.bmminsize = snewn(sz, int);
1085
1086 printv("trying to solve this:\n");
1087 print_board(ss.board, w, h);
1088
1089 init_solver_state(&ss, w, h);
1090 do {
1091 if (learn_blocked_expansion(&ss, w, h)) continue;
1092 if (learn_expand_or_one(&ss, w, h)) continue;
1093 if (learn_critical_square(&ss, w, h)) continue;
1094 if (learn_bitmap_deductions(&ss, w, h)) continue;
1095 break;
1096 } while (ss.nempty);
1097
1098 printv("best guess:\n");
1099 print_board(ss.board, w, h);
1100
1101 if (solution) {
1102 int i;
1103 *solution = snewn(sz + 2, char);
1104 **solution = 's';
1105 for (i = 0; i < sz; ++i) (*solution)[i + 1] = ss.board[i] + '0';
1106 (*solution)[sz + 1] = '\0';
1107 /* We don't need the \0 for execute_move (the only user)
1108 * I'm just being printf-friendly in case I wanna print */
1109 }
1110
1111 sfree(ss.dsf);
1112 sfree(ss.board);
1113 sfree(ss.connected);
1114 sfree(ss.bm);
1115 sfree(ss.bmdsf);
1116 sfree(ss.bmminsize);
1117
1118 return !ss.nempty;
1119}
1120
1121static int *make_dsf(int *dsf, int *board, const int w, const int h) {
1122 const int sz = w * h;
1123 int i;
1124
1125 if (!dsf)
1126 dsf = snew_dsf(w * h);
1127 else
1128 dsf_init(dsf, w * h);
1129
1130 for (i = 0; i < sz; ++i) {
1131 int j;
1132 for (j = 0; j < 4; ++j) {
1133 const int x = (i % w) + dx[j];
1134 const int y = (i / w) + dy[j];
1135 const int k = w*y + x;
1136 if (x < 0 || x >= w || y < 0 || y >= h) continue;
1137 if (board[i] == board[k]) dsf_merge(dsf, i, k);
1138 }
1139 }
1140 return dsf;
1141}
1142
1143static void minimize_clue_set(int *board, int w, int h, random_state *rs)
1144{
1145 const int sz = w * h;
1146 int *shuf = snewn(sz, int), i;
1147 int *dsf, *next;
1148
1149 for (i = 0; i < sz; ++i) shuf[i] = i;
1150 shuffle(shuf, sz, sizeof (int), rs);
1151
1152 /*
1153 * First, try to eliminate an entire region at a time if possible,
1154 * because inferring the existence of a completely unclued region
1155 * is a particularly good aspect of this puzzle type and we want
1156 * to encourage it to happen.
1157 *
1158 * Begin by identifying the regions as linked lists of cells using
1159 * the 'next' array.
1160 */
1161 dsf = make_dsf(NULL, board, w, h);
1162 next = snewn(sz, int);
1163 for (i = 0; i < sz; ++i) {
1164 int j = dsf_canonify(dsf, i);
1165 if (i == j) {
1166 /* First cell of a region; set next[i] = -1 to indicate
1167 * end-of-list. */
1168 next[i] = -1;
1169 } else {
1170 /* Add this cell to a region which already has a
1171 * linked-list head, by pointing the canonical element j
1172 * at this one, and pointing this one in turn at wherever
1173 * j previously pointed. (This should end up with the
1174 * elements linked in the order 1,n,n-1,n-2,...,2, which
1175 * is a bit weird-looking, but any order is fine.)
1176 */
1177 assert(j < i);
1178 next[i] = next[j];
1179 next[j] = i;
1180 }
1181 }
1182
1183 /*
1184 * Now loop over the grid cells in our shuffled order, and each
1185 * time we encounter a region for the first time, try to remove it
1186 * all. Then we set next[canonical index] to -2 rather than -1, to
1187 * mark it as already tried.
1188 *
1189 * Doing this in a loop over _cells_, rather than extracting and
1190 * shuffling a list of _regions_, is intended to skew the
1191 * probabilities towards trying to remove larger regions first
1192 * (but without anything as crudely predictable as enforcing that
1193 * we _always_ process regions in descending size order). Region
1194 * removals might well be mutually exclusive, and larger ghost
1195 * regions are more interesting, so we want to bias towards them
1196 * if we can.
1197 */
1198 for (i = 0; i < sz; ++i) {
1199 int j = dsf_canonify(dsf, shuf[i]);
1200 if (next[j] != -2) {
1201 int tmp = board[j];
1202 int k;
1203
1204 /* Blank out the whole thing. */
1205 for (k = j; k >= 0; k = next[k])
1206 board[k] = EMPTY;
1207
1208 if (!solver(board, w, h, NULL)) {
1209 /* Wasn't still solvable; reinstate it all */
1210 for (k = j; k >= 0; k = next[k])
1211 board[k] = tmp;
1212 }
1213
1214 /* Either way, don't try this region again. */
1215 next[j] = -2;
1216 }
1217 }
1218 sfree(next);
1219 sfree(dsf);
1220
1221 /*
1222 * Now go through individual cells, in the same shuffled order,
1223 * and try to remove each one by itself.
1224 */
1225 for (i = 0; i < sz; ++i) {
1226 int tmp = board[shuf[i]];
1227 board[shuf[i]] = EMPTY;
1228 if (!solver(board, w, h, NULL)) board[shuf[i]] = tmp;
1229 }
1230
1231 sfree(shuf);
1232}
1233
1234static int encode_run(char *buffer, int run)
1235{
1236 int i = 0;
1237 for (; run > 26; run -= 26)
1238 buffer[i++] = 'z';
1239 if (run)
1240 buffer[i++] = 'a' - 1 + run;
1241 return i;
1242}
1243
1244static char *new_game_desc(const game_params *params, random_state *rs,
1245 char **aux, int interactive)
1246{
1247 const int w = params->w, h = params->h, sz = w * h;
1248 int *board = snewn(sz, int), i, j, run;
1249 char *description = snewn(sz + 1, char);
1250
1251 make_board(board, w, h, rs);
1252 minimize_clue_set(board, w, h, rs);
1253
1254 for (run = j = i = 0; i < sz; ++i) {
1255 assert(board[i] >= 0);
1256 assert(board[i] < 10);
1257 if (board[i] == 0) {
1258 ++run;
1259 } else {
1260 j += encode_run(description + j, run);
1261 run = 0;
1262 description[j++] = board[i] + '0';
1263 }
1264 }
1265 j += encode_run(description + j, run);
1266 description[j++] = '\0';
1267
1268 sfree(board);
1269
1270 return sresize(description, j, char);
1271}
1272
1273static char *validate_desc(const game_params *params, const char *desc)
1274{
1275 const int sz = params->w * params->h;
1276 const char m = '0' + max(max(params->w, params->h), 3);
1277 int area;
1278
1279 for (area = 0; *desc; ++desc) {
1280 if (*desc >= 'a' && *desc <= 'z') area += *desc - 'a' + 1;
1281 else if (*desc >= '0' && *desc <= m) ++area;
1282 else {
1283 static char s[] = "Invalid character '%""' in game description";
1284 int n = sprintf(s, "Invalid character '%1c' in game description",
1285 *desc);
1286 assert(n + 1 <= lenof(s)); /* +1 for the terminating NUL */
1287 return s;
1288 }
1289 if (area > sz) return "Too much data to fit in grid";
1290 }
1291 return (area < sz) ? "Not enough data to fill grid" : NULL;
1292}
1293
1294static game_state *new_game(midend *me, const game_params *params,
1295 const char *desc)
1296{
1297 game_state *state = snew(game_state);
1298 int sz = params->w * params->h;
1299 int i;
1300
1301 state->cheated = state->completed = FALSE;
1302 state->shared = snew(struct shared_state);
1303 state->shared->refcnt = 1;
1304 state->shared->params = *params; /* struct copy */
1305 state->shared->clues = snewn(sz, int);
1306
1307 for (i = 0; *desc; ++desc) {
1308 if (*desc >= 'a' && *desc <= 'z') {
1309 int j = *desc - 'a' + 1;
1310 assert(i + j <= sz);
1311 for (; j; --j) state->shared->clues[i++] = 0;
1312 } else state->shared->clues[i++] = *desc - '0';
1313 }
1314 state->board = memdup(state->shared->clues, sz, sizeof (int));
1315
1316 return state;
1317}
1318
1319static game_state *dup_game(const game_state *state)
1320{
1321 const int sz = state->shared->params.w * state->shared->params.h;
1322 game_state *ret = snew(game_state);
1323
1324 ret->board = memdup(state->board, sz, sizeof (int));
1325 ret->shared = state->shared;
1326 ret->cheated = state->cheated;
1327 ret->completed = state->completed;
1328 ++ret->shared->refcnt;
1329
1330 return ret;
1331}
1332
1333static void free_game(game_state *state)
1334{
1335 assert(state);
1336 sfree(state->board);
1337 if (--state->shared->refcnt == 0) {
1338 sfree(state->shared->clues);
1339 sfree(state->shared);
1340 }
1341 sfree(state);
1342}
1343
1344static char *solve_game(const game_state *state, const game_state *currstate,
1345 const char *aux, char **error)
1346{
1347 if (aux == NULL) {
1348 const int w = state->shared->params.w;
1349 const int h = state->shared->params.h;
1350 char *new_aux;
1351 if (!solver(state->board, w, h, &new_aux))
1352 *error = "Sorry, I couldn't find a solution";
1353 return new_aux;
1354 }
1355 return dupstr(aux);
1356}
1357
1358/*****************************************************************************
1359 * USER INTERFACE STATE AND ACTION *
1360 *****************************************************************************/
1361
1362struct game_ui {
1363 int *sel; /* w*h highlighted squares, or NULL */
1364 int cur_x, cur_y, cur_visible, keydragging;
1365};
1366
1367static game_ui *new_ui(const game_state *state)
1368{
1369 game_ui *ui = snew(game_ui);
1370
1371 ui->sel = NULL;
1372 ui->cur_x = ui->cur_y = ui->cur_visible = ui->keydragging = 0;
1373
1374 return ui;
1375}
1376
1377static void free_ui(game_ui *ui)
1378{
1379 if (ui->sel)
1380 sfree(ui->sel);
1381 sfree(ui);
1382}
1383
1384static char *encode_ui(const game_ui *ui)
1385{
1386 return NULL;
1387}
1388
1389static void decode_ui(game_ui *ui, const char *encoding)
1390{
1391}
1392
1393static void game_changed_state(game_ui *ui, const game_state *oldstate,
1394 const game_state *newstate)
1395{
1396 /* Clear any selection */
1397 if (ui->sel) {
1398 sfree(ui->sel);
1399 ui->sel = NULL;
1400 }
1401 ui->keydragging = FALSE;
1402}
1403
1404#define PREFERRED_TILE_SIZE 32
1405#define TILE_SIZE (ds->tilesize)
1406#define BORDER (TILE_SIZE / 2)
1407#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
1408
1409struct game_drawstate {
1410 struct game_params params;
1411 int tilesize;
1412 int started;
1413 int *v, *flags;
1414 int *dsf_scratch, *border_scratch;
1415};
1416
1417static char *interpret_move(const game_state *state, game_ui *ui,
1418 const game_drawstate *ds,
1419 int x, int y, int button)
1420{
1421 const int w = state->shared->params.w;
1422 const int h = state->shared->params.h;
1423
1424 const int tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
1425 const int ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
1426
1427 char *move = NULL;
1428 int i;
1429
1430 assert(ui);
1431 assert(ds);
1432
1433 button &= ~MOD_MASK;
1434
1435 if (button == LEFT_BUTTON || button == LEFT_DRAG) {
1436 /* A left-click anywhere will clear the current selection. */
1437 if (button == LEFT_BUTTON) {
1438 if (ui->sel) {
1439 sfree(ui->sel);
1440 ui->sel = NULL;
1441 }
1442 }
1443 if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
1444 if (!ui->sel) {
1445 ui->sel = snewn(w*h, int);
1446 memset(ui->sel, 0, w*h*sizeof(int));
1447 }
1448 if (!state->shared->clues[w*ty+tx])
1449 ui->sel[w*ty+tx] = 1;
1450 }
1451 ui->cur_visible = 0;
1452 return ""; /* redraw */
1453 }
1454
1455 if (IS_CURSOR_MOVE(button)) {
1456 ui->cur_visible = 1;
1457 move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, 0);
1458 if (ui->keydragging) goto select_square;
1459 return "";
1460 }
1461 if (button == CURSOR_SELECT) {
1462 if (!ui->cur_visible) {
1463 ui->cur_visible = 1;
1464 return "";
1465 }
1466 ui->keydragging = !ui->keydragging;
1467 if (!ui->keydragging) return "";
1468
1469 select_square:
1470 if (!ui->sel) {
1471 ui->sel = snewn(w*h, int);
1472 memset(ui->sel, 0, w*h*sizeof(int));
1473 }
1474 if (!state->shared->clues[w*ui->cur_y + ui->cur_x])
1475 ui->sel[w*ui->cur_y + ui->cur_x] = 1;
1476 return "";
1477 }
1478 if (button == CURSOR_SELECT2) {
1479 if (!ui->cur_visible) {
1480 ui->cur_visible = 1;
1481 return "";
1482 }
1483 if (!ui->sel) {
1484 ui->sel = snewn(w*h, int);
1485 memset(ui->sel, 0, w*h*sizeof(int));
1486 }
1487 ui->keydragging = FALSE;
1488 if (!state->shared->clues[w*ui->cur_y + ui->cur_x])
1489 ui->sel[w*ui->cur_y + ui->cur_x] ^= 1;
1490 for (i = 0; i < w*h && !ui->sel[i]; i++);
1491 if (i == w*h) {
1492 sfree(ui->sel);
1493 ui->sel = NULL;
1494 }
1495 return "";
1496 }
1497
1498 if (button == '\b' || button == 27) {
1499 sfree(ui->sel);
1500 ui->sel = NULL;
1501 ui->keydragging = FALSE;
1502 return "";
1503 }
1504
1505 if (button < '0' || button > '9') return NULL;
1506 button -= '0';
1507 if (button > (w == 2 && h == 2 ? 3 : max(w, h))) return NULL;
1508 ui->keydragging = FALSE;
1509
1510 for (i = 0; i < w*h; i++) {
1511 char buf[32];
1512 if ((ui->sel && ui->sel[i]) ||
1513 (!ui->sel && ui->cur_visible && (w*ui->cur_y+ui->cur_x) == i)) {
1514 if (state->shared->clues[i] != 0) continue; /* in case cursor is on clue */
1515 if (state->board[i] != button) {
1516 sprintf(buf, "%s%d", move ? "," : "", i);
1517 if (move) {
1518 move = srealloc(move, strlen(move)+strlen(buf)+1);
1519 strcat(move, buf);
1520 } else {
1521 move = smalloc(strlen(buf)+1);
1522 strcpy(move, buf);
1523 }
1524 }
1525 }
1526 }
1527 if (move) {
1528 char buf[32];
1529 sprintf(buf, "_%d", button);
1530 move = srealloc(move, strlen(move)+strlen(buf)+1);
1531 strcat(move, buf);
1532 }
1533 if (!ui->sel) return move ? move : NULL;
1534 sfree(ui->sel);
1535 ui->sel = NULL;
1536 /* Need to update UI at least, as we cleared the selection */
1537 return move ? move : "";
1538}
1539
1540static game_state *execute_move(const game_state *state, const char *move)
1541{
1542 game_state *new_state = NULL;
1543 const int sz = state->shared->params.w * state->shared->params.h;
1544
1545 if (*move == 's') {
1546 int i = 0;
1547 new_state = dup_game(state);
1548 for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0';
1549 new_state->cheated = TRUE;
1550 } else {
1551 int value;
1552 char *endptr, *delim = strchr(move, '_');
1553 if (!delim) goto err;
1554 value = strtol(delim+1, &endptr, 0);
1555 if (*endptr || endptr == delim+1) goto err;
1556 if (value < 0 || value > 9) goto err;
1557 new_state = dup_game(state);
1558 while (*move) {
1559 const int i = strtol(move, &endptr, 0);
1560 if (endptr == move) goto err;
1561 if (i < 0 || i >= sz) goto err;
1562 new_state->board[i] = value;
1563 if (*endptr == '_') break;
1564 if (*endptr != ',') goto err;
1565 move = endptr + 1;
1566 }
1567 }
1568
1569 /*
1570 * Check for completion.
1571 */
1572 if (!new_state->completed) {
1573 const int w = new_state->shared->params.w;
1574 const int h = new_state->shared->params.h;
1575 const int sz = w * h;
1576 int *dsf = make_dsf(NULL, new_state->board, w, h);
1577 int i;
1578 for (i = 0; i < sz && new_state->board[i] == dsf_size(dsf, i); ++i);
1579 sfree(dsf);
1580 if (i == sz)
1581 new_state->completed = TRUE;
1582 }
1583
1584 return new_state;
1585
1586err:
1587 if (new_state) free_game(new_state);
1588 return NULL;
1589}
1590
1591/* ----------------------------------------------------------------------
1592 * Drawing routines.
1593 */
1594
1595#define FLASH_TIME 0.4F
1596
1597#define COL_CLUE COL_GRID
1598enum {
1599 COL_BACKGROUND,
1600 COL_GRID,
1601 COL_HIGHLIGHT,
1602 COL_CORRECT,
1603 COL_ERROR,
1604 COL_USER,
1605 COL_CURSOR,
1606 NCOLOURS
1607};
1608
1609static void game_compute_size(const game_params *params, int tilesize,
1610 int *x, int *y)
1611{
1612 *x = (params->w + 1) * tilesize;
1613 *y = (params->h + 1) * tilesize;
1614}
1615
1616static void game_set_size(drawing *dr, game_drawstate *ds,
1617 const game_params *params, int tilesize)
1618{
1619 ds->tilesize = tilesize;
1620}
1621
1622static float *game_colours(frontend *fe, int *ncolours)
1623{
1624 float *ret = snewn(3 * NCOLOURS, float);
1625
1626 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1627
1628 ret[COL_GRID * 3 + 0] = 0.0F;
1629 ret[COL_GRID * 3 + 1] = 0.0F;
1630 ret[COL_GRID * 3 + 2] = 0.0F;
1631
1632 ret[COL_HIGHLIGHT * 3 + 0] = 0.85F * ret[COL_BACKGROUND * 3 + 0];
1633 ret[COL_HIGHLIGHT * 3 + 1] = 0.85F * ret[COL_BACKGROUND * 3 + 1];
1634 ret[COL_HIGHLIGHT * 3 + 2] = 0.85F * ret[COL_BACKGROUND * 3 + 2];
1635
1636 ret[COL_CORRECT * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0];
1637 ret[COL_CORRECT * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1];
1638 ret[COL_CORRECT * 3 + 2] = 0.9F * ret[COL_BACKGROUND * 3 + 2];
1639
1640 ret[COL_CURSOR * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1641 ret[COL_CURSOR * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1642 ret[COL_CURSOR * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
1643
1644 ret[COL_ERROR * 3 + 0] = 1.0F;
1645 ret[COL_ERROR * 3 + 1] = 0.85F * ret[COL_BACKGROUND * 3 + 1];
1646 ret[COL_ERROR * 3 + 2] = 0.85F * ret[COL_BACKGROUND * 3 + 2];
1647
1648 ret[COL_USER * 3 + 0] = 0.0F;
1649 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1650 ret[COL_USER * 3 + 2] = 0.0F;
1651
1652 *ncolours = NCOLOURS;
1653 return ret;
1654}
1655
1656static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1657{
1658 struct game_drawstate *ds = snew(struct game_drawstate);
1659 int i;
1660
1661 ds->tilesize = PREFERRED_TILE_SIZE;
1662 ds->started = 0;
1663 ds->params = state->shared->params;
1664 ds->v = snewn(ds->params.w * ds->params.h, int);
1665 ds->flags = snewn(ds->params.w * ds->params.h, int);
1666 for (i = 0; i < ds->params.w * ds->params.h; i++)
1667 ds->v[i] = ds->flags[i] = -1;
1668 ds->border_scratch = snewn(ds->params.w * ds->params.h, int);
1669 ds->dsf_scratch = NULL;
1670
1671 return ds;
1672}
1673
1674static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1675{
1676 sfree(ds->v);
1677 sfree(ds->flags);
1678 sfree(ds->border_scratch);
1679 sfree(ds->dsf_scratch);
1680 sfree(ds);
1681}
1682
1683#define BORDER_U 0x001
1684#define BORDER_D 0x002
1685#define BORDER_L 0x004
1686#define BORDER_R 0x008
1687#define BORDER_UR 0x010
1688#define BORDER_DR 0x020
1689#define BORDER_UL 0x040
1690#define BORDER_DL 0x080
1691#define HIGH_BG 0x100
1692#define CORRECT_BG 0x200
1693#define ERROR_BG 0x400
1694#define USER_COL 0x800
1695#define CURSOR_SQ 0x1000
1696
1697static void draw_square(drawing *dr, game_drawstate *ds, int x, int y,
1698 int n, int flags)
1699{
1700 assert(dr);
1701 assert(ds);
1702
1703 /*
1704 * Clip to the grid square.
1705 */
1706 clip(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE,
1707 TILE_SIZE, TILE_SIZE);
1708
1709 /*
1710 * Clear the square.
1711 */
1712 draw_rect(dr,
1713 BORDER + x*TILE_SIZE,
1714 BORDER + y*TILE_SIZE,
1715 TILE_SIZE,
1716 TILE_SIZE,
1717 (flags & HIGH_BG ? COL_HIGHLIGHT :
1718 flags & ERROR_BG ? COL_ERROR :
1719 flags & CORRECT_BG ? COL_CORRECT : COL_BACKGROUND));
1720
1721 /*
1722 * Draw the grid lines.
1723 */
1724 draw_line(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE,
1725 BORDER + (x+1)*TILE_SIZE, BORDER + y*TILE_SIZE, COL_GRID);
1726 draw_line(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE,
1727 BORDER + x*TILE_SIZE, BORDER + (y+1)*TILE_SIZE, COL_GRID);
1728
1729 /*
1730 * Draw the number.
1731 */
1732 if (n) {
1733 char buf[2];
1734 buf[0] = n + '0';
1735 buf[1] = '\0';
1736 draw_text(dr,
1737 (x + 1) * TILE_SIZE,
1738 (y + 1) * TILE_SIZE,
1739 FONT_VARIABLE,
1740 TILE_SIZE / 2,
1741 ALIGN_VCENTRE | ALIGN_HCENTRE,
1742 flags & USER_COL ? COL_USER : COL_CLUE,
1743 buf);
1744 }
1745
1746 /*
1747 * Draw bold lines around the borders.
1748 */
1749 if (flags & BORDER_L)
1750 draw_rect(dr,
1751 BORDER + x*TILE_SIZE + 1,
1752 BORDER + y*TILE_SIZE + 1,
1753 BORDER_WIDTH,
1754 TILE_SIZE - 1,
1755 COL_GRID);
1756 if (flags & BORDER_U)
1757 draw_rect(dr,
1758 BORDER + x*TILE_SIZE + 1,
1759 BORDER + y*TILE_SIZE + 1,
1760 TILE_SIZE - 1,
1761 BORDER_WIDTH,
1762 COL_GRID);
1763 if (flags & BORDER_R)
1764 draw_rect(dr,
1765 BORDER + (x+1)*TILE_SIZE - BORDER_WIDTH,
1766 BORDER + y*TILE_SIZE + 1,
1767 BORDER_WIDTH,
1768 TILE_SIZE - 1,
1769 COL_GRID);
1770 if (flags & BORDER_D)
1771 draw_rect(dr,
1772 BORDER + x*TILE_SIZE + 1,
1773 BORDER + (y+1)*TILE_SIZE - BORDER_WIDTH,
1774 TILE_SIZE - 1,
1775 BORDER_WIDTH,
1776 COL_GRID);
1777 if (flags & BORDER_UL)
1778 draw_rect(dr,
1779 BORDER + x*TILE_SIZE + 1,
1780 BORDER + y*TILE_SIZE + 1,
1781 BORDER_WIDTH,
1782 BORDER_WIDTH,
1783 COL_GRID);
1784 if (flags & BORDER_UR)
1785 draw_rect(dr,
1786 BORDER + (x+1)*TILE_SIZE - BORDER_WIDTH,
1787 BORDER + y*TILE_SIZE + 1,
1788 BORDER_WIDTH,
1789 BORDER_WIDTH,
1790 COL_GRID);
1791 if (flags & BORDER_DL)
1792 draw_rect(dr,
1793 BORDER + x*TILE_SIZE + 1,
1794 BORDER + (y+1)*TILE_SIZE - BORDER_WIDTH,
1795 BORDER_WIDTH,
1796 BORDER_WIDTH,
1797 COL_GRID);
1798 if (flags & BORDER_DR)
1799 draw_rect(dr,
1800 BORDER + (x+1)*TILE_SIZE - BORDER_WIDTH,
1801 BORDER + (y+1)*TILE_SIZE - BORDER_WIDTH,
1802 BORDER_WIDTH,
1803 BORDER_WIDTH,
1804 COL_GRID);
1805
1806 if (flags & CURSOR_SQ) {
1807 int coff = TILE_SIZE/8;
1808 draw_rect_outline(dr,
1809 BORDER + x*TILE_SIZE + coff,
1810 BORDER + y*TILE_SIZE + coff,
1811 TILE_SIZE - coff*2,
1812 TILE_SIZE - coff*2,
1813 COL_CURSOR);
1814 }
1815
1816 unclip(dr);
1817
1818 draw_update(dr,
1819 BORDER + x*TILE_SIZE,
1820 BORDER + y*TILE_SIZE,
1821 TILE_SIZE,
1822 TILE_SIZE);
1823}
1824
1825static void draw_grid(drawing *dr, game_drawstate *ds, const game_state *state,
1826 const game_ui *ui, int flashy, int borders, int shading)
1827{
1828 const int w = state->shared->params.w;
1829 const int h = state->shared->params.h;
1830 int x;
1831 int y;
1832
1833 /*
1834 * Build a dsf for the board in its current state, to use for
1835 * highlights and hints.
1836 */
1837 ds->dsf_scratch = make_dsf(ds->dsf_scratch, state->board, w, h);
1838
1839 /*
1840 * Work out where we're putting borders between the cells.
1841 */
1842 for (y = 0; y < w*h; y++)
1843 ds->border_scratch[y] = 0;
1844
1845 for (y = 0; y < h; y++)
1846 for (x = 0; x < w; x++) {
1847 int dx, dy;
1848 int v1, s1, v2, s2;
1849
1850 for (dx = 0; dx <= 1; dx++) {
1851 int border = FALSE;
1852
1853 dy = 1 - dx;
1854
1855 if (x+dx >= w || y+dy >= h)
1856 continue;
1857
1858 v1 = state->board[y*w+x];
1859 v2 = state->board[(y+dy)*w+(x+dx)];
1860 s1 = dsf_size(ds->dsf_scratch, y*w+x);
1861 s2 = dsf_size(ds->dsf_scratch, (y+dy)*w+(x+dx));
1862
1863 /*
1864 * We only ever draw a border between two cells if
1865 * they don't have the same contents.
1866 */
1867 if (v1 != v2) {
1868 /*
1869 * But in that situation, we don't always draw
1870 * a border. We do if the two cells both
1871 * contain actual numbers...
1872 */
1873 if (v1 && v2)
1874 border = TRUE;
1875
1876 /*
1877 * ... or if at least one of them is a
1878 * completed or overfull omino.
1879 */
1880 if (v1 && s1 >= v1)
1881 border = TRUE;
1882 if (v2 && s2 >= v2)
1883 border = TRUE;
1884 }
1885
1886 if (border)
1887 ds->border_scratch[y*w+x] |= (dx ? 1 : 2);
1888 }
1889 }
1890
1891 /*
1892 * Actually do the drawing.
1893 */
1894 for (y = 0; y < h; ++y)
1895 for (x = 0; x < w; ++x) {
1896 /*
1897 * Determine what we need to draw in this square.
1898 */
1899 int i = y*w+x, v = state->board[i];
1900 int flags = 0;
1901
1902 if (flashy || !shading) {
1903 /* clear all background flags */
1904 } else if (ui && ui->sel && ui->sel[i]) {
1905 flags |= HIGH_BG;
1906 } else if (v) {
1907 int size = dsf_size(ds->dsf_scratch, i);
1908 if (size == v)
1909 flags |= CORRECT_BG;
1910 else if (size > v)
1911 flags |= ERROR_BG;
1912 else {
1913 int rt = dsf_canonify(ds->dsf_scratch, i), j;
1914 for (j = 0; j < w*h; ++j) {
1915 int k;
1916 if (dsf_canonify(ds->dsf_scratch, j) != rt) continue;
1917 for (k = 0; k < 4; ++k) {
1918 const int xx = j % w + dx[k], yy = j / w + dy[k];
1919 if (xx >= 0 && xx < w && yy >= 0 && yy < h &&
1920 state->board[yy*w + xx] == EMPTY)
1921 goto noflag;
1922 }
1923 }
1924 flags |= ERROR_BG;
1925 noflag:
1926 ;
1927 }
1928 }
1929 if (ui && ui->cur_visible && x == ui->cur_x && y == ui->cur_y)
1930 flags |= CURSOR_SQ;
1931
1932 /*
1933 * Borders at the very edges of the grid are
1934 * independent of the `borders' flag.
1935 */
1936 if (x == 0)
1937 flags |= BORDER_L;
1938 if (y == 0)
1939 flags |= BORDER_U;
1940 if (x == w-1)
1941 flags |= BORDER_R;
1942 if (y == h-1)
1943 flags |= BORDER_D;
1944
1945 if (borders) {
1946 if (x == 0 || (ds->border_scratch[y*w+(x-1)] & 1))
1947 flags |= BORDER_L;
1948 if (y == 0 || (ds->border_scratch[(y-1)*w+x] & 2))
1949 flags |= BORDER_U;
1950 if (x == w-1 || (ds->border_scratch[y*w+x] & 1))
1951 flags |= BORDER_R;
1952 if (y == h-1 || (ds->border_scratch[y*w+x] & 2))
1953 flags |= BORDER_D;
1954
1955 if (y > 0 && x > 0 && (ds->border_scratch[(y-1)*w+(x-1)]))
1956 flags |= BORDER_UL;
1957 if (y > 0 && x < w-1 &&
1958 ((ds->border_scratch[(y-1)*w+x] & 1) ||
1959 (ds->border_scratch[(y-1)*w+(x+1)] & 2)))
1960 flags |= BORDER_UR;
1961 if (y < h-1 && x > 0 &&
1962 ((ds->border_scratch[y*w+(x-1)] & 2) ||
1963 (ds->border_scratch[(y+1)*w+(x-1)] & 1)))
1964 flags |= BORDER_DL;
1965 if (y < h-1 && x < w-1 &&
1966 ((ds->border_scratch[y*w+(x+1)] & 2) ||
1967 (ds->border_scratch[(y+1)*w+x] & 1)))
1968 flags |= BORDER_DR;
1969 }
1970
1971 if (!state->shared->clues[y*w+x])
1972 flags |= USER_COL;
1973
1974 if (ds->v[y*w+x] != v || ds->flags[y*w+x] != flags) {
1975 draw_square(dr, ds, x, y, v, flags);
1976 ds->v[y*w+x] = v;
1977 ds->flags[y*w+x] = flags;
1978 }
1979 }
1980}
1981
1982static void game_redraw(drawing *dr, game_drawstate *ds,
1983 const game_state *oldstate, const game_state *state,
1984 int dir, const game_ui *ui,
1985 float animtime, float flashtime)
1986{
1987 const int w = state->shared->params.w;
1988 const int h = state->shared->params.h;
1989
1990 const int flashy =
1991 flashtime > 0 &&
1992 (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3);
1993
1994 if (!ds->started) {
1995 /*
1996 * The initial contents of the window are not guaranteed and
1997 * can vary with front ends. To be on the safe side, all games
1998 * should start by drawing a big background-colour rectangle
1999 * covering the whole window.
2000 */
2001 draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER,
2002 COL_BACKGROUND);
2003
2004 /*
2005 * Smaller black rectangle which is the main grid.
2006 */
2007 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2008 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
2009 h*TILE_SIZE + 2*BORDER_WIDTH + 1,
2010 COL_GRID);
2011
2012 draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER);
2013
2014 ds->started = TRUE;
2015 }
2016
2017 draw_grid(dr, ds, state, ui, flashy, TRUE, TRUE);
2018}
2019
2020static float game_anim_length(const game_state *oldstate,
2021 const game_state *newstate, int dir, game_ui *ui)
2022{
2023 return 0.0F;
2024}
2025
2026static float game_flash_length(const game_state *oldstate,
2027 const game_state *newstate, int dir, game_ui *ui)
2028{
2029 assert(oldstate);
2030 assert(newstate);
2031 assert(newstate->shared);
2032 assert(oldstate->shared == newstate->shared);
2033 if (!oldstate->completed && newstate->completed &&
2034 !oldstate->cheated && !newstate->cheated)
2035 return FLASH_TIME;
2036 return 0.0F;
2037}
2038
2039static int game_status(const game_state *state)
2040{
2041 return state->completed ? +1 : 0;
2042}
2043
2044static int game_timing_state(const game_state *state, game_ui *ui)
2045{
2046 return TRUE;
2047}
2048
2049static void game_print_size(const game_params *params, float *x, float *y)
2050{
2051 int pw, ph;
2052
2053 /*
2054 * I'll use 6mm squares by default.
2055 */
2056 game_compute_size(params, 600, &pw, &ph);
2057 *x = pw / 100.0F;
2058 *y = ph / 100.0F;
2059}
2060
2061static void game_print(drawing *dr, const game_state *state, int tilesize)
2062{
2063 const int w = state->shared->params.w;
2064 const int h = state->shared->params.h;
2065 int c, i, borders;
2066
2067 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2068 game_drawstate *ds = game_new_drawstate(dr, state);
2069 game_set_size(dr, ds, NULL, tilesize);
2070
2071 c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
2072 c = print_mono_colour(dr, 0); assert(c == COL_GRID);
2073 c = print_mono_colour(dr, 1); assert(c == COL_HIGHLIGHT);
2074 c = print_mono_colour(dr, 1); assert(c == COL_CORRECT);
2075 c = print_mono_colour(dr, 1); assert(c == COL_ERROR);
2076 c = print_mono_colour(dr, 0); assert(c == COL_USER);
2077
2078 /*
2079 * Border.
2080 */
2081 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2082 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
2083 h*TILE_SIZE + 2*BORDER_WIDTH + 1,
2084 COL_GRID);
2085
2086 /*
2087 * We'll draw borders between the ominoes iff the grid is not
2088 * pristine. So scan it to see if it is.
2089 */
2090 borders = FALSE;
2091 for (i = 0; i < w*h; i++)
2092 if (state->board[i] && !state->shared->clues[i])
2093 borders = TRUE;
2094
2095 /*
2096 * Draw grid.
2097 */
2098 print_line_width(dr, TILE_SIZE / 64);
2099 draw_grid(dr, ds, state, NULL, FALSE, borders, FALSE);
2100
2101 /*
2102 * Clean up.
2103 */
2104 game_free_drawstate(dr, ds);
2105}
2106
2107#ifdef COMBINED
2108#define thegame filling
2109#endif
2110
2111const struct game thegame = {
2112 "Filling", "games.filling", "filling",
2113 default_params,
2114 game_fetch_preset, NULL,
2115 decode_params,
2116 encode_params,
2117 free_params,
2118 dup_params,
2119 TRUE, game_configure, custom_params,
2120 validate_params,
2121 new_game_desc,
2122 validate_desc,
2123 new_game,
2124 dup_game,
2125 free_game,
2126 TRUE, solve_game,
2127 TRUE, game_can_format_as_text_now, game_text_format,
2128 new_ui,
2129 free_ui,
2130 encode_ui,
2131 decode_ui,
2132 game_changed_state,
2133 interpret_move,
2134 execute_move,
2135 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2136 game_colours,
2137 game_new_drawstate,
2138 game_free_drawstate,
2139 game_redraw,
2140 game_anim_length,
2141 game_flash_length,
2142 game_status,
2143 TRUE, FALSE, game_print_size, game_print,
2144 FALSE, /* wants_statusbar */
2145 FALSE, game_timing_state,
2146 REQUIRE_NUMPAD, /* flags */
2147};
2148
2149#ifdef STANDALONE_SOLVER /* solver? hah! */
2150
2151int main(int argc, char **argv) {
2152 while (*++argv) {
2153 game_params *params;
2154 game_state *state;
2155 char *par;
2156 char *desc;
2157
2158 for (par = desc = *argv; *desc != '\0' && *desc != ':'; ++desc);
2159 if (*desc == '\0') {
2160 fprintf(stderr, "bad puzzle id: %s", par);
2161 continue;
2162 }
2163
2164 *desc++ = '\0';
2165
2166 params = snew(game_params);
2167 decode_params(params, par);
2168 state = new_game(NULL, params, desc);
2169 if (solver(state->board, params->w, params->h, NULL))
2170 printf("%s:%s: solvable\n", par, desc);
2171 else
2172 printf("%s:%s: not solvable\n", par, desc);
2173 }
2174 return 0;
2175}
2176
2177#endif
2178
2179/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/filling.html b/apps/plugins/puzzles/src/filling.html
new file mode 100644
index 0000000000..3ccbf06e4d
--- /dev/null
+++ b/apps/plugins/puzzles/src/filling.html
@@ -0,0 +1,50 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Filling</title>
7<link rel="previous" href="galaxies.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="keen.html">
12</head>
13<body>
14<p><a href="galaxies.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="keen.html">Next</a></p>
15<h1><a name="C29"></a>Chapter 29: <a name="i0"></a>Filling</h1>
16<p>
17You have a grid of squares, some of which contain digits, and the rest of which are empty. Your job is to fill in digits in the empty squares, in such a way that each connected region of squares all containing the same digit has an area equal to that digit.
18</p>
19<p>
20(&#8216;Connected region&#8217;, for the purposes of this game, does not count diagonally separated squares as adjacent.)
21</p>
22<p>
23For example, it follows that no square can contain a zero, and that two adjacent squares can not both contain a one. No region has an area greater than 9 (because then its area would not be a single digit).
24</p>
25<p>
26Credit for this puzzle goes to <a name="i1"></a>Nikoli <a href="#p0">[14]</a>.
27</p>
28<p>
29Filling was contributed to this collection by Jonas K&#246;lker.
30</p>
31<p><a name="p0"></a>
32[14] <a href="http://www.nikoli.co.jp/en/puzzles/fillomino.html"><code>http://www.nikoli.co.jp/en/puzzles/fillomino.html</code></a>
33</p>
34<h2><a name="S29.1"></a>29.1 <a name="i2"></a>Filling controls</h2>
35<p>
36To play Filling, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. By dragging the mouse, you can select multiple squares to fill with a single keypress. If you make a mistake, click the mouse in the incorrect square and press 0, Space, Backspace or Enter to clear it again (or use the Undo feature).
37</p>
38<p>
39You can also move around the grid with the cursor keys; typing a digit will fill the square containing the cursor with that number; typing 0 will clear it. You can also select multiple squares for numbering or clearing with the return and arrow keys, before typing a digit to fill or clear the highlighted squares (as above). The space bar adds and removes single squares to and from the selection. Backspace and escape remove all squares from the selection.
40</p>
41<p>
42(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
43</p>
44<h2><a name="S29.2"></a>29.2 <a name="i3"></a>Filling parameters</h2>
45<p>
46Filling allows you to configure the number of rows and columns of the grid, through the &#8216;Type&#8217; menu.
47</p>
48
49<hr><address></address></body>
50</html>
diff --git a/apps/plugins/puzzles/src/findloop.c b/apps/plugins/puzzles/src/findloop.c
new file mode 100644
index 0000000000..e6b2654cad
--- /dev/null
+++ b/apps/plugins/puzzles/src/findloop.c
@@ -0,0 +1,500 @@
1/*
2 * Routine for finding loops in graphs, reusable across multiple
3 * puzzles.
4 *
5 * The strategy is Tarjan's bridge-finding algorithm, which is
6 * designed to list all edges whose removal would disconnect a
7 * previously connected component of the graph. We're interested in
8 * exactly the reverse - edges that are part of a loop in the graph
9 * are precisely those which _wouldn't_ disconnect anything if removed
10 * (individually) - but of course flipping the sense of the output is
11 * easy.
12 */
13
14#include "puzzles.h"
15
16struct findloopstate {
17 int parent, child, sibling, visited;
18 int index, minindex, maxindex;
19 int minreachable, maxreachable;
20 int bridge;
21};
22
23struct findloopstate *findloop_new_state(int nvertices)
24{
25 /*
26 * Allocate a findloopstate structure for each vertex, and one
27 * extra one at the end which will be the overall root of a
28 * 'super-tree', which links the whole graph together to make it
29 * as easy as possible to iterate over all the connected
30 * components.
31 */
32 return snewn(nvertices + 1, struct findloopstate);
33}
34
35void findloop_free_state(struct findloopstate *state)
36{
37 sfree(state);
38}
39
40int findloop_is_loop_edge(struct findloopstate *pv, int u, int v)
41{
42 /*
43 * Since the algorithm is intended for finding bridges, and a
44 * bridge must be part of any spanning tree, it follows that there
45 * is at most one bridge per vertex.
46 *
47 * Furthermore, by finding a _rooted_ spanning tree (so that each
48 * bridge is a parent->child link), you can find an injection from
49 * bridges to vertices (namely, map each bridge to the vertex at
50 * its child end).
51 *
52 * So if the u-v edge is a bridge, then either v was u's parent
53 * when the algorithm ran and we set pv[u].bridge = v, or vice
54 * versa.
55 */
56 return !(pv[u].bridge == v || pv[v].bridge == u);
57}
58
59int findloop_run(struct findloopstate *pv, int nvertices,
60 neighbour_fn_t neighbour, void *ctx)
61{
62 int u, v, w, root, index;
63 int nbridges, nedges;
64
65 root = nvertices;
66
67 /*
68 * First pass: organise the graph into a rooted spanning forest.
69 * That is, a tree structure with a clear up/down orientation -
70 * every node has exactly one parent (which may be 'root') and
71 * zero or more children, and every parent-child link corresponds
72 * to a graph edge.
73 *
74 * (A side effect of this is to find all the connected components,
75 * which of course we could do less confusingly with a dsf - but
76 * then we'd have to do that *and* build the tree, so it's less
77 * effort to do it all at once.)
78 */
79 for (v = 0; v <= nvertices; v++) {
80 pv[v].parent = root;
81 pv[v].child = -2;
82 pv[v].sibling = -1;
83 pv[v].visited = FALSE;
84 }
85 pv[root].child = -1;
86 nedges = 0;
87 debug(("------------- new find_loops, nvertices=%d\n", nvertices));
88 for (v = 0; v < nvertices; v++) {
89 if (pv[v].parent == root) {
90 /*
91 * Found a new connected component. Enumerate and treeify
92 * it.
93 */
94 pv[v].sibling = pv[root].child;
95 pv[root].child = v;
96 debug(("%d is new child of root\n", v));
97
98 u = v;
99 while (1) {
100 if (!pv[u].visited) {
101 pv[u].visited = TRUE;
102
103 /*
104 * Enumerate the neighbours of u, and any that are
105 * as yet not in the tree structure (indicated by
106 * child==-2, and distinct from the 'visited'
107 * flag) become children of u.
108 */
109 debug((" component pass: processing %d\n", u));
110 for (w = neighbour(u, ctx); w >= 0;
111 w = neighbour(-1, ctx)) {
112 debug((" edge %d-%d\n", u, w));
113 if (pv[w].child == -2) {
114 debug((" -> new child\n"));
115 pv[w].child = -1;
116 pv[w].sibling = pv[u].child;
117 pv[w].parent = u;
118 pv[u].child = w;
119 }
120
121 /* While we're here, count the edges in the whole
122 * graph, so that we can easily check at the end
123 * whether all of them are bridges, i.e. whether
124 * no loop exists at all. */
125 if (w > u) /* count each edge only in one direction */
126 nedges++;
127 }
128
129 /*
130 * Now descend in depth-first search.
131 */
132 if (pv[u].child >= 0) {
133 u = pv[u].child;
134 debug((" descending to %d\n", u));
135 continue;
136 }
137 }
138
139 if (u == v) {
140 debug((" back at %d, done this component\n", u));
141 break;
142 } else if (pv[u].sibling >= 0) {
143 u = pv[u].sibling;
144 debug((" sideways to %d\n", u));
145 } else {
146 u = pv[u].parent;
147 debug((" ascending to %d\n", u));
148 }
149 }
150 }
151 }
152
153 /*
154 * Second pass: index all the vertices in such a way that every
155 * subtree has a contiguous range of indices. (Easily enough done,
156 * by iterating through the tree structure we just built and
157 * numbering its elements as if they were those of a sorted list.)
158 *
159 * For each vertex, we compute the min and max index of the
160 * subtree starting there.
161 *
162 * (We index the vertices in preorder, per Tarjan's original
163 * description, so that each vertex's min subtree index is its own
164 * index; but that doesn't actually matter; either way round would
165 * do. The important thing is that we have a simple arithmetic
166 * criterion that tells us whether a vertex is in a given subtree
167 * or not.)
168 */
169 debug(("--- begin indexing pass\n"));
170 index = 0;
171 for (v = 0; v < nvertices; v++)
172 pv[v].visited = FALSE;
173 pv[root].visited = TRUE;
174 u = pv[root].child;
175 while (1) {
176 if (!pv[u].visited) {
177 pv[u].visited = TRUE;
178
179 /*
180 * Index this node.
181 */
182 pv[u].minindex = pv[u].index = index;
183 debug((" vertex %d <- index %d\n", u, index));
184 index++;
185
186 /*
187 * Now descend in depth-first search.
188 */
189 if (pv[u].child >= 0) {
190 u = pv[u].child;
191 debug((" descending to %d\n", u));
192 continue;
193 }
194 }
195
196 if (u == root) {
197 debug((" back at %d, done indexing\n", u));
198 break;
199 }
200
201 /*
202 * As we re-ascend to here from its children (or find that we
203 * had no children to descend to in the first place), fill in
204 * its maxindex field.
205 */
206 pv[u].maxindex = index-1;
207 debug((" vertex %d <- maxindex %d\n", u, pv[u].maxindex));
208
209 if (pv[u].sibling >= 0) {
210 u = pv[u].sibling;
211 debug((" sideways to %d\n", u));
212 } else {
213 u = pv[u].parent;
214 debug((" ascending to %d\n", u));
215 }
216 }
217
218 /*
219 * We're ready to generate output now, so initialise the output
220 * fields.
221 */
222 for (v = 0; v < nvertices; v++)
223 pv[v].bridge = -1;
224
225 /*
226 * Final pass: determine the min and max index of the vertices
227 * reachable from every subtree, not counting the link back to
228 * each vertex's parent. Then our criterion is: given a vertex u,
229 * defining a subtree consisting of u and all its descendants, we
230 * compare the range of vertex indices _in_ that subtree (which is
231 * just the minindex and maxindex of u) with the range of vertex
232 * indices in the _neighbourhood_ of the subtree (computed in this
233 * final pass, and not counting u's own edge to its parent), and
234 * if the latter includes anything outside the former, then there
235 * must be some path from u to outside its subtree which does not
236 * go through the parent edge - i.e. the edge from u to its parent
237 * is part of a loop.
238 */
239 debug(("--- begin min-max pass\n"));
240 nbridges = 0;
241 for (v = 0; v < nvertices; v++)
242 pv[v].visited = FALSE;
243 u = pv[root].child;
244 pv[root].visited = TRUE;
245 while (1) {
246 if (!pv[u].visited) {
247 pv[u].visited = TRUE;
248
249 /*
250 * Look for vertices reachable directly from u, including
251 * u itself.
252 */
253 debug((" processing vertex %d\n", u));
254 pv[u].minreachable = pv[u].maxreachable = pv[u].minindex;
255 for (w = neighbour(u, ctx); w >= 0; w = neighbour(-1, ctx)) {
256 debug((" edge %d-%d\n", u, w));
257 if (w != pv[u].parent) {
258 int i = pv[w].index;
259 if (pv[u].minreachable > i)
260 pv[u].minreachable = i;
261 if (pv[u].maxreachable < i)
262 pv[u].maxreachable = i;
263 }
264 }
265 debug((" initial min=%d max=%d\n",
266 pv[u].minreachable, pv[u].maxreachable));
267
268 /*
269 * Now descend in depth-first search.
270 */
271 if (pv[u].child >= 0) {
272 u = pv[u].child;
273 debug((" descending to %d\n", u));
274 continue;
275 }
276 }
277
278 if (u == root) {
279 debug((" back at %d, done min-maxing\n", u));
280 break;
281 }
282
283 /*
284 * As we re-ascend to this vertex, go back through its
285 * immediate children and do a post-update of its min/max.
286 */
287 for (v = pv[u].child; v >= 0; v = pv[v].sibling) {
288 if (pv[u].minreachable > pv[v].minreachable)
289 pv[u].minreachable = pv[v].minreachable;
290 if (pv[u].maxreachable < pv[v].maxreachable)
291 pv[u].maxreachable = pv[v].maxreachable;
292 }
293
294 debug((" postorder update of %d: min=%d max=%d (indices %d-%d)\n", u,
295 pv[u].minreachable, pv[u].maxreachable,
296 pv[u].minindex, pv[u].maxindex));
297
298 /*
299 * And now we know whether each to our own parent is a bridge.
300 */
301 if ((v = pv[u].parent) != root) {
302 if (pv[u].minreachable >= pv[u].minindex &&
303 pv[u].maxreachable <= pv[u].maxindex) {
304 /* Yes, it's a bridge. */
305 pv[u].bridge = v;
306 nbridges++;
307 debug((" %d-%d is a bridge\n", v, u));
308 } else {
309 debug((" %d-%d is not a bridge\n", v, u));
310 }
311 }
312
313 if (pv[u].sibling >= 0) {
314 u = pv[u].sibling;
315 debug((" sideways to %d\n", u));
316 } else {
317 u = pv[u].parent;
318 debug((" ascending to %d\n", u));
319 }
320 }
321
322 debug(("finished, nedges=%d nbridges=%d\n", nedges, nbridges));
323
324 /*
325 * Done.
326 */
327 return nbridges < nedges;
328}
329
330/*
331 * Appendix: the long and painful history of loop detection in these puzzles
332 * =========================================================================
333 *
334 * For interest, I thought I'd write up the five loop-finding methods
335 * I've gone through before getting to this algorithm. It's a case
336 * study in all the ways you can solve this particular problem
337 * wrongly, and also how much effort you can waste by not managing to
338 * find the existing solution in the literature :-(
339 *
340 * Vertex dsf
341 * ----------
342 *
343 * Initially, in puzzles where you need to not have any loops in the
344 * solution graph, I detected them by using a dsf to track connected
345 * components of vertices. Iterate over each edge unifying the two
346 * vertices it connects; but before that, check if the two vertices
347 * are _already_ known to be connected. If so, then the new edge is
348 * providing a second path between them, i.e. a loop exists.
349 *
350 * That's adequate for automated solvers, where you just need to know
351 * _whether_ a loop exists, so as to rule out that move and do
352 * something else. But during play, you want to do better than that:
353 * you want to _point out_ the loops with error highlighting.
354 *
355 * Graph pruning
356 * -------------
357 *
358 * So my second attempt worked by iteratively pruning the graph. Find
359 * a vertex with degree 1; remove that edge; repeat until you can't
360 * find such a vertex any more. This procedure will remove *every*
361 * edge of the graph if and only if there were no loops; so if there
362 * are any edges remaining, highlight them.
363 *
364 * This successfully highlights loops, but not _only_ loops. If the
365 * graph contains a 'dumb-bell' shaped subgraph consisting of two
366 * loops connected by a path, then we'll end up highlighting the
367 * connecting path as well as the loops. That's not what we wanted.
368 *
369 * Vertex dsf with ad-hoc loop tracing
370 * -----------------------------------
371 *
372 * So my third attempt was to go back to the dsf strategy, only this
373 * time, when you detect that a particular edge connects two
374 * already-connected vertices (and hence is part of a loop), you try
375 * to trace round that loop to highlight it - before adding the new
376 * edge, search for a path between its endpoints among the edges the
377 * algorithm has already visited, and when you find one (which you
378 * must), highlight the loop consisting of that path plus the new
379 * edge.
380 *
381 * This solves the dumb-bell problem - we definitely now cannot
382 * accidentally highlight any edge that is *not* part of a loop. But
383 * it's far from clear that we'll highlight *every* edge that *is*
384 * part of a loop - what if there were multiple paths between the two
385 * vertices? It would be difficult to guarantee that we'd always catch
386 * every single one.
387 *
388 * On the other hand, it is at least guaranteed that we'll highlight
389 * _something_ if any loop exists, and in other error highlighting
390 * situations (see in particular the Tents connected component
391 * analysis) I've been known to consider that sufficient. So this
392 * version hung around for quite a while, until I had a better idea.
393 *
394 * Face dsf
395 * --------
396 *
397 * Round about the time Loopy was being revamped to include non-square
398 * grids, I had a much cuter idea, making use of the fact that the
399 * graph is planar, and hence has a concept of faces.
400 *
401 * In Loopy, there are really two graphs: the 'grid', consisting of
402 * all the edges that the player *might* fill in, and the solution
403 * graph of the edges the player actually *has* filled in. The
404 * algorithm is: set up a dsf on the *faces* of the grid. Iterate over
405 * each edge of the grid which is _not_ marked by the player as an
406 * edge of the solution graph, unifying the faces on either side of
407 * that edge. This groups the faces into connected components. Now,
408 * there is more than one connected component iff a loop exists, and
409 * moreover, an edge of the solution graph is part of a loop iff the
410 * faces on either side of it are in different connected components!
411 *
412 * This is the first algorithm I came up with that I was confident
413 * would successfully highlight exactly the correct set of edges in
414 * all cases. It's also conceptually elegant, and very easy to
415 * implement and to be confident you've got it right (since it just
416 * consists of two very simple loops over the edge set, one building
417 * the dsf and one reading it off). I was very pleased with it.
418 *
419 * Doing the same thing in Slant is slightly more difficult because
420 * the set of edges the user can fill in do not form a planar graph
421 * (the two potential edges in each square cross in the middle). But
422 * you can still apply the same principle by considering the 'faces'
423 * to be diamond-shaped regions of space around each horizontal or
424 * vertical grid line. Equivalently, pretend each edge added by the
425 * player is really divided into two edges, each from a square-centre
426 * to one of the square's corners, and now the grid graph is planar
427 * again.
428 *
429 * However, it fell down when - much later - I tried to implement the
430 * same algorithm in Net.
431 *
432 * Net doesn't *absolutely need* loop detection, because of its system
433 * of highlighting squares connected to the source square: an argument
434 * involving counting vertex degrees shows that if any loop exists,
435 * then it must be counterbalanced by some disconnected square, so
436 * there will be _some_ error highlight in any invalid grid even
437 * without loop detection. However, in large complicated cases, it's
438 * still nice to highlight the loop itself, so that once the player is
439 * clued in to its existence by a disconnected square elsewhere, they
440 * don't have to spend forever trying to find it.
441 *
442 * The new wrinkle in Net, compared to other loop-disallowing puzzles,
443 * is that it can be played with wrapping walls, or - topologically
444 * speaking - on a torus. And a torus has a property that algebraic
445 * topologists would know of as a 'non-trivial H_1 homology group',
446 * which essentially means that there can exist a loop on a torus
447 * which *doesn't* separate the surface into two regions disconnected
448 * from each other.
449 *
450 * In other words, using this algorithm in Net will do fine at finding
451 * _small_ localised loops, but a large-scale loop that goes (say) off
452 * the top of the grid, back on at the bottom, and meets up in the
453 * middle again will not be detected.
454 *
455 * Footpath dsf
456 * ------------
457 *
458 * To solve this homology problem in Net, I hastily thought up another
459 * dsf-based algorithm.
460 *
461 * This time, let's consider each edge of the graph to be a road, with
462 * a separate pedestrian footpath down each side. We'll form a dsf on
463 * those imaginary segments of footpath.
464 *
465 * At each vertex of the graph, we go round the edges leaving that
466 * vertex, in order around the vertex. For each pair of edges adjacent
467 * in this order, we unify their facing pair of footpaths (e.g. if
468 * edge E appears anticlockwise of F, then we unify the anticlockwise
469 * footpath of F with the clockwise one of E) . In particular, if a
470 * vertex has degree 1, then the two footpaths on either side of its
471 * single edge are unified.
472 *
473 * Then, an edge is part of a loop iff its two footpaths are not
474 * reachable from one another.
475 *
476 * This algorithm is almost as simple to implement as the face dsf,
477 * and it works on a wider class of graphs embedded in plane-like
478 * surfaces; in particular, it fixes the torus bug in the face-dsf
479 * approach. However, it still depends on the graph having _some_ sort
480 * of embedding in a 2-manifold, because it relies on there being a
481 * meaningful notion of 'order of edges around a vertex' in the first
482 * place, so you couldn't use it on a wildly nonplanar graph like the
483 * diamond lattice. Also, more subtly, it depends on the graph being
484 * embedded in an _orientable_ surface - and that's a thing that might
485 * much more plausibly change in future puzzles, because it's not at
486 * all unlikely that at some point I might feel moved to implement a
487 * puzzle that can be played on the surface of a Mobius strip or a
488 * Klein bottle. And then even this algorithm won't work.
489 *
490 * Tarjan's bridge-finding algorithm
491 * ---------------------------------
492 *
493 * And so, finally, we come to the algorithm above. This one is pure
494 * graph theory: it doesn't depend on any concept of 'faces', or 'edge
495 * ordering around a vertex', or any other trapping of a planar or
496 * quasi-planar graph embedding. It should work on any graph
497 * whatsoever, and reliably identify precisely the set of edges that
498 * form part of some loop. So *hopefully* this long string of failures
499 * has finally come to an end...
500 */
diff --git a/apps/plugins/puzzles/src/flip.R b/apps/plugins/puzzles/src/flip.R
new file mode 100644
index 0000000000..03241f015b
--- /dev/null
+++ b/apps/plugins/puzzles/src/flip.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3FLIP_EXTRA = tree234
4
5flip : [X] GTK COMMON flip FLIP_EXTRA flip-icon|no-icon
6
7flip : [G] WINDOWS COMMON flip FLIP_EXTRA flip.res|noicon.res
8
9ALL += flip[COMBINED] FLIP_EXTRA
10
11!begin am gtk
12GAMES += flip
13!end
14
15!begin >list.c
16 A(flip) \
17!end
18
19!begin >gamedesc.txt
20flip:flip.exe:Flip:Tile inversion puzzle:Flip groups of squares to light them all up at once.
21!end
diff --git a/apps/plugins/puzzles/src/flip.c b/apps/plugins/puzzles/src/flip.c
new file mode 100644
index 0000000000..c7126fb7d9
--- /dev/null
+++ b/apps/plugins/puzzles/src/flip.c
@@ -0,0 +1,1349 @@
1/*
2 * flip.c: Puzzle involving lighting up all the squares on a grid,
3 * where each click toggles an overlapping set of lights.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <assert.h>
10#include <ctype.h>
11#include <math.h>
12
13#include "puzzles.h"
14#include "tree234.h"
15
16enum {
17 COL_BACKGROUND,
18 COL_WRONG,
19 COL_RIGHT,
20 COL_GRID,
21 COL_DIAG,
22 COL_HINT,
23 COL_CURSOR,
24 NCOLOURS
25};
26
27#define PREFERRED_TILE_SIZE 48
28#define TILE_SIZE (ds->tilesize)
29#define BORDER (TILE_SIZE / 2)
30#define COORD(x) ( (x) * TILE_SIZE + BORDER )
31#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
32
33#define ANIM_TIME 0.25F
34#define FLASH_FRAME 0.07F
35
36/*
37 * Possible ways to decide which lights are toggled by each click.
38 * Essentially, each of these describes a means of inventing a
39 * matrix over GF(2).
40 */
41enum {
42 CROSSES, RANDOM
43};
44
45struct game_params {
46 int w, h;
47 int matrix_type;
48};
49
50/*
51 * This structure is shared between all the game_states describing
52 * a particular game, so it's reference-counted.
53 */
54struct matrix {
55 int refcount;
56 unsigned char *matrix; /* array of (w*h) by (w*h) */
57};
58
59struct game_state {
60 int w, h;
61 int moves, completed, cheated, hints_active;
62 unsigned char *grid; /* array of w*h */
63 struct matrix *matrix;
64};
65
66static game_params *default_params(void)
67{
68 game_params *ret = snew(game_params);
69
70 ret->w = ret->h = 5;
71 ret->matrix_type = CROSSES;
72
73 return ret;
74}
75
76static const struct game_params flip_presets[] = {
77 {3, 3, CROSSES},
78 {4, 4, CROSSES},
79 {5, 5, CROSSES},
80 {3, 3, RANDOM},
81 {4, 4, RANDOM},
82 {5, 5, RANDOM},
83};
84
85static int game_fetch_preset(int i, char **name, game_params **params)
86{
87 game_params *ret;
88 char str[80];
89
90 if (i < 0 || i >= lenof(flip_presets))
91 return FALSE;
92
93 ret = snew(game_params);
94 *ret = flip_presets[i];
95
96 sprintf(str, "%dx%d %s", ret->w, ret->h,
97 ret->matrix_type == CROSSES ? "Crosses" : "Random");
98
99 *name = dupstr(str);
100 *params = ret;
101 return TRUE;
102}
103
104static void free_params(game_params *params)
105{
106 sfree(params);
107}
108
109static game_params *dup_params(const game_params *params)
110{
111 game_params *ret = snew(game_params);
112 *ret = *params; /* structure copy */
113 return ret;
114}
115
116static void decode_params(game_params *ret, char const *string)
117{
118 ret->w = ret->h = atoi(string);
119 while (*string && isdigit((unsigned char)*string)) string++;
120 if (*string == 'x') {
121 string++;
122 ret->h = atoi(string);
123 while (*string && isdigit((unsigned char)*string)) string++;
124 }
125 if (*string == 'r') {
126 string++;
127 ret->matrix_type = RANDOM;
128 } else if (*string == 'c') {
129 string++;
130 ret->matrix_type = CROSSES;
131 }
132}
133
134static char *encode_params(const game_params *params, int full)
135{
136 char data[256];
137
138 sprintf(data, "%dx%d%s", params->w, params->h,
139 !full ? "" : params->matrix_type == CROSSES ? "c" : "r");
140
141 return dupstr(data);
142}
143
144static config_item *game_configure(const game_params *params)
145{
146 config_item *ret = snewn(4, config_item);
147 char buf[80];
148
149 ret[0].name = "Width";
150 ret[0].type = C_STRING;
151 sprintf(buf, "%d", params->w);
152 ret[0].sval = dupstr(buf);
153 ret[0].ival = 0;
154
155 ret[1].name = "Height";
156 ret[1].type = C_STRING;
157 sprintf(buf, "%d", params->h);
158 ret[1].sval = dupstr(buf);
159 ret[1].ival = 0;
160
161 ret[2].name = "Shape type";
162 ret[2].type = C_CHOICES;
163 ret[2].sval = ":Crosses:Random";
164 ret[2].ival = params->matrix_type;
165
166 ret[3].name = NULL;
167 ret[3].type = C_END;
168 ret[3].sval = NULL;
169 ret[3].ival = 0;
170
171 return ret;
172}
173
174static game_params *custom_params(const config_item *cfg)
175{
176 game_params *ret = snew(game_params);
177
178 ret->w = atoi(cfg[0].sval);
179 ret->h = atoi(cfg[1].sval);
180 ret->matrix_type = cfg[2].ival;
181
182 return ret;
183}
184
185static char *validate_params(const game_params *params, int full)
186{
187 if (params->w <= 0 || params->h <= 0)
188 return "Width and height must both be greater than zero";
189 return NULL;
190}
191
192static char *encode_bitmap(unsigned char *bmp, int len)
193{
194 int slen = (len + 3) / 4;
195 char *ret;
196 int i;
197
198 ret = snewn(slen + 1, char);
199 for (i = 0; i < slen; i++) {
200 int j, v;
201 v = 0;
202 for (j = 0; j < 4; j++)
203 if (i*4+j < len && bmp[i*4+j])
204 v |= 8 >> j;
205 ret[i] = "0123456789abcdef"[v];
206 }
207 ret[slen] = '\0';
208 return ret;
209}
210
211static void decode_bitmap(unsigned char *bmp, int len, const char *hex)
212{
213 int slen = (len + 3) / 4;
214 int i;
215
216 for (i = 0; i < slen; i++) {
217 int j, v, c = hex[i];
218 if (c >= '0' && c <= '9')
219 v = c - '0';
220 else if (c >= 'A' && c <= 'F')
221 v = c - 'A' + 10;
222 else if (c >= 'a' && c <= 'f')
223 v = c - 'a' + 10;
224 else
225 v = 0; /* shouldn't happen */
226 for (j = 0; j < 4; j++) {
227 if (i*4+j < len) {
228 if (v & (8 >> j))
229 bmp[i*4+j] = 1;
230 else
231 bmp[i*4+j] = 0;
232 }
233 }
234 }
235}
236
237/*
238 * Structure used during random matrix generation, and a compare
239 * function to permit storage in a tree234.
240 */
241struct sq {
242 int cx, cy; /* coords of click square */
243 int x, y; /* coords of output square */
244 /*
245 * Number of click squares which currently affect this output
246 * square.
247 */
248 int coverage;
249 /*
250 * Number of output squares currently affected by this click
251 * square.
252 */
253 int ominosize;
254};
255#define SORT(field) do { \
256 if (a->field < b->field) \
257 return -1; \
258 else if (a->field > b->field) \
259 return +1; \
260} while (0)
261/*
262 * Compare function for choosing the next square to add. We must
263 * sort by coverage, then by omino size, then everything else.
264 */
265static int sqcmp_pick(void *av, void *bv)
266{
267 struct sq *a = (struct sq *)av;
268 struct sq *b = (struct sq *)bv;
269 SORT(coverage);
270 SORT(ominosize);
271 SORT(cy);
272 SORT(cx);
273 SORT(y);
274 SORT(x);
275 return 0;
276}
277/*
278 * Compare function for adjusting the coverage figures after a
279 * change. We sort first by coverage and output square, then by
280 * everything else.
281 */
282static int sqcmp_cov(void *av, void *bv)
283{
284 struct sq *a = (struct sq *)av;
285 struct sq *b = (struct sq *)bv;
286 SORT(coverage);
287 SORT(y);
288 SORT(x);
289 SORT(ominosize);
290 SORT(cy);
291 SORT(cx);
292 return 0;
293}
294/*
295 * Compare function for adjusting the omino sizes after a change.
296 * We sort first by omino size and input square, then by everything
297 * else.
298 */
299static int sqcmp_osize(void *av, void *bv)
300{
301 struct sq *a = (struct sq *)av;
302 struct sq *b = (struct sq *)bv;
303 SORT(ominosize);
304 SORT(cy);
305 SORT(cx);
306 SORT(coverage);
307 SORT(y);
308 SORT(x);
309 return 0;
310}
311static void addsq(tree234 *t, int w, int h, int cx, int cy,
312 int x, int y, unsigned char *matrix)
313{
314 int wh = w * h;
315 struct sq *sq;
316 int i;
317
318 if (x < 0 || x >= w || y < 0 || y >= h)
319 return;
320 if (abs(x-cx) > 1 || abs(y-cy) > 1)
321 return;
322 if (matrix[(cy*w+cx) * wh + y*w+x])
323 return;
324
325 sq = snew(struct sq);
326 sq->cx = cx;
327 sq->cy = cy;
328 sq->x = x;
329 sq->y = y;
330 sq->coverage = sq->ominosize = 0;
331 for (i = 0; i < wh; i++) {
332 if (matrix[i * wh + y*w+x])
333 sq->coverage++;
334 if (matrix[(cy*w+cx) * wh + i])
335 sq->ominosize++;
336 }
337
338 if (add234(t, sq) != sq)
339 sfree(sq); /* already there */
340}
341static void addneighbours(tree234 *t, int w, int h, int cx, int cy,
342 int x, int y, unsigned char *matrix)
343{
344 addsq(t, w, h, cx, cy, x-1, y, matrix);
345 addsq(t, w, h, cx, cy, x+1, y, matrix);
346 addsq(t, w, h, cx, cy, x, y-1, matrix);
347 addsq(t, w, h, cx, cy, x, y+1, matrix);
348}
349
350static char *new_game_desc(const game_params *params, random_state *rs,
351 char **aux, int interactive)
352{
353 int w = params->w, h = params->h, wh = w * h;
354 int i, j;
355 unsigned char *matrix, *grid;
356 char *mbmp, *gbmp, *ret;
357
358 matrix = snewn(wh * wh, unsigned char);
359 grid = snewn(wh, unsigned char);
360
361 /*
362 * First set up the matrix.
363 */
364 switch (params->matrix_type) {
365 case CROSSES:
366 for (i = 0; i < wh; i++) {
367 int ix = i % w, iy = i / w;
368 for (j = 0; j < wh; j++) {
369 int jx = j % w, jy = j / w;
370 if (abs(jx - ix) + abs(jy - iy) <= 1)
371 matrix[i*wh+j] = 1;
372 else
373 matrix[i*wh+j] = 0;
374 }
375 }
376 break;
377 case RANDOM:
378 while (1) {
379 tree234 *pick, *cov, *osize;
380 int limit;
381
382 pick = newtree234(sqcmp_pick);
383 cov = newtree234(sqcmp_cov);
384 osize = newtree234(sqcmp_osize);
385
386 memset(matrix, 0, wh * wh);
387 for (i = 0; i < wh; i++) {
388 matrix[i*wh+i] = 1;
389 }
390
391 for (i = 0; i < wh; i++) {
392 int ix = i % w, iy = i / w;
393 addneighbours(pick, w, h, ix, iy, ix, iy, matrix);
394 addneighbours(cov, w, h, ix, iy, ix, iy, matrix);
395 addneighbours(osize, w, h, ix, iy, ix, iy, matrix);
396 }
397
398 /*
399 * Repeatedly choose a square to add to the matrix,
400 * until we have enough. I'll arbitrarily choose our
401 * limit to be the same as the total number of set bits
402 * in the crosses matrix.
403 */
404 limit = 4*wh - 2*(w+h); /* centre squares already present */
405
406 while (limit-- > 0) {
407 struct sq *sq, *sq2, sqlocal;
408 int k;
409
410 /*
411 * Find the lowest element in the pick tree.
412 */
413 sq = index234(pick, 0);
414
415 /*
416 * Find the highest element with the same coverage
417 * and omino size, by setting all other elements to
418 * lots.
419 */
420 sqlocal = *sq;
421 sqlocal.cx = sqlocal.cy = sqlocal.x = sqlocal.y = wh;
422 sq = findrelpos234(pick, &sqlocal, NULL, REL234_LT, &k);
423 assert(sq != 0);
424
425 /*
426 * Pick at random from all elements up to k of the
427 * pick tree.
428 */
429 k = random_upto(rs, k+1);
430 sq = delpos234(pick, k);
431 del234(cov, sq);
432 del234(osize, sq);
433
434 /*
435 * Add this square to the matrix.
436 */
437 matrix[(sq->cy * w + sq->cx) * wh + (sq->y * w + sq->x)] = 1;
438
439 /*
440 * Correct the matrix coverage field of any sq
441 * which points at this output square.
442 */
443 sqlocal = *sq;
444 sqlocal.cx = sqlocal.cy = sqlocal.ominosize = -1;
445 while ((sq2 = findrel234(cov, &sqlocal, NULL,
446 REL234_GT)) != NULL &&
447 sq2->coverage == sq->coverage &&
448 sq2->x == sq->x && sq2->y == sq->y) {
449 del234(pick, sq2);
450 del234(cov, sq2);
451 del234(osize, sq2);
452 sq2->coverage++;
453 add234(pick, sq2);
454 add234(cov, sq2);
455 add234(osize, sq2);
456 }
457
458 /*
459 * Correct the omino size field of any sq which
460 * points at this input square.
461 */
462 sqlocal = *sq;
463 sqlocal.x = sqlocal.y = sqlocal.coverage = -1;
464 while ((sq2 = findrel234(osize, &sqlocal, NULL,
465 REL234_GT)) != NULL &&
466 sq2->ominosize == sq->ominosize &&
467 sq2->cx == sq->cx && sq2->cy == sq->cy) {
468 del234(pick, sq2);
469 del234(cov, sq2);
470 del234(osize, sq2);
471 sq2->ominosize++;
472 add234(pick, sq2);
473 add234(cov, sq2);
474 add234(osize, sq2);
475 }
476
477 /*
478 * The sq we actually picked out of the tree is
479 * finished with; but its neighbours now need to
480 * appear.
481 */
482 addneighbours(pick, w,h, sq->cx,sq->cy, sq->x,sq->y, matrix);
483 addneighbours(cov, w,h, sq->cx,sq->cy, sq->x,sq->y, matrix);
484 addneighbours(osize, w,h, sq->cx,sq->cy, sq->x,sq->y, matrix);
485 sfree(sq);
486 }
487
488 /*
489 * Free all remaining sq structures.
490 */
491 {
492 struct sq *sq;
493 while ((sq = delpos234(pick, 0)) != NULL)
494 sfree(sq);
495 }
496 freetree234(pick);
497 freetree234(cov);
498 freetree234(osize);
499
500 /*
501 * Finally, check to see if any two matrix rows are
502 * exactly identical. If so, this is not an acceptable
503 * matrix, and we give up and go round again.
504 *
505 * I haven't been immediately able to think of a
506 * plausible means of algorithmically avoiding this
507 * situation (by, say, making a small perturbation to
508 * an offending matrix), so for the moment I'm just
509 * going to deal with it by throwing the whole thing
510 * away. I suspect this will lead to scalability
511 * problems (since most of the things happening in
512 * these matrices are local, the chance of _some_
513 * neighbourhood having two identical regions will
514 * increase with the grid area), but so far this puzzle
515 * seems to be really hard at large sizes so I'm not
516 * massively worried yet. Anyone needs this done
517 * better, they're welcome to submit a patch.
518 */
519 for (i = 0; i < wh; i++) {
520 for (j = 0; j < wh; j++)
521 if (i != j &&
522 !memcmp(matrix + i * wh, matrix + j * wh, wh))
523 break;
524 if (j < wh)
525 break;
526 }
527 if (i == wh)
528 break; /* no matches found */
529 }
530 break;
531 }
532
533 /*
534 * Now invent a random initial set of lights.
535 *
536 * At first glance it looks as if it might be quite difficult
537 * to choose equiprobably from all soluble light sets. After
538 * all, soluble light sets are those in the image space of the
539 * transformation matrix; so first we'd have to identify that
540 * space and its dimension, then pick a random coordinate for
541 * each basis vector and recombine. Lot of fiddly matrix
542 * algebra there.
543 *
544 * However, vector spaces are nicely orthogonal and relieve us
545 * of all that difficulty. For every point in the image space,
546 * there are precisely as many points in the input space that
547 * map to it as there are elements in the kernel of the
548 * transformation matrix (because adding any kernel element to
549 * the input does not change the output, and because any two
550 * inputs mapping to the same output must differ by an element
551 * of the kernel because that's what the kernel _is_); and
552 * these cosets are all disjoint (obviously, since no input
553 * point can map to more than one output point) and cover the
554 * whole space (equally obviously, because no input point can
555 * map to fewer than one output point!).
556 *
557 * So the input space contains the same number of points for
558 * each point in the output space; thus, we can simply choose
559 * equiprobably from elements of the _input_ space, and filter
560 * the result through the transformation matrix in the obvious
561 * way, and we thereby guarantee to choose equiprobably from
562 * all the output points. Phew!
563 */
564 while (1) {
565 memset(grid, 0, wh);
566 for (i = 0; i < wh; i++) {
567 int v = random_upto(rs, 2);
568 if (v) {
569 for (j = 0; j < wh; j++)
570 grid[j] ^= matrix[i*wh+j];
571 }
572 }
573 /*
574 * Ensure we don't have the starting state already!
575 */
576 for (i = 0; i < wh; i++)
577 if (grid[i])
578 break;
579 if (i < wh)
580 break;
581 }
582
583 /*
584 * Now encode the matrix and the starting grid as a game
585 * description. We'll do this by concatenating two great big
586 * hex bitmaps.
587 */
588 mbmp = encode_bitmap(matrix, wh*wh);
589 gbmp = encode_bitmap(grid, wh);
590 ret = snewn(strlen(mbmp) + strlen(gbmp) + 2, char);
591 sprintf(ret, "%s,%s", mbmp, gbmp);
592 sfree(mbmp);
593 sfree(gbmp);
594 sfree(matrix);
595 sfree(grid);
596 return ret;
597}
598
599static char *validate_desc(const game_params *params, const char *desc)
600{
601 int w = params->w, h = params->h, wh = w * h;
602 int mlen = (wh*wh+3)/4, glen = (wh+3)/4;
603
604 if (strspn(desc, "0123456789abcdefABCDEF") != mlen)
605 return "Matrix description is wrong length";
606 if (desc[mlen] != ',')
607 return "Expected comma after matrix description";
608 if (strspn(desc+mlen+1, "0123456789abcdefABCDEF") != glen)
609 return "Grid description is wrong length";
610 if (desc[mlen+1+glen])
611 return "Unexpected data after grid description";
612
613 return NULL;
614}
615
616static game_state *new_game(midend *me, const game_params *params,
617 const char *desc)
618{
619 int w = params->w, h = params->h, wh = w * h;
620 int mlen = (wh*wh+3)/4;
621
622 game_state *state = snew(game_state);
623
624 state->w = w;
625 state->h = h;
626 state->completed = FALSE;
627 state->cheated = FALSE;
628 state->hints_active = FALSE;
629 state->moves = 0;
630 state->matrix = snew(struct matrix);
631 state->matrix->refcount = 1;
632 state->matrix->matrix = snewn(wh*wh, unsigned char);
633 decode_bitmap(state->matrix->matrix, wh*wh, desc);
634 state->grid = snewn(wh, unsigned char);
635 decode_bitmap(state->grid, wh, desc + mlen + 1);
636
637 return state;
638}
639
640static game_state *dup_game(const game_state *state)
641{
642 game_state *ret = snew(game_state);
643
644 ret->w = state->w;
645 ret->h = state->h;
646 ret->completed = state->completed;
647 ret->cheated = state->cheated;
648 ret->hints_active = state->hints_active;
649 ret->moves = state->moves;
650 ret->matrix = state->matrix;
651 state->matrix->refcount++;
652 ret->grid = snewn(ret->w * ret->h, unsigned char);
653 memcpy(ret->grid, state->grid, ret->w * ret->h);
654
655 return ret;
656}
657
658static void free_game(game_state *state)
659{
660 sfree(state->grid);
661 if (--state->matrix->refcount <= 0) {
662 sfree(state->matrix->matrix);
663 sfree(state->matrix);
664 }
665 sfree(state);
666}
667
668static void rowxor(unsigned char *row1, unsigned char *row2, int len)
669{
670 int i;
671 for (i = 0; i < len; i++)
672 row1[i] ^= row2[i];
673}
674
675static char *solve_game(const game_state *state, const game_state *currstate,
676 const char *aux, char **error)
677{
678 int w = state->w, h = state->h, wh = w * h;
679 unsigned char *equations, *solution, *shortest;
680 int *und, nund;
681 int rowsdone, colsdone;
682 int i, j, k, len, bestlen;
683 char *ret;
684
685 /*
686 * Set up a list of simultaneous equations. Each one is of
687 * length (wh+1) and has wh coefficients followed by a value.
688 */
689 equations = snewn((wh + 1) * wh, unsigned char);
690 for (i = 0; i < wh; i++) {
691 for (j = 0; j < wh; j++)
692 equations[i * (wh+1) + j] = currstate->matrix->matrix[j*wh+i];
693 equations[i * (wh+1) + wh] = currstate->grid[i] & 1;
694 }
695
696 /*
697 * Perform Gaussian elimination over GF(2).
698 */
699 rowsdone = colsdone = 0;
700 nund = 0;
701 und = snewn(wh, int);
702 do {
703 /*
704 * Find the leftmost column which has a 1 in it somewhere
705 * outside the first `rowsdone' rows.
706 */
707 j = -1;
708 for (i = colsdone; i < wh; i++) {
709 for (j = rowsdone; j < wh; j++)
710 if (equations[j * (wh+1) + i])
711 break;
712 if (j < wh)
713 break; /* found one */
714 /*
715 * This is a column which will not have an equation
716 * controlling it. Mark it as undetermined.
717 */
718 und[nund++] = i;
719 }
720
721 /*
722 * If there wasn't one, then we've finished: all remaining
723 * equations are of the form 0 = constant. Check to see if
724 * any of them wants 0 to be equal to 1; this is the
725 * condition which indicates an insoluble problem
726 * (therefore _hopefully_ one typed in by a user!).
727 */
728 if (i == wh) {
729 for (j = rowsdone; j < wh; j++)
730 if (equations[j * (wh+1) + wh]) {
731 *error = "No solution exists for this position";
732 sfree(equations);
733 sfree(und);
734 return NULL;
735 }
736 break;
737 }
738
739 /*
740 * We've found a 1. It's in column i, and the topmost 1 in
741 * that column is in row j. Do a row-XOR to move it up to
742 * the topmost row if it isn't already there.
743 */
744 assert(j != -1);
745 if (j > rowsdone)
746 rowxor(equations + rowsdone*(wh+1), equations + j*(wh+1), wh+1);
747
748 /*
749 * Do row-XORs to eliminate that 1 from all rows below the
750 * topmost row.
751 */
752 for (j = rowsdone + 1; j < wh; j++)
753 if (equations[j*(wh+1) + i])
754 rowxor(equations + j*(wh+1),
755 equations + rowsdone*(wh+1), wh+1);
756
757 /*
758 * Mark this row and column as done.
759 */
760 rowsdone++;
761 colsdone = i+1;
762
763 /*
764 * If we've done all the rows, terminate.
765 */
766 } while (rowsdone < wh);
767
768 /*
769 * If we reach here, we have the ability to produce a solution.
770 * So we go through _all_ possible solutions (each
771 * corresponding to a set of arbitrary choices of those
772 * components not directly determined by an equation), and pick
773 * one requiring the smallest number of flips.
774 */
775 solution = snewn(wh, unsigned char);
776 shortest = snewn(wh, unsigned char);
777 memset(solution, 0, wh);
778 bestlen = wh + 1;
779 while (1) {
780 /*
781 * Find a solution based on the current values of the
782 * undetermined variables.
783 */
784 for (j = rowsdone; j-- ;) {
785 int v;
786
787 /*
788 * Find the leftmost set bit in this equation.
789 */
790 for (i = 0; i < wh; i++)
791 if (equations[j * (wh+1) + i])
792 break;
793 assert(i < wh); /* there must have been one! */
794
795 /*
796 * Compute this variable using the rest.
797 */
798 v = equations[j * (wh+1) + wh];
799 for (k = i+1; k < wh; k++)
800 if (equations[j * (wh+1) + k])
801 v ^= solution[k];
802
803 solution[i] = v;
804 }
805
806 /*
807 * Compare this solution to the current best one, and
808 * replace the best one if this one is shorter.
809 */
810 len = 0;
811 for (i = 0; i < wh; i++)
812 if (solution[i])
813 len++;
814 if (len < bestlen) {
815 bestlen = len;
816 memcpy(shortest, solution, wh);
817 }
818
819 /*
820 * Now increment the binary number given by the
821 * undetermined variables: turn all 1s into 0s until we see
822 * a 0, at which point we turn it into a 1.
823 */
824 for (i = 0; i < nund; i++) {
825 solution[und[i]] = !solution[und[i]];
826 if (solution[und[i]])
827 break;
828 }
829
830 /*
831 * If we didn't find a 0 at any point, we have wrapped
832 * round and are back at the start, i.e. we have enumerated
833 * all solutions.
834 */
835 if (i == nund)
836 break;
837 }
838
839 /*
840 * We have a solution. Produce a move string encoding the
841 * solution.
842 */
843 ret = snewn(wh + 2, char);
844 ret[0] = 'S';
845 for (i = 0; i < wh; i++)
846 ret[i+1] = shortest[i] ? '1' : '0';
847 ret[wh+1] = '\0';
848
849 sfree(shortest);
850 sfree(solution);
851 sfree(equations);
852 sfree(und);
853
854 return ret;
855}
856
857static int game_can_format_as_text_now(const game_params *params)
858{
859 return TRUE;
860}
861
862#define RIGHT 1
863#define DOWN gw
864
865static char *game_text_format(const game_state *state)
866{
867 int w = state->w, h = state->h, wh = w*h, r, c, dx, dy;
868 int cw = 4, ch = 4, gw = w * cw + 2, gh = h * ch + 1, len = gw * gh;
869 char *board = snewn(len + 1, char);
870
871 memset(board, ' ', len - 1);
872
873 for (r = 0; r < h; ++r) {
874 for (c = 0; c < w; ++c) {
875 int cell = r*ch*gw + c*cw, center = cell+(ch/2)*DOWN + cw/2*RIGHT;
876 char flip = (state->grid[r*w + c] & 1) ? '#' : '.';
877 for (dy = -1 + (r == 0); dy <= 1 - (r == h - 1); ++dy)
878 for (dx = -1 + (c == 0); dx <= 1 - (c == w - 1); ++dx)
879 if (state->matrix->matrix[(r*w+c)*wh + ((r+dy)*w + c+dx)])
880 board[center + dy*DOWN + dx*RIGHT] = flip;
881 board[cell] = '+';
882 for (dx = 1; dx < cw; ++dx) board[cell+dx*RIGHT] = '-';
883 for (dy = 1; dy < ch; ++dy) board[cell+dy*DOWN] = '|';
884 }
885 board[r*ch*gw + gw - 2] = '+';
886 board[r*ch*gw + gw - 1] = '\n';
887 for (dy = 1; dy < ch; ++dy) {
888 board[r*ch*gw + gw - 2 + dy*DOWN] = '|';
889 board[r*ch*gw + gw - 1 + dy*DOWN] = '\n';
890 }
891 }
892 memset(board + len - gw, '-', gw - 2);
893 for (c = 0; c <= w; ++c) board[len - gw + cw*c] = '+';
894 board[len - 1] = '\n';
895 board[len] = '\0';
896 return board;
897}
898
899#undef RIGHT
900#undef DOWN
901
902struct game_ui {
903 int cx, cy, cdraw;
904};
905
906static game_ui *new_ui(const game_state *state)
907{
908 game_ui *ui = snew(game_ui);
909 ui->cx = ui->cy = ui->cdraw = 0;
910 return ui;
911}
912
913static void free_ui(game_ui *ui)
914{
915 sfree(ui);
916}
917
918static char *encode_ui(const game_ui *ui)
919{
920 return NULL;
921}
922
923static void decode_ui(game_ui *ui, const char *encoding)
924{
925}
926
927static void game_changed_state(game_ui *ui, const game_state *oldstate,
928 const game_state *newstate)
929{
930}
931
932struct game_drawstate {
933 int w, h, started;
934 unsigned char *tiles;
935 int tilesize;
936};
937
938static char *interpret_move(const game_state *state, game_ui *ui,
939 const game_drawstate *ds,
940 int x, int y, int button)
941{
942 int w = state->w, h = state->h, wh = w * h;
943 char buf[80], *nullret = NULL;
944
945 if (button == LEFT_BUTTON || IS_CURSOR_SELECT(button)) {
946 int tx, ty;
947 if (button == LEFT_BUTTON) {
948 tx = FROMCOORD(x), ty = FROMCOORD(y);
949 ui->cdraw = 0;
950 } else {
951 tx = ui->cx; ty = ui->cy;
952 ui->cdraw = 1;
953 }
954 nullret = "";
955
956 if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
957 /*
958 * It's just possible that a manually entered game ID
959 * will have at least one square do nothing whatsoever.
960 * If so, we avoid encoding a move at all.
961 */
962 int i = ty*w+tx, j, makemove = FALSE;
963 for (j = 0; j < wh; j++) {
964 if (state->matrix->matrix[i*wh+j])
965 makemove = TRUE;
966 }
967 if (makemove) {
968 sprintf(buf, "M%d,%d", tx, ty);
969 return dupstr(buf);
970 } else {
971 return NULL;
972 }
973 }
974 }
975 else if (IS_CURSOR_MOVE(button)) {
976 int dx = 0, dy = 0;
977 switch (button) {
978 case CURSOR_UP: dy = -1; break;
979 case CURSOR_DOWN: dy = 1; break;
980 case CURSOR_RIGHT: dx = 1; break;
981 case CURSOR_LEFT: dx = -1; break;
982 default: assert(!"shouldn't get here");
983 }
984 ui->cx += dx; ui->cy += dy;
985 ui->cx = min(max(ui->cx, 0), state->w - 1);
986 ui->cy = min(max(ui->cy, 0), state->h - 1);
987 ui->cdraw = 1;
988 nullret = "";
989 }
990
991 return nullret;
992}
993
994static game_state *execute_move(const game_state *from, const char *move)
995{
996 int w = from->w, h = from->h, wh = w * h;
997 game_state *ret;
998 int x, y;
999
1000 if (move[0] == 'S' && strlen(move) == wh+1) {
1001 int i;
1002
1003 ret = dup_game(from);
1004 ret->hints_active = TRUE;
1005 ret->cheated = TRUE;
1006 for (i = 0; i < wh; i++) {
1007 ret->grid[i] &= ~2;
1008 if (move[i+1] != '0')
1009 ret->grid[i] |= 2;
1010 }
1011 return ret;
1012 } else if (move[0] == 'M' &&
1013 sscanf(move+1, "%d,%d", &x, &y) == 2 &&
1014 x >= 0 && x < w && y >= 0 && y < h) {
1015 int i, j, done;
1016
1017 ret = dup_game(from);
1018
1019 if (!ret->completed)
1020 ret->moves++;
1021
1022 i = y * w + x;
1023
1024 done = TRUE;
1025 for (j = 0; j < wh; j++) {
1026 ret->grid[j] ^= ret->matrix->matrix[i*wh+j];
1027 if (ret->grid[j] & 1)
1028 done = FALSE;
1029 }
1030 ret->grid[i] ^= 2; /* toggle hint */
1031 if (done) {
1032 ret->completed = TRUE;
1033 ret->hints_active = FALSE;
1034 }
1035
1036 return ret;
1037 } else
1038 return NULL; /* can't parse move string */
1039}
1040
1041/* ----------------------------------------------------------------------
1042 * Drawing routines.
1043 */
1044
1045static void game_compute_size(const game_params *params, int tilesize,
1046 int *x, int *y)
1047{
1048 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1049 struct { int tilesize; } ads, *ds = &ads;
1050 ads.tilesize = tilesize;
1051
1052 *x = TILE_SIZE * params->w + 2 * BORDER;
1053 *y = TILE_SIZE * params->h + 2 * BORDER;
1054}
1055
1056static void game_set_size(drawing *dr, game_drawstate *ds,
1057 const game_params *params, int tilesize)
1058{
1059 ds->tilesize = tilesize;
1060}
1061
1062static float *game_colours(frontend *fe, int *ncolours)
1063{
1064 float *ret = snewn(3 * NCOLOURS, float);
1065
1066 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1067
1068 ret[COL_WRONG * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 3;
1069 ret[COL_WRONG * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 3;
1070 ret[COL_WRONG * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 3;
1071
1072 ret[COL_RIGHT * 3 + 0] = 1.0F;
1073 ret[COL_RIGHT * 3 + 1] = 1.0F;
1074 ret[COL_RIGHT * 3 + 2] = 1.0F;
1075
1076 ret[COL_GRID * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F;
1077 ret[COL_GRID * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F;
1078 ret[COL_GRID * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F;
1079
1080 ret[COL_DIAG * 3 + 0] = ret[COL_GRID * 3 + 0];
1081 ret[COL_DIAG * 3 + 1] = ret[COL_GRID * 3 + 1];
1082 ret[COL_DIAG * 3 + 2] = ret[COL_GRID * 3 + 2];
1083
1084 ret[COL_HINT * 3 + 0] = 1.0F;
1085 ret[COL_HINT * 3 + 1] = 0.0F;
1086 ret[COL_HINT * 3 + 2] = 0.0F;
1087
1088 ret[COL_CURSOR * 3 + 0] = 0.8F;
1089 ret[COL_CURSOR * 3 + 1] = 0.0F;
1090 ret[COL_CURSOR * 3 + 2] = 0.0F;
1091
1092 *ncolours = NCOLOURS;
1093 return ret;
1094}
1095
1096static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1097{
1098 struct game_drawstate *ds = snew(struct game_drawstate);
1099 int i;
1100
1101 ds->started = FALSE;
1102 ds->w = state->w;
1103 ds->h = state->h;
1104 ds->tiles = snewn(ds->w*ds->h, unsigned char);
1105 ds->tilesize = 0; /* haven't decided yet */
1106 for (i = 0; i < ds->w*ds->h; i++)
1107 ds->tiles[i] = -1;
1108
1109 return ds;
1110}
1111
1112static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1113{
1114 sfree(ds->tiles);
1115 sfree(ds);
1116}
1117
1118static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
1119 int x, int y, int tile, int anim, float animtime)
1120{
1121 int w = ds->w, h = ds->h, wh = w * h;
1122 int bx = x * TILE_SIZE + BORDER, by = y * TILE_SIZE + BORDER;
1123 int i, j, dcol = (tile & 4) ? COL_CURSOR : COL_DIAG;
1124
1125 clip(dr, bx+1, by+1, TILE_SIZE-1, TILE_SIZE-1);
1126
1127 draw_rect(dr, bx+1, by+1, TILE_SIZE-1, TILE_SIZE-1,
1128 anim ? COL_BACKGROUND : tile & 1 ? COL_WRONG : COL_RIGHT);
1129 if (anim) {
1130 /*
1131 * Draw a polygon indicating that the square is diagonally
1132 * flipping over.
1133 */
1134 int coords[8], colour;
1135
1136 coords[0] = bx + TILE_SIZE;
1137 coords[1] = by;
1138 coords[2] = bx + (int)((float)TILE_SIZE * animtime);
1139 coords[3] = by + (int)((float)TILE_SIZE * animtime);
1140 coords[4] = bx;
1141 coords[5] = by + TILE_SIZE;
1142 coords[6] = bx + TILE_SIZE - (int)((float)TILE_SIZE * animtime);
1143 coords[7] = by + TILE_SIZE - (int)((float)TILE_SIZE * animtime);
1144
1145 colour = (tile & 1 ? COL_WRONG : COL_RIGHT);
1146 if (animtime < 0.5)
1147 colour = COL_WRONG + COL_RIGHT - colour;
1148
1149 draw_polygon(dr, coords, 4, colour, COL_GRID);
1150 }
1151
1152 /*
1153 * Draw a little diagram in the tile which indicates which
1154 * surrounding tiles flip when this one is clicked.
1155 */
1156 for (i = 0; i < h; i++)
1157 for (j = 0; j < w; j++)
1158 if (state->matrix->matrix[(y*w+x)*wh + i*w+j]) {
1159 int ox = j - x, oy = i - y;
1160 int td = TILE_SIZE / 16;
1161 int cx = (bx + TILE_SIZE/2) + (2 * ox - 1) * td;
1162 int cy = (by + TILE_SIZE/2) + (2 * oy - 1) * td;
1163 if (ox == 0 && oy == 0)
1164 draw_rect(dr, cx, cy, 2*td+1, 2*td+1, dcol);
1165 else {
1166 draw_line(dr, cx, cy, cx+2*td, cy, dcol);
1167 draw_line(dr, cx, cy+2*td, cx+2*td, cy+2*td, dcol);
1168 draw_line(dr, cx, cy, cx, cy+2*td, dcol);
1169 draw_line(dr, cx+2*td, cy, cx+2*td, cy+2*td, dcol);
1170 }
1171 }
1172
1173 /*
1174 * Draw a hint rectangle if required.
1175 */
1176 if (tile & 2) {
1177 int x1 = bx + TILE_SIZE / 20, x2 = bx + TILE_SIZE - TILE_SIZE / 20;
1178 int y1 = by + TILE_SIZE / 20, y2 = by + TILE_SIZE - TILE_SIZE / 20;
1179 int i = 3;
1180 while (i--) {
1181 draw_line(dr, x1, y1, x2, y1, COL_HINT);
1182 draw_line(dr, x1, y2, x2, y2, COL_HINT);
1183 draw_line(dr, x1, y1, x1, y2, COL_HINT);
1184 draw_line(dr, x2, y1, x2, y2, COL_HINT);
1185 x1++, y1++, x2--, y2--;
1186 }
1187 }
1188
1189 unclip(dr);
1190
1191 draw_update(dr, bx+1, by+1, TILE_SIZE-1, TILE_SIZE-1);
1192}
1193
1194static void game_redraw(drawing *dr, game_drawstate *ds,
1195 const game_state *oldstate, const game_state *state,
1196 int dir, const game_ui *ui,
1197 float animtime, float flashtime)
1198{
1199 int w = ds->w, h = ds->h, wh = w * h;
1200 int i, flashframe;
1201
1202 if (!ds->started) {
1203 draw_rect(dr, 0, 0, TILE_SIZE * w + 2 * BORDER,
1204 TILE_SIZE * h + 2 * BORDER, COL_BACKGROUND);
1205
1206 /*
1207 * Draw the grid lines.
1208 */
1209 for (i = 0; i <= w; i++)
1210 draw_line(dr, i * TILE_SIZE + BORDER, BORDER,
1211 i * TILE_SIZE + BORDER, h * TILE_SIZE + BORDER,
1212 COL_GRID);
1213 for (i = 0; i <= h; i++)
1214 draw_line(dr, BORDER, i * TILE_SIZE + BORDER,
1215 w * TILE_SIZE + BORDER, i * TILE_SIZE + BORDER,
1216 COL_GRID);
1217
1218 draw_update(dr, 0, 0, TILE_SIZE * w + 2 * BORDER,
1219 TILE_SIZE * h + 2 * BORDER);
1220
1221 ds->started = TRUE;
1222 }
1223
1224 if (flashtime)
1225 flashframe = (int)(flashtime / FLASH_FRAME);
1226 else
1227 flashframe = -1;
1228
1229 animtime /= ANIM_TIME; /* scale it so it goes from 0 to 1 */
1230
1231 for (i = 0; i < wh; i++) {
1232 int x = i % w, y = i / w;
1233 int fx, fy, fd;
1234 int v = state->grid[i];
1235 int vv;
1236
1237 if (flashframe >= 0) {
1238 fx = (w+1)/2 - min(x+1, w-x);
1239 fy = (h+1)/2 - min(y+1, h-y);
1240 fd = max(fx, fy);
1241 if (fd == flashframe)
1242 v |= 1;
1243 else if (fd == flashframe - 1)
1244 v &= ~1;
1245 }
1246
1247 if (!state->hints_active)
1248 v &= ~2;
1249 if (ui->cdraw && ui->cx == x && ui->cy == y)
1250 v |= 4;
1251
1252 if (oldstate && ((state->grid[i] ^ oldstate->grid[i]) &~ 2))
1253 vv = 255; /* means `animated' */
1254 else
1255 vv = v;
1256
1257 if (ds->tiles[i] == 255 || vv == 255 || ds->tiles[i] != vv) {
1258 draw_tile(dr, ds, state, x, y, v, vv == 255, animtime);
1259 ds->tiles[i] = vv;
1260 }
1261 }
1262
1263 {
1264 char buf[256];
1265
1266 sprintf(buf, "%sMoves: %d",
1267 (state->completed ?
1268 (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
1269 (state->cheated ? "Auto-solver used. " : "")),
1270 state->moves);
1271
1272 status_bar(dr, buf);
1273 }
1274}
1275
1276static float game_anim_length(const game_state *oldstate,
1277 const game_state *newstate, int dir, game_ui *ui)
1278{
1279 return ANIM_TIME;
1280}
1281
1282static float game_flash_length(const game_state *oldstate,
1283 const game_state *newstate, int dir, game_ui *ui)
1284{
1285 if (!oldstate->completed && newstate->completed)
1286 return FLASH_FRAME * (max((newstate->w+1)/2, (newstate->h+1)/2)+1);
1287
1288 return 0.0F;
1289}
1290
1291static int game_status(const game_state *state)
1292{
1293 return state->completed ? +1 : 0;
1294}
1295
1296static int game_timing_state(const game_state *state, game_ui *ui)
1297{
1298 return TRUE;
1299}
1300
1301static void game_print_size(const game_params *params, float *x, float *y)
1302{
1303}
1304
1305static void game_print(drawing *dr, const game_state *state, int tilesize)
1306{
1307}
1308
1309#ifdef COMBINED
1310#define thegame flip
1311#endif
1312
1313const struct game thegame = {
1314 "Flip", "games.flip", "flip",
1315 default_params,
1316 game_fetch_preset, NULL,
1317 decode_params,
1318 encode_params,
1319 free_params,
1320 dup_params,
1321 TRUE, game_configure, custom_params,
1322 validate_params,
1323 new_game_desc,
1324 validate_desc,
1325 new_game,
1326 dup_game,
1327 free_game,
1328 TRUE, solve_game,
1329 TRUE, game_can_format_as_text_now, game_text_format,
1330 new_ui,
1331 free_ui,
1332 encode_ui,
1333 decode_ui,
1334 game_changed_state,
1335 interpret_move,
1336 execute_move,
1337 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1338 game_colours,
1339 game_new_drawstate,
1340 game_free_drawstate,
1341 game_redraw,
1342 game_anim_length,
1343 game_flash_length,
1344 game_status,
1345 FALSE, FALSE, game_print_size, game_print,
1346 TRUE, /* wants_statusbar */
1347 FALSE, game_timing_state,
1348 0, /* flags */
1349};
diff --git a/apps/plugins/puzzles/src/flip.html b/apps/plugins/puzzles/src/flip.html
new file mode 100644
index 0000000000..52790dba6c
--- /dev/null
+++ b/apps/plugins/puzzles/src/flip.html
@@ -0,0 +1,54 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Flip</title>
7<link rel="previous" href="samegame.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="guess.html">
12</head>
13<body>
14<p><a href="samegame.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="guess.html">Next</a></p>
15<h1><a name="C14"></a>Chapter 14: <a name="i0"></a>Flip</h1>
16<p>
17You have a grid of squares, some light and some dark. Your aim is to light all the squares up at the same time. You can choose any square and flip its state from light to dark or dark to light, but when you do so, other squares around it change state as well.
18</p>
19<p>
20Each square contains a small diagram showing which other squares change when you flip it.
21</p>
22<h2><a name="S14.1"></a>14.1 <a name="i1"></a>Flip controls</h2>
23<p>
24This game can be played with either the keyboard or the mouse.
25</p>
26<p>
27Left-click in a square to flip it and its associated squares, or use the cursor keys to choose a square and the space bar or Enter key to flip.
28</p>
29<p>
30If you use the &#8216;Solve&#8217; function on this game, it will mark some of the squares in red. If you click once in every square with a red mark, the game should be solved. (If you click in a square <em>without</em> a red mark, a red mark will appear in it to indicate that you will need to reverse that operation to reach the solution.)
31</p>
32<p>
33(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
34</p>
35<h2><a name="S14.2"></a>14.2 <a name="i2"></a>Flip parameters</h2>
36<p>
37These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
38</p>
39<dl><dt>
40<em>Width</em>, <em>Height</em>
41</dt>
42<dd>
43Size of grid in squares.
44</dd>
45<dt>
46<em>Shape type</em>
47</dt>
48<dd>
49This control determines the shape of the region which is flipped by clicking in any given square. The default setting, &#8216;Crosses&#8217;, causes every square to flip itself and its four immediate neighbours (or three or two if it's at an edge or corner). The other setting, &#8216;Random&#8217;, causes a random shape to be chosen for every square, so the game is different every time.
50</dd>
51</dl>
52
53<hr><address></address></body>
54</html>
diff --git a/apps/plugins/puzzles/src/flood.R b/apps/plugins/puzzles/src/flood.R
new file mode 100644
index 0000000000..359bbb5dce
--- /dev/null
+++ b/apps/plugins/puzzles/src/flood.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3flood : [X] GTK COMMON flood flood-icon|no-icon
4
5flood : [G] WINDOWS COMMON flood flood.res|noicon.res
6
7ALL += flood[COMBINED]
8
9!begin am gtk
10GAMES += flood
11!end
12
13!begin >list.c
14 A(flood) \
15!end
16
17!begin >gamedesc.txt
18flood:flood.exe:Flood:Flood-filling puzzle:Turn the grid the same colour in as few flood fills as possible.
19!end
diff --git a/apps/plugins/puzzles/src/flood.c b/apps/plugins/puzzles/src/flood.c
new file mode 100644
index 0000000000..1262be8175
--- /dev/null
+++ b/apps/plugins/puzzles/src/flood.c
@@ -0,0 +1,1372 @@
1/*
2 * flood.c: puzzle in which you make a grid all the same colour by
3 * repeatedly flood-filling the top left corner, and try to do so in
4 * as few moves as possible.
5 */
6
7/*
8 * Possible further work:
9 *
10 * - UI: perhaps we should only permit clicking on regions that can
11 * actually be reached by the next flood-fill - i.e. a click is
12 * only interpreted as a move if it would cause the clicked-on
13 * square to become part of the controlled area. This provides a
14 * hint in cases where you mistakenly thought that would happen,
15 * and protects you against typos in cases where you just
16 * mis-aimed.
17 *
18 * - UI: perhaps mark the fill square in some way? Or even mark the
19 * whole connected component _containing_ the fill square. Pro:
20 * that would make it easier to tell apart cases where almost all
21 * the yellow squares in the grid are part of the target component
22 * (hence, yellow is _done_ and you never have to fill in that
23 * colour again) from cases where there's still one yellow square
24 * that's only diagonally adjacent and hence will need coming back
25 * to. Con: but it would almost certainly be ugly.
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <stdarg.h>
31#include <string.h>
32#include <assert.h>
33#include <ctype.h>
34#include <math.h>
35
36#include "puzzles.h"
37
38enum {
39 COL_BACKGROUND, COL_SEPARATOR,
40 COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9, COL_10,
41 COL_HIGHLIGHT, COL_LOWLIGHT,
42 NCOLOURS
43};
44
45struct game_params {
46 int w, h;
47 int colours;
48 int leniency;
49};
50
51/* Just in case I want to make this changeable later, I'll put the
52 * coordinates of the flood-fill point here so that it'll be easy to
53 * find everywhere later that has to change. */
54#define FILLX 0
55#define FILLY 0
56
57typedef struct soln {
58 int refcount;
59 int nmoves;
60 char *moves;
61} soln;
62
63struct game_state {
64 int w, h, colours;
65 int moves, movelimit;
66 int complete;
67 char *grid;
68 int cheated;
69 int solnpos;
70 soln *soln;
71};
72
73static game_params *default_params(void)
74{
75 game_params *ret = snew(game_params);
76
77 ret->w = ret->h = 12;
78 ret->colours = 6;
79 ret->leniency = 5;
80
81 return ret;
82}
83
84static const struct {
85 struct game_params preset;
86 const char *name;
87} flood_presets[] = {
88 /* Default 12x12 size, three difficulty levels. */
89 {{12, 12, 6, 5}, "12x12 Easy"},
90 {{12, 12, 6, 2}, "12x12 Medium"},
91 {{12, 12, 6, 0}, "12x12 Hard"},
92 /* Larger puzzles, leaving off Easy in the expectation that people
93 * wanting a bigger grid will have played it enough to find Easy
94 * easy. */
95 {{16, 16, 6, 2}, "16x16 Medium"},
96 {{16, 16, 6, 0}, "16x16 Hard"},
97 /* A couple of different colour counts. It seems generally not too
98 * hard with fewer colours (probably because fewer choices), so no
99 * extra moves for these modes. */
100 {{12, 12, 3, 0}, "12x12, 3 colours"},
101 {{12, 12, 4, 0}, "12x12, 4 colours"},
102};
103
104static int game_fetch_preset(int i, char **name, game_params **params)
105{
106 game_params *ret;
107
108 if (i < 0 || i >= lenof(flood_presets))
109 return FALSE;
110
111 ret = snew(game_params);
112 *ret = flood_presets[i].preset;
113 *name = dupstr(flood_presets[i].name);
114 *params = ret;
115 return TRUE;
116}
117
118static void free_params(game_params *params)
119{
120 sfree(params);
121}
122
123static game_params *dup_params(const game_params *params)
124{
125 game_params *ret = snew(game_params);
126 *ret = *params; /* structure copy */
127 return ret;
128}
129
130static void decode_params(game_params *ret, char const *string)
131{
132 ret->w = ret->h = atoi(string);
133 while (*string && isdigit((unsigned char)*string)) string++;
134 if (*string == 'x') {
135 string++;
136 ret->h = atoi(string);
137 while (*string && isdigit((unsigned char)*string)) string++;
138 }
139 while (*string) {
140 if (*string == 'c') {
141 string++;
142 ret->colours = atoi(string);
143 while (string[1] && isdigit((unsigned char)string[1])) string++;
144 } else if (*string == 'm') {
145 string++;
146 ret->leniency = atoi(string);
147 while (string[1] && isdigit((unsigned char)string[1])) string++;
148 }
149 string++;
150 }
151}
152
153static char *encode_params(const game_params *params, int full)
154{
155 char buf[256];
156 sprintf(buf, "%dx%d", params->w, params->h);
157 if (full)
158 sprintf(buf + strlen(buf), "c%dm%d",
159 params->colours, params->leniency);
160 return dupstr(buf);
161}
162
163static config_item *game_configure(const game_params *params)
164{
165 config_item *ret;
166 char buf[80];
167
168 ret = snewn(5, config_item);
169
170 ret[0].name = "Width";
171 ret[0].type = C_STRING;
172 sprintf(buf, "%d", params->w);
173 ret[0].sval = dupstr(buf);
174 ret[0].ival = 0;
175
176 ret[1].name = "Height";
177 ret[1].type = C_STRING;
178 sprintf(buf, "%d", params->h);
179 ret[1].sval = dupstr(buf);
180 ret[1].ival = 0;
181
182 ret[2].name = "Colours";
183 ret[2].type = C_STRING;
184 sprintf(buf, "%d", params->colours);
185 ret[2].sval = dupstr(buf);
186 ret[2].ival = 0;
187
188 ret[3].name = "Extra moves permitted";
189 ret[3].type = C_STRING;
190 sprintf(buf, "%d", params->leniency);
191 ret[3].sval = dupstr(buf);
192 ret[3].ival = 0;
193
194 ret[4].name = NULL;
195 ret[4].type = C_END;
196 ret[4].sval = NULL;
197 ret[4].ival = 0;
198
199 return ret;
200}
201
202static game_params *custom_params(const config_item *cfg)
203{
204 game_params *ret = snew(game_params);
205
206 ret->w = atoi(cfg[0].sval);
207 ret->h = atoi(cfg[1].sval);
208 ret->colours = atoi(cfg[2].sval);
209 ret->leniency = atoi(cfg[3].sval);
210
211 return ret;
212}
213
214static char *validate_params(const game_params *params, int full)
215{
216 if (params->w < 2 && params->h < 2)
217 return "Grid must contain at least two squares";
218 if (params->colours < 3 || params->colours > 10)
219 return "Must have between 3 and 10 colours";
220 if (params->leniency < 0)
221 return "Leniency must be non-negative";
222 return NULL;
223}
224
225#if 0
226/*
227 * Bodge to permit varying the recursion depth for testing purposes.
228
229To test two Floods against each other:
230
231paste <(./flood.1 --generate 100 12x12c6m0#12345 | cut -f2 -d,) <(./flood.2 --generate 100 12x12c6m0#12345 | cut -f2 -d,) | awk '{print $2-$1}' | sort -n | uniq -c | awk '{print $2,$1}' | tee z
232
233and then run gnuplot and plot "z".
234
235 */
236static int rdepth = 0;
237#define RECURSION_DEPTH (rdepth)
238void check_recursion_depth(void)
239{
240 if (!rdepth) {
241 const char *depthstr = getenv("FLOOD_DEPTH");
242 rdepth = depthstr ? atoi(depthstr) : 1;
243 rdepth = rdepth > 0 ? rdepth : 1;
244 }
245}
246#else
247/*
248 * Last time I empirically checked this, depth 3 was a noticeable
249 * improvement on 2, but 4 only negligibly better than 3.
250 */
251#define RECURSION_DEPTH 3
252#define check_recursion_depth() (void)0
253#endif
254
255struct solver_scratch {
256 int *queue[2];
257 int *dist;
258 char *grid, *grid2;
259 char *rgrids;
260};
261
262static struct solver_scratch *new_scratch(int w, int h)
263{
264 int wh = w*h;
265 struct solver_scratch *scratch = snew(struct solver_scratch);
266 check_recursion_depth();
267 scratch->queue[0] = snewn(wh, int);
268 scratch->queue[1] = snewn(wh, int);
269 scratch->dist = snewn(wh, int);
270 scratch->grid = snewn(wh, char);
271 scratch->grid2 = snewn(wh, char);
272 scratch->rgrids = snewn(wh * RECURSION_DEPTH, char);
273 return scratch;
274}
275
276static void free_scratch(struct solver_scratch *scratch)
277{
278 sfree(scratch->queue[0]);
279 sfree(scratch->queue[1]);
280 sfree(scratch->dist);
281 sfree(scratch->grid);
282 sfree(scratch->grid2);
283 sfree(scratch->rgrids);
284 sfree(scratch);
285}
286
287#if 0
288/* Diagnostic routines you can uncomment if you need them */
289void dump_grid(int w, int h, const char *grid, const char *titlefmt, ...)
290{
291 int x, y;
292 if (titlefmt) {
293 va_list ap;
294 va_start(ap, titlefmt);
295 vprintf(titlefmt, ap);
296 va_end(ap);
297 printf(":\n");
298 } else {
299 printf("Grid:\n");
300 }
301 for (y = 0; y < h; y++) {
302 printf(" ");
303 for (x = 0; x < w; x++) {
304 printf("%1x", grid[y*w+x]);
305 }
306 printf("\n");
307 }
308}
309
310void dump_dist(int w, int h, const int *dists, const char *titlefmt, ...)
311{
312 int x, y;
313 if (titlefmt) {
314 va_list ap;
315 va_start(ap, titlefmt);
316 vprintf(titlefmt, ap);
317 va_end(ap);
318 printf(":\n");
319 } else {
320 printf("Distances:\n");
321 }
322 for (y = 0; y < h; y++) {
323 printf(" ");
324 for (x = 0; x < w; x++) {
325 printf("%3d", dists[y*w+x]);
326 }
327 printf("\n");
328 }
329}
330#endif
331
332/*
333 * Search a grid to find the most distant square(s). Return their
334 * distance and the number of them, and also the number of squares in
335 * the current controlled set (i.e. at distance zero).
336 */
337static void search(int w, int h, char *grid, int x0, int y0,
338 struct solver_scratch *scratch,
339 int *rdist, int *rnumber, int *rcontrol)
340{
341 int wh = w*h;
342 int i, qcurr, qhead, qtail, qnext, currdist, remaining;
343
344 for (i = 0; i < wh; i++)
345 scratch->dist[i] = -1;
346 scratch->queue[0][0] = y0*w+x0;
347 scratch->queue[1][0] = y0*w+x0;
348 scratch->dist[y0*w+x0] = 0;
349 currdist = 0;
350 qcurr = 0;
351 qtail = 0;
352 qhead = 1;
353 qnext = 1;
354 remaining = wh - 1;
355
356 while (1) {
357 if (qtail == qhead) {
358 /* Switch queues. */
359 if (currdist == 0)
360 *rcontrol = qhead;
361 currdist++;
362 qcurr ^= 1; /* switch queues */
363 qhead = qnext;
364 qtail = 0;
365 qnext = 0;
366#if 0
367 printf("switch queue, new dist %d, queue %d\n", currdist, qhead);
368#endif
369 } else if (remaining == 0 && qnext == 0) {
370 break;
371 } else {
372 int pos = scratch->queue[qcurr][qtail++];
373 int y = pos / w;
374 int x = pos % w;
375#if 0
376 printf("checking neighbours of %d,%d\n", x, y);
377#endif
378 int dir;
379 for (dir = 0; dir < 4; dir++) {
380 int y1 = y + (dir == 1 ? 1 : dir == 3 ? -1 : 0);
381 int x1 = x + (dir == 0 ? 1 : dir == 2 ? -1 : 0);
382 if (0 <= x1 && x1 < w && 0 <= y1 && y1 < h) {
383 int pos1 = y1*w+x1;
384#if 0
385 printf("trying %d,%d: colours %d-%d dist %d\n", x1, y1,
386 grid[pos], grid[pos1], scratch->dist[pos]);
387#endif
388 if (scratch->dist[pos1] == -1 &&
389 ((grid[pos1] == grid[pos] &&
390 scratch->dist[pos] == currdist) ||
391 (grid[pos1] != grid[pos] &&
392 scratch->dist[pos] == currdist - 1))) {
393#if 0
394 printf("marking %d,%d dist %d\n", x1, y1, currdist);
395#endif
396 scratch->queue[qcurr][qhead++] = pos1;
397 scratch->queue[qcurr^1][qnext++] = pos1;
398 scratch->dist[pos1] = currdist;
399 remaining--;
400 }
401 }
402 }
403 }
404 }
405
406 *rdist = currdist;
407 *rnumber = qhead;
408 if (currdist == 0)
409 *rcontrol = qhead;
410}
411
412/*
413 * Enact a flood-fill move on a grid.
414 */
415static void fill(int w, int h, char *grid, int x0, int y0, char newcolour,
416 int *queue)
417{
418 char oldcolour;
419 int qhead, qtail;
420
421 oldcolour = grid[y0*w+x0];
422 assert(oldcolour != newcolour);
423 grid[y0*w+x0] = newcolour;
424 queue[0] = y0*w+x0;
425 qtail = 0;
426 qhead = 1;
427
428 while (qtail < qhead) {
429 int pos = queue[qtail++];
430 int y = pos / w;
431 int x = pos % w;
432 int dir;
433 for (dir = 0; dir < 4; dir++) {
434 int y1 = y + (dir == 1 ? 1 : dir == 3 ? -1 : 0);
435 int x1 = x + (dir == 0 ? 1 : dir == 2 ? -1 : 0);
436 if (0 <= x1 && x1 < w && 0 <= y1 && y1 < h) {
437 int pos1 = y1*w+x1;
438 if (grid[pos1] == oldcolour) {
439 grid[pos1] = newcolour;
440 queue[qhead++] = pos1;
441 }
442 }
443 }
444 }
445}
446
447/*
448 * Detect a completed grid.
449 */
450static int completed(int w, int h, char *grid)
451{
452 int wh = w*h;
453 int i;
454
455 for (i = 1; i < wh; i++)
456 if (grid[i] != grid[0])
457 return FALSE;
458
459 return TRUE;
460}
461
462/*
463 * Try out every possible move on a grid, and choose whichever one
464 * reduced the result of search() by the most.
465 */
466static char choosemove_recurse(int w, int h, char *grid, int x0, int y0,
467 int maxmove, struct solver_scratch *scratch,
468 int depth, int *rbestdist, int *rbestnumber, int *rbestcontrol)
469{
470 int wh = w*h;
471 char move, bestmove;
472 int dist, number, control, bestdist, bestnumber, bestcontrol;
473 char *tmpgrid;
474
475 assert(0 <= depth && depth < RECURSION_DEPTH);
476 tmpgrid = scratch->rgrids + depth*wh;
477
478 bestdist = wh + 1;
479 bestnumber = 0;
480 bestcontrol = 0;
481 bestmove = -1;
482
483#if 0
484 dump_grid(w, h, grid, "before choosemove_recurse %d", depth);
485#endif
486 for (move = 0; move < maxmove; move++) {
487 if (grid[y0*w+x0] == move)
488 continue;
489 memcpy(tmpgrid, grid, wh * sizeof(*grid));
490 fill(w, h, tmpgrid, x0, y0, move, scratch->queue[0]);
491 if (completed(w, h, tmpgrid)) {
492 /*
493 * A move that wins is immediately the best, so stop
494 * searching. Record what depth of recursion that happened
495 * at, so that higher levels will choose a move that gets
496 * to a winning position sooner.
497 */
498 *rbestdist = -1;
499 *rbestnumber = depth;
500 *rbestcontrol = wh;
501 return move;
502 }
503 if (depth < RECURSION_DEPTH-1) {
504 choosemove_recurse(w, h, tmpgrid, x0, y0, maxmove, scratch,
505 depth+1, &dist, &number, &control);
506 } else {
507#if 0
508 dump_grid(w, h, tmpgrid, "after move %d at depth %d",
509 move, depth);
510#endif
511 search(w, h, tmpgrid, x0, y0, scratch, &dist, &number, &control);
512#if 0
513 dump_dist(w, h, scratch->dist, "after move %d at depth %d",
514 move, depth);
515 printf("move %d at depth %d: %d at %d\n",
516 depth, move, number, dist);
517#endif
518 }
519 if (dist < bestdist ||
520 (dist == bestdist &&
521 (number < bestnumber ||
522 (number == bestnumber &&
523 (control > bestcontrol))))) {
524 bestdist = dist;
525 bestnumber = number;
526 bestcontrol = control;
527 bestmove = move;
528 }
529 }
530#if 0
531 printf("best at depth %d was %d (%d at %d, %d controlled)\n",
532 depth, bestmove, bestnumber, bestdist, bestcontrol);
533#endif
534
535 *rbestdist = bestdist;
536 *rbestnumber = bestnumber;
537 *rbestcontrol = bestcontrol;
538 return bestmove;
539}
540static char choosemove(int w, int h, char *grid, int x0, int y0,
541 int maxmove, struct solver_scratch *scratch)
542{
543 int tmp0, tmp1, tmp2;
544 return choosemove_recurse(w, h, grid, x0, y0, maxmove, scratch,
545 0, &tmp0, &tmp1, &tmp2);
546}
547
548static char *new_game_desc(const game_params *params, random_state *rs,
549 char **aux, int interactive)
550{
551 int w = params->w, h = params->h, wh = w*h;
552 int i, moves;
553 char *desc;
554 struct solver_scratch *scratch;
555
556 scratch = new_scratch(w, h);
557
558 /*
559 * Invent a random grid.
560 */
561 for (i = 0; i < wh; i++)
562 scratch->grid[i] = random_upto(rs, params->colours);
563
564 /*
565 * Run the solver, and count how many moves it uses.
566 */
567 memcpy(scratch->grid2, scratch->grid, wh * sizeof(*scratch->grid2));
568 moves = 0;
569 check_recursion_depth();
570 while (!completed(w, h, scratch->grid2)) {
571 char move = choosemove(w, h, scratch->grid2, FILLX, FILLY,
572 params->colours, scratch);
573 fill(w, h, scratch->grid2, FILLX, FILLY, move, scratch->queue[0]);
574 moves++;
575 }
576
577 /*
578 * Adjust for difficulty.
579 */
580 moves += params->leniency;
581
582 /*
583 * Encode the game id.
584 */
585 desc = snewn(wh + 40, char);
586 for (i = 0; i < wh; i++) {
587 char colour = scratch->grid[i];
588 char textcolour = (colour > 9 ? 'A' : '0') + colour;
589 desc[i] = textcolour;
590 }
591 sprintf(desc+i, ",%d", moves);
592
593 free_scratch(scratch);
594
595 return desc;
596}
597
598static char *validate_desc(const game_params *params, const char *desc)
599{
600 int w = params->w, h = params->h, wh = w*h;
601 int i;
602 for (i = 0; i < wh; i++) {
603 char c = *desc++;
604 if (c == 0)
605 return "Not enough data in grid description";
606 if (c >= '0' && c <= '9')
607 c -= '0';
608 else if (c >= 'A' && c <= 'Z')
609 c = 10 + (c - 'A');
610 else
611 return "Bad character in grid description";
612 if ((unsigned)c >= params->colours)
613 return "Colour out of range in grid description";
614 }
615 if (*desc != ',')
616 return "Expected ',' after grid description";
617 desc++;
618 if (desc[strspn(desc, "0123456789")])
619 return "Badly formatted move limit after grid description";
620 return NULL;
621}
622
623static game_state *new_game(midend *me, const game_params *params,
624 const char *desc)
625{
626 int w = params->w, h = params->h, wh = w*h;
627 game_state *state = snew(game_state);
628 int i;
629
630 state->w = w;
631 state->h = h;
632 state->colours = params->colours;
633 state->moves = 0;
634 state->grid = snewn(wh, char);
635
636 for (i = 0; i < wh; i++) {
637 char c = *desc++;
638 assert(c);
639 if (c >= '0' && c <= '9')
640 c -= '0';
641 else if (c >= 'A' && c <= 'Z')
642 c = 10 + (c - 'A');
643 else
644 assert(!"bad colour");
645 state->grid[i] = c;
646 }
647 assert(*desc == ',');
648 desc++;
649
650 state->movelimit = atoi(desc);
651 state->complete = FALSE;
652 state->cheated = FALSE;
653 state->solnpos = 0;
654 state->soln = NULL;
655
656 return state;
657}
658
659static game_state *dup_game(const game_state *state)
660{
661 game_state *ret = snew(game_state);
662
663 ret->w = state->w;
664 ret->h = state->h;
665 ret->colours = state->colours;
666 ret->moves = state->moves;
667 ret->movelimit = state->movelimit;
668 ret->complete = state->complete;
669 ret->grid = snewn(state->w * state->h, char);
670 memcpy(ret->grid, state->grid, state->w * state->h * sizeof(*ret->grid));
671
672 ret->cheated = state->cheated;
673 ret->soln = state->soln;
674 if (ret->soln)
675 ret->soln->refcount++;
676 ret->solnpos = state->solnpos;
677
678 return ret;
679}
680
681static void free_game(game_state *state)
682{
683 if (state->soln && --state->soln->refcount == 0) {
684 sfree(state->soln->moves);
685 sfree(state->soln);
686 }
687 sfree(state->grid);
688 sfree(state);
689}
690
691static char *solve_game(const game_state *state, const game_state *currstate,
692 const char *aux, char **error)
693{
694 int w = state->w, h = state->h, wh = w*h;
695 char *moves, *ret, *p;
696 int i, len, nmoves;
697 char buf[256];
698 struct solver_scratch *scratch;
699
700 if (currstate->complete) {
701 *error = "Puzzle is already solved";
702 return NULL;
703 }
704
705 /*
706 * Find the best solution our solver can give.
707 */
708 moves = snewn(wh, char); /* sure to be enough */
709 nmoves = 0;
710 scratch = new_scratch(w, h);
711 memcpy(scratch->grid2, currstate->grid, wh * sizeof(*scratch->grid2));
712 check_recursion_depth();
713 while (!completed(w, h, scratch->grid2)) {
714 char move = choosemove(w, h, scratch->grid2, FILLX, FILLY,
715 currstate->colours, scratch);
716 fill(w, h, scratch->grid2, FILLX, FILLY, move, scratch->queue[0]);
717 assert(nmoves < wh);
718 moves[nmoves++] = move;
719 }
720 free_scratch(scratch);
721
722 /*
723 * Encode it as a move string.
724 */
725 len = 1; /* trailing NUL */
726 for (i = 0; i < nmoves; i++)
727 len += sprintf(buf, ",%d", moves[i]);
728 ret = snewn(len, char);
729 p = ret;
730 for (i = 0; i < nmoves; i++)
731 p += sprintf(p, "%c%d", (i==0 ? 'S' : ','), moves[i]);
732 assert(p - ret == len - 1);
733
734 sfree(moves);
735 return ret;
736}
737
738static int game_can_format_as_text_now(const game_params *params)
739{
740 return TRUE;
741}
742
743static char *game_text_format(const game_state *state)
744{
745 int w = state->w, h = state->h;
746 char *ret, *p;
747 int x, y, len;
748
749 len = h * (w+1); /* +1 for newline after each row */
750 ret = snewn(len+1, char); /* and +1 for terminating \0 */
751 p = ret;
752
753 for (y = 0; y < h; y++) {
754 for (x = 0; x < w; x++) {
755 char colour = state->grid[y*w+x];
756 char textcolour = (colour > 9 ? 'A' : '0') + colour;
757 *p++ = textcolour;
758 }
759 *p++ = '\n';
760 }
761
762 assert(p - ret == len);
763 *p = '\0';
764
765 return ret;
766}
767
768struct game_ui {
769 int cursor_visible;
770 int cx, cy;
771 enum { VICTORY, DEFEAT } flash_type;
772};
773
774static game_ui *new_ui(const game_state *state)
775{
776 struct game_ui *ui = snew(struct game_ui);
777 ui->cursor_visible = FALSE;
778 ui->cx = FILLX;
779 ui->cy = FILLY;
780 return ui;
781}
782
783static void free_ui(game_ui *ui)
784{
785 sfree(ui);
786}
787
788static char *encode_ui(const game_ui *ui)
789{
790 return NULL;
791}
792
793static void decode_ui(game_ui *ui, const char *encoding)
794{
795}
796
797static void game_changed_state(game_ui *ui, const game_state *oldstate,
798 const game_state *newstate)
799{
800}
801
802struct game_drawstate {
803 int started;
804 int tilesize;
805 int *grid;
806};
807
808#define TILESIZE (ds->tilesize)
809#define PREFERRED_TILESIZE 32
810#define BORDER (TILESIZE / 2)
811#define SEP_WIDTH (TILESIZE / 32)
812#define CURSOR_INSET (TILESIZE / 8)
813#define HIGHLIGHT_WIDTH (TILESIZE / 10)
814#define COORD(x) ( (x) * TILESIZE + BORDER )
815#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
816#define VICTORY_FLASH_FRAME 0.03F
817#define DEFEAT_FLASH_FRAME 0.10F
818
819static char *interpret_move(const game_state *state, game_ui *ui,
820 const game_drawstate *ds,
821 int x, int y, int button)
822{
823 int w = state->w, h = state->h;
824 int tx = -1, ty = -1, move = -1;
825
826 if (button == LEFT_BUTTON) {
827 tx = FROMCOORD(x);
828 ty = FROMCOORD(y);
829 ui->cursor_visible = FALSE;
830 } else if (button == CURSOR_LEFT && ui->cx > 0) {
831 ui->cx--;
832 ui->cursor_visible = TRUE;
833 return "";
834 } else if (button == CURSOR_RIGHT && ui->cx+1 < w) {
835 ui->cx++;
836 ui->cursor_visible = TRUE;
837 return "";
838 } else if (button == CURSOR_UP && ui->cy > 0) {
839 ui->cy--;
840 ui->cursor_visible = TRUE;
841 return "";
842 } else if (button == CURSOR_DOWN && ui->cy+1 < h) {
843 ui->cy++;
844 ui->cursor_visible = TRUE;
845 return "";
846 } else if (button == CURSOR_SELECT) {
847 tx = ui->cx;
848 ty = ui->cy;
849 } else if (button == CURSOR_SELECT2 &&
850 state->soln && state->solnpos < state->soln->nmoves) {
851 move = state->soln->moves[state->solnpos];
852 } else {
853 return NULL;
854 }
855
856 if (tx >= 0 && tx < w && ty >= 0 && ty < h &&
857 state->grid[0] != state->grid[ty*w+tx])
858 move = state->grid[ty*w+tx];
859
860 if (move >= 0 && !state->complete) {
861 char buf[256];
862 sprintf(buf, "M%d", move);
863 return dupstr(buf);
864 }
865
866 return NULL;
867}
868
869static game_state *execute_move(const game_state *state, const char *move)
870{
871 game_state *ret;
872 int c;
873
874 if (move[0] == 'M' &&
875 sscanf(move+1, "%d", &c) == 1 &&
876 c >= 0 &&
877 !state->complete) {
878 int *queue = snewn(state->w * state->h, int);
879 ret = dup_game(state);
880 fill(ret->w, ret->h, ret->grid, FILLX, FILLY, c, queue);
881 ret->moves++;
882 ret->complete = completed(ret->w, ret->h, ret->grid);
883
884 if (ret->soln) {
885 /*
886 * If this move is the correct next one in the stored
887 * solution path, advance solnpos.
888 */
889 if (c == ret->soln->moves[ret->solnpos] &&
890 ret->solnpos+1 < ret->soln->nmoves) {
891 ret->solnpos++;
892 } else {
893 /*
894 * Otherwise, the user has strayed from the path or
895 * else the path has come to an end; either way, the
896 * path is no longer valid.
897 */
898 ret->soln->refcount--;
899 assert(ret->soln->refcount > 0);/* `state' at least still exists */
900 ret->soln = NULL;
901 ret->solnpos = 0;
902 }
903 }
904
905 sfree(queue);
906 return ret;
907 } else if (*move == 'S') {
908 soln *sol;
909 const char *p;
910 int i;
911
912 /*
913 * This is a solve move, so we don't actually _change_ the
914 * grid but merely set up a stored solution path.
915 */
916 move++;
917 sol = snew(soln);
918
919 sol->nmoves = 1;
920 for (p = move; *p; p++) {
921 if (*p == ',')
922 sol->nmoves++;
923 }
924
925 sol->moves = snewn(sol->nmoves, char);
926 for (i = 0, p = move; i < sol->nmoves; i++) {
927 assert(*p);
928 sol->moves[i] = atoi(p);
929 p += strspn(p, "0123456789");
930 if (*p) {
931 assert(*p == ',');
932 p++;
933 }
934 }
935
936 ret = dup_game(state);
937 ret->cheated = TRUE;
938 if (ret->soln && --ret->soln->refcount == 0) {
939 sfree(ret->soln->moves);
940 sfree(ret->soln);
941 }
942 ret->soln = sol;
943 ret->solnpos = 0;
944 sol->refcount = 1;
945 return ret;
946 }
947
948 return NULL;
949}
950
951/* ----------------------------------------------------------------------
952 * Drawing routines.
953 */
954
955static void game_compute_size(const game_params *params, int tilesize,
956 int *x, int *y)
957{
958 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
959 struct { int tilesize; } ads, *ds = &ads;
960 ads.tilesize = tilesize;
961
962 *x = BORDER * 2 + TILESIZE * params->w;
963 *y = BORDER * 2 + TILESIZE * params->h;
964}
965
966static void game_set_size(drawing *dr, game_drawstate *ds,
967 const game_params *params, int tilesize)
968{
969 ds->tilesize = tilesize;
970}
971
972static float *game_colours(frontend *fe, int *ncolours)
973{
974 float *ret = snewn(3 * NCOLOURS, float);
975
976 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
977
978 ret[COL_SEPARATOR * 3 + 0] = 0.0F;
979 ret[COL_SEPARATOR * 3 + 1] = 0.0F;
980 ret[COL_SEPARATOR * 3 + 2] = 0.0F;
981
982 /* red */
983 ret[COL_1 * 3 + 0] = 1.0F;
984 ret[COL_1 * 3 + 1] = 0.0F;
985 ret[COL_1 * 3 + 2] = 0.0F;
986
987 /* yellow */
988 ret[COL_2 * 3 + 0] = 1.0F;
989 ret[COL_2 * 3 + 1] = 1.0F;
990 ret[COL_2 * 3 + 2] = 0.0F;
991
992 /* green */
993 ret[COL_3 * 3 + 0] = 0.0F;
994 ret[COL_3 * 3 + 1] = 1.0F;
995 ret[COL_3 * 3 + 2] = 0.0F;
996
997 /* blue */
998 ret[COL_4 * 3 + 0] = 0.2F;
999 ret[COL_4 * 3 + 1] = 0.3F;
1000 ret[COL_4 * 3 + 2] = 1.0F;
1001
1002 /* orange */
1003 ret[COL_5 * 3 + 0] = 1.0F;
1004 ret[COL_5 * 3 + 1] = 0.5F;
1005 ret[COL_5 * 3 + 2] = 0.0F;
1006
1007 /* purple */
1008 ret[COL_6 * 3 + 0] = 0.5F;
1009 ret[COL_6 * 3 + 1] = 0.0F;
1010 ret[COL_6 * 3 + 2] = 0.7F;
1011
1012 /* brown */
1013 ret[COL_7 * 3 + 0] = 0.5F;
1014 ret[COL_7 * 3 + 1] = 0.3F;
1015 ret[COL_7 * 3 + 2] = 0.3F;
1016
1017 /* light blue */
1018 ret[COL_8 * 3 + 0] = 0.4F;
1019 ret[COL_8 * 3 + 1] = 0.8F;
1020 ret[COL_8 * 3 + 2] = 1.0F;
1021
1022 /* light green */
1023 ret[COL_9 * 3 + 0] = 0.7F;
1024 ret[COL_9 * 3 + 1] = 1.0F;
1025 ret[COL_9 * 3 + 2] = 0.7F;
1026
1027 /* pink */
1028 ret[COL_10 * 3 + 0] = 1.0F;
1029 ret[COL_10 * 3 + 1] = 0.6F;
1030 ret[COL_10 * 3 + 2] = 1.0F;
1031
1032 *ncolours = NCOLOURS;
1033 return ret;
1034}
1035
1036static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1037{
1038 struct game_drawstate *ds = snew(struct game_drawstate);
1039 int w = state->w, h = state->h, wh = w*h;
1040 int i;
1041
1042 ds->started = FALSE;
1043 ds->tilesize = 0;
1044 ds->grid = snewn(wh, int);
1045 for (i = 0; i < wh; i++)
1046 ds->grid[i] = -1;
1047
1048 return ds;
1049}
1050
1051static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1052{
1053 sfree(ds->grid);
1054 sfree(ds);
1055}
1056
1057#define BORDER_L 0x001
1058#define BORDER_R 0x002
1059#define BORDER_U 0x004
1060#define BORDER_D 0x008
1061#define CORNER_UL 0x010
1062#define CORNER_UR 0x020
1063#define CORNER_DL 0x040
1064#define CORNER_DR 0x080
1065#define CURSOR 0x100
1066#define BADFLASH 0x200
1067#define SOLNNEXT 0x400
1068#define COLOUR_SHIFT 11
1069
1070static void draw_tile(drawing *dr, game_drawstate *ds,
1071 int x, int y, int tile)
1072{
1073 int colour;
1074 int tx = COORD(x), ty = COORD(y);
1075
1076 colour = tile >> COLOUR_SHIFT;
1077 if (tile & BADFLASH)
1078 colour = COL_SEPARATOR;
1079 else
1080 colour += COL_1;
1081 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, colour);
1082
1083 if (tile & BORDER_L)
1084 draw_rect(dr, tx, ty,
1085 SEP_WIDTH, TILESIZE, COL_SEPARATOR);
1086 if (tile & BORDER_R)
1087 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
1088 SEP_WIDTH, TILESIZE, COL_SEPARATOR);
1089 if (tile & BORDER_U)
1090 draw_rect(dr, tx, ty,
1091 TILESIZE, SEP_WIDTH, COL_SEPARATOR);
1092 if (tile & BORDER_D)
1093 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
1094 TILESIZE, SEP_WIDTH, COL_SEPARATOR);
1095
1096 if (tile & CORNER_UL)
1097 draw_rect(dr, tx, ty,
1098 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1099 if (tile & CORNER_UR)
1100 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
1101 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1102 if (tile & CORNER_DL)
1103 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
1104 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1105 if (tile & CORNER_DR)
1106 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty + TILESIZE - SEP_WIDTH,
1107 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1108
1109 if (tile & CURSOR)
1110 draw_rect_outline(dr, tx + CURSOR_INSET, ty + CURSOR_INSET,
1111 TILESIZE - 1 - CURSOR_INSET * 2,
1112 TILESIZE - 1 - CURSOR_INSET * 2,
1113 COL_SEPARATOR);
1114
1115 if (tile & SOLNNEXT) {
1116 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, TILESIZE/6,
1117 COL_SEPARATOR, COL_SEPARATOR);
1118 }
1119
1120 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
1121}
1122
1123static void game_redraw(drawing *dr, game_drawstate *ds,
1124 const game_state *oldstate, const game_state *state,
1125 int dir, const game_ui *ui,
1126 float animtime, float flashtime)
1127{
1128 int w = state->w, h = state->h, wh = w*h;
1129 int x, y, flashframe, solnmove;
1130 char *grid;
1131
1132 /* This was entirely cloned from fifteen.c; it should probably be
1133 * moved into some generic 'draw-recessed-rectangle' utility fn. */
1134 if (!ds->started) {
1135 int coords[10];
1136
1137 draw_rect(dr, 0, 0,
1138 TILESIZE * w + 2 * BORDER,
1139 TILESIZE * h + 2 * BORDER, COL_BACKGROUND);
1140 draw_update(dr, 0, 0,
1141 TILESIZE * w + 2 * BORDER,
1142 TILESIZE * h + 2 * BORDER);
1143
1144 /*
1145 * Recessed area containing the whole puzzle.
1146 */
1147 coords[0] = COORD(w) + HIGHLIGHT_WIDTH - 1;
1148 coords[1] = COORD(h) + HIGHLIGHT_WIDTH - 1;
1149 coords[2] = COORD(w) + HIGHLIGHT_WIDTH - 1;
1150 coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
1151 coords[4] = coords[2] - TILESIZE;
1152 coords[5] = coords[3] + TILESIZE;
1153 coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
1154 coords[9] = COORD(h) + HIGHLIGHT_WIDTH - 1;
1155 coords[6] = coords[8] + TILESIZE;
1156 coords[7] = coords[9] - TILESIZE;
1157 draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
1158
1159 coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
1160 coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
1161 draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
1162
1163 draw_rect(dr, COORD(0) - SEP_WIDTH, COORD(0) - SEP_WIDTH,
1164 TILESIZE * w + 2 * SEP_WIDTH, TILESIZE * h + 2 * SEP_WIDTH,
1165 COL_SEPARATOR);
1166
1167 ds->started = 1;
1168 }
1169
1170 if (flashtime > 0) {
1171 float frame = (ui->flash_type == VICTORY ?
1172 VICTORY_FLASH_FRAME : DEFEAT_FLASH_FRAME);
1173 flashframe = (int)(flashtime / frame);
1174 } else {
1175 flashframe = -1;
1176 }
1177
1178 grid = snewn(wh, char);
1179 memcpy(grid, state->grid, wh * sizeof(*grid));
1180
1181 if (state->soln && state->solnpos < state->soln->nmoves) {
1182 int i, *queue;
1183
1184 /*
1185 * Highlight as 'next auto-solver move' every square of the
1186 * target colour which is adjacent to the currently controlled
1187 * region. We do this by first enacting the actual move, then
1188 * flood-filling again in a nonexistent colour, and finally
1189 * reverting to the original grid anything in the new colour
1190 * that was part of the original controlled region. Then
1191 * regions coloured in the dummy colour should be displayed as
1192 * soln_move with the SOLNNEXT flag.
1193 */
1194 solnmove = state->soln->moves[state->solnpos];
1195
1196 queue = snewn(wh, int);
1197 fill(w, h, grid, FILLX, FILLY, solnmove, queue);
1198 fill(w, h, grid, FILLX, FILLY, state->colours, queue);
1199 sfree(queue);
1200
1201 for (i = 0; i < wh; i++)
1202 if (grid[i] == state->colours && state->grid[i] != solnmove)
1203 grid[i] = state->grid[i];
1204 } else {
1205 solnmove = 0; /* placate optimiser */
1206 }
1207
1208 if (flashframe >= 0 && ui->flash_type == VICTORY) {
1209 /*
1210 * Modify the display grid by superimposing our rainbow flash
1211 * on it.
1212 */
1213 for (x = 0; x < w; x++) {
1214 for (y = 0; y < h; y++) {
1215 int flashpos = flashframe - (abs(x - FILLX) + abs(y - FILLY));
1216 if (flashpos >= 0 && flashpos < state->colours)
1217 grid[y*w+x] = flashpos;
1218 }
1219 }
1220 }
1221
1222 for (x = 0; x < w; x++) {
1223 for (y = 0; y < h; y++) {
1224 int pos = y*w+x;
1225 int tile;
1226
1227 if (grid[pos] == state->colours) {
1228 tile = (solnmove << COLOUR_SHIFT) | SOLNNEXT;
1229 } else {
1230 tile = (int)grid[pos] << COLOUR_SHIFT;
1231 }
1232
1233 if (x == 0 || grid[pos-1] != grid[pos])
1234 tile |= BORDER_L;
1235 if (x==w-1 || grid[pos+1] != grid[pos])
1236 tile |= BORDER_R;
1237 if (y == 0 || grid[pos-w] != grid[pos])
1238 tile |= BORDER_U;
1239 if (y==h-1 || grid[pos+w] != grid[pos])
1240 tile |= BORDER_D;
1241 if (x == 0 || y == 0 || grid[pos-w-1] != grid[pos])
1242 tile |= CORNER_UL;
1243 if (x==w-1 || y == 0 || grid[pos-w+1] != grid[pos])
1244 tile |= CORNER_UR;
1245 if (x == 0 || y==h-1 || grid[pos+w-1] != grid[pos])
1246 tile |= CORNER_DL;
1247 if (x==w-1 || y==h-1 || grid[pos+w+1] != grid[pos])
1248 tile |= CORNER_DR;
1249 if (ui->cursor_visible && ui->cx == x && ui->cy == y)
1250 tile |= CURSOR;
1251
1252 if (flashframe >= 0 && ui->flash_type == DEFEAT && flashframe != 1)
1253 tile |= BADFLASH;
1254
1255 if (ds->grid[pos] != tile) {
1256 draw_tile(dr, ds, x, y, tile);
1257 ds->grid[pos] = tile;
1258 }
1259 }
1260 }
1261
1262 sfree(grid);
1263
1264 {
1265 char status[255];
1266
1267 sprintf(status, "%s%d / %d moves",
1268 (state->complete && state->moves <= state->movelimit ?
1269 (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
1270 state->moves >= state->movelimit ? "FAILED! " :
1271 state->cheated ? "Auto-solver used. " :
1272 ""),
1273 state->moves,
1274 state->movelimit);
1275
1276 status_bar(dr, status);
1277 }
1278}
1279
1280static float game_anim_length(const game_state *oldstate,
1281 const game_state *newstate, int dir, game_ui *ui)
1282{
1283 return 0.0F;
1284}
1285
1286static int game_status(const game_state *state)
1287{
1288 if (state->complete && state->moves <= state->movelimit) {
1289 return +1; /* victory! */
1290 } else if (state->moves >= state->movelimit) {
1291 return -1; /* defeat */
1292 } else {
1293 return 0; /* still playing */
1294 }
1295}
1296
1297static float game_flash_length(const game_state *oldstate,
1298 const game_state *newstate, int dir, game_ui *ui)
1299{
1300 if (dir == +1) {
1301 int old_status = game_status(oldstate);
1302 int new_status = game_status(newstate);
1303 if (old_status != new_status) {
1304 assert(old_status == 0);
1305
1306 if (new_status == +1) {
1307 int frames = newstate->w + newstate->h + newstate->colours - 2;
1308 ui->flash_type = VICTORY;
1309 return VICTORY_FLASH_FRAME * frames;
1310 } else {
1311 ui->flash_type = DEFEAT;
1312 return DEFEAT_FLASH_FRAME * 3;
1313 }
1314 }
1315 }
1316 return 0.0F;
1317}
1318
1319static int game_timing_state(const game_state *state, game_ui *ui)
1320{
1321 return TRUE;
1322}
1323
1324static void game_print_size(const game_params *params, float *x, float *y)
1325{
1326}
1327
1328static void game_print(drawing *dr, const game_state *state, int tilesize)
1329{
1330}
1331
1332#ifdef COMBINED
1333#define thegame flood
1334#endif
1335
1336const struct game thegame = {
1337 "Flood", "games.flood", "flood",
1338 default_params,
1339 game_fetch_preset, NULL,
1340 decode_params,
1341 encode_params,
1342 free_params,
1343 dup_params,
1344 TRUE, game_configure, custom_params,
1345 validate_params,
1346 new_game_desc,
1347 validate_desc,
1348 new_game,
1349 dup_game,
1350 free_game,
1351 TRUE, solve_game,
1352 TRUE, game_can_format_as_text_now, game_text_format,
1353 new_ui,
1354 free_ui,
1355 encode_ui,
1356 decode_ui,
1357 game_changed_state,
1358 interpret_move,
1359 execute_move,
1360 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1361 game_colours,
1362 game_new_drawstate,
1363 game_free_drawstate,
1364 game_redraw,
1365 game_anim_length,
1366 game_flash_length,
1367 game_status,
1368 FALSE, FALSE, game_print_size, game_print,
1369 TRUE, /* wants_statusbar */
1370 FALSE, game_timing_state,
1371 0, /* flags */
1372};
diff --git a/apps/plugins/puzzles/src/flood.html b/apps/plugins/puzzles/src/flood.html
new file mode 100644
index 0000000000..21beaa689b
--- /dev/null
+++ b/apps/plugins/puzzles/src/flood.html
@@ -0,0 +1,64 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Flood</title>
7<link rel="previous" href="unruly.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="tracks.html">
12</head>
13<body>
14<p><a href="unruly.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="tracks.html">Next</a></p>
15<h1><a name="C39"></a>Chapter 39: <a name="i0"></a>Flood</h1>
16<p>
17You are given a grid of squares, coloured at random in multiple colours. In each move, you can flood-fill the top left square in a colour of your choice (i.e. every square reachable from the starting square by an orthogonally connected path of squares all the same colour will be filled in the new colour). As you do this, more and more of the grid becomes connected to the starting square.
18</p>
19<p>
20Your aim is to make the whole grid the same colour, in as few moves as possible. The game will set a limit on the number of moves, based on running its own internal solver. You win if you can make the whole grid the same colour in that many moves or fewer.
21</p>
22<p>
23I saw this game (with a fixed grid size, fixed number of colours, and fixed move limit) at http://floodit.appspot.com (no longer accessible).
24</p>
25<h2><a name="S39.1"></a>39.1 <a name="i1"></a>Flood controls</h2>
26<p>
27To play Flood, click the mouse in a square. The top left corner and everything connected to it will be flood-filled with the colour of the square you clicked. Clicking a square the same colour as the top left corner has no effect, and therefore does not count as a move.
28</p>
29<p>
30You can also use the cursor keys to move a cursor (outline black square) around the grid. Pressing the return key will fill the top left corner in the colour of the square under the cursor.
31</p>
32<p>
33(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
34</p>
35<h2><a name="S39.2"></a>39.2 <a name="i2"></a>Flood parameters</h2>
36<p>
37These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
38</p>
39<dl><dt>
40<em>Width</em>, <em>Height</em>
41</dt>
42<dd>
43Size of the grid, in squares.
44</dd>
45<dt>
46<em>Colours</em>
47</dt>
48<dd>
49Number of colours used to fill the grid. Must be at least 3 (with two colours there would only be one legal move at any stage, hence no choice to make at all), and at most 10.
50</dd>
51<dt>
52<em>Extra moves permitted</em>
53</dt>
54<dd>
55Controls the difficulty of the puzzle, by increasing the move limit. In each new grid, Flood will run an internal solver to generate its own solution, and then the value in this field will be added to the length of Flood's solution to generate the game's move limit. So a value of 0 requires you to be just as efficient as Flood's automated solver, and a larger value makes it easier.
56<p>
57(Note that Flood's internal solver will not necessarily find the shortest possible solution, though I believe it's pretty close. For a real challenge, set this value to 0 and then try to solve a grid in <em>strictly fewer</em> moves than the limit you're given!)
58</p>
59
60</dd>
61</dl>
62
63<hr><address></address></body>
64</html>
diff --git a/apps/plugins/puzzles/src/galaxies.R b/apps/plugins/puzzles/src/galaxies.R
new file mode 100644
index 0000000000..957e5dad90
--- /dev/null
+++ b/apps/plugins/puzzles/src/galaxies.R
@@ -0,0 +1,28 @@
1# -*- makefile -*-
2
3GALAXIES_EXTRA = dsf
4
5galaxies : [X] GTK COMMON galaxies GALAXIES_EXTRA galaxies-icon|no-icon
6
7galaxies : [G] WINDOWS COMMON galaxies GALAXIES_EXTRA galaxies.res|noicon.res
8
9galaxiessolver : [U] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE m.lib
10galaxiessolver : [C] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE
11
12galaxiespicture : [U] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE
13 + m.lib
14galaxiespicture : [C] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE
15
16ALL += galaxies[COMBINED] GALAXIES_EXTRA
17
18!begin am gtk
19GAMES += galaxies
20!end
21
22!begin >list.c
23 A(galaxies) \
24!end
25
26!begin >gamedesc.txt
27galaxies:galaxies.exe:Galaxies:Symmetric polyomino puzzle:Divide the grid into rotationally symmetric regions each centred on a dot.
28!end
diff --git a/apps/plugins/puzzles/src/galaxies.c b/apps/plugins/puzzles/src/galaxies.c
new file mode 100644
index 0000000000..f4f75c629c
--- /dev/null
+++ b/apps/plugins/puzzles/src/galaxies.c
@@ -0,0 +1,3995 @@
1/*
2 * galaxies.c: implementation of 'Tentai Show' from Nikoli,
3 * also sometimes called 'Spiral Galaxies'.
4 *
5 * Notes:
6 *
7 * Grid is stored as size (2n-1), holding edges as well as spaces
8 * (and thus vertices too, at edge intersections).
9 *
10 * Any dot will thus be positioned at one of our grid points,
11 * which saves any faffing with half-of-a-square stuff.
12 *
13 * Edges have on/off state; obviously the actual edges of the
14 * board are fixed to on, and everything else starts as off.
15 *
16 * TTD:
17 * Cleverer solver
18 * Think about how to display remote groups of tiles?
19 *
20 * Bugs:
21 *
22 * Notable puzzle IDs:
23 *
24 * Nikoli's example [web site has wrong highlighting]
25 * (at http://www.nikoli.co.jp/en/puzzles/astronomical_show/):
26 * 5x5:eBbbMlaBbOEnf
27 *
28 * The 'spiral galaxies puzzles are NP-complete' paper
29 * (at http://www.stetson.edu/~efriedma/papers/spiral.pdf):
30 * 7x7:chpgdqqqoezdddki
31 *
32 * Puzzle competition pdf examples
33 * (at http://www.puzzleratings.org/Yurekli2006puz.pdf):
34 * 6x6:EDbaMucCohbrecEi
35 * 10x10:beFbufEEzowDlxldibMHezBQzCdcFzjlci
36 * 13x13:dCemIHFFkJajjgDfdbdBzdzEgjccoPOcztHjBczLDjczqktJjmpreivvNcggFi
37 *
38 */
39
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <assert.h>
44#include <ctype.h>
45#include <math.h>
46
47#include "puzzles.h"
48
49#ifdef DEBUGGING
50#define solvep debug
51#else
52int solver_show_working;
53#define solvep(x) do { if (solver_show_working) { printf x; } } while(0)
54#endif
55
56#ifdef STANDALONE_PICTURE_GENERATOR
57/*
58 * Dirty hack to enable the generator to construct a game ID which
59 * solves to a specified black-and-white bitmap. We define a global
60 * variable here which gives the desired colour of each square, and
61 * we arrange that the grid generator never merges squares of
62 * different colours.
63 *
64 * The bitmap as stored here is a simple int array (at these sizes
65 * it isn't worth doing fiddly bit-packing). picture[y*w+x] is 1
66 * iff the pixel at (x,y) is intended to be black.
67 *
68 * (It might be nice to be able to specify some pixels as
69 * don't-care, to give the generator more leeway. But that might be
70 * fiddly.)
71 */
72static int *picture;
73#endif
74
75enum {
76 COL_BACKGROUND,
77 COL_WHITEBG,
78 COL_BLACKBG,
79 COL_WHITEDOT,
80 COL_BLACKDOT,
81 COL_GRID,
82 COL_EDGE,
83 COL_ARROW,
84 COL_CURSOR,
85 NCOLOURS
86};
87
88#define DIFFLIST(A) \
89 A(NORMAL,Normal,n) \
90 A(UNREASONABLE,Unreasonable,u)
91
92#define ENUM(upper,title,lower) DIFF_ ## upper,
93#define TITLE(upper,title,lower) #title,
94#define ENCODE(upper,title,lower) #lower
95#define CONFIG(upper,title,lower) ":" #title
96enum { DIFFLIST(ENUM)
97 DIFF_IMPOSSIBLE, DIFF_AMBIGUOUS, DIFF_UNFINISHED, DIFF_MAX };
98static char const *const galaxies_diffnames[] = {
99 DIFFLIST(TITLE) "Impossible", "Ambiguous", "Unfinished" };
100static char const galaxies_diffchars[] = DIFFLIST(ENCODE);
101#define DIFFCONFIG DIFFLIST(CONFIG)
102
103struct game_params {
104 /* X and Y is the area of the board as seen by
105 * the user, not the (2n+1) area the game uses. */
106 int w, h, diff;
107};
108
109enum { s_tile, s_edge, s_vertex };
110
111#define F_DOT 1 /* there's a dot here */
112#define F_EDGE_SET 2 /* the edge is set */
113#define F_TILE_ASSOC 4 /* this tile is associated with a dot. */
114#define F_DOT_BLACK 8 /* (ui only) dot is black. */
115#define F_MARK 16 /* scratch flag */
116#define F_REACHABLE 32
117#define F_SCRATCH 64
118#define F_MULTIPLE 128
119#define F_DOT_HOLD 256
120#define F_GOOD 512
121
122typedef struct space {
123 int x, y; /* its position */
124 int type;
125 unsigned int flags;
126 int dotx, doty; /* if flags & F_TILE_ASSOC */
127 int nassoc; /* if flags & F_DOT */
128} space;
129
130#define INGRID(s,x,y) ((x) >= 0 && (y) >= 0 && \
131 (x) < (state)->sx && (y) < (state)->sy)
132#define INUI(s,x,y) ((x) > 0 && (y) > 0 && \
133 (x) < ((state)->sx-1) && (y) < ((state)->sy-1))
134
135#define GRID(s,g,x,y) ((s)->g[((y)*(s)->sx)+(x)])
136#define SPACE(s,x,y) GRID(s,grid,x,y)
137
138struct game_state {
139 int w, h; /* size from params */
140 int sx, sy; /* allocated size, (2x-1)*(2y-1) */
141 space *grid;
142 int completed, used_solve;
143 int ndots;
144 space **dots;
145
146 midend *me; /* to call supersede_game_desc */
147 int cdiff; /* difficulty of current puzzle (for status bar),
148 or -1 if stale. */
149};
150
151static int check_complete(const game_state *state, int *dsf, int *colours);
152static int solver_state(game_state *state, int maxdiff);
153static int solver_obvious(game_state *state);
154static int solver_obvious_dot(game_state *state, space *dot);
155static space *space_opposite_dot(const game_state *state, const space *sp,
156 const space *dot);
157static space *tile_opposite(const game_state *state, const space *sp);
158
159/* ----------------------------------------------------------
160 * Game parameters and presets
161 */
162
163/* make up some sensible default sizes */
164
165#define DEFAULT_PRESET 0
166
167static const game_params galaxies_presets[] = {
168 { 7, 7, DIFF_NORMAL },
169 { 7, 7, DIFF_UNREASONABLE },
170 { 10, 10, DIFF_NORMAL },
171 { 15, 15, DIFF_NORMAL },
172};
173
174static int game_fetch_preset(int i, char **name, game_params **params)
175{
176 game_params *ret;
177 char buf[80];
178
179 if (i < 0 || i >= lenof(galaxies_presets))
180 return FALSE;
181
182 ret = snew(game_params);
183 *ret = galaxies_presets[i]; /* structure copy */
184
185 sprintf(buf, "%dx%d %s", ret->w, ret->h,
186 galaxies_diffnames[ret->diff]);
187
188 if (name) *name = dupstr(buf);
189 *params = ret;
190 return TRUE;
191}
192
193static game_params *default_params(void)
194{
195 game_params *ret;
196 game_fetch_preset(DEFAULT_PRESET, NULL, &ret);
197 return ret;
198}
199
200static void free_params(game_params *params)
201{
202 sfree(params);
203}
204
205static game_params *dup_params(const game_params *params)
206{
207 game_params *ret = snew(game_params);
208 *ret = *params; /* structure copy */
209 return ret;
210}
211
212static void decode_params(game_params *params, char const *string)
213{
214 params->h = params->w = atoi(string);
215 params->diff = DIFF_NORMAL;
216 while (*string && isdigit((unsigned char)*string)) string++;
217 if (*string == 'x') {
218 string++;
219 params->h = atoi(string);
220 while (*string && isdigit((unsigned char)*string)) string++;
221 }
222 if (*string == 'd') {
223 int i;
224 string++;
225 for (i = 0; i <= DIFF_UNREASONABLE; i++)
226 if (*string == galaxies_diffchars[i])
227 params->diff = i;
228 if (*string) string++;
229 }
230}
231
232static char *encode_params(const game_params *params, int full)
233{
234 char str[80];
235 sprintf(str, "%dx%d", params->w, params->h);
236 if (full)
237 sprintf(str + strlen(str), "d%c", galaxies_diffchars[params->diff]);
238 return dupstr(str);
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].sval = dupstr(buf);
252 ret[0].ival = 0;
253
254 ret[1].name = "Height";
255 ret[1].type = C_STRING;
256 sprintf(buf, "%d", params->h);
257 ret[1].sval = dupstr(buf);
258 ret[1].ival = 0;
259
260 ret[2].name = "Difficulty";
261 ret[2].type = C_CHOICES;
262 ret[2].sval = DIFFCONFIG;
263 ret[2].ival = params->diff;
264
265 ret[3].name = NULL;
266 ret[3].type = C_END;
267 ret[3].sval = NULL;
268 ret[3].ival = 0;
269
270 return ret;
271}
272
273static game_params *custom_params(const config_item *cfg)
274{
275 game_params *ret = snew(game_params);
276
277 ret->w = atoi(cfg[0].sval);
278 ret->h = atoi(cfg[1].sval);
279 ret->diff = cfg[2].ival;
280
281 return ret;
282}
283
284static char *validate_params(const game_params *params, int full)
285{
286 if (params->w < 3 || params->h < 3)
287 return "Width and height must both be at least 3";
288 /*
289 * This shouldn't be able to happen at all, since decode_params
290 * and custom_params will never generate anything that isn't
291 * within range.
292 */
293 assert(params->diff <= DIFF_UNREASONABLE);
294
295 return NULL;
296}
297
298/* ----------------------------------------------------------
299 * Game utility functions.
300 */
301
302static void add_dot(space *space) {
303 assert(!(space->flags & F_DOT));
304 space->flags |= F_DOT;
305 space->nassoc = 0;
306}
307
308static void remove_dot(space *space) {
309 assert(space->flags & F_DOT);
310 space->flags &= ~F_DOT;
311}
312
313static void remove_assoc(const game_state *state, space *tile) {
314 if (tile->flags & F_TILE_ASSOC) {
315 SPACE(state, tile->dotx, tile->doty).nassoc--;
316 tile->flags &= ~F_TILE_ASSOC;
317 tile->dotx = -1;
318 tile->doty = -1;
319 }
320}
321
322static void remove_assoc_with_opposite(game_state *state, space *tile) {
323 space *opposite;
324
325 if (!(tile->flags & F_TILE_ASSOC)) {
326 return;
327 }
328
329 opposite = tile_opposite(state, tile);
330 remove_assoc(state, tile);
331
332 if (opposite != NULL && opposite != tile) {
333 remove_assoc(state, opposite);
334 }
335}
336
337static void add_assoc(const game_state *state, space *tile, space *dot) {
338 remove_assoc(state, tile);
339
340#ifdef STANDALONE_PICTURE_GENERATOR
341 if (picture)
342 assert(!picture[(tile->y/2) * state->w + (tile->x/2)] ==
343 !(dot->flags & F_DOT_BLACK));
344#endif
345 tile->flags |= F_TILE_ASSOC;
346 tile->dotx = dot->x;
347 tile->doty = dot->y;
348 dot->nassoc++;
349 /*debug(("add_assoc sp %d %d --> dot %d,%d, new nassoc %d.\n",
350 tile->x, tile->y, dot->x, dot->y, dot->nassoc));*/
351}
352
353static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
354 int *colors;
355 space *opposite = space_opposite_dot(state, tile, dot);
356
357 if (opposite == NULL) {
358 return;
359 }
360 if (opposite->flags & F_DOT) {
361 return;
362 }
363
364 colors = snewn(state->w * state->h, int);
365 check_complete(state, NULL, colors);
366 if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2]) {
367 sfree(colors);
368 return;
369 }
370 if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2]) {
371 sfree(colors);
372 return;
373 }
374
375 sfree(colors);
376 remove_assoc_with_opposite(state, tile);
377 add_assoc(state, tile, dot);
378 remove_assoc_with_opposite(state, opposite);
379 add_assoc(state, opposite, dot);
380}
381
382static space *sp2dot(const game_state *state, int x, int y)
383{
384 space *sp = &SPACE(state, x, y);
385 if (!(sp->flags & F_TILE_ASSOC)) return NULL;
386 return &SPACE(state, sp->dotx, sp->doty);
387}
388
389#define IS_VERTICAL_EDGE(x) ((x % 2) == 0)
390
391static int game_can_format_as_text_now(const game_params *params)
392{
393 return TRUE;
394}
395
396static char *game_text_format(const game_state *state)
397{
398 int maxlen = (state->sx+1)*state->sy, x, y;
399 char *ret, *p;
400 space *sp;
401
402 ret = snewn(maxlen+1, char);
403 p = ret;
404
405 for (y = 0; y < state->sy; y++) {
406 for (x = 0; x < state->sx; x++) {
407 sp = &SPACE(state, x, y);
408 if (sp->flags & F_DOT)
409 *p++ = 'o';
410#if 0
411 else if (sp->flags & (F_REACHABLE|F_MULTIPLE|F_MARK))
412 *p++ = (sp->flags & F_MULTIPLE) ? 'M' :
413 (sp->flags & F_REACHABLE) ? 'R' : 'X';
414#endif
415 else {
416 switch (sp->type) {
417 case s_tile:
418 if (sp->flags & F_TILE_ASSOC) {
419 space *dot = sp2dot(state, sp->x, sp->y);
420 if (dot && dot->flags & F_DOT)
421 *p++ = (dot->flags & F_DOT_BLACK) ? 'B' : 'W';
422 else
423 *p++ = '?'; /* association with not-a-dot. */
424 } else
425 *p++ = ' ';
426 break;
427
428 case s_vertex:
429 *p++ = '+';
430 break;
431
432 case s_edge:
433 if (sp->flags & F_EDGE_SET)
434 *p++ = (IS_VERTICAL_EDGE(x)) ? '|' : '-';
435 else
436 *p++ = ' ';
437 break;
438
439 default:
440 assert(!"shouldn't get here!");
441 }
442 }
443 }
444 *p++ = '\n';
445 }
446
447 assert(p - ret == maxlen);
448 *p = '\0';
449
450 return ret;
451}
452
453static void dbg_state(const game_state *state)
454{
455#ifdef DEBUGGING
456 char *temp = game_text_format(state);
457 debug(("%s\n", temp));
458 sfree(temp);
459#endif
460}
461
462/* Space-enumeration callbacks should all return 1 for 'progress made',
463 * -1 for 'impossible', and 0 otherwise. */
464typedef int (*space_cb)(game_state *state, space *sp, void *ctx);
465
466#define IMPOSSIBLE_QUITS 1
467
468static int foreach_sub(game_state *state, space_cb cb, unsigned int f,
469 void *ctx, int startx, int starty)
470{
471 int x, y, progress = 0, impossible = 0, ret;
472 space *sp;
473
474 for (y = starty; y < state->sy; y += 2) {
475 sp = &SPACE(state, startx, y);
476 for (x = startx; x < state->sx; x += 2) {
477 ret = cb(state, sp, ctx);
478 if (ret == -1) {
479 if (f & IMPOSSIBLE_QUITS) return -1;
480 impossible = -1;
481 } else if (ret == 1) {
482 progress = 1;
483 }
484 sp += 2;
485 }
486 }
487 return impossible ? -1 : progress;
488}
489
490static int foreach_tile(game_state *state, space_cb cb, unsigned int f,
491 void *ctx)
492{
493 return foreach_sub(state, cb, f, ctx, 1, 1);
494}
495
496static int foreach_edge(game_state *state, space_cb cb, unsigned int f,
497 void *ctx)
498{
499 int ret1, ret2;
500
501 ret1 = foreach_sub(state, cb, f, ctx, 0, 1);
502 ret2 = foreach_sub(state, cb, f, ctx, 1, 0);
503
504 if (ret1 == -1 || ret2 == -1) return -1;
505 return (ret1 || ret2) ? 1 : 0;
506}
507
508#if 0
509static int foreach_vertex(game_state *state, space_cb cb, unsigned int f,
510 void *ctx)
511{
512 return foreach_sub(state, cb, f, ctx, 0, 0);
513}
514#endif
515
516#if 0
517static int is_same_assoc(game_state *state,
518 int x1, int y1, int x2, int y2)
519{
520 space *s1, *s2;
521
522 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2))
523 return 0;
524
525 s1 = &SPACE(state, x1, y1);
526 s2 = &SPACE(state, x2, y2);
527 assert(s1->type == s_tile && s2->type == s_tile);
528 if ((s1->flags & F_TILE_ASSOC) && (s2->flags & F_TILE_ASSOC) &&
529 s1->dotx == s2->dotx && s1->doty == s2->doty)
530 return 1;
531 return 0; /* 0 if not same or not both associated. */
532}
533#endif
534
535#if 0
536static int edges_into_vertex(game_state *state,
537 int x, int y)
538{
539 int dx, dy, nx, ny, count = 0;
540
541 assert(SPACE(state, x, y).type == s_vertex);
542 for (dx = -1; dx <= 1; dx++) {
543 for (dy = -1; dy <= 1; dy++) {
544 if (dx != 0 && dy != 0) continue;
545 if (dx == 0 && dy == 0) continue;
546
547 nx = x+dx; ny = y+dy;
548 if (!INGRID(state, nx, ny)) continue;
549 assert(SPACE(state, nx, ny).type == s_edge);
550 if (SPACE(state, nx, ny).flags & F_EDGE_SET)
551 count++;
552 }
553 }
554 return count;
555}
556#endif
557
558static space *space_opposite_dot(const game_state *state, const space *sp,
559 const space *dot)
560{
561 int dx, dy, tx, ty;
562 space *sp2;
563
564 dx = sp->x - dot->x;
565 dy = sp->y - dot->y;
566 tx = dot->x - dx;
567 ty = dot->y - dy;
568 if (!INGRID(state, tx, ty)) return NULL;
569
570 sp2 = &SPACE(state, tx, ty);
571 assert(sp2->type == sp->type);
572 return sp2;
573}
574
575static space *tile_opposite(const game_state *state, const space *sp)
576{
577 space *dot;
578
579 assert(sp->flags & F_TILE_ASSOC);
580 dot = &SPACE(state, sp->dotx, sp->doty);
581 return space_opposite_dot(state, sp, dot);
582}
583
584static int dotfortile(game_state *state, space *tile, space *dot)
585{
586 space *tile_opp = space_opposite_dot(state, tile, dot);
587
588 if (!tile_opp) return 0; /* opposite would be off grid */
589 if (tile_opp->flags & F_TILE_ASSOC &&
590 (tile_opp->dotx != dot->x || tile_opp->doty != dot->y))
591 return 0; /* opposite already associated with diff. dot */
592 return 1;
593}
594
595static void adjacencies(game_state *state, space *sp, space **a1s, space **a2s)
596{
597 int dxs[4] = {-1, 1, 0, 0}, dys[4] = {0, 0, -1, 1};
598 int n, x, y;
599
600 /* this function needs optimising. */
601
602 for (n = 0; n < 4; n++) {
603 x = sp->x+dxs[n];
604 y = sp->y+dys[n];
605
606 if (INGRID(state, x, y)) {
607 a1s[n] = &SPACE(state, x, y);
608
609 x += dxs[n]; y += dys[n];
610
611 if (INGRID(state, x, y))
612 a2s[n] = &SPACE(state, x, y);
613 else
614 a2s[n] = NULL;
615 } else {
616 a1s[n] = a2s[n] = NULL;
617 }
618 }
619}
620
621static int outline_tile_fordot(game_state *state, space *tile, int mark)
622{
623 space *tadj[4], *eadj[4];
624 int i, didsth = 0, edge, same;
625
626 assert(tile->type == s_tile);
627 adjacencies(state, tile, eadj, tadj);
628 for (i = 0; i < 4; i++) {
629 if (!eadj[i]) continue;
630
631 edge = (eadj[i]->flags & F_EDGE_SET) ? 1 : 0;
632 if (tadj[i]) {
633 if (!(tile->flags & F_TILE_ASSOC))
634 same = (tadj[i]->flags & F_TILE_ASSOC) ? 0 : 1;
635 else
636 same = ((tadj[i]->flags & F_TILE_ASSOC) &&
637 tile->dotx == tadj[i]->dotx &&
638 tile->doty == tadj[i]->doty) ? 1 : 0;
639 } else
640 same = 0;
641
642 if (!edge && !same) {
643 if (mark) eadj[i]->flags |= F_EDGE_SET;
644 didsth = 1;
645 } else if (edge && same) {
646 if (mark) eadj[i]->flags &= ~F_EDGE_SET;
647 didsth = 1;
648 }
649 }
650 return didsth;
651}
652
653static void tiles_from_edge(game_state *state, space *sp, space **ts)
654{
655 int xs[2], ys[2];
656
657 if (IS_VERTICAL_EDGE(sp->x)) {
658 xs[0] = sp->x-1; ys[0] = sp->y;
659 xs[1] = sp->x+1; ys[1] = sp->y;
660 } else {
661 xs[0] = sp->x; ys[0] = sp->y-1;
662 xs[1] = sp->x; ys[1] = sp->y+1;
663 }
664 ts[0] = INGRID(state, xs[0], ys[0]) ? &SPACE(state, xs[0], ys[0]) : NULL;
665 ts[1] = INGRID(state, xs[1], ys[1]) ? &SPACE(state, xs[1], ys[1]) : NULL;
666}
667
668/* Returns a move string for use by 'solve', including the initial
669 * 'S' if issolve is true. */
670static char *diff_game(const game_state *src, const game_state *dest,
671 int issolve)
672{
673 int movelen = 0, movesize = 256, x, y, len;
674 char *move = snewn(movesize, char), buf[80], *sep = "";
675 char achar = issolve ? 'a' : 'A';
676 space *sps, *spd;
677
678 assert(src->sx == dest->sx && src->sy == dest->sy);
679
680 if (issolve) {
681 move[movelen++] = 'S';
682 sep = ";";
683 }
684 move[movelen] = '\0';
685 for (x = 0; x < src->sx; x++) {
686 for (y = 0; y < src->sy; y++) {
687 sps = &SPACE(src, x, y);
688 spd = &SPACE(dest, x, y);
689
690 assert(sps->type == spd->type);
691
692 len = 0;
693 if (sps->type == s_tile) {
694 if ((sps->flags & F_TILE_ASSOC) &&
695 (spd->flags & F_TILE_ASSOC)) {
696 if (sps->dotx != spd->dotx ||
697 sps->doty != spd->doty)
698 /* Both associated; change association, if different */
699 len = sprintf(buf, "%s%c%d,%d,%d,%d", sep,
700 (int)achar, x, y, spd->dotx, spd->doty);
701 } else if (sps->flags & F_TILE_ASSOC)
702 /* Only src associated; remove. */
703 len = sprintf(buf, "%sU%d,%d", sep, x, y);
704 else if (spd->flags & F_TILE_ASSOC)
705 /* Only dest associated; add. */
706 len = sprintf(buf, "%s%c%d,%d,%d,%d", sep,
707 (int)achar, x, y, spd->dotx, spd->doty);
708 } else if (sps->type == s_edge) {
709 if ((sps->flags & F_EDGE_SET) != (spd->flags & F_EDGE_SET))
710 /* edge flags are different; flip them. */
711 len = sprintf(buf, "%sE%d,%d", sep, x, y);
712 }
713 if (len) {
714 if (movelen + len >= movesize) {
715 movesize = movelen + len + 256;
716 move = sresize(move, movesize, char);
717 }
718 strcpy(move + movelen, buf);
719 movelen += len;
720 sep = ";";
721 }
722 }
723 }
724 debug(("diff_game src then dest:\n"));
725 dbg_state(src);
726 dbg_state(dest);
727 debug(("diff string %s\n", move));
728 return move;
729}
730
731/* Returns 1 if a dot here would not be too close to any other dots
732 * (and would avoid other game furniture). */
733static int dot_is_possible(game_state *state, space *sp, int allow_assoc)
734{
735 int bx = 0, by = 0, dx, dy;
736 space *adj;
737#ifdef STANDALONE_PICTURE_GENERATOR
738 int col = -1;
739#endif
740
741 switch (sp->type) {
742 case s_tile:
743 bx = by = 1; break;
744 case s_edge:
745 if (IS_VERTICAL_EDGE(sp->x)) {
746 bx = 2; by = 1;
747 } else {
748 bx = 1; by = 2;
749 }
750 break;
751 case s_vertex:
752 bx = by = 2; break;
753 }
754
755 for (dx = -bx; dx <= bx; dx++) {
756 for (dy = -by; dy <= by; dy++) {
757 if (!INGRID(state, sp->x+dx, sp->y+dy)) continue;
758
759 adj = &SPACE(state, sp->x+dx, sp->y+dy);
760
761#ifdef STANDALONE_PICTURE_GENERATOR
762 /*
763 * Check that all the squares we're looking at have the
764 * same colour.
765 */
766 if (picture) {
767 if (adj->type == s_tile) {
768 int c = picture[(adj->y / 2) * state->w + (adj->x / 2)];
769 if (col < 0)
770 col = c;
771 if (c != col)
772 return 0; /* colour mismatch */
773 }
774 }
775#endif
776
777 if (!allow_assoc && (adj->flags & F_TILE_ASSOC))
778 return 0;
779
780 if (dx != 0 || dy != 0) {
781 /* Other than our own square, no dots nearby. */
782 if (adj->flags & (F_DOT))
783 return 0;
784 }
785
786 /* We don't want edges within our rectangle
787 * (but don't care about edges on the edge) */
788 if (abs(dx) < bx && abs(dy) < by &&
789 adj->flags & F_EDGE_SET)
790 return 0;
791 }
792 }
793 return 1;
794}
795
796/* ----------------------------------------------------------
797 * Game generation, structure creation, and descriptions.
798 */
799
800static game_state *blank_game(int w, int h)
801{
802 game_state *state = snew(game_state);
803 int x, y;
804
805 state->w = w;
806 state->h = h;
807
808 state->sx = (w*2)+1;
809 state->sy = (h*2)+1;
810 state->grid = snewn(state->sx * state->sy, space);
811 state->completed = state->used_solve = 0;
812
813 for (x = 0; x < state->sx; x++) {
814 for (y = 0; y < state->sy; y++) {
815 space *sp = &SPACE(state, x, y);
816 memset(sp, 0, sizeof(space));
817 sp->x = x;
818 sp->y = y;
819 if ((x % 2) == 0 && (y % 2) == 0)
820 sp->type = s_vertex;
821 else if ((x % 2) == 0 || (y % 2) == 0) {
822 sp->type = s_edge;
823 if (x == 0 || y == 0 || x == state->sx-1 || y == state->sy-1)
824 sp->flags |= F_EDGE_SET;
825 } else
826 sp->type = s_tile;
827 }
828 }
829
830 state->ndots = 0;
831 state->dots = NULL;
832
833 state->me = NULL; /* filled in by new_game. */
834 state->cdiff = -1;
835
836 return state;
837}
838
839static void game_update_dots(game_state *state)
840{
841 int i, n, sz = state->sx * state->sy;
842
843 if (state->dots) sfree(state->dots);
844 state->ndots = 0;
845
846 for (i = 0; i < sz; i++) {
847 if (state->grid[i].flags & F_DOT) state->ndots++;
848 }
849 state->dots = snewn(state->ndots, space *);
850 n = 0;
851 for (i = 0; i < sz; i++) {
852 if (state->grid[i].flags & F_DOT)
853 state->dots[n++] = &state->grid[i];
854 }
855}
856
857static void clear_game(game_state *state, int cleardots)
858{
859 int x, y;
860
861 /* don't erase edge flags around outline! */
862 for (x = 1; x < state->sx-1; x++) {
863 for (y = 1; y < state->sy-1; y++) {
864 if (cleardots)
865 SPACE(state, x, y).flags = 0;
866 else
867 SPACE(state, x, y).flags &= (F_DOT|F_DOT_BLACK);
868 }
869 }
870 if (cleardots) game_update_dots(state);
871}
872
873static game_state *dup_game(const game_state *state)
874{
875 game_state *ret = blank_game(state->w, state->h);
876
877 ret->completed = state->completed;
878 ret->used_solve = state->used_solve;
879
880 memcpy(ret->grid, state->grid,
881 ret->sx*ret->sy*sizeof(space));
882
883 game_update_dots(ret);
884
885 ret->me = state->me;
886 ret->cdiff = state->cdiff;
887
888 return ret;
889}
890
891static void free_game(game_state *state)
892{
893 if (state->dots) sfree(state->dots);
894 sfree(state->grid);
895 sfree(state);
896}
897
898/* Game description is a sequence of letters representing the number
899 * of spaces (a = 0, y = 24) before the next dot; a-y for a white dot,
900 * and A-Y for a black dot. 'z' is 25 spaces (and no dot).
901 *
902 * I know it's a bitch to generate by hand, so we provide
903 * an edit mode.
904 */
905
906static char *encode_game(game_state *state)
907{
908 char *desc, *p;
909 int run, x, y, area;
910 unsigned int f;
911
912 area = (state->sx-2) * (state->sy-2);
913
914 desc = snewn(area, char);
915 p = desc;
916 run = 0;
917 for (y = 1; y < state->sy-1; y++) {
918 for (x = 1; x < state->sx-1; x++) {
919 f = SPACE(state, x, y).flags;
920
921 /* a/A is 0 spaces between, b/B is 1 space, ...
922 * y/Y is 24 spaces, za/zA is 25 spaces, ...
923 * It's easier to count from 0 because we then
924 * don't have to special-case the top left-hand corner
925 * (which could be a dot with 0 spaces before it). */
926 if (!(f & F_DOT))
927 run++;
928 else {
929 while (run > 24) {
930 *p++ = 'z';
931 run -= 25;
932 }
933 *p++ = ((f & F_DOT_BLACK) ? 'A' : 'a') + run;
934 run = 0;
935 }
936 }
937 }
938 assert(p - desc < area);
939 *p++ = '\0';
940 desc = sresize(desc, p - desc, char);
941
942 return desc;
943}
944
945struct movedot {
946 int op;
947 space *olddot, *newdot;
948};
949
950enum { MD_CHECK, MD_MOVE };
951
952static int movedot_cb(game_state *state, space *tile, void *vctx)
953{
954 struct movedot *md = (struct movedot *)vctx;
955 space *newopp = NULL;
956
957 assert(tile->type == s_tile);
958 assert(md->olddot && md->newdot);
959
960 if (!(tile->flags & F_TILE_ASSOC)) return 0;
961 if (tile->dotx != md->olddot->x || tile->doty != md->olddot->y)
962 return 0;
963
964 newopp = space_opposite_dot(state, tile, md->newdot);
965
966 switch (md->op) {
967 case MD_CHECK:
968 /* If the tile is associated with the old dot, check its
969 * opposite wrt the _new_ dot is empty or same assoc. */
970 if (!newopp) return -1; /* no new opposite */
971 if (newopp->flags & F_TILE_ASSOC) {
972 if (newopp->dotx != md->olddot->x ||
973 newopp->doty != md->olddot->y)
974 return -1; /* associated, but wrong dot. */
975 }
976#ifdef STANDALONE_PICTURE_GENERATOR
977 if (picture) {
978 /*
979 * Reject if either tile and the dot don't match in colour.
980 */
981 if (!(picture[(tile->y/2) * state->w + (tile->x/2)]) ^
982 !(md->newdot->flags & F_DOT_BLACK))
983 return -1;
984 if (!(picture[(newopp->y/2) * state->w + (newopp->x/2)]) ^
985 !(md->newdot->flags & F_DOT_BLACK))
986 return -1;
987 }
988#endif
989 break;
990
991 case MD_MOVE:
992 /* Move dot associations: anything that was associated
993 * with the old dot, and its opposite wrt the new dot,
994 * become associated with the new dot. */
995 assert(newopp);
996 debug(("Associating %d,%d and %d,%d with new dot %d,%d.\n",
997 tile->x, tile->y, newopp->x, newopp->y,
998 md->newdot->x, md->newdot->y));
999 add_assoc(state, tile, md->newdot);
1000 add_assoc(state, newopp, md->newdot);
1001 return 1; /* we did something! */
1002 }
1003 return 0;
1004}
1005
1006/* For the given dot, first see if we could expand it into all the given
1007 * extra spaces (by checking for empty spaces on the far side), and then
1008 * see if we can move the dot to shift the CoG to include the new spaces.
1009 */
1010static int dot_expand_or_move(game_state *state, space *dot,
1011 space **toadd, int nadd)
1012{
1013 space *tileopp;
1014 int i, ret, nnew, cx, cy;
1015 struct movedot md;
1016
1017 debug(("dot_expand_or_move: %d tiles for dot %d,%d\n",
1018 nadd, dot->x, dot->y));
1019 for (i = 0; i < nadd; i++)
1020 debug(("dot_expand_or_move: dot %d,%d\n",
1021 toadd[i]->x, toadd[i]->y));
1022 assert(dot->flags & F_DOT);
1023
1024#ifdef STANDALONE_PICTURE_GENERATOR
1025 if (picture) {
1026 /*
1027 * Reject the expansion totally if any of the new tiles are
1028 * the wrong colour.
1029 */
1030 for (i = 0; i < nadd; i++) {
1031 if (!(picture[(toadd[i]->y/2) * state->w + (toadd[i]->x/2)]) ^
1032 !(dot->flags & F_DOT_BLACK))
1033 return 0;
1034 }
1035 }
1036#endif
1037
1038 /* First off, could we just expand the current dot's tile to cover
1039 * the space(s) passed in and their opposites? */
1040 for (i = 0; i < nadd; i++) {
1041 tileopp = space_opposite_dot(state, toadd[i], dot);
1042 if (!tileopp) goto noexpand;
1043 if (tileopp->flags & F_TILE_ASSOC) goto noexpand;
1044#ifdef STANDALONE_PICTURE_GENERATOR
1045 if (picture) {
1046 /*
1047 * The opposite tiles have to be the right colour as well.
1048 */
1049 if (!(picture[(tileopp->y/2) * state->w + (tileopp->x/2)]) ^
1050 !(dot->flags & F_DOT_BLACK))
1051 goto noexpand;
1052 }
1053#endif
1054 }
1055 /* OK, all spaces have valid empty opposites: associate spaces and
1056 * opposites with our dot. */
1057 for (i = 0; i < nadd; i++) {
1058 tileopp = space_opposite_dot(state, toadd[i], dot);
1059 add_assoc(state, toadd[i], dot);
1060 add_assoc(state, tileopp, dot);
1061 debug(("Added associations %d,%d and %d,%d --> %d,%d\n",
1062 toadd[i]->x, toadd[i]->y,
1063 tileopp->x, tileopp->y,
1064 dot->x, dot->y));
1065 dbg_state(state);
1066 }
1067 return 1;
1068
1069noexpand:
1070 /* Otherwise, try to move dot so as to encompass given spaces: */
1071 /* first, calculate the 'centre of gravity' of the new dot. */
1072 nnew = dot->nassoc + nadd; /* number of tiles assoc. with new dot. */
1073 cx = dot->x * dot->nassoc;
1074 cy = dot->y * dot->nassoc;
1075 for (i = 0; i < nadd; i++) {
1076 cx += toadd[i]->x;
1077 cy += toadd[i]->y;
1078 }
1079 /* If the CoG isn't a whole number, it's not possible. */
1080 if ((cx % nnew) != 0 || (cy % nnew) != 0) {
1081 debug(("Unable to move dot %d,%d, CoG not whole number.\n",
1082 dot->x, dot->y));
1083 return 0;
1084 }
1085 cx /= nnew; cy /= nnew;
1086
1087 /* Check whether all spaces in the old tile would have a good
1088 * opposite wrt the new dot. */
1089 md.olddot = dot;
1090 md.newdot = &SPACE(state, cx, cy);
1091 md.op = MD_CHECK;
1092 ret = foreach_tile(state, movedot_cb, IMPOSSIBLE_QUITS, &md);
1093 if (ret == -1) {
1094 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1095 dot->x, dot->y));
1096 return 0;
1097 }
1098 /* Also check whether all spaces we're adding would have a good
1099 * opposite wrt the new dot. */
1100 for (i = 0; i < nadd; i++) {
1101 tileopp = space_opposite_dot(state, toadd[i], md.newdot);
1102 if (tileopp && (tileopp->flags & F_TILE_ASSOC) &&
1103 (tileopp->dotx != dot->x || tileopp->doty != dot->y)) {
1104 tileopp = NULL;
1105 }
1106 if (!tileopp) {
1107 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1108 dot->x, dot->y));
1109 return 0;
1110 }
1111#ifdef STANDALONE_PICTURE_GENERATOR
1112 if (picture) {
1113 if (!(picture[(tileopp->y/2) * state->w + (tileopp->x/2)]) ^
1114 !(dot->flags & F_DOT_BLACK))
1115 return 0;
1116 }
1117#endif
1118 }
1119
1120 /* If we've got here, we're ok. First, associate all of 'toadd'
1121 * with the _old_ dot (so they'll get fixed up, with their opposites,
1122 * in the next step). */
1123 for (i = 0; i < nadd; i++) {
1124 debug(("Associating to-add %d,%d with old dot %d,%d.\n",
1125 toadd[i]->x, toadd[i]->y, dot->x, dot->y));
1126 add_assoc(state, toadd[i], dot);
1127 }
1128
1129 /* Finally, move the dot and fix up all the old associations. */
1130 debug(("Moving dot at %d,%d to %d,%d\n",
1131 dot->x, dot->y, md.newdot->x, md.newdot->y));
1132 {
1133#ifdef STANDALONE_PICTURE_GENERATOR
1134 int f = dot->flags & F_DOT_BLACK;
1135#endif
1136 remove_dot(dot);
1137 add_dot(md.newdot);
1138#ifdef STANDALONE_PICTURE_GENERATOR
1139 md.newdot->flags |= f;
1140#endif
1141 }
1142
1143 md.op = MD_MOVE;
1144 ret = foreach_tile(state, movedot_cb, 0, &md);
1145 assert(ret == 1);
1146 dbg_state(state);
1147
1148 return 1;
1149}
1150
1151/* Hard-code to a max. of 2x2 squares, for speed (less malloc) */
1152#define MAX_TOADD 4
1153#define MAX_OUTSIDE 8
1154
1155#define MAX_TILE_PERC 20
1156
1157static int generate_try_block(game_state *state, random_state *rs,
1158 int x1, int y1, int x2, int y2)
1159{
1160 int x, y, nadd = 0, nout = 0, i, maxsz;
1161 space *sp, *toadd[MAX_TOADD], *outside[MAX_OUTSIDE], *dot;
1162
1163 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2)) return 0;
1164
1165 /* We limit the maximum size of tiles to be ~2*sqrt(area); so,
1166 * a 5x5 grid shouldn't have anything >10 tiles, a 20x20 grid
1167 * nothing >40 tiles. */
1168 maxsz = (int)sqrt((double)(state->w * state->h)) * 2;
1169 debug(("generate_try_block, maxsz %d\n", maxsz));
1170
1171 /* Make a static list of the spaces; if any space is already
1172 * associated then quit immediately. */
1173 for (x = x1; x <= x2; x += 2) {
1174 for (y = y1; y <= y2; y += 2) {
1175 assert(nadd < MAX_TOADD);
1176 sp = &SPACE(state, x, y);
1177 assert(sp->type == s_tile);
1178 if (sp->flags & F_TILE_ASSOC) return 0;
1179 toadd[nadd++] = sp;
1180 }
1181 }
1182
1183 /* Make a list of the spaces outside of our block, and shuffle it. */
1184#define OUTSIDE(x, y) do { \
1185 if (INGRID(state, (x), (y))) { \
1186 assert(nout < MAX_OUTSIDE); \
1187 outside[nout++] = &SPACE(state, (x), (y)); \
1188 } \
1189} while(0)
1190 for (x = x1; x <= x2; x += 2) {
1191 OUTSIDE(x, y1-2);
1192 OUTSIDE(x, y2+2);
1193 }
1194 for (y = y1; y <= y2; y += 2) {
1195 OUTSIDE(x1-2, y);
1196 OUTSIDE(x2+2, y);
1197 }
1198 shuffle(outside, nout, sizeof(space *), rs);
1199
1200 for (i = 0; i < nout; i++) {
1201 if (!(outside[i]->flags & F_TILE_ASSOC)) continue;
1202 dot = &SPACE(state, outside[i]->dotx, outside[i]->doty);
1203 if (dot->nassoc >= maxsz) {
1204 debug(("Not adding to dot %d,%d, large enough (%d) already.\n",
1205 dot->x, dot->y, dot->nassoc));
1206 continue;
1207 }
1208 if (dot_expand_or_move(state, dot, toadd, nadd)) return 1;
1209 }
1210 return 0;
1211}
1212
1213#ifdef STANDALONE_SOLVER
1214int maxtries;
1215#define MAXTRIES maxtries
1216#else
1217#define MAXTRIES 50
1218#endif
1219
1220#define GP_DOTS 1
1221
1222static void generate_pass(game_state *state, random_state *rs, int *scratch,
1223 int perc, unsigned int flags)
1224{
1225 int sz = state->sx*state->sy, nspc, i, ret;
1226
1227 shuffle(scratch, sz, sizeof(int), rs);
1228
1229 /* This bug took me a, er, little while to track down. On PalmOS,
1230 * which has 16-bit signed ints, puzzles over about 9x9 started
1231 * failing to generate because the nspc calculation would start
1232 * to overflow, causing the dots not to be filled in properly. */
1233 nspc = (int)(((long)perc * (long)sz) / 100L);
1234 debug(("generate_pass: %d%% (%d of %dx%d) squares, flags 0x%x\n",
1235 perc, nspc, state->sx, state->sy, flags));
1236
1237 for (i = 0; i < nspc; i++) {
1238 space *sp = &state->grid[scratch[i]];
1239 int x1 = sp->x, y1 = sp->y, x2 = sp->x, y2 = sp->y;
1240
1241 if (sp->type == s_edge) {
1242 if (IS_VERTICAL_EDGE(sp->x)) {
1243 x1--; x2++;
1244 } else {
1245 y1--; y2++;
1246 }
1247 }
1248 if (sp->type != s_vertex) {
1249 /* heuristic; expanding from vertices tends to generate lots of
1250 * too-big regions of tiles. */
1251 if (generate_try_block(state, rs, x1, y1, x2, y2))
1252 continue; /* we expanded successfully. */
1253 }
1254
1255 if (!(flags & GP_DOTS)) continue;
1256
1257 if ((sp->type == s_edge) && (i % 2)) {
1258 debug(("Omitting edge %d,%d as half-of.\n", sp->x, sp->y));
1259 continue;
1260 }
1261
1262 /* If we've got here we might want to put a dot down. Check
1263 * if we can, and add one if so. */
1264 if (dot_is_possible(state, sp, 0)) {
1265 add_dot(sp);
1266#ifdef STANDALONE_PICTURE_GENERATOR
1267 if (picture) {
1268 if (picture[(sp->y/2) * state->w + (sp->x/2)])
1269 sp->flags |= F_DOT_BLACK;
1270 }
1271#endif
1272 ret = solver_obvious_dot(state, sp);
1273 assert(ret != -1);
1274 debug(("Added dot (and obvious associations) at %d,%d\n",
1275 sp->x, sp->y));
1276 dbg_state(state);
1277 }
1278 }
1279 dbg_state(state);
1280}
1281
1282static char *new_game_desc(const game_params *params, random_state *rs,
1283 char **aux, int interactive)
1284{
1285 game_state *state = blank_game(params->w, params->h), *copy;
1286 char *desc;
1287 int *scratch, sz = state->sx*state->sy, i;
1288 int diff, ntries = 0, cc;
1289
1290 /* Random list of squares to try and process, one-by-one. */
1291 scratch = snewn(sz, int);
1292 for (i = 0; i < sz; i++) scratch[i] = i;
1293
1294generate:
1295 clear_game(state, 1);
1296 ntries++;
1297
1298 /* generate_pass(state, rs, scratch, 10, GP_DOTS); */
1299 /* generate_pass(state, rs, scratch, 100, 0); */
1300 generate_pass(state, rs, scratch, 100, GP_DOTS);
1301
1302 game_update_dots(state);
1303
1304 if (state->ndots == 1) goto generate;
1305
1306#ifdef DEBUGGING
1307 {
1308 char *tmp = encode_game(state);
1309 debug(("new_game_desc state %dx%d:%s\n", params->w, params->h, tmp));
1310 sfree(tmp);
1311 }
1312#endif
1313
1314 for (i = 0; i < state->sx*state->sy; i++)
1315 if (state->grid[i].type == s_tile)
1316 outline_tile_fordot(state, &state->grid[i], TRUE);
1317 cc = check_complete(state, NULL, NULL);
1318 assert(cc);
1319
1320 copy = dup_game(state);
1321 clear_game(copy, 0);
1322 dbg_state(copy);
1323 diff = solver_state(copy, params->diff);
1324 free_game(copy);
1325
1326 assert(diff != DIFF_IMPOSSIBLE);
1327 if (diff != params->diff) {
1328 /*
1329 * We'll grudgingly accept a too-easy puzzle, but we must
1330 * _not_ permit a too-hard one (one which the solver
1331 * couldn't handle at all).
1332 */
1333 if (diff > params->diff ||
1334 ntries < MAXTRIES) goto generate;
1335 }
1336
1337#ifdef STANDALONE_PICTURE_GENERATOR
1338 /*
1339 * Postprocessing pass to prevent excessive numbers of adjacent
1340 * singletons. Iterate over all edges in random shuffled order;
1341 * for each edge that separates two regions, investigate
1342 * whether removing that edge and merging the regions would
1343 * still yield a valid and soluble puzzle. (The two regions
1344 * must also be the same colour, of course.) If so, do it.
1345 *
1346 * This postprocessing pass is slow (due to repeated solver
1347 * invocations), and seems to be unnecessary during normal
1348 * unconstrained game generation. However, when generating a
1349 * game under colour constraints, excessive singletons seem to
1350 * turn up more often, so it's worth doing this.
1351 */
1352 {
1353 int *posns, nposns;
1354 int i, j, newdiff;
1355 game_state *copy2;
1356
1357 nposns = params->w * (params->h+1) + params->h * (params->w+1);
1358 posns = snewn(nposns, int);
1359 for (i = j = 0; i < state->sx*state->sy; i++)
1360 if (state->grid[i].type == s_edge)
1361 posns[j++] = i;
1362 assert(j == nposns);
1363
1364 shuffle(posns, nposns, sizeof(*posns), rs);
1365
1366 for (i = 0; i < nposns; i++) {
1367 int x, y, x0, y0, x1, y1, cx, cy, cn, cx0, cy0, cx1, cy1, tx, ty;
1368 space *s0, *s1, *ts, *d0, *d1, *dn;
1369 int ok;
1370
1371 /* Coordinates of edge space */
1372 x = posns[i] % state->sx;
1373 y = posns[i] / state->sx;
1374
1375 /* Coordinates of square spaces on either side of edge */
1376 x0 = ((x+1) & ~1) - 1; /* round down to next odd number */
1377 y0 = ((y+1) & ~1) - 1;
1378 x1 = 2*x-x0; /* and reflect about x to get x1 */
1379 y1 = 2*y-y0;
1380
1381 if (!INGRID(state, x0, y0) || !INGRID(state, x1, y1))
1382 continue; /* outermost edge of grid */
1383 s0 = &SPACE(state, x0, y0);
1384 s1 = &SPACE(state, x1, y1);
1385 assert(s0->type == s_tile && s1->type == s_tile);
1386
1387 if (s0->dotx == s1->dotx && s0->doty == s1->doty)
1388 continue; /* tiles _already_ owned by same dot */
1389
1390 d0 = &SPACE(state, s0->dotx, s0->doty);
1391 d1 = &SPACE(state, s1->dotx, s1->doty);
1392
1393 if ((d0->flags ^ d1->flags) & F_DOT_BLACK)
1394 continue; /* different colours: cannot merge */
1395
1396 /*
1397 * Work out where the centre of gravity of the new
1398 * region would be.
1399 */
1400 cx = d0->nassoc * d0->x + d1->nassoc * d1->x;
1401 cy = d0->nassoc * d0->y + d1->nassoc * d1->y;
1402 cn = d0->nassoc + d1->nassoc;
1403 if (cx % cn || cy % cn)
1404 continue; /* CoG not at integer coordinates */
1405 cx /= cn;
1406 cy /= cn;
1407 assert(INUI(state, cx, cy));
1408
1409 /*
1410 * Ensure that the CoG would actually be _in_ the new
1411 * region, by verifying that all its surrounding tiles
1412 * belong to one or other of our two dots.
1413 */
1414 cx0 = ((cx+1) & ~1) - 1; /* round down to next odd number */
1415 cy0 = ((cy+1) & ~1) - 1;
1416 cx1 = 2*cx-cx0; /* and reflect about cx to get cx1 */
1417 cy1 = 2*cy-cy0;
1418 ok = TRUE;
1419 for (ty = cy0; ty <= cy1; ty += 2)
1420 for (tx = cx0; tx <= cx1; tx += 2) {
1421 ts = &SPACE(state, tx, ty);
1422 assert(ts->type == s_tile);
1423 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1424 (ts->dotx != d1->x || ts->doty != d1->y))
1425 ok = FALSE;
1426 }
1427 if (!ok)
1428 continue;
1429
1430 /*
1431 * Verify that for every tile in either source region,
1432 * that tile's image in the new CoG is also in one of
1433 * the two source regions.
1434 */
1435 for (ty = 1; ty < state->sy; ty += 2) {
1436 for (tx = 1; tx < state->sx; tx += 2) {
1437 int tx1, ty1;
1438
1439 ts = &SPACE(state, tx, ty);
1440 assert(ts->type == s_tile);
1441 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1442 (ts->dotx != d1->x || ts->doty != d1->y))
1443 continue; /* not part of these tiles anyway */
1444 tx1 = 2*cx-tx;
1445 ty1 = 2*cy-ty;
1446 if (!INGRID(state, tx1, ty1)) {
1447 ok = FALSE;
1448 break;
1449 }
1450 ts = &SPACE(state, cx+cx-tx, cy+cy-ty);
1451 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1452 (ts->dotx != d1->x || ts->doty != d1->y)) {
1453 ok = FALSE;
1454 break;
1455 }
1456 }
1457 if (!ok)
1458 break;
1459 }
1460 if (!ok)
1461 continue;
1462
1463 /*
1464 * Now we're clear to attempt the merge. We take a copy
1465 * of the game state first, so we can revert it easily
1466 * if the resulting puzzle turns out to have become
1467 * insoluble.
1468 */
1469 copy2 = dup_game(state);
1470
1471 remove_dot(d0);
1472 remove_dot(d1);
1473 dn = &SPACE(state, cx, cy);
1474 add_dot(dn);
1475 dn->flags |= (d0->flags & F_DOT_BLACK);
1476 for (ty = 1; ty < state->sy; ty += 2) {
1477 for (tx = 1; tx < state->sx; tx += 2) {
1478 ts = &SPACE(state, tx, ty);
1479 assert(ts->type == s_tile);
1480 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1481 (ts->dotx != d1->x || ts->doty != d1->y))
1482 continue; /* not part of these tiles anyway */
1483 add_assoc(state, ts, dn);
1484 }
1485 }
1486
1487 copy = dup_game(state);
1488 clear_game(copy, 0);
1489 dbg_state(copy);
1490 newdiff = solver_state(copy, params->diff);
1491 free_game(copy);
1492 if (diff == newdiff) {
1493 /* Still just as soluble. Let the merge stand. */
1494 free_game(copy2);
1495 } else {
1496 /* Became insoluble. Revert. */
1497 free_game(state);
1498 state = copy2;
1499 }
1500 }
1501 sfree(posns);
1502 }
1503#endif
1504
1505 desc = encode_game(state);
1506#ifndef STANDALONE_SOLVER
1507 debug(("new_game_desc generated: \n"));
1508 dbg_state(state);
1509#endif
1510
1511 free_game(state);
1512 sfree(scratch);
1513
1514 return desc;
1515}
1516
1517static int dots_too_close(game_state *state)
1518{
1519 /* Quick-and-dirty check, using half the solver:
1520 * solver_obvious will only fail if the dots are
1521 * too close together, so dot-proximity associations
1522 * overlap. */
1523 game_state *tmp = dup_game(state);
1524 int ret = solver_obvious(tmp);
1525 free_game(tmp);
1526 return (ret == -1) ? 1 : 0;
1527}
1528
1529static game_state *load_game(const game_params *params, const char *desc,
1530 char **why_r)
1531{
1532 game_state *state = blank_game(params->w, params->h);
1533 char *why = NULL;
1534 int i, x, y, n;
1535 unsigned int df;
1536
1537 i = 0;
1538 while (*desc) {
1539 n = *desc++;
1540 if (n == 'z') {
1541 i += 25;
1542 continue;
1543 }
1544 if (n >= 'a' && n <= 'y') {
1545 i += n - 'a';
1546 df = 0;
1547 } else if (n >= 'A' && n <= 'Y') {
1548 i += n - 'A';
1549 df = F_DOT_BLACK;
1550 } else {
1551 why = "Invalid characters in game description"; goto fail;
1552 }
1553 /* if we got here we incremented i and have a dot to add. */
1554 y = (i / (state->sx-2)) + 1;
1555 x = (i % (state->sx-2)) + 1;
1556 if (!INUI(state, x, y)) {
1557 why = "Too much data to fit in grid"; goto fail;
1558 }
1559 add_dot(&SPACE(state, x, y));
1560 SPACE(state, x, y).flags |= df;
1561 i++;
1562 }
1563 game_update_dots(state);
1564
1565 if (dots_too_close(state)) {
1566 why = "Dots too close together"; goto fail;
1567 }
1568
1569 return state;
1570
1571fail:
1572 free_game(state);
1573 if (why_r) *why_r = why;
1574 return NULL;
1575}
1576
1577static char *validate_desc(const game_params *params, const char *desc)
1578{
1579 char *why = NULL;
1580 game_state *dummy = load_game(params, desc, &why);
1581 if (dummy) {
1582 free_game(dummy);
1583 assert(!why);
1584 } else
1585 assert(why);
1586 return why;
1587}
1588
1589static game_state *new_game(midend *me, const game_params *params,
1590 const char *desc)
1591{
1592 game_state *state = load_game(params, desc, NULL);
1593 if (!state) {
1594 assert("Unable to load ?validated game.");
1595 return NULL;
1596 }
1597#ifdef EDITOR
1598 state->me = me;
1599#endif
1600 return state;
1601}
1602
1603/* ----------------------------------------------------------
1604 * Solver and all its little wizards.
1605 */
1606
1607int solver_recurse_depth;
1608
1609typedef struct solver_ctx {
1610 game_state *state;
1611 int sz; /* state->sx * state->sy */
1612 space **scratch; /* size sz */
1613
1614} solver_ctx;
1615
1616static solver_ctx *new_solver(game_state *state)
1617{
1618 solver_ctx *sctx = snew(solver_ctx);
1619 sctx->state = state;
1620 sctx->sz = state->sx*state->sy;
1621 sctx->scratch = snewn(sctx->sz, space *);
1622 return sctx;
1623}
1624
1625static void free_solver(solver_ctx *sctx)
1626{
1627 sfree(sctx->scratch);
1628 sfree(sctx);
1629}
1630
1631 /* Solver ideas so far:
1632 *
1633 * For any empty space, work out how many dots it could associate
1634 * with:
1635 * it needs line-of-sight
1636 * it needs an empty space on the far side
1637 * any adjacent lines need corresponding line possibilities.
1638 */
1639
1640/* The solver_ctx should keep a list of dot positions, for quicker looping.
1641 *
1642 * Solver techniques, in order of difficulty:
1643 * obvious adjacency to dots
1644 * transferring tiles to opposite side
1645 * transferring lines to opposite side
1646 * one possible dot for a given tile based on opposite availability
1647 * tile with 3 definite edges next to an associated tile must associate
1648 with same dot.
1649 *
1650 * one possible dot for a given tile based on line-of-sight
1651 */
1652
1653static int solver_add_assoc(game_state *state, space *tile, int dx, int dy,
1654 const char *why)
1655{
1656 space *dot, *tile_opp;
1657
1658 dot = &SPACE(state, dx, dy);
1659 tile_opp = space_opposite_dot(state, tile, dot);
1660
1661 assert(tile->type == s_tile);
1662 if (tile->flags & F_TILE_ASSOC) {
1663 if ((tile->dotx != dx) || (tile->doty != dy)) {
1664 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1665 "already --> %d,%d.\n",
1666 solver_recurse_depth*4, "",
1667 tile->x, tile->y, dx, dy, why,
1668 tile->dotx, tile->doty));
1669 return -1;
1670 }
1671 return 0; /* no-op */
1672 }
1673 if (!tile_opp) {
1674 solvep(("%*s%d,%d --> %d,%d impossible, no opposite tile.\n",
1675 solver_recurse_depth*4, "", tile->x, tile->y, dx, dy));
1676 return -1;
1677 }
1678 if (tile_opp->flags & F_TILE_ASSOC &&
1679 (tile_opp->dotx != dx || tile_opp->doty != dy)) {
1680 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1681 "opposite already --> %d,%d.\n",
1682 solver_recurse_depth*4, "",
1683 tile->x, tile->y, dx, dy, why,
1684 tile_opp->dotx, tile_opp->doty));
1685 return -1;
1686 }
1687
1688 add_assoc(state, tile, dot);
1689 add_assoc(state, tile_opp, dot);
1690 solvep(("%*sSetting %d,%d --> %d,%d (%s).\n",
1691 solver_recurse_depth*4, "",
1692 tile->x, tile->y,dx, dy, why));
1693 solvep(("%*sSetting %d,%d --> %d,%d (%s, opposite).\n",
1694 solver_recurse_depth*4, "",
1695 tile_opp->x, tile_opp->y, dx, dy, why));
1696 return 1;
1697}
1698
1699static int solver_obvious_dot(game_state *state, space *dot)
1700{
1701 int dx, dy, ret, didsth = 0;
1702 space *tile;
1703
1704 debug(("%*ssolver_obvious_dot for %d,%d.\n",
1705 solver_recurse_depth*4, "", dot->x, dot->y));
1706
1707 assert(dot->flags & F_DOT);
1708 for (dx = -1; dx <= 1; dx++) {
1709 for (dy = -1; dy <= 1; dy++) {
1710 if (!INGRID(state, dot->x+dx, dot->y+dy)) continue;
1711
1712 tile = &SPACE(state, dot->x+dx, dot->y+dy);
1713 if (tile->type == s_tile) {
1714 ret = solver_add_assoc(state, tile, dot->x, dot->y,
1715 "next to dot");
1716 if (ret < 0) return -1;
1717 if (ret > 0) didsth = 1;
1718 }
1719 }
1720 }
1721 return didsth;
1722}
1723
1724static int solver_obvious(game_state *state)
1725{
1726 int i, didsth = 0, ret;
1727
1728 debug(("%*ssolver_obvious.\n", solver_recurse_depth*4, ""));
1729
1730 for (i = 0; i < state->ndots; i++) {
1731 ret = solver_obvious_dot(state, state->dots[i]);
1732 if (ret < 0) return -1;
1733 if (ret > 0) didsth = 1;
1734 }
1735 return didsth;
1736}
1737
1738static int solver_lines_opposite_cb(game_state *state, space *edge, void *ctx)
1739{
1740 int didsth = 0, n, dx, dy;
1741 space *tiles[2], *tile_opp, *edge_opp;
1742
1743 assert(edge->type == s_edge);
1744
1745 tiles_from_edge(state, edge, tiles);
1746
1747 /* if tiles[0] && tiles[1] && they're both associated
1748 * and they're both associated with different dots,
1749 * ensure the line is set. */
1750 if (!(edge->flags & F_EDGE_SET) &&
1751 tiles[0] && tiles[1] &&
1752 (tiles[0]->flags & F_TILE_ASSOC) &&
1753 (tiles[1]->flags & F_TILE_ASSOC) &&
1754 (tiles[0]->dotx != tiles[1]->dotx ||
1755 tiles[0]->doty != tiles[1]->doty)) {
1756 /* No edge, but the two adjacent tiles are both
1757 * associated with different dots; add the edge. */
1758 solvep(("%*sSetting edge %d,%d - tiles different dots.\n",
1759 solver_recurse_depth*4, "", edge->x, edge->y));
1760 edge->flags |= F_EDGE_SET;
1761 didsth = 1;
1762 }
1763
1764 if (!(edge->flags & F_EDGE_SET)) return didsth;
1765 for (n = 0; n < 2; n++) {
1766 if (!tiles[n]) continue;
1767 assert(tiles[n]->type == s_tile);
1768 if (!(tiles[n]->flags & F_TILE_ASSOC)) continue;
1769
1770 tile_opp = tile_opposite(state, tiles[n]);
1771 if (!tile_opp) {
1772 solvep(("%*simpossible: edge %d,%d has assoc. tile %d,%d"
1773 " with no opposite.\n",
1774 solver_recurse_depth*4, "",
1775 edge->x, edge->y, tiles[n]->x, tiles[n]->y));
1776 /* edge of tile has no opposite edge (off grid?);
1777 * this is impossible. */
1778 return -1;
1779 }
1780
1781 dx = tiles[n]->x - edge->x;
1782 dy = tiles[n]->y - edge->y;
1783 assert(INGRID(state, tile_opp->x+dx, tile_opp->y+dy));
1784 edge_opp = &SPACE(state, tile_opp->x+dx, tile_opp->y+dy);
1785 if (!(edge_opp->flags & F_EDGE_SET)) {
1786 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n",
1787 solver_recurse_depth*4, "",
1788 tile_opp->x-dx, tile_opp->y-dy, edge->x, edge->y));
1789 edge_opp->flags |= F_EDGE_SET;
1790 didsth = 1;
1791 }
1792 }
1793 return didsth;
1794}
1795
1796static int solver_spaces_oneposs_cb(game_state *state, space *tile, void *ctx)
1797{
1798 int n, eset, ret;
1799 space *edgeadj[4], *tileadj[4];
1800 int dotx, doty;
1801
1802 assert(tile->type == s_tile);
1803 if (tile->flags & F_TILE_ASSOC) return 0;
1804
1805 adjacencies(state, tile, edgeadj, tileadj);
1806
1807 /* Empty tile. If each edge is either set, or associated with
1808 * the same dot, we must also associate with dot. */
1809 eset = 0; dotx = -1; doty = -1;
1810 for (n = 0; n < 4; n++) {
1811 assert(edgeadj[n]);
1812 assert(edgeadj[n]->type == s_edge);
1813 if (edgeadj[n]->flags & F_EDGE_SET) {
1814 eset++;
1815 } else {
1816 assert(tileadj[n]);
1817 assert(tileadj[n]->type == s_tile);
1818
1819 /* If an adjacent tile is empty we can't make any deductions.*/
1820 if (!(tileadj[n]->flags & F_TILE_ASSOC))
1821 return 0;
1822
1823 /* If an adjacent tile is assoc. with a different dot
1824 * we can't make any deductions. */
1825 if (dotx != -1 && doty != -1 &&
1826 (tileadj[n]->dotx != dotx ||
1827 tileadj[n]->doty != doty))
1828 return 0;
1829
1830 dotx = tileadj[n]->dotx;
1831 doty = tileadj[n]->doty;
1832 }
1833 }
1834 if (eset == 4) {
1835 solvep(("%*simpossible: empty tile %d,%d has 4 edges\n",
1836 solver_recurse_depth*4, "",
1837 tile->x, tile->y));
1838 return -1;
1839 }
1840 assert(dotx != -1 && doty != -1);
1841
1842 ret = solver_add_assoc(state, tile, dotx, doty, "rest are edges");
1843 if (ret == -1) return -1;
1844 assert(ret != 0); /* really should have done something. */
1845
1846 return 1;
1847}
1848
1849/* Improved algorithm for tracking line-of-sight from dots, and not spaces.
1850 *
1851 * The solver_ctx already stores a list of dots: the algorithm proceeds by
1852 * expanding outwards from each dot in turn, expanding first to the boundary
1853 * of its currently-connected tile and then to all empty tiles that could see
1854 * it. Empty tiles will be flagged with a 'can see dot <x,y>' sticker.
1855 *
1856 * Expansion will happen by (symmetrically opposite) pairs of squares; if
1857 * a square hasn't an opposite number there's no point trying to expand through
1858 * it. Empty tiles will therefore also be tagged in pairs.
1859 *
1860 * If an empty tile already has a 'can see dot <x,y>' tag from a previous dot,
1861 * it (and its partner) gets untagged (or, rather, a 'can see two dots' tag)
1862 * because we're looking for single-dot possibilities.
1863 *
1864 * Once we've gone through all the dots, any which still have a 'can see dot'
1865 * tag get associated with that dot (because it must have been the only one);
1866 * any without any tag (i.e. that could see _no_ dots) cause an impossibility
1867 * marked.
1868 *
1869 * The expansion will happen each time with a stored list of (space *) pairs,
1870 * rather than a mark-and-sweep idea; that's horrifically inefficient.
1871 *
1872 * expansion algorithm:
1873 *
1874 * * allocate list of (space *) the size of s->sx*s->sy.
1875 * * allocate second grid for (flags, dotx, doty) size of sx*sy.
1876 *
1877 * clear second grid (flags = 0, all dotx and doty = 0)
1878 * flags: F_REACHABLE, F_MULTIPLE
1879 *
1880 *
1881 * * for each dot, start with one pair of tiles that are associated with it --
1882 * * vertex --> (dx+1, dy+1), (dx-1, dy-1)
1883 * * edge --> (adj1, adj2)
1884 * * tile --> (tile, tile) ???
1885 * * mark that pair of tiles with F_MARK, clear all other F_MARKs.
1886 * * add two tiles to start of list.
1887 *
1888 * set start = 0, end = next = 2
1889 *
1890 * from (start to end-1, step 2) {
1891 * * we have two tiles (t1, t2), opposites wrt our dot.
1892 * * for each (at1) sensible adjacent tile to t1 (i.e. not past an edge):
1893 * * work out at2 as the opposite to at1
1894 * * assert at1 and at2 have the same F_MARK values.
1895 * * if at1 & F_MARK ignore it (we've been there on a previous sweep)
1896 * * if either are associated with a different dot
1897 * * mark both with F_MARK (so we ignore them later)
1898 * * otherwise (assoc. with our dot, or empty):
1899 * * mark both with F_MARK
1900 * * add their space * values to the end of the list, set next += 2.
1901 * }
1902 *
1903 * if (end == next)
1904 * * we didn't add any new squares; exit the loop.
1905 * else
1906 * * set start = next+1, end = next. go round again
1907 *
1908 * We've finished expanding from the dot. Now, for each square we have
1909 * in our list (--> each square with F_MARK):
1910 * * if the tile is empty:
1911 * * if F_REACHABLE was already set
1912 * * set F_MULTIPLE
1913 * * otherwise
1914 * * set F_REACHABLE, set dotx and doty to our dot.
1915 *
1916 * Then, continue the whole thing for each dot in turn.
1917 *
1918 * Once we've done for each dot, go through the entire grid looking for
1919 * empty tiles: for each empty tile:
1920 * if F_REACHABLE and not F_MULTIPLE, set that dot (and its double)
1921 * if !F_REACHABLE, return as impossible.
1922 *
1923 */
1924
1925/* Returns 1 if this tile is either already associated with this dot,
1926 * or blank. */
1927static int solver_expand_checkdot(space *tile, space *dot)
1928{
1929 if (!(tile->flags & F_TILE_ASSOC)) return 1;
1930 if (tile->dotx == dot->x && tile->doty == dot->y) return 1;
1931 return 0;
1932}
1933
1934static void solver_expand_fromdot(game_state *state, space *dot, solver_ctx *sctx)
1935{
1936 int i, j, x, y, start, end, next;
1937 space *sp;
1938
1939 /* Clear the grid of the (space) flags we'll use. */
1940
1941 /* This is well optimised; analysis showed that:
1942 for (i = 0; i < sctx->sz; i++) state->grid[i].flags &= ~F_MARK;
1943 took up ~85% of the total function time! */
1944 for (y = 1; y < state->sy; y += 2) {
1945 sp = &SPACE(state, 1, y);
1946 for (x = 1; x < state->sx; x += 2, sp += 2)
1947 sp->flags &= ~F_MARK;
1948 }
1949
1950 /* Seed the list of marked squares with two that must be associated
1951 * with our dot (possibly the same space) */
1952 if (dot->type == s_tile) {
1953 sctx->scratch[0] = sctx->scratch[1] = dot;
1954 } else if (dot->type == s_edge) {
1955 tiles_from_edge(state, dot, sctx->scratch);
1956 } else if (dot->type == s_vertex) {
1957 /* pick two of the opposite ones arbitrarily. */
1958 sctx->scratch[0] = &SPACE(state, dot->x-1, dot->y-1);
1959 sctx->scratch[1] = &SPACE(state, dot->x+1, dot->y+1);
1960 }
1961 assert(sctx->scratch[0]->flags & F_TILE_ASSOC);
1962 assert(sctx->scratch[1]->flags & F_TILE_ASSOC);
1963
1964 sctx->scratch[0]->flags |= F_MARK;
1965 sctx->scratch[1]->flags |= F_MARK;
1966
1967 debug(("%*sexpand from dot %d,%d seeded with %d,%d and %d,%d.\n",
1968 solver_recurse_depth*4, "", dot->x, dot->y,
1969 sctx->scratch[0]->x, sctx->scratch[0]->y,
1970 sctx->scratch[1]->x, sctx->scratch[1]->y));
1971
1972 start = 0; end = 2; next = 2;
1973
1974expand:
1975 debug(("%*sexpand: start %d, end %d, next %d\n",
1976 solver_recurse_depth*4, "", start, end, next));
1977 for (i = start; i < end; i += 2) {
1978 space *t1 = sctx->scratch[i]/*, *t2 = sctx->scratch[i+1]*/;
1979 space *edges[4], *tileadj[4], *tileadj2;
1980
1981 adjacencies(state, t1, edges, tileadj);
1982
1983 for (j = 0; j < 4; j++) {
1984 assert(edges[j]);
1985 if (edges[j]->flags & F_EDGE_SET) continue;
1986 assert(tileadj[j]);
1987
1988 if (tileadj[j]->flags & F_MARK) continue; /* seen before. */
1989
1990 /* We have a tile adjacent to t1; find its opposite. */
1991 tileadj2 = space_opposite_dot(state, tileadj[j], dot);
1992 if (!tileadj2) {
1993 debug(("%*sMarking %d,%d, no opposite.\n",
1994 solver_recurse_depth*4, "",
1995 tileadj[j]->x, tileadj[j]->y));
1996 tileadj[j]->flags |= F_MARK;
1997 continue; /* no opposite, so mark for next time. */
1998 }
1999 /* If the tile had an opposite we should have either seen both of
2000 * these, or neither of these, before. */
2001 assert(!(tileadj2->flags & F_MARK));
2002
2003 if (solver_expand_checkdot(tileadj[j], dot) &&
2004 solver_expand_checkdot(tileadj2, dot)) {
2005 /* Both tiles could associate with this dot; add them to
2006 * our list. */
2007 debug(("%*sAdding %d,%d and %d,%d to possibles list.\n",
2008 solver_recurse_depth*4, "",
2009 tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y));
2010 sctx->scratch[next++] = tileadj[j];
2011 sctx->scratch[next++] = tileadj2;
2012 }
2013 /* Either way, we've seen these tiles already so mark them. */
2014 debug(("%*sMarking %d,%d and %d,%d.\n",
2015 solver_recurse_depth*4, "",
2016 tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y));
2017 tileadj[j]->flags |= F_MARK;
2018 tileadj2->flags |= F_MARK;
2019 }
2020 }
2021 if (next > end) {
2022 /* We added more squares; go back and try again. */
2023 start = end; end = next; goto expand;
2024 }
2025
2026 /* We've expanded as far as we can go. Now we update the main flags
2027 * on all tiles we've expanded into -- if they were empty, we have
2028 * found possible associations for this dot. */
2029 for (i = 0; i < end; i++) {
2030 if (sctx->scratch[i]->flags & F_TILE_ASSOC) continue;
2031 if (sctx->scratch[i]->flags & F_REACHABLE) {
2032 /* This is (at least) the second dot this tile could
2033 * associate with. */
2034 debug(("%*sempty tile %d,%d could assoc. other dot %d,%d\n",
2035 solver_recurse_depth*4, "",
2036 sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y));
2037 sctx->scratch[i]->flags |= F_MULTIPLE;
2038 } else {
2039 /* This is the first (possibly only) dot. */
2040 debug(("%*sempty tile %d,%d could assoc. 1st dot %d,%d\n",
2041 solver_recurse_depth*4, "",
2042 sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y));
2043 sctx->scratch[i]->flags |= F_REACHABLE;
2044 sctx->scratch[i]->dotx = dot->x;
2045 sctx->scratch[i]->doty = dot->y;
2046 }
2047 }
2048 dbg_state(state);
2049}
2050
2051static int solver_expand_postcb(game_state *state, space *tile, void *ctx)
2052{
2053 assert(tile->type == s_tile);
2054
2055 if (tile->flags & F_TILE_ASSOC) return 0;
2056
2057 if (!(tile->flags & F_REACHABLE)) {
2058 solvep(("%*simpossible: space (%d,%d) can reach no dots.\n",
2059 solver_recurse_depth*4, "", tile->x, tile->y));
2060 return -1;
2061 }
2062 if (tile->flags & F_MULTIPLE) return 0;
2063
2064 return solver_add_assoc(state, tile, tile->dotx, tile->doty,
2065 "single possible dot after expansion");
2066}
2067
2068static int solver_expand_dots(game_state *state, solver_ctx *sctx)
2069{
2070 int i;
2071
2072 for (i = 0; i < sctx->sz; i++)
2073 state->grid[i].flags &= ~(F_REACHABLE|F_MULTIPLE);
2074
2075 for (i = 0; i < state->ndots; i++)
2076 solver_expand_fromdot(state, state->dots[i], sctx);
2077
2078 return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx);
2079}
2080
2081struct recurse_ctx {
2082 space *best;
2083 int bestn;
2084};
2085
2086static int solver_recurse_cb(game_state *state, space *tile, void *ctx)
2087{
2088 struct recurse_ctx *rctx = (struct recurse_ctx *)ctx;
2089 int i, n = 0;
2090
2091 assert(tile->type == s_tile);
2092 if (tile->flags & F_TILE_ASSOC) return 0;
2093
2094 /* We're unassociated: count up all the dots we could associate with. */
2095 for (i = 0; i < state->ndots; i++) {
2096 if (dotfortile(state, tile, state->dots[i]))
2097 n++;
2098 }
2099 if (n > rctx->bestn) {
2100 rctx->bestn = n;
2101 rctx->best = tile;
2102 }
2103 return 0;
2104}
2105
2106#define MAXRECURSE 5
2107
2108static int solver_recurse(game_state *state, int maxdiff)
2109{
2110 int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy;
2111 space *ingrid, *outgrid = NULL, *bestopp;
2112 struct recurse_ctx rctx;
2113
2114 if (solver_recurse_depth >= MAXRECURSE) {
2115 solvep(("Limiting recursion to %d, returning.", MAXRECURSE));
2116 return DIFF_UNFINISHED;
2117 }
2118
2119 /* Work out the cell to recurse on; go through all unassociated tiles
2120 * and find which one has the most possible dots it could associate
2121 * with. */
2122 rctx.best = NULL;
2123 rctx.bestn = 0;
2124
2125 foreach_tile(state, solver_recurse_cb, 0, &rctx);
2126 if (rctx.bestn == 0) return DIFF_IMPOSSIBLE; /* or assert? */
2127 assert(rctx.best);
2128
2129 solvep(("%*sRecursing around %d,%d, with %d possible dots.\n",
2130 solver_recurse_depth*4, "",
2131 rctx.best->x, rctx.best->y, rctx.bestn));
2132
2133#ifdef STANDALONE_SOLVER
2134 solver_recurse_depth++;
2135#endif
2136
2137 ingrid = snewn(gsz, space);
2138 memcpy(ingrid, state->grid, gsz * sizeof(space));
2139
2140 for (n = 0; n < state->ndots; n++) {
2141 memcpy(state->grid, ingrid, gsz * sizeof(space));
2142
2143 if (!dotfortile(state, rctx.best, state->dots[n])) continue;
2144
2145 /* set cell (temporarily) pointing to that dot. */
2146 solver_add_assoc(state, rctx.best,
2147 state->dots[n]->x, state->dots[n]->y,
2148 "Attempting for recursion");
2149
2150 ret = solver_state(state, maxdiff);
2151
2152 if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) {
2153 /* we found our first solved grid; copy it away. */
2154 assert(!outgrid);
2155 outgrid = snewn(gsz, space);
2156 memcpy(outgrid, state->grid, gsz * sizeof(space));
2157 }
2158 /* reset cell back to unassociated. */
2159 bestopp = tile_opposite(state, rctx.best);
2160 assert(bestopp && bestopp->flags & F_TILE_ASSOC);
2161
2162 remove_assoc(state, rctx.best);
2163 remove_assoc(state, bestopp);
2164
2165 if (ret == DIFF_AMBIGUOUS || ret == DIFF_UNFINISHED)
2166 diff = ret;
2167 else if (ret == DIFF_IMPOSSIBLE)
2168 /* no change */;
2169 else {
2170 /* precisely one solution */
2171 if (diff == DIFF_IMPOSSIBLE)
2172 diff = DIFF_UNREASONABLE;
2173 else
2174 diff = DIFF_AMBIGUOUS;
2175 }
2176 /* if we've found >1 solution, or ran out of recursion,
2177 * give up immediately. */
2178 if (diff == DIFF_AMBIGUOUS || diff == DIFF_UNFINISHED)
2179 break;
2180 }
2181
2182#ifdef STANDALONE_SOLVER
2183 solver_recurse_depth--;
2184#endif
2185
2186 if (outgrid) {
2187 /* we found (at least one) soln; copy it back to state */
2188 memcpy(state->grid, outgrid, gsz * sizeof(space));
2189 sfree(outgrid);
2190 }
2191 sfree(ingrid);
2192 return diff;
2193}
2194
2195static int solver_state(game_state *state, int maxdiff)
2196{
2197 solver_ctx *sctx = new_solver(state);
2198 int ret, diff = DIFF_NORMAL;
2199
2200#ifdef STANDALONE_PICTURE_GENERATOR
2201 /* hack, hack: set picture to NULL during solving so that add_assoc
2202 * won't complain when we attempt recursive guessing and guess wrong */
2203 int *savepic = picture;
2204 picture = NULL;
2205#endif
2206
2207 ret = solver_obvious(state);
2208 if (ret < 0) {
2209 diff = DIFF_IMPOSSIBLE;
2210 goto got_result;
2211 }
2212
2213#define CHECKRET(d) do { \
2214 if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } \
2215 if (ret > 0) { diff = max(diff, (d)); goto cont; } \
2216} while(0)
2217
2218 while (1) {
2219cont:
2220 ret = foreach_edge(state, solver_lines_opposite_cb,
2221 IMPOSSIBLE_QUITS, sctx);
2222 CHECKRET(DIFF_NORMAL);
2223
2224 ret = foreach_tile(state, solver_spaces_oneposs_cb,
2225 IMPOSSIBLE_QUITS, sctx);
2226 CHECKRET(DIFF_NORMAL);
2227
2228 ret = solver_expand_dots(state, sctx);
2229 CHECKRET(DIFF_NORMAL);
2230
2231 if (maxdiff <= DIFF_NORMAL)
2232 break;
2233
2234 /* harder still? */
2235
2236 /* if we reach here, we've made no deductions, so we terminate. */
2237 break;
2238 }
2239
2240 if (check_complete(state, NULL, NULL)) goto got_result;
2241
2242 diff = (maxdiff >= DIFF_UNREASONABLE) ?
2243 solver_recurse(state, maxdiff) : DIFF_UNFINISHED;
2244
2245got_result:
2246 free_solver(sctx);
2247#ifndef STANDALONE_SOLVER
2248 debug(("solver_state ends, diff %s:\n", galaxies_diffnames[diff]));
2249 dbg_state(state);
2250#endif
2251
2252#ifdef STANDALONE_PICTURE_GENERATOR
2253 picture = savepic;
2254#endif
2255
2256 return diff;
2257}
2258
2259#ifndef EDITOR
2260static char *solve_game(const game_state *state, const game_state *currstate,
2261 const char *aux, char **error)
2262{
2263 game_state *tosolve;
2264 char *ret;
2265 int i;
2266 int diff;
2267
2268 tosolve = dup_game(currstate);
2269 diff = solver_state(tosolve, DIFF_UNREASONABLE);
2270 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2271 debug(("solve_game solved with current state.\n"));
2272 goto solved;
2273 }
2274 free_game(tosolve);
2275
2276 tosolve = dup_game(state);
2277 diff = solver_state(tosolve, DIFF_UNREASONABLE);
2278 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2279 debug(("solve_game solved with original state.\n"));
2280 goto solved;
2281 }
2282 free_game(tosolve);
2283
2284 return NULL;
2285
2286solved:
2287 /*
2288 * Clear tile associations: the solution will only include the
2289 * edges.
2290 */
2291 for (i = 0; i < tosolve->sx*tosolve->sy; i++)
2292 tosolve->grid[i].flags &= ~F_TILE_ASSOC;
2293 ret = diff_game(currstate, tosolve, 1);
2294 free_game(tosolve);
2295 return ret;
2296}
2297#endif
2298
2299/* ----------------------------------------------------------
2300 * User interface.
2301 */
2302
2303struct game_ui {
2304 int dragging;
2305 int dx, dy; /* pixel coords of drag pos. */
2306 int dotx, doty; /* grid coords of dot we're dragging from. */
2307 int srcx, srcy; /* grid coords of drag start */
2308 int cur_x, cur_y, cur_visible;
2309};
2310
2311static game_ui *new_ui(const game_state *state)
2312{
2313 game_ui *ui = snew(game_ui);
2314 ui->dragging = FALSE;
2315 ui->cur_x = ui->cur_y = 1;
2316 ui->cur_visible = 0;
2317 return ui;
2318}
2319
2320static void free_ui(game_ui *ui)
2321{
2322 sfree(ui);
2323}
2324
2325static char *encode_ui(const game_ui *ui)
2326{
2327 return NULL;
2328}
2329
2330static void decode_ui(game_ui *ui, const char *encoding)
2331{
2332}
2333
2334static void game_changed_state(game_ui *ui, const game_state *oldstate,
2335 const game_state *newstate)
2336{
2337}
2338
2339#define FLASH_TIME 0.15F
2340
2341#define PREFERRED_TILE_SIZE 32
2342#define TILE_SIZE (ds->tilesize)
2343#define DOT_SIZE (TILE_SIZE / 4)
2344#define EDGE_THICKNESS (max(TILE_SIZE / 16, 2))
2345#define BORDER TILE_SIZE
2346
2347#define COORD(x) ( (x) * TILE_SIZE + BORDER )
2348#define SCOORD(x) ( ((x) * TILE_SIZE)/2 + BORDER )
2349#define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE )
2350
2351#define DRAW_WIDTH (BORDER * 2 + ds->w * TILE_SIZE)
2352#define DRAW_HEIGHT (BORDER * 2 + ds->h * TILE_SIZE)
2353
2354#define CURSOR_SIZE DOT_SIZE
2355
2356struct game_drawstate {
2357 int started;
2358 int w, h;
2359 int tilesize;
2360 unsigned long *grid;
2361 int *dx, *dy;
2362 blitter *bl;
2363 blitter *blmirror;
2364
2365 int dragging, dragx, dragy;
2366
2367 int *colour_scratch;
2368
2369 int cx, cy, cur_visible;
2370 blitter *cur_bl;
2371};
2372
2373#define CORNER_TOLERANCE 0.15F
2374#define CENTRE_TOLERANCE 0.15F
2375
2376/*
2377 * Round FP coordinates to the centre of the nearest edge.
2378 */
2379#ifndef EDITOR
2380static void coord_round_to_edge(float x, float y, int *xr, int *yr)
2381{
2382 float xs, ys, xv, yv, dx, dy;
2383
2384 /*
2385 * Find the nearest square-centre.
2386 */
2387 xs = (float)floor(x) + 0.5F;
2388 ys = (float)floor(y) + 0.5F;
2389
2390 /*
2391 * Find the nearest grid vertex.
2392 */
2393 xv = (float)floor(x + 0.5F);
2394 yv = (float)floor(y + 0.5F);
2395
2396 /*
2397 * Determine whether the horizontal or vertical edge from that
2398 * vertex alongside that square is closer to us, by comparing
2399 * distances from the square cente.
2400 */
2401 dx = (float)fabs(x - xs);
2402 dy = (float)fabs(y - ys);
2403 if (dx > dy) {
2404 /* Vertical edge: x-coord of corner,
2405 * y-coord of square centre. */
2406 *xr = 2 * (int)xv;
2407 *yr = 1 + 2 * (int)floor(ys);
2408 } else {
2409 /* Horizontal edge: x-coord of square centre,
2410 * y-coord of corner. */
2411 *xr = 1 + 2 * (int)floor(xs);
2412 *yr = 2 * (int)yv;
2413 }
2414}
2415#endif
2416
2417#ifdef EDITOR
2418static char *interpret_move(const game_state *state, game_ui *ui,
2419 const game_drawstate *ds,
2420 int x, int y, int button)
2421{
2422 char buf[80];
2423 int px, py;
2424 space *sp;
2425
2426 px = 2*FROMCOORD((float)x) + 0.5;
2427 py = 2*FROMCOORD((float)y) + 0.5;
2428
2429 state->cdiff = -1;
2430
2431 if (button == 'C' || button == 'c') return dupstr("C");
2432
2433 if (button == 'S' || button == 's') {
2434 char *ret;
2435 game_state *tmp = dup_game(state);
2436 state->cdiff = solver_state(tmp, DIFF_UNREASONABLE-1);
2437 ret = diff_game(state, tmp, 0);
2438 free_game(tmp);
2439 return ret;
2440 }
2441
2442 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2443 if (!INUI(state, px, py)) return NULL;
2444 sp = &SPACE(state, px, py);
2445 if (!dot_is_possible(state, sp, 1)) return NULL;
2446 sprintf(buf, "%c%d,%d",
2447 (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py);
2448 return dupstr(buf);
2449 }
2450
2451 return NULL;
2452}
2453#else
2454static char *interpret_move(const game_state *state, game_ui *ui,
2455 const game_drawstate *ds,
2456 int x, int y, int button)
2457{
2458 /* UI operations (play mode):
2459 *
2460 * Toggle edge (set/unset) (left-click on edge)
2461 * Associate space with dot (left-drag from dot)
2462 * Unassociate space (left-drag from space off grid)
2463 * Autofill lines around shape? (right-click?)
2464 *
2465 * (edit mode; will clear all lines/associations)
2466 *
2467 * Add or remove dot (left-click)
2468 */
2469 char buf[80];
2470 const char *sep = "";
2471 int px, py;
2472 space *sp, *dot;
2473
2474 buf[0] = '\0';
2475
2476 if (button == 'H' || button == 'h') {
2477 char *ret;
2478 game_state *tmp = dup_game(state);
2479 solver_obvious(tmp);
2480 ret = diff_game(state, tmp, 0);
2481 free_game(tmp);
2482 return ret;
2483 }
2484
2485 if (button == LEFT_BUTTON) {
2486 ui->cur_visible = 0;
2487 coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y),
2488 &px, &py);
2489
2490 if (!INUI(state, px, py)) return NULL;
2491
2492 sp = &SPACE(state, px, py);
2493 assert(sp->type == s_edge);
2494 {
2495 sprintf(buf, "E%d,%d", px, py);
2496 return dupstr(buf);
2497 }
2498 } else if (button == RIGHT_BUTTON) {
2499 int px1, py1;
2500
2501 ui->cur_visible = 0;
2502
2503 px = (int)(2*FROMCOORD((float)x) + 0.5);
2504 py = (int)(2*FROMCOORD((float)y) + 0.5);
2505
2506 dot = NULL;
2507
2508 /*
2509 * If there's a dot anywhere nearby, we pick up an arrow
2510 * pointing at that dot.
2511 */
2512 for (py1 = py-1; py1 <= py+1; py1++)
2513 for (px1 = px-1; px1 <= px+1; px1++) {
2514 if (px1 >= 0 && px1 < state->sx &&
2515 py1 >= 0 && py1 < state->sy &&
2516 x >= SCOORD(px1-1) && x < SCOORD(px1+1) &&
2517 y >= SCOORD(py1-1) && y < SCOORD(py1+1) &&
2518 SPACE(state, px1, py1).flags & F_DOT) {
2519 /*
2520 * Found a dot. Begin a drag from it.
2521 */
2522 dot = &SPACE(state, px1, py1);
2523 ui->srcx = px1;
2524 ui->srcy = py1;
2525 goto done; /* multi-level break */
2526 }
2527 }
2528
2529 /*
2530 * Otherwise, find the nearest _square_, and pick up the
2531 * same arrow as it's got on it, if any.
2532 */
2533 if (!dot) {
2534 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2535 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2536 if (px >= 0 && px < state->sx && py >= 0 && py < state->sy) {
2537 sp = &SPACE(state, px, py);
2538 if (sp->flags & F_TILE_ASSOC) {
2539 dot = &SPACE(state, sp->dotx, sp->doty);
2540 ui->srcx = px;
2541 ui->srcy = py;
2542 }
2543 }
2544 }
2545
2546 done:
2547 /*
2548 * Now, if we've managed to find a dot, begin a drag.
2549 */
2550 if (dot) {
2551 ui->dragging = TRUE;
2552 ui->dx = x;
2553 ui->dy = y;
2554 ui->dotx = dot->x;
2555 ui->doty = dot->y;
2556 return "";
2557 }
2558 } else if (button == RIGHT_DRAG && ui->dragging) {
2559 /* just move the drag coords. */
2560 ui->dx = x;
2561 ui->dy = y;
2562 return "";
2563 } else if (button == RIGHT_RELEASE && ui->dragging) {
2564 ui->dragging = FALSE;
2565
2566 /*
2567 * Drags are always targeted at a single square.
2568 */
2569 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2570 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2571
2572 /*
2573 * Dragging an arrow on to the same square it started from
2574 * is a null move; just update the ui and finish.
2575 */
2576 if (px == ui->srcx && py == ui->srcy)
2577 return "";
2578
2579 /*
2580 * Otherwise, we remove the arrow from its starting
2581 * square if we didn't start from a dot...
2582 */
2583 if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) &&
2584 SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) {
2585 sprintf(buf + strlen(buf), "%sU%d,%d", sep, ui->srcx, ui->srcy);
2586 sep = ";";
2587 }
2588
2589 /*
2590 * ... and if the square we're moving it _to_ is valid, we
2591 * add one there instead.
2592 */
2593 if (INUI(state, px, py)) {
2594 sp = &SPACE(state, px, py);
2595
2596 if (!(sp->flags & F_DOT))
2597 sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
2598 sep, px, py, ui->dotx, ui->doty);
2599 }
2600
2601 if (buf[0])
2602 return dupstr(buf);
2603 else
2604 return "";
2605 } else if (IS_CURSOR_MOVE(button)) {
2606 move_cursor(button, &ui->cur_x, &ui->cur_y, state->sx-1, state->sy-1, 0);
2607 if (ui->cur_x < 1) ui->cur_x = 1;
2608 if (ui->cur_y < 1) ui->cur_y = 1;
2609 ui->cur_visible = 1;
2610 if (ui->dragging) {
2611 ui->dx = SCOORD(ui->cur_x);
2612 ui->dy = SCOORD(ui->cur_y);
2613 }
2614 return "";
2615 } else if (IS_CURSOR_SELECT(button)) {
2616 if (!ui->cur_visible) {
2617 ui->cur_visible = 1;
2618 return "";
2619 }
2620 sp = &SPACE(state, ui->cur_x, ui->cur_y);
2621 if (ui->dragging) {
2622 ui->dragging = FALSE;
2623
2624 if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) &&
2625 SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) {
2626 sprintf(buf, "%sU%d,%d", sep, ui->srcx, ui->srcy);
2627 sep = ";";
2628 }
2629 if (sp->type == s_tile && !(sp->flags & F_DOT) && !(sp->flags & F_TILE_ASSOC)) {
2630 sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
2631 sep, ui->cur_x, ui->cur_y, ui->dotx, ui->doty);
2632 }
2633 return dupstr(buf);
2634 } else if (sp->flags & F_DOT) {
2635 ui->dragging = TRUE;
2636 ui->dx = SCOORD(ui->cur_x);
2637 ui->dy = SCOORD(ui->cur_y);
2638 ui->dotx = ui->srcx = ui->cur_x;
2639 ui->doty = ui->srcy = ui->cur_y;
2640 return "";
2641 } else if (sp->flags & F_TILE_ASSOC) {
2642 assert(sp->type == s_tile);
2643 ui->dragging = TRUE;
2644 ui->dx = SCOORD(ui->cur_x);
2645 ui->dy = SCOORD(ui->cur_y);
2646 ui->dotx = sp->dotx;
2647 ui->doty = sp->doty;
2648 ui->srcx = ui->cur_x;
2649 ui->srcy = ui->cur_y;
2650 return "";
2651 } else if (sp->type == s_edge) {
2652 sprintf(buf, "E%d,%d", ui->cur_x, ui->cur_y);
2653 return dupstr(buf);
2654 }
2655 }
2656
2657 return NULL;
2658}
2659#endif
2660
2661static int check_complete(const game_state *state, int *dsf, int *colours)
2662{
2663 int w = state->w, h = state->h;
2664 int x, y, i, ret;
2665
2666 int free_dsf;
2667 struct sqdata {
2668 int minx, miny, maxx, maxy;
2669 int cx, cy;
2670 int valid, colour;
2671 } *sqdata;
2672
2673 if (!dsf) {
2674 dsf = snew_dsf(w*h);
2675 free_dsf = TRUE;
2676 } else {
2677 dsf_init(dsf, w*h);
2678 free_dsf = FALSE;
2679 }
2680
2681 /*
2682 * During actual game play, completion checking is done on the
2683 * basis of the edges rather than the square associations. So
2684 * first we must go through the grid figuring out the connected
2685 * components into which the edges divide it.
2686 */
2687 for (y = 0; y < h; y++)
2688 for (x = 0; x < w; x++) {
2689 if (y+1 < h && !(SPACE(state, 2*x+1, 2*y+2).flags & F_EDGE_SET))
2690 dsf_merge(dsf, y*w+x, (y+1)*w+x);
2691 if (x+1 < w && !(SPACE(state, 2*x+2, 2*y+1).flags & F_EDGE_SET))
2692 dsf_merge(dsf, y*w+x, y*w+(x+1));
2693 }
2694
2695 /*
2696 * That gives us our connected components. Now, for each
2697 * component, decide whether it's _valid_. A valid component is
2698 * one which:
2699 *
2700 * - is 180-degree rotationally symmetric
2701 * - has a dot at its centre of symmetry
2702 * - has no other dots anywhere within it (including on its
2703 * boundary)
2704 * - contains no internal edges (i.e. edges separating two
2705 * squares which are both part of the component).
2706 */
2707
2708 /*
2709 * First, go through the grid finding the bounding box of each
2710 * component.
2711 */
2712 sqdata = snewn(w*h, struct sqdata);
2713 for (i = 0; i < w*h; i++) {
2714 sqdata[i].minx = w+1;
2715 sqdata[i].miny = h+1;
2716 sqdata[i].maxx = sqdata[i].maxy = -1;
2717 sqdata[i].valid = FALSE;
2718 }
2719 for (y = 0; y < h; y++)
2720 for (x = 0; x < w; x++) {
2721 i = dsf_canonify(dsf, y*w+x);
2722 if (sqdata[i].minx > x)
2723 sqdata[i].minx = x;
2724 if (sqdata[i].maxx < x)
2725 sqdata[i].maxx = x;
2726 if (sqdata[i].miny > y)
2727 sqdata[i].miny = y;
2728 if (sqdata[i].maxy < y)
2729 sqdata[i].maxy = y;
2730 sqdata[i].valid = TRUE;
2731 }
2732
2733 /*
2734 * Now we're in a position to loop over each actual component
2735 * and figure out where its centre of symmetry has to be if
2736 * it's anywhere.
2737 */
2738 for (i = 0; i < w*h; i++)
2739 if (sqdata[i].valid) {
2740 int cx, cy;
2741 cx = sqdata[i].cx = sqdata[i].minx + sqdata[i].maxx + 1;
2742 cy = sqdata[i].cy = sqdata[i].miny + sqdata[i].maxy + 1;
2743 if (!(SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT))
2744 sqdata[i].valid = FALSE; /* no dot at centre of symmetry */
2745 if (dsf_canonify(dsf, (cy-1)/2*w+(cx-1)/2) != i ||
2746 dsf_canonify(dsf, (cy)/2*w+(cx-1)/2) != i ||
2747 dsf_canonify(dsf, (cy-1)/2*w+(cx)/2) != i ||
2748 dsf_canonify(dsf, (cy)/2*w+(cx)/2) != i)
2749 sqdata[i].valid = FALSE; /* dot at cx,cy isn't ours */
2750 if (SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT_BLACK)
2751 sqdata[i].colour = 2;
2752 else
2753 sqdata[i].colour = 1;
2754 }
2755
2756 /*
2757 * Now we loop over the whole grid again, this time finding
2758 * extraneous dots (any dot which wholly or partially overlaps
2759 * a square and is not at the centre of symmetry of that
2760 * square's component disqualifies the component from validity)
2761 * and extraneous edges (any edge separating two squares
2762 * belonging to the same component also disqualifies that
2763 * component).
2764 */
2765 for (y = 1; y < state->sy-1; y++)
2766 for (x = 1; x < state->sx-1; x++) {
2767 space *sp = &SPACE(state, x, y);
2768
2769 if (sp->flags & F_DOT) {
2770 /*
2771 * There's a dot here. Use it to disqualify any
2772 * component which deserves it.
2773 */
2774 int cx, cy;
2775 for (cy = (y-1) >> 1; cy <= y >> 1; cy++)
2776 for (cx = (x-1) >> 1; cx <= x >> 1; cx++) {
2777 i = dsf_canonify(dsf, cy*w+cx);
2778 if (x != sqdata[i].cx || y != sqdata[i].cy)
2779 sqdata[i].valid = FALSE;
2780 }
2781 }
2782
2783 if (sp->flags & F_EDGE_SET) {
2784 /*
2785 * There's an edge here. Use it to disqualify a
2786 * component if necessary.
2787 */
2788 int cx1 = (x-1) >> 1, cx2 = x >> 1;
2789 int cy1 = (y-1) >> 1, cy2 = y >> 1;
2790 assert((cx1==cx2) ^ (cy1==cy2));
2791 i = dsf_canonify(dsf, cy1*w+cx1);
2792 if (i == dsf_canonify(dsf, cy2*w+cx2))
2793 sqdata[i].valid = FALSE;
2794 }
2795 }
2796
2797 /*
2798 * And finally we test rotational symmetry: for each square in
2799 * the grid, find which component it's in, test that that
2800 * component also has a square in the symmetric position, and
2801 * disqualify it if it doesn't.
2802 */
2803 for (y = 0; y < h; y++)
2804 for (x = 0; x < w; x++) {
2805 int x2, y2;
2806
2807 i = dsf_canonify(dsf, y*w+x);
2808
2809 x2 = sqdata[i].cx - 1 - x;
2810 y2 = sqdata[i].cy - 1 - y;
2811 if (i != dsf_canonify(dsf, y2*w+x2))
2812 sqdata[i].valid = FALSE;
2813 }
2814
2815 /*
2816 * That's it. We now have all the connected components marked
2817 * as valid or not valid. So now we return a `colours' array if
2818 * we were asked for one, and also we return an overall
2819 * true/false value depending on whether _every_ square in the
2820 * grid is part of a valid component.
2821 */
2822 ret = TRUE;
2823 for (i = 0; i < w*h; i++) {
2824 int ci = dsf_canonify(dsf, i);
2825 int thisok = sqdata[ci].valid;
2826 if (colours)
2827 colours[i] = thisok ? sqdata[ci].colour : 0;
2828 ret = ret && thisok;
2829 }
2830
2831 sfree(sqdata);
2832 if (free_dsf)
2833 sfree(dsf);
2834
2835 return ret;
2836}
2837
2838static game_state *execute_move(const game_state *state, const char *move)
2839{
2840 int x, y, ax, ay, n, dx, dy;
2841 game_state *ret = dup_game(state);
2842 space *sp, *dot;
2843 int currently_solving = FALSE;
2844
2845 debug(("%s\n", move));
2846
2847 while (*move) {
2848 char c = *move;
2849 if (c == 'E' || c == 'U' || c == 'M'
2850#ifdef EDITOR
2851 || c == 'D' || c == 'd'
2852#endif
2853 ) {
2854 move++;
2855 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
2856 !INUI(ret, x, y))
2857 goto badmove;
2858
2859 sp = &SPACE(ret, x, y);
2860#ifdef EDITOR
2861 if (c == 'D' || c == 'd') {
2862 unsigned int currf, newf, maskf;
2863
2864 if (!dot_is_possible(ret, sp, 1)) goto badmove;
2865
2866 newf = F_DOT | (c == 'd' ? F_DOT_BLACK : 0);
2867 currf = GRID(ret, grid, x, y).flags;
2868 maskf = F_DOT | F_DOT_BLACK;
2869 /* if we clicked 'white dot':
2870 * white --> empty, empty --> white, black --> white.
2871 * if we clicked 'black dot':
2872 * black --> empty, empty --> black, white --> black.
2873 */
2874 if (currf & maskf) {
2875 sp->flags &= ~maskf;
2876 if ((currf & maskf) != newf)
2877 sp->flags |= newf;
2878 } else
2879 sp->flags |= newf;
2880 sp->nassoc = 0; /* edit-mode disallows associations. */
2881 game_update_dots(ret);
2882 } else
2883#endif
2884 if (c == 'E') {
2885 if (sp->type != s_edge) goto badmove;
2886 sp->flags ^= F_EDGE_SET;
2887 } else if (c == 'U') {
2888 if (sp->type != s_tile || !(sp->flags & F_TILE_ASSOC))
2889 goto badmove;
2890 /* The solver doesn't assume we'll mirror things */
2891 if (currently_solving)
2892 remove_assoc(ret, sp);
2893 else
2894 remove_assoc_with_opposite(ret, sp);
2895 } else if (c == 'M') {
2896 if (!(sp->flags & F_DOT)) goto badmove;
2897 sp->flags ^= F_DOT_HOLD;
2898 }
2899 move += n;
2900 } else if (c == 'A' || c == 'a') {
2901 move++;
2902 if (sscanf(move, "%d,%d,%d,%d%n", &x, &y, &ax, &ay, &n) != 4 ||
2903 x < 1 || y < 1 || x >= (ret->sx-1) || y >= (ret->sy-1) ||
2904 ax < 1 || ay < 1 || ax >= (ret->sx-1) || ay >= (ret->sy-1))
2905 goto badmove;
2906
2907 dot = &GRID(ret, grid, ax, ay);
2908 if (!(dot->flags & F_DOT))goto badmove;
2909 if (dot->flags & F_DOT_HOLD) goto badmove;
2910
2911 for (dx = -1; dx <= 1; dx++) {
2912 for (dy = -1; dy <= 1; dy++) {
2913 sp = &GRID(ret, grid, x+dx, y+dy);
2914 if (sp->type != s_tile) continue;
2915 if (sp->flags & F_TILE_ASSOC) {
2916 space *dot = &SPACE(ret, sp->dotx, sp->doty);
2917 if (dot->flags & F_DOT_HOLD) continue;
2918 }
2919 /* The solver doesn't assume we'll mirror things */
2920 if (currently_solving)
2921 add_assoc(ret, sp, dot);
2922 else
2923 add_assoc_with_opposite(ret, sp, dot);
2924 }
2925 }
2926 move += n;
2927#ifdef EDITOR
2928 } else if (c == 'C') {
2929 move++;
2930 clear_game(ret, 1);
2931#endif
2932 } else if (c == 'S') {
2933 move++;
2934 ret->used_solve = 1;
2935 currently_solving = TRUE;
2936 } else
2937 goto badmove;
2938
2939 if (*move == ';')
2940 move++;
2941 else if (*move)
2942 goto badmove;
2943 }
2944 if (check_complete(ret, NULL, NULL))
2945 ret->completed = 1;
2946 return ret;
2947
2948badmove:
2949 free_game(ret);
2950 return NULL;
2951}
2952
2953/* ----------------------------------------------------------------------
2954 * Drawing routines.
2955 */
2956
2957/* Lines will be much smaller size than squares; say, 1/8 the size?
2958 *
2959 * Need a 'top-left corner of location XxY' to take this into account;
2960 * alternaticaly, that could give the middle of that location, and the
2961 * drawing code would just know the expected dimensions.
2962 *
2963 * We also need something to take a click and work out what it was
2964 * we were interested in. Clicking on vertices is required because
2965 * we may want to drag from them, for example.
2966 */
2967
2968static void game_compute_size(const game_params *params, int sz,
2969 int *x, int *y)
2970{
2971 struct { int tilesize, w, h; } ads, *ds = &ads;
2972
2973 ds->tilesize = sz;
2974 ds->w = params->w;
2975 ds->h = params->h;
2976
2977 *x = DRAW_WIDTH;
2978 *y = DRAW_HEIGHT;
2979}
2980
2981static void game_set_size(drawing *dr, game_drawstate *ds,
2982 const game_params *params, int sz)
2983{
2984 ds->tilesize = sz;
2985
2986 assert(TILE_SIZE > 0);
2987
2988 assert(!ds->bl);
2989 ds->bl = blitter_new(dr, TILE_SIZE, TILE_SIZE);
2990
2991 assert(!ds->blmirror);
2992 ds->blmirror = blitter_new(dr, TILE_SIZE, TILE_SIZE);
2993
2994 assert(!ds->cur_bl);
2995 ds->cur_bl = blitter_new(dr, TILE_SIZE, TILE_SIZE);
2996}
2997
2998static float *game_colours(frontend *fe, int *ncolours)
2999{
3000 float *ret = snewn(3 * NCOLOURS, float);
3001 int i;
3002
3003 /*
3004 * We call game_mkhighlight to ensure the background colour
3005 * isn't completely white. We don't actually use the high- and
3006 * lowlight colours it generates.
3007 */
3008 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_WHITEBG, COL_BLACKBG);
3009
3010 for (i = 0; i < 3; i++) {
3011 /*
3012 * Currently, white dots and white-background squares are
3013 * both pure white.
3014 */
3015 ret[COL_WHITEDOT * 3 + i] = 1.0F;
3016 ret[COL_WHITEBG * 3 + i] = 1.0F;
3017
3018 /*
3019 * But black-background squares are a dark grey, whereas
3020 * black dots are really black.
3021 */
3022 ret[COL_BLACKDOT * 3 + i] = 0.0F;
3023 ret[COL_BLACKBG * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.3F;
3024
3025 /*
3026 * In unfilled squares, we draw a faint gridwork.
3027 */
3028 ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.8F;
3029
3030 /*
3031 * Edges and arrows are filled in in pure black.
3032 */
3033 ret[COL_EDGE * 3 + i] = 0.0F;
3034 ret[COL_ARROW * 3 + i] = 0.0F;
3035 }
3036
3037#ifdef EDITOR
3038 /* tinge the edit background to bluey */
3039 ret[COL_BACKGROUND * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3040 ret[COL_BACKGROUND * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3041 ret[COL_BACKGROUND * 3 + 2] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F);
3042#endif
3043
3044 ret[COL_CURSOR * 3 + 0] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F);
3045 ret[COL_CURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3046 ret[COL_CURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3047
3048 *ncolours = NCOLOURS;
3049 return ret;
3050}
3051
3052static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
3053{
3054 struct game_drawstate *ds = snew(struct game_drawstate);
3055 int i;
3056
3057 ds->started = 0;
3058 ds->w = state->w;
3059 ds->h = state->h;
3060
3061 ds->grid = snewn(ds->w*ds->h, unsigned long);
3062 for (i = 0; i < ds->w*ds->h; i++)
3063 ds->grid[i] = 0xFFFFFFFFUL;
3064 ds->dx = snewn(ds->w*ds->h, int);
3065 ds->dy = snewn(ds->w*ds->h, int);
3066
3067 ds->bl = NULL;
3068 ds->blmirror = NULL;
3069 ds->dragging = FALSE;
3070 ds->dragx = ds->dragy = 0;
3071
3072 ds->colour_scratch = snewn(ds->w * ds->h, int);
3073
3074 ds->cur_bl = NULL;
3075 ds->cx = ds->cy = 0;
3076 ds->cur_visible = 0;
3077
3078 return ds;
3079}
3080
3081static void game_free_drawstate(drawing *dr, game_drawstate *ds)
3082{
3083 if (ds->cur_bl) blitter_free(dr, ds->cur_bl);
3084 sfree(ds->colour_scratch);
3085 if (ds->blmirror) blitter_free(dr, ds->blmirror);
3086 if (ds->bl) blitter_free(dr, ds->bl);
3087 sfree(ds->dx);
3088 sfree(ds->dy);
3089 sfree(ds->grid);
3090 sfree(ds);
3091}
3092
3093#define DRAW_EDGE_L 0x0001
3094#define DRAW_EDGE_R 0x0002
3095#define DRAW_EDGE_U 0x0004
3096#define DRAW_EDGE_D 0x0008
3097#define DRAW_CORNER_UL 0x0010
3098#define DRAW_CORNER_UR 0x0020
3099#define DRAW_CORNER_DL 0x0040
3100#define DRAW_CORNER_DR 0x0080
3101#define DRAW_WHITE 0x0100
3102#define DRAW_BLACK 0x0200
3103#define DRAW_ARROW 0x0400
3104#define DRAW_CURSOR 0x0800
3105#define DOT_SHIFT_C 12
3106#define DOT_SHIFT_M 2
3107#define DOT_WHITE 1UL
3108#define DOT_BLACK 2UL
3109
3110/*
3111 * Draw an arrow centred on (cx,cy), pointing in the direction
3112 * (ddx,ddy). (I.e. pointing at the point (cx+ddx, cy+ddy).
3113 */
3114static void draw_arrow(drawing *dr, game_drawstate *ds,
3115 int cx, int cy, int ddx, int ddy, int col)
3116{
3117 float vlen = (float)sqrt(ddx*ddx+ddy*ddy);
3118 float xdx = ddx/vlen, xdy = ddy/vlen;
3119 float ydx = -xdy, ydy = xdx;
3120 int e1x = cx + (int)(xdx*TILE_SIZE/3), e1y = cy + (int)(xdy*TILE_SIZE/3);
3121 int e2x = cx - (int)(xdx*TILE_SIZE/3), e2y = cy - (int)(xdy*TILE_SIZE/3);
3122 int adx = (int)((ydx-xdx)*TILE_SIZE/8), ady = (int)((ydy-xdy)*TILE_SIZE/8);
3123 int adx2 = (int)((-ydx-xdx)*TILE_SIZE/8), ady2 = (int)((-ydy-xdy)*TILE_SIZE/8);
3124
3125 draw_line(dr, e1x, e1y, e2x, e2y, col);
3126 draw_line(dr, e1x, e1y, e1x+adx, e1y+ady, col);
3127 draw_line(dr, e1x, e1y, e1x+adx2, e1y+ady2, col);
3128}
3129
3130static void draw_square(drawing *dr, game_drawstate *ds, int x, int y,
3131 unsigned long flags, int ddx, int ddy)
3132{
3133 int lx = COORD(x), ly = COORD(y);
3134 int dx, dy;
3135 int gridcol;
3136
3137 clip(dr, lx, ly, TILE_SIZE, TILE_SIZE);
3138
3139 /*
3140 * Draw the tile background.
3141 */
3142 draw_rect(dr, lx, ly, TILE_SIZE, TILE_SIZE,
3143 (flags & DRAW_WHITE ? COL_WHITEBG :
3144 flags & DRAW_BLACK ? COL_BLACKBG : COL_BACKGROUND));
3145
3146 /*
3147 * Draw the grid.
3148 */
3149 gridcol = (flags & DRAW_BLACK ? COL_BLACKDOT : COL_GRID);
3150 draw_rect(dr, lx, ly, 1, TILE_SIZE, gridcol);
3151 draw_rect(dr, lx, ly, TILE_SIZE, 1, gridcol);
3152
3153 /*
3154 * Draw the arrow, if present, or the cursor, if here.
3155 */
3156 if (flags & DRAW_ARROW)
3157 draw_arrow(dr, ds, lx + TILE_SIZE/2, ly + TILE_SIZE/2, ddx, ddy,
3158 (flags & DRAW_CURSOR) ? COL_CURSOR : COL_ARROW);
3159 else if (flags & DRAW_CURSOR)
3160 draw_rect_outline(dr,
3161 lx + TILE_SIZE/2 - CURSOR_SIZE,
3162 ly + TILE_SIZE/2 - CURSOR_SIZE,
3163 2*CURSOR_SIZE+1, 2*CURSOR_SIZE+1,
3164 COL_CURSOR);
3165
3166 /*
3167 * Draw the edges.
3168 */
3169 if (flags & DRAW_EDGE_L)
3170 draw_rect(dr, lx, ly, EDGE_THICKNESS, TILE_SIZE, COL_EDGE);
3171 if (flags & DRAW_EDGE_R)
3172 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly,
3173 EDGE_THICKNESS - 1, TILE_SIZE, COL_EDGE);
3174 if (flags & DRAW_EDGE_U)
3175 draw_rect(dr, lx, ly, TILE_SIZE, EDGE_THICKNESS, COL_EDGE);
3176 if (flags & DRAW_EDGE_D)
3177 draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1,
3178 TILE_SIZE, EDGE_THICKNESS - 1, COL_EDGE);
3179 if (flags & DRAW_CORNER_UL)
3180 draw_rect(dr, lx, ly, EDGE_THICKNESS, EDGE_THICKNESS, COL_EDGE);
3181 if (flags & DRAW_CORNER_UR)
3182 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly,
3183 EDGE_THICKNESS - 1, EDGE_THICKNESS, COL_EDGE);
3184 if (flags & DRAW_CORNER_DL)
3185 draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1,
3186 EDGE_THICKNESS, EDGE_THICKNESS - 1, COL_EDGE);
3187 if (flags & DRAW_CORNER_DR)
3188 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1,
3189 ly + TILE_SIZE - EDGE_THICKNESS + 1,
3190 EDGE_THICKNESS - 1, EDGE_THICKNESS - 1, COL_EDGE);
3191
3192 /*
3193 * Draw the dots.
3194 */
3195 for (dy = 0; dy < 3; dy++)
3196 for (dx = 0; dx < 3; dx++) {
3197 int dotval = (flags >> (DOT_SHIFT_C + DOT_SHIFT_M*(dy*3+dx)));
3198 dotval &= (1 << DOT_SHIFT_M)-1;
3199
3200 if (dotval)
3201 draw_circle(dr, lx+dx*TILE_SIZE/2, ly+dy*TILE_SIZE/2,
3202 DOT_SIZE,
3203 (dotval == 1 ? COL_WHITEDOT : COL_BLACKDOT),
3204 COL_BLACKDOT);
3205 }
3206
3207 unclip(dr);
3208 draw_update(dr, lx, ly, TILE_SIZE, TILE_SIZE);
3209}
3210
3211static void calculate_opposite_point(const game_ui *ui,
3212 const game_drawstate *ds, const int x,
3213 const int y, int *oppositex,
3214 int *oppositey)
3215{
3216 /* oppositex - dotx = dotx - x <=> oppositex = 2 * dotx - x */
3217 *oppositex = 2 * SCOORD(ui->dotx) - x;
3218 *oppositey = 2 * SCOORD(ui->doty) - y;
3219}
3220
3221static void game_redraw(drawing *dr, game_drawstate *ds,
3222 const game_state *oldstate, const game_state *state,
3223 int dir, const game_ui *ui,
3224 float animtime, float flashtime)
3225{
3226 int w = ds->w, h = ds->h;
3227 int x, y, flashing = FALSE;
3228 int oppx, oppy;
3229
3230 if (flashtime > 0) {
3231 int frame = (int)(flashtime / FLASH_TIME);
3232 flashing = (frame % 2 == 0);
3233 }
3234
3235 if (ds->dragging) {
3236 assert(ds->bl);
3237 assert(ds->blmirror);
3238 calculate_opposite_point(ui, ds, ds->dragx + TILE_SIZE/2,
3239 ds->dragy + TILE_SIZE/2, &oppx, &oppy);
3240 oppx -= TILE_SIZE/2;
3241 oppy -= TILE_SIZE/2;
3242 blitter_load(dr, ds->bl, ds->dragx, ds->dragy);
3243 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3244 blitter_load(dr, ds->blmirror, oppx, oppy);
3245 draw_update(dr, oppx, oppy, TILE_SIZE, TILE_SIZE);
3246 ds->dragging = FALSE;
3247 }
3248 if (ds->cur_visible) {
3249 assert(ds->cur_bl);
3250 blitter_load(dr, ds->cur_bl, ds->cx, ds->cy);
3251 draw_update(dr, ds->cx, ds->cy, CURSOR_SIZE*2+1, CURSOR_SIZE*2+1);
3252 ds->cur_visible = FALSE;
3253 }
3254
3255 if (!ds->started) {
3256 draw_rect(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT, COL_BACKGROUND);
3257 draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1,
3258 w*TILE_SIZE + EDGE_THICKNESS*2 - 1,
3259 h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE);
3260 draw_update(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT);
3261 ds->started = TRUE;
3262 }
3263
3264 check_complete(state, NULL, ds->colour_scratch);
3265
3266 for (y = 0; y < h; y++)
3267 for (x = 0; x < w; x++) {
3268 unsigned long flags = 0;
3269 int ddx = 0, ddy = 0;
3270 space *sp, *opp;
3271 int dx, dy;
3272
3273 /*
3274 * Set up the flags for this square. Firstly, see if we
3275 * have edges.
3276 */
3277 if (SPACE(state, x*2, y*2+1).flags & F_EDGE_SET)
3278 flags |= DRAW_EDGE_L;
3279 if (SPACE(state, x*2+2, y*2+1).flags & F_EDGE_SET)
3280 flags |= DRAW_EDGE_R;
3281 if (SPACE(state, x*2+1, y*2).flags & F_EDGE_SET)
3282 flags |= DRAW_EDGE_U;
3283 if (SPACE(state, x*2+1, y*2+2).flags & F_EDGE_SET)
3284 flags |= DRAW_EDGE_D;
3285
3286 /*
3287 * Also, mark corners of neighbouring edges.
3288 */
3289 if ((x > 0 && SPACE(state, x*2-1, y*2).flags & F_EDGE_SET) ||
3290 (y > 0 && SPACE(state, x*2, y*2-1).flags & F_EDGE_SET))
3291 flags |= DRAW_CORNER_UL;
3292 if ((x+1 < w && SPACE(state, x*2+3, y*2).flags & F_EDGE_SET) ||
3293 (y > 0 && SPACE(state, x*2+2, y*2-1).flags & F_EDGE_SET))
3294 flags |= DRAW_CORNER_UR;
3295 if ((x > 0 && SPACE(state, x*2-1, y*2+2).flags & F_EDGE_SET) ||
3296 (y+1 < h && SPACE(state, x*2, y*2+3).flags & F_EDGE_SET))
3297 flags |= DRAW_CORNER_DL;
3298 if ((x+1 < w && SPACE(state, x*2+3, y*2+2).flags & F_EDGE_SET) ||
3299 (y+1 < h && SPACE(state, x*2+2, y*2+3).flags & F_EDGE_SET))
3300 flags |= DRAW_CORNER_DR;
3301
3302 /*
3303 * If this square is part of a valid region, paint it
3304 * that region's colour. Exception: if we're flashing,
3305 * everything goes briefly back to background colour.
3306 */
3307 sp = &SPACE(state, x*2+1, y*2+1);
3308 if (sp->flags & F_TILE_ASSOC) {
3309 opp = tile_opposite(state, sp);
3310 } else {
3311 opp = NULL;
3312 }
3313 if (ds->colour_scratch[y*w+x] && !flashing) {
3314 flags |= (ds->colour_scratch[y*w+x] == 2 ?
3315 DRAW_BLACK : DRAW_WHITE);
3316 }
3317
3318 /*
3319 * If this square is associated with a dot but it isn't
3320 * part of a valid region, draw an arrow in it pointing
3321 * in the direction of that dot.
3322 *
3323 * Exception: if this is the source point of an active
3324 * drag, we don't draw the arrow.
3325 */
3326 if ((sp->flags & F_TILE_ASSOC) && !ds->colour_scratch[y*w+x]) {
3327 if (ui->dragging && ui->srcx == x*2+1 && ui->srcy == y*2+1) {
3328 /* tile is the source, don't do it */
3329 } else if (ui->dragging && opp && ui->srcx == opp->x && ui->srcy == opp->y) {
3330 /* opposite tile is the source, don't do it */
3331 } else if (sp->doty != y*2+1 || sp->dotx != x*2+1) {
3332 flags |= DRAW_ARROW;
3333 ddy = sp->doty - (y*2+1);
3334 ddx = sp->dotx - (x*2+1);
3335 }
3336 }
3337
3338 /*
3339 * Now go through the nine possible places we could
3340 * have dots.
3341 */
3342 for (dy = 0; dy < 3; dy++)
3343 for (dx = 0; dx < 3; dx++) {
3344 sp = &SPACE(state, x*2+dx, y*2+dy);
3345 if (sp->flags & F_DOT) {
3346 unsigned long dotval = (sp->flags & F_DOT_BLACK ?
3347 DOT_BLACK : DOT_WHITE);
3348 flags |= dotval << (DOT_SHIFT_C +
3349 DOT_SHIFT_M*(dy*3+dx));
3350 }
3351 }
3352
3353 /*
3354 * Now work out if we have to draw a cursor for this square;
3355 * cursors-on-lines are taken care of below.
3356 */
3357 if (ui->cur_visible &&
3358 ui->cur_x == x*2+1 && ui->cur_y == y*2+1 &&
3359 !(SPACE(state, x*2+1, y*2+1).flags & F_DOT))
3360 flags |= DRAW_CURSOR;
3361
3362 /*
3363 * Now we have everything we're going to need. Draw the
3364 * square.
3365 */
3366 if (ds->grid[y*w+x] != flags ||
3367 ds->dx[y*w+x] != ddx ||
3368 ds->dy[y*w+x] != ddy) {
3369 draw_square(dr, ds, x, y, flags, ddx, ddy);
3370 ds->grid[y*w+x] = flags;
3371 ds->dx[y*w+x] = ddx;
3372 ds->dy[y*w+x] = ddy;
3373 }
3374 }
3375
3376 /*
3377 * Draw a cursor. This secondary blitter is much less invasive than trying
3378 * to fix up all of the rest of the code with sufficient flags to be able to
3379 * display this sensibly.
3380 */
3381 if (ui->cur_visible) {
3382 space *sp = &SPACE(state, ui->cur_x, ui->cur_y);
3383 ds->cur_visible = TRUE;
3384 ds->cx = SCOORD(ui->cur_x) - CURSOR_SIZE;
3385 ds->cy = SCOORD(ui->cur_y) - CURSOR_SIZE;
3386 blitter_save(dr, ds->cur_bl, ds->cx, ds->cy);
3387 if (sp->flags & F_DOT) {
3388 /* draw a red dot (over the top of whatever would be there already) */
3389 draw_circle(dr, SCOORD(ui->cur_x), SCOORD(ui->cur_y), DOT_SIZE,
3390 COL_CURSOR, COL_BLACKDOT);
3391 } else if (sp->type != s_tile) {
3392 /* draw an edge/vertex square; tile cursors are dealt with above. */
3393 int dx = (ui->cur_x % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3394 int dy = (ui->cur_y % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3395 int x1 = SCOORD(ui->cur_x)-dx, y1 = SCOORD(ui->cur_y)-dy;
3396 int xs = dx*2+1, ys = dy*2+1;
3397
3398 draw_rect(dr, x1, y1, xs, ys, COL_CURSOR);
3399 }
3400 draw_update(dr, ds->cx, ds->cy, CURSOR_SIZE*2+1, CURSOR_SIZE*2+1);
3401 }
3402
3403 if (ui->dragging) {
3404 ds->dragging = TRUE;
3405 ds->dragx = ui->dx - TILE_SIZE/2;
3406 ds->dragy = ui->dy - TILE_SIZE/2;
3407 calculate_opposite_point(ui, ds, ui->dx, ui->dy, &oppx, &oppy);
3408 blitter_save(dr, ds->bl, ds->dragx, ds->dragy);
3409 blitter_save(dr, ds->blmirror, oppx - TILE_SIZE/2, oppy - TILE_SIZE/2);
3410 draw_arrow(dr, ds, ui->dx, ui->dy, SCOORD(ui->dotx) - ui->dx,
3411 SCOORD(ui->doty) - ui->dy, COL_ARROW);
3412 draw_arrow(dr, ds, oppx, oppy, SCOORD(ui->dotx) - oppx,
3413 SCOORD(ui->doty) - oppy, COL_ARROW);
3414 }
3415#ifdef EDITOR
3416 {
3417 char buf[256];
3418 if (state->cdiff != -1)
3419 sprintf(buf, "Puzzle is %s.", galaxies_diffnames[state->cdiff]);
3420 else
3421 buf[0] = '\0';
3422 status_bar(dr, buf);
3423 }
3424#endif
3425}
3426
3427static float game_anim_length(const game_state *oldstate,
3428 const game_state *newstate, int dir, game_ui *ui)
3429{
3430 return 0.0F;
3431}
3432
3433static float game_flash_length(const game_state *oldstate,
3434 const game_state *newstate, int dir, game_ui *ui)
3435{
3436 if ((!oldstate->completed && newstate->completed) &&
3437 !(newstate->used_solve))
3438 return 3 * FLASH_TIME;
3439 else
3440 return 0.0F;
3441}
3442
3443static int game_status(const game_state *state)
3444{
3445 return state->completed ? +1 : 0;
3446}
3447
3448static int game_timing_state(const game_state *state, game_ui *ui)
3449{
3450 return TRUE;
3451}
3452
3453#ifndef EDITOR
3454static void game_print_size(const game_params *params, float *x, float *y)
3455{
3456 int pw, ph;
3457
3458 /*
3459 * 8mm squares by default. (There isn't all that much detail
3460 * that needs to go in each square.)
3461 */
3462 game_compute_size(params, 800, &pw, &ph);
3463 *x = pw / 100.0F;
3464 *y = ph / 100.0F;
3465}
3466
3467static void game_print(drawing *dr, const game_state *state, int sz)
3468{
3469 int w = state->w, h = state->h;
3470 int white, black, blackish;
3471 int x, y, i, j;
3472 int *colours, *dsf;
3473 int *coords = NULL;
3474 int ncoords = 0, coordsize = 0;
3475
3476 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3477 game_drawstate ads, *ds = &ads;
3478 ds->tilesize = sz;
3479
3480 white = print_mono_colour(dr, 1);
3481 black = print_mono_colour(dr, 0);
3482 blackish = print_hatched_colour(dr, HATCH_X);
3483
3484 /*
3485 * Get the completion information.
3486 */
3487 dsf = snewn(w * h, int);
3488 colours = snewn(w * h, int);
3489 check_complete(state, dsf, colours);
3490
3491 /*
3492 * Draw the grid.
3493 */
3494 print_line_width(dr, TILE_SIZE / 64);
3495 for (x = 1; x < w; x++)
3496 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
3497 for (y = 1; y < h; y++)
3498 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
3499
3500 /*
3501 * Shade the completed regions. Just in case any particular
3502 * printing platform deals badly with adjacent
3503 * similarly-hatched regions, we'll fill each one as a single
3504 * polygon.
3505 */
3506 for (i = 0; i < w*h; i++) {
3507 j = dsf_canonify(dsf, i);
3508 if (colours[j] != 0) {
3509 int dx, dy, t;
3510
3511 /*
3512 * This is the first square we've run into belonging to
3513 * this polyomino, which means an edge of the polyomino
3514 * is certain to be to our left. (After we finish
3515 * tracing round it, we'll set the colours[] entry to
3516 * zero to prevent accidentally doing it again.)
3517 */
3518
3519 x = i % w;
3520 y = i / w;
3521 dx = -1;
3522 dy = 0;
3523 ncoords = 0;
3524 while (1) {
3525 /*
3526 * We are currently sitting on square (x,y), which
3527 * we know to be in our polyomino, and we also know
3528 * that (x+dx,y+dy) is not. The way I visualise
3529 * this is that we're standing to the right of a
3530 * boundary line, stretching our left arm out to
3531 * point to the exterior square on the far side.
3532 */
3533
3534 /*
3535 * First, check if we've gone round the entire
3536 * polyomino.
3537 */
3538 if (ncoords > 0 &&
3539 (x == i%w && y == i/w && dx == -1 && dy == 0))
3540 break;
3541
3542 /*
3543 * Add to our coordinate list the coordinate
3544 * backwards and to the left of where we are.
3545 */
3546 if (ncoords + 2 > coordsize) {
3547 coordsize = (ncoords * 3 / 2) + 64;
3548 coords = sresize(coords, coordsize, int);
3549 }
3550 coords[ncoords++] = COORD((2*x+1 + dx + dy) / 2);
3551 coords[ncoords++] = COORD((2*y+1 + dy - dx) / 2);
3552
3553 /*
3554 * Follow the edge round. If the square directly in
3555 * front of us is not part of the polyomino, we
3556 * turn right; if it is and so is the square in
3557 * front of (x+dx,y+dy), we turn left; otherwise we
3558 * go straight on.
3559 */
3560 if (x-dy < 0 || x-dy >= w || y+dx < 0 || y+dx >= h ||
3561 dsf_canonify(dsf, (y+dx)*w+(x-dy)) != j) {
3562 /* Turn right. */
3563 t = dx;
3564 dx = -dy;
3565 dy = t;
3566 } else if (x+dx-dy >= 0 && x+dx-dy < w &&
3567 y+dy+dx >= 0 && y+dy+dx < h &&
3568 dsf_canonify(dsf, (y+dy+dx)*w+(x+dx-dy)) == j) {
3569 /* Turn left. */
3570 x += dx;
3571 y += dy;
3572 t = dx;
3573 dx = dy;
3574 dy = -t;
3575 x -= dx;
3576 y -= dy;
3577 } else {
3578 /* Straight on. */
3579 x -= dy;
3580 y += dx;
3581 }
3582 }
3583
3584 /*
3585 * Now we have our polygon complete, so fill it.
3586 */
3587 draw_polygon(dr, coords, ncoords/2,
3588 colours[j] == 2 ? blackish : -1, black);
3589
3590 /*
3591 * And mark this polyomino as done.
3592 */
3593 colours[j] = 0;
3594 }
3595 }
3596
3597 /*
3598 * Draw the edges.
3599 */
3600 for (y = 0; y <= h; y++)
3601 for (x = 0; x <= w; x++) {
3602 if (x < w && SPACE(state, x*2+1, y*2).flags & F_EDGE_SET)
3603 draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS,
3604 EDGE_THICKNESS * 2 + TILE_SIZE, EDGE_THICKNESS * 2,
3605 black);
3606 if (y < h && SPACE(state, x*2, y*2+1).flags & F_EDGE_SET)
3607 draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS,
3608 EDGE_THICKNESS * 2, EDGE_THICKNESS * 2 + TILE_SIZE,
3609 black);
3610 }
3611
3612 /*
3613 * Draw the dots.
3614 */
3615 for (y = 0; y <= 2*h; y++)
3616 for (x = 0; x <= 2*w; x++)
3617 if (SPACE(state, x, y).flags & F_DOT) {
3618 draw_circle(dr, (int)COORD(x/2.0), (int)COORD(y/2.0), DOT_SIZE,
3619 (SPACE(state, x, y).flags & F_DOT_BLACK ?
3620 black : white), black);
3621 }
3622
3623 sfree(dsf);
3624 sfree(colours);
3625 sfree(coords);
3626}
3627#endif
3628
3629#ifdef COMBINED
3630#define thegame galaxies
3631#endif
3632
3633const struct game thegame = {
3634 "Galaxies", "games.galaxies", "galaxies",
3635 default_params,
3636 game_fetch_preset, NULL,
3637 decode_params,
3638 encode_params,
3639 free_params,
3640 dup_params,
3641 TRUE, game_configure, custom_params,
3642 validate_params,
3643 new_game_desc,
3644 validate_desc,
3645 new_game,
3646 dup_game,
3647 free_game,
3648#ifdef EDITOR
3649 FALSE, NULL,
3650#else
3651 TRUE, solve_game,
3652#endif
3653 TRUE, game_can_format_as_text_now, game_text_format,
3654 new_ui,
3655 free_ui,
3656 encode_ui,
3657 decode_ui,
3658 game_changed_state,
3659 interpret_move,
3660 execute_move,
3661 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3662 game_colours,
3663 game_new_drawstate,
3664 game_free_drawstate,
3665 game_redraw,
3666 game_anim_length,
3667 game_flash_length,
3668 game_status,
3669#ifdef EDITOR
3670 FALSE, FALSE, NULL, NULL,
3671 TRUE, /* wants_statusbar */
3672#else
3673 TRUE, FALSE, game_print_size, game_print,
3674 FALSE, /* wants_statusbar */
3675#endif
3676 FALSE, game_timing_state,
3677 REQUIRE_RBUTTON, /* flags */
3678};
3679
3680#ifdef STANDALONE_SOLVER
3681
3682const char *quis;
3683
3684#include <time.h>
3685
3686static void usage_exit(const char *msg)
3687{
3688 if (msg)
3689 fprintf(stderr, "%s: %s\n", quis, msg);
3690 fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
3691 exit(1);
3692}
3693
3694static void dump_state(game_state *state)
3695{
3696 char *temp = game_text_format(state);
3697 printf("%s\n", temp);
3698 sfree(temp);
3699}
3700
3701static int gen(game_params *p, random_state *rs, int debug)
3702{
3703 char *desc;
3704 int diff;
3705 game_state *state;
3706
3707#ifndef DEBUGGING
3708 solver_show_working = debug;
3709#endif
3710 printf("Generating a %dx%d %s puzzle.\n",
3711 p->w, p->h, galaxies_diffnames[p->diff]);
3712
3713 desc = new_game_desc(p, rs, NULL, 0);
3714 state = new_game(NULL, p, desc);
3715 dump_state(state);
3716
3717 diff = solver_state(state, DIFF_UNREASONABLE);
3718 printf("Generated %s game %dx%d:%s\n",
3719 galaxies_diffnames[diff], p->w, p->h, desc);
3720 dump_state(state);
3721
3722 free_game(state);
3723 sfree(desc);
3724
3725 return diff;
3726}
3727
3728static void soak(game_params *p, random_state *rs)
3729{
3730 time_t tt_start, tt_now, tt_last;
3731 char *desc;
3732 game_state *st;
3733 int diff, n = 0, i, diffs[DIFF_MAX], ndots = 0, nspaces = 0;
3734
3735#ifndef DEBUGGING
3736 solver_show_working = 0;
3737#endif
3738 tt_start = tt_now = time(NULL);
3739 for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0;
3740 maxtries = 1;
3741
3742 printf("Soak-generating a %dx%d grid, max. diff %s.\n",
3743 p->w, p->h, galaxies_diffnames[p->diff]);
3744 printf(" [");
3745 for (i = 0; i < DIFF_MAX; i++)
3746 printf("%s%s", (i == 0) ? "" : ", ", galaxies_diffnames[i]);
3747 printf("]\n");
3748
3749 while (1) {
3750 desc = new_game_desc(p, rs, NULL, 0);
3751 st = new_game(NULL, p, desc);
3752 diff = solver_state(st, p->diff);
3753 nspaces += st->w*st->h;
3754 for (i = 0; i < st->sx*st->sy; i++)
3755 if (st->grid[i].flags & F_DOT) ndots++;
3756 free_game(st);
3757 sfree(desc);
3758
3759 diffs[diff]++;
3760 n++;
3761 tt_last = time(NULL);
3762 if (tt_last > tt_now) {
3763 tt_now = tt_last;
3764 printf("%d total, %3.1f/s, [",
3765 n, (double)n / ((double)tt_now - tt_start));
3766 for (i = 0; i < DIFF_MAX; i++)
3767 printf("%s%.1f%%", (i == 0) ? "" : ", ",
3768 100.0 * ((double)diffs[i] / (double)n));
3769 printf("], %.1f%% dots\n",
3770 100.0 * ((double)ndots / (double)nspaces));
3771 }
3772 }
3773}
3774
3775int main(int argc, char **argv)
3776{
3777 game_params *p;
3778 char *id = NULL, *desc, *err;
3779 game_state *s;
3780 int diff, do_soak = 0, verbose = 0;
3781 random_state *rs;
3782 time_t seed = time(NULL);
3783
3784 quis = argv[0];
3785 while (--argc > 0) {
3786 char *p = *++argv;
3787 if (!strcmp(p, "-v")) {
3788 verbose = 1;
3789 } else if (!strcmp(p, "--seed")) {
3790 if (argc == 0) usage_exit("--seed needs an argument");
3791 seed = (time_t)atoi(*++argv);
3792 argc--;
3793 } else if (!strcmp(p, "--soak")) {
3794 do_soak = 1;
3795 } else if (*p == '-') {
3796 usage_exit("unrecognised option");
3797 } else {
3798 id = p;
3799 }
3800 }
3801
3802 maxtries = 50;
3803
3804 p = default_params();
3805 rs = random_new((void*)&seed, sizeof(time_t));
3806
3807 if (do_soak) {
3808 if (!id) usage_exit("need one argument for --soak");
3809 decode_params(p, *argv);
3810 soak(p, rs);
3811 return 0;
3812 }
3813
3814 if (!id) {
3815 while (1) {
3816 p->w = random_upto(rs, 15) + 3;
3817 p->h = random_upto(rs, 15) + 3;
3818 p->diff = random_upto(rs, DIFF_UNREASONABLE);
3819 diff = gen(p, rs, 0);
3820 }
3821 return 0;
3822 }
3823
3824 desc = strchr(id, ':');
3825 if (!desc) {
3826 decode_params(p, id);
3827 gen(p, rs, verbose);
3828 } else {
3829#ifndef DEBUGGING
3830 solver_show_working = 1;
3831#endif
3832 *desc++ = '\0';
3833 decode_params(p, id);
3834 err = validate_desc(p, desc);
3835 if (err) {
3836 fprintf(stderr, "%s: %s\n", argv[0], err);
3837 exit(1);
3838 }
3839 s = new_game(NULL, p, desc);
3840 diff = solver_state(s, DIFF_UNREASONABLE);
3841 dump_state(s);
3842 printf("Puzzle is %s.\n", galaxies_diffnames[diff]);
3843 free_game(s);
3844 }
3845
3846 free_params(p);
3847
3848 return 0;
3849}
3850
3851#endif
3852
3853#ifdef STANDALONE_PICTURE_GENERATOR
3854
3855/*
3856 * Main program for the standalone picture generator. To use it,
3857 * simply provide it with an XBM-format bitmap file (note XBM, not
3858 * XPM) on standard input, and it will output a game ID in return.
3859 * For example:
3860 *
3861 * $ ./galaxiespicture < badly-drawn-cat.xbm
3862 * 11x11:eloMBLzFeEzLNMWifhaWYdDbixCymBbBMLoDdewGg
3863 *
3864 * If you want a puzzle with a non-standard difficulty level, pass
3865 * a partial parameters string as a command-line argument (e.g.
3866 * `./galaxiespicture du < foo.xbm', where `du' is the same suffix
3867 * which if it appeared in a random-seed game ID would set the
3868 * difficulty level to Unreasonable). However, be aware that if the
3869 * generator fails to produce an adequately difficult puzzle too
3870 * many times then it will give up and return an easier one (just
3871 * as it does during normal GUI play). To be sure you really have
3872 * the difficulty you asked for, use galaxiessolver to
3873 * double-check.
3874 *
3875 * (Perhaps I ought to include an option to make this standalone
3876 * generator carry on looping until it really does get the right
3877 * difficulty. Hmmm.)
3878 */
3879
3880#include <time.h>
3881
3882int main(int argc, char **argv)
3883{
3884 game_params *par;
3885 char *params, *desc;
3886 random_state *rs;
3887 time_t seed = time(NULL);
3888 char buf[4096];
3889 int i;
3890 int x, y;
3891
3892 par = default_params();
3893 if (argc > 1)
3894 decode_params(par, argv[1]); /* get difficulty */
3895 par->w = par->h = -1;
3896
3897 /*
3898 * Now read an XBM file from standard input. This is simple and
3899 * hacky and will do very little error detection, so don't feed
3900 * it bogus data.
3901 */
3902 picture = NULL;
3903 x = y = 0;
3904 while (fgets(buf, sizeof(buf), stdin)) {
3905 buf[strcspn(buf, "\r\n")] = '\0';
3906 if (!strncmp(buf, "#define", 7)) {
3907 /*
3908 * Lines starting `#define' give the width and height.
3909 */
3910 char *num = buf + strlen(buf);
3911 char *symend;
3912
3913 while (num > buf && isdigit((unsigned char)num[-1]))
3914 num--;
3915 symend = num;
3916 while (symend > buf && isspace((unsigned char)symend[-1]))
3917 symend--;
3918
3919 if (symend-5 >= buf && !strncmp(symend-5, "width", 5))
3920 par->w = atoi(num);
3921 else if (symend-6 >= buf && !strncmp(symend-6, "height", 6))
3922 par->h = atoi(num);
3923 } else {
3924 /*
3925 * Otherwise, break the string up into words and take
3926 * any word of the form `0x' plus hex digits to be a
3927 * byte.
3928 */
3929 char *p, *wordstart;
3930
3931 if (!picture) {
3932 if (par->w < 0 || par->h < 0) {
3933 printf("failed to read width and height\n");
3934 return 1;
3935 }
3936 picture = snewn(par->w * par->h, int);
3937 for (i = 0; i < par->w * par->h; i++)
3938 picture[i] = -1;
3939 }
3940
3941 p = buf;
3942 while (*p) {
3943 while (*p && (*p == ',' || isspace((unsigned char)*p)))
3944 p++;
3945 wordstart = p;
3946 while (*p && !(*p == ',' || *p == '}' ||
3947 isspace((unsigned char)*p)))
3948 p++;
3949 if (*p)
3950 *p++ = '\0';
3951
3952 if (wordstart[0] == '0' &&
3953 (wordstart[1] == 'x' || wordstart[1] == 'X') &&
3954 !wordstart[2 + strspn(wordstart+2,
3955 "0123456789abcdefABCDEF")]) {
3956 unsigned long byte = strtoul(wordstart+2, NULL, 16);
3957 for (i = 0; i < 8; i++) {
3958 int bit = (byte >> i) & 1;
3959 if (y < par->h && x < par->w)
3960 picture[y * par->w + x] = bit;
3961 x++;
3962 }
3963
3964 if (x >= par->w) {
3965 x = 0;
3966 y++;
3967 }
3968 }
3969 }
3970 }
3971 }
3972
3973 for (i = 0; i < par->w * par->h; i++)
3974 if (picture[i] < 0) {
3975 fprintf(stderr, "failed to read enough bitmap data\n");
3976 return 1;
3977 }
3978
3979 rs = random_new((void*)&seed, sizeof(time_t));
3980
3981 desc = new_game_desc(par, rs, NULL, FALSE);
3982 params = encode_params(par, FALSE);
3983 printf("%s:%s\n", params, desc);
3984
3985 sfree(desc);
3986 sfree(params);
3987 free_params(par);
3988 random_free(rs);
3989
3990 return 0;
3991}
3992
3993#endif
3994
3995/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/galaxies.html b/apps/plugins/puzzles/src/galaxies.html
new file mode 100644
index 0000000000..41c32c1b1e
--- /dev/null
+++ b/apps/plugins/puzzles/src/galaxies.html
@@ -0,0 +1,60 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Galaxies</title>
7<link rel="previous" href="unequal.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="filling.html">
12</head>
13<body>
14<p><a href="unequal.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="filling.html">Next</a></p>
15<h1><a name="C28"></a>Chapter 28: <a name="i0"></a>Galaxies</h1>
16<p>
17You have a rectangular grid containing a number of dots. Your aim is to draw edges along the grid lines which divide the rectangle into regions in such a way that every region is 180&#176; rotationally symmetric, and contains exactly one dot which is located at its centre of symmetry.
18</p>
19<p>
20This puzzle was invented by <a name="i1"></a>Nikoli <a href="#p0">[13]</a>, under the name &#8216;Tentai Show&#8217;; its name is commonly translated into English as &#8216;Spiral Galaxies&#8217;.
21</p>
22<p>
23Galaxies was contributed to this collection by James Harvey.
24</p>
25<p><a name="p0"></a>
26[13] <a href="http://www.nikoli.co.jp/en/puzzles/astronomical_show.html"><code>http://www.nikoli.co.jp/en/puzzles/astronomical_show.html</code></a>
27</p>
28<h2><a name="S28.1"></a>28.1 <a name="i2"></a>Galaxies controls</h2>
29<p>
30Left-click on any grid line to draw an edge if there isn't one already, or to remove one if there is. When you create a valid region (one which is closed, contains exactly one dot, is 180&#176; symmetric about that dot, and contains no extraneous edges inside it) it will be highlighted automatically; so your aim is to have the whole grid highlighted in that way.
31</p>
32<p>
33During solving, you might know that a particular grid square belongs to a specific dot, but not be sure of where the edges go and which other squares are connected to the dot. In order to mark this so you don't forget, you can right-click on the dot and drag, which will create an arrow marker pointing at the dot. Drop that in a square of your choice and it will remind you which dot it's associated with. You can also right-click on existing arrows to pick them up and move them, or destroy them by dropping them off the edge of the grid. (Also, if you're not sure which dot an arrow is pointing at, you can pick it up and move it around to make it clearer. It will swivel constantly as you drag it, to stay pointed at its parent dot.)
34</p>
35<p>
36You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a grid line will draw or clear its edge, as above. Pressing the return key when over a dot will pick up an arrow, to be dropped the next time the return key is pressed; this can also be used to move existing arrows around, removing them by dropping them on a dot or another arrow.
37</p>
38<p>
39(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
40</p>
41<h2><a name="S28.2"></a>28.2 <a name="i3"></a>Galaxies parameters</h2>
42<p>
43These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
44</p>
45<dl><dt>
46<em>Width</em>, <em>Height</em>
47</dt>
48<dd>
49Size of grid in squares.
50</dd>
51<dt>
52<em>Difficulty</em>
53</dt>
54<dd>
55Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, and the &#8216;Unreasonable&#8217; difficulty level may require backtracking.
56</dd>
57</dl>
58
59<hr><address></address></body>
60</html>
diff --git a/apps/plugins/puzzles/src/gamedesc.txt b/apps/plugins/puzzles/src/gamedesc.txt
new file mode 100644
index 0000000000..db0d9cbd01
--- /dev/null
+++ b/apps/plugins/puzzles/src/gamedesc.txt
@@ -0,0 +1,39 @@
1blackbox:blackbox.exe:Black Box:Ball-finding puzzle:Find the hidden balls in the box by bouncing laser beams off them.
2bridges:bridges.exe:Bridges:Bridge-placing puzzle:Connect all the islands with a network of bridges.
3cube:cube.exe:Cube:Rolling cube puzzle:Pick up all the blue squares by rolling the cube over them.
4dominosa:dominosa.exe:Dominosa:Domino tiling puzzle:Tile the rectangle with a full set of dominoes.
5fifteen:fifteen.exe:Fifteen:Sliding block puzzle:Slide the tiles around to arrange them into order.
6filling:filling.exe:Filling:Polyomino puzzle:Mark every square with the area of its containing region.
7flip:flip.exe:Flip:Tile inversion puzzle:Flip groups of squares to light them all up at once.
8flood:flood.exe:Flood:Flood-filling puzzle:Turn the grid the same colour in as few flood fills as possible.
9galaxies:galaxies.exe:Galaxies:Symmetric polyomino puzzle:Divide the grid into rotationally symmetric regions each centred on a dot.
10guess:guess.exe:Guess:Combination-guessing puzzle:Guess the hidden combination of colours.
11inertia:inertia.exe:Inertia:Gem-collecting puzzle:Collect all the gems without running into any of the mines.
12keen:keen.exe:Keen:Arithmetic Latin square puzzle:Complete the latin square in accordance with the arithmetic clues.
13lightup:lightup.exe:Light Up:Light-bulb placing puzzle:Place bulbs to light up all the squares.
14loopy:loopy.exe:Loopy:Loop-drawing puzzle:Draw a single closed loop, given clues about number of adjacent edges.
15magnets:magnets.exe:Magnets:Magnet-placing puzzle:Place magnets to satisfy the clues and avoid like poles touching.
16map:map.exe:Map:Map-colouring puzzle:Colour the map so that adjacent regions are never the same colour.
17mines:mines.exe:Mines:Mine-finding puzzle:Find all the mines without treading on any of them.
18net:netgame.exe:Net:Network jigsaw puzzle:Rotate each tile to reassemble the network.
19netslide:netslide.exe:Netslide:Toroidal sliding network puzzle:Slide a row at a time to reassemble the network.
20palisade:palisade.exe:Palisade:Grid-division puzzle:Divide the grid into equal-sized areas in accordance with the clues.
21pattern:pattern.exe:Pattern:Pattern puzzle:Fill in the pattern in the grid, given only the lengths of runs of black squares.
22pearl:pearl.exe:Pearl:Loop-drawing puzzle:Draw a single closed loop, given clues about corner and straight squares.
23pegs:pegs.exe:Pegs:Peg solitaire puzzle:Jump pegs over each other to remove all but one.
24range:range.exe:Range:Visible-distance puzzle:Place black squares to limit the visible distance from each numbered cell.
25rect:rect.exe:Rectangles:Rectangles puzzle:Divide the grid into rectangles with areas equal to the numbers.
26samegame:samegame.exe:Same Game:Block-clearing puzzle:Clear the grid by removing touching groups of the same colour squares.
27signpost:signpost.exe:Signpost:Square-connecting puzzle:Connect the squares into a path following the arrows.
28singles:singles.exe:Singles:Number-removing puzzle:Black out the right set of duplicate numbers.
29sixteen:sixteen.exe:Sixteen:Toroidal sliding block puzzle:Slide a row at a time to arrange the tiles into order.
30slant:slant.exe:Slant:Maze-drawing puzzle:Draw a maze of slanting lines that matches the clues.
31solo:solo.exe:Solo:Number placement puzzle:Fill in the grid so that each row, column and square block contains one of every digit.
32tents:tents.exe:Tents:Tent-placing puzzle:Place a tent next to each tree.
33towers:towers.exe:Towers:Tower-placing Latin square puzzle:Complete the latin square of towers in accordance with the clues.
34tracks:tracks.exe:Tracks:Path-finding railway track puzzle:Fill in the railway track according to the clues.
35twiddle:twiddle.exe:Twiddle:Rotational sliding block puzzle:Rotate the tiles around themselves to arrange them into order.
36undead:undead.exe:Undead:Monster-placing puzzle:Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors.
37unequal:unequal.exe:Unequal:Latin square puzzle:Complete the latin square in accordance with the > signs.
38unruly:unruly.exe:Unruly:Black and white grid puzzle:Fill in the black and white grid to avoid runs of three.
39untangle:untangle.exe:Untangle:Planar graph layout puzzle:Reposition the points so that the lines do not cross.
diff --git a/apps/plugins/puzzles/src/grid.c b/apps/plugins/puzzles/src/grid.c
new file mode 100644
index 0000000000..4929b5c7d3
--- /dev/null
+++ b/apps/plugins/puzzles/src/grid.c
@@ -0,0 +1,3065 @@
1/*
2 * (c) Lambros Lambrou 2008
3 *
4 * Code for working with general grids, which can be any planar graph
5 * with faces, edges and vertices (dots). Includes generators for a few
6 * types of grid, including square, hexagonal, triangular and others.
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <assert.h>
13#include <ctype.h>
14#include <math.h>
15#include <float.h>
16
17#include "puzzles.h"
18#include "tree234.h"
19#include "grid.h"
20#include "penrose.h"
21
22/* Debugging options */
23
24/*
25#define DEBUG_GRID
26*/
27
28/* ----------------------------------------------------------------------
29 * Deallocate or dereference a grid
30 */
31void grid_free(grid *g)
32{
33 assert(g->refcount);
34
35 g->refcount--;
36 if (g->refcount == 0) {
37 int i;
38 for (i = 0; i < g->num_faces; i++) {
39 sfree(g->faces[i].dots);
40 sfree(g->faces[i].edges);
41 }
42 for (i = 0; i < g->num_dots; i++) {
43 sfree(g->dots[i].faces);
44 sfree(g->dots[i].edges);
45 }
46 sfree(g->faces);
47 sfree(g->edges);
48 sfree(g->dots);
49 sfree(g);
50 }
51}
52
53/* Used by the other grid generators. Create a brand new grid with nothing
54 * initialised (all lists are NULL) */
55static grid *grid_empty(void)
56{
57 grid *g = snew(grid);
58 g->faces = NULL;
59 g->edges = NULL;
60 g->dots = NULL;
61 g->num_faces = g->num_edges = g->num_dots = 0;
62 g->refcount = 1;
63 g->lowest_x = g->lowest_y = g->highest_x = g->highest_y = 0;
64 return g;
65}
66
67/* Helper function to calculate perpendicular distance from
68 * a point P to a line AB. A and B mustn't be equal here.
69 *
70 * Well-known formula for area A of a triangle:
71 * / 1 1 1 \
72 * 2A = determinant of matrix | px ax bx |
73 * \ py ay by /
74 *
75 * Also well-known: 2A = base * height
76 * = perpendicular distance * line-length.
77 *
78 * Combining gives: distance = determinant / line-length(a,b)
79 */
80static double point_line_distance(long px, long py,
81 long ax, long ay,
82 long bx, long by)
83{
84 long det = ax*by - bx*ay + bx*py - px*by + px*ay - ax*py;
85 double len;
86 det = max(det, -det);
87 len = sqrt(SQ(ax - bx) + SQ(ay - by));
88 return det / len;
89}
90
91/* Determine nearest edge to where the user clicked.
92 * (x, y) is the clicked location, converted to grid coordinates.
93 * Returns the nearest edge, or NULL if no edge is reasonably
94 * near the position.
95 *
96 * Just judging edges by perpendicular distance is not quite right -
97 * the edge might be "off to one side". So we insist that the triangle
98 * with (x,y) has acute angles at the edge's dots.
99 *
100 * edge1
101 * *---------*------
102 * |
103 * | *(x,y)
104 * edge2 |
105 * | edge2 is OK, but edge1 is not, even though
106 * | edge1 is perpendicularly closer to (x,y)
107 * *
108 *
109 */
110grid_edge *grid_nearest_edge(grid *g, int x, int y)
111{
112 grid_edge *best_edge;
113 double best_distance = 0;
114 int i;
115
116 best_edge = NULL;
117
118 for (i = 0; i < g->num_edges; i++) {
119 grid_edge *e = &g->edges[i];
120 long e2; /* squared length of edge */
121 long a2, b2; /* squared lengths of other sides */
122 double dist;
123
124 /* See if edge e is eligible - the triangle must have acute angles
125 * at the edge's dots.
126 * Pythagoras formula h^2 = a^2 + b^2 detects right-angles,
127 * so detect acute angles by testing for h^2 < a^2 + b^2 */
128 e2 = SQ((long)e->dot1->x - (long)e->dot2->x) + SQ((long)e->dot1->y - (long)e->dot2->y);
129 a2 = SQ((long)e->dot1->x - (long)x) + SQ((long)e->dot1->y - (long)y);
130 b2 = SQ((long)e->dot2->x - (long)x) + SQ((long)e->dot2->y - (long)y);
131 if (a2 >= e2 + b2) continue;
132 if (b2 >= e2 + a2) continue;
133
134 /* e is eligible so far. Now check the edge is reasonably close
135 * to where the user clicked. Don't want to toggle an edge if the
136 * click was way off the grid.
137 * There is room for experimentation here. We could check the
138 * perpendicular distance is within a certain fraction of the length
139 * of the edge. That amounts to testing a rectangular region around
140 * the edge.
141 * Alternatively, we could check that the angle at the point is obtuse.
142 * That would amount to testing a circular region with the edge as
143 * diameter. */
144 dist = point_line_distance((long)x, (long)y,
145 (long)e->dot1->x, (long)e->dot1->y,
146 (long)e->dot2->x, (long)e->dot2->y);
147 /* Is dist more than half edge length ? */
148 if (4 * SQ(dist) > e2)
149 continue;
150
151 if (best_edge == NULL || dist < best_distance) {
152 best_edge = e;
153 best_distance = dist;
154 }
155 }
156 return best_edge;
157}
158
159/* ----------------------------------------------------------------------
160 * Grid generation
161 */
162
163#ifdef SVG_GRID
164
165#define SVG_DOTS 1
166#define SVG_EDGES 2
167#define SVG_FACES 4
168
169#define FACE_COLOUR "red"
170#define EDGE_COLOUR "blue"
171#define DOT_COLOUR "black"
172
173static void grid_output_svg(FILE *fp, grid *g, int which)
174{
175 int i, j;
176
177 fprintf(fp,"\
178<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
179<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n\
180\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\
181\n\
182<svg xmlns=\"http://www.w3.org/2000/svg\"\n\
183xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
184
185 if (which & SVG_FACES) {
186 fprintf(fp, "<g>\n");
187 for (i = 0; i < g->num_faces; i++) {
188 grid_face *f = g->faces + i;
189 fprintf(fp, "<polygon points=\"");
190 for (j = 0; j < f->order; j++) {
191 grid_dot *d = f->dots[j];
192 fprintf(fp, "%s%d,%d", (j == 0) ? "" : " ",
193 d->x, d->y);
194 }
195 fprintf(fp, "\" style=\"fill: %s; fill-opacity: 0.2; stroke: %s\" />\n",
196 FACE_COLOUR, FACE_COLOUR);
197 }
198 fprintf(fp, "</g>\n");
199 }
200 if (which & SVG_EDGES) {
201 fprintf(fp, "<g>\n");
202 for (i = 0; i < g->num_edges; i++) {
203 grid_edge *e = g->edges + i;
204 grid_dot *d1 = e->dot1, *d2 = e->dot2;
205
206 fprintf(fp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
207 "style=\"stroke: %s\" />\n",
208 d1->x, d1->y, d2->x, d2->y, EDGE_COLOUR);
209 }
210 fprintf(fp, "</g>\n");
211 }
212
213 if (which & SVG_DOTS) {
214 fprintf(fp, "<g>\n");
215 for (i = 0; i < g->num_dots; i++) {
216 grid_dot *d = g->dots + i;
217 fprintf(fp, "<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" fill=\"%s\" />",
218 d->x, d->y, g->tilesize/20, g->tilesize/20, DOT_COLOUR);
219 }
220 fprintf(fp, "</g>\n");
221 }
222
223 fprintf(fp, "</svg>\n");
224}
225#endif
226
227#ifdef SVG_GRID
228#include <errno.h>
229
230static void grid_try_svg(grid *g, int which)
231{
232 char *svg = getenv("PUZZLES_SVG_GRID");
233 if (svg) {
234 FILE *svgf = fopen(svg, "w");
235 if (svgf) {
236 grid_output_svg(svgf, g, which);
237 fclose(svgf);
238 } else {
239 fprintf(stderr, "Unable to open file `%s': %s", svg, strerror(errno));
240 }
241 }
242}
243#endif
244
245/* Show the basic grid information, before doing grid_make_consistent */
246static void grid_debug_basic(grid *g)
247{
248 /* TODO: Maybe we should generate an SVG image of the dots and lines
249 * of the grid here, before grid_make_consistent.
250 * Would help with debugging grid generation. */
251#ifdef DEBUG_GRID
252 int i;
253 printf("--- Basic Grid Data ---\n");
254 for (i = 0; i < g->num_faces; i++) {
255 grid_face *f = g->faces + i;
256 printf("Face %d: dots[", i);
257 int j;
258 for (j = 0; j < f->order; j++) {
259 grid_dot *d = f->dots[j];
260 printf("%s%d", j ? "," : "", (int)(d - g->dots));
261 }
262 printf("]\n");
263 }
264#endif
265#ifdef SVG_GRID
266 grid_try_svg(g, SVG_FACES);
267#endif
268}
269
270/* Show the derived grid information, computed by grid_make_consistent */
271static void grid_debug_derived(grid *g)
272{
273#ifdef DEBUG_GRID
274 /* edges */
275 int i;
276 printf("--- Derived Grid Data ---\n");
277 for (i = 0; i < g->num_edges; i++) {
278 grid_edge *e = g->edges + i;
279 printf("Edge %d: dots[%d,%d] faces[%d,%d]\n",
280 i, (int)(e->dot1 - g->dots), (int)(e->dot2 - g->dots),
281 e->face1 ? (int)(e->face1 - g->faces) : -1,
282 e->face2 ? (int)(e->face2 - g->faces) : -1);
283 }
284 /* faces */
285 for (i = 0; i < g->num_faces; i++) {
286 grid_face *f = g->faces + i;
287 int j;
288 printf("Face %d: faces[", i);
289 for (j = 0; j < f->order; j++) {
290 grid_edge *e = f->edges[j];
291 grid_face *f2 = (e->face1 == f) ? e->face2 : e->face1;
292 printf("%s%d", j ? "," : "", f2 ? (int)(f2 - g->faces) : -1);
293 }
294 printf("]\n");
295 }
296 /* dots */
297 for (i = 0; i < g->num_dots; i++) {
298 grid_dot *d = g->dots + i;
299 int j;
300 printf("Dot %d: dots[", i);
301 for (j = 0; j < d->order; j++) {
302 grid_edge *e = d->edges[j];
303 grid_dot *d2 = (e->dot1 == d) ? e->dot2 : e->dot1;
304 printf("%s%d", j ? "," : "", (int)(d2 - g->dots));
305 }
306 printf("] faces[");
307 for (j = 0; j < d->order; j++) {
308 grid_face *f = d->faces[j];
309 printf("%s%d", j ? "," : "", f ? (int)(f - g->faces) : -1);
310 }
311 printf("]\n");
312 }
313#endif
314#ifdef SVG_GRID
315 grid_try_svg(g, SVG_DOTS | SVG_EDGES | SVG_FACES);
316#endif
317}
318
319/* Helper function for building incomplete-edges list in
320 * grid_make_consistent() */
321static int grid_edge_bydots_cmpfn(void *v1, void *v2)
322{
323 grid_edge *a = v1;
324 grid_edge *b = v2;
325 grid_dot *da, *db;
326
327 /* Pointer subtraction is valid here, because all dots point into the
328 * same dot-list (g->dots).
329 * Edges are not "normalised" - the 2 dots could be stored in any order,
330 * so we need to take this into account when comparing edges. */
331
332 /* Compare first dots */
333 da = (a->dot1 < a->dot2) ? a->dot1 : a->dot2;
334 db = (b->dot1 < b->dot2) ? b->dot1 : b->dot2;
335 if (da != db)
336 return db - da;
337 /* Compare last dots */
338 da = (a->dot1 < a->dot2) ? a->dot2 : a->dot1;
339 db = (b->dot1 < b->dot2) ? b->dot2 : b->dot1;
340 if (da != db)
341 return db - da;
342
343 return 0;
344}
345
346/*
347 * 'Vigorously trim' a grid, by which I mean deleting any isolated or
348 * uninteresting faces. By which, in turn, I mean: ensure that the
349 * grid is composed solely of faces adjacent to at least one
350 * 'landlocked' dot (i.e. one not in contact with the infinite
351 * exterior face), and that all those dots are in a single connected
352 * component.
353 *
354 * This function operates on, and returns, a grid satisfying the
355 * preconditions to grid_make_consistent() rather than the
356 * postconditions. (So call it first.)
357 */
358static void grid_trim_vigorously(grid *g)
359{
360 int *dotpairs, *faces, *dots;
361 int *dsf;
362 int i, j, k, size, newfaces, newdots;
363
364 /*
365 * First construct a matrix in which each ordered pair of dots is
366 * mapped to the index of the face in which those dots occur in
367 * that order.
368 */
369 dotpairs = snewn(g->num_dots * g->num_dots, int);
370 for (i = 0; i < g->num_dots; i++)
371 for (j = 0; j < g->num_dots; j++)
372 dotpairs[i*g->num_dots+j] = -1;
373 for (i = 0; i < g->num_faces; i++) {
374 grid_face *f = g->faces + i;
375 int dot0 = f->dots[f->order-1] - g->dots;
376 for (j = 0; j < f->order; j++) {
377 int dot1 = f->dots[j] - g->dots;
378 dotpairs[dot0 * g->num_dots + dot1] = i;
379 dot0 = dot1;
380 }
381 }
382
383 /*
384 * Now we can identify landlocked dots: they're the ones all of
385 * whose edges have a mirror-image counterpart in this matrix.
386 */
387 dots = snewn(g->num_dots, int);
388 for (i = 0; i < g->num_dots; i++) {
389 dots[i] = TRUE;
390 for (j = 0; j < g->num_dots; j++) {
391 if ((dotpairs[i*g->num_dots+j] >= 0) ^
392 (dotpairs[j*g->num_dots+i] >= 0))
393 dots[i] = FALSE; /* non-duplicated edge: coastal dot */
394 }
395 }
396
397 /*
398 * Now identify connected pairs of landlocked dots, and form a dsf
399 * unifying them.
400 */
401 dsf = snew_dsf(g->num_dots);
402 for (i = 0; i < g->num_dots; i++)
403 for (j = 0; j < i; j++)
404 if (dots[i] && dots[j] &&
405 dotpairs[i*g->num_dots+j] >= 0 &&
406 dotpairs[j*g->num_dots+i] >= 0)
407 dsf_merge(dsf, i, j);
408
409 /*
410 * Now look for the largest component.
411 */
412 size = 0;
413 j = -1;
414 for (i = 0; i < g->num_dots; i++) {
415 int newsize;
416 if (dots[i] && dsf_canonify(dsf, i) == i &&
417 (newsize = dsf_size(dsf, i)) > size) {
418 j = i;
419 size = newsize;
420 }
421 }
422
423 /*
424 * Work out which faces we're going to keep (precisely those with
425 * at least one dot in the same connected component as j) and
426 * which dots (those required by any face we're keeping).
427 *
428 * At this point we reuse the 'dots' array to indicate the dots
429 * we're keeping, rather than the ones that are landlocked.
430 */
431 faces = snewn(g->num_faces, int);
432 for (i = 0; i < g->num_faces; i++)
433 faces[i] = 0;
434 for (i = 0; i < g->num_dots; i++)
435 dots[i] = 0;
436 for (i = 0; i < g->num_faces; i++) {
437 grid_face *f = g->faces + i;
438 int keep = FALSE;
439 for (k = 0; k < f->order; k++)
440 if (dsf_canonify(dsf, f->dots[k] - g->dots) == j)
441 keep = TRUE;
442 if (keep) {
443 faces[i] = TRUE;
444 for (k = 0; k < f->order; k++)
445 dots[f->dots[k]-g->dots] = TRUE;
446 }
447 }
448
449 /*
450 * Work out the new indices of those faces and dots, when we
451 * compact the arrays containing them.
452 */
453 for (i = newfaces = 0; i < g->num_faces; i++)
454 faces[i] = (faces[i] ? newfaces++ : -1);
455 for (i = newdots = 0; i < g->num_dots; i++)
456 dots[i] = (dots[i] ? newdots++ : -1);
457
458 /*
459 * Free the dynamically allocated 'dots' pointer lists in faces
460 * we're going to discard.
461 */
462 for (i = 0; i < g->num_faces; i++)
463 if (faces[i] < 0)
464 sfree(g->faces[i].dots);
465
466 /*
467 * Go through and compact the arrays.
468 */
469 for (i = 0; i < g->num_dots; i++)
470 if (dots[i] >= 0) {
471 grid_dot *dnew = g->dots + dots[i], *dold = g->dots + i;
472 *dnew = *dold; /* structure copy */
473 }
474 for (i = 0; i < g->num_faces; i++)
475 if (faces[i] >= 0) {
476 grid_face *fnew = g->faces + faces[i], *fold = g->faces + i;
477 *fnew = *fold; /* structure copy */
478 for (j = 0; j < fnew->order; j++) {
479 /*
480 * Reindex the dots in this face.
481 */
482 k = fnew->dots[j] - g->dots;
483 fnew->dots[j] = g->dots + dots[k];
484 }
485 }
486 g->num_faces = newfaces;
487 g->num_dots = newdots;
488
489 sfree(dotpairs);
490 sfree(dsf);
491 sfree(dots);
492 sfree(faces);
493}
494
495/* Input: grid has its dots and faces initialised:
496 * - dots have (optionally) x and y coordinates, but no edges or faces
497 * (pointers are NULL).
498 * - edges not initialised at all
499 * - faces initialised and know which dots they have (but no edges yet). The
500 * dots around each face are assumed to be clockwise.
501 *
502 * Output: grid is complete and valid with all relationships defined.
503 */
504static void grid_make_consistent(grid *g)
505{
506 int i;
507 tree234 *incomplete_edges;
508 grid_edge *next_new_edge; /* Where new edge will go into g->edges */
509
510 grid_debug_basic(g);
511
512 /* ====== Stage 1 ======
513 * Generate edges
514 */
515
516 /* We know how many dots and faces there are, so we can find the exact
517 * number of edges from Euler's polyhedral formula: F + V = E + 2 .
518 * We use "-1", not "-2" here, because Euler's formula includes the
519 * infinite face, which we don't count. */
520 g->num_edges = g->num_faces + g->num_dots - 1;
521 g->edges = snewn(g->num_edges, grid_edge);
522 next_new_edge = g->edges;
523
524 /* Iterate over faces, and over each face's dots, generating edges as we
525 * go. As we find each new edge, we can immediately fill in the edge's
526 * dots, but only one of the edge's faces. Later on in the iteration, we
527 * will find the same edge again (unless it's on the border), but we will
528 * know the other face.
529 * For efficiency, maintain a list of the incomplete edges, sorted by
530 * their dots. */
531 incomplete_edges = newtree234(grid_edge_bydots_cmpfn);
532 for (i = 0; i < g->num_faces; i++) {
533 grid_face *f = g->faces + i;
534 int j;
535 for (j = 0; j < f->order; j++) {
536 grid_edge e; /* fake edge for searching */
537 grid_edge *edge_found;
538 int j2 = j + 1;
539 if (j2 == f->order)
540 j2 = 0;
541 e.dot1 = f->dots[j];
542 e.dot2 = f->dots[j2];
543 /* Use del234 instead of find234, because we always want to
544 * remove the edge if found */
545 edge_found = del234(incomplete_edges, &e);
546 if (edge_found) {
547 /* This edge already added, so fill out missing face.
548 * Edge is already removed from incomplete_edges. */
549 edge_found->face2 = f;
550 } else {
551 assert(next_new_edge - g->edges < g->num_edges);
552 next_new_edge->dot1 = e.dot1;
553 next_new_edge->dot2 = e.dot2;
554 next_new_edge->face1 = f;
555 next_new_edge->face2 = NULL; /* potentially infinite face */
556 add234(incomplete_edges, next_new_edge);
557 ++next_new_edge;
558 }
559 }
560 }
561 freetree234(incomplete_edges);
562
563 /* ====== Stage 2 ======
564 * For each face, build its edge list.
565 */
566
567 /* Allocate space for each edge list. Can do this, because each face's
568 * edge-list is the same size as its dot-list. */
569 for (i = 0; i < g->num_faces; i++) {
570 grid_face *f = g->faces + i;
571 int j;
572 f->edges = snewn(f->order, grid_edge*);
573 /* Preload with NULLs, to help detect potential bugs. */
574 for (j = 0; j < f->order; j++)
575 f->edges[j] = NULL;
576 }
577
578 /* Iterate over each edge, and over both its faces. Add this edge to
579 * the face's edge-list, after finding where it should go in the
580 * sequence. */
581 for (i = 0; i < g->num_edges; i++) {
582 grid_edge *e = g->edges + i;
583 int j;
584 for (j = 0; j < 2; j++) {
585 grid_face *f = j ? e->face2 : e->face1;
586 int k, k2;
587 if (f == NULL) continue;
588 /* Find one of the dots around the face */
589 for (k = 0; k < f->order; k++) {
590 if (f->dots[k] == e->dot1)
591 break; /* found dot1 */
592 }
593 assert(k != f->order); /* Must find the dot around this face */
594
595 /* Labelling scheme: as we walk clockwise around the face,
596 * starting at dot0 (f->dots[0]), we hit:
597 * (dot0), edge0, dot1, edge1, dot2,...
598 *
599 * 0
600 * 0-----1
601 * |
602 * |1
603 * |
604 * 3-----2
605 * 2
606 *
607 * Therefore, edgeK joins dotK and dot{K+1}
608 */
609
610 /* Around this face, either the next dot or the previous dot
611 * must be e->dot2. Otherwise the edge is wrong. */
612 k2 = k + 1;
613 if (k2 == f->order)
614 k2 = 0;
615 if (f->dots[k2] == e->dot2) {
616 /* dot1(k) and dot2(k2) go clockwise around this face, so add
617 * this edge at position k (see diagram). */
618 assert(f->edges[k] == NULL);
619 f->edges[k] = e;
620 continue;
621 }
622 /* Try previous dot */
623 k2 = k - 1;
624 if (k2 == -1)
625 k2 = f->order - 1;
626 if (f->dots[k2] == e->dot2) {
627 /* dot1(k) and dot2(k2) go anticlockwise around this face. */
628 assert(f->edges[k2] == NULL);
629 f->edges[k2] = e;
630 continue;
631 }
632 assert(!"Grid broken: bad edge-face relationship");
633 }
634 }
635
636 /* ====== Stage 3 ======
637 * For each dot, build its edge-list and face-list.
638 */
639
640 /* We don't know how many edges/faces go around each dot, so we can't
641 * allocate the right space for these lists. Pre-compute the sizes by
642 * iterating over each edge and recording a tally against each dot. */
643 for (i = 0; i < g->num_dots; i++) {
644 g->dots[i].order = 0;
645 }
646 for (i = 0; i < g->num_edges; i++) {
647 grid_edge *e = g->edges + i;
648 ++(e->dot1->order);
649 ++(e->dot2->order);
650 }
651 /* Now we have the sizes, pre-allocate the edge and face lists. */
652 for (i = 0; i < g->num_dots; i++) {
653 grid_dot *d = g->dots + i;
654 int j;
655 assert(d->order >= 2); /* sanity check */
656 d->edges = snewn(d->order, grid_edge*);
657 d->faces = snewn(d->order, grid_face*);
658 for (j = 0; j < d->order; j++) {
659 d->edges[j] = NULL;
660 d->faces[j] = NULL;
661 }
662 }
663 /* For each dot, need to find a face that touches it, so we can seed
664 * the edge-face-edge-face process around each dot. */
665 for (i = 0; i < g->num_faces; i++) {
666 grid_face *f = g->faces + i;
667 int j;
668 for (j = 0; j < f->order; j++) {
669 grid_dot *d = f->dots[j];
670 d->faces[0] = f;
671 }
672 }
673 /* Each dot now has a face in its first slot. Generate the remaining
674 * faces and edges around the dot, by searching both clockwise and
675 * anticlockwise from the first face. Need to do both directions,
676 * because of the possibility of hitting the infinite face, which
677 * blocks progress. But there's only one such face, so we will
678 * succeed in finding every edge and face this way. */
679 for (i = 0; i < g->num_dots; i++) {
680 grid_dot *d = g->dots + i;
681 int current_face1 = 0; /* ascends clockwise */
682 int current_face2 = 0; /* descends anticlockwise */
683
684 /* Labelling scheme: as we walk clockwise around the dot, starting
685 * at face0 (d->faces[0]), we hit:
686 * (face0), edge0, face1, edge1, face2,...
687 *
688 * 0
689 * |
690 * 0 | 1
691 * |
692 * -----d-----1
693 * |
694 * | 2
695 * |
696 * 2
697 *
698 * So, for example, face1 should be joined to edge0 and edge1,
699 * and those edges should appear in an anticlockwise sense around
700 * that face (see diagram). */
701
702 /* clockwise search */
703 while (TRUE) {
704 grid_face *f = d->faces[current_face1];
705 grid_edge *e;
706 int j;
707 assert(f != NULL);
708 /* find dot around this face */
709 for (j = 0; j < f->order; j++) {
710 if (f->dots[j] == d)
711 break;
712 }
713 assert(j != f->order); /* must find dot */
714
715 /* Around f, required edge is anticlockwise from the dot. See
716 * the other labelling scheme higher up, for why we subtract 1
717 * from j. */
718 j--;
719 if (j == -1)
720 j = f->order - 1;
721 e = f->edges[j];
722 d->edges[current_face1] = e; /* set edge */
723 current_face1++;
724 if (current_face1 == d->order)
725 break;
726 else {
727 /* set face */
728 d->faces[current_face1] =
729 (e->face1 == f) ? e->face2 : e->face1;
730 if (d->faces[current_face1] == NULL)
731 break; /* cannot progress beyond infinite face */
732 }
733 }
734 /* If the clockwise search made it all the way round, don't need to
735 * bother with the anticlockwise search. */
736 if (current_face1 == d->order)
737 continue; /* this dot is complete, move on to next dot */
738
739 /* anticlockwise search */
740 while (TRUE) {
741 grid_face *f = d->faces[current_face2];
742 grid_edge *e;
743 int j;
744 assert(f != NULL);
745 /* find dot around this face */
746 for (j = 0; j < f->order; j++) {
747 if (f->dots[j] == d)
748 break;
749 }
750 assert(j != f->order); /* must find dot */
751
752 /* Around f, required edge is clockwise from the dot. */
753 e = f->edges[j];
754
755 current_face2--;
756 if (current_face2 == -1)
757 current_face2 = d->order - 1;
758 d->edges[current_face2] = e; /* set edge */
759
760 /* set face */
761 if (current_face2 == current_face1)
762 break;
763 d->faces[current_face2] =
764 (e->face1 == f) ? e->face2 : e->face1;
765 /* There's only 1 infinite face, so we must get all the way
766 * to current_face1 before we hit it. */
767 assert(d->faces[current_face2]);
768 }
769 }
770
771 /* ====== Stage 4 ======
772 * Compute other grid settings
773 */
774
775 /* Bounding rectangle */
776 for (i = 0; i < g->num_dots; i++) {
777 grid_dot *d = g->dots + i;
778 if (i == 0) {
779 g->lowest_x = g->highest_x = d->x;
780 g->lowest_y = g->highest_y = d->y;
781 } else {
782 g->lowest_x = min(g->lowest_x, d->x);
783 g->highest_x = max(g->highest_x, d->x);
784 g->lowest_y = min(g->lowest_y, d->y);
785 g->highest_y = max(g->highest_y, d->y);
786 }
787 }
788
789 grid_debug_derived(g);
790}
791
792/* Helpers for making grid-generation easier. These functions are only
793 * intended for use during grid generation. */
794
795/* Comparison function for the (tree234) sorted dot list */
796static int grid_point_cmp_fn(void *v1, void *v2)
797{
798 grid_dot *p1 = v1;
799 grid_dot *p2 = v2;
800 if (p1->y != p2->y)
801 return p2->y - p1->y;
802 else
803 return p2->x - p1->x;
804}
805/* Add a new face to the grid, with its dot list allocated.
806 * Assumes there's enough space allocated for the new face in grid->faces */
807static void grid_face_add_new(grid *g, int face_size)
808{
809 int i;
810 grid_face *new_face = g->faces + g->num_faces;
811 new_face->order = face_size;
812 new_face->dots = snewn(face_size, grid_dot*);
813 for (i = 0; i < face_size; i++)
814 new_face->dots[i] = NULL;
815 new_face->edges = NULL;
816 new_face->has_incentre = FALSE;
817 g->num_faces++;
818}
819/* Assumes dot list has enough space */
820static grid_dot *grid_dot_add_new(grid *g, int x, int y)
821{
822 grid_dot *new_dot = g->dots + g->num_dots;
823 new_dot->order = 0;
824 new_dot->edges = NULL;
825 new_dot->faces = NULL;
826 new_dot->x = x;
827 new_dot->y = y;
828 g->num_dots++;
829 return new_dot;
830}
831/* Retrieve a dot with these (x,y) coordinates. Either return an existing dot
832 * in the dot_list, or add a new dot to the grid (and the dot_list) and
833 * return that.
834 * Assumes g->dots has enough capacity allocated */
835static grid_dot *grid_get_dot(grid *g, tree234 *dot_list, int x, int y)
836{
837 grid_dot test, *ret;
838
839 test.order = 0;
840 test.edges = NULL;
841 test.faces = NULL;
842 test.x = x;
843 test.y = y;
844 ret = find234(dot_list, &test, NULL);
845 if (ret)
846 return ret;
847
848 ret = grid_dot_add_new(g, x, y);
849 add234(dot_list, ret);
850 return ret;
851}
852
853/* Sets the last face of the grid to include this dot, at this position
854 * around the face. Assumes num_faces is at least 1 (a new face has
855 * previously been added, with the required number of dots allocated) */
856static void grid_face_set_dot(grid *g, grid_dot *d, int position)
857{
858 grid_face *last_face = g->faces + g->num_faces - 1;
859 last_face->dots[position] = d;
860}
861
862/*
863 * Helper routines for grid_find_incentre.
864 */
865static int solve_2x2_matrix(double mx[4], double vin[2], double vout[2])
866{
867 double inv[4];
868 double det;
869 det = (mx[0]*mx[3] - mx[1]*mx[2]);
870 if (det == 0)
871 return FALSE;
872
873 inv[0] = mx[3] / det;
874 inv[1] = -mx[1] / det;
875 inv[2] = -mx[2] / det;
876 inv[3] = mx[0] / det;
877
878 vout[0] = inv[0]*vin[0] + inv[1]*vin[1];
879 vout[1] = inv[2]*vin[0] + inv[3]*vin[1];
880
881 return TRUE;
882}
883static int solve_3x3_matrix(double mx[9], double vin[3], double vout[3])
884{
885 double inv[9];
886 double det;
887
888 det = (mx[0]*mx[4]*mx[8] + mx[1]*mx[5]*mx[6] + mx[2]*mx[3]*mx[7] -
889 mx[0]*mx[5]*mx[7] - mx[1]*mx[3]*mx[8] - mx[2]*mx[4]*mx[6]);
890 if (det == 0)
891 return FALSE;
892
893 inv[0] = (mx[4]*mx[8] - mx[5]*mx[7]) / det;
894 inv[1] = (mx[2]*mx[7] - mx[1]*mx[8]) / det;
895 inv[2] = (mx[1]*mx[5] - mx[2]*mx[4]) / det;
896 inv[3] = (mx[5]*mx[6] - mx[3]*mx[8]) / det;
897 inv[4] = (mx[0]*mx[8] - mx[2]*mx[6]) / det;
898 inv[5] = (mx[2]*mx[3] - mx[0]*mx[5]) / det;
899 inv[6] = (mx[3]*mx[7] - mx[4]*mx[6]) / det;
900 inv[7] = (mx[1]*mx[6] - mx[0]*mx[7]) / det;
901 inv[8] = (mx[0]*mx[4] - mx[1]*mx[3]) / det;
902
903 vout[0] = inv[0]*vin[0] + inv[1]*vin[1] + inv[2]*vin[2];
904 vout[1] = inv[3]*vin[0] + inv[4]*vin[1] + inv[5]*vin[2];
905 vout[2] = inv[6]*vin[0] + inv[7]*vin[1] + inv[8]*vin[2];
906
907 return TRUE;
908}
909
910void grid_find_incentre(grid_face *f)
911{
912 double xbest, ybest, bestdist;
913 int i, j, k, m;
914 grid_dot *edgedot1[3], *edgedot2[3];
915 grid_dot *dots[3];
916 int nedges, ndots;
917
918 if (f->has_incentre)
919 return;
920
921 /*
922 * Find the point in the polygon with the maximum distance to any
923 * edge or corner.
924 *
925 * Such a point must exist which is in contact with at least three
926 * edges and/or vertices. (Proof: if it's only in contact with two
927 * edges and/or vertices, it can't even be at a _local_ maximum -
928 * any such circle can always be expanded in some direction.) So
929 * we iterate through all 3-subsets of the combined set of edges
930 * and vertices; for each subset we generate one or two candidate
931 * points that might be the incentre, and then we vet each one to
932 * see if it's inside the polygon and what its maximum radius is.
933 *
934 * (There's one case which this algorithm will get noticeably
935 * wrong, and that's when a continuum of equally good answers
936 * exists due to parallel edges. Consider a long thin rectangle,
937 * for instance, or a parallelogram. This algorithm will pick a
938 * point near one end, and choose the end arbitrarily; obviously a
939 * nicer point to choose would be in the centre. To fix this I
940 * would have to introduce a special-case system which detected
941 * parallel edges in advance, set aside all candidate points
942 * generated using both edges in a parallel pair, and generated
943 * some additional candidate points half way between them. Also,
944 * of course, I'd have to cope with rounding error making such a
945 * point look worse than one of its endpoints. So I haven't done
946 * this for the moment, and will cross it if necessary when I come
947 * to it.)
948 *
949 * We don't actually iterate literally over _edges_, in the sense
950 * of grid_edge structures. Instead, we fill in edgedot1[] and
951 * edgedot2[] with a pair of dots adjacent in the face's list of
952 * vertices. This ensures that we get the edges in consistent
953 * orientation, which we could not do from the grid structure
954 * alone. (A moment's consideration of an order-3 vertex should
955 * make it clear that if a notional arrow was written on each
956 * edge, _at least one_ of the three faces bordering that vertex
957 * would have to have the two arrows tip-to-tip or tail-to-tail
958 * rather than tip-to-tail.)
959 */
960 nedges = ndots = 0;
961 bestdist = 0;
962 xbest = ybest = 0;
963
964 for (i = 0; i+2 < 2*f->order; i++) {
965 if (i < f->order) {
966 edgedot1[nedges] = f->dots[i];
967 edgedot2[nedges++] = f->dots[(i+1)%f->order];
968 } else
969 dots[ndots++] = f->dots[i - f->order];
970
971 for (j = i+1; j+1 < 2*f->order; j++) {
972 if (j < f->order) {
973 edgedot1[nedges] = f->dots[j];
974 edgedot2[nedges++] = f->dots[(j+1)%f->order];
975 } else
976 dots[ndots++] = f->dots[j - f->order];
977
978 for (k = j+1; k < 2*f->order; k++) {
979 double cx[2], cy[2]; /* candidate positions */
980 int cn = 0; /* number of candidates */
981
982 if (k < f->order) {
983 edgedot1[nedges] = f->dots[k];
984 edgedot2[nedges++] = f->dots[(k+1)%f->order];
985 } else
986 dots[ndots++] = f->dots[k - f->order];
987
988 /*
989 * Find a point, or pair of points, equidistant from
990 * all the specified edges and/or vertices.
991 */
992 if (nedges == 3) {
993 /*
994 * Three edges. This is a linear matrix equation:
995 * each row of the matrix represents the fact that
996 * the point (x,y) we seek is at distance r from
997 * that edge, and we solve three of those
998 * simultaneously to obtain x,y,r. (We ignore r.)
999 */
1000 double matrix[9], vector[3], vector2[3];
1001 int m;
1002
1003 for (m = 0; m < 3; m++) {
1004 int x1 = edgedot1[m]->x, x2 = edgedot2[m]->x;
1005 int y1 = edgedot1[m]->y, y2 = edgedot2[m]->y;
1006 int dx = x2-x1, dy = y2-y1;
1007
1008 /*
1009 * ((x,y) - (x1,y1)) . (dy,-dx) = r |(dx,dy)|
1010 *
1011 * => x dy - y dx - r |(dx,dy)| = (x1 dy - y1 dx)
1012 */
1013 matrix[3*m+0] = dy;
1014 matrix[3*m+1] = -dx;
1015 matrix[3*m+2] = -sqrt((double)dx*dx+(double)dy*dy);
1016 vector[m] = (double)x1*dy - (double)y1*dx;
1017 }
1018
1019 if (solve_3x3_matrix(matrix, vector, vector2)) {
1020 cx[cn] = vector2[0];
1021 cy[cn] = vector2[1];
1022 cn++;
1023 }
1024 } else if (nedges == 2) {
1025 /*
1026 * Two edges and a dot. This will end up in a
1027 * quadratic equation.
1028 *
1029 * First, look at the two edges. Having our point
1030 * be some distance r from both of them gives rise
1031 * to a pair of linear equations in x,y,r of the
1032 * form
1033 *
1034 * (x-x1) dy - (y-y1) dx = r sqrt(dx^2+dy^2)
1035 *
1036 * We eliminate r between those equations to give
1037 * us a single linear equation in x,y describing
1038 * the locus of points equidistant from both lines
1039 * - i.e. the angle bisector.
1040 *
1041 * We then choose one of x,y to be a parameter t,
1042 * and derive linear formulae for x,y,r in terms
1043 * of t. This enables us to write down the
1044 * circular equation (x-xd)^2+(y-yd)^2=r^2 as a
1045 * quadratic in t; solving that and substituting
1046 * in for x,y gives us two candidate points.
1047 */
1048 double eqs[2][4]; /* a,b,c,d : ax+by+cr=d */
1049 double eq[3]; /* a,b,c: ax+by=c */
1050 double xt[2], yt[2], rt[2]; /* a,b: {x,y,r}=at+b */
1051 double q[3]; /* a,b,c: at^2+bt+c=0 */
1052 double disc;
1053
1054 /* Find equations of the two input lines. */
1055 for (m = 0; m < 2; m++) {
1056 int x1 = edgedot1[m]->x, x2 = edgedot2[m]->x;
1057 int y1 = edgedot1[m]->y, y2 = edgedot2[m]->y;
1058 int dx = x2-x1, dy = y2-y1;
1059
1060 eqs[m][0] = dy;
1061 eqs[m][1] = -dx;
1062 eqs[m][2] = -sqrt(dx*dx+dy*dy);
1063 eqs[m][3] = x1*dy - y1*dx;
1064 }
1065
1066 /* Derive the angle bisector by eliminating r. */
1067 eq[0] = eqs[0][0]*eqs[1][2] - eqs[1][0]*eqs[0][2];
1068 eq[1] = eqs[0][1]*eqs[1][2] - eqs[1][1]*eqs[0][2];
1069 eq[2] = eqs[0][3]*eqs[1][2] - eqs[1][3]*eqs[0][2];
1070
1071 /* Parametrise x and y in terms of some t. */
1072 if (fabs(eq[0]) < fabs(eq[1])) {
1073 /* Parameter is x. */
1074 xt[0] = 1; xt[1] = 0;
1075 yt[0] = -eq[0]/eq[1]; yt[1] = eq[2]/eq[1];
1076 } else {
1077 /* Parameter is y. */
1078 yt[0] = 1; yt[1] = 0;
1079 xt[0] = -eq[1]/eq[0]; xt[1] = eq[2]/eq[0];
1080 }
1081
1082 /* Find a linear representation of r using eqs[0]. */
1083 rt[0] = -(eqs[0][0]*xt[0] + eqs[0][1]*yt[0])/eqs[0][2];
1084 rt[1] = (eqs[0][3] - eqs[0][0]*xt[1] -
1085 eqs[0][1]*yt[1])/eqs[0][2];
1086
1087 /* Construct the quadratic equation. */
1088 q[0] = -rt[0]*rt[0];
1089 q[1] = -2*rt[0]*rt[1];
1090 q[2] = -rt[1]*rt[1];
1091 q[0] += xt[0]*xt[0];
1092 q[1] += 2*xt[0]*(xt[1]-dots[0]->x);
1093 q[2] += (xt[1]-dots[0]->x)*(xt[1]-dots[0]->x);
1094 q[0] += yt[0]*yt[0];
1095 q[1] += 2*yt[0]*(yt[1]-dots[0]->y);
1096 q[2] += (yt[1]-dots[0]->y)*(yt[1]-dots[0]->y);
1097
1098 /* And solve it. */
1099 disc = q[1]*q[1] - 4*q[0]*q[2];
1100 if (disc >= 0) {
1101 double t;
1102
1103 disc = sqrt(disc);
1104
1105 t = (-q[1] + disc) / (2*q[0]);
1106 cx[cn] = xt[0]*t + xt[1];
1107 cy[cn] = yt[0]*t + yt[1];
1108 cn++;
1109
1110 t = (-q[1] - disc) / (2*q[0]);
1111 cx[cn] = xt[0]*t + xt[1];
1112 cy[cn] = yt[0]*t + yt[1];
1113 cn++;
1114 }
1115 } else if (nedges == 1) {
1116 /*
1117 * Two dots and an edge. This one's another
1118 * quadratic equation.
1119 *
1120 * The point we want must lie on the perpendicular
1121 * bisector of the two dots; that much is obvious.
1122 * So we can construct a parametrisation of that
1123 * bisecting line, giving linear formulae for x,y
1124 * in terms of t. We can also express the distance
1125 * from the edge as such a linear formula.
1126 *
1127 * Then we set that equal to the radius of the
1128 * circle passing through the two points, which is
1129 * a Pythagoras exercise; that gives rise to a
1130 * quadratic in t, which we solve.
1131 */
1132 double xt[2], yt[2], rt[2]; /* a,b: {x,y,r}=at+b */
1133 double q[3]; /* a,b,c: at^2+bt+c=0 */
1134 double disc;
1135 double halfsep;
1136
1137 /* Find parametric formulae for x,y. */
1138 {
1139 int x1 = dots[0]->x, x2 = dots[1]->x;
1140 int y1 = dots[0]->y, y2 = dots[1]->y;
1141 int dx = x2-x1, dy = y2-y1;
1142 double d = sqrt((double)dx*dx + (double)dy*dy);
1143
1144 xt[1] = (x1+x2)/2.0;
1145 yt[1] = (y1+y2)/2.0;
1146 /* It's convenient if we have t at standard scale. */
1147 xt[0] = -dy/d;
1148 yt[0] = dx/d;
1149
1150 /* Also note down half the separation between
1151 * the dots, for use in computing the circle radius. */
1152 halfsep = 0.5*d;
1153 }
1154
1155 /* Find a parametric formula for r. */
1156 {
1157 int x1 = edgedot1[0]->x, x2 = edgedot2[0]->x;
1158 int y1 = edgedot1[0]->y, y2 = edgedot2[0]->y;
1159 int dx = x2-x1, dy = y2-y1;
1160 double d = sqrt((double)dx*dx + (double)dy*dy);
1161 rt[0] = (xt[0]*dy - yt[0]*dx) / d;
1162 rt[1] = ((xt[1]-x1)*dy - (yt[1]-y1)*dx) / d;
1163 }
1164
1165 /* Construct the quadratic equation. */
1166 q[0] = rt[0]*rt[0];
1167 q[1] = 2*rt[0]*rt[1];
1168 q[2] = rt[1]*rt[1];
1169 q[0] -= 1;
1170 q[2] -= halfsep*halfsep;
1171
1172 /* And solve it. */
1173 disc = q[1]*q[1] - 4*q[0]*q[2];
1174 if (disc >= 0) {
1175 double t;
1176
1177 disc = sqrt(disc);
1178
1179 t = (-q[1] + disc) / (2*q[0]);
1180 cx[cn] = xt[0]*t + xt[1];
1181 cy[cn] = yt[0]*t + yt[1];
1182 cn++;
1183
1184 t = (-q[1] - disc) / (2*q[0]);
1185 cx[cn] = xt[0]*t + xt[1];
1186 cy[cn] = yt[0]*t + yt[1];
1187 cn++;
1188 }
1189 } else if (nedges == 0) {
1190 /*
1191 * Three dots. This is another linear matrix
1192 * equation, this time with each row of the matrix
1193 * representing the perpendicular bisector between
1194 * two of the points. Of course we only need two
1195 * such lines to find their intersection, so we
1196 * need only solve a 2x2 matrix equation.
1197 */
1198
1199 double matrix[4], vector[2], vector2[2];
1200 int m;
1201
1202 for (m = 0; m < 2; m++) {
1203 int x1 = dots[m]->x, x2 = dots[m+1]->x;
1204 int y1 = dots[m]->y, y2 = dots[m+1]->y;
1205 int dx = x2-x1, dy = y2-y1;
1206
1207 /*
1208 * ((x,y) - (x1,y1)) . (dx,dy) = 1/2 |(dx,dy)|^2
1209 *
1210 * => 2x dx + 2y dy = dx^2+dy^2 + (2 x1 dx + 2 y1 dy)
1211 */
1212 matrix[2*m+0] = 2*dx;
1213 matrix[2*m+1] = 2*dy;
1214 vector[m] = ((double)dx*dx + (double)dy*dy +
1215 2.0*x1*dx + 2.0*y1*dy);
1216 }
1217
1218 if (solve_2x2_matrix(matrix, vector, vector2)) {
1219 cx[cn] = vector2[0];
1220 cy[cn] = vector2[1];
1221 cn++;
1222 }
1223 }
1224
1225 /*
1226 * Now go through our candidate points and see if any
1227 * of them are better than what we've got so far.
1228 */
1229 for (m = 0; m < cn; m++) {
1230 double x = cx[m], y = cy[m];
1231
1232 /*
1233 * First, disqualify the point if it's not inside
1234 * the polygon, which we work out by counting the
1235 * edges to the right of the point. (For
1236 * tiebreaking purposes when edges start or end on
1237 * our y-coordinate or go right through it, we
1238 * consider our point to be offset by a small
1239 * _positive_ epsilon in both the x- and
1240 * y-direction.)
1241 */
1242 int e, in = 0;
1243 for (e = 0; e < f->order; e++) {
1244 int xs = f->edges[e]->dot1->x;
1245 int xe = f->edges[e]->dot2->x;
1246 int ys = f->edges[e]->dot1->y;
1247 int ye = f->edges[e]->dot2->y;
1248 if ((y >= ys && y < ye) || (y >= ye && y < ys)) {
1249 /*
1250 * The line goes past our y-position. Now we need
1251 * to know if its x-coordinate when it does so is
1252 * to our right.
1253 *
1254 * The x-coordinate in question is mathematically
1255 * (y - ys) * (xe - xs) / (ye - ys), and we want
1256 * to know whether (x - xs) >= that. Of course we
1257 * avoid the division, so we can work in integers;
1258 * to do this we must multiply both sides of the
1259 * inequality by ye - ys, which means we must
1260 * first check that's not negative.
1261 */
1262 int num = xe - xs, denom = ye - ys;
1263 if (denom < 0) {
1264 num = -num;
1265 denom = -denom;
1266 }
1267 if ((x - xs) * denom >= (y - ys) * num)
1268 in ^= 1;
1269 }
1270 }
1271
1272 if (in) {
1273#ifdef HUGE_VAL
1274 double mindist = HUGE_VAL;
1275#else
1276#ifdef DBL_MAX
1277 double mindist = DBL_MAX;
1278#else
1279#error No way to get maximum floating-point number.
1280#endif
1281#endif
1282 int e, d;
1283
1284 /*
1285 * This point is inside the polygon, so now we check
1286 * its minimum distance to every edge and corner.
1287 * First the corners ...
1288 */
1289 for (d = 0; d < f->order; d++) {
1290 int xp = f->dots[d]->x;
1291 int yp = f->dots[d]->y;
1292 double dx = x - xp, dy = y - yp;
1293 double dist = dx*dx + dy*dy;
1294 if (mindist > dist)
1295 mindist = dist;
1296 }
1297
1298 /*
1299 * ... and now also check the perpendicular distance
1300 * to every edge, if the perpendicular lies between
1301 * the edge's endpoints.
1302 */
1303 for (e = 0; e < f->order; e++) {
1304 int xs = f->edges[e]->dot1->x;
1305 int xe = f->edges[e]->dot2->x;
1306 int ys = f->edges[e]->dot1->y;
1307 int ye = f->edges[e]->dot2->y;
1308
1309 /*
1310 * If s and e are our endpoints, and p our
1311 * candidate circle centre, the foot of a
1312 * perpendicular from p to the line se lies
1313 * between s and e if and only if (p-s).(e-s) lies
1314 * strictly between 0 and (e-s).(e-s).
1315 */
1316 int edx = xe - xs, edy = ye - ys;
1317 double pdx = x - xs, pdy = y - ys;
1318 double pde = pdx * edx + pdy * edy;
1319 long ede = (long)edx * edx + (long)edy * edy;
1320 if (0 < pde && pde < ede) {
1321 /*
1322 * Yes, the nearest point on this edge is
1323 * closer than either endpoint, so we must
1324 * take it into account by measuring the
1325 * perpendicular distance to the edge and
1326 * checking its square against mindist.
1327 */
1328
1329 double pdre = pdx * edy - pdy * edx;
1330 double sqlen = pdre * pdre / ede;
1331
1332 if (mindist > sqlen)
1333 mindist = sqlen;
1334 }
1335 }
1336
1337 /*
1338 * Right. Now we know the biggest circle around this
1339 * point, so we can check it against bestdist.
1340 */
1341 if (bestdist < mindist) {
1342 bestdist = mindist;
1343 xbest = x;
1344 ybest = y;
1345 }
1346 }
1347 }
1348
1349 if (k < f->order)
1350 nedges--;
1351 else
1352 ndots--;
1353 }
1354 if (j < f->order)
1355 nedges--;
1356 else
1357 ndots--;
1358 }
1359 if (i < f->order)
1360 nedges--;
1361 else
1362 ndots--;
1363 }
1364
1365 assert(bestdist > 0);
1366
1367 f->has_incentre = TRUE;
1368 f->ix = xbest + 0.5; /* round to nearest */
1369 f->iy = ybest + 0.5;
1370}
1371
1372/* ------ Generate various types of grid ------ */
1373
1374/* General method is to generate faces, by calculating their dot coordinates.
1375 * As new faces are added, we keep track of all the dots so we can tell when
1376 * a new face reuses an existing dot. For example, two squares touching at an
1377 * edge would generate six unique dots: four dots from the first face, then
1378 * two additional dots for the second face, because we detect the other two
1379 * dots have already been taken up. This list is stored in a tree234
1380 * called "points". No extra memory-allocation needed here - we store the
1381 * actual grid_dot* pointers, which all point into the g->dots list.
1382 * For this reason, we have to calculate coordinates in such a way as to
1383 * eliminate any rounding errors, so we can detect when a dot on one
1384 * face precisely lands on a dot of a different face. No floating-point
1385 * arithmetic here!
1386 */
1387
1388#define SQUARE_TILESIZE 20
1389
1390static void grid_size_square(int width, int height,
1391 int *tilesize, int *xextent, int *yextent)
1392{
1393 int a = SQUARE_TILESIZE;
1394
1395 *tilesize = a;
1396 *xextent = width * a;
1397 *yextent = height * a;
1398}
1399
1400static grid *grid_new_square(int width, int height, const char *desc)
1401{
1402 int x, y;
1403 /* Side length */
1404 int a = SQUARE_TILESIZE;
1405
1406 /* Upper bounds - don't have to be exact */
1407 int max_faces = width * height;
1408 int max_dots = (width + 1) * (height + 1);
1409
1410 tree234 *points;
1411
1412 grid *g = grid_empty();
1413 g->tilesize = a;
1414 g->faces = snewn(max_faces, grid_face);
1415 g->dots = snewn(max_dots, grid_dot);
1416
1417 points = newtree234(grid_point_cmp_fn);
1418
1419 /* generate square faces */
1420 for (y = 0; y < height; y++) {
1421 for (x = 0; x < width; x++) {
1422 grid_dot *d;
1423 /* face position */
1424 int px = a * x;
1425 int py = a * y;
1426
1427 grid_face_add_new(g, 4);
1428 d = grid_get_dot(g, points, px, py);
1429 grid_face_set_dot(g, d, 0);
1430 d = grid_get_dot(g, points, px + a, py);
1431 grid_face_set_dot(g, d, 1);
1432 d = grid_get_dot(g, points, px + a, py + a);
1433 grid_face_set_dot(g, d, 2);
1434 d = grid_get_dot(g, points, px, py + a);
1435 grid_face_set_dot(g, d, 3);
1436 }
1437 }
1438
1439 freetree234(points);
1440 assert(g->num_faces <= max_faces);
1441 assert(g->num_dots <= max_dots);
1442
1443 grid_make_consistent(g);
1444 return g;
1445}
1446
1447#define HONEY_TILESIZE 45
1448/* Vector for side of hexagon - ratio is close to sqrt(3) */
1449#define HONEY_A 15
1450#define HONEY_B 26
1451
1452static void grid_size_honeycomb(int width, int height,
1453 int *tilesize, int *xextent, int *yextent)
1454{
1455 int a = HONEY_A;
1456 int b = HONEY_B;
1457
1458 *tilesize = HONEY_TILESIZE;
1459 *xextent = (3 * a * (width-1)) + 4*a;
1460 *yextent = (2 * b * (height-1)) + 3*b;
1461}
1462
1463static grid *grid_new_honeycomb(int width, int height, const char *desc)
1464{
1465 int x, y;
1466 int a = HONEY_A;
1467 int b = HONEY_B;
1468
1469 /* Upper bounds - don't have to be exact */
1470 int max_faces = width * height;
1471 int max_dots = 2 * (width + 1) * (height + 1);
1472
1473 tree234 *points;
1474
1475 grid *g = grid_empty();
1476 g->tilesize = HONEY_TILESIZE;
1477 g->faces = snewn(max_faces, grid_face);
1478 g->dots = snewn(max_dots, grid_dot);
1479
1480 points = newtree234(grid_point_cmp_fn);
1481
1482 /* generate hexagonal faces */
1483 for (y = 0; y < height; y++) {
1484 for (x = 0; x < width; x++) {
1485 grid_dot *d;
1486 /* face centre */
1487 int cx = 3 * a * x;
1488 int cy = 2 * b * y;
1489 if (x % 2)
1490 cy += b;
1491 grid_face_add_new(g, 6);
1492
1493 d = grid_get_dot(g, points, cx - a, cy - b);
1494 grid_face_set_dot(g, d, 0);
1495 d = grid_get_dot(g, points, cx + a, cy - b);
1496 grid_face_set_dot(g, d, 1);
1497 d = grid_get_dot(g, points, cx + 2*a, cy);
1498 grid_face_set_dot(g, d, 2);
1499 d = grid_get_dot(g, points, cx + a, cy + b);
1500 grid_face_set_dot(g, d, 3);
1501 d = grid_get_dot(g, points, cx - a, cy + b);
1502 grid_face_set_dot(g, d, 4);
1503 d = grid_get_dot(g, points, cx - 2*a, cy);
1504 grid_face_set_dot(g, d, 5);
1505 }
1506 }
1507
1508 freetree234(points);
1509 assert(g->num_faces <= max_faces);
1510 assert(g->num_dots <= max_dots);
1511
1512 grid_make_consistent(g);
1513 return g;
1514}
1515
1516#define TRIANGLE_TILESIZE 18
1517/* Vector for side of triangle - ratio is close to sqrt(3) */
1518#define TRIANGLE_VEC_X 15
1519#define TRIANGLE_VEC_Y 26
1520
1521static void grid_size_triangular(int width, int height,
1522 int *tilesize, int *xextent, int *yextent)
1523{
1524 int vec_x = TRIANGLE_VEC_X;
1525 int vec_y = TRIANGLE_VEC_Y;
1526
1527 *tilesize = TRIANGLE_TILESIZE;
1528 *xextent = (width+1) * 2 * vec_x;
1529 *yextent = height * vec_y;
1530}
1531
1532static char *grid_validate_desc_triangular(grid_type type, int width,
1533 int height, const char *desc)
1534{
1535 /*
1536 * Triangular grids: an absent description is valid (indicating
1537 * the old-style approach which had 'ears', i.e. triangles
1538 * connected to only one other face, at some grid corners), and so
1539 * is a description reading just "0" (indicating the new-style
1540 * approach in which those ears are trimmed off). Anything else is
1541 * illegal.
1542 */
1543
1544 if (!desc || !strcmp(desc, "0"))
1545 return NULL;
1546
1547 return "Unrecognised grid description.";
1548}
1549
1550/* Doesn't use the previous method of generation, it pre-dates it!
1551 * A triangular grid is just about simple enough to do by "brute force" */
1552static grid *grid_new_triangular(int width, int height, const char *desc)
1553{
1554 int x,y;
1555 int version = (desc == NULL ? -1 : atoi(desc));
1556
1557 /* Vector for side of triangle - ratio is close to sqrt(3) */
1558 int vec_x = TRIANGLE_VEC_X;
1559 int vec_y = TRIANGLE_VEC_Y;
1560
1561 int index;
1562
1563 /* convenient alias */
1564 int w = width + 1;
1565
1566 grid *g = grid_empty();
1567 g->tilesize = TRIANGLE_TILESIZE;
1568
1569 if (version == -1) {
1570 /*
1571 * Old-style triangular grid generation, preserved as-is for
1572 * backwards compatibility with old game ids, in which it's
1573 * just a little asymmetric and there are 'ears' (faces linked
1574 * to only one other face) at two grid corners.
1575 *
1576 * Example old-style game ids, which should still work, and in
1577 * which you should see the ears in the TL/BR corners on the
1578 * first one and in the TL/BL corners on the second:
1579 *
1580 * 5x5t1:2c2a1a2a201a1a1a1112a1a2b1211f0b21a2a2a0a
1581 * 5x6t1:a022a212h1a1d1a12c2b11a012b1a20d1a0a12e
1582 */
1583
1584 g->num_faces = width * height * 2;
1585 g->num_dots = (width + 1) * (height + 1);
1586 g->faces = snewn(g->num_faces, grid_face);
1587 g->dots = snewn(g->num_dots, grid_dot);
1588
1589 /* generate dots */
1590 index = 0;
1591 for (y = 0; y <= height; y++) {
1592 for (x = 0; x <= width; x++) {
1593 grid_dot *d = g->dots + index;
1594 /* odd rows are offset to the right */
1595 d->order = 0;
1596 d->edges = NULL;
1597 d->faces = NULL;
1598 d->x = x * 2 * vec_x + ((y % 2) ? vec_x : 0);
1599 d->y = y * vec_y;
1600 index++;
1601 }
1602 }
1603
1604 /* generate faces */
1605 index = 0;
1606 for (y = 0; y < height; y++) {
1607 for (x = 0; x < width; x++) {
1608 /* initialise two faces for this (x,y) */
1609 grid_face *f1 = g->faces + index;
1610 grid_face *f2 = f1 + 1;
1611 f1->edges = NULL;
1612 f1->order = 3;
1613 f1->dots = snewn(f1->order, grid_dot*);
1614 f1->has_incentre = FALSE;
1615 f2->edges = NULL;
1616 f2->order = 3;
1617 f2->dots = snewn(f2->order, grid_dot*);
1618 f2->has_incentre = FALSE;
1619
1620 /* face descriptions depend on whether the row-number is
1621 * odd or even */
1622 if (y % 2) {
1623 f1->dots[0] = g->dots + y * w + x;
1624 f1->dots[1] = g->dots + (y + 1) * w + x + 1;
1625 f1->dots[2] = g->dots + (y + 1) * w + x;
1626 f2->dots[0] = g->dots + y * w + x;
1627 f2->dots[1] = g->dots + y * w + x + 1;
1628 f2->dots[2] = g->dots + (y + 1) * w + x + 1;
1629 } else {
1630 f1->dots[0] = g->dots + y * w + x;
1631 f1->dots[1] = g->dots + y * w + x + 1;
1632 f1->dots[2] = g->dots + (y + 1) * w + x;
1633 f2->dots[0] = g->dots + y * w + x + 1;
1634 f2->dots[1] = g->dots + (y + 1) * w + x + 1;
1635 f2->dots[2] = g->dots + (y + 1) * w + x;
1636 }
1637 index += 2;
1638 }
1639 }
1640 } else {
1641 /*
1642 * New-style approach, in which there are never any 'ears',
1643 * and if height is even then the grid is nicely 4-way
1644 * symmetric.
1645 *
1646 * Example new-style grids:
1647 *
1648 * 5x5t1:0_21120b11a1a01a1a00c1a0b211021c1h1a2a1a0a
1649 * 5x6t1:0_a1212c22c2a02a2f22a0c12a110d0e1c0c0a101121a1
1650 */
1651 tree234 *points = newtree234(grid_point_cmp_fn);
1652 /* Upper bounds - don't have to be exact */
1653 int max_faces = height * (2*width+1);
1654 int max_dots = (height+1) * (width+1) * 4;
1655
1656 g->faces = snewn(max_faces, grid_face);
1657 g->dots = snewn(max_dots, grid_dot);
1658
1659 for (y = 0; y < height; y++) {
1660 /*
1661 * Each row contains (width+1) triangles one way up, and
1662 * (width) triangles the other way up. Which way up is
1663 * which varies with parity of y. Also, the dots around
1664 * each face will flip direction with parity of y, so we
1665 * set up n1 and n2 to cope with that easily.
1666 */
1667 int y0, y1, n1, n2;
1668 y0 = y1 = y * vec_y;
1669 if (y % 2) {
1670 y1 += vec_y;
1671 n1 = 2; n2 = 1;
1672 } else {
1673 y0 += vec_y;
1674 n1 = 1; n2 = 2;
1675 }
1676
1677 for (x = 0; x <= width; x++) {
1678 int x0 = 2*x * vec_x, x1 = x0 + vec_x, x2 = x1 + vec_x;
1679
1680 /*
1681 * If the grid has odd height, then we skip the first
1682 * and last triangles on this row, otherwise they'll
1683 * end up as ears.
1684 */
1685 if (height % 2 == 1 && y == height-1 && (x == 0 || x == width))
1686 continue;
1687
1688 grid_face_add_new(g, 3);
1689 grid_face_set_dot(g, grid_get_dot(g, points, x0, y0), 0);
1690 grid_face_set_dot(g, grid_get_dot(g, points, x1, y1), n1);
1691 grid_face_set_dot(g, grid_get_dot(g, points, x2, y0), n2);
1692 }
1693
1694 for (x = 0; x < width; x++) {
1695 int x0 = (2*x+1) * vec_x, x1 = x0 + vec_x, x2 = x1 + vec_x;
1696
1697 grid_face_add_new(g, 3);
1698 grid_face_set_dot(g, grid_get_dot(g, points, x0, y1), 0);
1699 grid_face_set_dot(g, grid_get_dot(g, points, x1, y0), n2);
1700 grid_face_set_dot(g, grid_get_dot(g, points, x2, y1), n1);
1701 }
1702 }
1703
1704 freetree234(points);
1705 assert(g->num_faces <= max_faces);
1706 assert(g->num_dots <= max_dots);
1707 }
1708
1709 grid_make_consistent(g);
1710 return g;
1711}
1712
1713#define SNUBSQUARE_TILESIZE 18
1714/* Vector for side of triangle - ratio is close to sqrt(3) */
1715#define SNUBSQUARE_A 15
1716#define SNUBSQUARE_B 26
1717
1718static void grid_size_snubsquare(int width, int height,
1719 int *tilesize, int *xextent, int *yextent)
1720{
1721 int a = SNUBSQUARE_A;
1722 int b = SNUBSQUARE_B;
1723
1724 *tilesize = SNUBSQUARE_TILESIZE;
1725 *xextent = (a+b) * (width-1) + a + b;
1726 *yextent = (a+b) * (height-1) + a + b;
1727}
1728
1729static grid *grid_new_snubsquare(int width, int height, const char *desc)
1730{
1731 int x, y;
1732 int a = SNUBSQUARE_A;
1733 int b = SNUBSQUARE_B;
1734
1735 /* Upper bounds - don't have to be exact */
1736 int max_faces = 3 * width * height;
1737 int max_dots = 2 * (width + 1) * (height + 1);
1738
1739 tree234 *points;
1740
1741 grid *g = grid_empty();
1742 g->tilesize = SNUBSQUARE_TILESIZE;
1743 g->faces = snewn(max_faces, grid_face);
1744 g->dots = snewn(max_dots, grid_dot);
1745
1746 points = newtree234(grid_point_cmp_fn);
1747
1748 for (y = 0; y < height; y++) {
1749 for (x = 0; x < width; x++) {
1750 grid_dot *d;
1751 /* face position */
1752 int px = (a + b) * x;
1753 int py = (a + b) * y;
1754
1755 /* generate square faces */
1756 grid_face_add_new(g, 4);
1757 if ((x + y) % 2) {
1758 d = grid_get_dot(g, points, px + a, py);
1759 grid_face_set_dot(g, d, 0);
1760 d = grid_get_dot(g, points, px + a + b, py + a);
1761 grid_face_set_dot(g, d, 1);
1762 d = grid_get_dot(g, points, px + b, py + a + b);
1763 grid_face_set_dot(g, d, 2);
1764 d = grid_get_dot(g, points, px, py + b);
1765 grid_face_set_dot(g, d, 3);
1766 } else {
1767 d = grid_get_dot(g, points, px + b, py);
1768 grid_face_set_dot(g, d, 0);
1769 d = grid_get_dot(g, points, px + a + b, py + b);
1770 grid_face_set_dot(g, d, 1);
1771 d = grid_get_dot(g, points, px + a, py + a + b);
1772 grid_face_set_dot(g, d, 2);
1773 d = grid_get_dot(g, points, px, py + a);
1774 grid_face_set_dot(g, d, 3);
1775 }
1776
1777 /* generate up/down triangles */
1778 if (x > 0) {
1779 grid_face_add_new(g, 3);
1780 if ((x + y) % 2) {
1781 d = grid_get_dot(g, points, px + a, py);
1782 grid_face_set_dot(g, d, 0);
1783 d = grid_get_dot(g, points, px, py + b);
1784 grid_face_set_dot(g, d, 1);
1785 d = grid_get_dot(g, points, px - a, py);
1786 grid_face_set_dot(g, d, 2);
1787 } else {
1788 d = grid_get_dot(g, points, px, py + a);
1789 grid_face_set_dot(g, d, 0);
1790 d = grid_get_dot(g, points, px + a, py + a + b);
1791 grid_face_set_dot(g, d, 1);
1792 d = grid_get_dot(g, points, px - a, py + a + b);
1793 grid_face_set_dot(g, d, 2);
1794 }
1795 }
1796
1797 /* generate left/right triangles */
1798 if (y > 0) {
1799 grid_face_add_new(g, 3);
1800 if ((x + y) % 2) {
1801 d = grid_get_dot(g, points, px + a, py);
1802 grid_face_set_dot(g, d, 0);
1803 d = grid_get_dot(g, points, px + a + b, py - a);
1804 grid_face_set_dot(g, d, 1);
1805 d = grid_get_dot(g, points, px + a + b, py + a);
1806 grid_face_set_dot(g, d, 2);
1807 } else {
1808 d = grid_get_dot(g, points, px, py - a);
1809 grid_face_set_dot(g, d, 0);
1810 d = grid_get_dot(g, points, px + b, py);
1811 grid_face_set_dot(g, d, 1);
1812 d = grid_get_dot(g, points, px, py + a);
1813 grid_face_set_dot(g, d, 2);
1814 }
1815 }
1816 }
1817 }
1818
1819 freetree234(points);
1820 assert(g->num_faces <= max_faces);
1821 assert(g->num_dots <= max_dots);
1822
1823 grid_make_consistent(g);
1824 return g;
1825}
1826
1827#define CAIRO_TILESIZE 40
1828/* Vector for side of pentagon - ratio is close to (4+sqrt(7))/3 */
1829#define CAIRO_A 14
1830#define CAIRO_B 31
1831
1832static void grid_size_cairo(int width, int height,
1833 int *tilesize, int *xextent, int *yextent)
1834{
1835 int b = CAIRO_B; /* a unused in determining grid size. */
1836
1837 *tilesize = CAIRO_TILESIZE;
1838 *xextent = 2*b*(width-1) + 2*b;
1839 *yextent = 2*b*(height-1) + 2*b;
1840}
1841
1842static grid *grid_new_cairo(int width, int height, const char *desc)
1843{
1844 int x, y;
1845 int a = CAIRO_A;
1846 int b = CAIRO_B;
1847
1848 /* Upper bounds - don't have to be exact */
1849 int max_faces = 2 * width * height;
1850 int max_dots = 3 * (width + 1) * (height + 1);
1851
1852 tree234 *points;
1853
1854 grid *g = grid_empty();
1855 g->tilesize = CAIRO_TILESIZE;
1856 g->faces = snewn(max_faces, grid_face);
1857 g->dots = snewn(max_dots, grid_dot);
1858
1859 points = newtree234(grid_point_cmp_fn);
1860
1861 for (y = 0; y < height; y++) {
1862 for (x = 0; x < width; x++) {
1863 grid_dot *d;
1864 /* cell position */
1865 int px = 2 * b * x;
1866 int py = 2 * b * y;
1867
1868 /* horizontal pentagons */
1869 if (y > 0) {
1870 grid_face_add_new(g, 5);
1871 if ((x + y) % 2) {
1872 d = grid_get_dot(g, points, px + a, py - b);
1873 grid_face_set_dot(g, d, 0);
1874 d = grid_get_dot(g, points, px + 2*b - a, py - b);
1875 grid_face_set_dot(g, d, 1);
1876 d = grid_get_dot(g, points, px + 2*b, py);
1877 grid_face_set_dot(g, d, 2);
1878 d = grid_get_dot(g, points, px + b, py + a);
1879 grid_face_set_dot(g, d, 3);
1880 d = grid_get_dot(g, points, px, py);
1881 grid_face_set_dot(g, d, 4);
1882 } else {
1883 d = grid_get_dot(g, points, px, py);
1884 grid_face_set_dot(g, d, 0);
1885 d = grid_get_dot(g, points, px + b, py - a);
1886 grid_face_set_dot(g, d, 1);
1887 d = grid_get_dot(g, points, px + 2*b, py);
1888 grid_face_set_dot(g, d, 2);
1889 d = grid_get_dot(g, points, px + 2*b - a, py + b);
1890 grid_face_set_dot(g, d, 3);
1891 d = grid_get_dot(g, points, px + a, py + b);
1892 grid_face_set_dot(g, d, 4);
1893 }
1894 }
1895 /* vertical pentagons */
1896 if (x > 0) {
1897 grid_face_add_new(g, 5);
1898 if ((x + y) % 2) {
1899 d = grid_get_dot(g, points, px, py);
1900 grid_face_set_dot(g, d, 0);
1901 d = grid_get_dot(g, points, px + b, py + a);
1902 grid_face_set_dot(g, d, 1);
1903 d = grid_get_dot(g, points, px + b, py + 2*b - a);
1904 grid_face_set_dot(g, d, 2);
1905 d = grid_get_dot(g, points, px, py + 2*b);
1906 grid_face_set_dot(g, d, 3);
1907 d = grid_get_dot(g, points, px - a, py + b);
1908 grid_face_set_dot(g, d, 4);
1909 } else {
1910 d = grid_get_dot(g, points, px, py);
1911 grid_face_set_dot(g, d, 0);
1912 d = grid_get_dot(g, points, px + a, py + b);
1913 grid_face_set_dot(g, d, 1);
1914 d = grid_get_dot(g, points, px, py + 2*b);
1915 grid_face_set_dot(g, d, 2);
1916 d = grid_get_dot(g, points, px - b, py + 2*b - a);
1917 grid_face_set_dot(g, d, 3);
1918 d = grid_get_dot(g, points, px - b, py + a);
1919 grid_face_set_dot(g, d, 4);
1920 }
1921 }
1922 }
1923 }
1924
1925 freetree234(points);
1926 assert(g->num_faces <= max_faces);
1927 assert(g->num_dots <= max_dots);
1928
1929 grid_make_consistent(g);
1930 return g;
1931}
1932
1933#define GREATHEX_TILESIZE 18
1934/* Vector for side of triangle - ratio is close to sqrt(3) */
1935#define GREATHEX_A 15
1936#define GREATHEX_B 26
1937
1938static void grid_size_greathexagonal(int width, int height,
1939 int *tilesize, int *xextent, int *yextent)
1940{
1941 int a = GREATHEX_A;
1942 int b = GREATHEX_B;
1943
1944 *tilesize = GREATHEX_TILESIZE;
1945 *xextent = (3*a + b) * (width-1) + 4*a;
1946 *yextent = (2*a + 2*b) * (height-1) + 3*b + a;
1947}
1948
1949static grid *grid_new_greathexagonal(int width, int height, const char *desc)
1950{
1951 int x, y;
1952 int a = GREATHEX_A;
1953 int b = GREATHEX_B;
1954
1955 /* Upper bounds - don't have to be exact */
1956 int max_faces = 6 * (width + 1) * (height + 1);
1957 int max_dots = 6 * width * height;
1958
1959 tree234 *points;
1960
1961 grid *g = grid_empty();
1962 g->tilesize = GREATHEX_TILESIZE;
1963 g->faces = snewn(max_faces, grid_face);
1964 g->dots = snewn(max_dots, grid_dot);
1965
1966 points = newtree234(grid_point_cmp_fn);
1967
1968 for (y = 0; y < height; y++) {
1969 for (x = 0; x < width; x++) {
1970 grid_dot *d;
1971 /* centre of hexagon */
1972 int px = (3*a + b) * x;
1973 int py = (2*a + 2*b) * y;
1974 if (x % 2)
1975 py += a + b;
1976
1977 /* hexagon */
1978 grid_face_add_new(g, 6);
1979 d = grid_get_dot(g, points, px - a, py - b);
1980 grid_face_set_dot(g, d, 0);
1981 d = grid_get_dot(g, points, px + a, py - b);
1982 grid_face_set_dot(g, d, 1);
1983 d = grid_get_dot(g, points, px + 2*a, py);
1984 grid_face_set_dot(g, d, 2);
1985 d = grid_get_dot(g, points, px + a, py + b);
1986 grid_face_set_dot(g, d, 3);
1987 d = grid_get_dot(g, points, px - a, py + b);
1988 grid_face_set_dot(g, d, 4);
1989 d = grid_get_dot(g, points, px - 2*a, py);
1990 grid_face_set_dot(g, d, 5);
1991
1992 /* square below hexagon */
1993 if (y < height - 1) {
1994 grid_face_add_new(g, 4);
1995 d = grid_get_dot(g, points, px - a, py + b);
1996 grid_face_set_dot(g, d, 0);
1997 d = grid_get_dot(g, points, px + a, py + b);
1998 grid_face_set_dot(g, d, 1);
1999 d = grid_get_dot(g, points, px + a, py + 2*a + b);
2000 grid_face_set_dot(g, d, 2);
2001 d = grid_get_dot(g, points, px - a, py + 2*a + b);
2002 grid_face_set_dot(g, d, 3);
2003 }
2004
2005 /* square below right */
2006 if ((x < width - 1) && (((x % 2) == 0) || (y < height - 1))) {
2007 grid_face_add_new(g, 4);
2008 d = grid_get_dot(g, points, px + 2*a, py);
2009 grid_face_set_dot(g, d, 0);
2010 d = grid_get_dot(g, points, px + 2*a + b, py + a);
2011 grid_face_set_dot(g, d, 1);
2012 d = grid_get_dot(g, points, px + a + b, py + a + b);
2013 grid_face_set_dot(g, d, 2);
2014 d = grid_get_dot(g, points, px + a, py + b);
2015 grid_face_set_dot(g, d, 3);
2016 }
2017
2018 /* square below left */
2019 if ((x > 0) && (((x % 2) == 0) || (y < height - 1))) {
2020 grid_face_add_new(g, 4);
2021 d = grid_get_dot(g, points, px - 2*a, py);
2022 grid_face_set_dot(g, d, 0);
2023 d = grid_get_dot(g, points, px - a, py + b);
2024 grid_face_set_dot(g, d, 1);
2025 d = grid_get_dot(g, points, px - a - b, py + a + b);
2026 grid_face_set_dot(g, d, 2);
2027 d = grid_get_dot(g, points, px - 2*a - b, py + a);
2028 grid_face_set_dot(g, d, 3);
2029 }
2030
2031 /* Triangle below right */
2032 if ((x < width - 1) && (y < height - 1)) {
2033 grid_face_add_new(g, 3);
2034 d = grid_get_dot(g, points, px + a, py + b);
2035 grid_face_set_dot(g, d, 0);
2036 d = grid_get_dot(g, points, px + a + b, py + a + b);
2037 grid_face_set_dot(g, d, 1);
2038 d = grid_get_dot(g, points, px + a, py + 2*a + b);
2039 grid_face_set_dot(g, d, 2);
2040 }
2041
2042 /* Triangle below left */
2043 if ((x > 0) && (y < height - 1)) {
2044 grid_face_add_new(g, 3);
2045 d = grid_get_dot(g, points, px - a, py + b);
2046 grid_face_set_dot(g, d, 0);
2047 d = grid_get_dot(g, points, px - a, py + 2*a + b);
2048 grid_face_set_dot(g, d, 1);
2049 d = grid_get_dot(g, points, px - a - b, py + a + b);
2050 grid_face_set_dot(g, d, 2);
2051 }
2052 }
2053 }
2054
2055 freetree234(points);
2056 assert(g->num_faces <= max_faces);
2057 assert(g->num_dots <= max_dots);
2058
2059 grid_make_consistent(g);
2060 return g;
2061}
2062
2063#define OCTAGONAL_TILESIZE 40
2064/* b/a approx sqrt(2) */
2065#define OCTAGONAL_A 29
2066#define OCTAGONAL_B 41
2067
2068static void grid_size_octagonal(int width, int height,
2069 int *tilesize, int *xextent, int *yextent)
2070{
2071 int a = OCTAGONAL_A;
2072 int b = OCTAGONAL_B;
2073
2074 *tilesize = OCTAGONAL_TILESIZE;
2075 *xextent = (2*a + b) * width;
2076 *yextent = (2*a + b) * height;
2077}
2078
2079static grid *grid_new_octagonal(int width, int height, const char *desc)
2080{
2081 int x, y;
2082 int a = OCTAGONAL_A;
2083 int b = OCTAGONAL_B;
2084
2085 /* Upper bounds - don't have to be exact */
2086 int max_faces = 2 * width * height;
2087 int max_dots = 4 * (width + 1) * (height + 1);
2088
2089 tree234 *points;
2090
2091 grid *g = grid_empty();
2092 g->tilesize = OCTAGONAL_TILESIZE;
2093 g->faces = snewn(max_faces, grid_face);
2094 g->dots = snewn(max_dots, grid_dot);
2095
2096 points = newtree234(grid_point_cmp_fn);
2097
2098 for (y = 0; y < height; y++) {
2099 for (x = 0; x < width; x++) {
2100 grid_dot *d;
2101 /* cell position */
2102 int px = (2*a + b) * x;
2103 int py = (2*a + b) * y;
2104 /* octagon */
2105 grid_face_add_new(g, 8);
2106 d = grid_get_dot(g, points, px + a, py);
2107 grid_face_set_dot(g, d, 0);
2108 d = grid_get_dot(g, points, px + a + b, py);
2109 grid_face_set_dot(g, d, 1);
2110 d = grid_get_dot(g, points, px + 2*a + b, py + a);
2111 grid_face_set_dot(g, d, 2);
2112 d = grid_get_dot(g, points, px + 2*a + b, py + a + b);
2113 grid_face_set_dot(g, d, 3);
2114 d = grid_get_dot(g, points, px + a + b, py + 2*a + b);
2115 grid_face_set_dot(g, d, 4);
2116 d = grid_get_dot(g, points, px + a, py + 2*a + b);
2117 grid_face_set_dot(g, d, 5);
2118 d = grid_get_dot(g, points, px, py + a + b);
2119 grid_face_set_dot(g, d, 6);
2120 d = grid_get_dot(g, points, px, py + a);
2121 grid_face_set_dot(g, d, 7);
2122
2123 /* diamond */
2124 if ((x > 0) && (y > 0)) {
2125 grid_face_add_new(g, 4);
2126 d = grid_get_dot(g, points, px, py - a);
2127 grid_face_set_dot(g, d, 0);
2128 d = grid_get_dot(g, points, px + a, py);
2129 grid_face_set_dot(g, d, 1);
2130 d = grid_get_dot(g, points, px, py + a);
2131 grid_face_set_dot(g, d, 2);
2132 d = grid_get_dot(g, points, px - a, py);
2133 grid_face_set_dot(g, d, 3);
2134 }
2135 }
2136 }
2137
2138 freetree234(points);
2139 assert(g->num_faces <= max_faces);
2140 assert(g->num_dots <= max_dots);
2141
2142 grid_make_consistent(g);
2143 return g;
2144}
2145
2146#define KITE_TILESIZE 40
2147/* b/a approx sqrt(3) */
2148#define KITE_A 15
2149#define KITE_B 26
2150
2151static void grid_size_kites(int width, int height,
2152 int *tilesize, int *xextent, int *yextent)
2153{
2154 int a = KITE_A;
2155 int b = KITE_B;
2156
2157 *tilesize = KITE_TILESIZE;
2158 *xextent = 4*b * width + 2*b;
2159 *yextent = 6*a * (height-1) + 8*a;
2160}
2161
2162static grid *grid_new_kites(int width, int height, const char *desc)
2163{
2164 int x, y;
2165 int a = KITE_A;
2166 int b = KITE_B;
2167
2168 /* Upper bounds - don't have to be exact */
2169 int max_faces = 6 * width * height;
2170 int max_dots = 6 * (width + 1) * (height + 1);
2171
2172 tree234 *points;
2173
2174 grid *g = grid_empty();
2175 g->tilesize = KITE_TILESIZE;
2176 g->faces = snewn(max_faces, grid_face);
2177 g->dots = snewn(max_dots, grid_dot);
2178
2179 points = newtree234(grid_point_cmp_fn);
2180
2181 for (y = 0; y < height; y++) {
2182 for (x = 0; x < width; x++) {
2183 grid_dot *d;
2184 /* position of order-6 dot */
2185 int px = 4*b * x;
2186 int py = 6*a * y;
2187 if (y % 2)
2188 px += 2*b;
2189
2190 /* kite pointing up-left */
2191 grid_face_add_new(g, 4);
2192 d = grid_get_dot(g, points, px, py);
2193 grid_face_set_dot(g, d, 0);
2194 d = grid_get_dot(g, points, px + 2*b, py);
2195 grid_face_set_dot(g, d, 1);
2196 d = grid_get_dot(g, points, px + 2*b, py + 2*a);
2197 grid_face_set_dot(g, d, 2);
2198 d = grid_get_dot(g, points, px + b, py + 3*a);
2199 grid_face_set_dot(g, d, 3);
2200
2201 /* kite pointing up */
2202 grid_face_add_new(g, 4);
2203 d = grid_get_dot(g, points, px, py);
2204 grid_face_set_dot(g, d, 0);
2205 d = grid_get_dot(g, points, px + b, py + 3*a);
2206 grid_face_set_dot(g, d, 1);
2207 d = grid_get_dot(g, points, px, py + 4*a);
2208 grid_face_set_dot(g, d, 2);
2209 d = grid_get_dot(g, points, px - b, py + 3*a);
2210 grid_face_set_dot(g, d, 3);
2211
2212 /* kite pointing up-right */
2213 grid_face_add_new(g, 4);
2214 d = grid_get_dot(g, points, px, py);
2215 grid_face_set_dot(g, d, 0);
2216 d = grid_get_dot(g, points, px - b, py + 3*a);
2217 grid_face_set_dot(g, d, 1);
2218 d = grid_get_dot(g, points, px - 2*b, py + 2*a);
2219 grid_face_set_dot(g, d, 2);
2220 d = grid_get_dot(g, points, px - 2*b, py);
2221 grid_face_set_dot(g, d, 3);
2222
2223 /* kite pointing down-right */
2224 grid_face_add_new(g, 4);
2225 d = grid_get_dot(g, points, px, py);
2226 grid_face_set_dot(g, d, 0);
2227 d = grid_get_dot(g, points, px - 2*b, py);
2228 grid_face_set_dot(g, d, 1);
2229 d = grid_get_dot(g, points, px - 2*b, py - 2*a);
2230 grid_face_set_dot(g, d, 2);
2231 d = grid_get_dot(g, points, px - b, py - 3*a);
2232 grid_face_set_dot(g, d, 3);
2233
2234 /* kite pointing down */
2235 grid_face_add_new(g, 4);
2236 d = grid_get_dot(g, points, px, py);
2237 grid_face_set_dot(g, d, 0);
2238 d = grid_get_dot(g, points, px - b, py - 3*a);
2239 grid_face_set_dot(g, d, 1);
2240 d = grid_get_dot(g, points, px, py - 4*a);
2241 grid_face_set_dot(g, d, 2);
2242 d = grid_get_dot(g, points, px + b, py - 3*a);
2243 grid_face_set_dot(g, d, 3);
2244
2245 /* kite pointing down-left */
2246 grid_face_add_new(g, 4);
2247 d = grid_get_dot(g, points, px, py);
2248 grid_face_set_dot(g, d, 0);
2249 d = grid_get_dot(g, points, px + b, py - 3*a);
2250 grid_face_set_dot(g, d, 1);
2251 d = grid_get_dot(g, points, px + 2*b, py - 2*a);
2252 grid_face_set_dot(g, d, 2);
2253 d = grid_get_dot(g, points, px + 2*b, py);
2254 grid_face_set_dot(g, d, 3);
2255 }
2256 }
2257
2258 freetree234(points);
2259 assert(g->num_faces <= max_faces);
2260 assert(g->num_dots <= max_dots);
2261
2262 grid_make_consistent(g);
2263 return g;
2264}
2265
2266#define FLORET_TILESIZE 150
2267/* -py/px is close to tan(30 - atan(sqrt(3)/9))
2268 * using py=26 makes everything lean to the left, rather than right
2269 */
2270#define FLORET_PX 75
2271#define FLORET_PY -26
2272
2273static void grid_size_floret(int width, int height,
2274 int *tilesize, int *xextent, int *yextent)
2275{
2276 int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */
2277 int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
2278 int ry = qy-py;
2279 /* rx unused in determining grid size. */
2280
2281 *tilesize = FLORET_TILESIZE;
2282 *xextent = (6*px+3*qx)/2 * (width-1) + 4*qx + 2*px;
2283 *yextent = (5*qy-4*py) * (height-1) + 4*qy + 2*ry;
2284}
2285
2286static grid *grid_new_floret(int width, int height, const char *desc)
2287{
2288 int x, y;
2289 /* Vectors for sides; weird numbers needed to keep puzzle aligned with window
2290 * -py/px is close to tan(30 - atan(sqrt(3)/9))
2291 * using py=26 makes everything lean to the left, rather than right
2292 */
2293 int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */
2294 int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */
2295 int rx = qx-px, ry = qy-py; /* |(-15, 78)| = 79.38 */
2296
2297 /* Upper bounds - don't have to be exact */
2298 int max_faces = 6 * width * height;
2299 int max_dots = 9 * (width + 1) * (height + 1);
2300
2301 tree234 *points;
2302
2303 grid *g = grid_empty();
2304 g->tilesize = FLORET_TILESIZE;
2305 g->faces = snewn(max_faces, grid_face);
2306 g->dots = snewn(max_dots, grid_dot);
2307
2308 points = newtree234(grid_point_cmp_fn);
2309
2310 /* generate pentagonal faces */
2311 for (y = 0; y < height; y++) {
2312 for (x = 0; x < width; x++) {
2313 grid_dot *d;
2314 /* face centre */
2315 int cx = (6*px+3*qx)/2 * x;
2316 int cy = (4*py-5*qy) * y;
2317 if (x % 2)
2318 cy -= (4*py-5*qy)/2;
2319 else if (y && y == height-1)
2320 continue; /* make better looking grids? try 3x3 for instance */
2321
2322 grid_face_add_new(g, 5);
2323 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
2324 d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 1);
2325 d = grid_get_dot(g, points, cx+2*rx+qx, cy+2*ry+qy); grid_face_set_dot(g, d, 2);
2326 d = grid_get_dot(g, points, cx+2*qx+rx, cy+2*qy+ry); grid_face_set_dot(g, d, 3);
2327 d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 4);
2328
2329 grid_face_add_new(g, 5);
2330 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
2331 d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 1);
2332 d = grid_get_dot(g, points, cx+2*qx+px, cy+2*qy+py); grid_face_set_dot(g, d, 2);
2333 d = grid_get_dot(g, points, cx+2*px+qx, cy+2*py+qy); grid_face_set_dot(g, d, 3);
2334 d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 4);
2335
2336 grid_face_add_new(g, 5);
2337 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
2338 d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 1);
2339 d = grid_get_dot(g, points, cx+2*px-rx, cy+2*py-ry); grid_face_set_dot(g, d, 2);
2340 d = grid_get_dot(g, points, cx-2*rx+px, cy-2*ry+py); grid_face_set_dot(g, d, 3);
2341 d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 4);
2342
2343 grid_face_add_new(g, 5);
2344 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
2345 d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 1);
2346 d = grid_get_dot(g, points, cx-2*rx-qx, cy-2*ry-qy); grid_face_set_dot(g, d, 2);
2347 d = grid_get_dot(g, points, cx-2*qx-rx, cy-2*qy-ry); grid_face_set_dot(g, d, 3);
2348 d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 4);
2349
2350 grid_face_add_new(g, 5);
2351 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
2352 d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 1);
2353 d = grid_get_dot(g, points, cx-2*qx-px, cy-2*qy-py); grid_face_set_dot(g, d, 2);
2354 d = grid_get_dot(g, points, cx-2*px-qx, cy-2*py-qy); grid_face_set_dot(g, d, 3);
2355 d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 4);
2356
2357 grid_face_add_new(g, 5);
2358 d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0);
2359 d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 1);
2360 d = grid_get_dot(g, points, cx-2*px+rx, cy-2*py+ry); grid_face_set_dot(g, d, 2);
2361 d = grid_get_dot(g, points, cx+2*rx-px, cy+2*ry-py); grid_face_set_dot(g, d, 3);
2362 d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 4);
2363 }
2364 }
2365
2366 freetree234(points);
2367 assert(g->num_faces <= max_faces);
2368 assert(g->num_dots <= max_dots);
2369
2370 grid_make_consistent(g);
2371 return g;
2372}
2373
2374/* DODEC_* are used for dodecagonal and great-dodecagonal grids. */
2375#define DODEC_TILESIZE 26
2376/* Vector for side of triangle - ratio is close to sqrt(3) */
2377#define DODEC_A 15
2378#define DODEC_B 26
2379
2380static void grid_size_dodecagonal(int width, int height,
2381 int *tilesize, int *xextent, int *yextent)
2382{
2383 int a = DODEC_A;
2384 int b = DODEC_B;
2385
2386 *tilesize = DODEC_TILESIZE;
2387 *xextent = (4*a + 2*b) * (width-1) + 3*(2*a + b);
2388 *yextent = (3*a + 2*b) * (height-1) + 2*(2*a + b);
2389}
2390
2391static grid *grid_new_dodecagonal(int width, int height, const char *desc)
2392{
2393 int x, y;
2394 int a = DODEC_A;
2395 int b = DODEC_B;
2396
2397 /* Upper bounds - don't have to be exact */
2398 int max_faces = 3 * width * height;
2399 int max_dots = 14 * width * height;
2400
2401 tree234 *points;
2402
2403 grid *g = grid_empty();
2404 g->tilesize = DODEC_TILESIZE;
2405 g->faces = snewn(max_faces, grid_face);
2406 g->dots = snewn(max_dots, grid_dot);
2407
2408 points = newtree234(grid_point_cmp_fn);
2409
2410 for (y = 0; y < height; y++) {
2411 for (x = 0; x < width; x++) {
2412 grid_dot *d;
2413 /* centre of dodecagon */
2414 int px = (4*a + 2*b) * x;
2415 int py = (3*a + 2*b) * y;
2416 if (y % 2)
2417 px += 2*a + b;
2418
2419 /* dodecagon */
2420 grid_face_add_new(g, 12);
2421 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
2422 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
2423 d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
2424 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
2425 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
2426 d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
2427 d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
2428 d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
2429 d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
2430 d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
2431 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
2432 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
2433
2434 /* triangle below dodecagon */
2435 if ((y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) {
2436 grid_face_add_new(g, 3);
2437 d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0);
2438 d = grid_get_dot(g, points, px , py + (2*a + 2*b)); grid_face_set_dot(g, d, 1);
2439 d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 2);
2440 }
2441
2442 /* triangle above dodecagon */
2443 if ((y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) {
2444 grid_face_add_new(g, 3);
2445 d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0);
2446 d = grid_get_dot(g, points, px , py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
2447 d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 2);
2448 }
2449 }
2450 }
2451
2452 freetree234(points);
2453 assert(g->num_faces <= max_faces);
2454 assert(g->num_dots <= max_dots);
2455
2456 grid_make_consistent(g);
2457 return g;
2458}
2459
2460static void grid_size_greatdodecagonal(int width, int height,
2461 int *tilesize, int *xextent, int *yextent)
2462{
2463 int a = DODEC_A;
2464 int b = DODEC_B;
2465
2466 *tilesize = DODEC_TILESIZE;
2467 *xextent = (6*a + 2*b) * (width-1) + 2*(2*a + b) + 3*a + b;
2468 *yextent = (3*a + 3*b) * (height-1) + 2*(2*a + b);
2469}
2470
2471static grid *grid_new_greatdodecagonal(int width, int height, const char *desc)
2472{
2473 int x, y;
2474 /* Vector for side of triangle - ratio is close to sqrt(3) */
2475 int a = DODEC_A;
2476 int b = DODEC_B;
2477
2478 /* Upper bounds - don't have to be exact */
2479 int max_faces = 30 * width * height;
2480 int max_dots = 200 * width * height;
2481
2482 tree234 *points;
2483
2484 grid *g = grid_empty();
2485 g->tilesize = DODEC_TILESIZE;
2486 g->faces = snewn(max_faces, grid_face);
2487 g->dots = snewn(max_dots, grid_dot);
2488
2489 points = newtree234(grid_point_cmp_fn);
2490
2491 for (y = 0; y < height; y++) {
2492 for (x = 0; x < width; x++) {
2493 grid_dot *d;
2494 /* centre of dodecagon */
2495 int px = (6*a + 2*b) * x;
2496 int py = (3*a + 3*b) * y;
2497 if (y % 2)
2498 px += 3*a + b;
2499
2500 /* dodecagon */
2501 grid_face_add_new(g, 12);
2502 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
2503 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
2504 d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
2505 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
2506 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
2507 d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
2508 d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
2509 d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
2510 d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
2511 d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
2512 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
2513 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
2514
2515 /* hexagon below dodecagon */
2516 if (y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
2517 grid_face_add_new(g, 6);
2518 d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0);
2519 d = grid_get_dot(g, points, px + 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 1);
2520 d = grid_get_dot(g, points, px + a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 2);
2521 d = grid_get_dot(g, points, px - a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 3);
2522 d = grid_get_dot(g, points, px - 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 4);
2523 d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 5);
2524 }
2525
2526 /* hexagon above dodecagon */
2527 if (y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
2528 grid_face_add_new(g, 6);
2529 d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0);
2530 d = grid_get_dot(g, points, px - 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
2531 d = grid_get_dot(g, points, px - a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 2);
2532 d = grid_get_dot(g, points, px + a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 3);
2533 d = grid_get_dot(g, points, px + 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 4);
2534 d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 5);
2535 }
2536
2537 /* square on right of dodecagon */
2538 if (x < width - 1) {
2539 grid_face_add_new(g, 4);
2540 d = grid_get_dot(g, points, px + 2*a + b, py - a); grid_face_set_dot(g, d, 0);
2541 d = grid_get_dot(g, points, px + 4*a + b, py - a); grid_face_set_dot(g, d, 1);
2542 d = grid_get_dot(g, points, px + 4*a + b, py + a); grid_face_set_dot(g, d, 2);
2543 d = grid_get_dot(g, points, px + 2*a + b, py + a); grid_face_set_dot(g, d, 3);
2544 }
2545
2546 /* square on top right of dodecagon */
2547 if (y && (x < width - 1 || !(y % 2))) {
2548 grid_face_add_new(g, 4);
2549 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
2550 d = grid_get_dot(g, points, px + (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 1);
2551 d = grid_get_dot(g, points, px + (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 2);
2552 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 3);
2553 }
2554
2555 /* square on top left of dodecagon */
2556 if (y && (x || (y % 2))) {
2557 grid_face_add_new(g, 4);
2558 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 0);
2559 d = grid_get_dot(g, points, px - (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 1);
2560 d = grid_get_dot(g, points, px - (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 2);
2561 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 3);
2562 }
2563 }
2564 }
2565
2566 freetree234(points);
2567 assert(g->num_faces <= max_faces);
2568 assert(g->num_dots <= max_dots);
2569
2570 grid_make_consistent(g);
2571 return g;
2572}
2573
2574static void grid_size_greatgreatdodecagonal(int width, int height,
2575 int *tilesize, int *xextent, int *yextent)
2576{
2577 int a = DODEC_A;
2578 int b = DODEC_B;
2579
2580 *tilesize = DODEC_TILESIZE;
2581 *xextent = (4*a + 4*b) * (width-1) + 2*(2*a + b) + 2*a + 2*b;
2582 *yextent = (6*a + 2*b) * (height-1) + 2*(2*a + b);
2583}
2584
2585static grid *grid_new_greatgreatdodecagonal(int width, int height, const char *desc)
2586{
2587 int x, y;
2588 /* Vector for side of triangle - ratio is close to sqrt(3) */
2589 int a = DODEC_A;
2590 int b = DODEC_B;
2591
2592 /* Upper bounds - don't have to be exact */
2593 int max_faces = 50 * width * height;
2594 int max_dots = 300 * width * height;
2595
2596 tree234 *points;
2597
2598 grid *g = grid_empty();
2599 g->tilesize = DODEC_TILESIZE;
2600 g->faces = snewn(max_faces, grid_face);
2601 g->dots = snewn(max_dots, grid_dot);
2602
2603 points = newtree234(grid_point_cmp_fn);
2604
2605 for (y = 0; y < height; y++) {
2606 for (x = 0; x < width; x++) {
2607 grid_dot *d;
2608 /* centre of dodecagon */
2609 int px = (4*a + 4*b) * x;
2610 int py = (6*a + 2*b) * y;
2611 if (y % 2)
2612 px += 2*a + 2*b;
2613
2614 /* dodecagon */
2615 grid_face_add_new(g, 12);
2616 d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0);
2617 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1);
2618 d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
2619 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
2620 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4);
2621 d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5);
2622 d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6);
2623 d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7);
2624 d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8);
2625 d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9);
2626 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10);
2627 d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11);
2628
2629 /* hexagon on top right of dodecagon */
2630 if (y && (x < width - 1 || !(y % 2))) {
2631 grid_face_add_new(g, 6);
2632 d = grid_get_dot(g, points, px + (a + 2*b), py - (4*a + b)); grid_face_set_dot(g, d, 0);
2633 d = grid_get_dot(g, points, px + (a + 2*b), py - (2*a + b)); grid_face_set_dot(g, d, 1);
2634 d = grid_get_dot(g, points, px + (a + b), py - ( a + b)); grid_face_set_dot(g, d, 2);
2635 d = grid_get_dot(g, points, px + (a ), py - (2*a + b)); grid_face_set_dot(g, d, 3);
2636 d = grid_get_dot(g, points, px + (a ), py - (4*a + b)); grid_face_set_dot(g, d, 4);
2637 d = grid_get_dot(g, points, px + (a + b), py - (5*a + b)); grid_face_set_dot(g, d, 5);
2638 }
2639
2640 /* hexagon on right of dodecagon*/
2641 if (x < width - 1) {
2642 grid_face_add_new(g, 6);
2643 d = grid_get_dot(g, points, px + (2*a + 3*b), py - a); grid_face_set_dot(g, d, 0);
2644 d = grid_get_dot(g, points, px + (2*a + 3*b), py + a); grid_face_set_dot(g, d, 1);
2645 d = grid_get_dot(g, points, px + (2*a + 2*b), py + 2*a); grid_face_set_dot(g, d, 2);
2646 d = grid_get_dot(g, points, px + (2*a + b), py + a); grid_face_set_dot(g, d, 3);
2647 d = grid_get_dot(g, points, px + (2*a + b), py - a); grid_face_set_dot(g, d, 4);
2648 d = grid_get_dot(g, points, px + (2*a + 2*b), py - 2*a); grid_face_set_dot(g, d, 5);
2649 }
2650
2651 /* hexagon on bottom right of dodecagon */
2652 if ((y < height - 1) && (x < width - 1 || !(y % 2))) {
2653 grid_face_add_new(g, 6);
2654 d = grid_get_dot(g, points, px + (a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 0);
2655 d = grid_get_dot(g, points, px + (a + 2*b), py + (4*a + b)); grid_face_set_dot(g, d, 1);
2656 d = grid_get_dot(g, points, px + (a + b), py + (5*a + b)); grid_face_set_dot(g, d, 2);
2657 d = grid_get_dot(g, points, px + (a ), py + (4*a + b)); grid_face_set_dot(g, d, 3);
2658 d = grid_get_dot(g, points, px + (a ), py + (2*a + b)); grid_face_set_dot(g, d, 4);
2659 d = grid_get_dot(g, points, px + (a + b), py + ( a + b)); grid_face_set_dot(g, d, 5);
2660 }
2661
2662 /* square on top right of dodecagon */
2663 if (y && (x < width - 1 )) {
2664 grid_face_add_new(g, 4);
2665 d = grid_get_dot(g, points, px + ( a + 2*b), py - (2*a + b)); grid_face_set_dot(g, d, 0);
2666 d = grid_get_dot(g, points, px + (2*a + 2*b), py - (2*a )); grid_face_set_dot(g, d, 1);
2667 d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2);
2668 d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 3);
2669 }
2670
2671 /* square on bottom right of dodecagon */
2672 if ((y < height - 1) && (x < width - 1 )) {
2673 grid_face_add_new(g, 4);
2674 d = grid_get_dot(g, points, px + (2*a + 2*b), py + (2*a )); grid_face_set_dot(g, d, 0);
2675 d = grid_get_dot(g, points, px + ( a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 1);
2676 d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 2);
2677 d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3);
2678 }
2679
2680 /* square below dodecagon */
2681 if ((y < height - 1) && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
2682 grid_face_add_new(g, 4);
2683 d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0);
2684 d = grid_get_dot(g, points, px + a, py + (4*a + b)); grid_face_set_dot(g, d, 1);
2685 d = grid_get_dot(g, points, px - a, py + (4*a + b)); grid_face_set_dot(g, d, 2);
2686 d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 3);
2687 }
2688
2689 /* square on bottom left of dodecagon */
2690 if (x && (y < height - 1)) {
2691 grid_face_add_new(g, 4);
2692 d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 0);
2693 d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 1);
2694 d = grid_get_dot(g, points, px - ( a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 2);
2695 d = grid_get_dot(g, points, px - (2*a + 2*b), py + (2*a )); grid_face_set_dot(g, d, 3);
2696 }
2697
2698 /* square on top left of dodecagon */
2699 if (x && y) {
2700 grid_face_add_new(g, 4);
2701 d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 0);
2702 d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 1);
2703 d = grid_get_dot(g, points, px - (2*a + 2*b), py - (2*a )); grid_face_set_dot(g, d, 2);
2704 d = grid_get_dot(g, points, px - ( a + 2*b), py - (2*a + b)); grid_face_set_dot(g, d, 3);
2705
2706 }
2707
2708 /* square above dodecagon */
2709 if (y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) {
2710 grid_face_add_new(g, 4);
2711 d = grid_get_dot(g, points, px + a, py - (4*a + b)); grid_face_set_dot(g, d, 0);
2712 d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 1);
2713 d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 2);
2714 d = grid_get_dot(g, points, px - a, py - (4*a + b)); grid_face_set_dot(g, d, 3);
2715 }
2716
2717 /* upper triangle (v) */
2718 if (y && (x < width - 1)) {
2719 grid_face_add_new(g, 3);
2720 d = grid_get_dot(g, points, px + (3*a + 2*b), py - (2*a + b)); grid_face_set_dot(g, d, 0);
2721 d = grid_get_dot(g, points, px + (2*a + 2*b), py - (2*a )); grid_face_set_dot(g, d, 1);
2722 d = grid_get_dot(g, points, px + ( a + 2*b), py - (2*a + b)); grid_face_set_dot(g, d, 2);
2723 }
2724
2725 /* lower triangle (^) */
2726 if ((y < height - 1) && (x < width - 1)) {
2727 grid_face_add_new(g, 3);
2728 d = grid_get_dot(g, points, px + (3*a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 0);
2729 d = grid_get_dot(g, points, px + ( a + 2*b), py + (2*a + b)); grid_face_set_dot(g, d, 1);
2730 d = grid_get_dot(g, points, px + (2*a + 2*b), py + (2*a )); grid_face_set_dot(g, d, 2);
2731 }
2732 }
2733 }
2734
2735 freetree234(points);
2736 assert(g->num_faces <= max_faces);
2737 assert(g->num_dots <= max_dots);
2738
2739 grid_make_consistent(g);
2740 return g;
2741}
2742
2743typedef struct setface_ctx
2744{
2745 int xmin, xmax, ymin, ymax;
2746
2747 grid *g;
2748 tree234 *points;
2749} setface_ctx;
2750
2751static double round_int_nearest_away(double r)
2752{
2753 return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
2754}
2755
2756static int set_faces(penrose_state *state, vector *vs, int n, int depth)
2757{
2758 setface_ctx *sf_ctx = (setface_ctx *)state->ctx;
2759 int i;
2760 int xs[4], ys[4];
2761
2762 if (depth < state->max_depth) return 0;
2763#ifdef DEBUG_PENROSE
2764 if (n != 4) return 0; /* triangles are sent as debugging. */
2765#endif
2766
2767 for (i = 0; i < n; i++) {
2768 double tx = v_x(vs, i), ty = v_y(vs, i);
2769
2770 xs[i] = (int)round_int_nearest_away(tx);
2771 ys[i] = (int)round_int_nearest_away(ty);
2772
2773 if (xs[i] < sf_ctx->xmin || xs[i] > sf_ctx->xmax) return 0;
2774 if (ys[i] < sf_ctx->ymin || ys[i] > sf_ctx->ymax) return 0;
2775 }
2776
2777 grid_face_add_new(sf_ctx->g, n);
2778 debug(("penrose: new face l=%f gen=%d...",
2779 penrose_side_length(state->start_size, depth), depth));
2780 for (i = 0; i < n; i++) {
2781 grid_dot *d = grid_get_dot(sf_ctx->g, sf_ctx->points,
2782 xs[i], ys[i]);
2783 grid_face_set_dot(sf_ctx->g, d, i);
2784 debug((" ... dot 0x%x (%d,%d) (was %2.2f,%2.2f)",
2785 d, d->x, d->y, v_x(vs, i), v_y(vs, i)));
2786 }
2787
2788 return 0;
2789}
2790
2791#define PENROSE_TILESIZE 100
2792
2793static void grid_size_penrose(int width, int height,
2794 int *tilesize, int *xextent, int *yextent)
2795{
2796 int l = PENROSE_TILESIZE;
2797
2798 *tilesize = l;
2799 *xextent = l * width;
2800 *yextent = l * height;
2801}
2802
2803static grid *grid_new_penrose(int width, int height, int which, const char *desc); /* forward reference */
2804
2805static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs)
2806{
2807 int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff;
2808 double outer_radius;
2809 int inner_radius;
2810 char gd[255];
2811 int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
2812 grid *g;
2813
2814 while (1) {
2815 /* We want to produce a random bit of penrose tiling, so we
2816 * calculate a random offset (within the patch that penrose.c
2817 * calculates for us) and an angle (multiple of 36) to rotate
2818 * the patch. */
2819
2820 penrose_calculate_size(which, tilesize, width, height,
2821 &outer_radius, &startsz, &depth);
2822
2823 /* Calculate radius of (circumcircle of) patch, subtract from
2824 * radius calculated. */
2825 inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
2826
2827 /* Pick a random offset (the easy way: choose within outer
2828 * square, discarding while it's outside the circle) */
2829 do {
2830 xoff = random_upto(rs, 2*inner_radius) - inner_radius;
2831 yoff = random_upto(rs, 2*inner_radius) - inner_radius;
2832 } while (sqrt(xoff*xoff+yoff*yoff) > inner_radius);
2833
2834 aoff = random_upto(rs, 360/36) * 36;
2835
2836 debug(("grid_desc: ts %d, %dx%d patch, orad %2.2f irad %d",
2837 tilesize, width, height, outer_radius, inner_radius));
2838 debug((" -> xoff %d yoff %d aoff %d", xoff, yoff, aoff));
2839
2840 sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff);
2841
2842 /*
2843 * Now test-generate our grid, to make sure it actually
2844 * produces something.
2845 */
2846 g = grid_new_penrose(width, height, which, gd);
2847 if (g) {
2848 grid_free(g);
2849 break;
2850 }
2851 /* If not, go back to the top of this while loop and try again
2852 * with a different random offset. */
2853 }
2854
2855 return dupstr(gd);
2856}
2857
2858static char *grid_validate_desc_penrose(grid_type type, int width, int height,
2859 const char *desc)
2860{
2861 int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius;
2862 double outer_radius;
2863 int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
2864 grid *g;
2865
2866 if (!desc)
2867 return "Missing grid description string.";
2868
2869 penrose_calculate_size(which, tilesize, width, height,
2870 &outer_radius, &startsz, &depth);
2871 inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
2872
2873 if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3)
2874 return "Invalid format grid description string.";
2875
2876 if (sqrt(xoff*xoff + yoff*yoff) > inner_radius)
2877 return "Patch offset out of bounds.";
2878 if ((aoff % 36) != 0 || aoff < 0 || aoff >= 360)
2879 return "Angle offset out of bounds.";
2880
2881 /*
2882 * Test-generate to ensure these parameters don't end us up with
2883 * no grid at all.
2884 */
2885 g = grid_new_penrose(width, height, which, desc);
2886 if (!g)
2887 return "Patch coordinates do not identify a usable grid fragment";
2888 grid_free(g);
2889
2890 return NULL;
2891}
2892
2893/*
2894 * We're asked for a grid of a particular size, and we generate enough
2895 * of the tiling so we can be sure to have enough random grid from which
2896 * to pick.
2897 */
2898
2899static grid *grid_new_penrose(int width, int height, int which, const char *desc)
2900{
2901 int max_faces, max_dots, tilesize = PENROSE_TILESIZE;
2902 int xsz, ysz, xoff, yoff, aoff;
2903 double rradius;
2904
2905 tree234 *points;
2906 grid *g;
2907
2908 penrose_state ps;
2909 setface_ctx sf_ctx;
2910
2911 penrose_calculate_size(which, tilesize, width, height,
2912 &rradius, &ps.start_size, &ps.max_depth);
2913
2914 debug(("penrose: w%d h%d, tile size %d, start size %d, depth %d",
2915 width, height, tilesize, ps.start_size, ps.max_depth));
2916
2917 ps.new_tile = set_faces;
2918 ps.ctx = &sf_ctx;
2919
2920 max_faces = (width*3) * (height*3); /* somewhat paranoid... */
2921 max_dots = max_faces * 4; /* ditto... */
2922
2923 g = grid_empty();
2924 g->tilesize = tilesize;
2925 g->faces = snewn(max_faces, grid_face);
2926 g->dots = snewn(max_dots, grid_dot);
2927
2928 points = newtree234(grid_point_cmp_fn);
2929
2930 memset(&sf_ctx, 0, sizeof(sf_ctx));
2931 sf_ctx.g = g;
2932 sf_ctx.points = points;
2933
2934 if (desc != NULL) {
2935 if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3)
2936 assert(!"Invalid grid description.");
2937 } else {
2938 xoff = yoff = aoff = 0;
2939 }
2940
2941 xsz = width * tilesize;
2942 ysz = height * tilesize;
2943
2944 sf_ctx.xmin = xoff - xsz/2;
2945 sf_ctx.xmax = xoff + xsz/2;
2946 sf_ctx.ymin = yoff - ysz/2;
2947 sf_ctx.ymax = yoff + ysz/2;
2948
2949 debug(("penrose: centre (%f, %f) xsz %f ysz %f",
2950 0.0, 0.0, xsz, ysz));
2951 debug(("penrose: x range (%f --> %f), y range (%f --> %f)",
2952 sf_ctx.xmin, sf_ctx.xmax, sf_ctx.ymin, sf_ctx.ymax));
2953
2954 penrose(&ps, which, aoff);
2955
2956 freetree234(points);
2957 assert(g->num_faces <= max_faces);
2958 assert(g->num_dots <= max_dots);
2959
2960 debug(("penrose: %d faces total (equivalent to %d wide by %d high)",
2961 g->num_faces, g->num_faces/height, g->num_faces/width));
2962
2963 /*
2964 * Return NULL if we ended up with an empty grid, either because
2965 * the initial generation was over too small a rectangle to
2966 * encompass any face or because grid_trim_vigorously ended up
2967 * removing absolutely everything.
2968 */
2969 if (g->num_faces == 0 || g->num_dots == 0) {
2970 grid_free(g);
2971 return NULL;
2972 }
2973 grid_trim_vigorously(g);
2974 if (g->num_faces == 0 || g->num_dots == 0) {
2975 grid_free(g);
2976 return NULL;
2977 }
2978
2979 grid_make_consistent(g);
2980
2981 /*
2982 * Centre the grid in its originally promised rectangle.
2983 */
2984 g->lowest_x -= ((sf_ctx.xmax - sf_ctx.xmin) -
2985 (g->highest_x - g->lowest_x)) / 2;
2986 g->highest_x = g->lowest_x + (sf_ctx.xmax - sf_ctx.xmin);
2987 g->lowest_y -= ((sf_ctx.ymax - sf_ctx.ymin) -
2988 (g->highest_y - g->lowest_y)) / 2;
2989 g->highest_y = g->lowest_y + (sf_ctx.ymax - sf_ctx.ymin);
2990
2991 return g;
2992}
2993
2994static void grid_size_penrose_p2_kite(int width, int height,
2995 int *tilesize, int *xextent, int *yextent)
2996{
2997 grid_size_penrose(width, height, tilesize, xextent, yextent);
2998}
2999
3000static void grid_size_penrose_p3_thick(int width, int height,
3001 int *tilesize, int *xextent, int *yextent)
3002{
3003 grid_size_penrose(width, height, tilesize, xextent, yextent);
3004}
3005
3006static grid *grid_new_penrose_p2_kite(int width, int height, const char *desc)
3007{
3008 return grid_new_penrose(width, height, PENROSE_P2, desc);
3009}
3010
3011static grid *grid_new_penrose_p3_thick(int width, int height, const char *desc)
3012{
3013 return grid_new_penrose(width, height, PENROSE_P3, desc);
3014}
3015
3016/* ----------- End of grid generators ------------- */
3017
3018#define FNNEW(upper,lower) &grid_new_ ## lower,
3019#define FNSZ(upper,lower) &grid_size_ ## lower,
3020
3021static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) };
3022static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) };
3023
3024char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
3025{
3026 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
3027 return grid_new_desc_penrose(type, width, height, rs);
3028 } else if (type == GRID_TRIANGULAR) {
3029 return dupstr("0"); /* up-to-date version of triangular grid */
3030 } else {
3031 return NULL;
3032 }
3033}
3034
3035char *grid_validate_desc(grid_type type, int width, int height,
3036 const char *desc)
3037{
3038 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
3039 return grid_validate_desc_penrose(type, width, height, desc);
3040 } else if (type == GRID_TRIANGULAR) {
3041 return grid_validate_desc_triangular(type, width, height, desc);
3042 } else {
3043 if (desc != NULL)
3044 return "Grid description strings not used with this grid type";
3045 return NULL;
3046 }
3047}
3048
3049grid *grid_new(grid_type type, int width, int height, const char *desc)
3050{
3051 char *err = grid_validate_desc(type, width, height, desc);
3052 if (err) assert(!"Invalid grid description.");
3053
3054 return grid_news[type](width, height, desc);
3055}
3056
3057void grid_compute_size(grid_type type, int width, int height,
3058 int *tilesize, int *xextent, int *yextent)
3059{
3060 grid_sizes[type](width, height, tilesize, xextent, yextent);
3061}
3062
3063/* ----------- End of grid helpers ------------- */
3064
3065/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/grid.h b/apps/plugins/puzzles/src/grid.h
new file mode 100644
index 0000000000..fb8ac48790
--- /dev/null
+++ b/apps/plugins/puzzles/src/grid.h
@@ -0,0 +1,133 @@
1/*
2 * (c) Lambros Lambrou 2008
3 *
4 * Code for working with general grids, which can be any planar graph
5 * with faces, edges and vertices (dots). Includes generators for a few
6 * types of grid, including square, hexagonal, triangular and others.
7 */
8
9#ifndef PUZZLES_GRID_H
10#define PUZZLES_GRID_H
11
12#include "puzzles.h" /* for random_state */
13
14/* Useful macros */
15#define SQ(x) ( (x) * (x) )
16
17/* ----------------------------------------------------------------------
18 * Grid structures:
19 * A grid is made up of faces, edges and dots. These structures hold
20 * the incidence relationships between these types. For example, an
21 * edge always joins two dots, and is adjacent to two faces.
22 * The "grid_xxx **" members are lists of pointers which are dynamically
23 * allocated during grid generation.
24 * A pointer to a face/edge/dot will always point somewhere inside one of the
25 * three lists of the main "grid" structure: faces, edges, dots.
26 * Could have used integer offsets into these lists, but using actual
27 * pointers instead gives us type-safety.
28 */
29
30/* Need forward declarations */
31typedef struct grid_face grid_face;
32typedef struct grid_edge grid_edge;
33typedef struct grid_dot grid_dot;
34
35struct grid_face {
36 int order; /* Number of edges, also the number of dots */
37 grid_edge **edges; /* edges around this face */
38 grid_dot **dots; /* corners of this face */
39 /*
40 * For each face, we optionally compute and store its 'incentre'.
41 * The incentre of a triangle is the centre of a circle tangent to
42 * all three edges; I generalise the concept to arbitrary polygons
43 * by defining it to be the centre of the largest circle you can fit
44 * anywhere in the polygon. It's a useful thing to know because if
45 * you want to draw any symbol or text in the face (e.g. clue
46 * numbers in Loopy), that's the place it will most easily fit.
47 *
48 * When a grid is first generated, no face has this information
49 * computed, because it's fiddly to do. You can call
50 * grid_find_incentre() on a face, and it will fill in ix,iy below
51 * and set has_incentre to indicate that it's done so.
52 */
53 int has_incentre;
54 int ix, iy; /* incentre (centre of largest inscribed circle) */
55};
56struct grid_edge {
57 grid_dot *dot1, *dot2;
58 grid_face *face1, *face2; /* Use NULL for the infinite outside face */
59};
60struct grid_dot {
61 int order;
62 grid_edge **edges;
63 grid_face **faces; /* A NULL grid_face* means infinite outside face */
64
65 /* Position in some fairly arbitrary (Cartesian) coordinate system.
66 * Use large enough values such that we can get away with
67 * integer arithmetic, but small enough such that arithmetic
68 * won't overflow. */
69 int x, y;
70};
71typedef struct grid {
72 /* These are (dynamically allocated) arrays of all the
73 * faces, edges, dots that are in the grid. */
74 int num_faces; grid_face *faces;
75 int num_edges; grid_edge *edges;
76 int num_dots; grid_dot *dots;
77
78 /* Cache the bounding-box of the grid, so the drawing-code can quickly
79 * figure out the proper scaling to draw onto a given area. */
80 int lowest_x, lowest_y, highest_x, highest_y;
81
82 /* A measure of tile size for this grid (in grid coordinates), to help
83 * the renderer decide how large to draw the grid.
84 * Roughly the size of a single tile - for example the side-length
85 * of a square cell. */
86 int tilesize;
87
88 /* We really don't want to copy this monstrosity!
89 * A grid is immutable once generated.
90 */
91 int refcount;
92} grid;
93
94/* Grids are specified by type: GRID_SQUARE, GRID_KITE, etc. */
95
96#define GRIDGEN_LIST(A) \
97 A(SQUARE,square) \
98 A(HONEYCOMB,honeycomb) \
99 A(TRIANGULAR,triangular) \
100 A(SNUBSQUARE,snubsquare) \
101 A(CAIRO,cairo) \
102 A(GREATHEXAGONAL,greathexagonal) \
103 A(OCTAGONAL,octagonal) \
104 A(KITE,kites) \
105 A(FLORET,floret) \
106 A(DODECAGONAL,dodecagonal) \
107 A(GREATDODECAGONAL,greatdodecagonal) \
108 A(GREATGREATDODECAGONAL,greatgreatdodecagonal) \
109 A(PENROSE_P2,penrose_p2_kite) \
110 A(PENROSE_P3,penrose_p3_thick)
111
112#define ENUM(upper,lower) GRID_ ## upper,
113typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type;
114#undef ENUM
115
116/* Free directly after use if non-NULL. Will never contain an underscore
117 * (so clients can safely use that as a separator). */
118char *grid_new_desc(grid_type type, int width, int height, random_state *rs);
119char *grid_validate_desc(grid_type type, int width, int height,
120 const char *desc);
121
122grid *grid_new(grid_type type, int width, int height, const char *desc);
123
124void grid_free(grid *g);
125
126grid_edge *grid_nearest_edge(grid *g, int x, int y);
127
128void grid_compute_size(grid_type type, int width, int height,
129 int *tilesize, int *xextent, int *yextent);
130
131void grid_find_incentre(grid_face *f);
132
133#endif /* PUZZLES_GRID_H */
diff --git a/apps/plugins/puzzles/src/gtk.c b/apps/plugins/puzzles/src/gtk.c
new file mode 100644
index 0000000000..c5e3d1c997
--- /dev/null
+++ b/apps/plugins/puzzles/src/gtk.c
@@ -0,0 +1,3311 @@
1/*
2 * gtk.c: GTK front end for my puzzle collection.
3 */
4
5#include <stdio.h>
6#include <assert.h>
7#include <stdlib.h>
8#include <time.h>
9#include <stdarg.h>
10#include <string.h>
11#include <errno.h>
12#include <math.h>
13
14#include <sys/time.h>
15#include <sys/resource.h>
16
17#include <gtk/gtk.h>
18#include <gdk/gdkkeysyms.h>
19
20#include <gdk-pixbuf/gdk-pixbuf.h>
21
22#include <gdk/gdkx.h>
23#include <X11/Xlib.h>
24#include <X11/Xutil.h>
25#include <X11/Xatom.h>
26
27#include "puzzles.h"
28
29#if GTK_CHECK_VERSION(2,0,0)
30# define USE_PANGO
31# ifdef PANGO_VERSION_CHECK
32# if PANGO_VERSION_CHECK(1,8,0)
33# define HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
34# endif
35# endif
36#endif
37#if !GTK_CHECK_VERSION(2,4,0)
38# define OLD_FILESEL
39#endif
40#if GTK_CHECK_VERSION(2,8,0)
41# define USE_CAIRO
42# if GTK_CHECK_VERSION(3,0,0) || defined(GDK_DISABLE_DEPRECATED)
43# define USE_CAIRO_WITHOUT_PIXMAP
44# endif
45#endif
46
47#if GTK_CHECK_VERSION(3,0,0)
48/* The old names are still more concise! */
49#define gtk_hbox_new(x,y) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,y)
50#define gtk_vbox_new(x,y) gtk_box_new(GTK_ORIENTATION_VERTICAL,y)
51/* GTK 3 has retired stock button labels */
52#define LABEL_OK "_OK"
53#define LABEL_CANCEL "_Cancel"
54#define LABEL_NO "_No"
55#define LABEL_YES "_Yes"
56#define LABEL_SAVE "_Save"
57#define LABEL_OPEN "_Open"
58#define gtk_button_new_with_our_label gtk_button_new_with_mnemonic
59#else
60#define LABEL_OK GTK_STOCK_OK
61#define LABEL_CANCEL GTK_STOCK_CANCEL
62#define LABEL_NO GTK_STOCK_NO
63#define LABEL_YES GTK_STOCK_YES
64#define LABEL_SAVE GTK_STOCK_SAVE
65#define LABEL_OPEN GTK_STOCK_OPEN
66#define gtk_button_new_with_our_label gtk_button_new_from_stock
67#endif
68
69/* #undef USE_CAIRO */
70/* #define NO_THICK_LINE */
71#ifdef DEBUGGING
72static FILE *debug_fp = NULL;
73
74void dputs(char *buf)
75{
76 if (!debug_fp) {
77 debug_fp = fopen("debug.log", "w");
78 }
79
80 fputs(buf, stderr);
81
82 if (debug_fp) {
83 fputs(buf, debug_fp);
84 fflush(debug_fp);
85 }
86}
87
88void debug_printf(char *fmt, ...)
89{
90 char buf[4096];
91 va_list ap;
92
93 va_start(ap, fmt);
94 vsprintf(buf, fmt, ap);
95 dputs(buf);
96 va_end(ap);
97}
98#endif
99
100/* ----------------------------------------------------------------------
101 * Error reporting functions used elsewhere.
102 */
103
104void fatal(char *fmt, ...)
105{
106 va_list ap;
107
108 fprintf(stderr, "fatal error: ");
109
110 va_start(ap, fmt);
111 vfprintf(stderr, fmt, ap);
112 va_end(ap);
113
114 fprintf(stderr, "\n");
115 exit(1);
116}
117
118/* ----------------------------------------------------------------------
119 * GTK front end to puzzles.
120 */
121
122static void changed_preset(frontend *fe);
123
124struct font {
125#ifdef USE_PANGO
126 PangoFontDescription *desc;
127#else
128 GdkFont *font;
129#endif
130 int type;
131 int size;
132};
133
134/*
135 * This structure holds all the data relevant to a single window.
136 * In principle this would allow us to open multiple independent
137 * puzzle windows, although I can't currently see any real point in
138 * doing so. I'm just coding cleanly because there's no
139 * particularly good reason not to.
140 */
141struct frontend {
142 GtkWidget *window;
143 GtkAccelGroup *accelgroup;
144 GtkWidget *area;
145 GtkWidget *statusbar;
146 GtkWidget *menubar;
147#if GTK_CHECK_VERSION(3,20,0)
148 GtkCssProvider *css_provider;
149#endif
150 guint statusctx;
151 int w, h;
152 midend *me;
153#ifdef USE_CAIRO
154 const float *colours;
155 cairo_t *cr;
156 cairo_surface_t *image;
157#ifndef USE_CAIRO_WITHOUT_PIXMAP
158 GdkPixmap *pixmap;
159#endif
160 GdkColor background; /* for painting outside puzzle area */
161#else
162 GdkPixmap *pixmap;
163 GdkGC *gc;
164 GdkColor *colours;
165 GdkColormap *colmap;
166 int backgroundindex; /* which of colours[] is background */
167#endif
168 int ncolours;
169 int bbox_l, bbox_r, bbox_u, bbox_d;
170 int timer_active, timer_id;
171 struct timeval last_time;
172 struct font *fonts;
173 int nfonts, fontsize;
174 config_item *cfg;
175 int cfg_which, cfgret;
176 GtkWidget *cfgbox;
177 void *paste_data;
178 int paste_data_len;
179 int pw, ph; /* pixmap size (w, h are area size) */
180 int ox, oy; /* offset of pixmap in drawing area */
181#ifdef OLD_FILESEL
182 char *filesel_name;
183#endif
184 GSList *preset_radio;
185 int preset_threaded;
186 GtkWidget *preset_custom;
187 GtkWidget *copy_menu_item;
188#if !GTK_CHECK_VERSION(3,0,0)
189 int drawing_area_shrink_pending;
190 int menubar_is_local;
191#endif
192};
193
194struct blitter {
195#ifdef USE_CAIRO
196 cairo_surface_t *image;
197#else
198 GdkPixmap *pixmap;
199#endif
200 int w, h, x, y;
201};
202
203void get_random_seed(void **randseed, int *randseedsize)
204{
205 struct timeval *tvp = snew(struct timeval);
206 gettimeofday(tvp, NULL);
207 *randseed = (void *)tvp;
208 *randseedsize = sizeof(struct timeval);
209}
210
211void frontend_default_colour(frontend *fe, float *output)
212{
213#if !GTK_CHECK_VERSION(3,0,0)
214 /*
215 * Use the widget style's default background colour as the
216 * background for the puzzle drawing area.
217 */
218 GdkColor col = gtk_widget_get_style(fe->window)->bg[GTK_STATE_NORMAL];
219 output[0] = col.red / 65535.0;
220 output[1] = col.green / 65535.0;
221 output[2] = col.blue / 65535.0;
222#else
223 /*
224 * GTK 3 has decided that there's no such thing as a 'default
225 * background colour' any more, because widget styles might set
226 * the background to something more complicated like a background
227 * image. We don't want to get into overlaying our entire puzzle
228 * on an arbitrary background image, so we'll just make up a
229 * reasonable shade of grey.
230 */
231 output[0] = output[1] = output[2] = 0.9F;
232#endif
233}
234
235void gtk_status_bar(void *handle, char *text)
236{
237 frontend *fe = (frontend *)handle;
238
239 assert(fe->statusbar);
240
241 gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx);
242 gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text);
243}
244
245/* ----------------------------------------------------------------------
246 * Cairo drawing functions.
247 */
248
249#ifdef USE_CAIRO
250
251static void setup_drawing(frontend *fe)
252{
253 fe->cr = cairo_create(fe->image);
254 cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_GRAY);
255 cairo_set_line_width(fe->cr, 1.0);
256 cairo_set_line_cap(fe->cr, CAIRO_LINE_CAP_SQUARE);
257 cairo_set_line_join(fe->cr, CAIRO_LINE_JOIN_ROUND);
258}
259
260static void teardown_drawing(frontend *fe)
261{
262 cairo_destroy(fe->cr);
263 fe->cr = NULL;
264
265#ifndef USE_CAIRO_WITHOUT_PIXMAP
266 {
267 cairo_t *cr = gdk_cairo_create(fe->pixmap);
268 cairo_set_source_surface(cr, fe->image, 0, 0);
269 cairo_rectangle(cr,
270 fe->bbox_l - 1,
271 fe->bbox_u - 1,
272 fe->bbox_r - fe->bbox_l + 2,
273 fe->bbox_d - fe->bbox_u + 2);
274 cairo_fill(cr);
275 cairo_destroy(cr);
276 }
277#endif
278}
279
280static void snaffle_colours(frontend *fe)
281{
282 fe->colours = midend_colours(fe->me, &fe->ncolours);
283}
284
285static void set_colour(frontend *fe, int colour)
286{
287 cairo_set_source_rgb(fe->cr,
288 fe->colours[3*colour + 0],
289 fe->colours[3*colour + 1],
290 fe->colours[3*colour + 2]);
291}
292
293static void set_window_background(frontend *fe, int colour)
294{
295#if GTK_CHECK_VERSION(3,20,0)
296 char css_buf[512];
297 sprintf(css_buf, ".background { "
298 "background-color: #%02x%02x%02x; }",
299 (unsigned)(fe->colours[3*colour + 0] * 255),
300 (unsigned)(fe->colours[3*colour + 1] * 255),
301 (unsigned)(fe->colours[3*colour + 2] * 255));
302 if (!fe->css_provider)
303 fe->css_provider = gtk_css_provider_new();
304 if (!gtk_css_provider_load_from_data(
305 GTK_CSS_PROVIDER(fe->css_provider), css_buf, -1, NULL))
306 assert(0 && "Couldn't load CSS");
307 gtk_style_context_add_provider(
308 gtk_widget_get_style_context(fe->window),
309 GTK_STYLE_PROVIDER(fe->css_provider),
310 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
311 gtk_style_context_add_provider(
312 gtk_widget_get_style_context(fe->area),
313 GTK_STYLE_PROVIDER(fe->css_provider),
314 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
315#elif GTK_CHECK_VERSION(3,0,0)
316 GdkRGBA rgba;
317 rgba.red = fe->colours[3*colour + 0];
318 rgba.green = fe->colours[3*colour + 1];
319 rgba.blue = fe->colours[3*colour + 2];
320 rgba.alpha = 1.0;
321 gdk_window_set_background_rgba(gtk_widget_get_window(fe->area), &rgba);
322 gdk_window_set_background_rgba(gtk_widget_get_window(fe->window), &rgba);
323#else
324 GdkColormap *colmap;
325
326 colmap = gdk_colormap_get_system();
327 fe->background.red = fe->colours[3*colour + 0] * 65535;
328 fe->background.green = fe->colours[3*colour + 1] * 65535;
329 fe->background.blue = fe->colours[3*colour + 2] * 65535;
330 if (!gdk_colormap_alloc_color(colmap, &fe->background, FALSE, FALSE)) {
331 g_error("couldn't allocate background (#%02x%02x%02x)\n",
332 fe->background.red >> 8, fe->background.green >> 8,
333 fe->background.blue >> 8);
334 }
335 gdk_window_set_background(gtk_widget_get_window(fe->area),
336 &fe->background);
337 gdk_window_set_background(gtk_widget_get_window(fe->window),
338 &fe->background);
339#endif
340}
341
342static PangoLayout *make_pango_layout(frontend *fe)
343{
344 return (pango_cairo_create_layout(fe->cr));
345}
346
347static void draw_pango_layout(frontend *fe, PangoLayout *layout,
348 int x, int y)
349{
350 cairo_move_to(fe->cr, x, y);
351 pango_cairo_show_layout(fe->cr, layout);
352}
353
354static void save_screenshot_png(frontend *fe, const char *screenshot_file)
355{
356 cairo_surface_write_to_png(fe->image, screenshot_file);
357}
358
359static void do_clip(frontend *fe, int x, int y, int w, int h)
360{
361 cairo_new_path(fe->cr);
362 cairo_rectangle(fe->cr, x, y, w, h);
363 cairo_clip(fe->cr);
364}
365
366static void do_unclip(frontend *fe)
367{
368 cairo_reset_clip(fe->cr);
369}
370
371static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
372{
373 cairo_save(fe->cr);
374 cairo_new_path(fe->cr);
375 cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE);
376 cairo_rectangle(fe->cr, x, y, w, h);
377 cairo_fill(fe->cr);
378 cairo_restore(fe->cr);
379}
380
381static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
382{
383 cairo_new_path(fe->cr);
384 cairo_move_to(fe->cr, x1 + 0.5, y1 + 0.5);
385 cairo_line_to(fe->cr, x2 + 0.5, y2 + 0.5);
386 cairo_stroke(fe->cr);
387}
388
389static void do_draw_thick_line(frontend *fe, float thickness,
390 float x1, float y1, float x2, float y2)
391{
392 cairo_save(fe->cr);
393 cairo_set_line_width(fe->cr, thickness);
394 cairo_new_path(fe->cr);
395 cairo_move_to(fe->cr, x1, y1);
396 cairo_line_to(fe->cr, x2, y2);
397 cairo_stroke(fe->cr);
398 cairo_restore(fe->cr);
399}
400
401static void do_draw_poly(frontend *fe, int *coords, int npoints,
402 int fillcolour, int outlinecolour)
403{
404 int i;
405
406 cairo_new_path(fe->cr);
407 for (i = 0; i < npoints; i++)
408 cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5);
409 cairo_close_path(fe->cr);
410 if (fillcolour >= 0) {
411 set_colour(fe, fillcolour);
412 cairo_fill_preserve(fe->cr);
413 }
414 assert(outlinecolour >= 0);
415 set_colour(fe, outlinecolour);
416 cairo_stroke(fe->cr);
417}
418
419static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
420 int fillcolour, int outlinecolour)
421{
422 cairo_new_path(fe->cr);
423 cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI);
424 cairo_close_path(fe->cr); /* Just in case... */
425 if (fillcolour >= 0) {
426 set_colour(fe, fillcolour);
427 cairo_fill_preserve(fe->cr);
428 }
429 assert(outlinecolour >= 0);
430 set_colour(fe, outlinecolour);
431 cairo_stroke(fe->cr);
432}
433
434static void setup_blitter(blitter *bl, int w, int h)
435{
436 bl->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
437}
438
439static void teardown_blitter(blitter *bl)
440{
441 cairo_surface_destroy(bl->image);
442}
443
444static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
445{
446 cairo_t *cr = cairo_create(bl->image);
447
448 cairo_set_source_surface(cr, fe->image, -x, -y);
449 cairo_paint(cr);
450 cairo_destroy(cr);
451}
452
453static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
454{
455 cairo_set_source_surface(fe->cr, bl->image, x, y);
456 cairo_paint(fe->cr);
457}
458
459static void clear_backing_store(frontend *fe)
460{
461 fe->image = NULL;
462}
463
464static void wipe_and_maybe_destroy_cairo(frontend *fe, cairo_t *cr,
465 int destroy)
466{
467 cairo_set_source_rgb(cr, fe->colours[0], fe->colours[1], fe->colours[2]);
468 cairo_paint(cr);
469 if (destroy)
470 cairo_destroy(cr);
471}
472
473static void setup_backing_store(frontend *fe)
474{
475#ifndef USE_CAIRO_WITHOUT_PIXMAP
476 fe->pixmap = gdk_pixmap_new(gtk_widget_get_window(fe->area),
477 fe->pw, fe->ph, -1);
478#endif
479 fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
480 fe->pw, fe->ph);
481
482 wipe_and_maybe_destroy_cairo(fe, cairo_create(fe->image), TRUE);
483#ifndef USE_CAIRO_WITHOUT_PIXMAP
484 wipe_and_maybe_destroy_cairo(fe, gdk_cairo_create(fe->pixmap), TRUE);
485#endif
486#if GTK_CHECK_VERSION(3,22,0)
487 {
488 GdkWindow *gdkwin;
489 cairo_region_t *region;
490 GdkDrawingContext *drawctx;
491 cairo_t *cr;
492
493 gdkwin = gtk_widget_get_window(fe->area);
494 region = gdk_window_get_clip_region(gdkwin);
495 drawctx = gdk_window_begin_draw_frame(gdkwin, region);
496 cr = gdk_drawing_context_get_cairo_context(drawctx);
497 wipe_and_maybe_destroy_cairo(fe, cr, FALSE);
498 gdk_window_end_draw_frame(gdkwin, drawctx);
499 cairo_region_destroy(region);
500 }
501#else
502 wipe_and_maybe_destroy_cairo(
503 fe, gdk_cairo_create(gtk_widget_get_window(fe->area)), TRUE);
504#endif
505}
506
507static int backing_store_ok(frontend *fe)
508{
509 return (!!fe->image);
510}
511
512static void teardown_backing_store(frontend *fe)
513{
514 cairo_surface_destroy(fe->image);
515#ifndef USE_CAIRO_WITHOUT_PIXMAP
516 gdk_pixmap_unref(fe->pixmap);
517#endif
518 fe->image = NULL;
519}
520
521#endif
522
523/* ----------------------------------------------------------------------
524 * GDK drawing functions.
525 */
526
527#ifndef USE_CAIRO
528
529static void setup_drawing(frontend *fe)
530{
531 fe->gc = gdk_gc_new(fe->area->window);
532}
533
534static void teardown_drawing(frontend *fe)
535{
536 gdk_gc_unref(fe->gc);
537 fe->gc = NULL;
538}
539
540static void snaffle_colours(frontend *fe)
541{
542 int i, ncolours;
543 float *colours;
544 gboolean *success;
545
546 fe->colmap = gdk_colormap_get_system();
547 colours = midend_colours(fe->me, &ncolours);
548 fe->ncolours = ncolours;
549 fe->colours = snewn(ncolours, GdkColor);
550 for (i = 0; i < ncolours; i++) {
551 fe->colours[i].red = colours[i*3] * 0xFFFF;
552 fe->colours[i].green = colours[i*3+1] * 0xFFFF;
553 fe->colours[i].blue = colours[i*3+2] * 0xFFFF;
554 }
555 success = snewn(ncolours, gboolean);
556 gdk_colormap_alloc_colors(fe->colmap, fe->colours, ncolours,
557 FALSE, FALSE, success);
558 for (i = 0; i < ncolours; i++) {
559 if (!success[i]) {
560 g_error("couldn't allocate colour %d (#%02x%02x%02x)\n",
561 i, fe->colours[i].red >> 8,
562 fe->colours[i].green >> 8,
563 fe->colours[i].blue >> 8);
564 }
565 }
566}
567
568static void set_window_background(frontend *fe, int colour)
569{
570 fe->backgroundindex = colour;
571 gdk_window_set_background(fe->area->window, &fe->colours[colour]);
572 gdk_window_set_background(fe->window->window, &fe->colours[colour]);
573}
574
575static void set_colour(frontend *fe, int colour)
576{
577 gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
578}
579
580#ifdef USE_PANGO
581static PangoLayout *make_pango_layout(frontend *fe)
582{
583 return (pango_layout_new(gtk_widget_get_pango_context(fe->area)));
584}
585
586static void draw_pango_layout(frontend *fe, PangoLayout *layout,
587 int x, int y)
588{
589 gdk_draw_layout(fe->pixmap, fe->gc, x, y, layout);
590}
591#endif
592
593static void save_screenshot_png(frontend *fe, const char *screenshot_file)
594{
595 GdkPixbuf *pb;
596 GError *gerror = NULL;
597
598 midend_redraw(fe->me);
599
600 pb = gdk_pixbuf_get_from_drawable(NULL, fe->pixmap,
601 NULL, 0, 0, 0, 0, -1, -1);
602 gdk_pixbuf_save(pb, screenshot_file, "png", &gerror, NULL);
603}
604
605static void do_clip(frontend *fe, int x, int y, int w, int h)
606{
607 GdkRectangle rect;
608
609 rect.x = x;
610 rect.y = y;
611 rect.width = w;
612 rect.height = h;
613 gdk_gc_set_clip_rectangle(fe->gc, &rect);
614}
615
616static void do_unclip(frontend *fe)
617{
618 GdkRectangle rect;
619
620 rect.x = 0;
621 rect.y = 0;
622 rect.width = fe->w;
623 rect.height = fe->h;
624 gdk_gc_set_clip_rectangle(fe->gc, &rect);
625}
626
627static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
628{
629 gdk_draw_rectangle(fe->pixmap, fe->gc, 1, x, y, w, h);
630}
631
632static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
633{
634 gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
635}
636
637static void do_draw_thick_line(frontend *fe, float thickness,
638 float x1, float y1, float x2, float y2)
639{
640 GdkGCValues save;
641
642 gdk_gc_get_values(fe->gc, &save);
643 gdk_gc_set_line_attributes(fe->gc,
644 thickness,
645 GDK_LINE_SOLID,
646 GDK_CAP_BUTT,
647 GDK_JOIN_BEVEL);
648 gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
649 gdk_gc_set_line_attributes(fe->gc,
650 save.line_width,
651 save.line_style,
652 save.cap_style,
653 save.join_style);
654}
655
656static void do_draw_poly(frontend *fe, int *coords, int npoints,
657 int fillcolour, int outlinecolour)
658{
659 GdkPoint *points = snewn(npoints, GdkPoint);
660 int i;
661
662 for (i = 0; i < npoints; i++) {
663 points[i].x = coords[i*2];
664 points[i].y = coords[i*2+1];
665 }
666
667 if (fillcolour >= 0) {
668 set_colour(fe, fillcolour);
669 gdk_draw_polygon(fe->pixmap, fe->gc, TRUE, points, npoints);
670 }
671 assert(outlinecolour >= 0);
672 set_colour(fe, outlinecolour);
673
674 /*
675 * In principle we ought to be able to use gdk_draw_polygon for
676 * the outline as well. In fact, it turns out to interact badly
677 * with a clipping region, for no terribly obvious reason, so I
678 * draw the outline as a sequence of lines instead.
679 */
680 for (i = 0; i < npoints; i++)
681 gdk_draw_line(fe->pixmap, fe->gc,
682 points[i].x, points[i].y,
683 points[(i+1)%npoints].x, points[(i+1)%npoints].y);
684
685 sfree(points);
686}
687
688static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
689 int fillcolour, int outlinecolour)
690{
691 if (fillcolour >= 0) {
692 set_colour(fe, fillcolour);
693 gdk_draw_arc(fe->pixmap, fe->gc, TRUE,
694 cx - radius, cy - radius,
695 2 * radius, 2 * radius, 0, 360 * 64);
696 }
697
698 assert(outlinecolour >= 0);
699 set_colour(fe, outlinecolour);
700 gdk_draw_arc(fe->pixmap, fe->gc, FALSE,
701 cx - radius, cy - radius,
702 2 * radius, 2 * radius, 0, 360 * 64);
703}
704
705static void setup_blitter(blitter *bl, int w, int h)
706{
707 /*
708 * We can't create the pixmap right now, because fe->window
709 * might not yet exist. So we just cache w and h and create it
710 * during the firs call to blitter_save.
711 */
712 bl->pixmap = NULL;
713}
714
715static void teardown_blitter(blitter *bl)
716{
717 if (bl->pixmap)
718 gdk_pixmap_unref(bl->pixmap);
719}
720
721static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
722{
723 if (!bl->pixmap)
724 bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1);
725 gdk_draw_pixmap(bl->pixmap,
726 fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
727 fe->pixmap,
728 x, y, 0, 0, bl->w, bl->h);
729}
730
731static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
732{
733 assert(bl->pixmap);
734 gdk_draw_pixmap(fe->pixmap,
735 fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
736 bl->pixmap,
737 0, 0, x, y, bl->w, bl->h);
738}
739
740static void clear_backing_store(frontend *fe)
741{
742 fe->pixmap = NULL;
743}
744
745static void setup_backing_store(frontend *fe)
746{
747 GdkGC *gc;
748
749 fe->pixmap = gdk_pixmap_new(fe->area->window, fe->pw, fe->ph, -1);
750
751 gc = gdk_gc_new(fe->area->window);
752 gdk_gc_set_foreground(gc, &fe->colours[0]);
753 gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->pw, fe->ph);
754 gdk_draw_rectangle(fe->area->window, gc, 1, 0, 0, fe->w, fe->h);
755 gdk_gc_unref(gc);
756}
757
758static int backing_store_ok(frontend *fe)
759{
760 return (!!fe->pixmap);
761}
762
763static void teardown_backing_store(frontend *fe)
764{
765 gdk_pixmap_unref(fe->pixmap);
766 fe->pixmap = NULL;
767}
768
769#endif
770
771#ifndef USE_CAIRO_WITHOUT_PIXMAP
772static void repaint_rectangle(frontend *fe, GtkWidget *widget,
773 int x, int y, int w, int h)
774{
775 GdkGC *gc = gdk_gc_new(gtk_widget_get_window(widget));
776#ifdef USE_CAIRO
777 gdk_gc_set_foreground(gc, &fe->background);
778#else
779 gdk_gc_set_foreground(gc, &fe->colours[fe->backgroundindex]);
780#endif
781 if (x < fe->ox) {
782 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
783 TRUE, x, y, fe->ox - x, h);
784 w -= (fe->ox - x);
785 x = fe->ox;
786 }
787 if (y < fe->oy) {
788 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
789 TRUE, x, y, w, fe->oy - y);
790 h -= (fe->oy - y);
791 y = fe->oy;
792 }
793 if (w > fe->pw) {
794 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
795 TRUE, x + fe->pw, y, w - fe->pw, h);
796 w = fe->pw;
797 }
798 if (h > fe->ph) {
799 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
800 TRUE, x, y + fe->ph, w, h - fe->ph);
801 h = fe->ph;
802 }
803 gdk_draw_pixmap(gtk_widget_get_window(widget), gc, fe->pixmap,
804 x - fe->ox, y - fe->oy, x, y, w, h);
805 gdk_gc_unref(gc);
806}
807#endif
808
809/* ----------------------------------------------------------------------
810 * Pango font functions.
811 */
812
813#ifdef USE_PANGO
814
815static void add_font(frontend *fe, int index, int fonttype, int fontsize)
816{
817 /*
818 * Use Pango to find the closest match to the requested
819 * font.
820 */
821 PangoFontDescription *fd;
822
823 fd = pango_font_description_new();
824 /* `Monospace' and `Sans' are meta-families guaranteed to exist */
825 pango_font_description_set_family(fd, fonttype == FONT_FIXED ?
826 "Monospace" : "Sans");
827 pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD);
828 /*
829 * I found some online Pango documentation which
830 * described a function called
831 * pango_font_description_set_absolute_size(), which is
832 * _exactly_ what I want here. Unfortunately, none of
833 * my local Pango installations have it (presumably
834 * they're too old), so I'm going to have to hack round
835 * it by figuring out the point size myself. This
836 * limits me to X and probably also breaks in later
837 * Pango installations, so ideally I should add another
838 * CHECK_VERSION type ifdef and use set_absolute_size
839 * where available. All very annoying.
840 */
841#ifdef HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
842 pango_font_description_set_absolute_size(fd, PANGO_SCALE*fontsize);
843#else
844 {
845 Display *d = GDK_DISPLAY();
846 int s = DefaultScreen(d);
847 double resolution =
848 (PANGO_SCALE * 72.27 / 25.4) *
849 ((double) DisplayWidthMM(d, s) / DisplayWidth (d, s));
850 pango_font_description_set_size(fd, resolution * fontsize);
851 }
852#endif
853 fe->fonts[index].desc = fd;
854}
855
856static void align_and_draw_text(frontend *fe,
857 int index, int align, int x, int y,
858 const char *text)
859{
860 PangoLayout *layout;
861 PangoRectangle rect;
862
863 layout = make_pango_layout(fe);
864
865 /*
866 * Create a layout.
867 */
868 pango_layout_set_font_description(layout, fe->fonts[index].desc);
869 pango_layout_set_text(layout, text, strlen(text));
870 pango_layout_get_pixel_extents(layout, NULL, &rect);
871
872 if (align & ALIGN_VCENTRE)
873 rect.y -= rect.height / 2;
874 else
875 rect.y -= rect.height;
876
877 if (align & ALIGN_HCENTRE)
878 rect.x -= rect.width / 2;
879 else if (align & ALIGN_HRIGHT)
880 rect.x -= rect.width;
881
882 draw_pango_layout(fe, layout, rect.x + x, rect.y + y);
883
884 g_object_unref(layout);
885}
886
887#endif
888
889/* ----------------------------------------------------------------------
890 * Old-fashioned font functions.
891 */
892
893#ifndef USE_PANGO
894
895static void add_font(int index, int fonttype, int fontsize)
896{
897 /*
898 * In GTK 1.2, I don't know of any plausible way to
899 * pick a suitable font, so I'm just going to be
900 * tedious.
901 */
902 fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ?
903 "fixed" : "variable");
904}
905
906static void align_and_draw_text(int index, int align, int x, int y,
907 const char *text)
908{
909 int lb, rb, wid, asc, desc;
910
911 /*
912 * Measure vertical string extents with respect to the same
913 * string always...
914 */
915 gdk_string_extents(fe->fonts[i].font,
916 "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
917 &lb, &rb, &wid, &asc, &desc);
918 if (align & ALIGN_VCENTRE)
919 y += asc - (asc+desc)/2;
920 else
921 y += asc;
922
923 /*
924 * ... but horizontal extents with respect to the provided
925 * string. This means that multiple pieces of text centred
926 * on the same y-coordinate don't have different baselines.
927 */
928 gdk_string_extents(fe->fonts[i].font, text,
929 &lb, &rb, &wid, &asc, &desc);
930
931 if (align & ALIGN_HCENTRE)
932 x -= wid / 2;
933 else if (align & ALIGN_HRIGHT)
934 x -= wid;
935
936 /*
937 * Actually draw the text.
938 */
939 gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text);
940}
941
942#endif
943
944/* ----------------------------------------------------------------------
945 * The exported drawing functions.
946 */
947
948void gtk_start_draw(void *handle)
949{
950 frontend *fe = (frontend *)handle;
951 fe->bbox_l = fe->w;
952 fe->bbox_r = 0;
953 fe->bbox_u = fe->h;
954 fe->bbox_d = 0;
955 setup_drawing(fe);
956}
957
958void gtk_clip(void *handle, int x, int y, int w, int h)
959{
960 frontend *fe = (frontend *)handle;
961 do_clip(fe, x, y, w, h);
962}
963
964void gtk_unclip(void *handle)
965{
966 frontend *fe = (frontend *)handle;
967 do_unclip(fe);
968}
969
970void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
971 int align, int colour, char *text)
972{
973 frontend *fe = (frontend *)handle;
974 int i;
975
976 /*
977 * Find or create the font.
978 */
979 for (i = 0; i < fe->nfonts; i++)
980 if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
981 break;
982
983 if (i == fe->nfonts) {
984 if (fe->fontsize <= fe->nfonts) {
985 fe->fontsize = fe->nfonts + 10;
986 fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
987 }
988
989 fe->nfonts++;
990
991 fe->fonts[i].type = fonttype;
992 fe->fonts[i].size = fontsize;
993 add_font(fe, i, fonttype, fontsize);
994 }
995
996 /*
997 * Do the job.
998 */
999 set_colour(fe, colour);
1000 align_and_draw_text(fe, i, align, x, y, text);
1001}
1002
1003void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour)
1004{
1005 frontend *fe = (frontend *)handle;
1006 set_colour(fe, colour);
1007 do_draw_rect(fe, x, y, w, h);
1008}
1009
1010void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
1011{
1012 frontend *fe = (frontend *)handle;
1013 set_colour(fe, colour);
1014 do_draw_line(fe, x1, y1, x2, y2);
1015}
1016
1017void gtk_draw_thick_line(void *handle, float thickness,
1018 float x1, float y1, float x2, float y2, int colour)
1019{
1020 frontend *fe = (frontend *)handle;
1021 set_colour(fe, colour);
1022 do_draw_thick_line(fe, thickness, x1, y1, x2, y2);
1023}
1024
1025void gtk_draw_poly(void *handle, int *coords, int npoints,
1026 int fillcolour, int outlinecolour)
1027{
1028 frontend *fe = (frontend *)handle;
1029 do_draw_poly(fe, coords, npoints, fillcolour, outlinecolour);
1030}
1031
1032void gtk_draw_circle(void *handle, int cx, int cy, int radius,
1033 int fillcolour, int outlinecolour)
1034{
1035 frontend *fe = (frontend *)handle;
1036 do_draw_circle(fe, cx, cy, radius, fillcolour, outlinecolour);
1037}
1038
1039blitter *gtk_blitter_new(void *handle, int w, int h)
1040{
1041 blitter *bl = snew(blitter);
1042 setup_blitter(bl, w, h);
1043 bl->w = w;
1044 bl->h = h;
1045 return bl;
1046}
1047
1048void gtk_blitter_free(void *handle, blitter *bl)
1049{
1050 teardown_blitter(bl);
1051 sfree(bl);
1052}
1053
1054void gtk_blitter_save(void *handle, blitter *bl, int x, int y)
1055{
1056 frontend *fe = (frontend *)handle;
1057 do_blitter_save(fe, bl, x, y);
1058 bl->x = x;
1059 bl->y = y;
1060}
1061
1062void gtk_blitter_load(void *handle, blitter *bl, int x, int y)
1063{
1064 frontend *fe = (frontend *)handle;
1065 if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
1066 x = bl->x;
1067 y = bl->y;
1068 }
1069 do_blitter_load(fe, bl, x, y);
1070}
1071
1072void gtk_draw_update(void *handle, int x, int y, int w, int h)
1073{
1074 frontend *fe = (frontend *)handle;
1075 if (fe->bbox_l > x ) fe->bbox_l = x ;
1076 if (fe->bbox_r < x+w) fe->bbox_r = x+w;
1077 if (fe->bbox_u > y ) fe->bbox_u = y ;
1078 if (fe->bbox_d < y+h) fe->bbox_d = y+h;
1079}
1080
1081void gtk_end_draw(void *handle)
1082{
1083 frontend *fe = (frontend *)handle;
1084
1085 teardown_drawing(fe);
1086
1087 if (fe->bbox_l < fe->bbox_r && fe->bbox_u < fe->bbox_d) {
1088#ifdef USE_CAIRO_WITHOUT_PIXMAP
1089 gtk_widget_queue_draw_area(fe->area,
1090 fe->bbox_l - 1 + fe->ox,
1091 fe->bbox_u - 1 + fe->oy,
1092 fe->bbox_r - fe->bbox_l + 2,
1093 fe->bbox_d - fe->bbox_u + 2);
1094#else
1095 repaint_rectangle(fe, fe->area,
1096 fe->bbox_l - 1 + fe->ox,
1097 fe->bbox_u - 1 + fe->oy,
1098 fe->bbox_r - fe->bbox_l + 2,
1099 fe->bbox_d - fe->bbox_u + 2);
1100#endif
1101 }
1102}
1103
1104#ifdef USE_PANGO
1105char *gtk_text_fallback(void *handle, const char *const *strings, int nstrings)
1106{
1107 /*
1108 * We assume Pango can cope with any UTF-8 likely to be emitted
1109 * by a puzzle.
1110 */
1111 return dupstr(strings[0]);
1112}
1113#endif
1114
1115const struct drawing_api gtk_drawing = {
1116 gtk_draw_text,
1117 gtk_draw_rect,
1118 gtk_draw_line,
1119 gtk_draw_poly,
1120 gtk_draw_circle,
1121 gtk_draw_update,
1122 gtk_clip,
1123 gtk_unclip,
1124 gtk_start_draw,
1125 gtk_end_draw,
1126 gtk_status_bar,
1127 gtk_blitter_new,
1128 gtk_blitter_free,
1129 gtk_blitter_save,
1130 gtk_blitter_load,
1131 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
1132 NULL, NULL, /* line_width, line_dotted */
1133#ifdef USE_PANGO
1134 gtk_text_fallback,
1135#else
1136 NULL,
1137#endif
1138#ifdef NO_THICK_LINE
1139 NULL,
1140#else
1141 gtk_draw_thick_line,
1142#endif
1143};
1144
1145static void destroy(GtkWidget *widget, gpointer data)
1146{
1147 frontend *fe = (frontend *)data;
1148 deactivate_timer(fe);
1149 midend_free(fe->me);
1150 gtk_main_quit();
1151}
1152
1153static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
1154{
1155 frontend *fe = (frontend *)data;
1156 int keyval;
1157 int shift = (event->state & GDK_SHIFT_MASK) ? MOD_SHFT : 0;
1158 int ctrl = (event->state & GDK_CONTROL_MASK) ? MOD_CTRL : 0;
1159
1160 if (!backing_store_ok(fe))
1161 return TRUE;
1162
1163#if !GTK_CHECK_VERSION(2,0,0)
1164 /* Gtk 1.2 passes a key event to this function even if it's also
1165 * defined as an accelerator.
1166 * Gtk 2 doesn't do this, and this function appears not to exist there. */
1167 if (fe->accelgroup &&
1168 gtk_accel_group_get_entry(fe->accelgroup,
1169 event->keyval, event->state))
1170 return TRUE;
1171#endif
1172
1173 /* Handle mnemonics. */
1174 if (gtk_window_activate_key(GTK_WINDOW(fe->window), event))
1175 return TRUE;
1176
1177 if (event->keyval == GDK_KEY_Up)
1178 keyval = shift | ctrl | CURSOR_UP;
1179 else if (event->keyval == GDK_KEY_KP_Up ||
1180 event->keyval == GDK_KEY_KP_8)
1181 keyval = MOD_NUM_KEYPAD | '8';
1182 else if (event->keyval == GDK_KEY_Down)
1183 keyval = shift | ctrl | CURSOR_DOWN;
1184 else if (event->keyval == GDK_KEY_KP_Down ||
1185 event->keyval == GDK_KEY_KP_2)
1186 keyval = MOD_NUM_KEYPAD | '2';
1187 else if (event->keyval == GDK_KEY_Left)
1188 keyval = shift | ctrl | CURSOR_LEFT;
1189 else if (event->keyval == GDK_KEY_KP_Left ||
1190 event->keyval == GDK_KEY_KP_4)
1191 keyval = MOD_NUM_KEYPAD | '4';
1192 else if (event->keyval == GDK_KEY_Right)
1193 keyval = shift | ctrl | CURSOR_RIGHT;
1194 else if (event->keyval == GDK_KEY_KP_Right ||
1195 event->keyval == GDK_KEY_KP_6)
1196 keyval = MOD_NUM_KEYPAD | '6';
1197 else if (event->keyval == GDK_KEY_KP_Home ||
1198 event->keyval == GDK_KEY_KP_7)
1199 keyval = MOD_NUM_KEYPAD | '7';
1200 else if (event->keyval == GDK_KEY_KP_End ||
1201 event->keyval == GDK_KEY_KP_1)
1202 keyval = MOD_NUM_KEYPAD | '1';
1203 else if (event->keyval == GDK_KEY_KP_Page_Up ||
1204 event->keyval == GDK_KEY_KP_9)
1205 keyval = MOD_NUM_KEYPAD | '9';
1206 else if (event->keyval == GDK_KEY_KP_Page_Down ||
1207 event->keyval == GDK_KEY_KP_3)
1208 keyval = MOD_NUM_KEYPAD | '3';
1209 else if (event->keyval == GDK_KEY_KP_Insert ||
1210 event->keyval == GDK_KEY_KP_0)
1211 keyval = MOD_NUM_KEYPAD | '0';
1212 else if (event->keyval == GDK_KEY_KP_Begin ||
1213 event->keyval == GDK_KEY_KP_5)
1214 keyval = MOD_NUM_KEYPAD | '5';
1215 else if (event->keyval == GDK_KEY_BackSpace ||
1216 event->keyval == GDK_KEY_Delete ||
1217 event->keyval == GDK_KEY_KP_Delete)
1218 keyval = '\177';
1219 else if (event->string[0] && !event->string[1])
1220 keyval = (unsigned char)event->string[0];
1221 else
1222 keyval = -1;
1223
1224 if (keyval >= 0 &&
1225 !midend_process_key(fe->me, 0, 0, keyval))
1226 gtk_widget_destroy(fe->window);
1227
1228 return TRUE;
1229}
1230
1231static gint button_event(GtkWidget *widget, GdkEventButton *event,
1232 gpointer data)
1233{
1234 frontend *fe = (frontend *)data;
1235 int button;
1236
1237 if (!backing_store_ok(fe))
1238 return TRUE;
1239
1240 if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
1241 return TRUE;
1242
1243 if (event->button == 2 || (event->state & GDK_SHIFT_MASK))
1244 button = MIDDLE_BUTTON;
1245 else if (event->button == 3 || (event->state & GDK_MOD1_MASK))
1246 button = RIGHT_BUTTON;
1247 else if (event->button == 1)
1248 button = LEFT_BUTTON;
1249 else if (event->button == 8 && event->type == GDK_BUTTON_PRESS)
1250 button = 'u';
1251 else if (event->button == 9 && event->type == GDK_BUTTON_PRESS)
1252 button = 'r';
1253 else
1254 return FALSE; /* don't even know what button! */
1255
1256 if (event->type == GDK_BUTTON_RELEASE && button >= LEFT_BUTTON)
1257 button += LEFT_RELEASE - LEFT_BUTTON;
1258
1259 if (!midend_process_key(fe->me, event->x - fe->ox,
1260 event->y - fe->oy, button))
1261 gtk_widget_destroy(fe->window);
1262
1263 return TRUE;
1264}
1265
1266static gint motion_event(GtkWidget *widget, GdkEventMotion *event,
1267 gpointer data)
1268{
1269 frontend *fe = (frontend *)data;
1270 int button;
1271
1272 if (!backing_store_ok(fe))
1273 return TRUE;
1274
1275 if (event->state & (GDK_BUTTON2_MASK | GDK_SHIFT_MASK))
1276 button = MIDDLE_DRAG;
1277 else if (event->state & GDK_BUTTON1_MASK)
1278 button = LEFT_DRAG;
1279 else if (event->state & GDK_BUTTON3_MASK)
1280 button = RIGHT_DRAG;
1281 else
1282 return FALSE; /* don't even know what button! */
1283
1284 if (!midend_process_key(fe->me, event->x - fe->ox,
1285 event->y - fe->oy, button))
1286 gtk_widget_destroy(fe->window);
1287#if GTK_CHECK_VERSION(2,12,0)
1288 gdk_event_request_motions(event);
1289#else
1290 gdk_window_get_pointer(gtk_widget_get_window(widget), NULL, NULL, NULL);
1291#endif
1292
1293 return TRUE;
1294}
1295
1296#if GTK_CHECK_VERSION(3,0,0)
1297static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
1298{
1299 frontend *fe = (frontend *)data;
1300 GdkRectangle dirtyrect;
1301
1302 gdk_cairo_get_clip_rectangle(cr, &dirtyrect);
1303 cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy);
1304 cairo_rectangle(cr, dirtyrect.x, dirtyrect.y,
1305 dirtyrect.width, dirtyrect.height);
1306 cairo_fill(cr);
1307
1308 return TRUE;
1309}
1310#else
1311static gint expose_area(GtkWidget *widget, GdkEventExpose *event,
1312 gpointer data)
1313{
1314 frontend *fe = (frontend *)data;
1315
1316 if (backing_store_ok(fe)) {
1317#ifdef USE_CAIRO_WITHOUT_PIXMAP
1318 cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
1319 cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy);
1320 cairo_rectangle(cr, event->area.x, event->area.y,
1321 event->area.width, event->area.height);
1322 cairo_fill(cr);
1323 cairo_destroy(cr);
1324#else
1325 repaint_rectangle(fe, widget,
1326 event->area.x, event->area.y,
1327 event->area.width, event->area.height);
1328#endif
1329 }
1330 return TRUE;
1331}
1332#endif
1333
1334static gint map_window(GtkWidget *widget, GdkEvent *event,
1335 gpointer data)
1336{
1337 frontend *fe = (frontend *)data;
1338
1339 /*
1340 * Apparently we need to do this because otherwise the status
1341 * bar will fail to update immediately. Annoying, but there we
1342 * go.
1343 */
1344 gtk_widget_queue_draw(fe->window);
1345
1346 return TRUE;
1347}
1348
1349static gint configure_area(GtkWidget *widget,
1350 GdkEventConfigure *event, gpointer data)
1351{
1352 frontend *fe = (frontend *)data;
1353 int x, y;
1354 int oldw = fe->w, oldpw = fe->pw, oldh = fe->h, oldph = fe->ph;
1355
1356 x = event->width;
1357 y = event->height;
1358 fe->w = x;
1359 fe->h = y;
1360 midend_size(fe->me, &x, &y, TRUE);
1361 fe->pw = x;
1362 fe->ph = y;
1363 fe->ox = (fe->w - fe->pw) / 2;
1364 fe->oy = (fe->h - fe->ph) / 2;
1365
1366 if (oldw != fe->w || oldpw != fe->pw ||
1367 oldh != fe->h || oldph != fe->ph || !backing_store_ok(fe)) {
1368 if (backing_store_ok(fe))
1369 teardown_backing_store(fe);
1370 setup_backing_store(fe);
1371 }
1372
1373 midend_force_redraw(fe->me);
1374
1375 return TRUE;
1376}
1377
1378static gint timer_func(gpointer data)
1379{
1380 frontend *fe = (frontend *)data;
1381
1382 if (fe->timer_active) {
1383 struct timeval now;
1384 float elapsed;
1385 gettimeofday(&now, NULL);
1386 elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
1387 (now.tv_sec - fe->last_time.tv_sec));
1388 midend_timer(fe->me, elapsed); /* may clear timer_active */
1389 fe->last_time = now;
1390 }
1391
1392 return fe->timer_active;
1393}
1394
1395void deactivate_timer(frontend *fe)
1396{
1397 if (!fe)
1398 return; /* can happen due to --generate */
1399 if (fe->timer_active)
1400 g_source_remove(fe->timer_id);
1401 fe->timer_active = FALSE;
1402}
1403
1404void activate_timer(frontend *fe)
1405{
1406 if (!fe)
1407 return; /* can happen due to --generate */
1408 if (!fe->timer_active) {
1409 fe->timer_id = g_timeout_add(20, timer_func, fe);
1410 gettimeofday(&fe->last_time, NULL);
1411 }
1412 fe->timer_active = TRUE;
1413}
1414
1415static void window_destroy(GtkWidget *widget, gpointer data)
1416{
1417 gtk_main_quit();
1418}
1419
1420static int win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
1421{
1422 GObject *cancelbutton = G_OBJECT(data);
1423
1424 /*
1425 * `Escape' effectively clicks the cancel button
1426 */
1427 if (event->keyval == GDK_KEY_Escape) {
1428 g_signal_emit_by_name(cancelbutton, "clicked");
1429 return TRUE;
1430 }
1431
1432 return FALSE;
1433}
1434
1435enum { MB_OK, MB_YESNO };
1436
1437static void align_label(GtkLabel *label, double x, double y)
1438{
1439#if GTK_CHECK_VERSION(3,16,0)
1440 gtk_label_set_xalign(label, x);
1441 gtk_label_set_yalign(label, y);
1442#elif GTK_CHECK_VERSION(3,14,0)
1443 gtk_widget_set_halign(GTK_WIDGET(label),
1444 x == 0 ? GTK_ALIGN_START :
1445 x == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER);
1446 gtk_widget_set_valign(GTK_WIDGET(label),
1447 y == 0 ? GTK_ALIGN_START :
1448 y == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER);
1449#else
1450 gtk_misc_set_alignment(GTK_MISC(label), x, y);
1451#endif
1452}
1453
1454#if GTK_CHECK_VERSION(3,0,0)
1455int message_box(GtkWidget *parent, char *title, char *msg, int centre,
1456 int type)
1457{
1458 GtkWidget *window;
1459 gint ret;
1460
1461 window = gtk_message_dialog_new
1462 (GTK_WINDOW(parent),
1463 (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1464 (type == MB_OK ? GTK_MESSAGE_INFO : GTK_MESSAGE_QUESTION),
1465 (type == MB_OK ? GTK_BUTTONS_OK : GTK_BUTTONS_YES_NO),
1466 "%s", msg);
1467 gtk_window_set_title(GTK_WINDOW(window), title);
1468 ret = gtk_dialog_run(GTK_DIALOG(window));
1469 gtk_widget_destroy(window);
1470 return (type == MB_OK ? TRUE : (ret == GTK_RESPONSE_YES));
1471}
1472#else /* GTK_CHECK_VERSION(3,0,0) */
1473static void msgbox_button_clicked(GtkButton *button, gpointer data)
1474{
1475 GtkWidget *window = GTK_WIDGET(data);
1476 int v, *ip;
1477
1478 ip = (int *)g_object_get_data(G_OBJECT(window), "user-data");
1479 v = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "user-data"));
1480 *ip = v;
1481
1482 gtk_widget_destroy(GTK_WIDGET(data));
1483}
1484
1485int message_box(GtkWidget *parent, char *title, char *msg, int centre,
1486 int type)
1487{
1488 GtkWidget *window, *hbox, *text, *button;
1489 char *titles;
1490 int i, def, cancel;
1491
1492 window = gtk_dialog_new();
1493 text = gtk_label_new(msg);
1494 align_label(GTK_LABEL(text), 0.0, 0.0);
1495 hbox = gtk_hbox_new(FALSE, 0);
1496 gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 20);
1497 gtk_box_pack_start
1498 (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
1499 hbox, FALSE, FALSE, 20);
1500 gtk_widget_show(text);
1501 gtk_widget_show(hbox);
1502 gtk_window_set_title(GTK_WINDOW(window), title);
1503 gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
1504
1505 if (type == MB_OK) {
1506 titles = LABEL_OK "\0";
1507 def = cancel = 0;
1508 } else {
1509 assert(type == MB_YESNO);
1510 titles = LABEL_NO "\0" LABEL_YES "\0";
1511 def = 1;
1512 cancel = 0;
1513 }
1514 i = 0;
1515
1516 while (*titles) {
1517 button = gtk_button_new_with_our_label(titles);
1518 gtk_box_pack_end
1519 (GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(window))),
1520 button, FALSE, FALSE, 0);
1521 gtk_widget_show(button);
1522 if (i == def) {
1523 gtk_widget_set_can_default(button, TRUE);
1524 gtk_window_set_default(GTK_WINDOW(window), button);
1525 }
1526 if (i == cancel) {
1527 g_signal_connect(G_OBJECT(window), "key_press_event",
1528 G_CALLBACK(win_key_press), button);
1529 }
1530 g_signal_connect(G_OBJECT(button), "clicked",
1531 G_CALLBACK(msgbox_button_clicked), window);
1532 g_object_set_data(G_OBJECT(button), "user-data",
1533 GINT_TO_POINTER(i));
1534 titles += strlen(titles)+1;
1535 i++;
1536 }
1537 g_object_set_data(G_OBJECT(window), "user-data", &i);
1538 g_signal_connect(G_OBJECT(window), "destroy",
1539 G_CALLBACK(window_destroy), NULL);
1540 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1541 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent));
1542 /* set_transient_window_pos(parent, window); */
1543 gtk_widget_show(window);
1544 i = -1;
1545 gtk_main();
1546 return (type == MB_YESNO ? i == 1 : TRUE);
1547}
1548#endif /* GTK_CHECK_VERSION(3,0,0) */
1549
1550void error_box(GtkWidget *parent, char *msg)
1551{
1552 message_box(parent, "Error", msg, FALSE, MB_OK);
1553}
1554
1555static void config_ok_button_clicked(GtkButton *button, gpointer data)
1556{
1557 frontend *fe = (frontend *)data;
1558 char *err;
1559
1560 err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
1561
1562 if (err)
1563 error_box(fe->cfgbox, err);
1564 else {
1565 fe->cfgret = TRUE;
1566 gtk_widget_destroy(fe->cfgbox);
1567 changed_preset(fe);
1568 }
1569}
1570
1571static void config_cancel_button_clicked(GtkButton *button, gpointer data)
1572{
1573 frontend *fe = (frontend *)data;
1574
1575 gtk_widget_destroy(fe->cfgbox);
1576}
1577
1578static int editbox_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1579{
1580 /*
1581 * GtkEntry has a nasty habit of eating the Return key, which
1582 * is unhelpful since it doesn't actually _do_ anything with it
1583 * (it calls gtk_widget_activate, but our edit boxes never need
1584 * activating). So I catch Return before GtkEntry sees it, and
1585 * pass it straight on to the parent widget. Effect: hitting
1586 * Return in an edit box will now activate the default button
1587 * in the dialog just like it will everywhere else.
1588 */
1589 if (event->keyval == GDK_KEY_Return &&
1590 gtk_widget_get_parent(widget) != NULL) {
1591 gint return_val;
1592 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1593 g_signal_emit_by_name(G_OBJECT(gtk_widget_get_parent(widget)),
1594 "key_press_event", event, &return_val);
1595 return return_val;
1596 }
1597 return FALSE;
1598}
1599
1600static void editbox_changed(GtkEditable *ed, gpointer data)
1601{
1602 config_item *i = (config_item *)data;
1603
1604 sfree(i->sval);
1605 i->sval = dupstr(gtk_entry_get_text(GTK_ENTRY(ed)));
1606}
1607
1608static void button_toggled(GtkToggleButton *tb, gpointer data)
1609{
1610 config_item *i = (config_item *)data;
1611
1612 i->ival = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb));
1613}
1614
1615static void droplist_sel(GtkComboBox *combo, gpointer data)
1616{
1617 config_item *i = (config_item *)data;
1618
1619 i->ival = gtk_combo_box_get_active(combo);
1620}
1621
1622static int get_config(frontend *fe, int which)
1623{
1624 GtkWidget *w, *table, *cancel;
1625 GtkBox *content_box, *button_box;
1626 char *title;
1627 config_item *i;
1628 int y;
1629
1630 fe->cfg = midend_get_config(fe->me, which, &title);
1631 fe->cfg_which = which;
1632 fe->cfgret = FALSE;
1633
1634#if GTK_CHECK_VERSION(3,0,0)
1635 /* GtkDialog isn't quite flexible enough */
1636 fe->cfgbox = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1637 content_box = GTK_BOX(gtk_vbox_new(FALSE, 8));
1638 g_object_set(G_OBJECT(content_box), "margin", 8, (const char *)NULL);
1639 gtk_widget_show(GTK_WIDGET(content_box));
1640 gtk_container_add(GTK_CONTAINER(fe->cfgbox), GTK_WIDGET(content_box));
1641 button_box = GTK_BOX(gtk_hbox_new(FALSE, 8));
1642 gtk_widget_show(GTK_WIDGET(button_box));
1643 gtk_box_pack_end(content_box, GTK_WIDGET(button_box), FALSE, FALSE, 0);
1644 {
1645 GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1646 gtk_widget_show(sep);
1647 gtk_box_pack_end(content_box, sep, FALSE, FALSE, 0);
1648 }
1649#else
1650 fe->cfgbox = gtk_dialog_new();
1651 content_box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(fe->cfgbox)));
1652 button_box = GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(fe->cfgbox)));
1653#endif
1654 gtk_window_set_title(GTK_WINDOW(fe->cfgbox), title);
1655 sfree(title);
1656
1657 w = gtk_button_new_with_our_label(LABEL_CANCEL);
1658 gtk_box_pack_end(button_box, w, FALSE, FALSE, 0);
1659 gtk_widget_show(w);
1660 g_signal_connect(G_OBJECT(w), "clicked",
1661 G_CALLBACK(config_cancel_button_clicked), fe);
1662 cancel = w;
1663
1664 w = gtk_button_new_with_our_label(LABEL_OK);
1665 gtk_box_pack_end(button_box, w, FALSE, FALSE, 0);
1666 gtk_widget_show(w);
1667 gtk_widget_set_can_default(w, TRUE);
1668 gtk_window_set_default(GTK_WINDOW(fe->cfgbox), w);
1669 g_signal_connect(G_OBJECT(w), "clicked",
1670 G_CALLBACK(config_ok_button_clicked), fe);
1671
1672#if GTK_CHECK_VERSION(3,0,0)
1673 table = gtk_grid_new();
1674#else
1675 table = gtk_table_new(1, 2, FALSE);
1676#endif
1677 y = 0;
1678 gtk_box_pack_start(content_box, table, FALSE, FALSE, 0);
1679 gtk_widget_show(table);
1680
1681 for (i = fe->cfg; i->type != C_END; i++) {
1682#if !GTK_CHECK_VERSION(3,0,0)
1683 gtk_table_resize(GTK_TABLE(table), y+1, 2);
1684#endif
1685
1686 switch (i->type) {
1687 case C_STRING:
1688 /*
1689 * Edit box with a label beside it.
1690 */
1691
1692 w = gtk_label_new(i->name);
1693 align_label(GTK_LABEL(w), 0.0, 0.5);
1694#if GTK_CHECK_VERSION(3,0,0)
1695 gtk_grid_attach(GTK_GRID(table), w, 0, y, 1, 1);
1696#else
1697 gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
1698 GTK_SHRINK | GTK_FILL,
1699 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1700 3, 3);
1701#endif
1702 gtk_widget_show(w);
1703
1704 w = gtk_entry_new();
1705#if GTK_CHECK_VERSION(3,0,0)
1706 gtk_grid_attach(GTK_GRID(table), w, 1, y, 1, 1);
1707 g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
1708#else
1709 gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
1710 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1711 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1712 3, 3);
1713#endif
1714 gtk_entry_set_text(GTK_ENTRY(w), i->sval);
1715 g_signal_connect(G_OBJECT(w), "changed",
1716 G_CALLBACK(editbox_changed), i);
1717 g_signal_connect(G_OBJECT(w), "key_press_event",
1718 G_CALLBACK(editbox_key), NULL);
1719 gtk_widget_show(w);
1720
1721 break;
1722
1723 case C_BOOLEAN:
1724 /*
1725 * Simple checkbox.
1726 */
1727 w = gtk_check_button_new_with_label(i->name);
1728 g_signal_connect(G_OBJECT(w), "toggled",
1729 G_CALLBACK(button_toggled), i);
1730#if GTK_CHECK_VERSION(3,0,0)
1731 gtk_grid_attach(GTK_GRID(table), w, 0, y, 2, 1);
1732 g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
1733#else
1734 gtk_table_attach(GTK_TABLE(table), w, 0, 2, y, y+1,
1735 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1736 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1737 3, 3);
1738#endif
1739 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), i->ival);
1740 gtk_widget_show(w);
1741 break;
1742
1743 case C_CHOICES:
1744 /*
1745 * Drop-down list (GtkComboBox).
1746 */
1747
1748 w = gtk_label_new(i->name);
1749 align_label(GTK_LABEL(w), 0.0, 0.5);
1750#if GTK_CHECK_VERSION(3,0,0)
1751 gtk_grid_attach(GTK_GRID(table), w, 0, y, 1, 1);
1752#else
1753 gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
1754 GTK_SHRINK | GTK_FILL,
1755 GTK_EXPAND | GTK_SHRINK | GTK_FILL ,
1756 3, 3);
1757#endif
1758 gtk_widget_show(w);
1759
1760 {
1761 int c;
1762 char *p, *q, *name;
1763 GtkListStore *model;
1764 GtkCellRenderer *cr;
1765 GtkTreeIter iter;
1766
1767 model = gtk_list_store_new(1, G_TYPE_STRING);
1768
1769 c = *i->sval;
1770 p = i->sval+1;
1771
1772 while (*p) {
1773 q = p;
1774 while (*q && *q != c)
1775 q++;
1776
1777 name = snewn(q-p+1, char);
1778 strncpy(name, p, q-p);
1779 name[q-p] = '\0';
1780
1781 if (*q) q++; /* eat delimiter */
1782
1783 gtk_list_store_append(model, &iter);
1784 gtk_list_store_set(model, &iter, 0, name, -1);
1785
1786 p = q;
1787 }
1788
1789 w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
1790
1791 gtk_combo_box_set_active(GTK_COMBO_BOX(w), i->ival);
1792
1793 cr = gtk_cell_renderer_text_new();
1794 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), cr, TRUE);
1795 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), cr,
1796 "text", 0, NULL);
1797
1798 g_signal_connect(G_OBJECT(w), "changed",
1799 G_CALLBACK(droplist_sel), i);
1800 }
1801
1802#if GTK_CHECK_VERSION(3,0,0)
1803 gtk_grid_attach(GTK_GRID(table), w, 1, y, 1, 1);
1804 g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
1805#else
1806 gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
1807 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1808 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1809 3, 3);
1810#endif
1811 gtk_widget_show(w);
1812 break;
1813 }
1814
1815 y++;
1816 }
1817
1818 g_signal_connect(G_OBJECT(fe->cfgbox), "destroy",
1819 G_CALLBACK(window_destroy), NULL);
1820 g_signal_connect(G_OBJECT(fe->cfgbox), "key_press_event",
1821 G_CALLBACK(win_key_press), cancel);
1822 gtk_window_set_modal(GTK_WINDOW(fe->cfgbox), TRUE);
1823 gtk_window_set_transient_for(GTK_WINDOW(fe->cfgbox),
1824 GTK_WINDOW(fe->window));
1825 /* set_transient_window_pos(fe->window, fe->cfgbox); */
1826 gtk_widget_show(fe->cfgbox);
1827 gtk_main();
1828
1829 free_cfg(fe->cfg);
1830
1831 return fe->cfgret;
1832}
1833
1834static void menu_key_event(GtkMenuItem *menuitem, gpointer data)
1835{
1836 frontend *fe = (frontend *)data;
1837 int key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
1838 "user-data"));
1839 if (!midend_process_key(fe->me, 0, 0, key))
1840 gtk_widget_destroy(fe->window);
1841}
1842
1843static void get_size(frontend *fe, int *px, int *py)
1844{
1845 int x, y;
1846
1847 /*
1848 * Currently I don't want to make the GTK port scale large
1849 * puzzles to fit on the screen. This is because X does permit
1850 * extremely large windows and many window managers provide a
1851 * means of navigating round them, and the users I consulted
1852 * before deciding said that they'd rather have enormous puzzle
1853 * windows spanning multiple screen pages than have them
1854 * shrunk. I could change my mind later or introduce
1855 * configurability; this would be the place to do so, by
1856 * replacing the initial values of x and y with the screen
1857 * dimensions.
1858 */
1859 x = INT_MAX;
1860 y = INT_MAX;
1861 midend_size(fe->me, &x, &y, FALSE);
1862 *px = x;
1863 *py = y;
1864}
1865
1866#if !GTK_CHECK_VERSION(2,0,0)
1867#define gtk_window_resize(win, x, y) \
1868 gdk_window_resize(GTK_WIDGET(win)->window, x, y)
1869#endif
1870
1871/*
1872 * Called when any other code in this file has changed the
1873 * selected game parameters.
1874 */
1875static void changed_preset(frontend *fe)
1876{
1877 int n = midend_which_preset(fe->me);
1878
1879 fe->preset_threaded = TRUE;
1880 if (n < 0 && fe->preset_custom) {
1881 gtk_check_menu_item_set_active(
1882 GTK_CHECK_MENU_ITEM(fe->preset_custom),
1883 TRUE);
1884 } else {
1885 GSList *gs = fe->preset_radio;
1886 GSList *found = NULL;
1887
1888 for (gs = fe->preset_radio; gs; gs = gs->next) {
1889 struct preset_menu_entry *entry =
1890 (struct preset_menu_entry *)g_object_get_data(
1891 G_OBJECT(gs->data), "user-data");
1892
1893 if (entry && entry->id != n)
1894 gtk_check_menu_item_set_active(
1895 GTK_CHECK_MENU_ITEM(gs->data), FALSE);
1896 else
1897 found = gs;
1898 }
1899 if (found)
1900 gtk_check_menu_item_set_active(
1901 GTK_CHECK_MENU_ITEM(found->data), FALSE);
1902 }
1903 fe->preset_threaded = FALSE;
1904
1905 /*
1906 * Update the greying on the Copy menu option.
1907 */
1908 if (fe->copy_menu_item) {
1909 int enabled = midend_can_format_as_text_now(fe->me);
1910 gtk_widget_set_sensitive(fe->copy_menu_item, enabled);
1911 }
1912}
1913
1914#if !GTK_CHECK_VERSION(3,0,0)
1915static gboolean not_size_allocated_yet(GtkWidget *w)
1916{
1917 /*
1918 * This function tests whether a widget has not yet taken up space
1919 * on the screen which it will occupy in future. (Therefore, it
1920 * returns true only if the widget does exist but does not have a
1921 * size allocation. A null widget is already taking up all the
1922 * space it ever will.)
1923 */
1924 if (!w)
1925 return FALSE; /* nonexistent widgets aren't a problem */
1926
1927#if GTK_CHECK_VERSION(2,18,0) /* skip if no gtk_widget_get_allocation */
1928 {
1929 GtkAllocation a;
1930 gtk_widget_get_allocation(w, &a);
1931 if (a.height == 0 || a.width == 0)
1932 return TRUE; /* widget exists but has no size yet */
1933 }
1934#endif
1935
1936 return FALSE;
1937}
1938
1939static void try_shrink_drawing_area(frontend *fe)
1940{
1941 if (fe->drawing_area_shrink_pending &&
1942 (!fe->menubar_is_local || !not_size_allocated_yet(fe->menubar)) &&
1943 !not_size_allocated_yet(fe->statusbar)) {
1944 /*
1945 * In order to permit the user to resize the window smaller as
1946 * well as bigger, we call this function after the window size
1947 * has ended up where we want it. This shouldn't shrink the
1948 * window immediately; it just arranges that the next time the
1949 * user tries to shrink it, they can.
1950 *
1951 * However, at puzzle creation time, we defer the first of
1952 * these operations until after the menu bar and status bar
1953 * are actually visible. On Ubuntu 12.04 I've found that these
1954 * can take a while to be displayed, and that it's a mistake
1955 * to reduce the drawing area's size allocation before they've
1956 * turned up or else the drawing area makes room for them by
1957 * shrinking to less than the size we intended.
1958 */
1959 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1);
1960 fe->drawing_area_shrink_pending = FALSE;
1961 }
1962}
1963#endif /* !GTK_CHECK_VERSION(3,0,0) */
1964
1965static gint configure_window(GtkWidget *widget,
1966 GdkEventConfigure *event, gpointer data)
1967{
1968#if !GTK_CHECK_VERSION(3,0,0)
1969 /*
1970 * When the main puzzle window changes size, it might be because
1971 * the menu bar or status bar has turned up after starting off
1972 * absent, in which case we should have another go at enacting a
1973 * pending shrink of the drawing area.
1974 */
1975 frontend *fe = (frontend *)data;
1976 try_shrink_drawing_area(fe);
1977#endif
1978 return FALSE;
1979}
1980
1981#if GTK_CHECK_VERSION(3,0,0)
1982static int window_extra_height(frontend *fe)
1983{
1984 int ret = 0;
1985 if (fe->menubar) {
1986 GtkRequisition req;
1987 gtk_widget_get_preferred_size(fe->menubar, &req, NULL);
1988 ret += req.height;
1989 }
1990 if (fe->statusbar) {
1991 GtkRequisition req;
1992 gtk_widget_get_preferred_size(fe->statusbar, &req, NULL);
1993 ret += req.height;
1994 }
1995 return ret;
1996}
1997#endif
1998
1999static void resize_fe(frontend *fe)
2000{
2001 int x, y;
2002
2003 get_size(fe, &x, &y);
2004
2005#if GTK_CHECK_VERSION(3,0,0)
2006 gtk_window_resize(GTK_WINDOW(fe->window), x, y + window_extra_height(fe));
2007#else
2008 fe->drawing_area_shrink_pending = FALSE;
2009 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
2010 {
2011 GtkRequisition req;
2012 gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
2013 gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
2014 }
2015 fe->drawing_area_shrink_pending = TRUE;
2016 try_shrink_drawing_area(fe);
2017#endif
2018}
2019
2020static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
2021{
2022 frontend *fe = (frontend *)data;
2023 struct preset_menu_entry *entry =
2024 (struct preset_menu_entry *)g_object_get_data(
2025 G_OBJECT(menuitem), "user-data");
2026
2027 if (fe->preset_threaded ||
2028 (GTK_IS_CHECK_MENU_ITEM(menuitem) &&
2029 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))))
2030 return;
2031 midend_set_params(fe->me, entry->params);
2032 midend_new_game(fe->me);
2033 changed_preset(fe);
2034 resize_fe(fe);
2035 midend_redraw(fe->me);
2036}
2037
2038GdkAtom compound_text_atom, utf8_string_atom;
2039int paste_initialised = FALSE;
2040
2041static void set_selection(frontend *fe, GdkAtom selection)
2042{
2043 if (!paste_initialised) {
2044 compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
2045 utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
2046 paste_initialised = TRUE;
2047 }
2048
2049 /*
2050 * For this simple application we can safely assume that the
2051 * data passed to this function is pure ASCII, which means we
2052 * can return precisely the same stuff for types STRING,
2053 * COMPOUND_TEXT or UTF8_STRING.
2054 */
2055
2056 if (gtk_selection_owner_set(fe->area, selection, CurrentTime)) {
2057 gtk_selection_clear_targets(fe->area, selection);
2058 gtk_selection_add_target(fe->area, selection,
2059 GDK_SELECTION_TYPE_STRING, 1);
2060 gtk_selection_add_target(fe->area, selection, compound_text_atom, 1);
2061 gtk_selection_add_target(fe->area, selection, utf8_string_atom, 1);
2062 }
2063}
2064
2065void write_clip(frontend *fe, char *data)
2066{
2067 if (fe->paste_data)
2068 sfree(fe->paste_data);
2069
2070 fe->paste_data = data;
2071 fe->paste_data_len = strlen(data);
2072
2073 set_selection(fe, GDK_SELECTION_PRIMARY);
2074 set_selection(fe, GDK_SELECTION_CLIPBOARD);
2075}
2076
2077void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
2078 guint info, guint time_stamp, gpointer data)
2079{
2080 frontend *fe = (frontend *)data;
2081 gtk_selection_data_set(seldata, gtk_selection_data_get_target(seldata), 8,
2082 fe->paste_data, fe->paste_data_len);
2083}
2084
2085gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
2086 gpointer data)
2087{
2088 frontend *fe = (frontend *)data;
2089
2090 if (fe->paste_data)
2091 sfree(fe->paste_data);
2092 fe->paste_data = NULL;
2093 fe->paste_data_len = 0;
2094 return TRUE;
2095}
2096
2097static void menu_copy_event(GtkMenuItem *menuitem, gpointer data)
2098{
2099 frontend *fe = (frontend *)data;
2100 char *text;
2101
2102 text = midend_text_format(fe->me);
2103
2104 if (text) {
2105 write_clip(fe, text);
2106 } else {
2107 gdk_beep();
2108 }
2109}
2110
2111#ifdef OLD_FILESEL
2112
2113static void filesel_ok(GtkButton *button, gpointer data)
2114{
2115 frontend *fe = (frontend *)data;
2116
2117 gpointer filesel = g_object_get_data(G_OBJECT(button), "user-data");
2118
2119 const char *name =
2120 gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
2121
2122 fe->filesel_name = dupstr(name);
2123}
2124
2125static char *file_selector(frontend *fe, char *title, int save)
2126{
2127 GtkWidget *filesel =
2128 gtk_file_selection_new(title);
2129
2130 fe->filesel_name = NULL;
2131
2132 gtk_window_set_modal(GTK_WINDOW(filesel), TRUE);
2133 g_object_set_data
2134 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",
2135 (gpointer)filesel);
2136 g_signal_connect
2137 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
2138 G_CALLBACK(filesel_ok), fe);
2139 g_signal_connect_swapped
2140 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
2141 G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
2142 g_signal_connect_object
2143 (G_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",
2144 G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
2145 g_signal_connect(G_OBJECT(filesel), "destroy",
2146 G_CALLBACK(window_destroy), NULL);
2147 gtk_widget_show(filesel);
2148 gtk_window_set_transient_for(GTK_WINDOW(filesel), GTK_WINDOW(fe->window));
2149 gtk_main();
2150
2151 return fe->filesel_name;
2152}
2153
2154#else
2155
2156static char *file_selector(frontend *fe, char *title, int save)
2157{
2158 char *filesel_name = NULL;
2159
2160 GtkWidget *filesel =
2161 gtk_file_chooser_dialog_new(title,
2162 GTK_WINDOW(fe->window),
2163 save ? GTK_FILE_CHOOSER_ACTION_SAVE :
2164 GTK_FILE_CHOOSER_ACTION_OPEN,
2165 LABEL_CANCEL, GTK_RESPONSE_CANCEL,
2166 save ? LABEL_SAVE : LABEL_OPEN,
2167 GTK_RESPONSE_ACCEPT,
2168 NULL);
2169
2170 if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
2171 char *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
2172 filesel_name = dupstr(name);
2173 g_free(name);
2174 }
2175
2176 gtk_widget_destroy(filesel);
2177
2178 return filesel_name;
2179}
2180
2181#endif
2182
2183struct savefile_write_ctx {
2184 FILE *fp;
2185 int error;
2186};
2187
2188static void savefile_write(void *wctx, void *buf, int len)
2189{
2190 struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)wctx;
2191 if (fwrite(buf, 1, len, ctx->fp) < len)
2192 ctx->error = errno;
2193}
2194
2195static int savefile_read(void *wctx, void *buf, int len)
2196{
2197 FILE *fp = (FILE *)wctx;
2198 int ret;
2199
2200 ret = fread(buf, 1, len, fp);
2201 return (ret == len);
2202}
2203
2204static void menu_save_event(GtkMenuItem *menuitem, gpointer data)
2205{
2206 frontend *fe = (frontend *)data;
2207 char *name;
2208
2209 name = file_selector(fe, "Enter name of game file to save", TRUE);
2210
2211 if (name) {
2212 FILE *fp;
2213
2214 if ((fp = fopen(name, "r")) != NULL) {
2215 char buf[256 + FILENAME_MAX];
2216 fclose(fp);
2217 /* file exists */
2218
2219 sprintf(buf, "Are you sure you want to overwrite the"
2220 " file \"%.*s\"?",
2221 FILENAME_MAX, name);
2222 if (!message_box(fe->window, "Question", buf, TRUE, MB_YESNO))
2223 goto free_and_return;
2224 }
2225
2226 fp = fopen(name, "w");
2227
2228 if (!fp) {
2229 error_box(fe->window, "Unable to open save file");
2230 goto free_and_return;
2231 }
2232
2233 {
2234 struct savefile_write_ctx ctx;
2235 ctx.fp = fp;
2236 ctx.error = 0;
2237 midend_serialise(fe->me, savefile_write, &ctx);
2238 fclose(fp);
2239 if (ctx.error) {
2240 char boxmsg[512];
2241 sprintf(boxmsg, "Error writing save file: %.400s",
2242 strerror(errno));
2243 error_box(fe->window, boxmsg);
2244 goto free_and_return;
2245 }
2246 }
2247 free_and_return:
2248 sfree(name);
2249 }
2250}
2251
2252static void menu_load_event(GtkMenuItem *menuitem, gpointer data)
2253{
2254 frontend *fe = (frontend *)data;
2255 char *name, *err;
2256
2257 name = file_selector(fe, "Enter name of saved game file to load", FALSE);
2258
2259 if (name) {
2260 FILE *fp = fopen(name, "r");
2261 sfree(name);
2262
2263 if (!fp) {
2264 error_box(fe->window, "Unable to open saved game file");
2265 return;
2266 }
2267
2268 err = midend_deserialise(fe->me, savefile_read, fp);
2269
2270 fclose(fp);
2271
2272 if (err) {
2273 error_box(fe->window, err);
2274 return;
2275 }
2276
2277 changed_preset(fe);
2278 resize_fe(fe);
2279 midend_redraw(fe->me);
2280 }
2281}
2282
2283static void menu_solve_event(GtkMenuItem *menuitem, gpointer data)
2284{
2285 frontend *fe = (frontend *)data;
2286 char *msg;
2287
2288 msg = midend_solve(fe->me);
2289
2290 if (msg)
2291 error_box(fe->window, msg);
2292}
2293
2294static void menu_restart_event(GtkMenuItem *menuitem, gpointer data)
2295{
2296 frontend *fe = (frontend *)data;
2297
2298 midend_restart_game(fe->me);
2299}
2300
2301static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
2302{
2303 frontend *fe = (frontend *)data;
2304 int which = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
2305 "user-data"));
2306
2307 if (fe->preset_threaded ||
2308 (GTK_IS_CHECK_MENU_ITEM(menuitem) &&
2309 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))))
2310 return;
2311 changed_preset(fe); /* Put the old preset back! */
2312 if (!get_config(fe, which))
2313 return;
2314
2315 midend_new_game(fe->me);
2316 resize_fe(fe);
2317 midend_redraw(fe->me);
2318}
2319
2320static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
2321{
2322 frontend *fe = (frontend *)data;
2323
2324#if GTK_CHECK_VERSION(3,0,0)
2325 extern char *const *const xpm_icons[];
2326 extern const int n_xpm_icons;
2327 GdkPixbuf *icon = gdk_pixbuf_new_from_xpm_data
2328 ((const gchar **)xpm_icons[n_xpm_icons-1]);
2329 gtk_show_about_dialog
2330 (GTK_WINDOW(fe->window),
2331 "program-name", thegame.name,
2332 "version", ver,
2333 "comments", "Part of Simon Tatham's Portable Puzzle Collection",
2334 "logo", icon,
2335 (const gchar *)NULL);
2336 g_object_unref(G_OBJECT(icon));
2337#else
2338 char titlebuf[256];
2339 char textbuf[1024];
2340
2341 sprintf(titlebuf, "About %.200s", thegame.name);
2342 sprintf(textbuf,
2343 "%.200s\n\n"
2344 "from Simon Tatham's Portable Puzzle Collection\n\n"
2345 "%.500s", thegame.name, ver);
2346
2347 message_box(fe->window, titlebuf, textbuf, TRUE, MB_OK);
2348#endif
2349}
2350
2351static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont,
2352 char *text, int key)
2353{
2354 GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
2355 int keyqual;
2356 gtk_container_add(cont, menuitem);
2357 g_object_set_data(G_OBJECT(menuitem), "user-data", GINT_TO_POINTER(key));
2358 g_signal_connect(G_OBJECT(menuitem), "activate",
2359 G_CALLBACK(menu_key_event), fe);
2360 switch (key & ~0x1F) {
2361 case 0x00:
2362 key += 0x60;
2363 keyqual = GDK_CONTROL_MASK;
2364 break;
2365 case 0x40:
2366 key += 0x20;
2367 keyqual = GDK_SHIFT_MASK;
2368 break;
2369 default:
2370 keyqual = 0;
2371 break;
2372 }
2373 gtk_widget_add_accelerator(menuitem,
2374 "activate", fe->accelgroup,
2375 key, keyqual,
2376 GTK_ACCEL_VISIBLE);
2377 gtk_widget_show(menuitem);
2378 return menuitem;
2379}
2380
2381static void add_menu_separator(GtkContainer *cont)
2382{
2383 GtkWidget *menuitem = gtk_menu_item_new();
2384 gtk_container_add(cont, menuitem);
2385 gtk_widget_show(menuitem);
2386}
2387
2388static void populate_gtk_preset_menu(frontend *fe, struct preset_menu *menu,
2389 GtkWidget *gtkmenu)
2390{
2391 int i;
2392
2393 for (i = 0; i < menu->n_entries; i++) {
2394 struct preset_menu_entry *entry = &menu->entries[i];
2395 GtkWidget *menuitem;
2396
2397 if (entry->params) {
2398 menuitem = gtk_radio_menu_item_new_with_label(
2399 fe->preset_radio, entry->title);
2400 fe->preset_radio = gtk_radio_menu_item_get_group(
2401 GTK_RADIO_MENU_ITEM(menuitem));
2402 g_object_set_data(G_OBJECT(menuitem), "user-data", entry);
2403 g_signal_connect(G_OBJECT(menuitem), "activate",
2404 G_CALLBACK(menu_preset_event), fe);
2405 } else {
2406 GtkWidget *submenu;
2407 menuitem = gtk_menu_item_new_with_label(entry->title);
2408 submenu = gtk_menu_new();
2409 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
2410 populate_gtk_preset_menu(fe, entry->submenu, submenu);
2411 }
2412
2413 gtk_container_add(GTK_CONTAINER(gtkmenu), menuitem);
2414 gtk_widget_show(menuitem);
2415 }
2416}
2417
2418enum { ARG_EITHER, ARG_SAVE, ARG_ID }; /* for argtype */
2419
2420static frontend *new_window(char *arg, int argtype, char **error)
2421{
2422 frontend *fe;
2423 GtkBox *vbox, *hbox;
2424 GtkWidget *menu, *menuitem;
2425 GList *iconlist;
2426 int x, y, n;
2427 char errbuf[1024];
2428 extern char *const *const xpm_icons[];
2429 extern const int n_xpm_icons;
2430 struct preset_menu *preset_menu;
2431
2432 fe = snew(frontend);
2433#if GTK_CHECK_VERSION(3,20,0)
2434 fe->css_provider = NULL;
2435#endif
2436
2437 fe->timer_active = FALSE;
2438 fe->timer_id = -1;
2439
2440 fe->me = midend_new(fe, &thegame, &gtk_drawing, fe);
2441
2442 if (arg) {
2443 char *err;
2444 FILE *fp;
2445
2446 errbuf[0] = '\0';
2447
2448 switch (argtype) {
2449 case ARG_ID:
2450 err = midend_game_id(fe->me, arg);
2451 if (!err)
2452 midend_new_game(fe->me);
2453 else
2454 sprintf(errbuf, "Invalid game ID: %.800s", err);
2455 break;
2456 case ARG_SAVE:
2457 fp = fopen(arg, "r");
2458 if (!fp) {
2459 sprintf(errbuf, "Error opening file: %.800s", strerror(errno));
2460 } else {
2461 err = midend_deserialise(fe->me, savefile_read, fp);
2462 if (err)
2463 sprintf(errbuf, "Invalid save file: %.800s", err);
2464 fclose(fp);
2465 }
2466 break;
2467 default /*case ARG_EITHER*/:
2468 /*
2469 * First try treating the argument as a game ID.
2470 */
2471 err = midend_game_id(fe->me, arg);
2472 if (!err) {
2473 /*
2474 * It's a valid game ID.
2475 */
2476 midend_new_game(fe->me);
2477 } else {
2478 FILE *fp = fopen(arg, "r");
2479 if (!fp) {
2480 sprintf(errbuf, "Supplied argument is neither a game ID (%.400s)"
2481 " nor a save file (%.400s)", err, strerror(errno));
2482 } else {
2483 err = midend_deserialise(fe->me, savefile_read, fp);
2484 if (err)
2485 sprintf(errbuf, "%.800s", err);
2486 fclose(fp);
2487 }
2488 }
2489 break;
2490 }
2491 if (*errbuf) {
2492 *error = dupstr(errbuf);
2493 midend_free(fe->me);
2494 sfree(fe);
2495 return NULL;
2496 }
2497
2498 } else {
2499 midend_new_game(fe->me);
2500 }
2501
2502#if !GTK_CHECK_VERSION(3,0,0)
2503 {
2504 /*
2505 * try_shrink_drawing_area() will do some fiddling with the
2506 * window size request (see comment in that function) after
2507 * all the bits and pieces such as the menu bar and status bar
2508 * have appeared in the puzzle window.
2509 *
2510 * However, on Unity systems, the menu bar _doesn't_ appear in
2511 * the puzzle window, because the Unity shell hijacks it into
2512 * the menu bar at the very top of the screen. We therefore
2513 * try to detect that situation here, so that we don't sit
2514 * here forever waiting for a menu bar.
2515 */
2516 const char prop[] = "gtk-shell-shows-menubar";
2517 GtkSettings *settings = gtk_settings_get_default();
2518 if (!g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
2519 prop)) {
2520 fe->menubar_is_local = TRUE;
2521 } else {
2522 int unity_mode;
2523 g_object_get(gtk_settings_get_default(),
2524 prop, &unity_mode,
2525 (const gchar *)NULL);
2526 fe->menubar_is_local = !unity_mode;
2527 }
2528 }
2529#endif
2530
2531 fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2532 gtk_window_set_title(GTK_WINDOW(fe->window), thegame.name);
2533
2534 vbox = GTK_BOX(gtk_vbox_new(FALSE, 0));
2535 gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox));
2536 gtk_widget_show(GTK_WIDGET(vbox));
2537
2538 fe->accelgroup = gtk_accel_group_new();
2539 gtk_window_add_accel_group(GTK_WINDOW(fe->window), fe->accelgroup);
2540
2541 hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
2542 gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0);
2543 gtk_widget_show(GTK_WIDGET(hbox));
2544
2545 fe->menubar = gtk_menu_bar_new();
2546 gtk_box_pack_start(hbox, fe->menubar, TRUE, TRUE, 0);
2547 gtk_widget_show(fe->menubar);
2548
2549 menuitem = gtk_menu_item_new_with_mnemonic("_Game");
2550 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
2551 gtk_widget_show(menuitem);
2552
2553 menu = gtk_menu_new();
2554 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2555
2556 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n');
2557
2558 menuitem = gtk_menu_item_new_with_label("Restart");
2559 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2560 g_signal_connect(G_OBJECT(menuitem), "activate",
2561 G_CALLBACK(menu_restart_event), fe);
2562 gtk_widget_show(menuitem);
2563
2564 menuitem = gtk_menu_item_new_with_label("Specific...");
2565 g_object_set_data(G_OBJECT(menuitem), "user-data",
2566 GINT_TO_POINTER(CFG_DESC));
2567 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2568 g_signal_connect(G_OBJECT(menuitem), "activate",
2569 G_CALLBACK(menu_config_event), fe);
2570 gtk_widget_show(menuitem);
2571
2572 menuitem = gtk_menu_item_new_with_label("Random Seed...");
2573 g_object_set_data(G_OBJECT(menuitem), "user-data",
2574 GINT_TO_POINTER(CFG_SEED));
2575 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2576 g_signal_connect(G_OBJECT(menuitem), "activate",
2577 G_CALLBACK(menu_config_event), fe);
2578 gtk_widget_show(menuitem);
2579
2580 fe->preset_radio = NULL;
2581 fe->preset_custom = NULL;
2582 fe->preset_threaded = FALSE;
2583
2584 preset_menu = midend_get_presets(fe->me, NULL);
2585 if (preset_menu->n_entries > 0 || thegame.can_configure) {
2586 GtkWidget *submenu;
2587
2588 menuitem = gtk_menu_item_new_with_mnemonic("_Type");
2589 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
2590 gtk_widget_show(menuitem);
2591
2592 submenu = gtk_menu_new();
2593 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
2594
2595 populate_gtk_preset_menu(fe, preset_menu, submenu);
2596
2597 if (thegame.can_configure) {
2598 menuitem = fe->preset_custom =
2599 gtk_radio_menu_item_new_with_label(fe->preset_radio,
2600 "Custom...");
2601 fe->preset_radio =
2602 gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
2603 gtk_container_add(GTK_CONTAINER(submenu), menuitem);
2604 g_object_set_data(G_OBJECT(menuitem), "user-data",
2605 GINT_TO_POINTER(CFG_SETTINGS));
2606 g_signal_connect(G_OBJECT(menuitem), "activate",
2607 G_CALLBACK(menu_config_event), fe);
2608 gtk_widget_show(menuitem);
2609 }
2610
2611 }
2612
2613 add_menu_separator(GTK_CONTAINER(menu));
2614 menuitem = gtk_menu_item_new_with_label("Load...");
2615 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2616 g_signal_connect(G_OBJECT(menuitem), "activate",
2617 G_CALLBACK(menu_load_event), fe);
2618 gtk_widget_show(menuitem);
2619 menuitem = gtk_menu_item_new_with_label("Save...");
2620 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2621 g_signal_connect(G_OBJECT(menuitem), "activate",
2622 G_CALLBACK(menu_save_event), fe);
2623 gtk_widget_show(menuitem);
2624#ifndef STYLUS_BASED
2625 add_menu_separator(GTK_CONTAINER(menu));
2626 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u');
2627 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", 'r');
2628#endif
2629 if (thegame.can_format_as_text_ever) {
2630 add_menu_separator(GTK_CONTAINER(menu));
2631 menuitem = gtk_menu_item_new_with_label("Copy");
2632 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2633 g_signal_connect(G_OBJECT(menuitem), "activate",
2634 G_CALLBACK(menu_copy_event), fe);
2635 gtk_widget_show(menuitem);
2636 fe->copy_menu_item = menuitem;
2637 } else {
2638 fe->copy_menu_item = NULL;
2639 }
2640 if (thegame.can_solve) {
2641 add_menu_separator(GTK_CONTAINER(menu));
2642 menuitem = gtk_menu_item_new_with_label("Solve");
2643 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2644 g_signal_connect(G_OBJECT(menuitem), "activate",
2645 G_CALLBACK(menu_solve_event), fe);
2646 gtk_widget_show(menuitem);
2647 }
2648 add_menu_separator(GTK_CONTAINER(menu));
2649 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
2650
2651 menuitem = gtk_menu_item_new_with_mnemonic("_Help");
2652 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
2653 gtk_widget_show(menuitem);
2654
2655 menu = gtk_menu_new();
2656 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2657
2658 menuitem = gtk_menu_item_new_with_label("About");
2659 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2660 g_signal_connect(G_OBJECT(menuitem), "activate",
2661 G_CALLBACK(menu_about_event), fe);
2662 gtk_widget_show(menuitem);
2663
2664#ifdef STYLUS_BASED
2665 menuitem=gtk_button_new_with_mnemonic("_Redo");
2666 g_object_set_data(G_OBJECT(menuitem), "user-data",
2667 GINT_TO_POINTER((int)('r')));
2668 g_signal_connect(G_OBJECT(menuitem), "clicked",
2669 G_CALLBACK(menu_key_event), fe);
2670 gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0);
2671 gtk_widget_show(menuitem);
2672
2673 menuitem=gtk_button_new_with_mnemonic("_Undo");
2674 g_object_set_data(G_OBJECT(menuitem), "user-data",
2675 GINT_TO_POINTER((int)('u')));
2676 g_signal_connect(G_OBJECT(menuitem), "clicked",
2677 G_CALLBACK(menu_key_event), fe);
2678 gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0);
2679 gtk_widget_show(menuitem);
2680
2681 if (thegame.flags & REQUIRE_NUMPAD) {
2682 hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
2683 gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0);
2684 gtk_widget_show(GTK_WIDGET(hbox));
2685
2686 *((int*)errbuf)=0;
2687 errbuf[1]='\0';
2688 for(errbuf[0]='0';errbuf[0]<='9';errbuf[0]++) {
2689 menuitem=gtk_button_new_with_label(errbuf);
2690 g_object_set_data(G_OBJECT(menuitem), "user-data",
2691 GINT_TO_POINTER((int)(errbuf[0])));
2692 g_signal_connect(G_OBJECT(menuitem), "clicked",
2693 G_CALLBACK(menu_key_event), fe);
2694 gtk_box_pack_start(hbox, menuitem, TRUE, TRUE, 0);
2695 gtk_widget_show(menuitem);
2696 }
2697 }
2698#endif /* STYLUS_BASED */
2699
2700 changed_preset(fe);
2701
2702 snaffle_colours(fe);
2703
2704 if (midend_wants_statusbar(fe->me)) {
2705 GtkWidget *viewport;
2706 GtkRequisition req;
2707
2708 viewport = gtk_viewport_new(NULL, NULL);
2709 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
2710 fe->statusbar = gtk_statusbar_new();
2711 gtk_container_add(GTK_CONTAINER(viewport), fe->statusbar);
2712 gtk_widget_show(viewport);
2713 gtk_box_pack_end(vbox, viewport, FALSE, FALSE, 0);
2714 gtk_widget_show(fe->statusbar);
2715 fe->statusctx = gtk_statusbar_get_context_id
2716 (GTK_STATUSBAR(fe->statusbar), "game");
2717 gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx,
2718 DEFAULT_STATUSBAR_TEXT);
2719#if GTK_CHECK_VERSION(3,0,0)
2720 gtk_widget_get_preferred_size(fe->statusbar, &req, NULL);
2721#else
2722 gtk_widget_size_request(fe->statusbar, &req);
2723#endif
2724 gtk_widget_set_size_request(viewport, -1, req.height);
2725 } else
2726 fe->statusbar = NULL;
2727
2728 fe->area = gtk_drawing_area_new();
2729#if GTK_CHECK_VERSION(2,0,0) && !GTK_CHECK_VERSION(3,0,0)
2730 gtk_widget_set_double_buffered(fe->area, FALSE);
2731#endif
2732 {
2733 GdkGeometry geom;
2734 geom.base_width = 0;
2735#if GTK_CHECK_VERSION(3,0,0)
2736 geom.base_height = window_extra_height(fe);
2737 gtk_window_set_geometry_hints(GTK_WINDOW(fe->window), NULL,
2738 &geom, GDK_HINT_BASE_SIZE);
2739#else
2740 geom.base_height = 0;
2741 gtk_window_set_geometry_hints(GTK_WINDOW(fe->window), fe->area,
2742 &geom, GDK_HINT_BASE_SIZE);
2743#endif
2744 }
2745 fe->w = -1;
2746 fe->h = -1;
2747 get_size(fe, &x, &y);
2748#if GTK_CHECK_VERSION(3,0,0)
2749 gtk_window_set_default_size(GTK_WINDOW(fe->window),
2750 x, y + window_extra_height(fe));
2751#else
2752 fe->drawing_area_shrink_pending = FALSE;
2753 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
2754#endif
2755
2756 gtk_box_pack_end(vbox, fe->area, TRUE, TRUE, 0);
2757
2758 clear_backing_store(fe);
2759 fe->fonts = NULL;
2760 fe->nfonts = fe->fontsize = 0;
2761
2762 fe->paste_data = NULL;
2763 fe->paste_data_len = 0;
2764
2765 g_signal_connect(G_OBJECT(fe->window), "destroy",
2766 G_CALLBACK(destroy), fe);
2767 g_signal_connect(G_OBJECT(fe->window), "key_press_event",
2768 G_CALLBACK(key_event), fe);
2769 g_signal_connect(G_OBJECT(fe->area), "button_press_event",
2770 G_CALLBACK(button_event), fe);
2771 g_signal_connect(G_OBJECT(fe->area), "button_release_event",
2772 G_CALLBACK(button_event), fe);
2773 g_signal_connect(G_OBJECT(fe->area), "motion_notify_event",
2774 G_CALLBACK(motion_event), fe);
2775 g_signal_connect(G_OBJECT(fe->area), "selection_get",
2776 G_CALLBACK(selection_get), fe);
2777 g_signal_connect(G_OBJECT(fe->area), "selection_clear_event",
2778 G_CALLBACK(selection_clear), fe);
2779#if GTK_CHECK_VERSION(3,0,0)
2780 g_signal_connect(G_OBJECT(fe->area), "draw",
2781 G_CALLBACK(draw_area), fe);
2782#else
2783 g_signal_connect(G_OBJECT(fe->area), "expose_event",
2784 G_CALLBACK(expose_area), fe);
2785#endif
2786 g_signal_connect(G_OBJECT(fe->window), "map_event",
2787 G_CALLBACK(map_window), fe);
2788 g_signal_connect(G_OBJECT(fe->area), "configure_event",
2789 G_CALLBACK(configure_area), fe);
2790 g_signal_connect(G_OBJECT(fe->window), "configure_event",
2791 G_CALLBACK(configure_window), fe);
2792
2793 gtk_widget_add_events(GTK_WIDGET(fe->area),
2794 GDK_BUTTON_PRESS_MASK |
2795 GDK_BUTTON_RELEASE_MASK |
2796 GDK_BUTTON_MOTION_MASK |
2797 GDK_POINTER_MOTION_HINT_MASK);
2798
2799 if (n_xpm_icons) {
2800 gtk_window_set_icon(GTK_WINDOW(fe->window),
2801 gdk_pixbuf_new_from_xpm_data
2802 ((const gchar **)xpm_icons[0]));
2803
2804 iconlist = NULL;
2805 for (n = 0; n < n_xpm_icons; n++) {
2806 iconlist =
2807 g_list_append(iconlist,
2808 gdk_pixbuf_new_from_xpm_data((const gchar **)
2809 xpm_icons[n]));
2810 }
2811 gtk_window_set_icon_list(GTK_WINDOW(fe->window), iconlist);
2812 }
2813
2814 gtk_widget_show(fe->area);
2815 gtk_widget_show(fe->window);
2816
2817#if !GTK_CHECK_VERSION(3,0,0)
2818 fe->drawing_area_shrink_pending = TRUE;
2819 try_shrink_drawing_area(fe);
2820#endif
2821
2822 set_window_background(fe, 0);
2823
2824 return fe;
2825}
2826
2827char *fgetline(FILE *fp)
2828{
2829 char *ret = snewn(512, char);
2830 int size = 512, len = 0;
2831 while (fgets(ret + len, size - len, fp)) {
2832 len += strlen(ret + len);
2833 if (ret[len-1] == '\n')
2834 break; /* got a newline, we're done */
2835 size = len + 512;
2836 ret = sresize(ret, size, char);
2837 }
2838 if (len == 0) { /* first fgets returned NULL */
2839 sfree(ret);
2840 return NULL;
2841 }
2842 ret[len] = '\0';
2843 return ret;
2844}
2845
2846static void list_presets_from_menu(struct preset_menu *menu)
2847{
2848 int i;
2849
2850 for (i = 0; i < menu->n_entries; i++) {
2851 if (menu->entries[i].params) {
2852 char *paramstr = thegame.encode_params(
2853 menu->entries[i].params, TRUE);
2854 printf("%s %s\n", paramstr, menu->entries[i].title);
2855 sfree(paramstr);
2856 } else {
2857 list_presets_from_menu(menu->entries[i].submenu);
2858 }
2859 }
2860}
2861
2862int main(int argc, char **argv)
2863{
2864 char *pname = argv[0];
2865 char *error;
2866 int ngenerate = 0, print = FALSE, px = 1, py = 1;
2867 int time_generation = FALSE, test_solve = FALSE, list_presets = FALSE;
2868 int soln = FALSE, colour = FALSE;
2869 float scale = 1.0F;
2870 float redo_proportion = 0.0F;
2871 char *savefile = NULL, *savesuffix = NULL;
2872 char *arg = NULL;
2873 int argtype = ARG_EITHER;
2874 char *screenshot_file = NULL;
2875 int doing_opts = TRUE;
2876 int ac = argc;
2877 char **av = argv;
2878 char errbuf[500];
2879
2880 /*
2881 * Command line parsing in this function is rather fiddly,
2882 * because GTK wants to have a go at argc/argv _first_ - and
2883 * yet we can't let it, because gtk_init() will bomb out if it
2884 * can't open an X display, whereas in fact we want to permit
2885 * our --generate and --print modes to run without an X
2886 * display.
2887 *
2888 * So what we do is:
2889 * - we parse the command line ourselves, without modifying
2890 * argc/argv
2891 * - if we encounter an error which might plausibly be the
2892 * result of a GTK command line (i.e. not detailed errors in
2893 * particular options of ours) we store the error message
2894 * and terminate parsing.
2895 * - if we got enough out of the command line to know it
2896 * specifies a non-X mode of operation, we either display
2897 * the stored error and return failure, or if there is no
2898 * stored error we do the non-X operation and return
2899 * success.
2900 * - otherwise, we go straight to gtk_init().
2901 */
2902
2903 errbuf[0] = '\0';
2904 while (--ac > 0) {
2905 char *p = *++av;
2906 if (doing_opts && !strcmp(p, "--version")) {
2907 printf("%s, from Simon Tatham's Portable Puzzle Collection\n%s\n",
2908 thegame.name, ver);
2909 return 0;
2910 } else if (doing_opts && !strcmp(p, "--generate")) {
2911 if (--ac > 0) {
2912 ngenerate = atoi(*++av);
2913 if (!ngenerate) {
2914 fprintf(stderr, "%s: '--generate' expected a number\n",
2915 pname);
2916 return 1;
2917 }
2918 } else
2919 ngenerate = 1;
2920 } else if (doing_opts && !strcmp(p, "--time-generation")) {
2921 time_generation = TRUE;
2922 } else if (doing_opts && !strcmp(p, "--test-solve")) {
2923 test_solve = TRUE;
2924 } else if (doing_opts && !strcmp(p, "--list-presets")) {
2925 list_presets = TRUE;
2926 } else if (doing_opts && !strcmp(p, "--save")) {
2927 if (--ac > 0) {
2928 savefile = *++av;
2929 } else {
2930 fprintf(stderr, "%s: '--save' expected a filename\n",
2931 pname);
2932 return 1;
2933 }
2934 } else if (doing_opts && (!strcmp(p, "--save-suffix") ||
2935 !strcmp(p, "--savesuffix"))) {
2936 if (--ac > 0) {
2937 savesuffix = *++av;
2938 } else {
2939 fprintf(stderr, "%s: '--save-suffix' expected a filename\n",
2940 pname);
2941 return 1;
2942 }
2943 } else if (doing_opts && !strcmp(p, "--print")) {
2944 if (!thegame.can_print) {
2945 fprintf(stderr, "%s: this game does not support printing\n",
2946 pname);
2947 return 1;
2948 }
2949 print = TRUE;
2950 if (--ac > 0) {
2951 char *dim = *++av;
2952 if (sscanf(dim, "%dx%d", &px, &py) != 2) {
2953 fprintf(stderr, "%s: unable to parse argument '%s' to "
2954 "'--print'\n", pname, dim);
2955 return 1;
2956 }
2957 } else {
2958 px = py = 1;
2959 }
2960 } else if (doing_opts && !strcmp(p, "--scale")) {
2961 if (--ac > 0) {
2962 scale = atof(*++av);
2963 } else {
2964 fprintf(stderr, "%s: no argument supplied to '--scale'\n",
2965 pname);
2966 return 1;
2967 }
2968 } else if (doing_opts && !strcmp(p, "--redo")) {
2969 /*
2970 * This is an internal option which I don't expect
2971 * users to have any particular use for. The effect of
2972 * --redo is that once the game has been loaded and
2973 * initialised, the next move in the redo chain is
2974 * replayed, and the game screen is redrawn part way
2975 * through the making of the move. This is only
2976 * meaningful if there _is_ a next move in the redo
2977 * chain, which means in turn that this option is only
2978 * useful if you're also passing a save file on the
2979 * command line.
2980 *
2981 * This option is used by the script which generates
2982 * the puzzle icons and website screenshots, and I
2983 * don't imagine it's useful for anything else.
2984 * (Unless, I suppose, users don't like my screenshots
2985 * and want to generate their own in the same way for
2986 * some repackaged version of the puzzles.)
2987 */
2988 if (--ac > 0) {
2989 redo_proportion = atof(*++av);
2990 } else {
2991 fprintf(stderr, "%s: no argument supplied to '--redo'\n",
2992 pname);
2993 return 1;
2994 }
2995 } else if (doing_opts && !strcmp(p, "--screenshot")) {
2996 /*
2997 * Another internal option for the icon building
2998 * script. This causes a screenshot of the central
2999 * drawing area (i.e. not including the menu bar or
3000 * status bar) to be saved to a PNG file once the
3001 * window has been drawn, and then the application
3002 * quits immediately.
3003 */
3004 if (--ac > 0) {
3005 screenshot_file = *++av;
3006 } else {
3007 fprintf(stderr, "%s: no argument supplied to '--screenshot'\n",
3008 pname);
3009 return 1;
3010 }
3011 } else if (doing_opts && (!strcmp(p, "--with-solutions") ||
3012 !strcmp(p, "--with-solution") ||
3013 !strcmp(p, "--with-solns") ||
3014 !strcmp(p, "--with-soln") ||
3015 !strcmp(p, "--solutions") ||
3016 !strcmp(p, "--solution") ||
3017 !strcmp(p, "--solns") ||
3018 !strcmp(p, "--soln"))) {
3019 soln = TRUE;
3020 } else if (doing_opts && !strcmp(p, "--colour")) {
3021 if (!thegame.can_print_in_colour) {
3022 fprintf(stderr, "%s: this game does not support colour"
3023 " printing\n", pname);
3024 return 1;
3025 }
3026 colour = TRUE;
3027 } else if (doing_opts && !strcmp(p, "--load")) {
3028 argtype = ARG_SAVE;
3029 } else if (doing_opts && !strcmp(p, "--game")) {
3030 argtype = ARG_ID;
3031 } else if (doing_opts && !strcmp(p, "--")) {
3032 doing_opts = FALSE;
3033 } else if (!doing_opts || p[0] != '-') {
3034 if (arg) {
3035 fprintf(stderr, "%s: more than one argument supplied\n",
3036 pname);
3037 return 1;
3038 }
3039 arg = p;
3040 } else {
3041 sprintf(errbuf, "%.100s: unrecognised option '%.100s'\n",
3042 pname, p);
3043 break;
3044 }
3045 }
3046
3047 /*
3048 * Special standalone mode for generating puzzle IDs on the
3049 * command line. Useful for generating puzzles to be printed
3050 * out and solved offline (for puzzles where that even makes
3051 * sense - Solo, for example, is a lot more pencil-and-paper
3052 * friendly than Twiddle!)
3053 *
3054 * Usage:
3055 *
3056 * <puzzle-name> --generate [<n> [<params>]]
3057 *
3058 * <n>, if present, is the number of puzzle IDs to generate.
3059 * <params>, if present, is the same type of parameter string
3060 * you would pass to the puzzle when running it in GUI mode,
3061 * including optional extras such as the expansion factor in
3062 * Rectangles and the difficulty level in Solo.
3063 *
3064 * If you specify <params>, you must also specify <n> (although
3065 * you may specify it to be 1). Sorry; that was the
3066 * simplest-to-parse command-line syntax I came up with.
3067 */
3068 if (ngenerate > 0 || print || savefile || savesuffix) {
3069 int i, n = 1;
3070 midend *me;
3071 char *id;
3072 document *doc = NULL;
3073
3074 /*
3075 * If we're in this branch, we should display any pending
3076 * error message from the command line, since GTK isn't going
3077 * to take another crack at making sense of it.
3078 */
3079 if (*errbuf) {
3080 fputs(errbuf, stderr);
3081 return 1;
3082 }
3083
3084 n = ngenerate;
3085
3086 me = midend_new(NULL, &thegame, NULL, NULL);
3087 i = 0;
3088
3089 if (savefile && !savesuffix)
3090 savesuffix = "";
3091 if (!savefile && savesuffix)
3092 savefile = "";
3093
3094 if (print)
3095 doc = document_new(px, py, scale);
3096
3097 /*
3098 * In this loop, we either generate a game ID or read one
3099 * from stdin depending on whether we're in generate mode;
3100 * then we either write it to stdout or print it, depending
3101 * on whether we're in print mode. Thus, this loop handles
3102 * generate-to-stdout, print-from-stdin and generate-and-
3103 * immediately-print modes.
3104 *
3105 * (It could also handle a copy-stdin-to-stdout mode,
3106 * although there's currently no combination of options
3107 * which will cause this loop to be activated in that mode.
3108 * It wouldn't be _entirely_ pointless, though, because
3109 * stdin could contain bare params strings or random-seed
3110 * IDs, and stdout would contain nothing but fully
3111 * generated descriptive game IDs.)
3112 */
3113 while (ngenerate == 0 || i < n) {
3114 char *pstr, *err, *seed;
3115 struct rusage before, after;
3116
3117 if (ngenerate == 0) {
3118 pstr = fgetline(stdin);
3119 if (!pstr)
3120 break;
3121 pstr[strcspn(pstr, "\r\n")] = '\0';
3122 } else {
3123 if (arg) {
3124 pstr = snewn(strlen(arg) + 40, char);
3125
3126 strcpy(pstr, arg);
3127 if (i > 0 && strchr(arg, '#'))
3128 sprintf(pstr + strlen(pstr), "-%d", i);
3129 } else
3130 pstr = NULL;
3131 }
3132
3133 if (pstr) {
3134 err = midend_game_id(me, pstr);
3135 if (err) {
3136 fprintf(stderr, "%s: error parsing '%s': %s\n",
3137 pname, pstr, err);
3138 return 1;
3139 }
3140 }
3141
3142 if (time_generation)
3143 getrusage(RUSAGE_SELF, &before);
3144
3145 midend_new_game(me);
3146
3147 seed = midend_get_random_seed(me);
3148
3149 if (time_generation) {
3150 double elapsed;
3151
3152 getrusage(RUSAGE_SELF, &after);
3153
3154 elapsed = (after.ru_utime.tv_sec -
3155 before.ru_utime.tv_sec);
3156 elapsed += (after.ru_utime.tv_usec -
3157 before.ru_utime.tv_usec) / 1000000.0;
3158
3159 printf("%s %s: %.6f\n", thegame.name, seed, elapsed);
3160 }
3161
3162 if (test_solve && thegame.can_solve) {
3163 /*
3164 * Now destroy the aux_info in the midend, by means of
3165 * re-entering the same game id, and then try to solve
3166 * it.
3167 */
3168 char *game_id, *err;
3169
3170 game_id = midend_get_game_id(me);
3171 err = midend_game_id(me, game_id);
3172 if (err) {
3173 fprintf(stderr, "%s %s: game id re-entry error: %s\n",
3174 thegame.name, seed, err);
3175 return 1;
3176 }
3177 midend_new_game(me);
3178 sfree(game_id);
3179
3180 err = midend_solve(me);
3181 /*
3182 * If the solve operation returned the error "Solution
3183 * not known for this puzzle", that's OK, because that
3184 * just means it's a puzzle for which we don't have an
3185 * algorithmic solver and hence can't solve it without
3186 * the aux_info, e.g. Netslide. Any other error is a
3187 * problem, though.
3188 */
3189 if (err && strcmp(err, "Solution not known for this puzzle")) {
3190 fprintf(stderr, "%s %s: solve error: %s\n",
3191 thegame.name, seed, err);
3192 return 1;
3193 }
3194 }
3195
3196 sfree(pstr);
3197 sfree(seed);
3198
3199 if (doc) {
3200 err = midend_print_puzzle(me, doc, soln);
3201 if (err) {
3202 fprintf(stderr, "%s: error in printing: %s\n", pname, err);
3203 return 1;
3204 }
3205 }
3206 if (savefile) {
3207 struct savefile_write_ctx ctx;
3208 char *realname = snewn(40 + strlen(savefile) +
3209 strlen(savesuffix), char);
3210 sprintf(realname, "%s%d%s", savefile, i, savesuffix);
3211
3212 if (soln) {
3213 char *err = midend_solve(me);
3214 if (err) {
3215 fprintf(stderr, "%s: unable to show solution: %s\n",
3216 realname, err);
3217 return 1;
3218 }
3219 }
3220
3221 ctx.fp = fopen(realname, "w");
3222 if (!ctx.fp) {
3223 fprintf(stderr, "%s: open: %s\n", realname,
3224 strerror(errno));
3225 return 1;
3226 }
3227 ctx.error = 0;
3228 midend_serialise(me, savefile_write, &ctx);
3229 if (ctx.error) {
3230 fprintf(stderr, "%s: write: %s\n", realname,
3231 strerror(ctx.error));
3232 return 1;
3233 }
3234 if (fclose(ctx.fp)) {
3235 fprintf(stderr, "%s: close: %s\n", realname,
3236 strerror(errno));
3237 return 1;
3238 }
3239 sfree(realname);
3240 }
3241 if (!doc && !savefile && !time_generation) {
3242 id = midend_get_game_id(me);
3243 puts(id);
3244 sfree(id);
3245 }
3246
3247 i++;
3248 }
3249
3250 if (doc) {
3251 psdata *ps = ps_init(stdout, colour);
3252 document_print(doc, ps_drawing_api(ps));
3253 document_free(doc);
3254 ps_free(ps);
3255 }
3256
3257 midend_free(me);
3258
3259 return 0;
3260 } else if (list_presets) {
3261 /*
3262 * Another specialist mode which causes the puzzle to list the
3263 * game_params strings for all its preset configurations.
3264 */
3265 midend *me;
3266 struct preset_menu *menu;
3267
3268 me = midend_new(NULL, &thegame, NULL, NULL);
3269 menu = midend_get_presets(me, NULL);
3270 list_presets_from_menu(menu);
3271 midend_free(me);
3272 return 0;
3273 } else {
3274 frontend *fe;
3275
3276 gtk_init(&argc, &argv);
3277
3278 fe = new_window(arg, argtype, &error);
3279
3280 if (!fe) {
3281 fprintf(stderr, "%s: %s\n", pname, error);
3282 return 1;
3283 }
3284
3285 if (screenshot_file) {
3286 /*
3287 * Some puzzles will not redraw their entire area if
3288 * given a partially completed animation, which means
3289 * we must redraw now and _then_ redraw again after
3290 * freezing the move timer.
3291 */
3292 midend_force_redraw(fe->me);
3293 }
3294
3295 if (redo_proportion) {
3296 /* Start a redo. */
3297 midend_process_key(fe->me, 0, 0, 'r');
3298 /* And freeze the timer at the specified position. */
3299 midend_freeze_timer(fe->me, redo_proportion);
3300 }
3301
3302 if (screenshot_file) {
3303 save_screenshot_png(fe, screenshot_file);
3304 exit(0);
3305 }
3306
3307 gtk_main();
3308 }
3309
3310 return 0;
3311}
diff --git a/apps/plugins/puzzles/src/guess.R b/apps/plugins/puzzles/src/guess.R
new file mode 100644
index 0000000000..0e1a00c073
--- /dev/null
+++ b/apps/plugins/puzzles/src/guess.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3guess : [X] GTK COMMON guess guess-icon|no-icon
4
5guess : [G] WINDOWS COMMON guess guess.res|noicon.res
6
7ALL += guess[COMBINED]
8
9!begin am gtk
10GAMES += guess
11!end
12
13!begin >list.c
14 A(guess) \
15!end
16
17!begin >gamedesc.txt
18guess:guess.exe:Guess:Combination-guessing puzzle:Guess the hidden combination of colours.
19!end
diff --git a/apps/plugins/puzzles/src/guess.c b/apps/plugins/puzzles/src/guess.c
new file mode 100644
index 0000000000..8f058638da
--- /dev/null
+++ b/apps/plugins/puzzles/src/guess.c
@@ -0,0 +1,1518 @@
1/*
2 * guess.c: Mastermind clone.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13
14#define FLASH_FRAME 0.5F
15
16enum {
17 COL_BACKGROUND,
18 COL_FRAME, COL_CURSOR, COL_FLASH, COL_HOLD,
19 COL_EMPTY, /* must be COL_1 - 1 */
20 COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9, COL_10,
21 COL_CORRECTPLACE, COL_CORRECTCOLOUR,
22 NCOLOURS
23};
24
25struct game_params {
26 int ncolours, npegs, nguesses;
27 int allow_blank, allow_multiple;
28};
29
30#define FEEDBACK_CORRECTPLACE 1
31#define FEEDBACK_CORRECTCOLOUR 2
32
33typedef struct pegrow {
34 int npegs;
35 int *pegs; /* 0 is 'empty' */
36 int *feedback; /* may well be unused */
37} *pegrow;
38
39struct game_state {
40 game_params params;
41 pegrow *guesses; /* length params->nguesses */
42 int *holds;
43 pegrow solution;
44 int next_go; /* from 0 to nguesses-1;
45 if next_go == nguesses then they've lost. */
46 int solved; /* +1 = win, -1 = lose, 0 = still playing */
47};
48
49static game_params *default_params(void)
50{
51 game_params *ret = snew(game_params);
52
53 /* AFAIK this is the canonical Mastermind ruleset. */
54 ret->ncolours = 6;
55 ret->npegs = 4;
56 ret->nguesses = 10;
57
58 ret->allow_blank = 0;
59 ret->allow_multiple = 1;
60
61 return ret;
62}
63
64static void free_params(game_params *params)
65{
66 sfree(params);
67}
68
69static game_params *dup_params(const game_params *params)
70{
71 game_params *ret = snew(game_params);
72 *ret = *params; /* structure copy */
73 return ret;
74}
75
76static const struct {
77 char *name;
78 game_params params;
79} guess_presets[] = {
80 {"Standard", {6, 4, 10, FALSE, TRUE}},
81 {"Super", {8, 5, 12, FALSE, TRUE}},
82};
83
84
85static int game_fetch_preset(int i, char **name, game_params **params)
86{
87 if (i < 0 || i >= lenof(guess_presets))
88 return FALSE;
89
90 *name = dupstr(guess_presets[i].name);
91 /*
92 * get round annoying const issues
93 */
94 {
95 game_params tmp = guess_presets[i].params;
96 *params = dup_params(&tmp);
97 }
98
99 return TRUE;
100}
101
102static void decode_params(game_params *params, char const *string)
103{
104 char const *p = string;
105 game_params *defs = default_params();
106
107 *params = *defs; free_params(defs);
108
109 while (*p) {
110 switch (*p++) {
111 case 'c':
112 params->ncolours = atoi(p);
113 while (*p && isdigit((unsigned char)*p)) p++;
114 break;
115
116 case 'p':
117 params->npegs = atoi(p);
118 while (*p && isdigit((unsigned char)*p)) p++;
119 break;
120
121 case 'g':
122 params->nguesses = atoi(p);
123 while (*p && isdigit((unsigned char)*p)) p++;
124 break;
125
126 case 'b':
127 params->allow_blank = 1;
128 break;
129
130 case 'B':
131 params->allow_blank = 0;
132 break;
133
134 case 'm':
135 params->allow_multiple = 1;
136 break;
137
138 case 'M':
139 params->allow_multiple = 0;
140 break;
141
142 default:
143 ;
144 }
145 }
146}
147
148static char *encode_params(const game_params *params, int full)
149{
150 char data[256];
151
152 sprintf(data, "c%dp%dg%d%s%s",
153 params->ncolours, params->npegs, params->nguesses,
154 params->allow_blank ? "b" : "B", params->allow_multiple ? "m" : "M");
155
156 return dupstr(data);
157}
158
159static config_item *game_configure(const game_params *params)
160{
161 config_item *ret;
162 char buf[80];
163
164 ret = snewn(6, config_item);
165
166 ret[0].name = "Colours";
167 ret[0].type = C_STRING;
168 sprintf(buf, "%d", params->ncolours);
169 ret[0].sval = dupstr(buf);
170 ret[0].ival = 0;
171
172 ret[1].name = "Pegs per guess";
173 ret[1].type = C_STRING;
174 sprintf(buf, "%d", params->npegs);
175 ret[1].sval = dupstr(buf);
176 ret[1].ival = 0;
177
178 ret[2].name = "Guesses";
179 ret[2].type = C_STRING;
180 sprintf(buf, "%d", params->nguesses);
181 ret[2].sval = dupstr(buf);
182 ret[2].ival = 0;
183
184 ret[3].name = "Allow blanks";
185 ret[3].type = C_BOOLEAN;
186 ret[3].sval = NULL;
187 ret[3].ival = params->allow_blank;
188
189 ret[4].name = "Allow duplicates";
190 ret[4].type = C_BOOLEAN;
191 ret[4].sval = NULL;
192 ret[4].ival = params->allow_multiple;
193
194 ret[5].name = NULL;
195 ret[5].type = C_END;
196 ret[5].sval = NULL;
197 ret[5].ival = 0;
198
199 return ret;
200}
201
202static game_params *custom_params(const config_item *cfg)
203{
204 game_params *ret = snew(game_params);
205
206 ret->ncolours = atoi(cfg[0].sval);
207 ret->npegs = atoi(cfg[1].sval);
208 ret->nguesses = atoi(cfg[2].sval);
209
210 ret->allow_blank = cfg[3].ival;
211 ret->allow_multiple = cfg[4].ival;
212
213 return ret;
214}
215
216static char *validate_params(const game_params *params, int full)
217{
218 if (params->ncolours < 2 || params->npegs < 2)
219 return "Trivial solutions are uninteresting";
220 /* NB as well as the no. of colours we define, max(ncolours) must
221 * also fit in an unsigned char; see new_game_desc. */
222 if (params->ncolours > 10)
223 return "Too many colours";
224 if (params->nguesses < 1)
225 return "Must have at least one guess";
226 if (!params->allow_multiple && params->ncolours < params->npegs)
227 return "Disallowing multiple colours requires at least as many colours as pegs";
228 return NULL;
229}
230
231static pegrow new_pegrow(int npegs)
232{
233 pegrow pegs = snew(struct pegrow);
234
235 pegs->npegs = npegs;
236 pegs->pegs = snewn(pegs->npegs, int);
237 memset(pegs->pegs, 0, pegs->npegs * sizeof(int));
238 pegs->feedback = snewn(pegs->npegs, int);
239 memset(pegs->feedback, 0, pegs->npegs * sizeof(int));
240
241 return pegs;
242}
243
244static pegrow dup_pegrow(pegrow pegs)
245{
246 pegrow newpegs = new_pegrow(pegs->npegs);
247
248 memcpy(newpegs->pegs, pegs->pegs, newpegs->npegs * sizeof(int));
249 memcpy(newpegs->feedback, pegs->feedback, newpegs->npegs * sizeof(int));
250
251 return newpegs;
252}
253
254static void invalidate_pegrow(pegrow pegs)
255{
256 memset(pegs->pegs, -1, pegs->npegs * sizeof(int));
257 memset(pegs->feedback, -1, pegs->npegs * sizeof(int));
258}
259
260static void free_pegrow(pegrow pegs)
261{
262 sfree(pegs->pegs);
263 sfree(pegs->feedback);
264 sfree(pegs);
265}
266
267static char *new_game_desc(const game_params *params, random_state *rs,
268 char **aux, int interactive)
269{
270 unsigned char *bmp = snewn(params->npegs, unsigned char);
271 char *ret;
272 int i, c;
273 pegrow colcount = new_pegrow(params->ncolours);
274
275 for (i = 0; i < params->npegs; i++) {
276newcol:
277 c = random_upto(rs, params->ncolours);
278 if (!params->allow_multiple && colcount->pegs[c]) goto newcol;
279 colcount->pegs[c]++;
280 bmp[i] = (unsigned char)(c+1);
281 }
282 obfuscate_bitmap(bmp, params->npegs*8, FALSE);
283
284 ret = bin2hex(bmp, params->npegs);
285 sfree(bmp);
286 free_pegrow(colcount);
287 return ret;
288}
289
290static char *validate_desc(const game_params *params, const char *desc)
291{
292 unsigned char *bmp;
293 int i;
294
295 /* desc is just an (obfuscated) bitmap of the solution; check that
296 * it's the correct length and (when unobfuscated) contains only
297 * sensible colours. */
298 if (strlen(desc) != params->npegs * 2)
299 return "Game description is wrong length";
300 bmp = hex2bin(desc, params->npegs);
301 obfuscate_bitmap(bmp, params->npegs*8, TRUE);
302 for (i = 0; i < params->npegs; i++) {
303 if (bmp[i] < 1 || bmp[i] > params->ncolours) {
304 sfree(bmp);
305 return "Game description is corrupted";
306 }
307 }
308 sfree(bmp);
309
310 return NULL;
311}
312
313static game_state *new_game(midend *me, const game_params *params,
314 const char *desc)
315{
316 game_state *state = snew(game_state);
317 unsigned char *bmp;
318 int i;
319
320 state->params = *params;
321 state->guesses = snewn(params->nguesses, pegrow);
322 for (i = 0; i < params->nguesses; i++)
323 state->guesses[i] = new_pegrow(params->npegs);
324 state->holds = snewn(params->npegs, int);
325 state->solution = new_pegrow(params->npegs);
326
327 bmp = hex2bin(desc, params->npegs);
328 obfuscate_bitmap(bmp, params->npegs*8, TRUE);
329 for (i = 0; i < params->npegs; i++)
330 state->solution->pegs[i] = (int)bmp[i];
331 sfree(bmp);
332
333 memset(state->holds, 0, sizeof(int) * params->npegs);
334 state->next_go = state->solved = 0;
335
336 return state;
337}
338
339static game_state *dup_game(const game_state *state)
340{
341 game_state *ret = snew(game_state);
342 int i;
343
344 *ret = *state;
345
346 ret->guesses = snewn(state->params.nguesses, pegrow);
347 for (i = 0; i < state->params.nguesses; i++)
348 ret->guesses[i] = dup_pegrow(state->guesses[i]);
349 ret->holds = snewn(state->params.npegs, int);
350 memcpy(ret->holds, state->holds, sizeof(int) * state->params.npegs);
351 ret->solution = dup_pegrow(state->solution);
352
353 return ret;
354}
355
356static void free_game(game_state *state)
357{
358 int i;
359
360 free_pegrow(state->solution);
361 for (i = 0; i < state->params.nguesses; i++)
362 free_pegrow(state->guesses[i]);
363 sfree(state->holds);
364 sfree(state->guesses);
365
366 sfree(state);
367}
368
369static char *solve_game(const game_state *state, const game_state *currstate,
370 const char *aux, char **error)
371{
372 return dupstr("S");
373}
374
375static int game_can_format_as_text_now(const game_params *params)
376{
377 return TRUE;
378}
379
380static char *game_text_format(const game_state *state)
381{
382 return NULL;
383}
384
385static int is_markable(const game_params *params, pegrow pegs)
386{
387 int i, nset = 0, nrequired, ret = 0;
388 pegrow colcount = new_pegrow(params->ncolours);
389
390 nrequired = params->allow_blank ? 1 : params->npegs;
391
392 for (i = 0; i < params->npegs; i++) {
393 int c = pegs->pegs[i];
394 if (c > 0) {
395 colcount->pegs[c-1]++;
396 nset++;
397 }
398 }
399 if (nset < nrequired) goto done;
400
401 if (!params->allow_multiple) {
402 for (i = 0; i < params->ncolours; i++) {
403 if (colcount->pegs[i] > 1) goto done;
404 }
405 }
406 ret = 1;
407done:
408 free_pegrow(colcount);
409 return ret;
410}
411
412struct game_ui {
413 game_params params;
414 pegrow curr_pegs; /* half-finished current move */
415 int *holds;
416 int colour_cur; /* position of up-down colour picker cursor */
417 int peg_cur; /* position of left-right peg picker cursor */
418 int display_cur, markable;
419
420 int drag_col, drag_x, drag_y; /* x and y are *center* of peg! */
421 int drag_opeg; /* peg index, if dragged from a peg (from current guess), otherwise -1 */
422
423 int show_labels; /* label the colours with letters */
424 pegrow hint;
425};
426
427static game_ui *new_ui(const game_state *state)
428{
429 game_ui *ui = snew(game_ui);
430 memset(ui, 0, sizeof(game_ui));
431 ui->params = state->params; /* structure copy */
432 ui->curr_pegs = new_pegrow(state->params.npegs);
433 ui->holds = snewn(state->params.npegs, int);
434 memset(ui->holds, 0, sizeof(int)*state->params.npegs);
435 ui->drag_opeg = -1;
436 return ui;
437}
438
439static void free_ui(game_ui *ui)
440{
441 if (ui->hint)
442 free_pegrow(ui->hint);
443 free_pegrow(ui->curr_pegs);
444 sfree(ui->holds);
445 sfree(ui);
446}
447
448static char *encode_ui(const game_ui *ui)
449{
450 char *ret, *p, *sep;
451 int i;
452
453 /*
454 * For this game it's worth storing the contents of the current
455 * guess, and the current set of holds.
456 */
457 ret = snewn(40 * ui->curr_pegs->npegs, char);
458 p = ret;
459 sep = "";
460 for (i = 0; i < ui->curr_pegs->npegs; i++) {
461 p += sprintf(p, "%s%d%s", sep, ui->curr_pegs->pegs[i],
462 ui->holds[i] ? "_" : "");
463 sep = ",";
464 }
465 *p++ = '\0';
466 assert(p - ret < 40 * ui->curr_pegs->npegs);
467 return sresize(ret, p - ret, char);
468}
469
470static void decode_ui(game_ui *ui, const char *encoding)
471{
472 int i;
473 const char *p = encoding;
474 for (i = 0; i < ui->curr_pegs->npegs; i++) {
475 ui->curr_pegs->pegs[i] = atoi(p);
476 while (*p && isdigit((unsigned char)*p)) p++;
477 if (*p == '_') {
478 /* NB: old versions didn't store holds */
479 ui->holds[i] = 1;
480 p++;
481 } else
482 ui->holds[i] = 0;
483 if (*p == ',') p++;
484 }
485 ui->markable = is_markable(&ui->params, ui->curr_pegs);
486}
487
488static void game_changed_state(game_ui *ui, const game_state *oldstate,
489 const game_state *newstate)
490{
491 int i;
492
493 if (newstate->next_go < oldstate->next_go) {
494 sfree(ui->hint);
495 ui->hint = NULL;
496 }
497
498 /* Implement holds, clear other pegs.
499 * This does something that is arguably the Right Thing even
500 * for undo. */
501 for (i = 0; i < newstate->solution->npegs; i++) {
502 if (newstate->solved)
503 ui->holds[i] = 0;
504 else
505 ui->holds[i] = newstate->holds[i];
506 if (newstate->solved || (newstate->next_go == 0) || !ui->holds[i]) {
507 ui->curr_pegs->pegs[i] = 0;
508 } else
509 ui->curr_pegs->pegs[i] =
510 newstate->guesses[newstate->next_go-1]->pegs[i];
511 }
512 ui->markable = is_markable(&newstate->params, ui->curr_pegs);
513 /* Clean up cursor position */
514 if (!ui->markable && ui->peg_cur == newstate->solution->npegs)
515 ui->peg_cur--;
516}
517
518#define PEGSZ (ds->pegsz)
519#define PEGOFF (ds->pegsz + ds->gapsz)
520#define HINTSZ (ds->hintsz)
521#define HINTOFF (ds->hintsz + ds->gapsz)
522
523#define GAP (ds->gapsz)
524#define CGAP (ds->gapsz / 2)
525
526#define PEGRAD (ds->pegrad)
527#define HINTRAD (ds->hintrad)
528
529#define COL_OX (ds->colx)
530#define COL_OY (ds->coly)
531#define COL_X(c) (COL_OX)
532#define COL_Y(c) (COL_OY + (c)*PEGOFF)
533#define COL_W PEGOFF
534#define COL_H (ds->colours->npegs*PEGOFF)
535
536#define GUESS_OX (ds->guessx)
537#define GUESS_OY (ds->guessy)
538#define GUESS_X(g,p) (GUESS_OX + (p)*PEGOFF)
539#define GUESS_Y(g,p) (GUESS_OY + (g)*PEGOFF)
540#define GUESS_W (ds->solution->npegs*PEGOFF)
541#define GUESS_H (ds->nguesses*PEGOFF)
542
543#define HINT_OX (GUESS_OX + GUESS_W + ds->gapsz)
544#define HINT_OY (GUESS_OY + (PEGSZ - HINTOFF - HINTSZ) / 2)
545#define HINT_X(g) HINT_OX
546#define HINT_Y(g) (HINT_OY + (g)*PEGOFF)
547#define HINT_W ((ds->hintw*HINTOFF) - GAP)
548#define HINT_H GUESS_H
549
550#define SOLN_OX GUESS_OX
551#define SOLN_OY (GUESS_OY + GUESS_H + ds->gapsz + 2)
552#define SOLN_W GUESS_W
553#define SOLN_H PEGOFF
554
555struct game_drawstate {
556 int nguesses;
557 pegrow *guesses; /* same size as state->guesses */
558 pegrow solution; /* only displayed if state->solved */
559 pegrow colours; /* length ncolours, not npegs */
560
561 int pegsz, hintsz, gapsz; /* peg size (diameter), etc. */
562 int pegrad, hintrad; /* radius of peg, hint */
563 int border;
564 int colx, coly; /* origin of colours vertical bar */
565 int guessx, guessy; /* origin of guesses */
566 int solnx, solny; /* origin of solution */
567 int hintw; /* no. of hint tiles we're wide per row */
568 int w, h, started, solved;
569
570 int next_go;
571
572 blitter *blit_peg;
573 int drag_col, blit_ox, blit_oy;
574};
575
576static void set_peg(const game_params *params, game_ui *ui, int peg, int col)
577{
578 ui->curr_pegs->pegs[peg] = col;
579 ui->markable = is_markable(params, ui->curr_pegs);
580}
581
582static int mark_pegs(pegrow guess, const pegrow solution, int ncols)
583{
584 int nc_place = 0, nc_colour = 0, i, j;
585
586 assert(guess && solution && (guess->npegs == solution->npegs));
587
588 for (i = 0; i < guess->npegs; i++) {
589 if (guess->pegs[i] == solution->pegs[i]) nc_place++;
590 }
591
592 /* slight bit of cleverness: we have the following formula, from
593 * http://mathworld.wolfram.com/Mastermind.html that gives:
594 *
595 * nc_colour = sum(colours, min(#solution, #guess)) - nc_place
596 *
597 * I think this is due to Knuth.
598 */
599 for (i = 1; i <= ncols; i++) {
600 int n_guess = 0, n_solution = 0;
601 for (j = 0; j < guess->npegs; j++) {
602 if (guess->pegs[j] == i) n_guess++;
603 if (solution->pegs[j] == i) n_solution++;
604 }
605 nc_colour += min(n_guess, n_solution);
606 }
607 nc_colour -= nc_place;
608
609 debug(("mark_pegs, %d pegs, %d right place, %d right colour",
610 guess->npegs, nc_place, nc_colour));
611 assert((nc_colour + nc_place) <= guess->npegs);
612
613 memset(guess->feedback, 0, guess->npegs*sizeof(int));
614 for (i = 0, j = 0; i < nc_place; i++)
615 guess->feedback[j++] = FEEDBACK_CORRECTPLACE;
616 for (i = 0; i < nc_colour; i++)
617 guess->feedback[j++] = FEEDBACK_CORRECTCOLOUR;
618
619 return nc_place;
620}
621
622static char *encode_move(const game_state *from, game_ui *ui)
623{
624 char *buf, *p, *sep;
625 int len, i;
626
627 len = ui->curr_pegs->npegs * 20 + 2;
628 buf = snewn(len, char);
629 p = buf;
630 *p++ = 'G';
631 sep = "";
632 for (i = 0; i < ui->curr_pegs->npegs; i++) {
633 p += sprintf(p, "%s%d%s", sep, ui->curr_pegs->pegs[i],
634 ui->holds[i] ? "_" : "");
635 sep = ",";
636 }
637 *p++ = '\0';
638 assert(p - buf <= len);
639 buf = sresize(buf, len, char);
640
641 return buf;
642}
643
644static void compute_hint(const game_state *state, game_ui *ui)
645{
646 /* Suggest the lexicographically first row consistent with all
647 * previous feedback. This is not only a useful hint, but also
648 * a reasonable strategy if applied consistently. If the user
649 * uses hints in every turn, they may be able to intuit this
650 * strategy, or one similar to it. I (Jonas Kölker) came up
651 * with something close to it without seeing it in action. */
652
653 /* Some performance characteristics: I want to ask for each n,
654 * how many solutions are guessed in exactly n guesses if you
655 * use the hint in each turn.
656 *
657 * With 4 pegs and 6 colours you get the following histogram:
658 *
659 * 1 guesses: 1 solution
660 * 2 guesses: 4 solutions
661 * 3 guesses: 25 solutions
662 * 4 guesses: 108 solutions
663 * 5 guesses: 305 solutions
664 * 6 guesses: 602 solutions
665 * 7 guesses: 196 solutions
666 * 8 guesses: 49 solutions
667 * 9 guesses: 6 solutions
668 * (note: the tenth guess is never necessary.)
669 *
670 * With 5 pegs and 8 colours you get the following histogram:
671 *
672 * 1 guesses: 1 solution
673 * 2 guesses: 5 solutions
674 * 3 guesses: 43 solutions
675 * 4 guesses: 278 solutions
676 * 5 guesses: 1240 solutions
677 * 6 guesses: 3515 solutions
678 * 7 guesses: 7564 solutions
679 * 8 guesses: 14086 solutions
680 * 9 guesses: 4614 solutions
681 * 10 guesses: 1239 solutions
682 * 11 guesses: 175 solutions
683 * 12 guesses: 7 solutions
684 * 13 guesses: 1 solution
685 *
686 * The solution which takes too many guesses is {8, 8, 5, 6, 7}.
687 * The game ID is c8p5g12Bm:4991e5e41a. */
688
689 int mincolour = 1, maxcolour = 0, i, j;
690
691 /* For large values of npegs and ncolours, the lexicographically
692 * next guess make take a while to find. Finding upper and
693 * lower limits on which colours we have to consider will speed
694 * this up, as will caching our progress from one invocation to
695 * the next. The latter strategy works, since if we have ruled
696 * out a candidate we will never reverse this judgment in the
697 * light of new information. Removing information, i.e. undo,
698 * will require us to backtrack somehow. We backtrack by fully
699 * forgetting our progress (and recomputing it if required). */
700
701 for (i = 0; i < state->next_go; ++i)
702 for (j = 0; j < state->params.npegs; ++j)
703 if (state->guesses[i]->pegs[j] > maxcolour)
704 maxcolour = state->guesses[i]->pegs[j];
705 maxcolour = min(maxcolour + 1, state->params.ncolours);
706
707increase_mincolour:
708 for (i = 0; i < state->next_go; ++i) {
709 if (state->guesses[i]->feedback[0])
710 goto next_iteration;
711 for (j = 0; j < state->params.npegs; ++j)
712 if (state->guesses[i]->pegs[j] != mincolour)
713 goto next_iteration;
714 ++mincolour;
715 goto increase_mincolour;
716 next_iteration:
717 ;
718 }
719
720 if (!ui->hint) {
721 ui->hint = new_pegrow(state->params.npegs);
722 for (i = 0; i < state->params.npegs; ++i)
723 ui->hint->pegs[i] = 1;
724 }
725
726 while (ui->hint->pegs[0] <= state->params.ncolours) {
727 for (i = 0; i < state->next_go; ++i) {
728 mark_pegs(ui->hint, state->guesses[i], maxcolour);
729 for (j = 0; j < state->params.npegs; ++j)
730 if (ui->hint->feedback[j] != state->guesses[i]->feedback[j])
731 goto increment_pegrow;
732 }
733 /* a valid guess was found; install it and return */
734 for (i = 0; i < state->params.npegs; ++i)
735 ui->curr_pegs->pegs[i] = ui->hint->pegs[i];
736
737 ui->markable = TRUE;
738 ui->peg_cur = state->params.npegs;
739 ui->display_cur = 1;
740 return;
741
742 increment_pegrow:
743 for (i = ui->hint->npegs;
744 ++ui->hint->pegs[--i], i && ui->hint->pegs[i] > maxcolour;
745 ui->hint->pegs[i] = mincolour);
746 }
747 /* No solution is compatible with the given hints. Impossible! */
748 /* (hack new_game_desc to create invalid solutions to get here) */
749
750 /* For some values of npegs and ncolours, the hinting function takes a
751 * long time to complete. To visually indicate completion with failure,
752 * should it ever happen, update the ui in some trivial way. This gives
753 * the user a sense of broken(ish)ness and futility. */
754 if (!ui->display_cur) {
755 ui->display_cur = 1;
756 } else if (state->params.npegs == 1) {
757 ui->display_cur = 0;
758 } else {
759 ui->peg_cur = (ui->peg_cur + 1) % state->params.npegs;
760 }
761}
762
763static char *interpret_move(const game_state *from, game_ui *ui,
764 const game_drawstate *ds,
765 int x, int y, int button)
766{
767 int over_col = 0; /* one-indexed */
768 int over_guess = -1; /* zero-indexed */
769 int over_past_guess_y = -1; /* zero-indexed */
770 int over_past_guess_x = -1; /* zero-indexed */
771 int over_hint = 0; /* zero or one */
772 char *ret = NULL;
773
774 int guess_ox = GUESS_X(from->next_go, 0);
775 int guess_oy = GUESS_Y(from->next_go, 0);
776
777 /*
778 * Enable or disable labels on colours.
779 */
780 if (button == 'l' || button == 'L') {
781 ui->show_labels = !ui->show_labels;
782 return "";
783 }
784
785 if (from->solved) return NULL;
786
787 if (x >= COL_OX && x < (COL_OX + COL_W) &&
788 y >= COL_OY && y < (COL_OY + COL_H)) {
789 over_col = ((y - COL_OY) / PEGOFF) + 1;
790 assert(over_col >= 1 && over_col <= ds->colours->npegs);
791 } else if (x >= guess_ox &&
792 y >= guess_oy && y < (guess_oy + GUESS_H)) {
793 if (x < (guess_ox + GUESS_W)) {
794 over_guess = (x - guess_ox) / PEGOFF;
795 assert(over_guess >= 0 && over_guess < ds->solution->npegs);
796 } else {
797 over_hint = 1;
798 }
799 } else if (x >= guess_ox && x < (guess_ox + GUESS_W) &&
800 y >= GUESS_OY && y < guess_oy) {
801 over_past_guess_y = (y - GUESS_OY) / PEGOFF;
802 over_past_guess_x = (x - guess_ox) / PEGOFF;
803 assert(over_past_guess_y >= 0 && over_past_guess_y < from->next_go);
804 assert(over_past_guess_x >= 0 && over_past_guess_x < ds->solution->npegs);
805 }
806 debug(("make_move: over_col %d, over_guess %d, over_hint %d,"
807 " over_past_guess (%d,%d)", over_col, over_guess, over_hint,
808 over_past_guess_x, over_past_guess_y));
809
810 assert(ds->blit_peg);
811
812 /* mouse input */
813 if (button == LEFT_BUTTON) {
814 if (over_col > 0) {
815 ui->drag_col = over_col;
816 ui->drag_opeg = -1;
817 debug(("Start dragging from colours"));
818 } else if (over_guess > -1) {
819 int col = ui->curr_pegs->pegs[over_guess];
820 if (col) {
821 ui->drag_col = col;
822 ui->drag_opeg = over_guess;
823 debug(("Start dragging from a guess"));
824 }
825 } else if (over_past_guess_y > -1) {
826 int col =
827 from->guesses[over_past_guess_y]->pegs[over_past_guess_x];
828 if (col) {
829 ui->drag_col = col;
830 ui->drag_opeg = -1;
831 debug(("Start dragging from a past guess"));
832 }
833 }
834 if (ui->drag_col) {
835 ui->drag_x = x;
836 ui->drag_y = y;
837 debug(("Start dragging, col = %d, (%d,%d)",
838 ui->drag_col, ui->drag_x, ui->drag_y));
839 ret = "";
840 }
841 } else if (button == LEFT_DRAG && ui->drag_col) {
842 ui->drag_x = x;
843 ui->drag_y = y;
844 debug(("Keep dragging, (%d,%d)", ui->drag_x, ui->drag_y));
845 ret = "";
846 } else if (button == LEFT_RELEASE && ui->drag_col) {
847 if (over_guess > -1) {
848 debug(("Dropping colour %d onto guess peg %d",
849 ui->drag_col, over_guess));
850 set_peg(&from->params, ui, over_guess, ui->drag_col);
851 } else {
852 if (ui->drag_opeg > -1) {
853 debug(("Removing colour %d from peg %d",
854 ui->drag_col, ui->drag_opeg));
855 set_peg(&from->params, ui, ui->drag_opeg, 0);
856 }
857 }
858 ui->drag_col = 0;
859 ui->drag_opeg = -1;
860 ui->display_cur = 0;
861 debug(("Stop dragging."));
862 ret = "";
863 } else if (button == RIGHT_BUTTON) {
864 if (over_guess > -1) {
865 /* we use ths feedback in the game_ui to signify
866 * 'carry this peg to the next guess as well'. */
867 ui->holds[over_guess] = 1 - ui->holds[over_guess];
868 ret = "";
869 }
870 } else if (button == LEFT_RELEASE && over_hint && ui->markable) {
871 /* NB this won't trigger if on the end of a drag; that's on
872 * purpose, in case you drop by mistake... */
873 ret = encode_move(from, ui);
874 }
875
876 /* keyboard input */
877 if (button == CURSOR_UP || button == CURSOR_DOWN) {
878 ui->display_cur = 1;
879 if (button == CURSOR_DOWN && (ui->colour_cur+1) < from->params.ncolours)
880 ui->colour_cur++;
881 if (button == CURSOR_UP && ui->colour_cur > 0)
882 ui->colour_cur--;
883 ret = "";
884 } else if (button == 'h' || button == 'H' || button == '?') {
885 compute_hint(from, ui);
886 ret = "";
887 } else if (button == CURSOR_LEFT || button == CURSOR_RIGHT) {
888 int maxcur = from->params.npegs;
889 if (ui->markable) maxcur++;
890
891 ui->display_cur = 1;
892 if (button == CURSOR_RIGHT && (ui->peg_cur+1) < maxcur)
893 ui->peg_cur++;
894 if (button == CURSOR_LEFT && ui->peg_cur > 0)
895 ui->peg_cur--;
896 ret = "";
897 } else if (IS_CURSOR_SELECT(button)) {
898 ui->display_cur = 1;
899 if (ui->peg_cur == from->params.npegs) {
900 ret = encode_move(from, ui);
901 } else {
902 set_peg(&from->params, ui, ui->peg_cur, ui->colour_cur+1);
903 ret = "";
904 }
905 } else if (button == 'D' || button == 'd' || button == '\b') {
906 ui->display_cur = 1;
907 set_peg(&from->params, ui, ui->peg_cur, 0);
908 ret = "";
909 } else if (button == CURSOR_SELECT2) {
910 if (ui->peg_cur == from->params.npegs)
911 return NULL;
912 ui->display_cur = 1;
913 ui->holds[ui->peg_cur] = 1 - ui->holds[ui->peg_cur];
914 ret = "";
915 }
916 return ret;
917}
918
919static game_state *execute_move(const game_state *from, const char *move)
920{
921 int i, nc_place;
922 game_state *ret;
923 const char *p;
924
925 if (!strcmp(move, "S")) {
926 ret = dup_game(from);
927 ret->solved = -1;
928 return ret;
929 } else if (move[0] == 'G') {
930 p = move+1;
931
932 ret = dup_game(from);
933
934 for (i = 0; i < from->solution->npegs; i++) {
935 int val = atoi(p);
936 int min_colour = from->params.allow_blank? 0 : 1;
937 if (val < min_colour || val > from->params.ncolours) {
938 free_game(ret);
939 return NULL;
940 }
941 ret->guesses[from->next_go]->pegs[i] = atoi(p);
942 while (*p && isdigit((unsigned char)*p)) p++;
943 if (*p == '_') {
944 ret->holds[i] = 1;
945 p++;
946 } else
947 ret->holds[i] = 0;
948 if (*p == ',') p++;
949 }
950
951 nc_place = mark_pegs(ret->guesses[from->next_go], ret->solution, ret->params.ncolours);
952
953 if (nc_place == ret->solution->npegs) {
954 ret->solved = +1; /* win! */
955 } else {
956 ret->next_go = from->next_go + 1;
957 if (ret->next_go >= ret->params.nguesses)
958 ret->solved = -1; /* lose, meaning we show the pegs. */
959 }
960
961 return ret;
962 } else
963 return NULL;
964}
965
966/* ----------------------------------------------------------------------
967 * Drawing routines.
968 */
969
970#define PEG_PREFER_SZ 32
971
972/* next three are multipliers for pegsz. It will look much nicer if
973 * (2*PEG_HINT) + PEG_GAP = 1.0 as the hints are formatted like that. */
974#define PEG_GAP 0.10
975#define PEG_HINT 0.35
976
977#define BORDER 0.5
978
979static void game_compute_size(const game_params *params, int tilesize,
980 int *x, int *y)
981{
982 double hmul, vmul_c, vmul_g, vmul;
983 int hintw = (params->npegs+1)/2;
984
985 hmul = BORDER * 2.0 + /* border */
986 1.0 * 2.0 + /* vertical colour bar */
987 1.0 * params->npegs + /* guess pegs */
988 PEG_GAP * params->npegs + /* guess gaps */
989 PEG_HINT * hintw + /* hint pegs */
990 PEG_GAP * (hintw - 1); /* hint gaps */
991
992 vmul_c = BORDER * 2.0 + /* border */
993 1.0 * params->ncolours + /* colour pegs */
994 PEG_GAP * (params->ncolours - 1); /* colour gaps */
995
996 vmul_g = BORDER * 2.0 + /* border */
997 1.0 * (params->nguesses + 1) + /* guesses plus solution */
998 PEG_GAP * (params->nguesses + 1); /* gaps plus gap above soln */
999
1000 vmul = max(vmul_c, vmul_g);
1001
1002 *x = (int)ceil((double)tilesize * hmul);
1003 *y = (int)ceil((double)tilesize * vmul);
1004}
1005
1006static void game_set_size(drawing *dr, game_drawstate *ds,
1007 const game_params *params, int tilesize)
1008{
1009 int colh, guessh;
1010
1011 ds->pegsz = tilesize;
1012
1013 ds->hintsz = (int)((double)ds->pegsz * PEG_HINT);
1014 ds->gapsz = (int)((double)ds->pegsz * PEG_GAP);
1015 ds->border = (int)((double)ds->pegsz * BORDER);
1016
1017 ds->pegrad = (ds->pegsz -1)/2; /* radius of peg to fit in pegsz (which is 2r+1) */
1018 ds->hintrad = (ds->hintsz-1)/2;
1019
1020 colh = ((ds->pegsz + ds->gapsz) * params->ncolours) - ds->gapsz;
1021 guessh = ((ds->pegsz + ds->gapsz) * params->nguesses); /* guesses */
1022 guessh += ds->gapsz + ds->pegsz; /* solution */
1023
1024 game_compute_size(params, tilesize, &ds->w, &ds->h);
1025 ds->colx = ds->border;
1026 ds->coly = (ds->h - colh) / 2;
1027
1028 ds->guessx = ds->solnx = ds->border + ds->pegsz * 2; /* border + colours */
1029 ds->guessy = (ds->h - guessh) / 2;
1030 ds->solny = ds->guessy + ((ds->pegsz + ds->gapsz) * params->nguesses) + ds->gapsz;
1031
1032 assert(ds->pegsz > 0);
1033 assert(!ds->blit_peg); /* set_size is never called twice */
1034 ds->blit_peg = blitter_new(dr, ds->pegsz+2, ds->pegsz+2);
1035}
1036
1037static float *game_colours(frontend *fe, int *ncolours)
1038{
1039 float *ret = snewn(3 * NCOLOURS, float), max;
1040 int i;
1041
1042 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1043
1044 /* red */
1045 ret[COL_1 * 3 + 0] = 1.0F;
1046 ret[COL_1 * 3 + 1] = 0.0F;
1047 ret[COL_1 * 3 + 2] = 0.0F;
1048
1049 /* yellow */
1050 ret[COL_2 * 3 + 0] = 1.0F;
1051 ret[COL_2 * 3 + 1] = 1.0F;
1052 ret[COL_2 * 3 + 2] = 0.0F;
1053
1054 /* green */
1055 ret[COL_3 * 3 + 0] = 0.0F;
1056 ret[COL_3 * 3 + 1] = 1.0F;
1057 ret[COL_3 * 3 + 2] = 0.0F;
1058
1059 /* blue */
1060 ret[COL_4 * 3 + 0] = 0.2F;
1061 ret[COL_4 * 3 + 1] = 0.3F;
1062 ret[COL_4 * 3 + 2] = 1.0F;
1063
1064 /* orange */
1065 ret[COL_5 * 3 + 0] = 1.0F;
1066 ret[COL_5 * 3 + 1] = 0.5F;
1067 ret[COL_5 * 3 + 2] = 0.0F;
1068
1069 /* purple */
1070 ret[COL_6 * 3 + 0] = 0.5F;
1071 ret[COL_6 * 3 + 1] = 0.0F;
1072 ret[COL_6 * 3 + 2] = 0.7F;
1073
1074 /* brown */
1075 ret[COL_7 * 3 + 0] = 0.5F;
1076 ret[COL_7 * 3 + 1] = 0.3F;
1077 ret[COL_7 * 3 + 2] = 0.3F;
1078
1079 /* light blue */
1080 ret[COL_8 * 3 + 0] = 0.4F;
1081 ret[COL_8 * 3 + 1] = 0.8F;
1082 ret[COL_8 * 3 + 2] = 1.0F;
1083
1084 /* light green */
1085 ret[COL_9 * 3 + 0] = 0.7F;
1086 ret[COL_9 * 3 + 1] = 1.0F;
1087 ret[COL_9 * 3 + 2] = 0.7F;
1088
1089 /* pink */
1090 ret[COL_10 * 3 + 0] = 1.0F;
1091 ret[COL_10 * 3 + 1] = 0.6F;
1092 ret[COL_10 * 3 + 2] = 1.0F;
1093
1094 ret[COL_FRAME * 3 + 0] = 0.0F;
1095 ret[COL_FRAME * 3 + 1] = 0.0F;
1096 ret[COL_FRAME * 3 + 2] = 0.0F;
1097
1098 ret[COL_CURSOR * 3 + 0] = 0.0F;
1099 ret[COL_CURSOR * 3 + 1] = 0.0F;
1100 ret[COL_CURSOR * 3 + 2] = 0.0F;
1101
1102 ret[COL_FLASH * 3 + 0] = 0.5F;
1103 ret[COL_FLASH * 3 + 1] = 1.0F;
1104 ret[COL_FLASH * 3 + 2] = 1.0F;
1105
1106 ret[COL_HOLD * 3 + 0] = 1.0F;
1107 ret[COL_HOLD * 3 + 1] = 0.5F;
1108 ret[COL_HOLD * 3 + 2] = 0.5F;
1109
1110 ret[COL_CORRECTPLACE*3 + 0] = 0.0F;
1111 ret[COL_CORRECTPLACE*3 + 1] = 0.0F;
1112 ret[COL_CORRECTPLACE*3 + 2] = 0.0F;
1113
1114 ret[COL_CORRECTCOLOUR*3 + 0] = 1.0F;
1115 ret[COL_CORRECTCOLOUR*3 + 1] = 1.0F;
1116 ret[COL_CORRECTCOLOUR*3 + 2] = 1.0F;
1117
1118 /* We want to make sure we can distinguish COL_CORRECTCOLOUR
1119 * (which we hard-code as white) from COL_BACKGROUND (which
1120 * could default to white on some platforms).
1121 * Code borrowed from fifteen.c. */
1122 max = ret[COL_BACKGROUND*3];
1123 for (i = 1; i < 3; i++)
1124 if (ret[COL_BACKGROUND*3+i] > max)
1125 max = ret[COL_BACKGROUND*3+i];
1126 if (max * 1.2F > 1.0F) {
1127 for (i = 0; i < 3; i++)
1128 ret[COL_BACKGROUND*3+i] /= (max * 1.2F);
1129 }
1130
1131 /* We also want to be able to tell the difference between BACKGROUND
1132 * and EMPTY, for similar distinguishing-hint reasons. */
1133 ret[COL_EMPTY * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F;
1134 ret[COL_EMPTY * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F;
1135 ret[COL_EMPTY * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F;
1136
1137 *ncolours = NCOLOURS;
1138 return ret;
1139}
1140
1141static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1142{
1143 struct game_drawstate *ds = snew(struct game_drawstate);
1144 int i;
1145
1146 memset(ds, 0, sizeof(struct game_drawstate));
1147
1148 ds->guesses = snewn(state->params.nguesses, pegrow);
1149 ds->nguesses = state->params.nguesses;
1150 for (i = 0; i < state->params.nguesses; i++) {
1151 ds->guesses[i] = new_pegrow(state->params.npegs);
1152 invalidate_pegrow(ds->guesses[i]);
1153 }
1154 ds->solution = new_pegrow(state->params.npegs);
1155 invalidate_pegrow(ds->solution);
1156 ds->colours = new_pegrow(state->params.ncolours);
1157 invalidate_pegrow(ds->colours);
1158
1159 ds->hintw = (state->params.npegs+1)/2; /* must round up */
1160
1161 ds->blit_peg = NULL;
1162
1163 return ds;
1164}
1165
1166static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1167{
1168 int i;
1169
1170 if (ds->blit_peg) blitter_free(dr, ds->blit_peg);
1171 free_pegrow(ds->colours);
1172 free_pegrow(ds->solution);
1173 for (i = 0; i < ds->nguesses; i++)
1174 free_pegrow(ds->guesses[i]);
1175 sfree(ds->guesses);
1176 sfree(ds);
1177}
1178
1179static void draw_peg(drawing *dr, game_drawstate *ds, int cx, int cy,
1180 int moving, int labelled, int col)
1181{
1182 /*
1183 * Some platforms antialias circles, which means we shouldn't
1184 * overwrite a circle of one colour with a circle of another
1185 * colour without erasing the background first. However, if the
1186 * peg is the one being dragged, we don't erase the background
1187 * because we _want_ it to alpha-blend nicely into whatever's
1188 * behind it.
1189 */
1190 if (!moving)
1191 draw_rect(dr, cx-CGAP, cy-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2,
1192 COL_BACKGROUND);
1193 if (PEGRAD > 0) {
1194 draw_circle(dr, cx+PEGRAD, cy+PEGRAD, PEGRAD,
1195 COL_EMPTY + col, (col ? COL_FRAME : COL_EMPTY));
1196 } else
1197 draw_rect(dr, cx, cy, PEGSZ, PEGSZ, COL_EMPTY + col);
1198
1199 if (labelled && col) {
1200 char buf[2];
1201 buf[0] = 'a'-1 + col;
1202 buf[1] = '\0';
1203 draw_text(dr, cx+PEGRAD, cy+PEGRAD, FONT_VARIABLE, PEGRAD,
1204 ALIGN_HCENTRE|ALIGN_VCENTRE, COL_FRAME, buf);
1205 }
1206
1207 draw_update(dr, cx-CGAP, cy-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2);
1208}
1209
1210static void draw_cursor(drawing *dr, game_drawstate *ds, int x, int y)
1211{
1212 draw_circle(dr, x+PEGRAD, y+PEGRAD, PEGRAD+CGAP, -1, COL_CURSOR);
1213
1214 draw_update(dr, x-CGAP, y-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2);
1215}
1216
1217static void guess_redraw(drawing *dr, game_drawstate *ds, int guess,
1218 pegrow src, int *holds, int cur_col, int force,
1219 int labelled)
1220{
1221 pegrow dest;
1222 int rowx, rowy, i, scol;
1223
1224 if (guess == -1) {
1225 dest = ds->solution;
1226 rowx = SOLN_OX;
1227 rowy = SOLN_OY;
1228 } else {
1229 dest = ds->guesses[guess];
1230 rowx = GUESS_X(guess,0);
1231 rowy = GUESS_Y(guess,0);
1232 }
1233 if (src) assert(src->npegs == dest->npegs);
1234
1235 for (i = 0; i < dest->npegs; i++) {
1236 scol = src ? src->pegs[i] : 0;
1237 if (i == cur_col)
1238 scol |= 0x1000;
1239 if (holds && holds[i])
1240 scol |= 0x2000;
1241 if (labelled)
1242 scol |= 0x4000;
1243 if ((dest->pegs[i] != scol) || force) {
1244 draw_peg(dr, ds, rowx + PEGOFF * i, rowy, FALSE, labelled,
1245 scol &~ 0x7000);
1246 /*
1247 * Hold marker.
1248 */
1249 draw_rect(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2,
1250 PEGSZ, 2, (scol & 0x2000 ? COL_HOLD : COL_BACKGROUND));
1251 draw_update(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2,
1252 PEGSZ, 2);
1253 if (scol & 0x1000)
1254 draw_cursor(dr, ds, rowx + PEGOFF * i, rowy);
1255 }
1256 dest->pegs[i] = scol;
1257 }
1258}
1259
1260static void hint_redraw(drawing *dr, game_drawstate *ds, int guess,
1261 pegrow src, int force, int cursor, int markable)
1262{
1263 pegrow dest = ds->guesses[guess];
1264 int rowx, rowy, i, scol, col, hintlen;
1265 int need_redraw;
1266 int emptycol = (markable ? COL_FLASH : COL_EMPTY);
1267
1268 if (src) assert(src->npegs == dest->npegs);
1269
1270 hintlen = (dest->npegs + 1)/2;
1271
1272 /*
1273 * Because of the possible presence of the cursor around this
1274 * entire section, we redraw all or none of it but never part.
1275 */
1276 need_redraw = FALSE;
1277
1278 for (i = 0; i < dest->npegs; i++) {
1279 scol = src ? src->feedback[i] : 0;
1280 if (i == 0 && cursor)
1281 scol |= 0x1000;
1282 if (i == 0 && markable)
1283 scol |= 0x2000;
1284 if ((scol != dest->feedback[i]) || force) {
1285 need_redraw = TRUE;
1286 }
1287 dest->feedback[i] = scol;
1288 }
1289
1290 if (need_redraw) {
1291 int hinth = HINTSZ + GAP + HINTSZ;
1292 int hx,hy,hw,hh;
1293
1294 hx = HINT_X(guess)-GAP; hy = HINT_Y(guess)-GAP;
1295 hw = HINT_W+GAP*2; hh = hinth+GAP*2;
1296
1297 /* erase a large background rectangle */
1298 draw_rect(dr, hx, hy, hw, hh, COL_BACKGROUND);
1299
1300 for (i = 0; i < dest->npegs; i++) {
1301 scol = src ? src->feedback[i] : 0;
1302 col = ((scol == FEEDBACK_CORRECTPLACE) ? COL_CORRECTPLACE :
1303 (scol == FEEDBACK_CORRECTCOLOUR) ? COL_CORRECTCOLOUR :
1304 emptycol);
1305
1306 rowx = HINT_X(guess);
1307 rowy = HINT_Y(guess);
1308 if (i < hintlen) {
1309 rowx += HINTOFF * i;
1310 } else {
1311 rowx += HINTOFF * (i - hintlen);
1312 rowy += HINTOFF;
1313 }
1314 if (HINTRAD > 0) {
1315 draw_circle(dr, rowx+HINTRAD, rowy+HINTRAD, HINTRAD, col,
1316 (col == emptycol ? emptycol : COL_FRAME));
1317 } else {
1318 draw_rect(dr, rowx, rowy, HINTSZ, HINTSZ, col);
1319 }
1320 }
1321 if (cursor) {
1322 int x1,y1,x2,y2;
1323 x1 = hx + CGAP; y1 = hy + CGAP;
1324 x2 = hx + hw - CGAP; y2 = hy + hh - CGAP;
1325 draw_line(dr, x1, y1, x2, y1, COL_CURSOR);
1326 draw_line(dr, x2, y1, x2, y2, COL_CURSOR);
1327 draw_line(dr, x2, y2, x1, y2, COL_CURSOR);
1328 draw_line(dr, x1, y2, x1, y1, COL_CURSOR);
1329 }
1330
1331 draw_update(dr, hx, hy, hw, hh);
1332 }
1333}
1334
1335static void currmove_redraw(drawing *dr, game_drawstate *ds, int guess, int col)
1336{
1337 int ox = GUESS_X(guess, 0), oy = GUESS_Y(guess, 0), off = PEGSZ/4;
1338
1339 draw_rect(dr, ox-off-1, oy, 2, PEGSZ, col);
1340 draw_update(dr, ox-off-1, oy, 2, PEGSZ);
1341}
1342
1343static void game_redraw(drawing *dr, game_drawstate *ds,
1344 const game_state *oldstate, const game_state *state,
1345 int dir, const game_ui *ui,
1346 float animtime, float flashtime)
1347{
1348 int i, new_move;
1349
1350 new_move = (state->next_go != ds->next_go) || !ds->started;
1351
1352 if (!ds->started) {
1353 draw_rect(dr, 0, 0, ds->w, ds->h, COL_BACKGROUND);
1354 draw_rect(dr, SOLN_OX, SOLN_OY - ds->gapsz - 1, SOLN_W, 2, COL_FRAME);
1355 draw_update(dr, 0, 0, ds->w, ds->h);
1356 }
1357
1358 if (ds->drag_col != 0) {
1359 debug(("Loading from blitter."));
1360 blitter_load(dr, ds->blit_peg, ds->blit_ox, ds->blit_oy);
1361 draw_update(dr, ds->blit_ox, ds->blit_oy, PEGSZ, PEGSZ);
1362 }
1363
1364 /* draw the colours */
1365 for (i = 0; i < state->params.ncolours; i++) {
1366 int val = i+1;
1367 if (ui->display_cur && ui->colour_cur == i)
1368 val |= 0x1000;
1369 if (ui->show_labels)
1370 val |= 0x2000;
1371 if (ds->colours->pegs[i] != val) {
1372 draw_peg(dr, ds, COL_X(i), COL_Y(i), FALSE, ui->show_labels, i+1);
1373 if (val & 0x1000)
1374 draw_cursor(dr, ds, COL_X(i), COL_Y(i));
1375 ds->colours->pegs[i] = val;
1376 }
1377 }
1378
1379 /* draw the guesses (so far) and the hints
1380 * (in reverse order to avoid trampling holds, and postponing the
1381 * next_go'th to not overrender the top of the circular cursor) */
1382 for (i = state->params.nguesses - 1; i >= 0; i--) {
1383 if (i < state->next_go || state->solved) {
1384 /* this info is stored in the game_state already */
1385 guess_redraw(dr, ds, i, state->guesses[i], NULL, -1, 0,
1386 ui->show_labels);
1387 hint_redraw(dr, ds, i, state->guesses[i],
1388 i == (state->next_go-1) ? 1 : 0, FALSE, FALSE);
1389 } else if (i > state->next_go) {
1390 /* we've not got here yet; it's blank. */
1391 guess_redraw(dr, ds, i, NULL, NULL, -1, 0, ui->show_labels);
1392 hint_redraw(dr, ds, i, NULL, 0, FALSE, FALSE);
1393 }
1394 }
1395 if (!state->solved) {
1396 /* this is the one we're on; the (incomplete) guess is stored in
1397 * the game_ui. */
1398 guess_redraw(dr, ds, state->next_go, ui->curr_pegs,
1399 ui->holds, ui->display_cur ? ui->peg_cur : -1, 0,
1400 ui->show_labels);
1401 hint_redraw(dr, ds, state->next_go, NULL, 1,
1402 ui->display_cur && ui->peg_cur == state->params.npegs,
1403 ui->markable);
1404 }
1405
1406 /* draw the 'current move' and 'able to mark' sign. */
1407 if (new_move)
1408 currmove_redraw(dr, ds, ds->next_go, COL_BACKGROUND);
1409 if (!state->solved)
1410 currmove_redraw(dr, ds, state->next_go, COL_HOLD);
1411
1412 /* draw the solution (or the big rectangle) */
1413 if ((!state->solved ^ !ds->solved) || !ds->started) {
1414 draw_rect(dr, SOLN_OX, SOLN_OY, SOLN_W, SOLN_H,
1415 state->solved ? COL_BACKGROUND : COL_EMPTY);
1416 draw_update(dr, SOLN_OX, SOLN_OY, SOLN_W, SOLN_H);
1417 }
1418 if (state->solved)
1419 guess_redraw(dr, ds, -1, state->solution, NULL, -1, !ds->solved,
1420 ui->show_labels);
1421 ds->solved = state->solved;
1422
1423 ds->next_go = state->next_go;
1424
1425 /* if ui->drag_col != 0, save the screen to the blitter,
1426 * draw the peg where we saved, and set ds->drag_* == ui->drag_*. */
1427 if (ui->drag_col != 0) {
1428 int ox = ui->drag_x - (PEGSZ/2);
1429 int oy = ui->drag_y - (PEGSZ/2);
1430 ds->blit_ox = ox - 1; ds->blit_oy = oy - 1;
1431 debug(("Saving to blitter at (%d,%d)", ds->blit_ox, ds->blit_oy));
1432 blitter_save(dr, ds->blit_peg, ds->blit_ox, ds->blit_oy);
1433 draw_peg(dr, ds, ox, oy, TRUE, ui->show_labels, ui->drag_col);
1434 }
1435 ds->drag_col = ui->drag_col;
1436
1437 ds->started = 1;
1438}
1439
1440static float game_anim_length(const game_state *oldstate,
1441 const game_state *newstate, int dir, game_ui *ui)
1442{
1443 return 0.0F;
1444}
1445
1446static float game_flash_length(const game_state *oldstate,
1447 const game_state *newstate, int dir, game_ui *ui)
1448{
1449 return 0.0F;
1450}
1451
1452static int game_status(const game_state *state)
1453{
1454 /*
1455 * We return nonzero whenever the solution has been revealed, even
1456 * (on spoiler grounds) if it wasn't guessed correctly. The
1457 * correct return value from this function is already in
1458 * state->solved.
1459 */
1460 return state->solved;
1461}
1462
1463static int game_timing_state(const game_state *state, game_ui *ui)
1464{
1465 return TRUE;
1466}
1467
1468static void game_print_size(const game_params *params, float *x, float *y)
1469{
1470}
1471
1472static void game_print(drawing *dr, const game_state *state, int tilesize)
1473{
1474}
1475
1476#ifdef COMBINED
1477#define thegame guess
1478#endif
1479
1480const struct game thegame = {
1481 "Guess", "games.guess", "guess",
1482 default_params,
1483 game_fetch_preset, NULL,
1484 decode_params,
1485 encode_params,
1486 free_params,
1487 dup_params,
1488 TRUE, game_configure, custom_params,
1489 validate_params,
1490 new_game_desc,
1491 validate_desc,
1492 new_game,
1493 dup_game,
1494 free_game,
1495 TRUE, solve_game,
1496 FALSE, game_can_format_as_text_now, game_text_format,
1497 new_ui,
1498 free_ui,
1499 encode_ui,
1500 decode_ui,
1501 game_changed_state,
1502 interpret_move,
1503 execute_move,
1504 PEG_PREFER_SZ, game_compute_size, game_set_size,
1505 game_colours,
1506 game_new_drawstate,
1507 game_free_drawstate,
1508 game_redraw,
1509 game_anim_length,
1510 game_flash_length,
1511 game_status,
1512 FALSE, FALSE, game_print_size, game_print,
1513 FALSE, /* wants_statusbar */
1514 FALSE, game_timing_state,
1515 0, /* flags */
1516};
1517
1518/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/guess.html b/apps/plugins/puzzles/src/guess.html
new file mode 100644
index 0000000000..b530f71a75
--- /dev/null
+++ b/apps/plugins/puzzles/src/guess.html
@@ -0,0 +1,94 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Guess</title>
7<link rel="previous" href="flip.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="pegs.html">
12</head>
13<body>
14<p><a href="flip.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="pegs.html">Next</a></p>
15<h1><a name="C15"></a>Chapter 15: <a name="i0"></a>Guess</h1>
16<p>
17You have a set of coloured pegs, and have to reproduce a predetermined sequence of them (chosen by the computer) within a certain number of guesses.
18</p>
19<p>
20Each guess gets marked with the number of correctly-coloured pegs in the correct places (in black), and also the number of correctly-coloured pegs in the wrong places (in white).
21</p>
22<p>
23This game is also known (and marketed, by Hasbro, mainly) as a board game &#8216;<a name="i1"></a>Mastermind&#8217;, with 6 colours, 4 pegs per row, and 10 guesses. However, this version allows custom settings of number of colours (up to 10), number of pegs per row, and number of guesses.
24</p>
25<p>
26Guess was contributed to this collection by James Harvey.
27</p>
28<h2><a name="S15.1"></a>15.1 <a name="i2"></a>Guess controls</h2>
29<p>
30This game can be played with either the keyboard or the mouse.
31</p>
32<p>
33With the mouse, drag a coloured peg from the tray on the left-hand side to its required position in the current guess; pegs may also be dragged from current and past guesses to copy them elsewhere. To remove a peg, drag it off its current position to somewhere invalid.
34</p>
35<p>
36Right-clicking in the current guess adds a &#8216;hold&#8217; marker; pegs that have hold markers will be automatically added to the next guess after marking.
37</p>
38<p>
39Alternatively, with the keyboard, the up and down cursor keys can be used to select a peg colour, the left and right keys to select a peg position, and the space bar or Enter key to place a peg of the selected colour in the chosen position. &#8216;D&#8217; or Backspace removes a peg, and Space adds a hold marker.
40</p>
41<p>
42Pressing &#8216;h&#8217; or &#8216;?&#8217; will fill the current guess with a suggested guess. Using this is not recommended for 10 or more pegs as it is slow.
43</p>
44<p>
45When the guess is complete, the smaller feedback pegs will be highlighted; clicking on these (or moving the peg cursor to them with the arrow keys and pressing the space bar or Enter key) will mark the current guess, copy any held pegs to the next guess, and move the &#8216;current guess&#8217; marker.
46</p>
47<p>
48If you correctly position all the pegs the solution will be displayed below; if you run out of guesses (or select &#8216;Solve...&#8217;) the solution will also be revealed.
49</p>
50<p>
51(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
52</p>
53<h2><a name="S15.2"></a>15.2 <a name="i3"></a>Guess parameters</h2>
54<p>
55These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu. The default game matches the parameters for the board game &#8216;Mastermind&#8217;.
56</p>
57<dl><dt>
58<em>Colours</em>
59</dt>
60<dd>
61Number of colours the solution is chosen from; from 2 to 10 (more is harder).
62</dd>
63<dt>
64<em>Pegs per guess</em>
65</dt>
66<dd>
67Number of pegs per guess (more is harder).
68</dd>
69<dt>
70<em>Guesses</em>
71</dt>
72<dd>
73Number of guesses you have to find the solution in (fewer is harder).
74</dd>
75<dt>
76<em>Allow blanks</em>
77</dt>
78<dd>
79Allows blank pegs to be given as part of a guess (makes it easier, because you know that those will never be counted as part of the solution). This is turned off by default.
80<p>
81Note that this doesn't allow blank pegs in the solution; if you really wanted that, use one extra colour.
82</p>
83
84</dd>
85<dt>
86<em>Allow duplicates</em>
87</dt>
88<dd>
89Allows the solution (and the guesses) to contain colours more than once; this increases the search space (making things harder), and is turned on by default.
90</dd>
91</dl>
92
93<hr><address></address></body>
94</html>
diff --git a/apps/plugins/puzzles/src/html/blackbox.html b/apps/plugins/puzzles/src/html/blackbox.html
new file mode 100644
index 0000000000..f98604f12f
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/blackbox.html
@@ -0,0 +1,16 @@
1Black Box
2<p>
3Determine where the hidden balls are in the box, by observing the
4behaviour of light beams fired into the box from the sides.
5<p>
6Click in a square around the edge of the box to send a beam into the
7box. Possible results are 'H' (the beam hit a ball dead-on and
8stopped), 'R' (the beam was either reflected back the way it came or
9there was a ball just to one side of its entry point) or a number
10appearing in two squares (indicating that the beam entered one of
11those squares and emerged from the other).
12<p>
13Click in the middle of the box to place your guessed ball positions.
14When you have placed enough, a green button will appear in the top
15left; click that to indicate that you think you have the answer.
16You can also right-click to mark squares as definitely known.
diff --git a/apps/plugins/puzzles/src/html/bridges.html b/apps/plugins/puzzles/src/html/bridges.html
new file mode 100644
index 0000000000..06ec2d4d4b
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/bridges.html
@@ -0,0 +1,13 @@
1Bridges
2<p>
3Draw horizontal or vertical bridges to link up all the islands.
4Bridges may be single or double; they may not cross; the islands
5must all end up connected to each other; the number in each island
6must match the number of bridges that end at that island (counting
7double bridges as two). Note that loops of bridges are permitted.
8<p>
9Click on an island and drag left, right, up or down to draw a bridge
10to the next island in that direction. Do the same again to create a
11double bridge, and again to remove the bridge if you change your
12mind. Click on an island without dragging to mark the island as
13completed once you think you have placed all its bridges.
diff --git a/apps/plugins/puzzles/src/html/cube.html b/apps/plugins/puzzles/src/html/cube.html
new file mode 100644
index 0000000000..f08e16c38d
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/cube.html
@@ -0,0 +1,14 @@
1Cube
2<p>
3Roll the cube around the grid, picking up the blue squares on its
4faces. Try to get all the blue squares on to the object at the same
5time, in as few moves as possible.
6<p>
7Use the arrow keys to roll the cube, or click the mouse where you
8want it to roll towards. After every roll, the grid square and cube
9face that you brought into contact swap their colours, so that a
10non-blue cube face can pick up a blue square, but a blue face rolled
11on to a non-blue square puts it down again.
12<p>
13When you have mastered the cube, use the Type menu to select other
14regular solids!
diff --git a/apps/plugins/puzzles/src/html/dominosa.html b/apps/plugins/puzzles/src/html/dominosa.html
new file mode 100644
index 0000000000..d2f672806a
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/dominosa.html
@@ -0,0 +1,10 @@
1Dominosa
2<p>
3Tile the rectangle with dominoes (1&times;2 rectangles) so that
4every possible domino appears exactly once (that is, every possible
5pair of numbers, including doubles).
6<p>
7Click between two adjacent numbers to place or remove a domino.
8Right-click to place a line between numbers if you think a domino
9definitely cannot go there. Dominoes light up red if two identical
10ones appear on the grid.
diff --git a/apps/plugins/puzzles/src/html/fifteen.html b/apps/plugins/puzzles/src/html/fifteen.html
new file mode 100644
index 0000000000..53053b440d
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/fifteen.html
@@ -0,0 +1,6 @@
1Fifteen
2<p>
3Slide the tiles around the box until they appear in numerical order
4from the top left, with the hole in the bottom right corner.
5<p>
6Click on a tile to slide it towards the hole.
diff --git a/apps/plugins/puzzles/src/html/filling.html b/apps/plugins/puzzles/src/html/filling.html
new file mode 100644
index 0000000000..70ce16d4a9
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/filling.html
@@ -0,0 +1,12 @@
1Filling
2<p>
3Write a number in every blank square of the grid. When the grid is
4full, every orthogonally connected group of identical numbers should
5have an area equal to that number: so 1s always appear alone, 2s in
6pairs, and so on.
7<p>
8To place a number, click the mouse in a blank square to select it,
9then type the number you want on the keyboard. You can also drag to
10select multiple squares, and then type a number to place it in all
11of them. To erase numbers, select one or more squares in the same
12way and then press Backspace.
diff --git a/apps/plugins/puzzles/src/html/flip.html b/apps/plugins/puzzles/src/html/flip.html
new file mode 100644
index 0000000000..404aae6b9a
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/flip.html
@@ -0,0 +1,10 @@
1Flip
2<p>
3Try to light up all the squares in the grid by flipping combinations
4of them.
5<p>
6Click in a square to flip it and some of its neighbours. The diagram
7in each square indicates which other squares will flip.
8<p>
9Select one of the 'Random' settings from the Type menu for more
10varied puzzles.
diff --git a/apps/plugins/puzzles/src/html/flood.html b/apps/plugins/puzzles/src/html/flood.html
new file mode 100644
index 0000000000..cf09eac766
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/flood.html
@@ -0,0 +1,8 @@
1Flood
2<p>
3Try to get the whole grid to be the same colour within the given
4number of moves, by repeatedly flood-filling the top left corner in
5different colours.
6<p>
7Click in a square to flood-fill the top left corner with that square's
8colour.
diff --git a/apps/plugins/puzzles/src/html/galaxies.html b/apps/plugins/puzzles/src/html/galaxies.html
new file mode 100644
index 0000000000..8041a95cee
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/galaxies.html
@@ -0,0 +1,11 @@
1Galaxies
2<p>
3Draw lines along grid edges so as to divide the grid up into
4regions. Every region should have two-way rotational symmetry, and
5should contain exactly one dot which is in its centre.
6<p>
7Click on a grid edge to add or remove a line. Right-click on a dot
8and drag the mouse to place an arrow in a grid square pointing to
9that dot, to indicate that you think that square must belong in the
10same region as that dot. Right-drag an existing arrow to move it, or
11drop it off the edge of the grid to remove it.
diff --git a/apps/plugins/puzzles/src/html/group.html b/apps/plugins/puzzles/src/html/group.html
new file mode 100644
index 0000000000..c0f52de629
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/group.html
@@ -0,0 +1,52 @@
1unfinished:Group
2<p>
3Fill in the grid with the letters shown to the top and left of it, so
4that the full grid is a valid
5<a href="http://en.wikipedia.org/wiki/Cayley_table">Cayley table</a>
6for a
7<a href="http://en.wikipedia.org/wiki/Group_(mathematics)">group</a>.
8<p>
9If you don't already know what a group is, I don't really recommend
10trying to play this game. But if you want to try anyway, the above is
11equivalent to saying that the following conditions must be satisfied:
12<ul>
13<li>
14<strong>Latin square</strong>. Every row and column must contain
15exactly one of each letter.
16<li>
17<strong>Identity</strong>. There must be some letter <i>e</i> such
18that, for all <i>a</i>, the letter in row <i>e</i> column <i>a</i> and
19the one in row <i>a</i> column <i>e</i> are both <i>a</i>. In the
20default mode, this letter is always <i>e</i> and its row and column
21are filled in for you; by reconfiguring the game using the Type menu,
22you can select a mode in which you have to work out which letter is
23the identity.
24<li>
25<strong>Inverses</strong>. For every letter <i>a</i>, there must be
26some letter <i>b</i> (which may sometimes be the same letter
27as <i>a</i>) such that the letters in row <i>a</i> column <i>b</i> and
28in row <i>b</i> column <i>a</i> are both the identity letter (as
29defined above).
30<li>
31<strong>Associativity</strong>. For every combination of
32letters <i>a</i>, <i>b</i>, and <i>c</i>, denote the letter in
33row <i>a</i> column <i>b</i> by <i>d</i>, and the one in row <i>b</i>
34column <i>c</i> by <i>e</i>. Then the letters in row <i>d</i>
35column <i>c</i> and in row <i>a</i> column <i>e</i> must be the same.
36</ul>
37<p>
38To place a letter, click in a square to select it, then type the
39letter on the keyboard. To erase a letter, click to select a square
40and then press Backspace.
41<p>
42Right-click in a square and then type a letter to add or remove the
43number as a pencil mark, indicating letters that you think
44<em>might</em> go in that square.
45<p>
46You can rearrange the order of elements in the rows and columns by
47dragging the column or row headings back and forth. (The rows and
48columns will stay in sync with each other.) Also,
49left-clicking <em>between</em> two row or column headings will add or
50remove a thick line between those two rows and the corresponding pair
51of columns (which is useful if you're considering a subgroup and its
52cosets).
diff --git a/apps/plugins/puzzles/src/html/guess.html b/apps/plugins/puzzles/src/html/guess.html
new file mode 100644
index 0000000000..1e4fc1ed04
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/guess.html
@@ -0,0 +1,12 @@
1Guess
2<p>
3Try to guess the hidden combination of colours. You will be given
4limited information about each guess you make, enabling you to
5refine the next guess.
6<p>
7Drag from the colours on the left into the topmost unfilled row to
8make a guess; then click on the small circles to submit that guess.
9The small circles give you your feedback: black pegs indicate how
10many of the colours you guessed were the right colour in the right
11place, and white pegs indicate how many of the rest were the right
12colours but in the wrong place.
diff --git a/apps/plugins/puzzles/src/html/inertia.html b/apps/plugins/puzzles/src/html/inertia.html
new file mode 100644
index 0000000000..20077c0048
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/inertia.html
@@ -0,0 +1,14 @@
1Inertia
2<p>
3Slide the ball around the grid picking up the gems. Every time the
4ball moves, it will keep sliding until it either hits a wall, or
5stops on a stop square (the broken circles). Try to collect every
6gem without running into any of the mines.
7<p>
8Use the numeric keypad to slide the ball horizontally, vertically or
9diagonally. Alternatively, click on the grid to make the ball move
10towards where you clicked.
11<p>
12If you hit a mine and explode, you can select Undo from the Game
13menu and continue playing; the game will track how many times you
14died.
diff --git a/apps/plugins/puzzles/src/html/javapage.pl b/apps/plugins/puzzles/src/html/javapage.pl
new file mode 100755
index 0000000000..cd5e6a1669
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/javapage.pl
@@ -0,0 +1,104 @@
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6open my $footerfile, "<", shift @ARGV or die "footer: open: $!\n";
7my $footer = "";
8$footer .= $_ while <$footerfile>;
9close $footerfile;
10
11for my $arg (@ARGV) {
12 $arg =~ /(.*\/)?([^\/]+)\.html$/ or die;
13 my $filename = $2;
14 open my $gamefile, "<", $arg or die "$arg: open: $!\n";
15 my $unfinished = 0;
16 my $docname = $filename;
17 chomp(my $puzzlename = <$gamefile>);
18 while ($puzzlename =~ s/^([^:=]+)(=([^:]+))?://) {
19 if ($1 eq "unfinished") {
20 $unfinished = 1;
21 } elsif ($1 eq "docname") {
22 $docname = $3;
23 } else {
24 die "$arg: unknown keyword '$1'\n";
25 }
26 }
27 my $instructions = "";
28 $instructions .= $_ while <$gamefile>;
29 close $gamefile;
30
31 open my $outpage, ">", "${filename}.html";
32
33 my $unfinishedtitlefragment = $unfinished ? "an unfinished puzzle " : "";
34 my $unfinishedheading = $unfinished ? "<h2 align=center>an unfinished puzzle</h2>\n" : "";
35 my $unfinishedpara;
36 my $links;
37 if ($unfinished) {
38 $unfinishedpara = <<EOF;
39<p>
40You have found your way to a page containing an <em>unfinished</em>
41puzzle in my collection, not linked from the <a href="../">main
42puzzles page</a>. Don't be surprised if things are hard to understand
43or don't work as you expect.
44EOF
45 $links = <<EOF;
46<p align="center">
47<a href="../">Back to main puzzles page</a> (which does not link to this)
48EOF
49 } else {
50 $unfinishedpara = "";
51 $links = <<EOF;
52<p align="center">
53<a href="../doc/${docname}.html#${docname}">Full instructions</a>
54|
55<a href="../">Back to main puzzles page</a>
56EOF
57 }
58
59 print $outpage <<EOF;
60<html>
61<head>
62<title>${puzzlename}, ${unfinishedtitlefragment}from Simon Tatham's Portable Puzzle Collection</title>
63<link rel="stylesheet" type="text/css" href="../../sitestyle.css" name="Simon Tatham's Home Page Style">
64<script type="text/javascript" src="resize-puzzle-applet.js"></script>
65</head>
66<body onLoad="initResizablePuzzleApplet();">
67<h1 align=center>${puzzlename}</h1>
68${unfinishedheading}
69<h2 align=center>from Simon Tatham's Portable Puzzle Collection</h2>
70
71${unfinishedpara}
72
73<p align="center">
74<table cellpadding="0">
75<tr>
76<td>
77<applet id="applet" archive="${filename}.jar" code="PuzzleApplet"
78 width="700" height="500">
79</applet>
80</td>
81<td>
82<div id="eresize" style="width:5px;height:500px;cursor:e-resize;"></div>
83</td>
84</tr>
85<td>
86<div id="sresize" style="width:700px;height:5px;cursor:s-resize;"></div>
87</td>
88<td>
89<div id="seresize" style="width:5px;height:5px;cursor:se-resize;"></div>
90</td>
91</tr>
92</table>
93
94${instructions}
95
96${links}
97
98${footer}
99</body>
100</html>
101EOF
102
103 close $outpage;
104}
diff --git a/apps/plugins/puzzles/src/html/jspage.pl b/apps/plugins/puzzles/src/html/jspage.pl
new file mode 100755
index 0000000000..a21f977166
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/jspage.pl
@@ -0,0 +1,242 @@
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6open my $footerfile, "<", shift @ARGV or die "footer: open: $!\n";
7my $footer = "";
8$footer .= $_ while <$footerfile>;
9close $footerfile;
10
11for my $arg (@ARGV) {
12 $arg =~ /(.*\/)?([^\/]+)\.html$/ or die;
13 my $filename = $2;
14 open my $gamefile, "<", $arg or die "$arg: open: $!\n";
15 my $unfinished = 0;
16 my $docname = $filename;
17 chomp(my $puzzlename = <$gamefile>);
18 while ($puzzlename =~ s/^([^:=]+)(=([^:]+))?://) {
19 if ($1 eq "unfinished") {
20 $unfinished = 1;
21 } elsif ($1 eq "docname") {
22 $docname = $3;
23 } else {
24 die "$arg: unknown keyword '$1'\n";
25 }
26 }
27 my $instructions = "";
28 $instructions .= $_ while <$gamefile>;
29 close $gamefile;
30
31 open my $outpage, ">", "${filename}.html";
32
33 my $unfinishedtitlefragment = $unfinished ? "an unfinished puzzle " : "";
34 my $unfinishedheading = $unfinished ? "<h2 align=center>an unfinished puzzle</h2>\n" : "";
35 my $unfinishedpara;
36 my $links;
37 if ($unfinished) {
38 $unfinishedpara = <<EOF;
39<p>
40You have found your way to a page containing an <em>unfinished</em>
41puzzle in my collection, not linked from the <a href="../">main
42puzzles page</a>. Don't be surprised if things are hard to understand
43or don't work as you expect.
44EOF
45 $links = <<EOF;
46<p align="center">
47<a href="../">Back to main puzzles page</a> (which does not link to this)
48EOF
49 } else {
50 $unfinishedpara = "";
51 $links = <<EOF;
52<p align="center">
53<a href="../doc/${docname}.html#${docname}">Full instructions</a>
54|
55<a href="../">Back to main puzzles page</a>
56EOF
57 }
58
59 print $outpage <<EOF;
60<!DOCTYPE html>
61<html>
62<head>
63<meta http-equiv="Content-Type" content="text/html; charset=ASCII" />
64<title>${puzzlename}, ${unfinishedtitlefragment}from Simon Tatham's Portable Puzzle Collection</title>
65<script type="text/javascript" src="${filename}.js"></script>
66<style class="text/css">
67/* Margins and centring on the top-level div for the game menu */
68#gamemenu { margin-top: 0; margin-bottom: 0.5em; text-align: center }
69
70/* Inside that div, the main menu bar and every submenu inside it is a <ul> */
71#gamemenu ul {
72 list-style: none; /* get rid of the normal unordered-list bullets */
73 display: inline; /* make top-level menu bar items appear side by side */
74 position: relative; /* allow submenus to position themselves near parent */
75 margin: 0;
76 margin-bottom: 0.5em;
77 padding: 0;
78}
79
80/* Individual menu items are <li> elements within such a <ul> */
81#gamemenu ul li {
82 /* Add a little mild text formatting */
83 font-weight: bold; font-size: 0.8em;
84 /* Line height and padding appropriate to top-level menu items */
85 padding-left: 0.75em; padding-right: 0.75em;
86 padding-top: 0.2em; padding-bottom: 0.2em;
87 margin: 0;
88 /* Make top-level menu items appear side by side, not vertically stacked */
89 display: inline;
90 /* Suppress the text-selection I-beam pointer */
91 cursor: default;
92 /* Surround each menu item with a border. The left border is removed
93 * because it will abut the right border of the previous item. (A rule
94 * below will reinstate the left border for the leftmost menu item.) */
95 border-left: 0;
96 border-right: 1px solid rgba(0,0,0,0.3);
97 border-top: 1px solid rgba(0,0,0,0.3);
98 border-bottom: 1px solid rgba(0,0,0,0.3);
99}
100
101#gamemenu ul li.disabled {
102 /* Grey out menu items with the "disabled" class */
103 color: rgba(0,0,0,0.5);
104}
105
106#gamemenu ul li:first-of-type {
107 /* Reinstate the left border for the leftmost top-level menu item */
108 border-left: 1px solid rgba(0,0,0,0.3);
109}
110
111#gamemenu ul li:hover {
112 /* When the mouse is over a menu item, highlight it */
113 background: rgba(0,0,0,0.3);
114 /* Set position:relative, so that if this item has a submenu it can
115 * position itself relative to the parent item. */
116 position: relative;
117}
118
119#gamemenu ul li.disabled:hover {
120 /* Disabled menu items don't get a highlight on mouse hover */
121 background: inherit;
122}
123
124#gamemenu ul ul {
125 /* Second-level menus and below are not displayed by default */
126 display: none;
127 /* When they are displayed, they are positioned immediately below
128 * their parent <li>, and with the left edge aligning */
129 position: absolute;
130 top: 100%;
131 left: 0;
132 /* We must specify an explicit background colour for submenus, because
133 * they must be opaque (don't want other page contents showing through
134 * them). */
135 background: white;
136 /* And make sure they appear in front. */
137 z-index: 1;
138}
139
140#gamemenu ul ul.left {
141 /* A second-level menu with class "left" aligns its right edge with
142 * its parent, rather than its left edge */
143 left: inherit; right: 0;
144}
145
146/* Menu items in second-level menus and below */
147#gamemenu ul ul li {
148 /* Go back to vertical stacking, for drop-down submenus */
149 display: block;
150 /* Inhibit wrapping, so the submenu will expand its width as needed. */
151 white-space: nowrap;
152 /* Override the text-align:center from above */
153 text-align: left;
154 /* Don't make the text any smaller than the previous level of menu */
155 font-size: 100%;
156 /* This time it's the top border that we omit on all but the first
157 * element in the submenu, since now they're vertically stacked */
158 border-left: 1px solid rgba(0,0,0,0.3);
159 border-right: 1px solid rgba(0,0,0,0.3);
160 border-top: 0;
161 border-bottom: 1px solid rgba(0,0,0,0.3);
162}
163
164#gamemenu ul ul li:first-of-type {
165 /* Reinstate top border for first item in a submenu */
166 border-top: 1px solid rgba(0,0,0,0.3);
167}
168
169#gamemenu ul ul ul {
170 /* Third-level submenus are drawn to the side of their parent menu
171 * item, not below it */
172 top: 0; left: 100%;
173}
174
175#gamemenu ul ul ul.left {
176 /* A submenu with class "left" goes to the left of its parent,
177 * not the right */
178 left: inherit; right: 100%;
179}
180
181#gamemenu ul li:hover > ul {
182 /* Last but by no means least, the all-important line that makes
183 * submenus be displayed! Any <ul> whose parent <li> is being
184 * hovered over gets display:block overriding the display:none
185 * from above. */
186 display: block;
187}
188</style>
189</head>
190<body onLoad="initPuzzle();">
191<h1 align=center>${puzzlename}</h1>
192${unfinishedheading}
193<h2 align=center>from Simon Tatham's Portable Puzzle Collection</h2>
194
195${unfinishedpara}
196
197<hr>
198<div id="puzzle" style="display: none">
199<div id="gamemenu"><ul><li id="new">New game</li
200><li id="restart">Restart game</li
201><li id="undo">Undo move</li
202><li id="redo">Redo move</li
203><li id="solve">Solve game</li
204><li id="specific">Enter game ID</li
205><li id="random">Enter random seed</li
206><li>Select game type<ul id="gametype" class="left"></ul></li
207></ul></div>
208<div align=center>
209 <div id="resizable" style="position:relative; left:0; top:0">
210 <canvas style="display: block" id="puzzlecanvas" width="1px" height="1px" tabindex="1">
211 </canvas>
212 <div id="statusbarholder" style="display: block">
213 </div>
214 </div>
215 <p>
216 Link to this puzzle:
217 <a id="permalink-desc">by game ID</a>
218 <a id="permalink-seed">by random seed</a>
219 </p>
220</div>
221</div>
222<div id="apology">
223Sorry, this Javascript puzzle doesn't seem to work in your web
224browser. Perhaps you have Javascript disabled, or perhaps your browser
225doesn't provide a feature that the puzzle code requires (such as
226<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Typed_arrays">typed arrays</a>).
227These puzzles have been successfully run in Firefox 19, Chrome 26,
228Internet Explorer 10 and Safari 6.
229</div>
230<hr>
231
232${instructions}
233
234${links}
235
236${footer}
237</body>
238</html>
239EOF
240
241 close $outpage;
242}
diff --git a/apps/plugins/puzzles/src/html/keen.html b/apps/plugins/puzzles/src/html/keen.html
new file mode 100644
index 0000000000..bd0eb3644d
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/keen.html
@@ -0,0 +1,15 @@
1Keen
2<p>
3Fill in the grid with digits from 1 to the grid size, so that every
4digit appears exactly once in each row and column, and so that all
5the arithmetic clues are satisfied (i.e. the clue number in each
6thick box should be possible to construct from the digits in the box
7using the specified arithmetic operation).
8<p>
9To place a number, click in a square to select it, then type the
10number on the keyboard. To erase a number, click to select a square
11and then press Backspace.
12<p>
13Right-click in a square and then type a number to add or remove the
14number as a pencil mark, indicating numbers that you think
15<em>might</em> go in that square.
diff --git a/apps/plugins/puzzles/src/html/lightup.html b/apps/plugins/puzzles/src/html/lightup.html
new file mode 100644
index 0000000000..2de2f91bb9
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/lightup.html
@@ -0,0 +1,10 @@
1Light Up
2<p>
3Place light bulbs in the grid so as to light up all the blank
4squares. A light illuminates its own square and all the squares in
5the same row or column unless blocked by walls (black squares).
6Lights may not illuminate each other. Each numbered square must be
7orthogonally adjacent to exactly the given number of lights.
8<p>
9Click on a square to place or remove a light. Right-click to place a
10dot indicating that you think there is no light in that square.
diff --git a/apps/plugins/puzzles/src/html/loopy.html b/apps/plugins/puzzles/src/html/loopy.html
new file mode 100644
index 0000000000..96f3a9d908
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/loopy.html
@@ -0,0 +1,13 @@
1Loopy
2<p>
3Form a single closed loop out of the grid edges, in such a way that
4every numbered square has exactly that many of its edges included in
5the loop.
6<p>
7Click on a grid edge to mark it as part of the loop (black), and
8again to return to marking it as undecided (yellow). Right-click on
9a grid edge to mark it as definitely not part of the loop (faint
10grey), and again to mark it as undecided again.
11<p>
12When you have mastered the square grid, look in the Type menu for
13many other types of tiling!
diff --git a/apps/plugins/puzzles/src/html/magnets.html b/apps/plugins/puzzles/src/html/magnets.html
new file mode 100644
index 0000000000..2807569a6c
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/magnets.html
@@ -0,0 +1,17 @@
1Magnets
2<p>
3Fill each domino shape with either a magnet (consisting of a + and
4&#8722; pole) or a neutral domino (green).
5<p>
6The number of + poles that in each row and column must match the
7numbers along the top and left; the number of &#8722; poles must
8match the numbers along the bottom and right. Two + poles may not be
9orthogonally adjacent to each other, and similarly two &#8722; poles.
10<p>
11Left-click a domino to toggle it between being empty and being a
12magnet (the + is placed in the end you click). Right-click to toggle
13between empty, neutral, and a ?? mark indicating that you're sure
14it's a magnet but don't yet know which way round it goes.
15<p>
16Left-click a clue to mark it as done (grey it out). To unmark a clue
17as done, left-click it again.
diff --git a/apps/plugins/puzzles/src/html/map.html b/apps/plugins/puzzles/src/html/map.html
new file mode 100644
index 0000000000..5f81793054
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/map.html
@@ -0,0 +1,15 @@
1Map
2<p>
3Colour the map with four colours, so that no two adjacent regions
4have the same colour. (Regions touching at only one corner do not
5count as adjacent.) There is a unique colouring consistent with the
6coloured regions you are already given.
7<p>
8Drag from a coloured region to a blank one to colour the latter the
9same colour as the former. Drag from outside the grid into a region
10to erase its colour. (You cannot change the colours of the regions
11you are given at the start of the game.)
12<p>
13Right-drag from a coloured region to a blank one to add dots marking
14the latter region as <em>possibly</em> the same colour as the
15former, or to remove those dots again.
diff --git a/apps/plugins/puzzles/src/html/mines.html b/apps/plugins/puzzles/src/html/mines.html
new file mode 100644
index 0000000000..d17d6ffa80
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/mines.html
@@ -0,0 +1,18 @@
1Mines
2<p>
3Try to expose every square in the grid that is not one of the hidden
4mines, without opening any square that is a mine.
5<p>
6Click in a square to open it. Every opened square are marked with
7the number of mines in the surrounding 8 squares, if there are any;
8if not, all the surrounding squares are automatically opened.
9<p>
10Right-click in a square to mark it with a flag if you think it is a
11mine. If a numbered square has exactly the right number of flags
12around it, you can click in it to open all the squares around it
13that are not flagged.
14<p>
15The first square you open is guaranteed to be safe, and (by default)
16you are guaranteed to be able to solve the whole grid by deduction
17rather than guesswork. (Deductions may require you to think about
18the total number of mines.)
diff --git a/apps/plugins/puzzles/src/html/net.html b/apps/plugins/puzzles/src/html/net.html
new file mode 100644
index 0000000000..08bffbac3e
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/net.html
@@ -0,0 +1,17 @@
1Net
2<p>
3Rotate the grid squares so that they all join up into a single
4connected network with no loops.
5<p>
6Left-click in a square to rotate it anticlockwise. Right-click to
7rotate it clockwise. Middle-click, or shift-left-click if you have
8no middle mouse button, to lock a square once you think it is
9correct (so you don't accidentally rotate it again); do the same
10again to unlock it if you change your mind.
11<p>
12Squares connected to the middle square are lit up. Aim to light up
13every square in the grid (not just the endpoint blobs).
14<p>
15When this gets too easy, select a 'wrapping' variant from the Type
16menu to enable grid lines to run off one edge of the playing area
17and come back on the opposite edge!
diff --git a/apps/plugins/puzzles/src/html/netslide.html b/apps/plugins/puzzles/src/html/netslide.html
new file mode 100644
index 0000000000..f1877417d4
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/netslide.html
@@ -0,0 +1,14 @@
1Netslide
2<p>
3Slide the grid squares around so that they all join up into a single
4connected network with no loops.
5<p>
6Click on the arrows at the edges of the grid to move a row or column
7left, right, up or down. The square that falls off the end of the
8row comes back on the other end.
9<p>
10Squares connected to the middle square are lit up. Aim to light up
11every square in the grid (not just the endpoint blobs).
12<p>
13Connecting across a red barrier line is forbidden. On harder levels,
14there are fewer barriers, which makes it harder rather than easier!
diff --git a/apps/plugins/puzzles/src/html/palisade.html b/apps/plugins/puzzles/src/html/palisade.html
new file mode 100644
index 0000000000..5b6b933104
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/palisade.html
@@ -0,0 +1,11 @@
1Palisade
2<p>
3Draw lines along the grid edges, in such a way that the grid is
4divided into connected regions, all of the size shown in the status
5line. Also, each square containing a number should have that many of
6its edges drawn in.
7<p>
8Click on a grid edge to mark it as a division between regions (black),
9and again to return to marking it as undecided (yellow). Right-click
10on a grid edge to mark it as definitely not part of the loop (faint
11grey), and again to mark it as undecided again.
diff --git a/apps/plugins/puzzles/src/html/pattern.html b/apps/plugins/puzzles/src/html/pattern.html
new file mode 100644
index 0000000000..54e05d6416
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/pattern.html
@@ -0,0 +1,12 @@
1Pattern
2<p>
3Fill in the grid with a pattern of black and white squares, so that
4the numbers in each row and column match the lengths of consecutive
5runs of black squares.
6<p>
7Left-click in a square to mark it black; right-click (or hold Ctrl
8while left-clicking) to mark it white. Click and drag along a row or
9column to mark multiple squares black or white at once. Middle-click
10(or hold Shift while left-clicking) to return a square to grey
11(meaning undecided): dragging like that can erase a whole rectangle,
12not just a row or column.
diff --git a/apps/plugins/puzzles/src/html/pearl.html b/apps/plugins/puzzles/src/html/pearl.html
new file mode 100644
index 0000000000..2ca25a5ee0
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/pearl.html
@@ -0,0 +1,13 @@
1Pearl
2<p>
3Draw a single closed loop by connecting together the centres of
4adjacent grid squares, so that some squares end up as corners, some as
5straights (horizontal or vertical), and some may be empty. Every
6square containing a black circle must be a corner not connected
7directly to another corner; every square containing a white circle
8must be a straight which is connected to <em>at least one</em> corner.
9<p>
10Drag between squares to draw or undraw pieces of the loop.
11Alternatively, left-click the edge between two squares to turn it on
12or off. Right-click an edge to mark it with a cross indicating that
13you are sure the loop does not go through it.
diff --git a/apps/plugins/puzzles/src/html/pegs.html b/apps/plugins/puzzles/src/html/pegs.html
new file mode 100644
index 0000000000..4a2378873e
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/pegs.html
@@ -0,0 +1,8 @@
1Pegs
2<p>
3Jump one peg over another to remove the one you jumped over. Try to
4remove all but one peg.
5<p>
6Drag a peg into an empty space to make a move. The target space must
7be exactly two holes away from the starting peg, in an orthogonal
8direction, and there must be a peg in the hole in between.
diff --git a/apps/plugins/puzzles/src/html/range.html b/apps/plugins/puzzles/src/html/range.html
new file mode 100644
index 0000000000..bb5b59c4d2
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/range.html
@@ -0,0 +1,21 @@
1Range
2<p>
3Colour some squares black, so as to meet the following conditions:
4<ul>
5<li>
6No two black squares are orthogonally adjacent.
7<li>
8No group of white squares is separated from the rest of the grid by
9black squares.
10<li>
11Each numbered cell can see precisely that many white squares in
12total by looking in all four orthogonal directions, counting itself.
13(Black squares block the view. So, for example, a 2 clue must be
14adjacent to three black squares or grid edges, and in the fourth
15direction there must be one white square and then a black one beyond
16it.)
17</ul>
18
19<p>
20Left-click to colour a square black. Right-click to mark a square
21with a dot, if you know it should not be black.
diff --git a/apps/plugins/puzzles/src/html/rect.html b/apps/plugins/puzzles/src/html/rect.html
new file mode 100644
index 0000000000..d23d827663
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/rect.html
@@ -0,0 +1,10 @@
1docname=rectangles:Rectangles
2<p>
3Draw lines along the grid edges to divide the grid into rectangles,
4so that each rectangle contains exactly one numbered square and its
5area is equal to the number written in that square.
6<p>
7Click and drag from one grid corner to another, or from one square
8centre to another, to draw a rectangle. You can also drag along a
9grid line to just draw a line at a time, or just click on a single
10grid edge to draw or erase it.
diff --git a/apps/plugins/puzzles/src/html/samegame.html b/apps/plugins/puzzles/src/html/samegame.html
new file mode 100644
index 0000000000..e6de095210
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/samegame.html
@@ -0,0 +1,14 @@
1Same Game
2<p>
3Try to empty the playing area completely, by removing connected
4groups of two or more squares of the same colour. Then try to score
5as much as possible, by removing large groups at a time instead of
6small ones.
7<p>
8Click on a coloured square to highlight the rest of its connected
9group. The status line will print the number of squares selected,
10and the score you would gain by removing them. Click again to remove
11the group; other squares will fall down to fill the space, and if
12you empty a whole column then the other columns will move up. You
13cannot remove a single isolated square: try to avoid dead-end
14positions where all remaining squares are isolated.
diff --git a/apps/plugins/puzzles/src/html/signpost.html b/apps/plugins/puzzles/src/html/signpost.html
new file mode 100644
index 0000000000..fa23e99de0
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/signpost.html
@@ -0,0 +1,14 @@
1Signpost
2<p>
3Connect all the squares together into a sequence, so that every
4square's arrow points towards the square that follows it (though the
5next square can be any distance away in that direction).
6
7<p>
8Left-drag from a square to the square that should follow it, or
9right-drag from a square to the square that should precede it.
10
11<p>
12Left-drag a square off the grid to break all links to it. Right-drag
13a square off the grid to break all links to it and everything else
14in its connected chain.
diff --git a/apps/plugins/puzzles/src/html/singles.html b/apps/plugins/puzzles/src/html/singles.html
new file mode 100644
index 0000000000..252bffb380
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/singles.html
@@ -0,0 +1,11 @@
1Singles
2<p>
3Black out some of the squares, in such a way that:
4<ul><li>no number appears twice in any row or column
5<li>no two black squares are adjacent
6<li>the white squares form a single connected group (connections
7along diagonals do not count).</ul>
8<p>
9Click in a square to black it out, and again to uncover it.
10Right-click in a square to mark it with a circle, indicating that
11you're sure it should <em>not</em> be blacked out.
diff --git a/apps/plugins/puzzles/src/html/sixteen.html b/apps/plugins/puzzles/src/html/sixteen.html
new file mode 100644
index 0000000000..4530469fe6
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/sixteen.html
@@ -0,0 +1,8 @@
1Sixteen
2<p>
3Slide the grid squares around so that the numbers end up in
4consecutive order from the top left corner.
5<p>
6Click on the arrows at the edges of the grid to move a row or column
7left, right, up or down. The square that falls off the end of the
8row comes back on the other end.
diff --git a/apps/plugins/puzzles/src/html/slant.html b/apps/plugins/puzzles/src/html/slant.html
new file mode 100644
index 0000000000..d6d31aa302
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/slant.html
@@ -0,0 +1,9 @@
1Slant
2<p>
3Fill in a diagonal line in every grid square so that there are no
4loops in the grid, and so that every numbered point has that many
5lines meeting at it.
6<p>
7Left-click in a square to mark it with a <code>\</code>; right-click
8to mark it with a <code>/</code>. Keep clicking in a square to
9cycle it between <code>\</code>, <code>/</code> and empty.
diff --git a/apps/plugins/puzzles/src/html/solo.html b/apps/plugins/puzzles/src/html/solo.html
new file mode 100644
index 0000000000..88ebd5cb29
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/solo.html
@@ -0,0 +1,20 @@
1Solo
2<p>
3Fill in a number in every square so that every number appears
4exactly once in each row, each column and each block marked by thick
5lines.
6<p>
7To place a number, click in a square to select it, then type the
8number on the keyboard. To erase a number, click to select a square
9and then press Backspace.
10<p>
11Right-click in a square and then type a number to add or remove the
12number as a pencil mark, indicating numbers that you think
13<em>might</em> go in that square.
14<p>
15When you master the basic game, try Jigsaw mode (irregularly shaped
16blocks), X mode (the two main diagonals of the grid must also
17contain every number once), Killer mode (instead of single-cell
18clues you are given regions of the grid each of which must add up to
19a given total, again without reusing any digits), or all of those at
20once!
diff --git a/apps/plugins/puzzles/src/html/tents.html b/apps/plugins/puzzles/src/html/tents.html
new file mode 100644
index 0000000000..e3f6d5f0ea
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/tents.html
@@ -0,0 +1,20 @@
1Tents
2<p>
3Place tents in the empty squares in such a way that:
4<ul>
5<li>no two tents are adjacent, even diagonally
6<li>the number of tents in each row and column matches the numbers
7around the edge of the grid
8<li>it is possible to match tents to trees so that each tree is
9orthogonally adjacent to its own tent (but may also be adjacent to
10other tents).
11</ul>
12<p>
13Click in a square to place or remove a tent. Right-click to mark a
14square as empty (not a tent). Right-click and drag along a row or
15column to mark many squares at once as empty.
16<p>
17Warning '!' marks appear to indicate adjacent tents. Numbers round
18the edge of the grid light up red to indicate they do not match the
19number of tents in the row. Groups of tents light up red to indicate
20that they have too few trees between them, and vice versa.
diff --git a/apps/plugins/puzzles/src/html/towers.html b/apps/plugins/puzzles/src/html/towers.html
new file mode 100644
index 0000000000..a710e0ab6e
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/towers.html
@@ -0,0 +1,22 @@
1Towers
2<p>
3Fill in the grid with towers whose heights range from 1 to the grid
4size, so that every possible height appears exactly once in each row
5and column, and so that each clue around the edge counts the number
6of towers that are visible when looking into the grid from that
7direction. (Taller towers hide shorter ones behind them. So the
8sequence 2,1,4,3,5 would match a clue of 3 on the left, because the
91 is hidden behind the 2 and the 3 is hidden behind the 4. On the
10right, it would match a clue of 1 because the 5 hides everything
11else.)
12<p>
13To place a tower, click in a square to select it, then type the
14desired height on the keyboard. To erase a tower, click to select a
15square and then press Backspace.
16<p>
17Right-click in a square and then type a number to add or remove the
18number as a pencil mark, indicating tower heights that you think
19<em>might</em> go in that square.
20<p>
21Left-click on a clue to mark it as done (grey it out). To unmark a
22clue as done, left-click on it again. \ No newline at end of file
diff --git a/apps/plugins/puzzles/src/html/tracks.html b/apps/plugins/puzzles/src/html/tracks.html
new file mode 100644
index 0000000000..afabed37ac
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/tracks.html
@@ -0,0 +1,19 @@
1Tracks
2<p>
3Complete the track from A to B so that the rows and
4columns contain the same number of track segments as are indicated in the
5clues to the top and right of the grid. There are only straight and
690-degree curved rail sections, and the track may not cross itself.
7<p>
8Left-click on an edge between two squares to add a track segment between
9the two squares. Right-click on an edge to add a cross on the edge,
10indicating no track is possible there.
11<p>
12Left-click in a square to add a colour indicator showing that you know the
13square must contain a track, even if you don't know which edges it crosses
14yet. Right-click in a square to add a cross indicating it contains no
15track segment.
16<p>
17Left- or right-drag between squares to lay a straight line of is-track or
18is-not-track indicators, useful for filling in rows or columns to match the
19clue.
diff --git a/apps/plugins/puzzles/src/html/twiddle.html b/apps/plugins/puzzles/src/html/twiddle.html
new file mode 100644
index 0000000000..5f94e4e120
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/twiddle.html
@@ -0,0 +1,15 @@
1Twiddle
2<p>
3Rotate square sections of the grid to arrange the squares into
4numerical order starting from the top left.
5<p>
6In the basic game, you rotate a 2&times;2 square section. Left-click
7in the centre of that section (i.e. on a corner point between four
8squares) to rotate the whole section anticlockwise. Right-click to
9rotate the section clockwise.
10<p>
11When you master the basic game, go to the Type menu to try it with
12larger rotating groups (for a 3&times;3 group you must click in the
13centre of a square to rotate the block around it). Or select the
14'orientable' mode in which every square must end up the right way
15round as well as in the right place. Or both!
diff --git a/apps/plugins/puzzles/src/html/undead.html b/apps/plugins/puzzles/src/html/undead.html
new file mode 100644
index 0000000000..c21374f6dc
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/undead.html
@@ -0,0 +1,22 @@
1Undead
2<p>
3Fill in every grid square which doesn't contain a mirror with either a
4ghost, a vampire, or a zombie. The numbers round the grid edges show
5how many monsters must be visible along your line of sight if you look
6directly into the grid from that position, along a row or column.
7Zombies are always visible; ghosts are only visible when reflected in
8at least one mirror; vampires are only visible when not reflected in
9any mirror.
10
11<p>
12To place a monster, click in a square to select it, then type the
13monster's letter on the keyboard: G for a ghost, V for a vampire or Z
14for a zombie. To erase a monster, click to select a square and then
15press Backspace.
16<p>
17Right-click in a square and then type a letter to add or remove the
18monster as a pencil mark, indicating monsters that you think
19<em>might</em> go in that square.
20<p>
21Left-click on a clue to mark it as done (grey it out). To unmark a
22clue as done, left-click on it again.
diff --git a/apps/plugins/puzzles/src/html/unequal.html b/apps/plugins/puzzles/src/html/unequal.html
new file mode 100644
index 0000000000..085f82effc
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/unequal.html
@@ -0,0 +1,14 @@
1Unequal
2<p>
3Fill in the grid with numbers from 1 to the grid size, so that every
4number appears exactly once in each row and column, and so that all
5the <code>&lt;</code> signs represent true inequalities (i.e. the
6number at the pointed end is smaller than the number at the open end).
7<p>
8To place a number, click in a square to select it, then type the
9number on the keyboard. To erase a number, click to select a square
10and then press Backspace.
11<p>
12Right-click in a square and then type a number to add or remove the
13number as a pencil mark, indicating numbers that you think
14<em>might</em> go in that square.
diff --git a/apps/plugins/puzzles/src/html/unruly.html b/apps/plugins/puzzles/src/html/unruly.html
new file mode 100644
index 0000000000..2cd3fb25f5
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/unruly.html
@@ -0,0 +1,11 @@
1Unruly
2<p>
3Colour every square either black or white, in such a way that:
4<ul><li>no three consecutive squares, horizontally or vertically, are
5the same colour
6<li>each row and column contains the same number of black and white
7squares.</ul>
8<p>
9Left-click in an empty square to turn it black, or right-click to turn
10it white. Click again in an already-filled square to cycle it between
11black and white and empty; middle-click to reset any square to empty.
diff --git a/apps/plugins/puzzles/src/html/untangle.html b/apps/plugins/puzzles/src/html/untangle.html
new file mode 100644
index 0000000000..7171a3d55d
--- /dev/null
+++ b/apps/plugins/puzzles/src/html/untangle.html
@@ -0,0 +1,5 @@
1Untangle
2<p>
3Move the points around so that none of the lines cross.
4<p>
5Click on a point and drag it to move it.
diff --git a/apps/plugins/puzzles/src/icons/Makefile b/apps/plugins/puzzles/src/icons/Makefile
new file mode 100644
index 0000000000..00dae1f841
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/Makefile
@@ -0,0 +1,153 @@
1# Makefile for Puzzles icons.
2
3PUZZLES = blackbox bridges cube dominosa fifteen filling flip flood \
4 galaxies guess inertia keen lightup loopy magnets map mines \
5 net netslide palisade pattern pearl pegs range rect \
6 samegame signpost singles sixteen slant solo tents towers \
7 twiddle tracks undead unequal unruly untangle
8
9BASE = $(patsubst %,%-base.png,$(PUZZLES))
10WEB = $(patsubst %,%-web.png,$(PUZZLES))
11
12IBASE = $(patsubst %,%-ibase.png,$(PUZZLES))
13IBASE4 = $(patsubst %,%-ibase4.png,$(PUZZLES))
14P48D24 = $(patsubst %,%-48d24.png,$(PUZZLES))
15P48D8 = $(patsubst %,%-48d8.png,$(PUZZLES))
16P48D4 = $(patsubst %,%-48d4.png,$(PUZZLES))
17P32D24 = $(patsubst %,%-32d24.png,$(PUZZLES))
18P32D8 = $(patsubst %,%-32d8.png,$(PUZZLES))
19P32D4 = $(patsubst %,%-32d4.png,$(PUZZLES))
20P16D24 = $(patsubst %,%-16d24.png,$(PUZZLES))
21P16D8 = $(patsubst %,%-16d8.png,$(PUZZLES))
22P16D4 = $(patsubst %,%-16d4.png,$(PUZZLES))
23ICONS = $(patsubst %,%.ico,$(PUZZLES))
24CICONS = $(patsubst %,%-icon.c,$(PUZZLES))
25RC = $(patsubst %,%.rc,$(PUZZLES))
26
27BIN = ../
28PIC = ./
29
30# Work around newer ImageMagick unilaterally distorting colours when
31# converting to PNG.
32CSP = -set colorspace RGB
33
34base: $(BASE)
35web: $(WEB)
36pngicons: $(P48D24) $(P32D24) $(P16D24)
37winicons: $(ICONS) $(RC)
38gtkicons: $(CICONS)
39all: base web pngicons winicons gtkicons
40
41# Build the base puzzle screenshots from which all the other images
42# are derived. Some of them involve showing a move animation
43# part-way through.
44fifteen-base.png : override REDO=0.3
45flip-base.png : override REDO=0.3
46netslide-base.png : override REDO=0.3
47sixteen-base.png : override REDO=0.3
48twiddle-base.png : override REDO=0.3
49$(BASE): %-base.png: $(BIN)% $(PIC)%.sav
50 $(PIC)screenshot.sh $(BIN)$* $(PIC)$*.sav $@ $(REDO)
51
52# Build the screenshots for the web, by scaling the original base
53# images to a uniform size.
54$(WEB): %-web.png: %-base.png
55 $(PIC)square.pl 150 5 $^ $@
56
57# Build the base _icon_ images, by careful cropping of the base
58# images: icons are very small so it's often necessary to zoom in
59# on a smaller portion of the screenshot.
60blackbox-ibase.png : override CROP=352x352 144x144+0+208
61bridges-ibase.png : override CROP=264x264 107x107+157+157
62dominosa-ibase.png : override CROP=304x272 152x152+152+0
63fifteen-ibase.png : override CROP=240x240 120x120+0+120
64filling-ibase.png : override CROP=256x256 133x133+14+78
65flip-ibase.png : override CROP=288x288 145x145+120+72
66galaxies-ibase.png : override CROP=288x288 165x165+0+0
67guess-ibase.png : override CROP=263x420 178x178+75+17
68inertia-ibase.png : override CROP=321x321 128x128+193+0
69keen-ibase.png : override CROP=288x288 96x96+24+120
70lightup-ibase.png : override CROP=256x256 112x112+144+0
71loopy-ibase.png : override CROP=257x257 113x113+0+0
72magnets-ibase.png : override CROP=264x232 96x96+36+100
73mines-ibase.png : override CROP=240x240 110x110+130+130
74net-ibase.png : override CROP=193x193 113x113+0+80
75netslide-ibase.png : override CROP=289x289 144x144+0+0
76palisade-ibase.png : override CROP=288x288 192x192+0+0
77pattern-ibase.png : override CROP=384x384 223x223+0+0
78pearl-ibase.png : override CROP=216x216 94x94+108+15
79pegs-ibase.png : override CROP=263x263 147x147+116+0
80range-ibase.png : override CROP=256x256 98x98+111+15
81rect-ibase.png : override CROP=205x205 115x115+90+0
82signpost-ibase.png : override CROP=240x240 98x98+23+23
83singles-ibase.png : override CROP=224x224 98x98+15+15
84sixteen-ibase.png : override CROP=288x288 144x144+144+144
85slant-ibase.png : override CROP=321x321 160x160+160+160
86solo-ibase.png : override CROP=481x481 145x145+24+24
87tents-ibase.png : override CROP=320x320 165x165+142+0
88towers-ibase.png : override CROP=300x300 102x102+151+6
89tracks-ibase.png : override CROP=246x246 118x118+6+6
90twiddle-ibase.png : override CROP=192x192 102x102+69+21
91undead-ibase.png : override CROP=416x480 192x192+16+80
92unequal-ibase.png : override CROP=208x208 104x104+104+104
93untangle-ibase.png : override CROP=320x320 164x164+3+116
94$(IBASE): %-ibase.png: %-base.png
95 $(PIC)crop.sh $^ $@ $(CROP)
96
97# Convert the full-size icon images to 4-bit colour, because that
98# seems to work better than reducing it in 24 bits and then
99# dithering.
100$(IBASE4): %-ibase4.png: %-ibase.png
101 convert -colors 16 +dither $(CSP) -map $(PIC)win16pal.xpm $^ $@
102
103# Build the 24-bit PNGs for the icons, at three sizes.
104$(P48D24): %-48d24.png: %-ibase.png
105 $(PIC)square.pl 48 4 $^ $@
106$(P32D24): %-32d24.png: %-ibase.png
107 $(PIC)square.pl 32 2 $^ $@
108$(P16D24): %-16d24.png: %-ibase.png
109 $(PIC)square.pl 16 1 $^ $@
110
111# The 8-bit icon PNGs are just custom-paletted quantisations of the
112# 24-bit ones.
113$(P48D8) $(P32D8) $(P16D8): %d8.png: %d24.png
114 convert -colors 256 $^ $@
115
116# But the depth-4 images work better if we re-shrink from the
117# ibase4 versions of the images, and then normalise the colours
118# again afterwards. (They're still not very good, but my hope is
119# that on most modern Windows machines this won't matter too
120# much...)
121$(P48D4): %-48d4.png: %-ibase4.png
122 $(PIC)square.pl 48 1 $^ $@-tmp2.png
123 convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@
124 rm -f $@-tmp2.png
125$(P32D4): %-32d4.png: %-ibase.png
126 $(PIC)square.pl 32 1 $^ $@-tmp2.png
127 convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@
128 rm -f $@-tmp2.png
129$(P16D4): %-16d4.png: %-ibase.png
130 $(PIC)square.pl 16 1 $^ $@-tmp2.png
131 convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@
132 rm -f $@-tmp2.png
133
134# Build the actual Windows icons themselves, by feeding all those
135# PNGs to my icon builder script.
136$(ICONS): %.ico: %-48d24.png %-48d8.png %-48d4.png \
137 %-32d24.png %-32d8.png %-32d4.png \
138 %-16d24.png %-16d8.png %-16d4.png
139 $(PIC)icon.pl -24 $*-48d24.png $*-32d24.png $*-16d24.png \
140 -8 $*-48d8.png $*-32d8.png $*-16d8.png \
141 -4 $*-48d4.png $*-32d4.png $*-16d4.png > $@
142
143# Build the .RC files which bind the icons into the applications.
144$(RC): %.rc:
145 echo '#include "puzzles.rc2"' > $@
146 echo '200 ICON "$*.ico"' >> $@
147
148# Build the GTK icon source files.
149$(CICONS): %-icon.c: %-16d24.png %-32d24.png %-48d24.png
150 $(PIC)cicon.pl $^ > $@
151
152clean:
153 rm -f *.png *.ico *.rc *-icon.c
diff --git a/apps/plugins/puzzles/src/icons/blackbox.sav b/apps/plugins/puzzles/src/icons/blackbox.sav
new file mode 100644
index 0000000000..4483f3c81a
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/blackbox.sav
@@ -0,0 +1,27 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :9:Black Box
4PARAMS :8:w8h8m5M5
5CPARAMS :8:w8h8m5M5
6SEED :15:999785320716678
7DESC :24:c8b9f8528193756b9a2fd24d
8UI :2:E0
9NSTATES :2:18
10STATEPOS:2:13
11MOVE :2:F2
12MOVE :2:F4
13MOVE :2:F5
14MOVE :3:F25
15MOVE :3:F26
16MOVE :3:F11
17MOVE :3:F12
18MOVE :3:F15
19MOVE :4:T7,7
20MOVE :4:T7,4
21MOVE :4:T1,7
22MOVE :2:F3
23MOVE :2:F9
24MOVE :3:F27
25MOVE :4:T5,7
26MOVE :4:T1,8
27MOVE :1:R
diff --git a/apps/plugins/puzzles/src/icons/bridges.sav b/apps/plugins/puzzles/src/icons/bridges.sav
new file mode 100644
index 0000000000..ddae7085cd
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/bridges.sav
@@ -0,0 +1,72 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Bridges
4PARAMS :15:10x10i30e10m2d2
5CPARAMS :15:10x10i30e10m2d2
6SEED :15:944199396008454
7DESC :49:a1a4a4b4k4b6b2a4b2b2b1e2e1a4b4c3j25d2a1f2c3a4d4c3
8AUXINFO :838:bd75eb5f7b129109b5cdcff0925c77ca5c0a135365002b93b44c5013c7a307b9504affcfb8ad934263196fc3e6d0b023abe48d254d46d29520e50a5e423c0fb1bc01ccc51cad61045c439e7c2bb8e5788bc7f3622aaa3a8125ebde11c9cd69b6f2393246fd094ad91e81ae58cd557b73bd1c9839cfad5835c8519e44298204eaca58dfd79289546959bfbabdc5f3cb7a27b8d3fb2d0b062bd5c2e469493c19f8c89989df73d8a3ab02d9afcbfedf245306d15881a01d153122f8374c7526abecc90919f99ff62e9789cabc402249af095ceb14c8c59c0d9ffbcdd731d50114e7c30c31ef0638f4d352abbfd04b4315d368d65bbfe005b6586245bc5244e5050098cf4c1b6986120f40d5ce038c10a3f309286f950cdc287e495aa13c70ab0c1f113a135556d7ce895fd8244afcbad43fe51f275837f223a1cb95151de8a158cb0add7fa8c9f1fa0e09a1ce842136c1679144cead56b164c4ef1a09ed36fd9704ba191b5957bc3d5bb97d8a1f7451d357a6638ac320b0beb0cd35aa404c8f1621c6d400960aa97bf6ce3a944339d7e401c4d98c31773b2a8881352d5653fdb5e8f7c04b
9NSTATES :2:63
10STATEPOS:2:41
11MOVE :10:L8,0,5,0,1
12MOVE :10:L8,0,5,0,2
13MOVE :10:L8,0,8,2,1
14MOVE :10:L8,0,8,2,2
15MOVE :4:M8,0
16MOVE :10:L0,2,3,2,1
17MOVE :10:L0,2,3,2,2
18MOVE :10:L0,2,0,7,1
19MOVE :10:L0,2,0,7,2
20MOVE :4:M0,2
21MOVE :10:L1,0,3,0,1
22MOVE :4:M1,0
23MOVE :10:L3,0,5,0,1
24MOVE :10:L3,0,3,2,1
25MOVE :10:L1,3,1,5,1
26MOVE :10:L0,7,5,7,1
27MOVE :10:L0,7,0,9,1
28MOVE :10:L0,9,5,9,1
29MOVE :10:L0,9,5,9,2
30MOVE :10:L0,9,0,7,2
31MOVE :4:M0,9
32MOVE :4:M0,7
33MOVE :10:L4,8,8,8,1
34MOVE :10:L4,8,8,8,2
35MOVE :4:M4,8
36MOVE :10:L5,9,9,9,1
37MOVE :10:L5,9,9,9,2
38MOVE :4:M5,9
39MOVE :10:L9,9,9,6,1
40MOVE :4:M9,9
41MOVE :10:L8,8,8,5,1
42MOVE :4:M8,8
43MOVE :10:L9,6,9,4,1
44MOVE :4:M9,6
45MOVE :4:M9,4
46MOVE :10:L1,5,4,5,1
47MOVE :10:L1,5,4,5,2
48MOVE :10:L1,5,1,3,2
49MOVE :4:M1,3
50MOVE :4:M1,5
51MOVE :10:L3,4,3,2,1
52MOVE :10:L3,4,3,2,2
53MOVE :4:M3,4
54MOVE :10:L4,5,8,5,1
55MOVE :10:L7,7,5,7,1
56MOVE :4:M5,7
57MOVE :4:M7,7
58MOVE :10:L7,3,4,3,1
59MOVE :4:M7,3
60MOVE :10:L5,0,3,0,2
61MOVE :4:M5,0
62MOVE :4:M3,0
63MOVE :10:L3,2,6,2,1
64MOVE :4:M3,2
65MOVE :10:L6,2,8,2,1
66MOVE :4:M6,2
67MOVE :10:L8,2,8,5,1
68MOVE :4:M8,2
69MOVE :4:M8,5
70MOVE :10:L4,5,4,3,1
71MOVE :4:M4,3
72MOVE :4:M4,5
diff --git a/apps/plugins/puzzles/src/icons/cicon.pl b/apps/plugins/puzzles/src/icons/cicon.pl
new file mode 100755
index 0000000000..3578bd33fe
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/cicon.pl
@@ -0,0 +1,27 @@
1#!/usr/bin/perl
2
3# Given a list of input PNGs, create a C source file file
4# containing a const array of XPMs, under the name `xpm_icon'.
5
6$k = 0;
7@xpms = ();
8foreach $f (@ARGV) {
9 # XPM format is generated directly by ImageMagick, so that's easy
10 # enough. We just have to adjust the declaration line so that it
11 # has the right name, linkage and storage class.
12 @lines = ();
13 open XPM, "convert $f xpm:- |";
14 push @lines, $_ while <XPM>;
15 close XPM;
16 die "XPM from $f in unexpected format\n" unless $lines[1] =~ /^static.*\{$/;
17 $lines[1] = "static const char *const xpm_icon_$k"."[] = {\n";
18 $k++;
19 push @xpms, @lines, "\n";
20}
21
22# Now output.
23foreach $line (@xpms) { print $line; }
24print "const char *const *const xpm_icons[] = {\n";
25for ($i = 0; $i < $k; $i++) { print " xpm_icon_$i,\n"; }
26print "};\n";
27print "const int n_xpm_icons = $k;\n";
diff --git a/apps/plugins/puzzles/src/icons/crop.sh b/apps/plugins/puzzles/src/icons/crop.sh
new file mode 100755
index 0000000000..0d15d3c9b9
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/crop.sh
@@ -0,0 +1,37 @@
1#!/bin/sh
2
3# Crop one image into another, after first checking that the source
4# image has the expected size in pixels.
5#
6# This is used in the Puzzles icon build scripts to construct icons
7# which are zoomed in on a particular sub-area of the puzzle's
8# basic screenshot. This way I can define crop areas in pixels,
9# while not having to worry too much that if I adjust the source
10# puzzle so as to alter the layout the crop area might start
11# hitting the wrong bit of picture. Most layout changes I can
12# conveniently imagine will also alter the overall image size, so
13# this script will give a build error and alert me to the fact that
14# I need to fiddle with the icon makefile.
15
16infile="$1"
17outfile="$2"
18insize="$3"
19crop="$4"
20
21# Special case: if no input size or crop parameter was specified at
22# all, we just copy the input to the output file.
23
24if test $# -lt 3; then
25 cp "$infile" "$outfile"
26 exit 0
27fi
28
29# Check the input image size.
30realsize=`identify -format %wx%h "$infile"`
31if test "x$insize" != "x$realsize"; then
32 echo "crop.sh: '$infile' has wrong initial size: $realsize != $insize" >&2
33 exit 1
34fi
35
36# And crop.
37convert -crop "$crop" "$infile" "$outfile"
diff --git a/apps/plugins/puzzles/src/icons/cube.sav b/apps/plugins/puzzles/src/icons/cube.sav
new file mode 100644
index 0000000000..bb123f4e74
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/cube.sav
@@ -0,0 +1,22 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :4:Cube
4PARAMS :4:c4x4
5CPARAMS :4:c4x4
6SEED :15:125146248070163
7DESC :6:C461,3
8NSTATES :2:14
9STATEPOS:1:5
10MOVE :1:D
11MOVE :1:D
12MOVE :1:D
13MOVE :1:U
14MOVE :1:L
15MOVE :1:L
16MOVE :1:D
17MOVE :1:U
18MOVE :1:D
19MOVE :1:U
20MOVE :1:U
21MOVE :1:U
22MOVE :1:L
diff --git a/apps/plugins/puzzles/src/icons/dominosa.sav b/apps/plugins/puzzles/src/icons/dominosa.sav
new file mode 100644
index 0000000000..5991f3e57e
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/dominosa.sav
@@ -0,0 +1,53 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :8:Dominosa
4PARAMS :1:6
5CPARAMS :1:6
6DESC :56:55521461210004364611033535444421636022603153156422620503
7NSTATES :2:46
8STATEPOS:2:33
9MOVE :6:D18,19
10MOVE :6:E22,23
11MOVE :6:E22,30
12MOVE :5:E9,17
13MOVE :6:D38,46
14MOVE :6:E14,15
15MOVE :5:E6,14
16MOVE :6:E33,34
17MOVE :6:E34,42
18MOVE :6:E26,34
19MOVE :6:D34,35
20MOVE :6:E42,50
21MOVE :6:E16,24
22MOVE :4:D4,5
23MOVE :4:D6,7
24MOVE :6:D15,23
25MOVE :6:E17,25
26MOVE :6:D16,17
27MOVE :6:E11,12
28MOVE :6:D51,52
29MOVE :5:E3,11
30MOVE :6:D10,11
31MOVE :4:D2,3
32MOVE :6:E37,45
33MOVE :6:D49,50
34MOVE :6:D40,48
35MOVE :6:D25,26
36MOVE :6:D24,32
37MOVE :6:D33,41
38MOVE :6:D42,43
39MOVE :6:D27,28
40MOVE :6:E21,29
41MOVE :6:D31,39
42MOVE :6:D47,55
43MOVE :6:E13,21
44MOVE :6:E13,14
45MOVE :6:D12,13
46MOVE :6:D20,21
47MOVE :6:D14,22
48MOVE :6:D29,30
49MOVE :6:D36,37
50MOVE :6:D44,45
51MOVE :6:D53,54
52MOVE :4:D0,1
53MOVE :4:D8,9
diff --git a/apps/plugins/puzzles/src/icons/fifteen.sav b/apps/plugins/puzzles/src/icons/fifteen.sav
new file mode 100644
index 0000000000..d81345a7d8
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/fifteen.sav
@@ -0,0 +1,74 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Fifteen
4PARAMS :3:4x4
5CPARAMS :3:4x4
6SEED :15:307905346810973
7DESC :37:8,11,3,6,14,13,4,2,0,9,12,10,5,1,7,15
8NSTATES :2:66
9STATEPOS:2:47
10MOVE :4:M1,2
11MOVE :4:M1,3
12MOVE :4:M0,3
13MOVE :4:M0,1
14MOVE :4:M1,1
15MOVE :4:M1,2
16MOVE :4:M0,2
17MOVE :4:M0,0
18MOVE :4:M1,0
19MOVE :4:M1,1
20MOVE :4:M0,1
21MOVE :4:M0,0
22MOVE :4:M1,0
23MOVE :4:M3,0
24MOVE :4:M3,1
25MOVE :4:M1,1
26MOVE :4:M1,0
27MOVE :4:M3,0
28MOVE :4:M3,1
29MOVE :4:M1,1
30MOVE :4:M1,0
31MOVE :4:M2,0
32MOVE :4:M2,1
33MOVE :4:M1,1
34MOVE :4:M1,3
35MOVE :4:M0,3
36MOVE :4:M0,1
37MOVE :4:M1,1
38MOVE :4:M1,2
39MOVE :4:M0,2
40MOVE :4:M0,1
41MOVE :4:M2,1
42MOVE :4:M3,1
43MOVE :4:M3,2
44MOVE :4:M2,2
45MOVE :4:M2,3
46MOVE :4:M3,3
47MOVE :4:M3,1
48MOVE :4:M2,1
49MOVE :4:M2,2
50MOVE :4:M1,2
51MOVE :4:M1,3
52MOVE :4:M2,3
53MOVE :4:M2,2
54MOVE :4:M1,2
55MOVE :4:M0,2
56MOVE :4:M0,3
57MOVE :4:M1,3
58MOVE :4:M1,2
59MOVE :4:M2,2
60MOVE :4:M2,3
61MOVE :4:M0,3
62MOVE :4:M0,2
63MOVE :4:M1,2
64MOVE :4:M2,2
65MOVE :4:M2,3
66MOVE :4:M1,3
67MOVE :4:M1,2
68MOVE :4:M3,2
69MOVE :4:M3,3
70MOVE :4:M1,3
71MOVE :4:M1,2
72MOVE :4:M2,2
73MOVE :4:M2,3
74MOVE :4:M3,3
diff --git a/apps/plugins/puzzles/src/icons/filling.sav b/apps/plugins/puzzles/src/icons/filling.sav
new file mode 100644
index 0000000000..caf0bb2d4c
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/filling.sav
@@ -0,0 +1,38 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Filling
4PARAMS :3:7x7
5CPARAMS :3:7x7
6SEED :15:279172739852696
7DESC :49:0000000031051240010004000001106171000400001013105
8NSTATES :2:30
9STATEPOS:2:13
10MOVE :4:38_3
11MOVE :4:39_3
12MOVE :4:36_4
13MOVE :4:43_4
14MOVE :4:35_4
15MOVE :4:47_5
16MOVE :4:40_5
17MOVE :4:34_5
18MOVE :4:41_5
19MOVE :4:25_7
20MOVE :4:23_6
21MOVE :4:16_6
22MOVE :4:18_7
23MOVE :4:19_7
24MOVE :4:20_7
25MOVE :4:26_7
26MOVE :4:24_7
27MOVE :4:29_6
28MOVE :4:22_6
29MOVE :4:15_6
30MOVE :3:7_4
31MOVE :3:0_4
32MOVE :3:1_3
33MOVE :3:2_3
34MOVE :3:6_2
35MOVE :3:5_5
36MOVE :3:4_5
37MOVE :3:3_5
38MOVE :4:10_5
diff --git a/apps/plugins/puzzles/src/icons/flip.sav b/apps/plugins/puzzles/src/icons/flip.sav
new file mode 100644
index 0000000000..82b4c49357
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/flip.sav
@@ -0,0 +1,20 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :4:Flip
4PARAMS :4:5x5c
5CPARAMS :4:5x5c
6SEED :15:158897339725978
7DESC :165:c400007100001c4000071000018400043100011c400047100011c400046100010c400047100011c4000471000118400043100011c400047100011c400046100010c000047000011c0000470000118,5c18b48
8NSTATES :2:12
9STATEPOS:1:4
10MOVE :4:M4,3
11MOVE :4:M3,0
12MOVE :4:M2,2
13MOVE :4:M3,2
14MOVE :4:M2,3
15MOVE :4:M0,2
16MOVE :4:M0,3
17MOVE :4:M1,4
18MOVE :4:M0,0
19MOVE :4:M1,0
20MOVE :4:M1,1
diff --git a/apps/plugins/puzzles/src/icons/flood.sav b/apps/plugins/puzzles/src/icons/flood.sav
new file mode 100644
index 0000000000..ac4adf7020
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/flood.sav
@@ -0,0 +1,14 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Flood
4PARAMS :7:6x6c6m5
5CPARAMS :7:6x6c6m5
6SEED :15:967543368167853
7DESC :39:032242034203340350204502505323231342,17
8NSTATES :1:6
9STATEPOS:1:6
10MOVE :2:M3
11MOVE :2:M2
12MOVE :2:M0
13MOVE :2:M5
14MOVE :2:M3
diff --git a/apps/plugins/puzzles/src/icons/galaxies.sav b/apps/plugins/puzzles/src/icons/galaxies.sav
new file mode 100644
index 0000000000..42d18bc335
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/galaxies.sav
@@ -0,0 +1,51 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :8:Galaxies
4PARAMS :5:7x7dh
5CPARAMS :5:7x7dh
6SEED :15:483644862786314
7DESC :13:ikrqfedzljjsp
8NSTATES :2:43
9STATEPOS:2:37
10MOVE :5:E12,9
11MOVE :5:E8,11
12MOVE :5:E5,12
13MOVE :5:E7,12
14MOVE :5:E4,13
15MOVE :5:E2,11
16MOVE :5:E3,10
17MOVE :4:E2,5
18MOVE :4:E4,5
19MOVE :4:E6,7
20MOVE :4:E8,1
21MOVE :5:E10,1
22MOVE :4:E9,2
23MOVE :4:E6,3
24MOVE :4:E7,4
25MOVE :5:E10,3
26MOVE :5:E10,5
27MOVE :5:E11,6
28MOVE :5:E13,6
29MOVE :5:E8,13
30MOVE :5:E12,7
31MOVE :6:E12,11
32MOVE :6:E13,12
33MOVE :4:E8,9
34MOVE :4:E7,8
35MOVE :4:E7,6
36MOVE :4:E9,6
37MOVE :4:E8,5
38MOVE :4:E9,4
39MOVE :4:E5,2
40MOVE :4:E4,1
41MOVE :4:E3,6
42MOVE :4:E2,7
43MOVE :4:E3,8
44MOVE :4:E3,4
45MOVE :4:E4,9
46MOVE :4:E2,9
47MOVE :5:E5,10
48MOVE :5:E6,11
49MOVE :4:E2,3
50MOVE :4:E2,1
51MOVE :5:E1,12
diff --git a/apps/plugins/puzzles/src/icons/guess.sav b/apps/plugins/puzzles/src/icons/guess.sav
new file mode 100644
index 0000000000..69852bf769
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/guess.sav
@@ -0,0 +1,15 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Guess
4PARAMS :9:c6p4g10Bm
5CPARAMS :9:c6p4g10Bm
6SEED :15:313100730915729
7DESC :8:b5f3faed
8UI :7:0,0,0,0
9NSTATES :1:6
10STATEPOS:1:6
11MOVE :8:G1,1,2,2
12MOVE :8:G4,3,1,1
13MOVE :8:G5,5,1,1
14MOVE :8:G4,2,1,6
15MOVE :8:G2,3,1,6
diff --git a/apps/plugins/puzzles/src/icons/icon.pl b/apps/plugins/puzzles/src/icons/icon.pl
new file mode 100755
index 0000000000..fcb1aa3e2c
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/icon.pl
@@ -0,0 +1,270 @@
1#!/usr/bin/perl
2
3# Take a collection of input image files and convert them into a
4# multi-resolution Windows .ICO icon file.
5#
6# The input images can be treated as having four different colour
7# depths:
8#
9# - 24-bit true colour
10# - 8-bit with custom palette
11# - 4-bit using the Windows 16-colour palette (see comment below
12# for details)
13# - 1-bit using black and white only.
14#
15# The images can be supplied in any input format acceptable to
16# ImageMagick, but their actual colour usage must already be
17# appropriate for the specified mode; this script will not do any
18# substantive conversion. So if an image intended to be used in 4-
19# or 1-bit mode contains any colour not in the appropriate fixed
20# palette, that's a fatal error; if an image to be used in 8-bit
21# mode contains more than 256 distinct colours, that's also a fatal
22# error.
23#
24# Command-line syntax is:
25#
26# icon.pl -depth imagefile [imagefile...] [-depth imagefile [imagefile...]]
27#
28# where `-depth' is one of `-24', `-8', `-4' or `-1', and tells the
29# script how to treat all the image files given after that option
30# until the next depth option. For example, you might execute
31#
32# icon.pl -24 48x48x24.png 32x32x24.png -8 32x32x8.png -1 monochrome.png
33#
34# to build an icon file containing two differently sized 24-bit
35# images, one 8-bit image and one black and white image.
36#
37# Windows .ICO files support a 1-bit alpha channel on all these
38# image types. That is, any pixel can be either opaque or fully
39# transparent, but not partially transparent. The alpha channel is
40# separate from the main image data, meaning that `transparent' is
41# not required to take up a palette entry. (So an 8-bit image can
42# have 256 distinct _opaque_ colours, plus transparent pixels as
43# well.) If the input images have alpha channels, they will be used
44# to determine which pixels of the icon are transparent, by simple
45# quantisation half way up (e.g. in a PNG image with an 8-bit alpha
46# channel, alpha values of 00-7F will be mapped to transparent
47# pixels, and 80-FF will become opaque).
48
49# The Windows 16-colour palette consists of:
50# - the eight corners of the colour cube (000000, 0000FF, 00FF00,
51# 00FFFF, FF0000, FF00FF, FFFF00, FFFFFF)
52# - dim versions of the seven non-black corners, at 128/255 of the
53# brightness (000080, 008000, 008080, 800000, 800080, 808000,
54# 808080)
55# - light grey at 192/255 of full brightness (C0C0C0).
56%win16pal = (
57 "\x00\x00\x00\x00" => 0,
58 "\x00\x00\x80\x00" => 1,
59 "\x00\x80\x00\x00" => 2,
60 "\x00\x80\x80\x00" => 3,
61 "\x80\x00\x00\x00" => 4,
62 "\x80\x00\x80\x00" => 5,
63 "\x80\x80\x00\x00" => 6,
64 "\xC0\xC0\xC0\x00" => 7,
65 "\x80\x80\x80\x00" => 8,
66 "\x00\x00\xFF\x00" => 9,
67 "\x00\xFF\x00\x00" => 10,
68 "\x00\xFF\xFF\x00" => 11,
69 "\xFF\x00\x00\x00" => 12,
70 "\xFF\x00\xFF\x00" => 13,
71 "\xFF\xFF\x00\x00" => 14,
72 "\xFF\xFF\xFF\x00" => 15,
73);
74@win16pal = sort { $win16pal{$a} <=> $win16pal{$b} } keys %win16pal;
75
76# The black and white palette consists of black (000000) and white
77# (FFFFFF), obviously.
78%win2pal = (
79 "\x00\x00\x00\x00" => 0,
80 "\xFF\xFF\xFF\x00" => 1,
81);
82@win2pal = sort { $win16pal{$a} <=> $win2pal{$b} } keys %win2pal;
83
84@hdr = ();
85@dat = ();
86
87$depth = undef;
88foreach $_ (@ARGV) {
89 if (/^-(24|8|4|1)$/) {
90 $depth = $1;
91 } elsif (defined $depth) {
92 &readicon($_, $depth);
93 } else {
94 $usage = 1;
95 }
96}
97if ($usage || length @hdr == 0) {
98 print "usage: icon.pl ( -24 | -8 | -4 | -1 ) image [image...]\n";
99 print " [ ( -24 | -8 | -4 | -1 ) image [image...] ...]\n";
100 exit 0;
101}
102
103# Now write out the output icon file.
104print pack "vvv", 0, 1, scalar @hdr; # file-level header
105$filepos = 6 + 16 * scalar @hdr;
106for ($i = 0; $i < scalar @hdr; $i++) {
107 print $hdr[$i];
108 print pack "V", $filepos;
109 $filepos += length($dat[$i]);
110}
111for ($i = 0; $i < scalar @hdr; $i++) {
112 print $dat[$i];
113}
114
115sub readicon {
116 my $filename = shift @_;
117 my $depth = shift @_;
118 my $pix;
119 my $i;
120 my %pal;
121
122 # Determine the icon's width and height.
123 my $w = `identify -format %w $filename`;
124 my $h = `identify -format %h $filename`;
125
126 # Read the file in as RGBA data. We flip vertically at this
127 # point, to avoid having to do it ourselves (.BMP and hence
128 # .ICO are bottom-up).
129 my $data = [];
130 open IDATA, "convert -set colorspace sRGB -flip -depth 8 $filename rgba:- |";
131 push @$data, $rgb while (read IDATA,$rgb,4,0) == 4;
132 close IDATA;
133 # Check we have the right amount of data.
134 $xl = $w * $h;
135 $al = scalar @$data;
136 die "wrong amount of image data ($al, expected $xl) from $filename\n"
137 unless $al == $xl;
138
139 # Build the alpha channel now, so we can exclude transparent
140 # pixels from the palette analysis. We replace transparent
141 # pixels with undef in the data array.
142 #
143 # We quantise the alpha channel half way up, so that alpha of
144 # 0x80 or more is taken to be fully opaque and 0x7F or less is
145 # fully transparent. Nasty, but the best we can do without
146 # dithering (and don't even suggest we do that!).
147 my $x;
148 my $y;
149 my $alpha = "";
150
151 for ($y = 0; $y < $h; $y++) {
152 my $currbyte = 0, $currbits = 0;
153 for ($x = 0; $x < (($w+31)|31)-31; $x++) {
154 $pix = ($x < $w ? $data->[$y*$w+$x] : "\x00\x00\x00\xFF");
155 my @rgba = unpack "CCCC", $pix;
156 $currbyte <<= 1;
157 $currbits++;
158 if ($rgba[3] < 0x80) {
159 if ($x < $w) {
160 $data->[$y*$w+$x] = undef;
161 }
162 $currbyte |= 1; # MS has the alpha channel inverted :-)
163 } else {
164 # Might as well flip RGBA into BGR0 while we're here.
165 if ($x < $w) {
166 $data->[$y*$w+$x] = pack "CCCC",
167 $rgba[2], $rgba[1], $rgba[0], 0;
168 }
169 }
170 if ($currbits >= 8) {
171 $alpha .= pack "C", $currbyte;
172 $currbits -= 8;
173 }
174 }
175 }
176
177 # For an 8-bit image, check we have at most 256 distinct
178 # colours, and build the palette.
179 %pal = ();
180 if ($depth == 8) {
181 my $palindex = 0;
182 foreach $pix (@$data) {
183 next unless defined $pix;
184 $pal{$pix} = $palindex++ unless defined $pal{$pix};
185 }
186 die "too many colours in 8-bit image $filename\n" unless $palindex <= 256;
187 } elsif ($depth == 4) {
188 %pal = %win16pal;
189 } elsif ($depth == 1) {
190 %pal = %win2pal;
191 }
192
193 my $raster = "";
194 if ($depth < 24) {
195 # For a non-24-bit image, flatten the image into one palette
196 # index per pixel.
197 $pad = 32 / $depth; # number of pixels to pad scanline to 4-byte align
198 $pmask = $pad-1;
199 for ($y = 0; $y < $h; $y++) {
200 my $currbyte = 0, $currbits = 0;
201 for ($x = 0; $x < (($w+$pmask)|$pmask)-$pmask; $x++) {
202 $currbyte <<= $depth;
203 $currbits += $depth;
204 if ($x < $w && defined ($pix = $data->[$y*$w+$x])) {
205 if (!defined $pal{$pix}) {
206 my $pixprintable = unpack "H*", $pix;
207 die "illegal colour value $pixprintable at pixel ($x,$y) in $filename\n";
208 }
209 $currbyte |= $pal{$pix};
210 }
211 if ($currbits >= 8) {
212 $raster .= pack "C", $currbyte;
213 $currbits -= 8;
214 }
215 }
216 }
217 } else {
218 # For a 24-bit image, reverse the order of the R,G,B values
219 # and stick a padding zero on the end.
220 #
221 # (In this loop we don't need to bother padding the
222 # scanline out to a multiple of four bytes, because every
223 # pixel takes four whole bytes anyway.)
224 for ($i = 0; $i < scalar @$data; $i++) {
225 if (defined $data->[$i]) {
226 $raster .= $data->[$i];
227 } else {
228 $raster .= "\x00\x00\x00\x00";
229 }
230 }
231 $depth = 32; # and adjust this
232 }
233
234 # Prepare the icon data. First the header...
235 my $data = pack "VVVvvVVVVVV",
236 40, # size of bitmap info header
237 $w, # icon width
238 $h*2, # icon height (x2 to indicate the subsequent alpha channel)
239 1, # 1 plane (common to all MS image formats)
240 $depth, # bits per pixel
241 0, # no compression
242 length $raster, # image size
243 0, 0, 0, 0; # resolution, colours used, colours important (ignored)
244 # ... then the palette ...
245 if ($depth <= 8) {
246 my $ncols = (1 << $depth);
247 my $palette = "\x00\x00\x00\x00" x $ncols;
248 foreach $i (keys %pal) {
249 substr($palette, $pal{$i}*4, 4) = $i;
250 }
251 $data .= $palette;
252 }
253 # ... the raster data we already had ready ...
254 $data .= $raster;
255 # ... and the alpha channel we already had as well.
256 $data .= $alpha;
257
258 # Prepare the header which will represent this image in the
259 # icon file.
260 my $header = pack "CCCCvvV",
261 $w, $h, # width and height (this time the real height)
262 1 << $depth, # number of colours, if less than 256
263 0, # reserved
264 1, # planes
265 $depth, # bits per pixel
266 length $data; # size of real icon data
267
268 push @hdr, $header;
269 push @dat, $data;
270}
diff --git a/apps/plugins/puzzles/src/icons/inertia.sav b/apps/plugins/puzzles/src/icons/inertia.sav
new file mode 100644
index 0000000000..a6d6faed1b
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/inertia.sav
@@ -0,0 +1,30 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Inertia
4PARAMS :3:8x8
5CPARAMS :3:8x8
6SEED :15:739970145068932
7DESC :64:sbgwsmswwgggwggmmbwgwbssbwbsbwbbwsSmgbbsbbmsgbmssgmggbmmwmbmmwsw
8UI :2:D0
9NSTATES :2:21
10STATEPOS:1:5
11MOVE :1:6
12MOVE :1:5
13MOVE :1:3
14MOVE :1:1
15MOVE :1:6
16MOVE :1:0
17MOVE :1:5
18MOVE :1:7
19MOVE :1:5
20MOVE :1:6
21MOVE :1:1
22MOVE :1:3
23MOVE :1:4
24MOVE :1:2
25MOVE :1:6
26MOVE :1:5
27MOVE :1:3
28MOVE :1:1
29MOVE :1:5
30MOVE :1:3
diff --git a/apps/plugins/puzzles/src/icons/keen.sav b/apps/plugins/puzzles/src/icons/keen.sav
new file mode 100644
index 0000000000..4adbd42d17
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/keen.sav
@@ -0,0 +1,62 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :4:Keen
4PARAMS :3:5de
5CPARAMS :3:5de
6SEED :15:846699649745236
7DESC :48:a__a_3a_5a_a_a_3b_bac_,a5m15a7a10s2s2d2s3m40m2s2
8AUXINFO :52:6105af67c6ebc8de056b59ebc9a463aa54e75f647055c0a6c1bd
9NSTATES :2:53
10STATEPOS:2:39
11MOVE :6:P0,4,2
12MOVE :6:P0,4,4
13MOVE :6:P0,4,5
14MOVE :6:P1,4,2
15MOVE :6:P1,4,4
16MOVE :6:P1,4,5
17MOVE :6:P1,3,2
18MOVE :6:P1,3,4
19MOVE :6:P1,3,5
20MOVE :6:R2,2,4
21MOVE :6:R1,2,2
22MOVE :6:P1,3,2
23MOVE :6:P1,4,2
24MOVE :6:R0,4,2
25MOVE :6:R2,3,2
26MOVE :6:R2,4,1
27MOVE :6:R1,4,4
28MOVE :6:R1,3,5
29MOVE :6:P3,4,3
30MOVE :6:P3,4,5
31MOVE :6:P4,4,3
32MOVE :6:P4,4,5
33MOVE :6:R4,4,5
34MOVE :6:R3,4,3
35MOVE :6:P3,1,2
36MOVE :6:P3,1,5
37MOVE :6:P3,0,2
38MOVE :6:P3,0,5
39MOVE :6:R3,2,1
40MOVE :6:R3,3,4
41MOVE :6:P2,0,3
42MOVE :6:P2,0,5
43MOVE :6:P2,1,3
44MOVE :6:P2,1,5
45MOVE :6:P0,1,1
46MOVE :6:P0,1,3
47MOVE :6:P1,1,1
48MOVE :6:P1,1,3
49MOVE :6:R2,0,3
50MOVE :6:R2,1,5
51MOVE :6:R3,0,5
52MOVE :6:R3,1,2
53MOVE :6:R4,1,4
54MOVE :6:R4,2,3
55MOVE :6:R4,0,2
56MOVE :6:R4,3,1
57MOVE :6:R0,2,5
58MOVE :6:R0,3,3
59MOVE :6:R1,1,3
60MOVE :6:R0,1,1
61MOVE :6:R1,0,1
62MOVE :6:R0,0,4
diff --git a/apps/plugins/puzzles/src/icons/lightup.sav b/apps/plugins/puzzles/src/icons/lightup.sav
new file mode 100644
index 0000000000..21b59bdec4
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/lightup.sav
@@ -0,0 +1,24 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :8:Light Up
4PARAMS :10:7x7b20s4d1
5CPARAMS :10:7x7b25s4d1
6SEED :15:538922615851330
7DESC :25:b3gB0c0dBaBaBa1aBd1c01g0b
8NSTATES :2:16
9STATEPOS:2:16
10MOVE :4:L1,0
11MOVE :4:L2,1
12MOVE :4:L3,0
13MOVE :4:L0,3
14MOVE :4:L6,1
15MOVE :4:L3,4
16MOVE :4:I6,5
17MOVE :4:I1,5
18MOVE :4:I2,6
19MOVE :4:I3,6
20MOVE :4:I4,5
21MOVE :4:I5,6
22MOVE :4:L5,5
23MOVE :4:I6,4
24MOVE :4:I4,2
diff --git a/apps/plugins/puzzles/src/icons/loopy.sav b/apps/plugins/puzzles/src/icons/loopy.sav
new file mode 100644
index 0000000000..11611818af
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/loopy.sav
@@ -0,0 +1,120 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Loopy
4PARAMS :7:7x7t0de
5CPARAMS :7:7x7t0de
6DESC :31:02g222b3b2e2a2b322b2a2a3a2a1d1b
7NSTATES :3:113
8STATEPOS:2:75
9MOVE :2:3n
10MOVE :2:0n
11MOVE :2:1n
12MOVE :2:2n
13MOVE :2:4n
14MOVE :2:6y
15MOVE :2:5y
16MOVE :3:25n
17MOVE :2:9n
18MOVE :3:28y
19MOVE :3:27y
20MOVE :3:42n
21MOVE :3:30n
22MOVE :3:45y
23MOVE :3:44y
24MOVE :3:59n
25MOVE :3:47n
26MOVE :3:62y
27MOVE :3:61y
28MOVE :3:76n
29MOVE :3:64n
30MOVE :3:79y
31MOVE :3:78y
32MOVE :3:93n
33MOVE :3:81n
34MOVE :4:110y
35MOVE :4:111y
36MOVE :3:99y
37MOVE :3:98y
38MOVE :3:24n
39MOVE :3:39y
40MOVE :3:23y
41MOVE :3:22y
42MOVE :3:26n
43MOVE :3:37n
44MOVE :3:38y
45MOVE :3:54n
46MOVE :3:69y
47MOVE :3:53y
48MOVE :3:40y
49MOVE :2:7y
50MOVE :3:88y
51MOVE :3:87y
52MOVE :4:102n
53MOVE :3:90n
54MOVE :3:52n
55MOVE :3:41y
56MOVE :3:55n
57MOVE :3:43n
58MOVE :2:8n
59MOVE :3:10y
60MOVE :3:12y
61MOVE :3:11n
62MOVE :3:13y
63MOVE :3:29n
64MOVE :3:15y
65MOVE :3:14n
66MOVE :3:16y
67MOVE :3:32y
68MOVE :3:31n
69MOVE :3:18y
70MOVE :3:17n
71MOVE :3:19y
72MOVE :3:20y
73MOVE :3:21n
74MOVE :3:33y
75MOVE :3:35y
76MOVE :3:36n
77MOVE :3:50y
78MOVE :3:57y
79MOVE :3:58y
80MOVE :3:72n
81MOVE :3:60n
82MOVE :3:74y
83MOVE :4:104y
84MOVE :4:107n
85MOVE :4:106n
86MOVE :3:92n
87MOVE :4:109n
88MOVE :3:89y
89MOVE :3:77n
90MOVE :3:75n
91MOVE :3:73y
92MOVE :3:85n
93MOVE :3:70n
94MOVE :3:56y
95MOVE :3:67n
96MOVE :3:71y
97MOVE :3:68y
98MOVE :3:84n
99MOVE :3:82n
100MOVE :3:83y
101MOVE :3:97n
102MOVE :3:86y
103MOVE :4:101y
104MOVE :4:100n
105MOVE :4:103y
106MOVE :4:105y
107MOVE :4:108y
108MOVE :3:96n
109MOVE :3:95y
110MOVE :3:94y
111MOVE :3:91y
112MOVE :3:80y
113MOVE :3:66n
114MOVE :3:65y
115MOVE :3:63y
116MOVE :3:51n
117MOVE :3:46n
118MOVE :3:34y
119MOVE :3:48n
120MOVE :3:49y
diff --git a/apps/plugins/puzzles/src/icons/magnets.sav b/apps/plugins/puzzles/src/icons/magnets.sav
new file mode 100644
index 0000000000..3c317b70ce
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/magnets.sav
@@ -0,0 +1,33 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Magnets
4PARAMS :6:6x5dtS
5CPARAMS :6:6x5dtS
6SEED :15:705856238774945
7DESC :56:2.2..1,.3.2.,2.21..,2..0.,TLRTLRBLRBTTLRLRBBLRTTTTLRBBBB
8AUXINFO :60:ebae280db3eec279c628b6cfe4aca5a03ba24d7eba91169f1bdf275fce3f
9NSTATES :2:24
10STATEPOS:2:15
11MOVE :4:.1,3
12MOVE :4:.0,1
13MOVE :4:?0,1
14MOVE :4:.2,1
15MOVE :4:?2,1
16MOVE :4:.2,4
17MOVE :4:?2,4
18MOVE :4:+2,3
19MOVE :4:.3,3
20MOVE :4:.0,2
21MOVE :4:?0,2
22MOVE :4:+1,4
23MOVE :4:+0,2
24MOVE :4:+0,0
25MOVE :4:+1,1
26MOVE :4:.2,2
27MOVE :4:+2,0
28MOVE :4:+3,1
29MOVE :4:.4,0
30MOVE :4:+5,1
31MOVE :4:.5,3
32MOVE :4:+4,3
33MOVE :4:.4,2
diff --git a/apps/plugins/puzzles/src/icons/map.sav b/apps/plugins/puzzles/src/icons/map.sav
new file mode 100644
index 0000000000..33863e1eeb
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/map.sav
@@ -0,0 +1,27 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :3:Map
4PARAMS :10:20x20n30dn
5CPARAMS :10:20x20n30dn
6SEED :15:794003990129265
7DESC :264:dcbakatgcaaedaccabfabadbaaaagaiaaaeaiadcaaaafabccbdcaaecabggedfaebqbadgbngcblabdaadaaaeagabaaaacaacacbcaebabaabaebaafaaakabdhcdanaaceagakacbbajaaadbacbaaccbcbicdafbadgbaccbkcdaafbacbcaaabcddacaaaaddbabcdbbacabbhagajabbobcdjaecaafabahaaaffead,23a01c3c1a3d2a20b01a3a
8AUXINFO :282:738e7a68c5d32445002968f3726646962b3604ef27a3657e0fdc0fd8180d5b747febd4619487bbc8bec5a48c709b154eb8da39c9b49be1e312a381fc2394e53126714079bd82e8444dad92419429635d1c816c53774b8c77b4ce03884c94d12bfb757cd93b5600471cb9726b3f2afe74d9932abeaa2efd6a496cad793ce5b221f943d620e883794f9d56741908
9NSTATES :2:18
10STATEPOS:2:12
11MOVE :4:3:20
12MOVE :4:0:24
13MOVE :4:3:10
14MOVE :4:1:18
15MOVE :4:2:11
16MOVE :4:3:17
17MOVE :4:1:23
18MOVE :4:3:27
19MOVE :4:1:29
20MOVE :4:0:16
21MOVE :4:2:13
22MOVE :3:1:6
23MOVE :3:2:2
24MOVE :3:0:7
25MOVE :3:2:9
26MOVE :4:0:15
27MOVE :3:3:5
diff --git a/apps/plugins/puzzles/src/icons/mines.sav b/apps/plugins/puzzles/src/icons/mines.sav
new file mode 100644
index 0000000000..a827541163
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/mines.sav
@@ -0,0 +1,67 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Mines
4PARAMS :6:9x9n35
5CPARAMS :6:9x9n35
6SEED :15:698938038698621
7DESC :26:0,0,me0691ca8a278f3c371688
8PRIVDESC:22:me0691ca8a278f3c371688
9UI :3:D0C
10TIME :7:75.2958
11NSTATES :2:56
12STATEPOS:2:41
13MOVE :4:O0,0
14MOVE :4:F1,2
15MOVE :4:F0,2
16MOVE :4:O2,2
17MOVE :4:F2,1
18MOVE :4:F3,1
19MOVE :4:F3,2
20MOVE :4:F3,3
21MOVE :4:F1,3
22MOVE :4:F2,3
23MOVE :4:C1,0
24MOVE :4:C2,0
25MOVE :4:C3,0
26MOVE :4:F5,0
27MOVE :4:F5,1
28MOVE :4:C4,1
29MOVE :4:O6,1
30MOVE :4:O6,2
31MOVE :4:O6,3
32MOVE :4:F7,1
33MOVE :4:O7,4
34MOVE :4:O5,4
35MOVE :4:F5,3
36MOVE :4:F5,5
37MOVE :4:F6,6
38MOVE :4:C6,5
39MOVE :4:F8,6
40MOVE :4:C6,3
41MOVE :4:F8,2
42MOVE :4:C7,2
43MOVE :4:F8,0
44MOVE :4:F7,0
45MOVE :4:F6,0
46MOVE :4:C4,2
47MOVE :4:F4,4
48MOVE :4:F4,5
49MOVE :4:F3,4
50MOVE :4:C5,6
51MOVE :4:F7,7
52MOVE :4:F8,7
53MOVE :4:F7,8
54MOVE :4:O4,8
55MOVE :4:F3,6
56MOVE :4:C4,6
57MOVE :4:F3,8
58MOVE :4:F5,8
59MOVE :4:C6,7
60MOVE :4:C3,7
61MOVE :4:F2,5
62MOVE :4:F2,4
63MOVE :4:F1,8
64MOVE :4:F1,7
65MOVE :4:C2,7
66MOVE :4:C2,6
67MOVE :4:C1,6
diff --git a/apps/plugins/puzzles/src/icons/net.sav b/apps/plugins/puzzles/src/icons/net.sav
new file mode 100644
index 0000000000..06a5426280
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/net.sav
@@ -0,0 +1,53 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :3:Net
4PARAMS :3:5x5
5CPARAMS :3:5x5
6DESC :25:1115337157375775157135131
7UI :9:O0,0;C2,2
8NSTATES :2:45
9STATEPOS:2:45
10MOVE :4:C0,0
11MOVE :4:L0,0
12MOVE :4:L0,1
13MOVE :4:C0,2
14MOVE :4:L0,2
15MOVE :4:A0,3
16MOVE :4:L0,3
17MOVE :4:L0,4
18MOVE :4:L1,4
19MOVE :4:A2,4
20MOVE :4:A2,4
21MOVE :4:L2,4
22MOVE :4:C1,0
23MOVE :4:L1,0
24MOVE :4:L3,0
25MOVE :4:L2,0
26MOVE :4:A4,0
27MOVE :4:A4,0
28MOVE :4:L4,0
29MOVE :4:A4,1
30MOVE :4:L4,1
31MOVE :4:L3,1
32MOVE :4:A3,2
33MOVE :4:A3,2
34MOVE :4:L3,2
35MOVE :4:L2,2
36MOVE :4:A2,1
37MOVE :4:A2,1
38MOVE :4:A1,1
39MOVE :4:A1,1
40MOVE :4:A1,1
41MOVE :4:A1,1
42MOVE :4:A1,1
43MOVE :4:A1,1
44MOVE :4:A1,2
45MOVE :4:A1,2
46MOVE :4:A1,3
47MOVE :4:C2,3
48MOVE :4:A3,3
49MOVE :4:A4,4
50MOVE :4:A4,4
51MOVE :4:A4,3
52MOVE :4:A4,2
53MOVE :4:A4,2
diff --git a/apps/plugins/puzzles/src/icons/netslide.sav b/apps/plugins/puzzles/src/icons/netslide.sav
new file mode 100644
index 0000000000..f37178ee0c
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/netslide.sav
@@ -0,0 +1,47 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :8:Netslide
4PARAMS :3:4x4
5CPARAMS :3:4x4
6SEED :15:344208514520242
7DESC :16:49b59aca247714b4
8AUXINFO :34:60d28a22f68cdb6078d67a4d6069b9ff54
9NSTATES :2:38
10STATEPOS:2:31
11MOVE :4:R0,1
12MOVE :4:C1,1
13MOVE :4:R1,1
14MOVE :4:R1,1
15MOVE :4:C3,1
16MOVE :4:C1,1
17MOVE :4:R1,1
18MOVE :4:R1,1
19MOVE :4:R3,1
20MOVE :4:R0,1
21MOVE :4:C1,1
22MOVE :5:R3,-1
23MOVE :5:R0,-1
24MOVE :4:R0,1
25MOVE :4:R0,1
26MOVE :4:C0,1
27MOVE :5:R0,-1
28MOVE :5:R0,-1
29MOVE :5:C1,-1
30MOVE :4:R1,1
31MOVE :4:C1,1
32MOVE :5:R1,-1
33MOVE :5:C0,-1
34MOVE :5:C0,-1
35MOVE :4:R3,1
36MOVE :4:C0,1
37MOVE :4:C0,1
38MOVE :5:R3,-1
39MOVE :4:C0,1
40MOVE :5:R3,-1
41MOVE :5:C1,-1
42MOVE :4:R0,1
43MOVE :4:C0,1
44MOVE :5:R0,-1
45MOVE :5:C0,-1
46MOVE :4:C1,1
47MOVE :4:R3,1
diff --git a/apps/plugins/puzzles/src/icons/palisade.sav b/apps/plugins/puzzles/src/icons/palisade.sav
new file mode 100644
index 0000000000..a935e890bb
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/palisade.sav
@@ -0,0 +1,50 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :8:Palisade
4PARAMS :5:5x5n5
5CPARAMS :5:5x5n5
6SEED :15:930059588777257
7DESC :13:2d23c33e2c1b2
8AUXINFO :52:14a191be1282597737537139d11d87fb4f21ad4a8f31e67b4441
9NSTATES :2:41
10STATEPOS:2:27
11MOVE :14:F0,1,16F0,0,64
12MOVE :15:F0,0,32F1,0,128
13MOVE :12:F1,1,8F0,1,2
14MOVE :12:F1,0,4F1,1,1
15MOVE :14:F0,2,16F0,1,64
16MOVE :12:F1,2,8F0,2,2
17MOVE :12:F0,3,1F0,2,4
18MOVE :15:F2,0,128F1,0,32
19MOVE :12:F2,0,4F2,1,1
20MOVE :12:F3,0,8F2,0,2
21MOVE :15:F1,4,128F0,4,32
22MOVE :14:F1,4,16F1,3,64
23MOVE :15:F2,4,128F1,4,32
24MOVE :14:F0,3,64F0,4,16
25MOVE :15:F1,3,128F0,3,32
26MOVE :12:F1,3,1F1,2,4
27MOVE :15:F4,4,128F3,4,32
28MOVE :14:F4,4,16F4,3,64
29MOVE :12:F3,4,8F2,4,2
30MOVE :12:F2,4,1F2,3,4
31MOVE :12:F2,3,8F1,3,2
32MOVE :14:F2,2,64F2,3,16
33MOVE :15:F2,3,32F3,3,128
34MOVE :12:F3,3,4F3,4,1
35MOVE :12:F4,3,8F3,3,2
36MOVE :14:F4,3,16F4,2,64
37MOVE :12:F1,2,1F1,1,4
38MOVE :15:F2,1,128F1,1,32
39MOVE :15:F2,2,128F1,2,32
40MOVE :12:F2,2,1F2,1,4
41MOVE :15:F3,2,128F2,2,32
42MOVE :14:F3,2,64F3,3,16
43MOVE :12:F4,2,8F3,2,2
44MOVE :12:F3,2,1F3,1,4
45MOVE :15:F2,1,32F3,1,128
46MOVE :14:F4,2,16F4,1,64
47MOVE :12:F4,1,8F3,1,2
48MOVE :14:F3,0,64F3,1,16
49MOVE :15:F4,0,128F3,0,32
50MOVE :12:F4,1,1F4,0,4
diff --git a/apps/plugins/puzzles/src/icons/pattern.sav b/apps/plugins/puzzles/src/icons/pattern.sav
new file mode 100644
index 0000000000..97c2396052
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/pattern.sav
@@ -0,0 +1,29 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Pattern
4PARAMS :5:10x10
5CPARAMS :5:10x10
6DESC :67:3.4/2.2/4.1/2.3/2.3.2/4/6/6/3.1/1/5/5/1.1/4/5/6/2.3/3.3/1.1.3/1.1.4
7NSTATES :2:22
8STATEPOS:2:22
9MOVE :8:F6,4,1,2
10MOVE :8:F7,4,1,2
11MOVE :8:F4,5,2,1
12MOVE :8:F5,4,1,1
13MOVE :8:E0,5,2,1
14MOVE :8:E0,4,3,1
15MOVE :8:F0,6,1,4
16MOVE :8:F0,1,1,2
17MOVE :8:F0,1,5,1
18MOVE :8:E1,2,1,1
19MOVE :8:F2,0,1,4
20MOVE :8:E3,2,1,1
21MOVE :8:F3,0,1,1
22MOVE :8:F4,0,1,1
23MOVE :8:F3,3,1,1
24MOVE :8:E6,3,4,1
25MOVE :8:F6,6,1,4
26MOVE :8:F7,6,1,4
27MOVE :8:E6,0,1,4
28MOVE :8:E7,0,1,3
29MOVE :8:E5,1,1,1
diff --git a/apps/plugins/puzzles/src/icons/pearl.sav b/apps/plugins/puzzles/src/icons/pearl.sav
new file mode 100644
index 0000000000..730ca85149
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/pearl.sav
@@ -0,0 +1,23 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Pearl
4PARAMS :5:6x6dt
5CPARAMS :5:6x6dt
6SEED :15:901944054393278
7DESC :17:BbBfWcWbWBaBeWgWa
8AUXINFO :72:f8bbe71b9be753d5fa143df207d7797ba62a9b3996eb8b8889487e1a2bd659d91a5e73e1
9NSTATES :2:14
10STATEPOS:1:7
11MOVE :55:F4,2,0;F1,1,0;F4,1,0;F1,0,0;F8,0,0;F2,0,1;F8,0,1;F2,0,2
12MOVE :27:F1,0,3;F4,1,3;F1,1,3;F4,2,3
13MOVE :27:F8,3,0;F2,3,1;F8,3,1;F2,3,2
14MOVE :97:F2,4,2;F8,4,1;F2,4,1;F8,4,0;F1,4,0;F4,5,0;F8,5,0;F2,5,1;F8,5,1;F2,5,2;F8,5,2;F2,5,3;F4,5,3;F1,4,3
15MOVE :13:F4,4,2;F1,3,2
16MOVE :13:F4,3,0;F1,2,0
17MOVE :69:F2,2,3;F8,2,2;F2,2,2;F8,2,1;F4,2,1;F1,1,1;F8,1,1;F2,1,2;F4,1,2;F1,0,2
18MOVE :41:F8,0,3;F2,0,4;F8,0,4;F2,0,5;F1,0,5;F4,1,5
19MOVE :27:F1,1,4;F4,2,4;F1,2,4;F4,3,4
20MOVE :13:F8,1,4;F2,1,5
21MOVE :55:F1,3,5;F4,4,5;F1,4,5;F4,5,5;F2,5,5;F8,5,4;F4,5,4;F1,4,4
22MOVE :13:F2,3,5;F8,3,4
23MOVE :13:F2,4,4;F8,4,3
diff --git a/apps/plugins/puzzles/src/icons/pegs.sav b/apps/plugins/puzzles/src/icons/pegs.sav
new file mode 100644
index 0000000000..22b8a0d82f
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/pegs.sav
@@ -0,0 +1,16 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :4:Pegs
4PARAMS :8:7x7cross
5CPARAMS :8:7x7cross
6SEED :15:103342250484448
7DESC :49:OOPPPOOOOPPPOOPPPPPPPPPPHPPPPPPPPPPOOPPPOOOOPPPOO
8NSTATES :1:8
9STATEPOS:1:8
10MOVE :7:3,1-3,3
11MOVE :7:5,2-3,2
12MOVE :7:5,4-5,2
13MOVE :7:3,4-5,4
14MOVE :7:6,4-4,4
15MOVE :7:4,0-4,2
16MOVE :7:2,0-4,0
diff --git a/apps/plugins/puzzles/src/icons/range.sav b/apps/plugins/puzzles/src/icons/range.sav
new file mode 100644
index 0000000000..708e7db248
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/range.sav
@@ -0,0 +1,36 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Range
4PARAMS :3:7x7
5CPARAMS :3:7x7
6SEED :15:989032078841515
7DESC :22:d7b3e8e5c7a7c13e4e8b4d
8UI :1:0
9NSTATES :2:27
10STATEPOS:2:27
11MOVE :5:W,4,2
12MOVE :5:W,4,3
13MOVE :5:W,4,4
14MOVE :5:W,4,5
15MOVE :5:W,4,6
16MOVE :5:W,4,0
17MOVE :5:W,3,1
18MOVE :5:W,2,1
19MOVE :5:W,1,1
20MOVE :5:W,0,1
21MOVE :5:W,6,1
22MOVE :5:W,5,1
23MOVE :5:W,5,5
24MOVE :5:W,1,5
25MOVE :5:B,5,2
26MOVE :5:W,5,3
27MOVE :5:W,6,3
28MOVE :5:W,3,6
29MOVE :5:W,2,6
30MOVE :5:B,3,5
31MOVE :5:W,2,4
32MOVE :5:W,2,2
33MOVE :5:B,2,3
34MOVE :5:W,1,3
35MOVE :5:W,3,3
36MOVE :5:W,0,5
diff --git a/apps/plugins/puzzles/src/icons/rect.sav b/apps/plugins/puzzles/src/icons/rect.sav
new file mode 100644
index 0000000000..17264daeeb
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/rect.sav
@@ -0,0 +1,17 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :10:Rectangles
4PARAMS :3:7x7
5CPARAMS :3:7x7
6DESC :33:a3d2b2a3_2a4a8h2a3c4_2b2c3a3_3c3b
7NSTATES :2:10
8STATEPOS:2:10
9MOVE :8:R0,6,3,1
10MOVE :8:R6,4,1,3
11MOVE :8:R3,6,3,1
12MOVE :8:R4,4,2,1
13MOVE :8:R3,5,3,1
14MOVE :8:R6,1,1,3
15MOVE :8:R5,0,2,1
16MOVE :8:R5,1,1,2
17MOVE :8:R4,3,2,1
diff --git a/apps/plugins/puzzles/src/icons/samegame.sav b/apps/plugins/puzzles/src/icons/samegame.sav
new file mode 100644
index 0000000000..f92b52d1b6
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/samegame.sav
@@ -0,0 +1,34 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :9:Same Game
4PARAMS :9:10x10c3s2
5CPARAMS :9:10x10c3s2
6SEED :15:785412408200083
7DESC :199:1,1,3,1,2,2,1,2,3,2,2,2,3,3,2,1,1,1,3,2,3,3,2,3,1,3,2,1,1,3,1,2,2,2,3,2,3,2,3,2,1,3,1,2,1,2,3,2,1,3,2,3,1,1,3,3,1,3,3,3,1,1,3,2,2,1,1,2,1,2,2,2,3,1,3,2,2,1,2,3,3,1,2,3,1,3,3,2,1,3,3,1,3,1,2,2,1,3,1,2
8NSTATES :2:26
9STATEPOS:2:13
10MOVE :6:M94,95
11MOVE :6:M83,84
12MOVE :9:M83,93,94
13MOVE :6:M93,94
14MOVE :6:M20,21
15MOVE :15:M20,21,22,31,32
16MOVE :6:M70,71
17MOVE :6:M80,90
18MOVE :9:M73,82,83
19MOVE :18:M72,73,74,82,83,92
20MOVE :12:M51,61,62,72
21MOVE :9:M35,36,46
22MOVE :12:M49,57,58,59
23MOVE :6:M59,69
24MOVE :9:M69,79,89
25MOVE :12:M78,79,89,99
26MOVE :24:M45,46,47,54,55,57,64,67
27MOVE :36:M36,46,55,56,57,66,67,68,77,78,88,98
28MOVE :9:M76,77,87
29MOVE :6:M97,98
30MOVE :6:M94,95
31MOVE :45:M50,60,61,70,71,81,82,83,84,85,90,91,92,93,94
32MOVE :12:M73,81,82,83
33MOVE :6:M92,93
34MOVE :9:M81,90,91
diff --git a/apps/plugins/puzzles/src/icons/screenshot.sh b/apps/plugins/puzzles/src/icons/screenshot.sh
new file mode 100755
index 0000000000..0e2a06eea7
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/screenshot.sh
@@ -0,0 +1,25 @@
1#!/bin/sh
2
3# Generate a screenshot from a puzzle save file. Takes the
4# following arguments, in order:
5#
6# - the name of the puzzle binary
7# - the name of the save file
8# - the name of the output image file
9# - (optionally) the proportion of the next move to redo before
10# taking the screenshot.
11#
12# This script requires access to an X server in order to run, but
13# seems to work fine under xvfb-run if you haven't got a real one
14# available (or if you don't want to use it for some reason).
15
16binary="$1"
17save="$2"
18image="$3"
19if test "x$4" != "x"; then
20 redo="--redo $4"
21else
22 redo=
23fi
24
25"$binary" $redo --screenshot "$image" --load "$save"
diff --git a/apps/plugins/puzzles/src/icons/signpost.sav b/apps/plugins/puzzles/src/icons/signpost.sav
new file mode 100644
index 0000000000..9ad1958ddf
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/signpost.sav
@@ -0,0 +1,23 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :8:Signpost
4PARAMS :4:4x4c
5CPARAMS :4:4x4c
6SEED :15:230468784719861
7DESC :19:1eceebecfbfhgcaa16a
8NSTATES :2:15
9STATEPOS:2:11
10MOVE :8:L2,1-3,1
11MOVE :8:L0,1-1,0
12MOVE :8:L2,2-1,1
13MOVE :8:L1,2-0,3
14MOVE :8:L0,2-2,0
15MOVE :8:L1,3-1,2
16MOVE :8:L1,1-1,3
17MOVE :8:L1,0-3,0
18MOVE :8:L0,0-0,1
19MOVE :8:L3,0-3,2
20MOVE :8:L3,2-0,2
21MOVE :8:L3,1-2,2
22MOVE :8:L2,3-2,1
23MOVE :8:L2,0-2,3
diff --git a/apps/plugins/puzzles/src/icons/singles.sav b/apps/plugins/puzzles/src/icons/singles.sav
new file mode 100644
index 0000000000..260fd1f2b3
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/singles.sav
@@ -0,0 +1,45 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Singles
4PARAMS :5:6x6dk
5CPARAMS :5:6x6dk
6SEED :15:781273601054598
7DESC :36:361566412253452144234115163346553461
8NSTATES :2:37
9STATEPOS:2:22
10MOVE :4:B1,0
11MOVE :4:C0,0
12MOVE :4:C1,1
13MOVE :4:C2,0
14MOVE :4:C0,1
15MOVE :4:B0,2
16MOVE :4:C0,3
17MOVE :4:C1,2
18MOVE :4:C4,3
19MOVE :4:B3,3
20MOVE :4:C3,2
21MOVE :4:C2,3
22MOVE :4:C3,4
23MOVE :4:B2,4
24MOVE :4:C1,4
25MOVE :4:C2,5
26MOVE :4:B1,5
27MOVE :4:C0,5
28MOVE :4:C0,4
29MOVE :4:C1,3
30MOVE :4:C3,5
31MOVE :4:B5,4
32MOVE :4:C4,4
33MOVE :4:C5,5
34MOVE :4:C5,3
35MOVE :4:C4,5
36MOVE :4:B4,0
37MOVE :4:C3,0
38MOVE :4:C4,1
39MOVE :4:C5,0
40MOVE :4:C5,1
41MOVE :4:B4,2
42MOVE :4:C5,2
43MOVE :4:C3,1
44MOVE :4:B2,1
45MOVE :4:C2,2
diff --git a/apps/plugins/puzzles/src/icons/sixteen.sav b/apps/plugins/puzzles/src/icons/sixteen.sav
new file mode 100644
index 0000000000..076b1fbd4d
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/sixteen.sav
@@ -0,0 +1,39 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Sixteen
4PARAMS :3:4x4
5CPARAMS :3:4x4
6SEED :15:601798566229573
7DESC :38:2,16,3,10,13,8,7,4,9,14,12,11,15,1,5,6
8NSTATES :2:31
9STATEPOS:2:24
10MOVE :5:C3,-1
11MOVE :4:R0,1
12MOVE :4:C1,1
13MOVE :5:R0,-1
14MOVE :5:C1,-1
15MOVE :5:C3,-1
16MOVE :4:R2,1
17MOVE :4:R2,1
18MOVE :4:C3,1
19MOVE :5:C2,-1
20MOVE :5:C2,-1
21MOVE :4:R1,1
22MOVE :4:R1,1
23MOVE :4:C2,1
24MOVE :4:C2,1
25MOVE :4:R3,1
26MOVE :4:R3,1
27MOVE :4:C1,1
28MOVE :5:R2,-1
29MOVE :5:R2,-1
30MOVE :5:C1,-1
31MOVE :4:R2,1
32MOVE :4:C0,1
33MOVE :4:R3,1
34MOVE :5:R2,-1
35MOVE :5:C1,-1
36MOVE :4:R2,1
37MOVE :4:C1,1
38MOVE :5:R3,-1
39MOVE :5:C0,-1
diff --git a/apps/plugins/puzzles/src/icons/slant.sav b/apps/plugins/puzzles/src/icons/slant.sav
new file mode 100644
index 0000000000..02017e5d48
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/slant.sav
@@ -0,0 +1,51 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Slant
4PARAMS :5:8x8de
5CPARAMS :5:8x8de
6DESC :47:a10h23a32a02b22e3a2c1g3a20d32a0c221a210i0a101b0
7NSTATES :2:44
8STATEPOS:2:44
9MOVE :4:/7,0
10MOVE :4:\7,1
11MOVE :4:\1,0
12MOVE :4:/2,0
13MOVE :4:\0,4
14MOVE :4:/0,5
15MOVE :4:\0,6
16MOVE :4:/0,7
17MOVE :4:\1,7
18MOVE :4:/7,7
19MOVE :4:/3,7
20MOVE :4:\4,7
21MOVE :4:\5,7
22MOVE :4:/2,7
23MOVE :4:/7,4
24MOVE :4:\7,5
25MOVE :4:\7,3
26MOVE :4:\7,2
27MOVE :4:/6,2
28MOVE :4:\6,3
29MOVE :4:\7,6
30MOVE :4:/3,0
31MOVE :4:/2,1
32MOVE :4:\3,1
33MOVE :4:/2,2
34MOVE :4:\3,2
35MOVE :4:/2,3
36MOVE :4:\3,3
37MOVE :4:\1,1
38MOVE :4:/0,1
39MOVE :4:\0,2
40MOVE :4:\1,2
41MOVE :4:\1,3
42MOVE :4:/0,3
43MOVE :4:\1,4
44MOVE :4:\0,0
45MOVE :4:\5,3
46MOVE :4:\6,4
47MOVE :4:/5,4
48MOVE :4:/5,5
49MOVE :4:\6,5
50MOVE :4:/4,5
51MOVE :4:\4,6
diff --git a/apps/plugins/puzzles/src/icons/solo.sav b/apps/plugins/puzzles/src/icons/solo.sav
new file mode 100644
index 0000000000..385cc68fe5
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/solo.sav
@@ -0,0 +1,36 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :4:Solo
4PARAMS :3:3x3
5CPARAMS :3:3x3
6DESC :73:a2e9a5b6a2b3_7a1_4a9_6a2b4_1a7_2b1_7e6_9b2_5a8_1b6a5_9a3_8a7_2b8a6b1a1e4a
7NSTATES :2:29
8STATEPOS:2:29
9MOVE :6:R7,1,1
10MOVE :6:R4,6,1
11MOVE :6:R5,0,1
12MOVE :6:R0,0,4
13MOVE :6:R2,0,6
14MOVE :6:R1,2,3
15MOVE :6:R0,3,9
16MOVE :6:R1,3,5
17MOVE :6:R2,4,8
18MOVE :6:R0,5,3
19MOVE :6:R1,5,6
20MOVE :6:R1,6,4
21MOVE :6:R4,7,4
22MOVE :6:R7,6,2
23MOVE :6:R7,7,5
24MOVE :6:R8,8,6
25MOVE :6:R7,3,3
26MOVE :6:R8,3,7
27MOVE :6:R8,3,8
28MOVE :6:R6,4,5
29MOVE :6:R7,5,7
30MOVE :6:R8,5,4
31MOVE :6:R7,2,8
32MOVE :6:R8,0,5
33MOVE :6:R4,2,5
34MOVE :6:R4,3,6
35MOVE :6:R5,4,4
36MOVE :6:R4,5,9
diff --git a/apps/plugins/puzzles/src/icons/square.pl b/apps/plugins/puzzles/src/icons/square.pl
new file mode 100755
index 0000000000..815b94b532
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/square.pl
@@ -0,0 +1,95 @@
1#!/usr/bin/perl
2
3# Read an input image, crop its border to a standard width, and
4# convert it into a square output image. Parameters are:
5#
6# - the required total image size
7# - the output border thickness
8# - the input image file name
9# - the output image file name.
10
11($osize, $oborder, $infile, $outfile) = @ARGV;
12
13# Determine the input image's size.
14$ident = `identify -format "%w %h" $infile`;
15$ident =~ /(\d+) (\d+)/ or die "unable to get size for $infile\n";
16($w, $h) = ($1, $2);
17
18# Read the input image data.
19$data = [];
20open IDATA, "convert -depth 8 $infile rgb:- |";
21push @$data, $rgb while (read IDATA,$rgb,3,0) == 3;
22close IDATA;
23# Check we have the right amount of data.
24$xl = $w * $h;
25$al = scalar @$data;
26die "wrong amount of image data ($al, expected $xl) from $infile\n"
27 unless $al == $xl;
28
29# Find the background colour, by looking around the entire border
30# and finding the most popular pixel colour.
31for ($i = 0; $i < $w; $i++) {
32 $pcount{$data->[$i]}++; # top row
33 $pcount{$data->[($h-1)*$w+$i]}++; # bottom row
34}
35for ($i = 1; $i < $h-1; $i++) {
36 $pcount{$data->[$i*$w]}++; # left column
37 $pcount{$data->[$i*$w+$w-1]}++; # right column
38}
39@plist = sort { $pcount{$b} <=> $pcount{$a} } keys %pcount;
40$back = $plist[0];
41
42# Crop rows and columns off the image to find the central rectangle
43# of non-background stuff.
44$ystart = 0;
45$ystart++ while $ystart < $h and scalar(grep { $_ ne $back } map { $data->[$ystart*$w+$_] } 0 .. ($w-1)) == 0;
46$yend = $h-1;
47$yend-- while $yend >= $ystart and scalar(grep { $_ ne $back } map { $data->[$yend*$w+$_] } 0 .. ($w-1)) == 0;
48$xstart = 0;
49$xstart++ while $xstart < $w and scalar(grep { $_ ne $back } map { $data->[$_*$w+$xstart] } 0 .. ($h-1)) == 0;
50$xend = $w-1;
51$xend-- while $xend >= $xstart and scalar(grep { $_ ne $back } map { $data->[$_*$w+$xend] } 0 .. ($h-1)) == 0;
52
53# Decide how much border we're going to put back on to make the
54# image perfectly square.
55$hexpand = ($yend-$ystart) - ($xend-$xstart);
56if ($hexpand > 0) {
57 $left = int($hexpand / 2);
58 $xstart -= $left;
59 $xend += $hexpand - $left;
60} elsif ($hexpand < 0) {
61 $vexpand = -$hexpand;
62 $top = int($vexpand / 2);
63 $ystart -= $top;
64 $yend += $vexpand - $top;
65}
66$ow = $xend - $xstart + 1;
67$oh = $yend - $ystart + 1;
68die "internal computation problem" if $ow != $oh; # should be square
69
70# And decide how much _more_ border goes on to add the bit around
71# the edge.
72$realow = int($ow * ($osize / ($osize - 2*$oborder)));
73$extra = $realow - $ow;
74$left = int($extra / 2);
75$xstart -= $left;
76$xend += $extra - $left;
77$top = int($extra / 2);
78$ystart -= $top;
79$yend += $extra - $top;
80$ow = $xend - $xstart + 1;
81$oh = $yend - $ystart + 1;
82die "internal computation problem" if $ow != $oh; # should be square
83
84# Now write out the resulting image, and resize it appropriately.
85open IDATA, "| convert -size ${ow}x${oh} -depth 8 -resize ${osize}x${osize}! rgb:- $outfile";
86for ($y = $ystart; $y <= $yend; $y++) {
87 for ($x = $xstart; $x <= $xend; $x++) {
88 if ($x >= 0 && $x < $w && $y >= 0 && $y < $h) {
89 print IDATA $data->[$y*$w+$x];
90 } else {
91 print IDATA $back;
92 }
93 }
94}
95close IDATA;
diff --git a/apps/plugins/puzzles/src/icons/tents.sav b/apps/plugins/puzzles/src/icons/tents.sav
new file mode 100644
index 0000000000..292c2d2d75
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/tents.sav
@@ -0,0 +1,32 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :5:Tents
4PARAMS :5:8x8de
5CPARAMS :5:8x8de
6DESC :45:ea_ddidfaabkd,3,0,2,1,2,2,1,1,3,1,1,1,1,1,3,1
7NSTATES :2:25
8STATEPOS:2:25
9MOVE :9:N5,6;N6,6
10MOVE :14:N5,7;N6,7;N7,7
11MOVE :9:N0,7;N1,7
12MOVE :4:N1,6
13MOVE :14:N6,2;N6,3;N6,4
14MOVE :9:N7,2;N7,3
15MOVE :14:N1,0;N2,0;N3,0
16MOVE :4:N3,1
17MOVE :4:N0,3
18MOVE :4:N3,4
19MOVE :4:N5,4
20MOVE :4:T6,0
21MOVE :4:T4,0
22MOVE :4:T0,0
23MOVE :4:N1,1
24MOVE :4:N4,1
25MOVE :9:N6,1;N7,1
26MOVE :4:T2,1
27MOVE :9:N1,2;N3,2
28MOVE :4:T5,2
29MOVE :4:N4,2
30MOVE :4:N5,3
31MOVE :4:N0,2
32MOVE :9:N1,3;N1,5
diff --git a/apps/plugins/puzzles/src/icons/towers.sav b/apps/plugins/puzzles/src/icons/towers.sav
new file mode 100644
index 0000000000..351d473a63
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/towers.sav
@@ -0,0 +1,26 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :6:Towers
4PARAMS :3:4de
5CPARAMS :3:4de
6SEED :15:888431554483015
7DESC :31:2/2/1/3/2/2/3/1/3/1/2/2/2/3/2/1
8AUXINFO :34:297d7a2fcf9e14403a74c976fe0fefd306
9NSTATES :2:17
10STATEPOS:2:10
11MOVE :6:R2,0,4
12MOVE :6:R0,1,4
13MOVE :6:R3,3,4
14MOVE :6:R1,2,4
15MOVE :6:R0,3,3
16MOVE :6:R1,0,3
17MOVE :6:R3,2,3
18MOVE :6:R2,1,3
19MOVE :6:R3,0,2
20MOVE :6:R3,1,1
21MOVE :6:R1,1,2
22MOVE :6:R1,3,1
23MOVE :6:R2,3,2
24MOVE :6:R2,2,1
25MOVE :6:R0,2,2
26MOVE :6:R0,0,1
diff --git a/apps/plugins/puzzles/src/icons/tracks.sav b/apps/plugins/puzzles/src/icons/tracks.sav
new file mode 100644
index 0000000000..ca30644506
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/tracks.sav
@@ -0,0 +1,31 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :12:Train Tracks
4PARAMS :5:6x6dt
5CPARAMS :5:6x6dt
6SEED :15:145870397370785
7DESC :31:l6t9b,3,2,1,S4,5,4,2,6,S3,2,3,3
8NSTATES :2:23
9STATEPOS:2:20
10MOVE :5:TD0,0
11MOVE :5:TR0,0
12MOVE :5:TL5,5
13MOVE :5:TU5,5
14MOVE :29:TS1,1;TS2,1;TS3,1;TS4,1;TS5,1
15MOVE :29:NS2,0;NS2,2;NS2,3;NS2,4;NS2,5
16MOVE :5:TU1,1
17MOVE :17:NS0,3;NS0,4;NS0,5
18MOVE :23:NS1,2;NS1,3;NS1,4;NS1,5
19MOVE :5:TL2,1
20MOVE :5:TL3,1
21MOVE :5:TS4,4
22MOVE :5:TS3,4
23MOVE :17:NS3,0;NS4,0;NS5,0
24MOVE :5:TS4,2
25MOVE :5:TS4,3
26MOVE :5:TU3,4
27MOVE :5:TL4,4
28MOVE :5:TL5,1
29MOVE :5:TU5,2
30MOVE :5:NS5,3
31MOVE :5:NS3,2
diff --git a/apps/plugins/puzzles/src/icons/twiddle.sav b/apps/plugins/puzzles/src/icons/twiddle.sav
new file mode 100644
index 0000000000..2863033f99
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/twiddle.sav
@@ -0,0 +1,35 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Twiddle
4PARAMS :5:3x3n2
5CPARAMS :5:3x3n2
6SEED :15:635499951462226
7DESC :17:3,7,2,6,5,1,8,4,9
8NSTATES :2:27
9STATEPOS:2:22
10MOVE :7:M0,0,-1
11MOVE :7:M1,0,-1
12MOVE :6:M1,1,1
13MOVE :6:M0,1,1
14MOVE :6:M0,0,1
15MOVE :6:M0,0,1
16MOVE :7:M1,1,-1
17MOVE :7:M0,1,-1
18MOVE :7:M0,1,-1
19MOVE :7:M1,1,-1
20MOVE :6:M0,1,1
21MOVE :7:M0,1,-1
22MOVE :6:M1,1,1
23MOVE :6:M1,1,1
24MOVE :6:M0,1,1
25MOVE :6:M0,1,1
26MOVE :7:M0,1,-1
27MOVE :7:M1,1,-1
28MOVE :7:M0,1,-1
29MOVE :7:M1,1,-1
30MOVE :6:M0,1,1
31MOVE :7:M1,0,-1
32MOVE :7:M0,1,-1
33MOVE :6:M1,0,1
34MOVE :6:M1,1,1
35MOVE :6:M1,1,1
diff --git a/apps/plugins/puzzles/src/icons/undead.sav b/apps/plugins/puzzles/src/icons/undead.sav
new file mode 100644
index 0000000000..3c314dde8a
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/undead.sav
@@ -0,0 +1,14 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :6:Undead
4PARAMS :6:4x4de2
5CPARAMS :6:4x4de2
6DESC :48:5,2,2,aRLgLLaLRL,2,0,1,2,1,1,2,5,0,0,0,2,1,3,1,1
7NSTATES :1:7
8STATEPOS:1:7
9MOVE :2:G0
10MOVE :2:V0
11MOVE :2:G2
12MOVE :2:G3
13MOVE :2:V3
14MOVE :2:Z3
diff --git a/apps/plugins/puzzles/src/icons/unequal.sav b/apps/plugins/puzzles/src/icons/unequal.sav
new file mode 100644
index 0000000000..c414513a22
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/unequal.sav
@@ -0,0 +1,25 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :7:Unequal
4PARAMS :3:4de
5CPARAMS :3:4de
6SEED :15:143029490219212
7DESC :37:0D,0,0L,0,0,0,0,0,0D,0U,0R,0,0,0,0,4,
8AUXINFO :34:f51274dc41e0a39caa38942fc525ed0108
9NSTATES :2:16
10STATEPOS:1:6
11MOVE :6:R2,1,4
12MOVE :6:R1,2,4
13MOVE :6:R0,0,4
14MOVE :6:R2,3,1
15MOVE :6:R3,2,1
16MOVE :6:R0,2,3
17MOVE :6:R2,2,2
18MOVE :6:R0,3,2
19MOVE :6:R1,3,3
20MOVE :6:R0,1,1
21MOVE :6:R1,1,2
22MOVE :6:R3,1,3
23MOVE :6:R3,0,2
24MOVE :6:R2,0,3
25MOVE :6:R1,0,1
diff --git a/apps/plugins/puzzles/src/icons/unruly.sav b/apps/plugins/puzzles/src/icons/unruly.sav
new file mode 100644
index 0000000000..0f7ca1b9dd
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/unruly.sav
@@ -0,0 +1,22 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :6:Unruly
4PARAMS :5:6x6de
5CPARAMS :5:6x6de
6DESC :10:faCADAJeBd
7NSTATES :2:15
8STATEPOS:2:15
9MOVE :6:P0,1,2
10MOVE :6:P0,4,2
11MOVE :6:P0,3,3
12MOVE :6:P0,3,0
13MOVE :6:P0,5,1
14MOVE :6:P0,2,1
15MOVE :6:P1,4,0
16MOVE :6:P1,1,1
17MOVE :6:P1,5,2
18MOVE :6:P0,0,2
19MOVE :6:P1,0,3
20MOVE :6:P1,0,0
21MOVE :6:P1,0,4
22MOVE :6:P0,2,4
diff --git a/apps/plugins/puzzles/src/icons/untangle.sav b/apps/plugins/puzzles/src/icons/untangle.sav
new file mode 100644
index 0000000000..016318a521
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/untangle.sav
@@ -0,0 +1,16 @@
1SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
2VERSION :1:1
3GAME :8:Untangle
4PARAMS :2:10
5CPARAMS :2:10
6SEED :15:761628688787632
7DESC :63:0-1,0-5,0-8,0-9,1-4,1-8,2-6,2-7,3-5,3-6,3-9,4-5,4-7,5-7,6-7,8-9
8AUXINFO :182:01bee8258e3164fe966f294b2837b6584b965b8d8e97571ba48f26c9bc0a91ac4b49fb4652bfaa5c340c82c57afbaa4620f2f6d49d7a7b330a66594d2b88c499d57c4093379b7dc322f2afa1ebab81004585751c39c19f8f9930c4
9NSTATES :1:7
10STATEPOS:1:6
11MOVE :12:P8:168,16/64
12MOVE :12:P0:186,85/64
13MOVE :12:P2:47,254/64
14MOVE :13:P5:131,153/64
15MOVE :12:P3:75,126/64
16MOVE :12:P7:93,303/64
diff --git a/apps/plugins/puzzles/src/icons/win16pal.xpm b/apps/plugins/puzzles/src/icons/win16pal.xpm
new file mode 100644
index 0000000000..66fd60a480
--- /dev/null
+++ b/apps/plugins/puzzles/src/icons/win16pal.xpm
@@ -0,0 +1,23 @@
1/* XPM */
2static char *win16pal[] = {
3/* columns rows colors chars-per-pixel */
4"16 1 16 1",
5" c #000000",
6". c #800000",
7"X c #008000",
8"o c #808000",
9"O c #000080",
10"+ c #800080",
11"@ c #008080",
12"# c #C0C0C0",
13"$ c #808080",
14"% c #FF0000",
15"& c #00FF00",
16"* c #FFFF00",
17"= c #0000FF",
18"- c #FF00FF",
19"; c #00FFFF",
20": c #FFFFFF",
21/* pixels */
22" .XoO+@#$%&*=-;:"
23};
diff --git a/apps/plugins/puzzles/src/index.html b/apps/plugins/puzzles/src/index.html
new file mode 100644
index 0000000000..460883a32c
--- /dev/null
+++ b/apps/plugins/puzzles/src/index.html
@@ -0,0 +1,67 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Simon Tatham's Portable Puzzle Collection</title>
7<link rel="index" href="docindex.html">
8<link rel="next" href="intro.html">
9<meta name="AppleTitle" content="Puzzles Help"></head>
10<body>
11<p>Previous | Contents | <a href="docindex.html">Index</a> | <a href="intro.html">Next</a></p>
12<h1>Simon Tatham's Portable Puzzle Collection</h1>
13<p>
14This is a collection of small one-player puzzle games.
15</p>
16<p>
17This manual is copyright 2004-2014 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See <a href="licence.html#AA">appendix A</a> for the licence text in full.
18</p>
19
20<ul>
21<li><a href="intro.html#C1">Chapter 1: Introduction</a></li>
22<li><a href="common.html#C2">Chapter 2: Common features</a></li>
23<li><a href="net.html#C3">Chapter 3: Net</a></li>
24<li><a href="cube.html#C4">Chapter 4: Cube</a></li>
25<li><a href="fifteen.html#C5">Chapter 5: Fifteen</a></li>
26<li><a href="sixteen.html#C6">Chapter 6: Sixteen</a></li>
27<li><a href="twiddle.html#C7">Chapter 7: Twiddle</a></li>
28<li><a href="rect.html#C8">Chapter 8: Rectangles</a></li>
29<li><a href="netslide.html#C9">Chapter 9: Netslide</a></li>
30<li><a href="pattern.html#C10">Chapter 10: Pattern</a></li>
31<li><a href="solo.html#C11">Chapter 11: Solo</a></li>
32<li><a href="mines.html#C12">Chapter 12: Mines</a></li>
33<li><a href="samegame.html#C13">Chapter 13: Same Game</a></li>
34<li><a href="flip.html#C14">Chapter 14: Flip</a></li>
35<li><a href="guess.html#C15">Chapter 15: Guess</a></li>
36<li><a href="pegs.html#C16">Chapter 16: Pegs</a></li>
37<li><a href="dominosa.html#C17">Chapter 17: Dominosa</a></li>
38<li><a href="untangle.html#C18">Chapter 18: Untangle</a></li>
39<li><a href="blackbox.html#C19">Chapter 19: Black Box</a></li>
40<li><a href="slant.html#C20">Chapter 20: Slant</a></li>
41<li><a href="lightup.html#C21">Chapter 21: Light Up</a></li>
42<li><a href="map.html#C22">Chapter 22: Map</a></li>
43<li><a href="loopy.html#C23">Chapter 23: Loopy</a></li>
44<li><a href="inertia.html#C24">Chapter 24: Inertia</a></li>
45<li><a href="tents.html#C25">Chapter 25: Tents</a></li>
46<li><a href="bridges.html#C26">Chapter 26: Bridges</a></li>
47<li><a href="unequal.html#C27">Chapter 27: Unequal</a></li>
48<li><a href="galaxies.html#C28">Chapter 28: Galaxies</a></li>
49<li><a href="filling.html#C29">Chapter 29: Filling</a></li>
50<li><a href="keen.html#C30">Chapter 30: Keen</a></li>
51<li><a href="towers.html#C31">Chapter 31: Towers</a></li>
52<li><a href="singles.html#C32">Chapter 32: Singles</a></li>
53<li><a href="magnets.html#C33">Chapter 33: Magnets</a></li>
54<li><a href="signpost.html#C34">Chapter 34: Signpost</a></li>
55<li><a href="range.html#C35">Chapter 35: Range</a></li>
56<li><a href="pearl.html#C36">Chapter 36: Pearl</a></li>
57<li><a href="undead.html#C37">Chapter 37: Undead</a></li>
58<li><a href="unruly.html#C38">Chapter 38: Unruly</a></li>
59<li><a href="flood.html#C39">Chapter 39: Flood</a></li>
60<li><a href="tracks.html#C40">Chapter 40: Tracks</a></li>
61<li><a href="palisade.html#C41">Chapter 41: Palisade</a></li>
62<li><a href="licence.html#AA">Appendix A: Licence</a></li>
63<li><a href="docindex.html#Index">Index</a></li>
64</ul>
65
66<hr><address></address></body>
67</html>
diff --git a/apps/plugins/puzzles/src/inertia.R b/apps/plugins/puzzles/src/inertia.R
new file mode 100644
index 0000000000..e6e86beaec
--- /dev/null
+++ b/apps/plugins/puzzles/src/inertia.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3inertia : [X] GTK COMMON inertia inertia-icon|no-icon
4
5inertia : [G] WINDOWS COMMON inertia inertia.res|noicon.res
6
7ALL += inertia[COMBINED]
8
9!begin am gtk
10GAMES += inertia
11!end
12
13!begin >list.c
14 A(inertia) \
15!end
16
17!begin >gamedesc.txt
18inertia:inertia.exe:Inertia:Gem-collecting puzzle:Collect all the gems without running into any of the mines.
19!end
diff --git a/apps/plugins/puzzles/src/inertia.c b/apps/plugins/puzzles/src/inertia.c
new file mode 100644
index 0000000000..c22d2e17d4
--- /dev/null
+++ b/apps/plugins/puzzles/src/inertia.c
@@ -0,0 +1,2249 @@
1/*
2 * inertia.c: Game involving navigating round a grid picking up
3 * gems.
4 *
5 * Game rules and basic generator design by Ben Olmstead.
6 * This re-implementation was written by Simon Tatham.
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <assert.h>
13#include <ctype.h>
14#include <math.h>
15
16#include "puzzles.h"
17
18/* Used in the game_state */
19#define BLANK 'b'
20#define GEM 'g'
21#define MINE 'm'
22#define STOP 's'
23#define WALL 'w'
24
25/* Used in the game IDs */
26#define START 'S'
27
28/* Used in the game generation */
29#define POSSGEM 'G'
30
31/* Used only in the game_drawstate*/
32#define UNDRAWN '?'
33
34#define DIRECTIONS 8
35#define DP1 (DIRECTIONS+1)
36#define DX(dir) ( (dir) & 3 ? (((dir) & 7) > 4 ? -1 : +1) : 0 )
37#define DY(dir) ( DX((dir)+6) )
38
39/*
40 * Lvalue macro which expects x and y to be in range.
41 */
42#define LV_AT(w, h, grid, x, y) ( (grid)[(y)*(w)+(x)] )
43
44/*
45 * Rvalue macro which can cope with x and y being out of range.
46 */
47#define AT(w, h, grid, x, y) ( (x)<0 || (x)>=(w) || (y)<0 || (y)>=(h) ? \
48 WALL : LV_AT(w, h, grid, x, y) )
49
50enum {
51 COL_BACKGROUND,
52 COL_OUTLINE,
53 COL_HIGHLIGHT,
54 COL_LOWLIGHT,
55 COL_PLAYER,
56 COL_DEAD_PLAYER,
57 COL_MINE,
58 COL_GEM,
59 COL_WALL,
60 COL_HINT,
61 NCOLOURS
62};
63
64struct game_params {
65 int w, h;
66};
67
68typedef struct soln {
69 int refcount;
70 int len;
71 unsigned char *list;
72} soln;
73
74struct game_state {
75 game_params p;
76 int px, py;
77 int gems;
78 char *grid;
79 int distance_moved;
80 int dead;
81 int cheated;
82 int solnpos;
83 soln *soln;
84};
85
86static game_params *default_params(void)
87{
88 game_params *ret = snew(game_params);
89
90 ret->w = 10;
91#ifdef PORTRAIT_SCREEN
92 ret->h = 10;
93#else
94 ret->h = 8;
95#endif
96 return ret;
97}
98
99static void free_params(game_params *params)
100{
101 sfree(params);
102}
103
104static game_params *dup_params(const game_params *params)
105{
106 game_params *ret = snew(game_params);
107 *ret = *params; /* structure copy */
108 return ret;
109}
110
111static const struct game_params inertia_presets[] = {
112#ifdef PORTRAIT_SCREEN
113 { 10, 10 },
114 { 12, 12 },
115 { 16, 16 },
116#else
117 { 10, 8 },
118 { 15, 12 },
119 { 20, 16 },
120#endif
121};
122
123static int game_fetch_preset(int i, char **name, game_params **params)
124{
125 game_params p, *ret;
126 char *retname;
127 char namebuf[80];
128
129 if (i < 0 || i >= lenof(inertia_presets))
130 return FALSE;
131
132 p = inertia_presets[i];
133 ret = dup_params(&p);
134 sprintf(namebuf, "%dx%d", ret->w, ret->h);
135 retname = dupstr(namebuf);
136
137 *params = ret;
138 *name = retname;
139 return TRUE;
140}
141
142static void decode_params(game_params *params, char const *string)
143{
144 params->w = params->h = atoi(string);
145 while (*string && isdigit((unsigned char)*string)) string++;
146 if (*string == 'x') {
147 string++;
148 params->h = atoi(string);
149 }
150}
151
152static char *encode_params(const game_params *params, int full)
153{
154 char data[256];
155
156 sprintf(data, "%dx%d", params->w, params->h);
157
158 return dupstr(data);
159}
160
161static config_item *game_configure(const game_params *params)
162{
163 config_item *ret;
164 char buf[80];
165
166 ret = snewn(3, config_item);
167
168 ret[0].name = "Width";
169 ret[0].type = C_STRING;
170 sprintf(buf, "%d", params->w);
171 ret[0].sval = dupstr(buf);
172 ret[0].ival = 0;
173
174 ret[1].name = "Height";
175 ret[1].type = C_STRING;
176 sprintf(buf, "%d", params->h);
177 ret[1].sval = dupstr(buf);
178 ret[1].ival = 0;
179
180 ret[2].name = NULL;
181 ret[2].type = C_END;
182 ret[2].sval = NULL;
183 ret[2].ival = 0;
184
185 return ret;
186}
187
188static game_params *custom_params(const config_item *cfg)
189{
190 game_params *ret = snew(game_params);
191
192 ret->w = atoi(cfg[0].sval);
193 ret->h = atoi(cfg[1].sval);
194
195 return ret;
196}
197
198static char *validate_params(const game_params *params, int full)
199{
200 /*
201 * Avoid completely degenerate cases which only have one
202 * row/column. We probably could generate completable puzzles
203 * of that shape, but they'd be forced to be extremely boring
204 * and at large sizes would take a while to happen upon at
205 * random as well.
206 */
207 if (params->w < 2 || params->h < 2)
208 return "Width and height must both be at least two";
209
210 /*
211 * The grid construction algorithm creates 1/5 as many gems as
212 * grid squares, and must create at least one gem to have an
213 * actual puzzle. However, an area-five grid is ruled out by
214 * the above constraint, so the practical minimum is six.
215 */
216 if (params->w * params->h < 6)
217 return "Grid area must be at least six squares";
218
219 return NULL;
220}
221
222/* ----------------------------------------------------------------------
223 * Solver used by grid generator.
224 */
225
226struct solver_scratch {
227 unsigned char *reachable_from, *reachable_to;
228 int *positions;
229};
230
231static struct solver_scratch *new_scratch(int w, int h)
232{
233 struct solver_scratch *sc = snew(struct solver_scratch);
234
235 sc->reachable_from = snewn(w * h * DIRECTIONS, unsigned char);
236 sc->reachable_to = snewn(w * h * DIRECTIONS, unsigned char);
237 sc->positions = snewn(w * h * DIRECTIONS, int);
238
239 return sc;
240}
241
242static void free_scratch(struct solver_scratch *sc)
243{
244 sfree(sc->reachable_from);
245 sfree(sc->reachable_to);
246 sfree(sc->positions);
247 sfree(sc);
248}
249
250static int can_go(int w, int h, char *grid,
251 int x1, int y1, int dir1, int x2, int y2, int dir2)
252{
253 /*
254 * Returns TRUE if we can transition directly from (x1,y1)
255 * going in direction dir1, to (x2,y2) going in direction dir2.
256 */
257
258 /*
259 * If we're actually in the middle of an unoccupyable square,
260 * we cannot make any move.
261 */
262 if (AT(w, h, grid, x1, y1) == WALL ||
263 AT(w, h, grid, x1, y1) == MINE)
264 return FALSE;
265
266 /*
267 * If a move is capable of stopping at x1,y1,dir1, and x2,y2 is
268 * the same coordinate as x1,y1, then we can make the
269 * transition (by stopping and changing direction).
270 *
271 * For this to be the case, we have to either have a wall
272 * beyond x1,y1,dir1, or have a stop on x1,y1.
273 */
274 if (x2 == x1 && y2 == y1 &&
275 (AT(w, h, grid, x1, y1) == STOP ||
276 AT(w, h, grid, x1, y1) == START ||
277 AT(w, h, grid, x1+DX(dir1), y1+DY(dir1)) == WALL))
278 return TRUE;
279
280 /*
281 * If a move is capable of continuing here, then x1,y1,dir1 can
282 * move one space further on.
283 */
284 if (x2 == x1+DX(dir1) && y2 == y1+DY(dir1) && dir1 == dir2 &&
285 (AT(w, h, grid, x2, y2) == BLANK ||
286 AT(w, h, grid, x2, y2) == GEM ||
287 AT(w, h, grid, x2, y2) == STOP ||
288 AT(w, h, grid, x2, y2) == START))
289 return TRUE;
290
291 /*
292 * That's it.
293 */
294 return FALSE;
295}
296
297static int find_gem_candidates(int w, int h, char *grid,
298 struct solver_scratch *sc)
299{
300 int wh = w*h;
301 int head, tail;
302 int sx, sy, gx, gy, gd, pass, possgems;
303
304 /*
305 * This function finds all the candidate gem squares, which are
306 * precisely those squares which can be picked up on a loop
307 * from the starting point back to the starting point. Doing
308 * this may involve passing through such a square in the middle
309 * of a move; so simple breadth-first search over the _squares_
310 * of the grid isn't quite adequate, because it might be that
311 * we can only reach a gem from the start by moving over it in
312 * one direction, but can only return to the start if we were
313 * moving over it in another direction.
314 *
315 * Instead, we BFS over a space which mentions each grid square
316 * eight times - once for each direction. We also BFS twice:
317 * once to find out what square+direction pairs we can reach
318 * _from_ the start point, and once to find out what pairs we
319 * can reach the start point from. Then a square is reachable
320 * if any of the eight directions for that square has both
321 * flags set.
322 */
323
324 memset(sc->reachable_from, 0, wh * DIRECTIONS);
325 memset(sc->reachable_to, 0, wh * DIRECTIONS);
326
327 /*
328 * Find the starting square.
329 */
330 sx = -1; /* placate optimiser */
331 for (sy = 0; sy < h; sy++) {
332 for (sx = 0; sx < w; sx++)
333 if (AT(w, h, grid, sx, sy) == START)
334 break;
335 if (sx < w)
336 break;
337 }
338 assert(sy < h);
339
340 for (pass = 0; pass < 2; pass++) {
341 unsigned char *reachable = (pass == 0 ? sc->reachable_from :
342 sc->reachable_to);
343 int sign = (pass == 0 ? +1 : -1);
344 int dir;
345
346#ifdef SOLVER_DIAGNOSTICS
347 printf("starting pass %d\n", pass);
348#endif
349
350 /*
351 * `head' and `tail' are indices within sc->positions which
352 * track the list of board positions left to process.
353 */
354 head = tail = 0;
355 for (dir = 0; dir < DIRECTIONS; dir++) {
356 int index = (sy*w+sx)*DIRECTIONS+dir;
357 sc->positions[tail++] = index;
358 reachable[index] = TRUE;
359#ifdef SOLVER_DIAGNOSTICS
360 printf("starting point %d,%d,%d\n", sx, sy, dir);
361#endif
362 }
363
364 /*
365 * Now repeatedly pick an element off the list and process
366 * it.
367 */
368 while (head < tail) {
369 int index = sc->positions[head++];
370 int dir = index % DIRECTIONS;
371 int x = (index / DIRECTIONS) % w;
372 int y = index / (w * DIRECTIONS);
373 int n, x2, y2, d2, i2;
374
375#ifdef SOLVER_DIAGNOSTICS
376 printf("processing point %d,%d,%d\n", x, y, dir);
377#endif
378 /*
379 * The places we attempt to switch to here are:
380 * - each possible direction change (all the other
381 * directions in this square)
382 * - one step further in the direction we're going (or
383 * one step back, if we're in the reachable_to pass).
384 */
385 for (n = -1; n < DIRECTIONS; n++) {
386 if (n < 0) {
387 x2 = x + sign * DX(dir);
388 y2 = y + sign * DY(dir);
389 d2 = dir;
390 } else {
391 x2 = x;
392 y2 = y;
393 d2 = n;
394 }
395 i2 = (y2*w+x2)*DIRECTIONS+d2;
396 if (x2 >= 0 && x2 < w &&
397 y2 >= 0 && y2 < h &&
398 !reachable[i2]) {
399 int ok;
400#ifdef SOLVER_DIAGNOSTICS
401 printf(" trying point %d,%d,%d", x2, y2, d2);
402#endif
403 if (pass == 0)
404 ok = can_go(w, h, grid, x, y, dir, x2, y2, d2);
405 else
406 ok = can_go(w, h, grid, x2, y2, d2, x, y, dir);
407#ifdef SOLVER_DIAGNOSTICS
408 printf(" - %sok\n", ok ? "" : "not ");
409#endif
410 if (ok) {
411 sc->positions[tail++] = i2;
412 reachable[i2] = TRUE;
413 }
414 }
415 }
416 }
417 }
418
419 /*
420 * And that should be it. Now all we have to do is find the
421 * squares for which there exists _some_ direction such that
422 * the square plus that direction form a tuple which is both
423 * reachable from the start and reachable to the start.
424 */
425 possgems = 0;
426 for (gy = 0; gy < h; gy++)
427 for (gx = 0; gx < w; gx++)
428 if (AT(w, h, grid, gx, gy) == BLANK) {
429 for (gd = 0; gd < DIRECTIONS; gd++) {
430 int index = (gy*w+gx)*DIRECTIONS+gd;
431 if (sc->reachable_from[index] && sc->reachable_to[index]) {
432#ifdef SOLVER_DIAGNOSTICS
433 printf("space at %d,%d is reachable via"
434 " direction %d\n", gx, gy, gd);
435#endif
436 LV_AT(w, h, grid, gx, gy) = POSSGEM;
437 possgems++;
438 break;
439 }
440 }
441 }
442
443 return possgems;
444}
445
446/* ----------------------------------------------------------------------
447 * Grid generation code.
448 */
449
450static char *gengrid(int w, int h, random_state *rs)
451{
452 int wh = w*h;
453 char *grid = snewn(wh+1, char);
454 struct solver_scratch *sc = new_scratch(w, h);
455 int maxdist_threshold, tries;
456
457 maxdist_threshold = 2;
458 tries = 0;
459
460 while (1) {
461 int i, j;
462 int possgems;
463 int *dist, *list, head, tail, maxdist;
464
465 /*
466 * We're going to fill the grid with the five basic piece
467 * types in about 1/5 proportion. For the moment, though,
468 * we leave out the gems, because we'll put those in
469 * _after_ we run the solver to tell us where the viable
470 * locations are.
471 */
472 i = 0;
473 for (j = 0; j < wh/5; j++)
474 grid[i++] = WALL;
475 for (j = 0; j < wh/5; j++)
476 grid[i++] = STOP;
477 for (j = 0; j < wh/5; j++)
478 grid[i++] = MINE;
479 assert(i < wh);
480 grid[i++] = START;
481 while (i < wh)
482 grid[i++] = BLANK;
483 shuffle(grid, wh, sizeof(*grid), rs);
484
485 /*
486 * Find the viable gem locations, and immediately give up
487 * and try again if there aren't enough of them.
488 */
489 possgems = find_gem_candidates(w, h, grid, sc);
490 if (possgems < wh/5)
491 continue;
492
493 /*
494 * We _could_ now select wh/5 of the POSSGEMs and set them
495 * to GEM, and have a viable level. However, there's a
496 * chance that a large chunk of the level will turn out to
497 * be unreachable, so first we test for that.
498 *
499 * We do this by finding the largest distance from any
500 * square to the nearest POSSGEM, by breadth-first search.
501 * If this is above a critical threshold, we abort and try
502 * again.
503 *
504 * (This search is purely geometric, without regard to
505 * walls and long ways round.)
506 */
507 dist = sc->positions;
508 list = sc->positions + wh;
509 for (i = 0; i < wh; i++)
510 dist[i] = -1;
511 head = tail = 0;
512 for (i = 0; i < wh; i++)
513 if (grid[i] == POSSGEM) {
514 dist[i] = 0;
515 list[tail++] = i;
516 }
517 maxdist = 0;
518 while (head < tail) {
519 int pos, x, y, d;
520
521 pos = list[head++];
522 if (maxdist < dist[pos])
523 maxdist = dist[pos];
524
525 x = pos % w;
526 y = pos / w;
527
528 for (d = 0; d < DIRECTIONS; d++) {
529 int x2, y2, p2;
530
531 x2 = x + DX(d);
532 y2 = y + DY(d);
533
534 if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h) {
535 p2 = y2*w+x2;
536 if (dist[p2] < 0) {
537 dist[p2] = dist[pos] + 1;
538 list[tail++] = p2;
539 }
540 }
541 }
542 }
543 assert(head == wh && tail == wh);
544
545 /*
546 * Now abandon this grid and go round again if maxdist is
547 * above the required threshold.
548 *
549 * We can safely start the threshold as low as 2. As we
550 * accumulate failed generation attempts, we gradually
551 * raise it as we get more desperate.
552 */
553 if (maxdist > maxdist_threshold) {
554 tries++;
555 if (tries == 50) {
556 maxdist_threshold++;
557 tries = 0;
558 }
559 continue;
560 }
561
562 /*
563 * Now our reachable squares are plausibly evenly
564 * distributed over the grid. I'm not actually going to
565 * _enforce_ that I place the gems in such a way as not to
566 * increase that maxdist value; I'm now just going to trust
567 * to the RNG to pick a sensible subset of the POSSGEMs.
568 */
569 j = 0;
570 for (i = 0; i < wh; i++)
571 if (grid[i] == POSSGEM)
572 list[j++] = i;
573 shuffle(list, j, sizeof(*list), rs);
574 for (i = 0; i < j; i++)
575 grid[list[i]] = (i < wh/5 ? GEM : BLANK);
576 break;
577 }
578
579 free_scratch(sc);
580
581 grid[wh] = '\0';
582
583 return grid;
584}
585
586static char *new_game_desc(const game_params *params, random_state *rs,
587 char **aux, int interactive)
588{
589 return gengrid(params->w, params->h, rs);
590}
591
592static char *validate_desc(const game_params *params, const char *desc)
593{
594 int w = params->w, h = params->h, wh = w*h;
595 int starts = 0, gems = 0, i;
596
597 for (i = 0; i < wh; i++) {
598 if (!desc[i])
599 return "Not enough data to fill grid";
600 if (desc[i] != WALL && desc[i] != START && desc[i] != STOP &&
601 desc[i] != GEM && desc[i] != MINE && desc[i] != BLANK)
602 return "Unrecognised character in game description";
603 if (desc[i] == START)
604 starts++;
605 if (desc[i] == GEM)
606 gems++;
607 }
608 if (desc[i])
609 return "Too much data to fill grid";
610 if (starts < 1)
611 return "No starting square specified";
612 if (starts > 1)
613 return "More than one starting square specified";
614 if (gems < 1)
615 return "No gems specified";
616
617 return NULL;
618}
619
620static game_state *new_game(midend *me, const game_params *params,
621 const char *desc)
622{
623 int w = params->w, h = params->h, wh = w*h;
624 int i;
625 game_state *state = snew(game_state);
626
627 state->p = *params; /* structure copy */
628
629 state->grid = snewn(wh, char);
630 assert(strlen(desc) == wh);
631 memcpy(state->grid, desc, wh);
632
633 state->px = state->py = -1;
634 state->gems = 0;
635 for (i = 0; i < wh; i++) {
636 if (state->grid[i] == START) {
637 state->grid[i] = STOP;
638 state->px = i % w;
639 state->py = i / w;
640 } else if (state->grid[i] == GEM) {
641 state->gems++;
642 }
643 }
644
645 assert(state->gems > 0);
646 assert(state->px >= 0 && state->py >= 0);
647
648 state->distance_moved = 0;
649 state->dead = FALSE;
650
651 state->cheated = FALSE;
652 state->solnpos = 0;
653 state->soln = NULL;
654
655 return state;
656}
657
658static game_state *dup_game(const game_state *state)
659{
660 int w = state->p.w, h = state->p.h, wh = w*h;
661 game_state *ret = snew(game_state);
662
663 ret->p = state->p;
664 ret->px = state->px;
665 ret->py = state->py;
666 ret->gems = state->gems;
667 ret->grid = snewn(wh, char);
668 ret->distance_moved = state->distance_moved;
669 ret->dead = FALSE;
670 memcpy(ret->grid, state->grid, wh);
671 ret->cheated = state->cheated;
672 ret->soln = state->soln;
673 if (ret->soln)
674 ret->soln->refcount++;
675 ret->solnpos = state->solnpos;
676
677 return ret;
678}
679
680static void free_game(game_state *state)
681{
682 if (state->soln && --state->soln->refcount == 0) {
683 sfree(state->soln->list);
684 sfree(state->soln);
685 }
686 sfree(state->grid);
687 sfree(state);
688}
689
690/*
691 * Internal function used by solver.
692 */
693static int move_goes_to(int w, int h, char *grid, int x, int y, int d)
694{
695 int dr;
696
697 /*
698 * See where we'd get to if we made this move.
699 */
700 dr = -1; /* placate optimiser */
701 while (1) {
702 if (AT(w, h, grid, x+DX(d), y+DY(d)) == WALL) {
703 dr = DIRECTIONS; /* hit a wall, so end up stationary */
704 break;
705 }
706 x += DX(d);
707 y += DY(d);
708 if (AT(w, h, grid, x, y) == STOP) {
709 dr = DIRECTIONS; /* hit a stop, so end up stationary */
710 break;
711 }
712 if (AT(w, h, grid, x, y) == GEM) {
713 dr = d; /* hit a gem, so we're still moving */
714 break;
715 }
716 if (AT(w, h, grid, x, y) == MINE)
717 return -1; /* hit a mine, so move is invalid */
718 }
719 assert(dr >= 0);
720 return (y*w+x)*DP1+dr;
721}
722
723static int compare_integers(const void *av, const void *bv)
724{
725 const int *a = (const int *)av;
726 const int *b = (const int *)bv;
727 if (*a < *b)
728 return -1;
729 else if (*a > *b)
730 return +1;
731 else
732 return 0;
733}
734
735static char *solve_game(const game_state *state, const game_state *currstate,
736 const char *aux, char **error)
737{
738 int w = currstate->p.w, h = currstate->p.h, wh = w*h;
739 int *nodes, *nodeindex, *edges, *backedges, *edgei, *backedgei, *circuit;
740 int nedges;
741 int *dist, *dist2, *list;
742 int *unvisited;
743 int circuitlen, circuitsize;
744 int head, tail, pass, i, j, n, x, y, d, dd;
745 char *err, *soln, *p;
746
747 /*
748 * Before anything else, deal with the special case in which
749 * all the gems are already collected.
750 */
751 for (i = 0; i < wh; i++)
752 if (currstate->grid[i] == GEM)
753 break;
754 if (i == wh) {
755 *error = "Game is already solved";
756 return NULL;
757 }
758
759 /*
760 * Solving Inertia is a question of first building up the graph
761 * of where you can get to from where, and secondly finding a
762 * tour of the graph which takes in every gem.
763 *
764 * This is of course a close cousin of the travelling salesman
765 * problem, which is NP-complete; so I rather doubt that any
766 * _optimal_ tour can be found in plausible time. Hence I'll
767 * restrict myself to merely finding a not-too-bad one.
768 *
769 * First construct the graph, by bfsing out move by move from
770 * the current player position. Graph vertices will be
771 * - every endpoint of a move (place the ball can be
772 * stationary)
773 * - every gem (place the ball can go through in motion).
774 * Vertices of this type have an associated direction, since
775 * if a gem can be collected by sliding through it in two
776 * different directions it doesn't follow that you can
777 * change direction at it.
778 *
779 * I'm going to refer to a non-directional vertex as
780 * (y*w+x)*DP1+DIRECTIONS, and a directional one as
781 * (y*w+x)*DP1+d.
782 */
783
784 /*
785 * nodeindex[] maps node codes as shown above to numeric
786 * indices in the nodes[] array.
787 */
788 nodeindex = snewn(DP1*wh, int);
789 for (i = 0; i < DP1*wh; i++)
790 nodeindex[i] = -1;
791
792 /*
793 * Do the bfs to find all the interesting graph nodes.
794 */
795 nodes = snewn(DP1*wh, int);
796 head = tail = 0;
797
798 nodes[tail] = (currstate->py * w + currstate->px) * DP1 + DIRECTIONS;
799 nodeindex[nodes[0]] = tail;
800 tail++;
801
802 while (head < tail) {
803 int nc = nodes[head++], nnc;
804
805 d = nc % DP1;
806
807 /*
808 * Plot all possible moves from this node. If the node is
809 * directed, there's only one.
810 */
811 for (dd = 0; dd < DIRECTIONS; dd++) {
812 x = nc / DP1;
813 y = x / w;
814 x %= w;
815
816 if (d < DIRECTIONS && d != dd)
817 continue;
818
819 nnc = move_goes_to(w, h, currstate->grid, x, y, dd);
820 if (nnc >= 0 && nnc != nc) {
821 if (nodeindex[nnc] < 0) {
822 nodes[tail] = nnc;
823 nodeindex[nnc] = tail;
824 tail++;
825 }
826 }
827 }
828 }
829 n = head;
830
831 /*
832 * Now we know how many nodes we have, allocate the edge array
833 * and go through setting up the edges.
834 */
835 edges = snewn(DIRECTIONS*n, int);
836 edgei = snewn(n+1, int);
837 nedges = 0;
838
839 for (i = 0; i < n; i++) {
840 int nc = nodes[i];
841
842 edgei[i] = nedges;
843
844 d = nc % DP1;
845 x = nc / DP1;
846 y = x / w;
847 x %= w;
848
849 for (dd = 0; dd < DIRECTIONS; dd++) {
850 int nnc;
851
852 if (d >= DIRECTIONS || d == dd) {
853 nnc = move_goes_to(w, h, currstate->grid, x, y, dd);
854
855 if (nnc >= 0 && nnc != nc)
856 edges[nedges++] = nodeindex[nnc];
857 }
858 }
859 }
860 edgei[n] = nedges;
861
862 /*
863 * Now set up the backedges array.
864 */
865 backedges = snewn(nedges, int);
866 backedgei = snewn(n+1, int);
867 for (i = j = 0; i < nedges; i++) {
868 while (j+1 < n && i >= edgei[j+1])
869 j++;
870 backedges[i] = edges[i] * n + j;
871 }
872 qsort(backedges, nedges, sizeof(int), compare_integers);
873 backedgei[0] = 0;
874 for (i = j = 0; i < nedges; i++) {
875 int k = backedges[i] / n;
876 backedges[i] %= n;
877 while (j < k)
878 backedgei[++j] = i;
879 }
880 backedgei[n] = nedges;
881
882 /*
883 * Set up the initial tour. At all times, our tour is a circuit
884 * of graph vertices (which may, and probably will often,
885 * repeat vertices). To begin with, it's got exactly one vertex
886 * in it, which is the player's current starting point.
887 */
888 circuitsize = 256;
889 circuit = snewn(circuitsize, int);
890 circuitlen = 0;
891 circuit[circuitlen++] = 0; /* node index 0 is the starting posn */
892
893 /*
894 * Track which gems are as yet unvisited.
895 */
896 unvisited = snewn(wh, int);
897 for (i = 0; i < wh; i++)
898 unvisited[i] = FALSE;
899 for (i = 0; i < wh; i++)
900 if (currstate->grid[i] == GEM)
901 unvisited[i] = TRUE;
902
903 /*
904 * Allocate space for doing bfses inside the main loop.
905 */
906 dist = snewn(n, int);
907 dist2 = snewn(n, int);
908 list = snewn(n, int);
909
910 err = NULL;
911 soln = NULL;
912
913 /*
914 * Now enter the main loop, in each iteration of which we
915 * extend the tour to take in an as yet uncollected gem.
916 */
917 while (1) {
918 int target, n1, n2, bestdist, extralen, targetpos;
919
920#ifdef TSP_DIAGNOSTICS
921 printf("circuit is");
922 for (i = 0; i < circuitlen; i++) {
923 int nc = nodes[circuit[i]];
924 printf(" (%d,%d,%d)", nc/DP1%w, nc/(DP1*w), nc%DP1);
925 }
926 printf("\n");
927 printf("moves are ");
928 x = nodes[circuit[0]] / DP1 % w;
929 y = nodes[circuit[0]] / DP1 / w;
930 for (i = 1; i < circuitlen; i++) {
931 int x2, y2, dx, dy;
932 if (nodes[circuit[i]] % DP1 != DIRECTIONS)
933 continue;
934 x2 = nodes[circuit[i]] / DP1 % w;
935 y2 = nodes[circuit[i]] / DP1 / w;
936 dx = (x2 > x ? +1 : x2 < x ? -1 : 0);
937 dy = (y2 > y ? +1 : y2 < y ? -1 : 0);
938 for (d = 0; d < DIRECTIONS; d++)
939 if (DX(d) == dx && DY(d) == dy)
940 printf("%c", "89632147"[d]);
941 x = x2;
942 y = y2;
943 }
944 printf("\n");
945#endif
946
947 /*
948 * First, start a pair of bfses at _every_ vertex currently
949 * in the tour, and extend them outwards to find the
950 * nearest as yet unreached gem vertex.
951 *
952 * This is largely a heuristic: we could pick _any_ doubly
953 * reachable node here and still get a valid tour as
954 * output. I hope that picking a nearby one will result in
955 * generally good tours.
956 */
957 for (pass = 0; pass < 2; pass++) {
958 int *ep = (pass == 0 ? edges : backedges);
959 int *ei = (pass == 0 ? edgei : backedgei);
960 int *dp = (pass == 0 ? dist : dist2);
961 head = tail = 0;
962 for (i = 0; i < n; i++)
963 dp[i] = -1;
964 for (i = 0; i < circuitlen; i++) {
965 int ni = circuit[i];
966 if (dp[ni] < 0) {
967 dp[ni] = 0;
968 list[tail++] = ni;
969 }
970 }
971 while (head < tail) {
972 int ni = list[head++];
973 for (i = ei[ni]; i < ei[ni+1]; i++) {
974 int ti = ep[i];
975 if (ti >= 0 && dp[ti] < 0) {
976 dp[ti] = dp[ni] + 1;
977 list[tail++] = ti;
978 }
979 }
980 }
981 }
982 /* Now find the nearest unvisited gem. */
983 bestdist = -1;
984 target = -1;
985 for (i = 0; i < n; i++) {
986 if (unvisited[nodes[i] / DP1] &&
987 dist[i] >= 0 && dist2[i] >= 0) {
988 int thisdist = dist[i] + dist2[i];
989 if (bestdist < 0 || bestdist > thisdist) {
990 bestdist = thisdist;
991 target = i;
992 }
993 }
994 }
995
996 if (target < 0) {
997 /*
998 * If we get to here, we haven't found a gem we can get
999 * at all, which means we terminate this loop.
1000 */
1001 break;
1002 }
1003
1004 /*
1005 * Now we have a graph vertex at list[tail-1] which is an
1006 * unvisited gem. We want to add that vertex to our tour.
1007 * So we run two more breadth-first searches: one starting
1008 * from that vertex and following forward edges, and
1009 * another starting from the same vertex and following
1010 * backward edges. This allows us to determine, for each
1011 * node on the current tour, how quickly we can get both to
1012 * and from the target vertex from that node.
1013 */
1014#ifdef TSP_DIAGNOSTICS
1015 printf("target node is %d (%d,%d,%d)\n", target, nodes[target]/DP1%w,
1016 nodes[target]/DP1/w, nodes[target]%DP1);
1017#endif
1018
1019 for (pass = 0; pass < 2; pass++) {
1020 int *ep = (pass == 0 ? edges : backedges);
1021 int *ei = (pass == 0 ? edgei : backedgei);
1022 int *dp = (pass == 0 ? dist : dist2);
1023
1024 for (i = 0; i < n; i++)
1025 dp[i] = -1;
1026 head = tail = 0;
1027
1028 dp[target] = 0;
1029 list[tail++] = target;
1030
1031 while (head < tail) {
1032 int ni = list[head++];
1033 for (i = ei[ni]; i < ei[ni+1]; i++) {
1034 int ti = ep[i];
1035 if (ti >= 0 && dp[ti] < 0) {
1036 dp[ti] = dp[ni] + 1;
1037/*printf("pass %d: set dist of vertex %d to %d (via %d)\n", pass, ti, dp[ti], ni);*/
1038 list[tail++] = ti;
1039 }
1040 }
1041 }
1042 }
1043
1044 /*
1045 * Now for every node n, dist[n] gives the length of the
1046 * shortest path from the target vertex to n, and dist2[n]
1047 * gives the length of the shortest path from n to the
1048 * target vertex.
1049 *
1050 * Our next step is to search linearly along the tour to
1051 * find the optimum place to insert a trip to the target
1052 * vertex and back. Our two options are either
1053 * (a) to find two adjacent vertices A,B in the tour and
1054 * replace the edge A->B with the path A->target->B
1055 * (b) to find a single vertex X in the tour and replace
1056 * it with the complete round trip X->target->X.
1057 * We do whichever takes the fewest moves.
1058 */
1059 n1 = n2 = -1;
1060 bestdist = -1;
1061 for (i = 0; i < circuitlen; i++) {
1062 int thisdist;
1063
1064 /*
1065 * Try a round trip from vertex i.
1066 */
1067 if (dist[circuit[i]] >= 0 &&
1068 dist2[circuit[i]] >= 0) {
1069 thisdist = dist[circuit[i]] + dist2[circuit[i]];
1070 if (bestdist < 0 || thisdist < bestdist) {
1071 bestdist = thisdist;
1072 n1 = n2 = i;
1073 }
1074 }
1075
1076 /*
1077 * Try a trip from vertex i via target to vertex i+1.
1078 */
1079 if (i+1 < circuitlen &&
1080 dist2[circuit[i]] >= 0 &&
1081 dist[circuit[i+1]] >= 0) {
1082 thisdist = dist2[circuit[i]] + dist[circuit[i+1]];
1083 if (bestdist < 0 || thisdist < bestdist) {
1084 bestdist = thisdist;
1085 n1 = i;
1086 n2 = i+1;
1087 }
1088 }
1089 }
1090 if (bestdist < 0) {
1091 /*
1092 * We couldn't find a round trip taking in this gem _at
1093 * all_. Give up.
1094 */
1095 err = "Unable to find a solution from this starting point";
1096 break;
1097 }
1098#ifdef TSP_DIAGNOSTICS
1099 printf("insertion point: n1=%d, n2=%d, dist=%d\n", n1, n2, bestdist);
1100#endif
1101
1102#ifdef TSP_DIAGNOSTICS
1103 printf("circuit before lengthening is");
1104 for (i = 0; i < circuitlen; i++) {
1105 printf(" %d", circuit[i]);
1106 }
1107 printf("\n");
1108#endif
1109
1110 /*
1111 * Now actually lengthen the tour to take in this round
1112 * trip.
1113 */
1114 extralen = dist2[circuit[n1]] + dist[circuit[n2]];
1115 if (n1 != n2)
1116 extralen--;
1117 circuitlen += extralen;
1118 if (circuitlen >= circuitsize) {
1119 circuitsize = circuitlen + 256;
1120 circuit = sresize(circuit, circuitsize, int);
1121 }
1122 memmove(circuit + n2 + extralen, circuit + n2,
1123 (circuitlen - n2 - extralen) * sizeof(int));
1124 n2 += extralen;
1125
1126#ifdef TSP_DIAGNOSTICS
1127 printf("circuit in middle of lengthening is");
1128 for (i = 0; i < circuitlen; i++) {
1129 printf(" %d", circuit[i]);
1130 }
1131 printf("\n");
1132#endif
1133
1134 /*
1135 * Find the shortest-path routes to and from the target,
1136 * and write them into the circuit.
1137 */
1138 targetpos = n1 + dist2[circuit[n1]];
1139 assert(targetpos - dist2[circuit[n1]] == n1);
1140 assert(targetpos + dist[circuit[n2]] == n2);
1141 for (pass = 0; pass < 2; pass++) {
1142 int dir = (pass == 0 ? -1 : +1);
1143 int *ep = (pass == 0 ? backedges : edges);
1144 int *ei = (pass == 0 ? backedgei : edgei);
1145 int *dp = (pass == 0 ? dist : dist2);
1146 int nn = (pass == 0 ? n2 : n1);
1147 int ni = circuit[nn], ti, dest = nn;
1148
1149 while (1) {
1150 circuit[dest] = ni;
1151 if (dp[ni] == 0)
1152 break;
1153 dest += dir;
1154 ti = -1;
1155/*printf("pass %d: looking at vertex %d\n", pass, ni);*/
1156 for (i = ei[ni]; i < ei[ni+1]; i++) {
1157 ti = ep[i];
1158 if (ti >= 0 && dp[ti] == dp[ni] - 1)
1159 break;
1160 }
1161 assert(i < ei[ni+1] && ti >= 0);
1162 ni = ti;
1163 }
1164 }
1165
1166#ifdef TSP_DIAGNOSTICS
1167 printf("circuit after lengthening is");
1168 for (i = 0; i < circuitlen; i++) {
1169 printf(" %d", circuit[i]);
1170 }
1171 printf("\n");
1172#endif
1173
1174 /*
1175 * Finally, mark all gems that the new piece of circuit
1176 * passes through as visited.
1177 */
1178 for (i = n1; i <= n2; i++) {
1179 int pos = nodes[circuit[i]] / DP1;
1180 assert(pos >= 0 && pos < wh);
1181 unvisited[pos] = FALSE;
1182 }
1183 }
1184
1185#ifdef TSP_DIAGNOSTICS
1186 printf("before reduction, moves are ");
1187 x = nodes[circuit[0]] / DP1 % w;
1188 y = nodes[circuit[0]] / DP1 / w;
1189 for (i = 1; i < circuitlen; i++) {
1190 int x2, y2, dx, dy;
1191 if (nodes[circuit[i]] % DP1 != DIRECTIONS)
1192 continue;
1193 x2 = nodes[circuit[i]] / DP1 % w;
1194 y2 = nodes[circuit[i]] / DP1 / w;
1195 dx = (x2 > x ? +1 : x2 < x ? -1 : 0);
1196 dy = (y2 > y ? +1 : y2 < y ? -1 : 0);
1197 for (d = 0; d < DIRECTIONS; d++)
1198 if (DX(d) == dx && DY(d) == dy)
1199 printf("%c", "89632147"[d]);
1200 x = x2;
1201 y = y2;
1202 }
1203 printf("\n");
1204#endif
1205
1206 /*
1207 * That's got a basic solution. Now optimise it by removing
1208 * redundant sections of the circuit: it's entirely possible
1209 * that a piece of circuit we carefully inserted at one stage
1210 * to collect a gem has become pointless because the steps
1211 * required to collect some _later_ gem necessarily passed
1212 * through the same one.
1213 *
1214 * So first we go through and work out how many times each gem
1215 * is collected. Then we look for maximal sections of circuit
1216 * which are redundant in the sense that their removal would
1217 * not reduce any gem's collection count to zero, and replace
1218 * each one with a bfs-derived fastest path between their
1219 * endpoints.
1220 */
1221 while (1) {
1222 int oldlen = circuitlen;
1223 int dir;
1224
1225 for (dir = +1; dir >= -1; dir -= 2) {
1226
1227 for (i = 0; i < wh; i++)
1228 unvisited[i] = 0;
1229 for (i = 0; i < circuitlen; i++) {
1230 int xy = nodes[circuit[i]] / DP1;
1231 if (currstate->grid[xy] == GEM)
1232 unvisited[xy]++;
1233 }
1234
1235 /*
1236 * If there's any gem we didn't end up visiting at all,
1237 * give up.
1238 */
1239 for (i = 0; i < wh; i++) {
1240 if (currstate->grid[i] == GEM && unvisited[i] == 0) {
1241 err = "Unable to find a solution from this starting point";
1242 break;
1243 }
1244 }
1245 if (i < wh)
1246 break;
1247
1248 for (i = j = (dir > 0 ? 0 : circuitlen-1);
1249 i < circuitlen && i >= 0;
1250 i += dir) {
1251 int xy = nodes[circuit[i]] / DP1;
1252 if (currstate->grid[xy] == GEM && unvisited[xy] > 1) {
1253 unvisited[xy]--;
1254 } else if (currstate->grid[xy] == GEM || i == circuitlen-1) {
1255 /*
1256 * circuit[i] collects a gem for the only time,
1257 * or is the last node in the circuit.
1258 * Therefore it cannot be removed; so we now
1259 * want to replace the path from circuit[j] to
1260 * circuit[i] with a bfs-shortest path.
1261 */
1262 int p, q, k, dest, ni, ti, thisdist;
1263
1264 /*
1265 * Set up the upper and lower bounds of the
1266 * reduced section.
1267 */
1268 p = min(i, j);
1269 q = max(i, j);
1270
1271#ifdef TSP_DIAGNOSTICS
1272 printf("optimising section from %d - %d\n", p, q);
1273#endif
1274
1275 for (k = 0; k < n; k++)
1276 dist[k] = -1;
1277 head = tail = 0;
1278
1279 dist[circuit[p]] = 0;
1280 list[tail++] = circuit[p];
1281
1282 while (head < tail && dist[circuit[q]] < 0) {
1283 int ni = list[head++];
1284 for (k = edgei[ni]; k < edgei[ni+1]; k++) {
1285 int ti = edges[k];
1286 if (ti >= 0 && dist[ti] < 0) {
1287 dist[ti] = dist[ni] + 1;
1288 list[tail++] = ti;
1289 }
1290 }
1291 }
1292
1293 thisdist = dist[circuit[q]];
1294 assert(thisdist >= 0 && thisdist <= q-p);
1295
1296 memmove(circuit+p+thisdist, circuit+q,
1297 (circuitlen - q) * sizeof(int));
1298 circuitlen -= q-p;
1299 q = p + thisdist;
1300 circuitlen += q-p;
1301
1302 if (dir > 0)
1303 i = q; /* resume loop from the right place */
1304
1305#ifdef TSP_DIAGNOSTICS
1306 printf("new section runs from %d - %d\n", p, q);
1307#endif
1308
1309 dest = q;
1310 assert(dest >= 0);
1311 ni = circuit[q];
1312
1313 while (1) {
1314 /* printf("dest=%d circuitlen=%d ni=%d dist[ni]=%d\n", dest, circuitlen, ni, dist[ni]); */
1315 circuit[dest] = ni;
1316 if (dist[ni] == 0)
1317 break;
1318 dest--;
1319 ti = -1;
1320 for (k = backedgei[ni]; k < backedgei[ni+1]; k++) {
1321 ti = backedges[k];
1322 if (ti >= 0 && dist[ti] == dist[ni] - 1)
1323 break;
1324 }
1325 assert(k < backedgei[ni+1] && ti >= 0);
1326 ni = ti;
1327 }
1328
1329 /*
1330 * Now re-increment the visit counts for the
1331 * new path.
1332 */
1333 while (++p < q) {
1334 int xy = nodes[circuit[p]] / DP1;
1335 if (currstate->grid[xy] == GEM)
1336 unvisited[xy]++;
1337 }
1338
1339 j = i;
1340
1341#ifdef TSP_DIAGNOSTICS
1342 printf("during reduction, circuit is");
1343 for (k = 0; k < circuitlen; k++) {
1344 int nc = nodes[circuit[k]];
1345 printf(" (%d,%d,%d)", nc/DP1%w, nc/(DP1*w), nc%DP1);
1346 }
1347 printf("\n");
1348 printf("moves are ");
1349 x = nodes[circuit[0]] / DP1 % w;
1350 y = nodes[circuit[0]] / DP1 / w;
1351 for (k = 1; k < circuitlen; k++) {
1352 int x2, y2, dx, dy;
1353 if (nodes[circuit[k]] % DP1 != DIRECTIONS)
1354 continue;
1355 x2 = nodes[circuit[k]] / DP1 % w;
1356 y2 = nodes[circuit[k]] / DP1 / w;
1357 dx = (x2 > x ? +1 : x2 < x ? -1 : 0);
1358 dy = (y2 > y ? +1 : y2 < y ? -1 : 0);
1359 for (d = 0; d < DIRECTIONS; d++)
1360 if (DX(d) == dx && DY(d) == dy)
1361 printf("%c", "89632147"[d]);
1362 x = x2;
1363 y = y2;
1364 }
1365 printf("\n");
1366#endif
1367 }
1368 }
1369
1370#ifdef TSP_DIAGNOSTICS
1371 printf("after reduction, moves are ");
1372 x = nodes[circuit[0]] / DP1 % w;
1373 y = nodes[circuit[0]] / DP1 / w;
1374 for (i = 1; i < circuitlen; i++) {
1375 int x2, y2, dx, dy;
1376 if (nodes[circuit[i]] % DP1 != DIRECTIONS)
1377 continue;
1378 x2 = nodes[circuit[i]] / DP1 % w;
1379 y2 = nodes[circuit[i]] / DP1 / w;
1380 dx = (x2 > x ? +1 : x2 < x ? -1 : 0);
1381 dy = (y2 > y ? +1 : y2 < y ? -1 : 0);
1382 for (d = 0; d < DIRECTIONS; d++)
1383 if (DX(d) == dx && DY(d) == dy)
1384 printf("%c", "89632147"[d]);
1385 x = x2;
1386 y = y2;
1387 }
1388 printf("\n");
1389#endif
1390 }
1391
1392 /*
1393 * If we've managed an entire reduction pass in each
1394 * direction and not made the solution any shorter, we're
1395 * _really_ done.
1396 */
1397 if (circuitlen == oldlen)
1398 break;
1399 }
1400
1401 /*
1402 * Encode the solution as a move string.
1403 */
1404 if (!err) {
1405 soln = snewn(circuitlen+2, char);
1406 p = soln;
1407 *p++ = 'S';
1408 x = nodes[circuit[0]] / DP1 % w;
1409 y = nodes[circuit[0]] / DP1 / w;
1410 for (i = 1; i < circuitlen; i++) {
1411 int x2, y2, dx, dy;
1412 if (nodes[circuit[i]] % DP1 != DIRECTIONS)
1413 continue;
1414 x2 = nodes[circuit[i]] / DP1 % w;
1415 y2 = nodes[circuit[i]] / DP1 / w;
1416 dx = (x2 > x ? +1 : x2 < x ? -1 : 0);
1417 dy = (y2 > y ? +1 : y2 < y ? -1 : 0);
1418 for (d = 0; d < DIRECTIONS; d++)
1419 if (DX(d) == dx && DY(d) == dy) {
1420 *p++ = '0' + d;
1421 break;
1422 }
1423 assert(d < DIRECTIONS);
1424 x = x2;
1425 y = y2;
1426 }
1427 *p++ = '\0';
1428 assert(p - soln < circuitlen+2);
1429 }
1430
1431 sfree(list);
1432 sfree(dist);
1433 sfree(dist2);
1434 sfree(unvisited);
1435 sfree(circuit);
1436 sfree(backedgei);
1437 sfree(backedges);
1438 sfree(edgei);
1439 sfree(edges);
1440 sfree(nodeindex);
1441 sfree(nodes);
1442
1443 if (err)
1444 *error = err;
1445
1446 return soln;
1447}
1448
1449static int game_can_format_as_text_now(const game_params *params)
1450{
1451 return TRUE;
1452}
1453
1454static char *game_text_format(const game_state *state)
1455{
1456 int w = state->p.w, h = state->p.h, r, c;
1457 int cw = 4, ch = 2, gw = cw*w + 2, gh = ch * h + 1, len = gw * gh;
1458 char *board = snewn(len + 1, char);
1459
1460 sprintf(board, "%*s+\n", len - 2, "");
1461
1462 for (r = 0; r < h; ++r) {
1463 for (c = 0; c < w; ++c) {
1464 int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2;
1465 int i = r*w + c;
1466 switch (state->grid[i]) {
1467 case BLANK: break;
1468 case GEM: board[center] = 'o'; break;
1469 case MINE: board[center] = 'M'; break;
1470 case STOP: board[center-1] = '('; board[center+1] = ')'; break;
1471 case WALL: memset(board + center - 1, 'X', 3);
1472 }
1473
1474 if (r == state->py && c == state->px) {
1475 if (!state->dead) board[center] = '@';
1476 else memcpy(board + center - 1, ":-(", 3);
1477 }
1478 board[cell] = '+';
1479 memset(board + cell + 1, '-', cw - 1);
1480 for (i = 1; i < ch; ++i) board[cell + i*gw] = '|';
1481 }
1482 for (c = 0; c < ch; ++c) {
1483 board[(r*ch+c)*gw + gw - 2] = "|+"[!c];
1484 board[(r*ch+c)*gw + gw - 1] = '\n';
1485 }
1486 }
1487 memset(board + len - gw, '-', gw - 2);
1488 for (c = 0; c < w; ++c) board[len - gw + cw*c] = '+';
1489
1490 return board;
1491}
1492
1493struct game_ui {
1494 float anim_length;
1495 int flashtype;
1496 int deaths;
1497 int just_made_move;
1498 int just_died;
1499};
1500
1501static game_ui *new_ui(const game_state *state)
1502{
1503 game_ui *ui = snew(game_ui);
1504 ui->anim_length = 0.0F;
1505 ui->flashtype = 0;
1506 ui->deaths = 0;
1507 ui->just_made_move = FALSE;
1508 ui->just_died = FALSE;
1509 return ui;
1510}
1511
1512static void free_ui(game_ui *ui)
1513{
1514 sfree(ui);
1515}
1516
1517static char *encode_ui(const game_ui *ui)
1518{
1519 char buf[80];
1520 /*
1521 * The deaths counter needs preserving across a serialisation.
1522 */
1523 sprintf(buf, "D%d", ui->deaths);
1524 return dupstr(buf);
1525}
1526
1527static void decode_ui(game_ui *ui, const char *encoding)
1528{
1529 int p = 0;
1530 sscanf(encoding, "D%d%n", &ui->deaths, &p);
1531}
1532
1533static void game_changed_state(game_ui *ui, const game_state *oldstate,
1534 const game_state *newstate)
1535{
1536 /*
1537 * Increment the deaths counter. We only do this if
1538 * ui->just_made_move is set (redoing a suicide move doesn't
1539 * kill you _again_), and also we only do it if the game wasn't
1540 * already completed (once you're finished, you can play).
1541 */
1542 if (!oldstate->dead && newstate->dead && ui->just_made_move &&
1543 oldstate->gems) {
1544 ui->deaths++;
1545 ui->just_died = TRUE;
1546 } else {
1547 ui->just_died = FALSE;
1548 }
1549 ui->just_made_move = FALSE;
1550}
1551
1552struct game_drawstate {
1553 game_params p;
1554 int tilesize;
1555 int started;
1556 unsigned short *grid;
1557 blitter *player_background;
1558 int player_bg_saved, pbgx, pbgy;
1559};
1560
1561#define PREFERRED_TILESIZE 32
1562#define TILESIZE (ds->tilesize)
1563#ifdef SMALL_SCREEN
1564#define BORDER (TILESIZE / 4)
1565#else
1566#define BORDER (TILESIZE)
1567#endif
1568#define HIGHLIGHT_WIDTH (TILESIZE / 10)
1569#define COORD(x) ( (x) * TILESIZE + BORDER )
1570#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1571
1572static char *interpret_move(const game_state *state, game_ui *ui,
1573 const game_drawstate *ds,
1574 int x, int y, int button)
1575{
1576 int w = state->p.w, h = state->p.h /*, wh = w*h */;
1577 int dir;
1578 char buf[80];
1579
1580 dir = -1;
1581
1582 if (button == LEFT_BUTTON) {
1583 /*
1584 * Mouse-clicking near the target point (or, more
1585 * accurately, in the appropriate octant) is an alternative
1586 * way to input moves.
1587 */
1588
1589 if (FROMCOORD(x) != state->px || FROMCOORD(y) != state->py) {
1590 int dx, dy;
1591 float angle;
1592
1593 dx = FROMCOORD(x) - state->px;
1594 dy = FROMCOORD(y) - state->py;
1595 /* I pass dx,dy rather than dy,dx so that the octants
1596 * end up the right way round. */
1597 angle = atan2(dx, -dy);
1598
1599 angle = (angle + (PI/8)) / (PI/4);
1600 assert(angle > -16.0F);
1601 dir = (int)(angle + 16.0F) & 7;
1602 }
1603 } else if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
1604 dir = 0;
1605 else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
1606 dir = 4;
1607 else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
1608 dir = 6;
1609 else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
1610 dir = 2;
1611 else if (button == (MOD_NUM_KEYPAD | '7'))
1612 dir = 7;
1613 else if (button == (MOD_NUM_KEYPAD | '1'))
1614 dir = 5;
1615 else if (button == (MOD_NUM_KEYPAD | '9'))
1616 dir = 1;
1617 else if (button == (MOD_NUM_KEYPAD | '3'))
1618 dir = 3;
1619 else if (IS_CURSOR_SELECT(button) &&
1620 state->soln && state->solnpos < state->soln->len)
1621 dir = state->soln->list[state->solnpos];
1622
1623 if (dir < 0)
1624 return NULL;
1625
1626 /*
1627 * Reject the move if we can't make it at all due to a wall
1628 * being in the way.
1629 */
1630 if (AT(w, h, state->grid, state->px+DX(dir), state->py+DY(dir)) == WALL)
1631 return NULL;
1632
1633 /*
1634 * Reject the move if we're dead!
1635 */
1636 if (state->dead)
1637 return NULL;
1638
1639 /*
1640 * Otherwise, we can make the move. All we need to specify is
1641 * the direction.
1642 */
1643 ui->just_made_move = TRUE;
1644 sprintf(buf, "%d", dir);
1645 return dupstr(buf);
1646}
1647
1648static void install_new_solution(game_state *ret, const char *move)
1649{
1650 int i;
1651 soln *sol;
1652 assert (*move == 'S');
1653 ++move;
1654
1655 sol = snew(soln);
1656 sol->len = strlen(move);
1657 sol->list = snewn(sol->len, unsigned char);
1658 for (i = 0; i < sol->len; ++i) sol->list[i] = move[i] - '0';
1659
1660 if (ret->soln && --ret->soln->refcount == 0) {
1661 sfree(ret->soln->list);
1662 sfree(ret->soln);
1663 }
1664
1665 ret->soln = sol;
1666 sol->refcount = 1;
1667
1668 ret->cheated = TRUE;
1669 ret->solnpos = 0;
1670}
1671
1672static void discard_solution(game_state *ret)
1673{
1674 --ret->soln->refcount;
1675 assert(ret->soln->refcount > 0); /* ret has a soln-pointing dup */
1676 ret->soln = NULL;
1677 ret->solnpos = 0;
1678}
1679
1680static game_state *execute_move(const game_state *state, const char *move)
1681{
1682 int w = state->p.w, h = state->p.h /*, wh = w*h */;
1683 int dir;
1684 game_state *ret;
1685
1686 if (*move == 'S') {
1687 /*
1688 * This is a solve move, so we don't actually _change_ the
1689 * grid but merely set up a stored solution path.
1690 */
1691 ret = dup_game(state);
1692 install_new_solution(ret, move);
1693 return ret;
1694 }
1695
1696 dir = atoi(move);
1697 if (dir < 0 || dir >= DIRECTIONS)
1698 return NULL; /* huh? */
1699
1700 if (state->dead)
1701 return NULL;
1702
1703 if (AT(w, h, state->grid, state->px+DX(dir), state->py+DY(dir)) == WALL)
1704 return NULL; /* wall in the way! */
1705
1706 /*
1707 * Now make the move.
1708 */
1709 ret = dup_game(state);
1710 ret->distance_moved = 0;
1711 while (1) {
1712 ret->px += DX(dir);
1713 ret->py += DY(dir);
1714 ret->distance_moved++;
1715
1716 if (AT(w, h, ret->grid, ret->px, ret->py) == GEM) {
1717 LV_AT(w, h, ret->grid, ret->px, ret->py) = BLANK;
1718 ret->gems--;
1719 }
1720
1721 if (AT(w, h, ret->grid, ret->px, ret->py) == MINE) {
1722 ret->dead = TRUE;
1723 break;
1724 }
1725
1726 if (AT(w, h, ret->grid, ret->px, ret->py) == STOP ||
1727 AT(w, h, ret->grid, ret->px+DX(dir),
1728 ret->py+DY(dir)) == WALL)
1729 break;
1730 }
1731
1732 if (ret->soln) {
1733 if (ret->dead || ret->gems == 0)
1734 discard_solution(ret);
1735 else if (ret->soln->list[ret->solnpos] == dir) {
1736 ++ret->solnpos;
1737 assert(ret->solnpos < ret->soln->len); /* or gems == 0 */
1738 assert(!ret->dead); /* or not a solution */
1739 } else {
1740 char *error = NULL, *soln = solve_game(NULL, ret, NULL, &error);
1741 if (!error) {
1742 install_new_solution(ret, soln);
1743 sfree(soln);
1744 } else discard_solution(ret);
1745 }
1746 }
1747
1748 return ret;
1749}
1750
1751/* ----------------------------------------------------------------------
1752 * Drawing routines.
1753 */
1754
1755static void game_compute_size(const game_params *params, int tilesize,
1756 int *x, int *y)
1757{
1758 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1759 struct { int tilesize; } ads, *ds = &ads;
1760 ads.tilesize = tilesize;
1761
1762 *x = 2 * BORDER + 1 + params->w * TILESIZE;
1763 *y = 2 * BORDER + 1 + params->h * TILESIZE;
1764}
1765
1766static void game_set_size(drawing *dr, game_drawstate *ds,
1767 const game_params *params, int tilesize)
1768{
1769 ds->tilesize = tilesize;
1770
1771 assert(!ds->player_background); /* set_size is never called twice */
1772 assert(!ds->player_bg_saved);
1773
1774 ds->player_background = blitter_new(dr, TILESIZE, TILESIZE);
1775}
1776
1777static float *game_colours(frontend *fe, int *ncolours)
1778{
1779 float *ret = snewn(3 * NCOLOURS, float);
1780 int i;
1781
1782 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1783
1784 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1785 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1786 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1787
1788 ret[COL_PLAYER * 3 + 0] = 0.0F;
1789 ret[COL_PLAYER * 3 + 1] = 1.0F;
1790 ret[COL_PLAYER * 3 + 2] = 0.0F;
1791
1792 ret[COL_DEAD_PLAYER * 3 + 0] = 1.0F;
1793 ret[COL_DEAD_PLAYER * 3 + 1] = 0.0F;
1794 ret[COL_DEAD_PLAYER * 3 + 2] = 0.0F;
1795
1796 ret[COL_MINE * 3 + 0] = 0.0F;
1797 ret[COL_MINE * 3 + 1] = 0.0F;
1798 ret[COL_MINE * 3 + 2] = 0.0F;
1799
1800 ret[COL_GEM * 3 + 0] = 0.6F;
1801 ret[COL_GEM * 3 + 1] = 1.0F;
1802 ret[COL_GEM * 3 + 2] = 1.0F;
1803
1804 for (i = 0; i < 3; i++) {
1805 ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] +
1806 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4;
1807 }
1808
1809 ret[COL_HINT * 3 + 0] = 1.0F;
1810 ret[COL_HINT * 3 + 1] = 1.0F;
1811 ret[COL_HINT * 3 + 2] = 0.0F;
1812
1813 *ncolours = NCOLOURS;
1814 return ret;
1815}
1816
1817static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1818{
1819 int w = state->p.w, h = state->p.h, wh = w*h;
1820 struct game_drawstate *ds = snew(struct game_drawstate);
1821 int i;
1822
1823 ds->tilesize = 0;
1824
1825 /* We can't allocate the blitter rectangle for the player background
1826 * until we know what size to make it. */
1827 ds->player_background = NULL;
1828 ds->player_bg_saved = FALSE;
1829 ds->pbgx = ds->pbgy = -1;
1830
1831 ds->p = state->p; /* structure copy */
1832 ds->started = FALSE;
1833 ds->grid = snewn(wh, unsigned short);
1834 for (i = 0; i < wh; i++)
1835 ds->grid[i] = UNDRAWN;
1836
1837 return ds;
1838}
1839
1840static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1841{
1842 if (ds->player_background)
1843 blitter_free(dr, ds->player_background);
1844 sfree(ds->grid);
1845 sfree(ds);
1846}
1847
1848static void draw_player(drawing *dr, game_drawstate *ds, int x, int y,
1849 int dead, int hintdir)
1850{
1851 if (dead) {
1852 int coords[DIRECTIONS*4];
1853 int d;
1854
1855 for (d = 0; d < DIRECTIONS; d++) {
1856 float x1, y1, x2, y2, x3, y3, len;
1857
1858 x1 = DX(d);
1859 y1 = DY(d);
1860 len = sqrt(x1*x1+y1*y1); x1 /= len; y1 /= len;
1861
1862 x3 = DX(d+1);
1863 y3 = DY(d+1);
1864 len = sqrt(x3*x3+y3*y3); x3 /= len; y3 /= len;
1865
1866 x2 = (x1+x3) / 4;
1867 y2 = (y1+y3) / 4;
1868
1869 coords[d*4+0] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x1);
1870 coords[d*4+1] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y1);
1871 coords[d*4+2] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x2);
1872 coords[d*4+3] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y2);
1873 }
1874 draw_polygon(dr, coords, DIRECTIONS*2, COL_DEAD_PLAYER, COL_OUTLINE);
1875 } else {
1876 draw_circle(dr, x + TILESIZE/2, y + TILESIZE/2,
1877 TILESIZE/3, COL_PLAYER, COL_OUTLINE);
1878 }
1879
1880 if (!dead && hintdir >= 0) {
1881 float scale = (DX(hintdir) && DY(hintdir) ? 0.8F : 1.0F);
1882 int ax = (TILESIZE*2/5) * scale * DX(hintdir);
1883 int ay = (TILESIZE*2/5) * scale * DY(hintdir);
1884 int px = -ay, py = ax;
1885 int ox = x + TILESIZE/2, oy = y + TILESIZE/2;
1886 int coords[14], *c;
1887
1888 c = coords;
1889 *c++ = ox + px/9;
1890 *c++ = oy + py/9;
1891 *c++ = ox + px/9 + ax*2/3;
1892 *c++ = oy + py/9 + ay*2/3;
1893 *c++ = ox + px/3 + ax*2/3;
1894 *c++ = oy + py/3 + ay*2/3;
1895 *c++ = ox + ax;
1896 *c++ = oy + ay;
1897 *c++ = ox - px/3 + ax*2/3;
1898 *c++ = oy - py/3 + ay*2/3;
1899 *c++ = ox - px/9 + ax*2/3;
1900 *c++ = oy - py/9 + ay*2/3;
1901 *c++ = ox - px/9;
1902 *c++ = oy - py/9;
1903 draw_polygon(dr, coords, 7, COL_HINT, COL_OUTLINE);
1904 }
1905
1906 draw_update(dr, x, y, TILESIZE, TILESIZE);
1907}
1908
1909#define FLASH_DEAD 0x100
1910#define FLASH_WIN 0x200
1911#define FLASH_MASK 0x300
1912
1913static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v)
1914{
1915 int tx = COORD(x), ty = COORD(y);
1916 int bg = (v & FLASH_DEAD ? COL_DEAD_PLAYER :
1917 v & FLASH_WIN ? COL_HIGHLIGHT : COL_BACKGROUND);
1918
1919 v &= ~FLASH_MASK;
1920
1921 clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1);
1922 draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg);
1923
1924 if (v == WALL) {
1925 int coords[6];
1926
1927 coords[0] = tx + TILESIZE;
1928 coords[1] = ty + TILESIZE;
1929 coords[2] = tx + TILESIZE;
1930 coords[3] = ty + 1;
1931 coords[4] = tx + 1;
1932 coords[5] = ty + TILESIZE;
1933 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
1934
1935 coords[0] = tx + 1;
1936 coords[1] = ty + 1;
1937 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1938
1939 draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH,
1940 TILESIZE - 2*HIGHLIGHT_WIDTH,
1941 TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL);
1942 } else if (v == MINE) {
1943 int cx = tx + TILESIZE / 2;
1944 int cy = ty + TILESIZE / 2;
1945 int r = TILESIZE / 2 - 3;
1946
1947 draw_circle(dr, cx, cy, 5*r/6, COL_MINE, COL_MINE);
1948 draw_rect(dr, cx - r/6, cy - r, 2*(r/6)+1, 2*r+1, COL_MINE);
1949 draw_rect(dr, cx - r, cy - r/6, 2*r+1, 2*(r/6)+1, COL_MINE);
1950 draw_rect(dr, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT);
1951 } else if (v == STOP) {
1952 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1953 TILESIZE*3/7, -1, COL_OUTLINE);
1954 draw_rect(dr, tx + TILESIZE*3/7, ty+1,
1955 TILESIZE - 2*(TILESIZE*3/7) + 1, TILESIZE-1, bg);
1956 draw_rect(dr, tx+1, ty + TILESIZE*3/7,
1957 TILESIZE-1, TILESIZE - 2*(TILESIZE*3/7) + 1, bg);
1958 } else if (v == GEM) {
1959 int coords[8];
1960
1961 coords[0] = tx+TILESIZE/2;
1962 coords[1] = ty+TILESIZE/2-TILESIZE*5/14;
1963 coords[2] = tx+TILESIZE/2-TILESIZE*5/14;
1964 coords[3] = ty+TILESIZE/2;
1965 coords[4] = tx+TILESIZE/2;
1966 coords[5] = ty+TILESIZE/2+TILESIZE*5/14;
1967 coords[6] = tx+TILESIZE/2+TILESIZE*5/14;
1968 coords[7] = ty+TILESIZE/2;
1969
1970 draw_polygon(dr, coords, 4, COL_GEM, COL_OUTLINE);
1971 }
1972
1973 unclip(dr);
1974 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
1975}
1976
1977#define BASE_ANIM_LENGTH 0.1F
1978#define FLASH_LENGTH 0.3F
1979
1980static void game_redraw(drawing *dr, game_drawstate *ds,
1981 const game_state *oldstate, const game_state *state,
1982 int dir, const game_ui *ui,
1983 float animtime, float flashtime)
1984{
1985 int w = state->p.w, h = state->p.h /*, wh = w*h */;
1986 int x, y;
1987 float ap;
1988 int player_dist;
1989 int flashtype;
1990 int gems, deaths;
1991 char status[256];
1992
1993 if (flashtime &&
1994 !((int)(flashtime * 3 / FLASH_LENGTH) % 2))
1995 flashtype = ui->flashtype;
1996 else
1997 flashtype = 0;
1998
1999 /*
2000 * Erase the player sprite.
2001 */
2002 if (ds->player_bg_saved) {
2003 assert(ds->player_background);
2004 blitter_load(dr, ds->player_background, ds->pbgx, ds->pbgy);
2005 draw_update(dr, ds->pbgx, ds->pbgy, TILESIZE, TILESIZE);
2006 ds->player_bg_saved = FALSE;
2007 }
2008
2009 /*
2010 * Initialise a fresh drawstate.
2011 */
2012 if (!ds->started) {
2013 int wid, ht;
2014
2015 /*
2016 * Blank out the window initially.
2017 */
2018 game_compute_size(&ds->p, TILESIZE, &wid, &ht);
2019 draw_rect(dr, 0, 0, wid, ht, COL_BACKGROUND);
2020 draw_update(dr, 0, 0, wid, ht);
2021
2022 /*
2023 * Draw the grid lines.
2024 */
2025 for (y = 0; y <= h; y++)
2026 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y),
2027 COL_LOWLIGHT);
2028 for (x = 0; x <= w; x++)
2029 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h),
2030 COL_LOWLIGHT);
2031
2032 ds->started = TRUE;
2033 }
2034
2035 /*
2036 * If we're in the process of animating a move, let's start by
2037 * working out how far the player has moved from their _older_
2038 * state.
2039 */
2040 if (oldstate) {
2041 ap = animtime / ui->anim_length;
2042 player_dist = ap * (dir > 0 ? state : oldstate)->distance_moved;
2043 } else {
2044 player_dist = 0;
2045 ap = 0.0F;
2046 }
2047
2048 /*
2049 * Draw the grid contents.
2050 *
2051 * We count the gems as we go round this loop, for the purposes
2052 * of the status bar. Of course we have a gems counter in the
2053 * game_state already, but if we do the counting in this loop
2054 * then it tracks gems being picked up in a sliding move, and
2055 * updates one by one.
2056 */
2057 gems = 0;
2058 for (y = 0; y < h; y++)
2059 for (x = 0; x < w; x++) {
2060 unsigned short v = (unsigned char)state->grid[y*w+x];
2061
2062 /*
2063 * Special case: if the player is in the process of
2064 * moving over a gem, we draw the gem iff they haven't
2065 * gone past it yet.
2066 */
2067 if (oldstate && oldstate->grid[y*w+x] != state->grid[y*w+x]) {
2068 /*
2069 * Compute the distance from this square to the
2070 * original player position.
2071 */
2072 int dist = max(abs(x - oldstate->px), abs(y - oldstate->py));
2073
2074 /*
2075 * If the player has reached here, use the new grid
2076 * element. Otherwise use the old one.
2077 */
2078 if (player_dist < dist)
2079 v = oldstate->grid[y*w+x];
2080 else
2081 v = state->grid[y*w+x];
2082 }
2083
2084 /*
2085 * Special case: erase the mine the dead player is
2086 * sitting on. Only at the end of the move.
2087 */
2088 if (v == MINE && !oldstate && state->dead &&
2089 x == state->px && y == state->py)
2090 v = BLANK;
2091
2092 if (v == GEM)
2093 gems++;
2094
2095 v |= flashtype;
2096
2097 if (ds->grid[y*w+x] != v) {
2098 draw_tile(dr, ds, x, y, v);
2099 ds->grid[y*w+x] = v;
2100 }
2101 }
2102
2103 /*
2104 * Gem counter in the status bar. We replace it with
2105 * `COMPLETED!' when it reaches zero ... or rather, when the
2106 * _current state_'s gem counter is zero. (Thus, `Gems: 0' is
2107 * shown between the collection of the last gem and the
2108 * completion of the move animation that did it.)
2109 */
2110 if (state->dead && (!oldstate || oldstate->dead)) {
2111 sprintf(status, "DEAD!");
2112 } else if (state->gems || (oldstate && oldstate->gems)) {
2113 if (state->cheated)
2114 sprintf(status, "Auto-solver used. ");
2115 else
2116 *status = '\0';
2117 sprintf(status + strlen(status), "Gems: %d", gems);
2118 } else if (state->cheated) {
2119 sprintf(status, "Auto-solved.");
2120 } else {
2121 sprintf(status, "COMPLETED!");
2122 }
2123 /* We subtract one from the visible death counter if we're still
2124 * animating the move at the end of which the death took place. */
2125 deaths = ui->deaths;
2126 if (oldstate && ui->just_died) {
2127 assert(deaths > 0);
2128 deaths--;
2129 }
2130 if (deaths)
2131 sprintf(status + strlen(status), " Deaths: %d", deaths);
2132 status_bar(dr, status);
2133
2134 /*
2135 * Draw the player sprite.
2136 */
2137 assert(!ds->player_bg_saved);
2138 assert(ds->player_background);
2139 {
2140 int ox, oy, nx, ny;
2141 nx = COORD(state->px);
2142 ny = COORD(state->py);
2143 if (oldstate) {
2144 ox = COORD(oldstate->px);
2145 oy = COORD(oldstate->py);
2146 } else {
2147 ox = nx;
2148 oy = ny;
2149 }
2150 ds->pbgx = ox + ap * (nx - ox);
2151 ds->pbgy = oy + ap * (ny - oy);
2152 }
2153 blitter_save(dr, ds->player_background, ds->pbgx, ds->pbgy);
2154 draw_player(dr, ds, ds->pbgx, ds->pbgy,
2155 (state->dead && !oldstate),
2156 (!oldstate && state->soln ?
2157 state->soln->list[state->solnpos] : -1));
2158 ds->player_bg_saved = TRUE;
2159}
2160
2161static float game_anim_length(const game_state *oldstate,
2162 const game_state *newstate, int dir, game_ui *ui)
2163{
2164 int dist;
2165 if (dir > 0)
2166 dist = newstate->distance_moved;
2167 else
2168 dist = oldstate->distance_moved;
2169 ui->anim_length = sqrt(dist) * BASE_ANIM_LENGTH;
2170 return ui->anim_length;
2171}
2172
2173static float game_flash_length(const game_state *oldstate,
2174 const game_state *newstate, int dir, game_ui *ui)
2175{
2176 if (!oldstate->dead && newstate->dead) {
2177 ui->flashtype = FLASH_DEAD;
2178 return FLASH_LENGTH;
2179 } else if (oldstate->gems && !newstate->gems) {
2180 ui->flashtype = FLASH_WIN;
2181 return FLASH_LENGTH;
2182 }
2183 return 0.0F;
2184}
2185
2186static int game_status(const game_state *state)
2187{
2188 /*
2189 * We never report the game as lost, on the grounds that if the
2190 * player has died they're quite likely to want to undo and carry
2191 * on.
2192 */
2193 return state->gems == 0 ? +1 : 0;
2194}
2195
2196static int game_timing_state(const game_state *state, game_ui *ui)
2197{
2198 return TRUE;
2199}
2200
2201static void game_print_size(const game_params *params, float *x, float *y)
2202{
2203}
2204
2205static void game_print(drawing *dr, const game_state *state, int tilesize)
2206{
2207}
2208
2209#ifdef COMBINED
2210#define thegame inertia
2211#endif
2212
2213const struct game thegame = {
2214 "Inertia", "games.inertia", "inertia",
2215 default_params,
2216 game_fetch_preset, NULL,
2217 decode_params,
2218 encode_params,
2219 free_params,
2220 dup_params,
2221 TRUE, game_configure, custom_params,
2222 validate_params,
2223 new_game_desc,
2224 validate_desc,
2225 new_game,
2226 dup_game,
2227 free_game,
2228 TRUE, solve_game,
2229 TRUE, game_can_format_as_text_now, game_text_format,
2230 new_ui,
2231 free_ui,
2232 encode_ui,
2233 decode_ui,
2234 game_changed_state,
2235 interpret_move,
2236 execute_move,
2237 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2238 game_colours,
2239 game_new_drawstate,
2240 game_free_drawstate,
2241 game_redraw,
2242 game_anim_length,
2243 game_flash_length,
2244 game_status,
2245 FALSE, FALSE, game_print_size, game_print,
2246 TRUE, /* wants_statusbar */
2247 FALSE, game_timing_state,
2248 0, /* flags */
2249};
diff --git a/apps/plugins/puzzles/src/inertia.html b/apps/plugins/puzzles/src/inertia.html
new file mode 100644
index 0000000000..a8cca75c37
--- /dev/null
+++ b/apps/plugins/puzzles/src/inertia.html
@@ -0,0 +1,54 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Inertia</title>
7<link rel="previous" href="loopy.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="tents.html">
12</head>
13<body>
14<p><a href="loopy.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="tents.html">Next</a></p>
15<h1><a name="C24"></a>Chapter 24: <a name="i0"></a>Inertia</h1>
16<p>
17You are a small green ball sitting in a grid full of obstacles. Your aim is to collect all the gems without running into any mines.
18</p>
19<p>
20You can move the ball in any orthogonal <em>or diagonal</em> direction. Once the ball starts moving, it will continue until something stops it. A wall directly in its path will stop it (but if it is moving diagonally, it will move through a diagonal gap between two other walls without stopping). Also, some of the squares are &#8216;stops&#8217;; when the ball moves on to a stop, it will stop moving no matter what direction it was going in. Gems do <em>not</em> stop the ball; it picks them up and keeps on going.
21</p>
22<p>
23Running into a mine is fatal. Even if you picked up the last gem in the same move which then hit a mine, the game will count you as dead rather than victorious.
24</p>
25<p>
26This game was originally implemented for Windows by Ben Olmstead <a href="#p0">[11]</a>, who was kind enough to release his source code on request so that it could be re-implemented for this collection.
27</p>
28<p><a name="p0"></a>
29[11] <a href="http://xn13.com/"><code>http://xn13.com/</code></a>
30</p>
31<h2><a name="S24.1"></a>24.1 <a name="i1"></a>Inertia controls</h2>
32<p>
33You can move the ball in any of the eight directions using the numeric keypad. Alternatively, if you click the left mouse button on the grid, the ball will begin a move in the general direction of where you clicked.
34</p>
35<p>
36If you use the &#8216;Solve&#8217; function on this game, the program will compute a path through the grid which collects all the remaining gems and returns to the current position. A hint arrow will appear on the ball indicating the direction in which you should move to begin on this path. If you then move in that direction, the arrow will update to indicate the next direction on the path. You can also press Space to automatically move in the direction of the hint arrow. If you move in a different direction from the one shown by the arrow, arrows will be shown only if the puzzle is still solvable.
37</p>
38<p>
39All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available. In particular, if you do run into a mine and die, you can use the Undo function and resume playing from before the fatal move. The game will keep track of the number of times you have done this.
40</p>
41<h2><a name="S24.2"></a>24.2 <a name="i2"></a>Inertia parameters</h2>
42<p>
43These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
44</p>
45<dl><dt>
46<em>Width</em>, <em>Height</em>
47</dt>
48<dd>
49Size of grid in squares.
50</dd>
51</dl>
52
53<hr><address></address></body>
54</html>
diff --git a/apps/plugins/puzzles/src/install-sh b/apps/plugins/puzzles/src/install-sh
new file mode 100755
index 0000000000..0b0fdcbba6
--- /dev/null
+++ b/apps/plugins/puzzles/src/install-sh
@@ -0,0 +1,501 @@
1#!/bin/sh
2# install - install a program, script, or datafile
3
4scriptversion=2013-12-25.23; # UTC
5
6# This originates from X11R5 (mit/util/scripts/install.sh), which was
7# later released in X11R6 (xc/config/util/install.sh) with the
8# following copyright and license.
9#
10# Copyright (C) 1994 X Consortium
11#
12# Permission is hereby granted, free of charge, to any person obtaining a copy
13# of this software and associated documentation files (the "Software"), to
14# deal in the Software without restriction, including without limitation the
15# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
16# sell copies of the Software, and to permit persons to whom the Software is
17# furnished to do so, subject to the following conditions:
18#
19# The above copyright notice and this permission notice shall be included in
20# all copies or substantial portions of the Software.
21#
22# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
27# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28#
29# Except as contained in this notice, the name of the X Consortium shall not
30# be used in advertising or otherwise to promote the sale, use or other deal-
31# ings in this Software without prior written authorization from the X Consor-
32# tium.
33#
34#
35# FSF changes to this file are in the public domain.
36#
37# Calling this script install-sh is preferred over install.sh, to prevent
38# 'make' implicit rules from creating a file called install from it
39# when there is no Makefile.
40#
41# This script is compatible with the BSD install script, but was written
42# from scratch.
43
44tab=' '
45nl='
46'
47IFS=" $tab$nl"
48
49# Set DOITPROG to "echo" to test this script.
50
51doit=${DOITPROG-}
52doit_exec=${doit:-exec}
53
54# Put in absolute file names if you don't have them in your path;
55# or use environment vars.
56
57chgrpprog=${CHGRPPROG-chgrp}
58chmodprog=${CHMODPROG-chmod}
59chownprog=${CHOWNPROG-chown}
60cmpprog=${CMPPROG-cmp}
61cpprog=${CPPROG-cp}
62mkdirprog=${MKDIRPROG-mkdir}
63mvprog=${MVPROG-mv}
64rmprog=${RMPROG-rm}
65stripprog=${STRIPPROG-strip}
66
67posix_mkdir=
68
69# Desired mode of installed file.
70mode=0755
71
72chgrpcmd=
73chmodcmd=$chmodprog
74chowncmd=
75mvcmd=$mvprog
76rmcmd="$rmprog -f"
77stripcmd=
78
79src=
80dst=
81dir_arg=
82dst_arg=
83
84copy_on_change=false
85is_target_a_directory=possibly
86
87usage="\
88Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
89 or: $0 [OPTION]... SRCFILES... DIRECTORY
90 or: $0 [OPTION]... -t DIRECTORY SRCFILES...
91 or: $0 [OPTION]... -d DIRECTORIES...
92
93In the 1st form, copy SRCFILE to DSTFILE.
94In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
95In the 4th, create DIRECTORIES.
96
97Options:
98 --help display this help and exit.
99 --version display version info and exit.
100
101 -c (ignored)
102 -C install only if different (preserve the last data modification time)
103 -d create directories instead of installing files.
104 -g GROUP $chgrpprog installed files to GROUP.
105 -m MODE $chmodprog installed files to MODE.
106 -o USER $chownprog installed files to USER.
107 -s $stripprog installed files.
108 -t DIRECTORY install into DIRECTORY.
109 -T report an error if DSTFILE is a directory.
110
111Environment variables override the default commands:
112 CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
113 RMPROG STRIPPROG
114"
115
116while test $# -ne 0; do
117 case $1 in
118 -c) ;;
119
120 -C) copy_on_change=true;;
121
122 -d) dir_arg=true;;
123
124 -g) chgrpcmd="$chgrpprog $2"
125 shift;;
126
127 --help) echo "$usage"; exit $?;;
128
129 -m) mode=$2
130 case $mode in
131 *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
132 echo "$0: invalid mode: $mode" >&2
133 exit 1;;
134 esac
135 shift;;
136
137 -o) chowncmd="$chownprog $2"
138 shift;;
139
140 -s) stripcmd=$stripprog;;
141
142 -t)
143 is_target_a_directory=always
144 dst_arg=$2
145 # Protect names problematic for 'test' and other utilities.
146 case $dst_arg in
147 -* | [=\(\)!]) dst_arg=./$dst_arg;;
148 esac
149 shift;;
150
151 -T) is_target_a_directory=never;;
152
153 --version) echo "$0 $scriptversion"; exit $?;;
154
155 --) shift
156 break;;
157
158 -*) echo "$0: invalid option: $1" >&2
159 exit 1;;
160
161 *) break;;
162 esac
163 shift
164done
165
166# We allow the use of options -d and -T together, by making -d
167# take the precedence; this is for compatibility with GNU install.
168
169if test -n "$dir_arg"; then
170 if test -n "$dst_arg"; then
171 echo "$0: target directory not allowed when installing a directory." >&2
172 exit 1
173 fi
174fi
175
176if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
177 # When -d is used, all remaining arguments are directories to create.
178 # When -t is used, the destination is already specified.
179 # Otherwise, the last argument is the destination. Remove it from $@.
180 for arg
181 do
182 if test -n "$dst_arg"; then
183 # $@ is not empty: it contains at least $arg.
184 set fnord "$@" "$dst_arg"
185 shift # fnord
186 fi
187 shift # arg
188 dst_arg=$arg
189 # Protect names problematic for 'test' and other utilities.
190 case $dst_arg in
191 -* | [=\(\)!]) dst_arg=./$dst_arg;;
192 esac
193 done
194fi
195
196if test $# -eq 0; then
197 if test -z "$dir_arg"; then
198 echo "$0: no input file specified." >&2
199 exit 1
200 fi
201 # It's OK to call 'install-sh -d' without argument.
202 # This can happen when creating conditional directories.
203 exit 0
204fi
205
206if test -z "$dir_arg"; then
207 if test $# -gt 1 || test "$is_target_a_directory" = always; then
208 if test ! -d "$dst_arg"; then
209 echo "$0: $dst_arg: Is not a directory." >&2
210 exit 1
211 fi
212 fi
213fi
214
215if test -z "$dir_arg"; then
216 do_exit='(exit $ret); exit $ret'
217 trap "ret=129; $do_exit" 1
218 trap "ret=130; $do_exit" 2
219 trap "ret=141; $do_exit" 13
220 trap "ret=143; $do_exit" 15
221
222 # Set umask so as not to create temps with too-generous modes.
223 # However, 'strip' requires both read and write access to temps.
224 case $mode in
225 # Optimize common cases.
226 *644) cp_umask=133;;
227 *755) cp_umask=22;;
228
229 *[0-7])
230 if test -z "$stripcmd"; then
231 u_plus_rw=
232 else
233 u_plus_rw='% 200'
234 fi
235 cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
236 *)
237 if test -z "$stripcmd"; then
238 u_plus_rw=
239 else
240 u_plus_rw=,u+rw
241 fi
242 cp_umask=$mode$u_plus_rw;;
243 esac
244fi
245
246for src
247do
248 # Protect names problematic for 'test' and other utilities.
249 case $src in
250 -* | [=\(\)!]) src=./$src;;
251 esac
252
253 if test -n "$dir_arg"; then
254 dst=$src
255 dstdir=$dst
256 test -d "$dstdir"
257 dstdir_status=$?
258 else
259
260 # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
261 # might cause directories to be created, which would be especially bad
262 # if $src (and thus $dsttmp) contains '*'.
263 if test ! -f "$src" && test ! -d "$src"; then
264 echo "$0: $src does not exist." >&2
265 exit 1
266 fi
267
268 if test -z "$dst_arg"; then
269 echo "$0: no destination specified." >&2
270 exit 1
271 fi
272 dst=$dst_arg
273
274 # If destination is a directory, append the input filename; won't work
275 # if double slashes aren't ignored.
276 if test -d "$dst"; then
277 if test "$is_target_a_directory" = never; then
278 echo "$0: $dst_arg: Is a directory" >&2
279 exit 1
280 fi
281 dstdir=$dst
282 dst=$dstdir/`basename "$src"`
283 dstdir_status=0
284 else
285 dstdir=`dirname "$dst"`
286 test -d "$dstdir"
287 dstdir_status=$?
288 fi
289 fi
290
291 obsolete_mkdir_used=false
292
293 if test $dstdir_status != 0; then
294 case $posix_mkdir in
295 '')
296 # Create intermediate dirs using mode 755 as modified by the umask.
297 # This is like FreeBSD 'install' as of 1997-10-28.
298 umask=`umask`
299 case $stripcmd.$umask in
300 # Optimize common cases.
301 *[2367][2367]) mkdir_umask=$umask;;
302 .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
303
304 *[0-7])
305 mkdir_umask=`expr $umask + 22 \
306 - $umask % 100 % 40 + $umask % 20 \
307 - $umask % 10 % 4 + $umask % 2
308 `;;
309 *) mkdir_umask=$umask,go-w;;
310 esac
311
312 # With -d, create the new directory with the user-specified mode.
313 # Otherwise, rely on $mkdir_umask.
314 if test -n "$dir_arg"; then
315 mkdir_mode=-m$mode
316 else
317 mkdir_mode=
318 fi
319
320 posix_mkdir=false
321 case $umask in
322 *[123567][0-7][0-7])
323 # POSIX mkdir -p sets u+wx bits regardless of umask, which
324 # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
325 ;;
326 *)
327 tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
328 trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
329
330 if (umask $mkdir_umask &&
331 exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
332 then
333 if test -z "$dir_arg" || {
334 # Check for POSIX incompatibilities with -m.
335 # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
336 # other-writable bit of parent directory when it shouldn't.
337 # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
338 ls_ld_tmpdir=`ls -ld "$tmpdir"`
339 case $ls_ld_tmpdir in
340 d????-?r-*) different_mode=700;;
341 d????-?--*) different_mode=755;;
342 *) false;;
343 esac &&
344 $mkdirprog -m$different_mode -p -- "$tmpdir" && {
345 ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
346 test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
347 }
348 }
349 then posix_mkdir=:
350 fi
351 rmdir "$tmpdir/d" "$tmpdir"
352 else
353 # Remove any dirs left behind by ancient mkdir implementations.
354 rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
355 fi
356 trap '' 0;;
357 esac;;
358 esac
359
360 if
361 $posix_mkdir && (
362 umask $mkdir_umask &&
363 $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
364 )
365 then :
366 else
367
368 # The umask is ridiculous, or mkdir does not conform to POSIX,
369 # or it failed possibly due to a race condition. Create the
370 # directory the slow way, step by step, checking for races as we go.
371
372 case $dstdir in
373 /*) prefix='/';;
374 [-=\(\)!]*) prefix='./';;
375 *) prefix='';;
376 esac
377
378 oIFS=$IFS
379 IFS=/
380 set -f
381 set fnord $dstdir
382 shift
383 set +f
384 IFS=$oIFS
385
386 prefixes=
387
388 for d
389 do
390 test X"$d" = X && continue
391
392 prefix=$prefix$d
393 if test -d "$prefix"; then
394 prefixes=
395 else
396 if $posix_mkdir; then
397 (umask=$mkdir_umask &&
398 $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
399 # Don't fail if two instances are running concurrently.
400 test -d "$prefix" || exit 1
401 else
402 case $prefix in
403 *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
404 *) qprefix=$prefix;;
405 esac
406 prefixes="$prefixes '$qprefix'"
407 fi
408 fi
409 prefix=$prefix/
410 done
411
412 if test -n "$prefixes"; then
413 # Don't fail if two instances are running concurrently.
414 (umask $mkdir_umask &&
415 eval "\$doit_exec \$mkdirprog $prefixes") ||
416 test -d "$dstdir" || exit 1
417 obsolete_mkdir_used=true
418 fi
419 fi
420 fi
421
422 if test -n "$dir_arg"; then
423 { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
424 { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
425 { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
426 test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
427 else
428
429 # Make a couple of temp file names in the proper directory.
430 dsttmp=$dstdir/_inst.$$_
431 rmtmp=$dstdir/_rm.$$_
432
433 # Trap to clean up those temp files at exit.
434 trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
435
436 # Copy the file name to the temp name.
437 (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
438
439 # and set any options; do chmod last to preserve setuid bits.
440 #
441 # If any of these fail, we abort the whole thing. If we want to
442 # ignore errors from any of these, just make sure not to ignore
443 # errors from the above "$doit $cpprog $src $dsttmp" command.
444 #
445 { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
446 { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
447 { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
448 { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
449
450 # If -C, don't bother to copy if it wouldn't change the file.
451 if $copy_on_change &&
452 old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
453 new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
454 set -f &&
455 set X $old && old=:$2:$4:$5:$6 &&
456 set X $new && new=:$2:$4:$5:$6 &&
457 set +f &&
458 test "$old" = "$new" &&
459 $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
460 then
461 rm -f "$dsttmp"
462 else
463 # Rename the file to the real destination.
464 $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
465
466 # The rename failed, perhaps because mv can't rename something else
467 # to itself, or perhaps because mv is so ancient that it does not
468 # support -f.
469 {
470 # Now remove or move aside any old file at destination location.
471 # We try this two ways since rm can't unlink itself on some
472 # systems and the destination file might be busy for other
473 # reasons. In this case, the final cleanup might fail but the new
474 # file should still install successfully.
475 {
476 test ! -f "$dst" ||
477 $doit $rmcmd -f "$dst" 2>/dev/null ||
478 { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
479 { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
480 } ||
481 { echo "$0: cannot unlink or rename $dst" >&2
482 (exit 1); exit 1
483 }
484 } &&
485
486 # Now rename the file to the real destination.
487 $doit $mvcmd "$dsttmp" "$dst"
488 }
489 fi || exit 1
490
491 trap '' 0
492 fi
493done
494
495# Local variables:
496# eval: (add-hook 'write-file-hooks 'time-stamp)
497# time-stamp-start: "scriptversion="
498# time-stamp-format: "%:y-%02m-%02d.%02H"
499# time-stamp-time-zone: "UTC"
500# time-stamp-end: "; # UTC"
501# End:
diff --git a/apps/plugins/puzzles/src/intro.html b/apps/plugins/puzzles/src/intro.html
new file mode 100644
index 0000000000..cf82c837de
--- /dev/null
+++ b/apps/plugins/puzzles/src/intro.html
@@ -0,0 +1,39 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Introduction</title>
7<link rel="previous" href="index.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="common.html">
12</head>
13<body>
14<p><a href="index.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="common.html">Next</a></p>
15<h1><a name="C1"></a>Chapter 1: Introduction</h1>
16<p>
17I wrote this collection because I thought there should be more small desktop toys available: little games you can pop up in a window and play for two or three minutes while you take a break from whatever else you were doing. And I was also annoyed that every time I found a good game on (say) <a name="i0"></a>Unix, it wasn't available the next time I was sitting at a <a name="i1"></a>Windows machine, or vice versa; so I arranged that everything in my personal puzzle collection will happily run on both, and have more recently done a port to <a name="i2"></a>Mac OS X as well. When I find (or perhaps invent) further puzzle games that I like, they'll be added to this collection and will immediately be available on both platforms. And if anyone feels like writing any other front ends &#8211; PocketPC, Mac OS pre-10, or whatever it might be &#8211; then all the games in this framework will immediately become available on another platform as well.
18</p>
19<p>
20The actual games in this collection were mostly not my invention; they are re-implementations of existing game concepts within my portable puzzle framework. I do not claim credit, in general, for inventing the rules of any of these puzzles. (I don't even claim authorship of all the code; some of the puzzles have been submitted by other authors.)
21</p>
22<p>
23This collection is distributed under the <a name="i3"></a>MIT licence (see <a href="licence.html#AA">appendix A</a>). This means that you can do pretty much anything you like with the game binaries or the code, except pretending you wrote them yourself, or suing me if anything goes wrong.
24</p>
25<p>
26The most recent versions, and <a name="i4"></a>source code, can be found at <a name="i5"></a><a href="http://www.chiark.greenend.org.uk/~sgtatham/puzzles/"><code>http://www.chiark.greenend.org.uk/~sgtatham/puzzles/</code></a>.
27</p>
28<p>
29Please report <a name="i6"></a><a name="i7"></a>bugs to <a href="mailto:anakin@pobox.com"><code>anakin@pobox.com</code></a>. You might find it helpful to read this article before reporting a bug:
30</p>
31<p>
32<a href="http://www.chiark.greenend.org.uk/~sgtatham/bugs.html"><code>http://www.chiark.greenend.org.uk/~sgtatham/bugs.html</code></a>
33</p>
34<p>
35<a name="i8"></a>Patches are welcome. Especially if they provide a new front end (to make all these games run on another platform), or a new game.
36</p>
37
38<hr><address></address></body>
39</html>
diff --git a/apps/plugins/puzzles/src/keen.R b/apps/plugins/puzzles/src/keen.R
new file mode 100644
index 0000000000..77609bc7fa
--- /dev/null
+++ b/apps/plugins/puzzles/src/keen.R
@@ -0,0 +1,25 @@
1# -*- makefile -*-
2
3KEEN_LATIN_EXTRA = tree234 maxflow dsf
4KEEN_EXTRA = latin KEEN_LATIN_EXTRA
5
6keen : [X] GTK COMMON keen KEEN_EXTRA keen-icon|no-icon
7
8keen : [G] WINDOWS COMMON keen KEEN_EXTRA keen.res|noicon.res
9
10keensolver : [U] keen[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] KEEN_LATIN_EXTRA STANDALONE
11keensolver : [C] keen[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] KEEN_LATIN_EXTRA STANDALONE
12
13ALL += keen[COMBINED] KEEN_EXTRA
14
15!begin am gtk
16GAMES += keen
17!end
18
19!begin >list.c
20 A(keen) \
21!end
22
23!begin >gamedesc.txt
24keen:keen.exe:Keen:Arithmetic Latin square puzzle:Complete the latin square in accordance with the arithmetic clues.
25!end
diff --git a/apps/plugins/puzzles/src/keen.c b/apps/plugins/puzzles/src/keen.c
new file mode 100644
index 0000000000..fdaae32e5d
--- /dev/null
+++ b/apps/plugins/puzzles/src/keen.c
@@ -0,0 +1,2479 @@
1/*
2 * keen.c: an implementation of the Times's 'KenKen' puzzle, and
3 * also of Nikoli's very similar 'Inshi No Heya' puzzle.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <assert.h>
10#include <ctype.h>
11#include <math.h>
12
13#include "puzzles.h"
14#include "latin.h"
15
16/*
17 * Difficulty levels. I do some macro ickery here to ensure that my
18 * enum and the various forms of my name list always match up.
19 */
20#define DIFFLIST(A) \
21 A(EASY,Easy,solver_easy,e) \
22 A(NORMAL,Normal,solver_normal,n) \
23 A(HARD,Hard,solver_hard,h) \
24 A(EXTREME,Extreme,NULL,x) \
25 A(UNREASONABLE,Unreasonable,NULL,u)
26#define ENUM(upper,title,func,lower) DIFF_ ## upper,
27#define TITLE(upper,title,func,lower) #title,
28#define ENCODE(upper,title,func,lower) #lower
29#define CONFIG(upper,title,func,lower) ":" #title
30enum { DIFFLIST(ENUM) DIFFCOUNT };
31static char const *const keen_diffnames[] = { DIFFLIST(TITLE) };
32static char const keen_diffchars[] = DIFFLIST(ENCODE);
33#define DIFFCONFIG DIFFLIST(CONFIG)
34
35/*
36 * Clue notation. Important here that ADD and MUL come before SUB
37 * and DIV, and that DIV comes last.
38 */
39#define C_ADD 0x00000000L
40#define C_MUL 0x20000000L
41#define C_SUB 0x40000000L
42#define C_DIV 0x60000000L
43#define CMASK 0x60000000L
44#define CUNIT 0x20000000L
45
46/*
47 * Maximum size of any clue block. Very large ones are annoying in UI
48 * terms (if they're multiplicative you end up with too many digits to
49 * fit in the square) and also in solver terms (too many possibilities
50 * to iterate over).
51 */
52#define MAXBLK 6
53
54enum {
55 COL_BACKGROUND,
56 COL_GRID,
57 COL_USER,
58 COL_HIGHLIGHT,
59 COL_ERROR,
60 COL_PENCIL,
61 NCOLOURS
62};
63
64struct game_params {
65 int w, diff, multiplication_only;
66};
67
68struct clues {
69 int refcount;
70 int w;
71 int *dsf;
72 long *clues;
73};
74
75struct game_state {
76 game_params par;
77 struct clues *clues;
78 digit *grid;
79 int *pencil; /* bitmaps using bits 1<<1..1<<n */
80 int completed, cheated;
81};
82
83static game_params *default_params(void)
84{
85 game_params *ret = snew(game_params);
86
87 ret->w = 6;
88 ret->diff = DIFF_NORMAL;
89 ret->multiplication_only = FALSE;
90
91 return ret;
92}
93
94const static struct game_params keen_presets[] = {
95 { 4, DIFF_EASY, FALSE },
96 { 5, DIFF_EASY, FALSE },
97 { 5, DIFF_EASY, TRUE },
98 { 6, DIFF_EASY, FALSE },
99 { 6, DIFF_NORMAL, FALSE },
100 { 6, DIFF_NORMAL, TRUE },
101 { 6, DIFF_HARD, FALSE },
102 { 6, DIFF_EXTREME, FALSE },
103 { 6, DIFF_UNREASONABLE, FALSE },
104 { 9, DIFF_NORMAL, FALSE },
105};
106
107static int game_fetch_preset(int i, char **name, game_params **params)
108{
109 game_params *ret;
110 char buf[80];
111
112 if (i < 0 || i >= lenof(keen_presets))
113 return FALSE;
114
115 ret = snew(game_params);
116 *ret = keen_presets[i]; /* structure copy */
117
118 sprintf(buf, "%dx%d %s%s", ret->w, ret->w, keen_diffnames[ret->diff],
119 ret->multiplication_only ? ", multiplication only" : "");
120
121 *name = dupstr(buf);
122 *params = ret;
123 return TRUE;
124}
125
126static void free_params(game_params *params)
127{
128 sfree(params);
129}
130
131static game_params *dup_params(const game_params *params)
132{
133 game_params *ret = snew(game_params);
134 *ret = *params; /* structure copy */
135 return ret;
136}
137
138static void decode_params(game_params *params, char const *string)
139{
140 char const *p = string;
141
142 params->w = atoi(p);
143 while (*p && isdigit((unsigned char)*p)) p++;
144
145 if (*p == 'd') {
146 int i;
147 p++;
148 params->diff = DIFFCOUNT+1; /* ...which is invalid */
149 if (*p) {
150 for (i = 0; i < DIFFCOUNT; i++) {
151 if (*p == keen_diffchars[i])
152 params->diff = i;
153 }
154 p++;
155 }
156 }
157
158 if (*p == 'm') {
159 p++;
160 params->multiplication_only = TRUE;
161 }
162}
163
164static char *encode_params(const game_params *params, int full)
165{
166 char ret[80];
167
168 sprintf(ret, "%d", params->w);
169 if (full)
170 sprintf(ret + strlen(ret), "d%c%s", keen_diffchars[params->diff],
171 params->multiplication_only ? "m" : "");
172
173 return dupstr(ret);
174}
175
176static config_item *game_configure(const game_params *params)
177{
178 config_item *ret;
179 char buf[80];
180
181 ret = snewn(4, config_item);
182
183 ret[0].name = "Grid size";
184 ret[0].type = C_STRING;
185 sprintf(buf, "%d", params->w);
186 ret[0].sval = dupstr(buf);
187 ret[0].ival = 0;
188
189 ret[1].name = "Difficulty";
190 ret[1].type = C_CHOICES;
191 ret[1].sval = DIFFCONFIG;
192 ret[1].ival = params->diff;
193
194 ret[2].name = "Multiplication only";
195 ret[2].type = C_BOOLEAN;
196 ret[2].sval = NULL;
197 ret[2].ival = params->multiplication_only;
198
199 ret[3].name = NULL;
200 ret[3].type = C_END;
201 ret[3].sval = NULL;
202 ret[3].ival = 0;
203
204 return ret;
205}
206
207static game_params *custom_params(const config_item *cfg)
208{
209 game_params *ret = snew(game_params);
210
211 ret->w = atoi(cfg[0].sval);
212 ret->diff = cfg[1].ival;
213 ret->multiplication_only = cfg[2].ival;
214
215 return ret;
216}
217
218static char *validate_params(const game_params *params, int full)
219{
220 if (params->w < 3 || params->w > 9)
221 return "Grid size must be between 3 and 9";
222 if (params->diff >= DIFFCOUNT)
223 return "Unknown difficulty rating";
224 return NULL;
225}
226
227/* ----------------------------------------------------------------------
228 * Solver.
229 */
230
231struct solver_ctx {
232 int w, diff;
233 int nboxes;
234 int *boxes, *boxlist, *whichbox;
235 long *clues;
236 digit *soln;
237 digit *dscratch;
238 int *iscratch;
239};
240
241static void solver_clue_candidate(struct solver_ctx *ctx, int diff, int box)
242{
243 int w = ctx->w;
244 int n = ctx->boxes[box+1] - ctx->boxes[box];
245 int j;
246
247 /*
248 * This function is called from the main clue-based solver
249 * routine when we discover a candidate layout for a given clue
250 * box consistent with everything we currently know about the
251 * digit constraints in that box. We expect to find the digits
252 * of the candidate layout in ctx->dscratch, and we update
253 * ctx->iscratch as appropriate.
254 *
255 * The contents of ctx->iscratch are completely different
256 * depending on whether diff == DIFF_HARD or not. This function
257 * uses iscratch completely differently between the two cases, and
258 * the code in solver_common() which consumes the result must
259 * likewise have an if statement with completely different
260 * branches for the two cases.
261 *
262 * In DIFF_EASY and DIFF_NORMAL modes, the valid entries in
263 * ctx->iscratch are 0,...,n-1, and each of those entries
264 * ctx->iscratch[i] gives a bitmap of the possible digits in the
265 * ith square of the clue box currently under consideration. So
266 * each entry of iscratch starts off as an empty bitmap, and we
267 * set bits in it as possible layouts for the clue box are
268 * considered (and the difference between DIFF_EASY and
269 * DIFF_NORMAL is just that in DIFF_EASY mode we deliberately set
270 * more bits than absolutely necessary, hence restricting our own
271 * knowledge).
272 *
273 * But in DIFF_HARD mode, the valid entries are 0,...,2*w-1 (at
274 * least outside *this* function - inside this function, we also
275 * use 2*w,...,4*w-1 as scratch space in the loop below); the
276 * first w of those give the possible digits in the intersection
277 * of the current clue box with each column of the puzzle, and the
278 * next w do the same for each row. In this mode, each iscratch
279 * entry starts off as a _full_ bitmap, and in this function we
280 * _clear_ bits for digits that are absent from a given row or
281 * column in each candidate layout, so that the only bits which
282 * remain set are those for digits which have to appear in a given
283 * row/column no matter how the clue box is laid out.
284 */
285 if (diff == DIFF_EASY) {
286 unsigned mask = 0;
287 /*
288 * Easy-mode clue deductions: we do not record information
289 * about which squares take which values, so we amalgamate
290 * all the values in dscratch and OR them all into
291 * everywhere.
292 */
293 for (j = 0; j < n; j++)
294 mask |= 1 << ctx->dscratch[j];
295 for (j = 0; j < n; j++)
296 ctx->iscratch[j] |= mask;
297 } else if (diff == DIFF_NORMAL) {
298 /*
299 * Normal-mode deductions: we process the information in
300 * dscratch in the obvious way.
301 */
302 for (j = 0; j < n; j++)
303 ctx->iscratch[j] |= 1 << ctx->dscratch[j];
304 } else if (diff == DIFF_HARD) {
305 /*
306 * Hard-mode deductions: instead of ruling things out
307 * _inside_ the clue box, we look for numbers which occur in
308 * a given row or column in all candidate layouts, and rule
309 * them out of all squares in that row or column that
310 * _aren't_ part of this clue box.
311 */
312 int *sq = ctx->boxlist + ctx->boxes[box];
313
314 for (j = 0; j < 2*w; j++)
315 ctx->iscratch[2*w+j] = 0;
316 for (j = 0; j < n; j++) {
317 int x = sq[j] / w, y = sq[j] % w;
318 ctx->iscratch[2*w+x] |= 1 << ctx->dscratch[j];
319 ctx->iscratch[3*w+y] |= 1 << ctx->dscratch[j];
320 }
321 for (j = 0; j < 2*w; j++)
322 ctx->iscratch[j] &= ctx->iscratch[2*w+j];
323 }
324}
325
326static int solver_common(struct latin_solver *solver, void *vctx, int diff)
327{
328 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
329 int w = ctx->w;
330 int box, i, j, k;
331 int ret = 0, total;
332
333 /*
334 * Iterate over each clue box and deduce what we can.
335 */
336 for (box = 0; box < ctx->nboxes; box++) {
337 int *sq = ctx->boxlist + ctx->boxes[box];
338 int n = ctx->boxes[box+1] - ctx->boxes[box];
339 long value = ctx->clues[box] & ~CMASK;
340 long op = ctx->clues[box] & CMASK;
341
342 /*
343 * Initialise ctx->iscratch for this clue box. At different
344 * difficulty levels we must initialise a different amount of
345 * it to different things; see the comments in
346 * solver_clue_candidate explaining what each version does.
347 */
348 if (diff == DIFF_HARD) {
349 for (i = 0; i < 2*w; i++)
350 ctx->iscratch[i] = (1 << (w+1)) - (1 << 1);
351 } else {
352 for (i = 0; i < n; i++)
353 ctx->iscratch[i] = 0;
354 }
355
356 switch (op) {
357 case C_SUB:
358 case C_DIV:
359 /*
360 * These two clue types must always apply to a box of
361 * area 2. Also, the two digits in these boxes can never
362 * be the same (because any domino must have its two
363 * squares in either the same row or the same column).
364 * So we simply iterate over all possibilities for the
365 * two squares (both ways round), rule out any which are
366 * inconsistent with the digit constraints we already
367 * have, and update the digit constraints with any new
368 * information thus garnered.
369 */
370 assert(n == 2);
371
372 for (i = 1; i <= w; i++) {
373 j = (op == C_SUB ? i + value : i * value);
374 if (j > w) break;
375
376 /* (i,j) is a valid digit pair. Try it both ways round. */
377
378 if (solver->cube[sq[0]*w+i-1] &&
379 solver->cube[sq[1]*w+j-1]) {
380 ctx->dscratch[0] = i;
381 ctx->dscratch[1] = j;
382 solver_clue_candidate(ctx, diff, box);
383 }
384
385 if (solver->cube[sq[0]*w+j-1] &&
386 solver->cube[sq[1]*w+i-1]) {
387 ctx->dscratch[0] = j;
388 ctx->dscratch[1] = i;
389 solver_clue_candidate(ctx, diff, box);
390 }
391 }
392
393 break;
394
395 case C_ADD:
396 case C_MUL:
397 /*
398 * For these clue types, I have no alternative but to go
399 * through all possible number combinations.
400 *
401 * Instead of a tedious physical recursion, I iterate in
402 * the scratch array through all possibilities. At any
403 * given moment, i indexes the element of the box that
404 * will next be incremented.
405 */
406 i = 0;
407 ctx->dscratch[i] = 0;
408 total = value; /* start with the identity */
409 while (1) {
410 if (i < n) {
411 /*
412 * Find the next valid value for cell i.
413 */
414 for (j = ctx->dscratch[i] + 1; j <= w; j++) {
415 if (op == C_ADD ? (total < j) : (total % j != 0))
416 continue; /* this one won't fit */
417 if (!solver->cube[sq[i]*w+j-1])
418 continue; /* this one is ruled out already */
419 for (k = 0; k < i; k++)
420 if (ctx->dscratch[k] == j &&
421 (sq[k] % w == sq[i] % w ||
422 sq[k] / w == sq[i] / w))
423 break; /* clashes with another row/col */
424 if (k < i)
425 continue;
426
427 /* Found one. */
428 break;
429 }
430
431 if (j > w) {
432 /* No valid values left; drop back. */
433 i--;
434 if (i < 0)
435 break; /* overall iteration is finished */
436 if (op == C_ADD)
437 total += ctx->dscratch[i];
438 else
439 total *= ctx->dscratch[i];
440 } else {
441 /* Got a valid value; store it and move on. */
442 ctx->dscratch[i++] = j;
443 if (op == C_ADD)
444 total -= j;
445 else
446 total /= j;
447 ctx->dscratch[i] = 0;
448 }
449 } else {
450 if (total == (op == C_ADD ? 0 : 1))
451 solver_clue_candidate(ctx, diff, box);
452 i--;
453 if (op == C_ADD)
454 total += ctx->dscratch[i];
455 else
456 total *= ctx->dscratch[i];
457 }
458 }
459
460 break;
461 }
462
463 /*
464 * Do deductions based on the information we've now
465 * accumulated in ctx->iscratch. See the comments above in
466 * solver_clue_candidate explaining what data is left in here,
467 * and how it differs between DIFF_HARD and lower difficulty
468 * levels (hence the big if statement here).
469 */
470 if (diff < DIFF_HARD) {
471#ifdef STANDALONE_SOLVER
472 char prefix[256];
473
474 if (solver_show_working)
475 sprintf(prefix, "%*susing clue at (%d,%d):\n",
476 solver_recurse_depth*4, "",
477 sq[0]/w+1, sq[0]%w+1);
478 else
479 prefix[0] = '\0'; /* placate optimiser */
480#endif
481
482 for (i = 0; i < n; i++)
483 for (j = 1; j <= w; j++) {
484 if (solver->cube[sq[i]*w+j-1] &&
485 !(ctx->iscratch[i] & (1 << j))) {
486#ifdef STANDALONE_SOLVER
487 if (solver_show_working) {
488 printf("%s%*s ruling out %d at (%d,%d)\n",
489 prefix, solver_recurse_depth*4, "",
490 j, sq[i]/w+1, sq[i]%w+1);
491 prefix[0] = '\0';
492 }
493#endif
494 solver->cube[sq[i]*w+j-1] = 0;
495 ret = 1;
496 }
497 }
498 } else {
499#ifdef STANDALONE_SOLVER
500 char prefix[256];
501
502 if (solver_show_working)
503 sprintf(prefix, "%*susing clue at (%d,%d):\n",
504 solver_recurse_depth*4, "",
505 sq[0]/w+1, sq[0]%w+1);
506 else
507 prefix[0] = '\0'; /* placate optimiser */
508#endif
509
510 for (i = 0; i < 2*w; i++) {
511 int start = (i < w ? i*w : i-w);
512 int step = (i < w ? 1 : w);
513 for (j = 1; j <= w; j++) if (ctx->iscratch[i] & (1 << j)) {
514#ifdef STANDALONE_SOLVER
515 char prefix2[256];
516
517 if (solver_show_working)
518 sprintf(prefix2, "%*s this clue requires %d in"
519 " %s %d:\n", solver_recurse_depth*4, "",
520 j, i < w ? "column" : "row", i%w+1);
521 else
522 prefix2[0] = '\0'; /* placate optimiser */
523#endif
524
525 for (k = 0; k < w; k++) {
526 int pos = start + k*step;
527 if (ctx->whichbox[pos] != box &&
528 solver->cube[pos*w+j-1]) {
529#ifdef STANDALONE_SOLVER
530 if (solver_show_working) {
531 printf("%s%s%*s ruling out %d at (%d,%d)\n",
532 prefix, prefix2,
533 solver_recurse_depth*4, "",
534 j, pos/w+1, pos%w+1);
535 prefix[0] = prefix2[0] = '\0';
536 }
537#endif
538 solver->cube[pos*w+j-1] = 0;
539 ret = 1;
540 }
541 }
542 }
543 }
544
545 /*
546 * Once we find one block we can do something with in
547 * this way, revert to trying easier deductions, so as
548 * not to generate solver diagnostics that make the
549 * problem look harder than it is. (We have to do this
550 * for the Hard deductions but not the Easy/Normal ones,
551 * because only the Hard deductions are cross-box.)
552 */
553 if (ret)
554 return ret;
555 }
556 }
557
558 return ret;
559}
560
561static int solver_easy(struct latin_solver *solver, void *vctx)
562{
563 /*
564 * Omit the EASY deductions when solving at NORMAL level, since
565 * the NORMAL deductions are a superset of them anyway and it
566 * saves on time and confusing solver diagnostics.
567 *
568 * Note that this breaks the natural semantics of the return
569 * value of latin_solver. Without this hack, you could determine
570 * a puzzle's difficulty in one go by trying to solve it at
571 * maximum difficulty and seeing what difficulty value was
572 * returned; but with this hack, solving an Easy puzzle on
573 * Normal difficulty will typically return Normal. Hence the
574 * uses of the solver to determine difficulty are all arranged
575 * so as to double-check by re-solving at the next difficulty
576 * level down and making sure it failed.
577 */
578 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
579 if (ctx->diff > DIFF_EASY)
580 return 0;
581 return solver_common(solver, vctx, DIFF_EASY);
582}
583
584static int solver_normal(struct latin_solver *solver, void *vctx)
585{
586 return solver_common(solver, vctx, DIFF_NORMAL);
587}
588
589static int solver_hard(struct latin_solver *solver, void *vctx)
590{
591 return solver_common(solver, vctx, DIFF_HARD);
592}
593
594#define SOLVER(upper,title,func,lower) func,
595static usersolver_t const keen_solvers[] = { DIFFLIST(SOLVER) };
596
597static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff)
598{
599 int a = w*w;
600 struct solver_ctx ctx;
601 int ret;
602 int i, j, n, m;
603
604 ctx.w = w;
605 ctx.soln = soln;
606 ctx.diff = maxdiff;
607
608 /*
609 * Transform the dsf-formatted clue list into one over which we
610 * can iterate more easily.
611 *
612 * Also transpose the x- and y-coordinates at this point,
613 * because the 'cube' array in the general Latin square solver
614 * puts x first (oops).
615 */
616 for (ctx.nboxes = i = 0; i < a; i++)
617 if (dsf_canonify(dsf, i) == i)
618 ctx.nboxes++;
619 ctx.boxlist = snewn(a, int);
620 ctx.boxes = snewn(ctx.nboxes+1, int);
621 ctx.clues = snewn(ctx.nboxes, long);
622 ctx.whichbox = snewn(a, int);
623 for (n = m = i = 0; i < a; i++)
624 if (dsf_canonify(dsf, i) == i) {
625 ctx.clues[n] = clues[i];
626 ctx.boxes[n] = m;
627 for (j = 0; j < a; j++)
628 if (dsf_canonify(dsf, j) == i) {
629 ctx.boxlist[m++] = (j % w) * w + (j / w); /* transpose */
630 ctx.whichbox[ctx.boxlist[m-1]] = n;
631 }
632 n++;
633 }
634 assert(n == ctx.nboxes);
635 assert(m == a);
636 ctx.boxes[n] = m;
637
638 ctx.dscratch = snewn(a+1, digit);
639 ctx.iscratch = snewn(max(a+1, 4*w), int);
640
641 ret = latin_solver(soln, w, maxdiff,
642 DIFF_EASY, DIFF_HARD, DIFF_EXTREME,
643 DIFF_EXTREME, DIFF_UNREASONABLE,
644 keen_solvers, &ctx, NULL, NULL);
645
646 sfree(ctx.dscratch);
647 sfree(ctx.iscratch);
648 sfree(ctx.whichbox);
649 sfree(ctx.boxlist);
650 sfree(ctx.boxes);
651 sfree(ctx.clues);
652
653 return ret;
654}
655
656/* ----------------------------------------------------------------------
657 * Grid generation.
658 */
659
660static char *encode_block_structure(char *p, int w, int *dsf)
661{
662 int i, currrun = 0;
663 char *orig, *q, *r, c;
664
665 orig = p;
666
667 /*
668 * Encode the block structure. We do this by encoding the
669 * pattern of dividing lines: first we iterate over the w*(w-1)
670 * internal vertical grid lines in ordinary reading order, then
671 * over the w*(w-1) internal horizontal ones in transposed
672 * reading order.
673 *
674 * We encode the number of non-lines between the lines; _ means
675 * zero (two adjacent divisions), a means 1, ..., y means 25,
676 * and z means 25 non-lines _and no following line_ (so that za
677 * means 26, zb 27 etc).
678 */
679 for (i = 0; i <= 2*w*(w-1); i++) {
680 int x, y, p0, p1, edge;
681
682 if (i == 2*w*(w-1)) {
683 edge = TRUE; /* terminating virtual edge */
684 } else {
685 if (i < w*(w-1)) {
686 y = i/(w-1);
687 x = i%(w-1);
688 p0 = y*w+x;
689 p1 = y*w+x+1;
690 } else {
691 x = i/(w-1) - w;
692 y = i%(w-1);
693 p0 = y*w+x;
694 p1 = (y+1)*w+x;
695 }
696 edge = (dsf_canonify(dsf, p0) != dsf_canonify(dsf, p1));
697 }
698
699 if (edge) {
700 while (currrun > 25)
701 *p++ = 'z', currrun -= 25;
702 if (currrun)
703 *p++ = 'a'-1 + currrun;
704 else
705 *p++ = '_';
706 currrun = 0;
707 } else
708 currrun++;
709 }
710
711 /*
712 * Now go through and compress the string by replacing runs of
713 * the same letter with a single copy of that letter followed by
714 * a repeat count, where that makes it shorter. (This puzzle
715 * seems to generate enough long strings of _ to make this a
716 * worthwhile step.)
717 */
718 for (q = r = orig; r < p ;) {
719 *q++ = c = *r;
720
721 for (i = 0; r+i < p && r[i] == c; i++);
722 r += i;
723
724 if (i == 2) {
725 *q++ = c;
726 } else if (i > 2) {
727 q += sprintf(q, "%d", i);
728 }
729 }
730
731 return q;
732}
733
734static char *parse_block_structure(const char **p, int w, int *dsf)
735{
736 int a = w*w;
737 int pos = 0;
738 int repc = 0, repn = 0;
739
740 dsf_init(dsf, a);
741
742 while (**p && (repn > 0 || **p != ',')) {
743 int c, adv;
744
745 if (repn > 0) {
746 repn--;
747 c = repc;
748 } else if (**p == '_' || (**p >= 'a' && **p <= 'z')) {
749 c = (**p == '_' ? 0 : **p - 'a' + 1);
750 (*p)++;
751 if (**p && isdigit((unsigned char)**p)) {
752 repc = c;
753 repn = atoi(*p)-1;
754 while (**p && isdigit((unsigned char)**p)) (*p)++;
755 }
756 } else
757 return "Invalid character in game description";
758
759 adv = (c != 25); /* 'z' is a special case */
760
761 while (c-- > 0) {
762 int p0, p1;
763
764 /*
765 * Non-edge; merge the two dsf classes on either
766 * side of it.
767 */
768 if (pos >= 2*w*(w-1))
769 return "Too much data in block structure specification";
770 if (pos < w*(w-1)) {
771 int y = pos/(w-1);
772 int x = pos%(w-1);
773 p0 = y*w+x;
774 p1 = y*w+x+1;
775 } else {
776 int x = pos/(w-1) - w;
777 int y = pos%(w-1);
778 p0 = y*w+x;
779 p1 = (y+1)*w+x;
780 }
781 dsf_merge(dsf, p0, p1);
782
783 pos++;
784 }
785 if (adv) {
786 pos++;
787 if (pos > 2*w*(w-1)+1)
788 return "Too much data in block structure specification";
789 }
790 }
791
792 /*
793 * When desc is exhausted, we expect to have gone exactly
794 * one space _past_ the end of the grid, due to the dummy
795 * edge at the end.
796 */
797 if (pos != 2*w*(w-1)+1)
798 return "Not enough data in block structure specification";
799
800 return NULL;
801}
802
803static char *new_game_desc(const game_params *params, random_state *rs,
804 char **aux, int interactive)
805{
806 int w = params->w, a = w*w;
807 digit *grid, *soln;
808 int *order, *revorder, *singletons, *dsf;
809 long *clues, *cluevals;
810 int i, j, k, n, x, y, ret;
811 int diff = params->diff;
812 char *desc, *p;
813
814 /*
815 * Difficulty exceptions: 3x3 puzzles at difficulty Hard or
816 * higher are currently not generable - the generator will spin
817 * forever looking for puzzles of the appropriate difficulty. We
818 * dial each of these down to the next lower difficulty.
819 *
820 * Remember to re-test this whenever a change is made to the
821 * solver logic!
822 *
823 * I tested it using the following shell command:
824
825for d in e n h x u; do
826 for i in {3..9}; do
827 echo ./keen --generate 1 ${i}d${d}
828 perl -e 'alarm 30; exec @ARGV' ./keen --generate 5 ${i}d${d} >/dev/null \
829 || echo broken
830 done
831done
832
833 * Of course, it's better to do that after taking the exceptions
834 * _out_, so as to detect exceptions that should be removed as
835 * well as those which should be added.
836 */
837 if (w == 3 && diff > DIFF_NORMAL)
838 diff = DIFF_NORMAL;
839
840 grid = NULL;
841
842 order = snewn(a, int);
843 revorder = snewn(a, int);
844 singletons = snewn(a, int);
845 dsf = snew_dsf(a);
846 clues = snewn(a, long);
847 cluevals = snewn(a, long);
848 soln = snewn(a, digit);
849
850 while (1) {
851 /*
852 * First construct a latin square to be the solution.
853 */
854 sfree(grid);
855 grid = latin_generate(w, rs);
856
857 /*
858 * Divide the grid into arbitrarily sized blocks, but so as
859 * to arrange plenty of dominoes which can be SUB/DIV clues.
860 * We do this by first placing dominoes at random for a
861 * while, then tying the remaining singletons one by one
862 * into neighbouring blocks.
863 */
864 for (i = 0; i < a; i++)
865 order[i] = i;
866 shuffle(order, a, sizeof(*order), rs);
867 for (i = 0; i < a; i++)
868 revorder[order[i]] = i;
869
870 for (i = 0; i < a; i++)
871 singletons[i] = TRUE;
872
873 dsf_init(dsf, a);
874
875 /* Place dominoes. */
876 for (i = 0; i < a; i++) {
877 if (singletons[i]) {
878 int best = -1;
879
880 x = i % w;
881 y = i / w;
882
883 if (x > 0 && singletons[i-1] &&
884 (best == -1 || revorder[i-1] < revorder[best]))
885 best = i-1;
886 if (x+1 < w && singletons[i+1] &&
887 (best == -1 || revorder[i+1] < revorder[best]))
888 best = i+1;
889 if (y > 0 && singletons[i-w] &&
890 (best == -1 || revorder[i-w] < revorder[best]))
891 best = i-w;
892 if (y+1 < w && singletons[i+w] &&
893 (best == -1 || revorder[i+w] < revorder[best]))
894 best = i+w;
895
896 /*
897 * When we find a potential domino, we place it with
898 * probability 3/4, which seems to strike a decent
899 * balance between plenty of dominoes and leaving
900 * enough singletons to make interesting larger
901 * shapes.
902 */
903 if (best >= 0 && random_upto(rs, 4)) {
904 singletons[i] = singletons[best] = FALSE;
905 dsf_merge(dsf, i, best);
906 }
907 }
908 }
909
910 /* Fold in singletons. */
911 for (i = 0; i < a; i++) {
912 if (singletons[i]) {
913 int best = -1;
914
915 x = i % w;
916 y = i / w;
917
918 if (x > 0 && dsf_size(dsf, i-1) < MAXBLK &&
919 (best == -1 || revorder[i-1] < revorder[best]))
920 best = i-1;
921 if (x+1 < w && dsf_size(dsf, i+1) < MAXBLK &&
922 (best == -1 || revorder[i+1] < revorder[best]))
923 best = i+1;
924 if (y > 0 && dsf_size(dsf, i-w) < MAXBLK &&
925 (best == -1 || revorder[i-w] < revorder[best]))
926 best = i-w;
927 if (y+1 < w && dsf_size(dsf, i+w) < MAXBLK &&
928 (best == -1 || revorder[i+w] < revorder[best]))
929 best = i+w;
930
931 if (best >= 0) {
932 singletons[i] = singletons[best] = FALSE;
933 dsf_merge(dsf, i, best);
934 }
935 }
936 }
937
938 /* Quit and start again if we have any singletons left over
939 * which we weren't able to do anything at all with. */
940 for (i = 0; i < a; i++)
941 if (singletons[i])
942 break;
943 if (i < a)
944 continue;
945
946 /*
947 * Decide what would be acceptable clues for each block.
948 *
949 * Blocks larger than 2 have free choice of ADD or MUL;
950 * blocks of size 2 can be anything in principle (except
951 * that they can only be DIV if the two numbers have an
952 * integer quotient, of course), but we rule out (or try to
953 * avoid) some clues because they're of low quality.
954 *
955 * Hence, we iterate once over the grid, stopping at the
956 * canonical element of every >2 block and the _non_-
957 * canonical element of every 2-block; the latter means that
958 * we can make our decision about a 2-block in the knowledge
959 * of both numbers in it.
960 *
961 * We reuse the 'singletons' array (finished with in the
962 * above loop) to hold information about which blocks are
963 * suitable for what.
964 */
965#define F_ADD 0x01
966#define F_SUB 0x02
967#define F_MUL 0x04
968#define F_DIV 0x08
969#define BAD_SHIFT 4
970
971 for (i = 0; i < a; i++) {
972 singletons[i] = 0;
973 j = dsf_canonify(dsf, i);
974 k = dsf_size(dsf, j);
975 if (params->multiplication_only)
976 singletons[j] = F_MUL;
977 else if (j == i && k > 2) {
978 singletons[j] |= F_ADD | F_MUL;
979 } else if (j != i && k == 2) {
980 /* Fetch the two numbers and sort them into order. */
981 int p = grid[j], q = grid[i], v;
982 if (p < q) {
983 int t = p; p = q; q = t;
984 }
985
986 /*
987 * Addition clues are always allowed, but we try to
988 * avoid sums of 3, 4, (2w-1) and (2w-2) if we can,
989 * because they're too easy - they only leave one
990 * option for the pair of numbers involved.
991 */
992 v = p + q;
993 if (v > 4 && v < 2*w-2)
994 singletons[j] |= F_ADD;
995 else
996 singletons[j] |= F_ADD << BAD_SHIFT;
997
998 /*
999 * Multiplication clues: above Normal difficulty, we
1000 * prefer (but don't absolutely insist on) clues of
1001 * this type which leave multiple options open.
1002 */
1003 v = p * q;
1004 n = 0;
1005 for (k = 1; k <= w; k++)
1006 if (v % k == 0 && v / k <= w && v / k != k)
1007 n++;
1008 if (n <= 2 && diff > DIFF_NORMAL)
1009 singletons[j] |= F_MUL << BAD_SHIFT;
1010 else
1011 singletons[j] |= F_MUL;
1012
1013 /*
1014 * Subtraction: we completely avoid a difference of
1015 * w-1.
1016 */
1017 v = p - q;
1018 if (v < w-1)
1019 singletons[j] |= F_SUB;
1020
1021 /*
1022 * Division: for a start, the quotient must be an
1023 * integer or the clue type is impossible. Also, we
1024 * never use quotients strictly greater than w/2,
1025 * because they're not only too easy but also
1026 * inelegant.
1027 */
1028 if (p % q == 0 && 2 * (p / q) <= w)
1029 singletons[j] |= F_DIV;
1030 }
1031 }
1032
1033 /*
1034 * Actually choose a clue for each block, trying to keep the
1035 * numbers of each type even, and starting with the
1036 * preferred candidates for each type where possible.
1037 *
1038 * I'm sure there should be a faster algorithm for doing
1039 * this, but I can't be bothered: O(N^2) is good enough when
1040 * N is at most the number of dominoes that fits into a 9x9
1041 * square.
1042 */
1043 shuffle(order, a, sizeof(*order), rs);
1044 for (i = 0; i < a; i++)
1045 clues[i] = 0;
1046 while (1) {
1047 int done_something = FALSE;
1048
1049 for (k = 0; k < 4; k++) {
1050 long clue;
1051 int good, bad;
1052 switch (k) {
1053 case 0: clue = C_DIV; good = F_DIV; break;
1054 case 1: clue = C_SUB; good = F_SUB; break;
1055 case 2: clue = C_MUL; good = F_MUL; break;
1056 default /* case 3 */ : clue = C_ADD; good = F_ADD; break;
1057 }
1058
1059 for (i = 0; i < a; i++) {
1060 j = order[i];
1061 if (singletons[j] & good) {
1062 clues[j] = clue;
1063 singletons[j] = 0;
1064 break;
1065 }
1066 }
1067 if (i == a) {
1068 /* didn't find a nice one, use a nasty one */
1069 bad = good << BAD_SHIFT;
1070 for (i = 0; i < a; i++) {
1071 j = order[i];
1072 if (singletons[j] & bad) {
1073 clues[j] = clue;
1074 singletons[j] = 0;
1075 break;
1076 }
1077 }
1078 }
1079 if (i < a)
1080 done_something = TRUE;
1081 }
1082
1083 if (!done_something)
1084 break;
1085 }
1086#undef F_ADD
1087#undef F_SUB
1088#undef F_MUL
1089#undef F_DIV
1090#undef BAD_SHIFT
1091
1092 /*
1093 * Having chosen the clue types, calculate the clue values.
1094 */
1095 for (i = 0; i < a; i++) {
1096 j = dsf_canonify(dsf, i);
1097 if (j == i) {
1098 cluevals[j] = grid[i];
1099 } else {
1100 switch (clues[j]) {
1101 case C_ADD:
1102 cluevals[j] += grid[i];
1103 break;
1104 case C_MUL:
1105 cluevals[j] *= grid[i];
1106 break;
1107 case C_SUB:
1108 cluevals[j] = abs(cluevals[j] - grid[i]);
1109 break;
1110 case C_DIV:
1111 {
1112 int d1 = cluevals[j], d2 = grid[i];
1113 if (d1 == 0 || d2 == 0)
1114 cluevals[j] = 0;
1115 else
1116 cluevals[j] = d2/d1 + d1/d2;/* one is 0 :-) */
1117 }
1118 break;
1119 }
1120 }
1121 }
1122
1123 for (i = 0; i < a; i++) {
1124 j = dsf_canonify(dsf, i);
1125 if (j == i) {
1126 clues[j] |= cluevals[j];
1127 }
1128 }
1129
1130 /*
1131 * See if the game can be solved at the specified difficulty
1132 * level, but not at the one below.
1133 */
1134 if (diff > 0) {
1135 memset(soln, 0, a);
1136 ret = solver(w, dsf, clues, soln, diff-1);
1137 if (ret <= diff-1)
1138 continue;
1139 }
1140 memset(soln, 0, a);
1141 ret = solver(w, dsf, clues, soln, diff);
1142 if (ret != diff)
1143 continue; /* go round again */
1144
1145 /*
1146 * I wondered if at this point it would be worth trying to
1147 * merge adjacent blocks together, to make the puzzle
1148 * gradually more difficult if it's currently easier than
1149 * specced, increasing the chance of a given generation run
1150 * being successful.
1151 *
1152 * It doesn't seem to be critical for the generation speed,
1153 * though, so for the moment I'm leaving it out.
1154 */
1155
1156 /*
1157 * We've got a usable puzzle!
1158 */
1159 break;
1160 }
1161
1162 /*
1163 * Encode the puzzle description.
1164 */
1165 desc = snewn(40*a, char);
1166 p = desc;
1167 p = encode_block_structure(p, w, dsf);
1168 *p++ = ',';
1169 for (i = 0; i < a; i++) {
1170 j = dsf_canonify(dsf, i);
1171 if (j == i) {
1172 switch (clues[j] & CMASK) {
1173 case C_ADD: *p++ = 'a'; break;
1174 case C_SUB: *p++ = 's'; break;
1175 case C_MUL: *p++ = 'm'; break;
1176 case C_DIV: *p++ = 'd'; break;
1177 }
1178 p += sprintf(p, "%ld", clues[j] & ~CMASK);
1179 }
1180 }
1181 *p++ = '\0';
1182 desc = sresize(desc, p - desc, char);
1183
1184 /*
1185 * Encode the solution.
1186 */
1187 assert(memcmp(soln, grid, a) == 0);
1188 *aux = snewn(a+2, char);
1189 (*aux)[0] = 'S';
1190 for (i = 0; i < a; i++)
1191 (*aux)[i+1] = '0' + soln[i];
1192 (*aux)[a+1] = '\0';
1193
1194 sfree(grid);
1195 sfree(order);
1196 sfree(revorder);
1197 sfree(singletons);
1198 sfree(dsf);
1199 sfree(clues);
1200 sfree(cluevals);
1201 sfree(soln);
1202
1203 return desc;
1204}
1205
1206/* ----------------------------------------------------------------------
1207 * Gameplay.
1208 */
1209
1210static char *validate_desc(const game_params *params, const char *desc)
1211{
1212 int w = params->w, a = w*w;
1213 int *dsf;
1214 char *ret;
1215 const char *p = desc;
1216 int i;
1217
1218 /*
1219 * Verify that the block structure makes sense.
1220 */
1221 dsf = snew_dsf(a);
1222 ret = parse_block_structure(&p, w, dsf);
1223 if (ret) {
1224 sfree(dsf);
1225 return ret;
1226 }
1227
1228 if (*p != ',')
1229 return "Expected ',' after block structure description";
1230 p++;
1231
1232 /*
1233 * Verify that the right number of clues are given, and that SUB
1234 * and DIV clues don't apply to blocks of the wrong size.
1235 */
1236 for (i = 0; i < a; i++) {
1237 if (dsf_canonify(dsf, i) == i) {
1238 if (*p == 'a' || *p == 'm') {
1239 /* these clues need no validation */
1240 } else if (*p == 'd' || *p == 's') {
1241 if (dsf_size(dsf, i) != 2)
1242 return "Subtraction and division blocks must have area 2";
1243 } else if (!*p) {
1244 return "Too few clues for block structure";
1245 } else {
1246 return "Unrecognised clue type";
1247 }
1248 p++;
1249 while (*p && isdigit((unsigned char)*p)) p++;
1250 }
1251 }
1252 if (*p)
1253 return "Too many clues for block structure";
1254
1255 return NULL;
1256}
1257
1258static game_state *new_game(midend *me, const game_params *params,
1259 const char *desc)
1260{
1261 int w = params->w, a = w*w;
1262 game_state *state = snew(game_state);
1263 const char *p = desc;
1264 int i;
1265
1266 state->par = *params; /* structure copy */
1267 state->clues = snew(struct clues);
1268 state->clues->refcount = 1;
1269 state->clues->w = w;
1270 state->clues->dsf = snew_dsf(a);
1271 parse_block_structure(&p, w, state->clues->dsf);
1272
1273 assert(*p == ',');
1274 p++;
1275
1276 state->clues->clues = snewn(a, long);
1277 for (i = 0; i < a; i++) {
1278 if (dsf_canonify(state->clues->dsf, i) == i) {
1279 long clue = 0;
1280 switch (*p) {
1281 case 'a':
1282 clue = C_ADD;
1283 break;
1284 case 'm':
1285 clue = C_MUL;
1286 break;
1287 case 's':
1288 clue = C_SUB;
1289 assert(dsf_size(state->clues->dsf, i) == 2);
1290 break;
1291 case 'd':
1292 clue = C_DIV;
1293 assert(dsf_size(state->clues->dsf, i) == 2);
1294 break;
1295 default:
1296 assert(!"Bad description in new_game");
1297 }
1298 p++;
1299 clue |= atol(p);
1300 while (*p && isdigit((unsigned char)*p)) p++;
1301 state->clues->clues[i] = clue;
1302 } else
1303 state->clues->clues[i] = 0;
1304 }
1305
1306 state->grid = snewn(a, digit);
1307 state->pencil = snewn(a, int);
1308 for (i = 0; i < a; i++) {
1309 state->grid[i] = 0;
1310 state->pencil[i] = 0;
1311 }
1312
1313 state->completed = state->cheated = FALSE;
1314
1315 return state;
1316}
1317
1318static game_state *dup_game(const game_state *state)
1319{
1320 int w = state->par.w, a = w*w;
1321 game_state *ret = snew(game_state);
1322
1323 ret->par = state->par; /* structure copy */
1324
1325 ret->clues = state->clues;
1326 ret->clues->refcount++;
1327
1328 ret->grid = snewn(a, digit);
1329 ret->pencil = snewn(a, int);
1330 memcpy(ret->grid, state->grid, a*sizeof(digit));
1331 memcpy(ret->pencil, state->pencil, a*sizeof(int));
1332
1333 ret->completed = state->completed;
1334 ret->cheated = state->cheated;
1335
1336 return ret;
1337}
1338
1339static void free_game(game_state *state)
1340{
1341 sfree(state->grid);
1342 sfree(state->pencil);
1343 if (--state->clues->refcount <= 0) {
1344 sfree(state->clues->dsf);
1345 sfree(state->clues->clues);
1346 sfree(state->clues);
1347 }
1348 sfree(state);
1349}
1350
1351static char *solve_game(const game_state *state, const game_state *currstate,
1352 const char *aux, char **error)
1353{
1354 int w = state->par.w, a = w*w;
1355 int i, ret;
1356 digit *soln;
1357 char *out;
1358
1359 if (aux)
1360 return dupstr(aux);
1361
1362 soln = snewn(a, digit);
1363 memset(soln, 0, a);
1364
1365 ret = solver(w, state->clues->dsf, state->clues->clues,
1366 soln, DIFFCOUNT-1);
1367
1368 if (ret == diff_impossible) {
1369 *error = "No solution exists for this puzzle";
1370 out = NULL;
1371 } else if (ret == diff_ambiguous) {
1372 *error = "Multiple solutions exist for this puzzle";
1373 out = NULL;
1374 } else {
1375 out = snewn(a+2, char);
1376 out[0] = 'S';
1377 for (i = 0; i < a; i++)
1378 out[i+1] = '0' + soln[i];
1379 out[a+1] = '\0';
1380 }
1381
1382 sfree(soln);
1383 return out;
1384}
1385
1386static int game_can_format_as_text_now(const game_params *params)
1387{
1388 return TRUE;
1389}
1390
1391static char *game_text_format(const game_state *state)
1392{
1393 return NULL;
1394}
1395
1396struct game_ui {
1397 /*
1398 * These are the coordinates of the currently highlighted
1399 * square on the grid, if hshow = 1.
1400 */
1401 int hx, hy;
1402 /*
1403 * This indicates whether the current highlight is a
1404 * pencil-mark one or a real one.
1405 */
1406 int hpencil;
1407 /*
1408 * This indicates whether or not we're showing the highlight
1409 * (used to be hx = hy = -1); important so that when we're
1410 * using the cursor keys it doesn't keep coming back at a
1411 * fixed position. When hshow = 1, pressing a valid number
1412 * or letter key or Space will enter that number or letter in the grid.
1413 */
1414 int hshow;
1415 /*
1416 * This indicates whether we're using the highlight as a cursor;
1417 * it means that it doesn't vanish on a keypress, and that it is
1418 * allowed on immutable squares.
1419 */
1420 int hcursor;
1421};
1422
1423static game_ui *new_ui(const game_state *state)
1424{
1425 game_ui *ui = snew(game_ui);
1426
1427 ui->hx = ui->hy = 0;
1428 ui->hpencil = ui->hshow = ui->hcursor = 0;
1429
1430 return ui;
1431}
1432
1433static void free_ui(game_ui *ui)
1434{
1435 sfree(ui);
1436}
1437
1438static char *encode_ui(const game_ui *ui)
1439{
1440 return NULL;
1441}
1442
1443static void decode_ui(game_ui *ui, const char *encoding)
1444{
1445}
1446
1447static void game_changed_state(game_ui *ui, const game_state *oldstate,
1448 const game_state *newstate)
1449{
1450 int w = newstate->par.w;
1451 /*
1452 * We prevent pencil-mode highlighting of a filled square, unless
1453 * we're using the cursor keys. So if the user has just filled in
1454 * a square which we had a pencil-mode highlight in (by Undo, or
1455 * by Redo, or by Solve), then we cancel the highlight.
1456 */
1457 if (ui->hshow && ui->hpencil && !ui->hcursor &&
1458 newstate->grid[ui->hy * w + ui->hx] != 0) {
1459 ui->hshow = 0;
1460 }
1461}
1462
1463#define PREFERRED_TILESIZE 48
1464#define TILESIZE (ds->tilesize)
1465#define BORDER (TILESIZE / 2)
1466#define GRIDEXTRA max((TILESIZE / 32),1)
1467#define COORD(x) ((x)*TILESIZE + BORDER)
1468#define FROMCOORD(x) (((x)+(TILESIZE-BORDER)) / TILESIZE - 1)
1469
1470#define FLASH_TIME 0.4F
1471
1472#define DF_PENCIL_SHIFT 16
1473#define DF_ERR_LATIN 0x8000
1474#define DF_ERR_CLUE 0x4000
1475#define DF_HIGHLIGHT 0x2000
1476#define DF_HIGHLIGHT_PENCIL 0x1000
1477#define DF_DIGIT_MASK 0x000F
1478
1479struct game_drawstate {
1480 int tilesize;
1481 int started;
1482 long *tiles;
1483 long *errors;
1484 char *minus_sign, *times_sign, *divide_sign;
1485};
1486
1487static int check_errors(const game_state *state, long *errors)
1488{
1489 int w = state->par.w, a = w*w;
1490 int i, j, x, y, errs = FALSE;
1491 long *cluevals;
1492 int *full;
1493
1494 cluevals = snewn(a, long);
1495 full = snewn(a, int);
1496
1497 if (errors)
1498 for (i = 0; i < a; i++) {
1499 errors[i] = 0;
1500 full[i] = TRUE;
1501 }
1502
1503 for (i = 0; i < a; i++) {
1504 long clue;
1505
1506 j = dsf_canonify(state->clues->dsf, i);
1507 if (j == i) {
1508 cluevals[i] = state->grid[i];
1509 } else {
1510 clue = state->clues->clues[j] & CMASK;
1511
1512 switch (clue) {
1513 case C_ADD:
1514 cluevals[j] += state->grid[i];
1515 break;
1516 case C_MUL:
1517 cluevals[j] *= state->grid[i];
1518 break;
1519 case C_SUB:
1520 cluevals[j] = abs(cluevals[j] - state->grid[i]);
1521 break;
1522 case C_DIV:
1523 {
1524 int d1 = min(cluevals[j], state->grid[i]);
1525 int d2 = max(cluevals[j], state->grid[i]);
1526 if (d1 == 0 || d2 % d1 != 0)
1527 cluevals[j] = 0;
1528 else
1529 cluevals[j] = d2 / d1;
1530 }
1531 break;
1532 }
1533 }
1534
1535 if (!state->grid[i])
1536 full[j] = FALSE;
1537 }
1538
1539 for (i = 0; i < a; i++) {
1540 j = dsf_canonify(state->clues->dsf, i);
1541 if (j == i) {
1542 if ((state->clues->clues[j] & ~CMASK) != cluevals[i]) {
1543 errs = TRUE;
1544 if (errors && full[j])
1545 errors[j] |= DF_ERR_CLUE;
1546 }
1547 }
1548 }
1549
1550 sfree(cluevals);
1551 sfree(full);
1552
1553 for (y = 0; y < w; y++) {
1554 int mask = 0, errmask = 0;
1555 for (x = 0; x < w; x++) {
1556 int bit = 1 << state->grid[y*w+x];
1557 errmask |= (mask & bit);
1558 mask |= bit;
1559 }
1560
1561 if (mask != (1 << (w+1)) - (1 << 1)) {
1562 errs = TRUE;
1563 errmask &= ~1;
1564 if (errors) {
1565 for (x = 0; x < w; x++)
1566 if (errmask & (1 << state->grid[y*w+x]))
1567 errors[y*w+x] |= DF_ERR_LATIN;
1568 }
1569 }
1570 }
1571
1572 for (x = 0; x < w; x++) {
1573 int mask = 0, errmask = 0;
1574 for (y = 0; y < w; y++) {
1575 int bit = 1 << state->grid[y*w+x];
1576 errmask |= (mask & bit);
1577 mask |= bit;
1578 }
1579
1580 if (mask != (1 << (w+1)) - (1 << 1)) {
1581 errs = TRUE;
1582 errmask &= ~1;
1583 if (errors) {
1584 for (y = 0; y < w; y++)
1585 if (errmask & (1 << state->grid[y*w+x]))
1586 errors[y*w+x] |= DF_ERR_LATIN;
1587 }
1588 }
1589 }
1590
1591 return errs;
1592}
1593
1594static char *interpret_move(const game_state *state, game_ui *ui,
1595 const game_drawstate *ds,
1596 int x, int y, int button)
1597{
1598 int w = state->par.w;
1599 int tx, ty;
1600 char buf[80];
1601
1602 button &= ~MOD_MASK;
1603
1604 tx = FROMCOORD(x);
1605 ty = FROMCOORD(y);
1606
1607 if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
1608 if (button == LEFT_BUTTON) {
1609 if (tx == ui->hx && ty == ui->hy &&
1610 ui->hshow && ui->hpencil == 0) {
1611 ui->hshow = 0;
1612 } else {
1613 ui->hx = tx;
1614 ui->hy = ty;
1615 ui->hshow = 1;
1616 ui->hpencil = 0;
1617 }
1618 ui->hcursor = 0;
1619 return ""; /* UI activity occurred */
1620 }
1621 if (button == RIGHT_BUTTON) {
1622 /*
1623 * Pencil-mode highlighting for non filled squares.
1624 */
1625 if (state->grid[ty*w+tx] == 0) {
1626 if (tx == ui->hx && ty == ui->hy &&
1627 ui->hshow && ui->hpencil) {
1628 ui->hshow = 0;
1629 } else {
1630 ui->hpencil = 1;
1631 ui->hx = tx;
1632 ui->hy = ty;
1633 ui->hshow = 1;
1634 }
1635 } else {
1636 ui->hshow = 0;
1637 }
1638 ui->hcursor = 0;
1639 return ""; /* UI activity occurred */
1640 }
1641 }
1642 if (IS_CURSOR_MOVE(button)) {
1643 move_cursor(button, &ui->hx, &ui->hy, w, w, 0);
1644 ui->hshow = ui->hcursor = 1;
1645 return "";
1646 }
1647 if (ui->hshow &&
1648 (button == CURSOR_SELECT)) {
1649 ui->hpencil = 1 - ui->hpencil;
1650 ui->hcursor = 1;
1651 return "";
1652 }
1653
1654 if (ui->hshow &&
1655 ((button >= '0' && button <= '9' && button - '0' <= w) ||
1656 button == CURSOR_SELECT2 || button == '\b')) {
1657 int n = button - '0';
1658 if (button == CURSOR_SELECT2 || button == '\b')
1659 n = 0;
1660
1661 /*
1662 * Can't make pencil marks in a filled square. This can only
1663 * become highlighted if we're using cursor keys.
1664 */
1665 if (ui->hpencil && state->grid[ui->hy*w+ui->hx])
1666 return NULL;
1667
1668 sprintf(buf, "%c%d,%d,%d",
1669 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1670
1671 if (!ui->hcursor) ui->hshow = 0;
1672
1673 return dupstr(buf);
1674 }
1675
1676 if (button == 'M' || button == 'm')
1677 return dupstr("M");
1678
1679 return NULL;
1680}
1681
1682static game_state *execute_move(const game_state *from, const char *move)
1683{
1684 int w = from->par.w, a = w*w;
1685 game_state *ret;
1686 int x, y, i, n;
1687
1688 if (move[0] == 'S') {
1689 ret = dup_game(from);
1690 ret->completed = ret->cheated = TRUE;
1691
1692 for (i = 0; i < a; i++) {
1693 if (move[i+1] < '1' || move[i+1] > '0'+w) {
1694 free_game(ret);
1695 return NULL;
1696 }
1697 ret->grid[i] = move[i+1] - '0';
1698 ret->pencil[i] = 0;
1699 }
1700
1701 if (move[a+1] != '\0') {
1702 free_game(ret);
1703 return NULL;
1704 }
1705
1706 return ret;
1707 } else if ((move[0] == 'P' || move[0] == 'R') &&
1708 sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
1709 x >= 0 && x < w && y >= 0 && y < w && n >= 0 && n <= w) {
1710
1711 ret = dup_game(from);
1712 if (move[0] == 'P' && n > 0) {
1713 ret->pencil[y*w+x] ^= 1 << n;
1714 } else {
1715 ret->grid[y*w+x] = n;
1716 ret->pencil[y*w+x] = 0;
1717
1718 if (!ret->completed && !check_errors(ret, NULL))
1719 ret->completed = TRUE;
1720 }
1721 return ret;
1722 } else if (move[0] == 'M') {
1723 /*
1724 * Fill in absolutely all pencil marks everywhere. (I
1725 * wouldn't use this for actual play, but it's a handy
1726 * starting point when following through a set of
1727 * diagnostics output by the standalone solver.)
1728 */
1729 ret = dup_game(from);
1730 for (i = 0; i < a; i++) {
1731 if (!ret->grid[i])
1732 ret->pencil[i] = (1 << (w+1)) - (1 << 1);
1733 }
1734 return ret;
1735 } else
1736 return NULL; /* couldn't parse move string */
1737}
1738
1739/* ----------------------------------------------------------------------
1740 * Drawing routines.
1741 */
1742
1743#define SIZE(w) ((w) * TILESIZE + 2*BORDER)
1744
1745static void game_compute_size(const game_params *params, int tilesize,
1746 int *x, int *y)
1747{
1748 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1749 struct { int tilesize; } ads, *ds = &ads;
1750 ads.tilesize = tilesize;
1751
1752 *x = *y = SIZE(params->w);
1753}
1754
1755static void game_set_size(drawing *dr, game_drawstate *ds,
1756 const game_params *params, int tilesize)
1757{
1758 ds->tilesize = tilesize;
1759}
1760
1761static float *game_colours(frontend *fe, int *ncolours)
1762{
1763 float *ret = snewn(3 * NCOLOURS, float);
1764
1765 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1766
1767 ret[COL_GRID * 3 + 0] = 0.0F;
1768 ret[COL_GRID * 3 + 1] = 0.0F;
1769 ret[COL_GRID * 3 + 2] = 0.0F;
1770
1771 ret[COL_USER * 3 + 0] = 0.0F;
1772 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1773 ret[COL_USER * 3 + 2] = 0.0F;
1774
1775 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
1776 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
1777 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
1778
1779 ret[COL_ERROR * 3 + 0] = 1.0F;
1780 ret[COL_ERROR * 3 + 1] = 0.0F;
1781 ret[COL_ERROR * 3 + 2] = 0.0F;
1782
1783 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1784 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1785 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1786
1787 *ncolours = NCOLOURS;
1788 return ret;
1789}
1790
1791static const char *const minus_signs[] = { "\xE2\x88\x92", "-" };
1792static const char *const times_signs[] = { "\xC3\x97", "*" };
1793static const char *const divide_signs[] = { "\xC3\xB7", "/" };
1794
1795static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1796{
1797 int w = state->par.w, a = w*w;
1798 struct game_drawstate *ds = snew(struct game_drawstate);
1799 int i;
1800
1801 ds->tilesize = 0;
1802 ds->started = FALSE;
1803 ds->tiles = snewn(a, long);
1804 for (i = 0; i < a; i++)
1805 ds->tiles[i] = -1;
1806 ds->errors = snewn(a, long);
1807 ds->minus_sign = text_fallback(dr, minus_signs, lenof(minus_signs));
1808 ds->times_sign = text_fallback(dr, times_signs, lenof(times_signs));
1809 ds->divide_sign = text_fallback(dr, divide_signs, lenof(divide_signs));
1810
1811 return ds;
1812}
1813
1814static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1815{
1816 sfree(ds->tiles);
1817 sfree(ds->errors);
1818 sfree(ds->minus_sign);
1819 sfree(ds->times_sign);
1820 sfree(ds->divide_sign);
1821 sfree(ds);
1822}
1823
1824static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1825 int x, int y, long tile, int only_one_op)
1826{
1827 int w = clues->w /* , a = w*w */;
1828 int tx, ty, tw, th;
1829 int cx, cy, cw, ch;
1830 char str[64];
1831
1832 tx = BORDER + x * TILESIZE + 1 + GRIDEXTRA;
1833 ty = BORDER + y * TILESIZE + 1 + GRIDEXTRA;
1834
1835 cx = tx;
1836 cy = ty;
1837 cw = tw = TILESIZE-1-2*GRIDEXTRA;
1838 ch = th = TILESIZE-1-2*GRIDEXTRA;
1839
1840 if (x > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x-1))
1841 cx -= GRIDEXTRA, cw += GRIDEXTRA;
1842 if (x+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x+1))
1843 cw += GRIDEXTRA;
1844 if (y > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y-1)*w+x))
1845 cy -= GRIDEXTRA, ch += GRIDEXTRA;
1846 if (y+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y+1)*w+x))
1847 ch += GRIDEXTRA;
1848
1849 clip(dr, cx, cy, cw, ch);
1850
1851 /* background needs erasing */
1852 draw_rect(dr, cx, cy, cw, ch,
1853 (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND);
1854
1855 /* pencil-mode highlight */
1856 if (tile & DF_HIGHLIGHT_PENCIL) {
1857 int coords[6];
1858 coords[0] = cx;
1859 coords[1] = cy;
1860 coords[2] = cx+cw/2;
1861 coords[3] = cy;
1862 coords[4] = cx;
1863 coords[5] = cy+ch/2;
1864 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1865 }
1866
1867 /*
1868 * Draw the corners of thick lines in corner-adjacent squares,
1869 * which jut into this square by one pixel.
1870 */
1871 if (x > 0 && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x-1))
1872 draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
1873 if (x+1 < w && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x+1))
1874 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
1875 if (x > 0 && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x-1))
1876 draw_rect(dr, tx-GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
1877 if (x+1 < w && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x+1))
1878 draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
1879
1880 /* Draw the box clue. */
1881 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) {
1882 long clue = clues->clues[y*w+x];
1883 long cluetype = clue & CMASK, clueval = clue & ~CMASK;
1884 int size = dsf_size(clues->dsf, y*w+x);
1885 /*
1886 * Special case of clue-drawing: a box with only one square
1887 * is written as just the number, with no operation, because
1888 * it doesn't matter whether the operation is ADD or MUL.
1889 * The generation code above should never produce puzzles
1890 * containing such a thing - I think they're inelegant - but
1891 * it's possible to type in game IDs from elsewhere, so I
1892 * want to display them right if so.
1893 */
1894 sprintf (str, "%ld%s", clueval,
1895 (size == 1 || only_one_op ? "" :
1896 cluetype == C_ADD ? "+" :
1897 cluetype == C_SUB ? ds->minus_sign :
1898 cluetype == C_MUL ? ds->times_sign :
1899 /* cluetype == C_DIV ? */ ds->divide_sign));
1900 draw_text(dr, tx + GRIDEXTRA * 2, ty + GRIDEXTRA * 2 + TILESIZE/4,
1901 FONT_VARIABLE, TILESIZE/4, ALIGN_VNORMAL | ALIGN_HLEFT,
1902 (tile & DF_ERR_CLUE ? COL_ERROR : COL_GRID), str);
1903 }
1904
1905 /* new number needs drawing? */
1906 if (tile & DF_DIGIT_MASK) {
1907 str[1] = '\0';
1908 str[0] = (tile & DF_DIGIT_MASK) + '0';
1909 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1910 FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1911 (tile & DF_ERR_LATIN) ? COL_ERROR : COL_USER, str);
1912 } else {
1913 int i, j, npencil;
1914 int pl, pr, pt, pb;
1915 float bestsize;
1916 int pw, ph, minph, pbest, fontsize;
1917
1918 /* Count the pencil marks required. */
1919 for (i = 1, npencil = 0; i <= w; i++)
1920 if (tile & (1L << (i + DF_PENCIL_SHIFT)))
1921 npencil++;
1922 if (npencil) {
1923
1924 minph = 2;
1925
1926 /*
1927 * Determine the bounding rectangle within which we're going
1928 * to put the pencil marks.
1929 */
1930 /* Start with the whole square */
1931 pl = tx + GRIDEXTRA;
1932 pr = pl + TILESIZE - GRIDEXTRA;
1933 pt = ty + GRIDEXTRA;
1934 pb = pt + TILESIZE - GRIDEXTRA;
1935 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) {
1936 /*
1937 * Make space for the clue text.
1938 */
1939 pt += TILESIZE/4;
1940 /* minph--; */
1941 }
1942
1943 /*
1944 * We arrange our pencil marks in a grid layout, with
1945 * the number of rows and columns adjusted to allow the
1946 * maximum font size.
1947 *
1948 * So now we work out what the grid size ought to be.
1949 */
1950 bestsize = 0.0;
1951 pbest = 0;
1952 /* Minimum */
1953 for (pw = 3; pw < max(npencil,4); pw++) {
1954 float fw, fh, fs;
1955
1956 ph = (npencil + pw - 1) / pw;
1957 ph = max(ph, minph);
1958 fw = (pr - pl) / (float)pw;
1959 fh = (pb - pt) / (float)ph;
1960 fs = min(fw, fh);
1961 if (fs > bestsize) {
1962 bestsize = fs;
1963 pbest = pw;
1964 }
1965 }
1966 assert(pbest > 0);
1967 pw = pbest;
1968 ph = (npencil + pw - 1) / pw;
1969 ph = max(ph, minph);
1970
1971 /*
1972 * Now we've got our grid dimensions, work out the pixel
1973 * size of a grid element, and round it to the nearest
1974 * pixel. (We don't want rounding errors to make the
1975 * grid look uneven at low pixel sizes.)
1976 */
1977 fontsize = min((pr - pl) / pw, (pb - pt) / ph);
1978
1979 /*
1980 * Centre the resulting figure in the square.
1981 */
1982 pl = tx + (TILESIZE - fontsize * pw) / 2;
1983 pt = ty + (TILESIZE - fontsize * ph) / 2;
1984
1985 /*
1986 * And move it down a bit if it's collided with some
1987 * clue text.
1988 */
1989 if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) {
1990 pt = max(pt, ty + GRIDEXTRA * 3 + TILESIZE/4);
1991 }
1992
1993 /*
1994 * Now actually draw the pencil marks.
1995 */
1996 for (i = 1, j = 0; i <= w; i++)
1997 if (tile & (1L << (i + DF_PENCIL_SHIFT))) {
1998 int dx = j % pw, dy = j / pw;
1999
2000 str[1] = '\0';
2001 str[0] = i + '0';
2002 draw_text(dr, pl + fontsize * (2*dx+1) / 2,
2003 pt + fontsize * (2*dy+1) / 2,
2004 FONT_VARIABLE, fontsize,
2005 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
2006 j++;
2007 }
2008 }
2009 }
2010
2011 unclip(dr);
2012
2013 draw_update(dr, cx, cy, cw, ch);
2014}
2015
2016static void game_redraw(drawing *dr, game_drawstate *ds,
2017 const game_state *oldstate, const game_state *state,
2018 int dir, const game_ui *ui,
2019 float animtime, float flashtime)
2020{
2021 int w = state->par.w /*, a = w*w */;
2022 int x, y;
2023
2024 if (!ds->started) {
2025 /*
2026 * The initial contents of the window are not guaranteed and
2027 * can vary with front ends. To be on the safe side, all
2028 * games should start by drawing a big background-colour
2029 * rectangle covering the whole window.
2030 */
2031 draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
2032
2033 /*
2034 * Big containing rectangle.
2035 */
2036 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA,
2037 w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2,
2038 COL_GRID);
2039
2040 draw_update(dr, 0, 0, SIZE(w), SIZE(w));
2041
2042 ds->started = TRUE;
2043 }
2044
2045 check_errors(state, ds->errors);
2046
2047 for (y = 0; y < w; y++) {
2048 for (x = 0; x < w; x++) {
2049 long tile = 0L;
2050
2051 if (state->grid[y*w+x])
2052 tile = state->grid[y*w+x];
2053 else
2054 tile = (long)state->pencil[y*w+x] << DF_PENCIL_SHIFT;
2055
2056 if (ui->hshow && ui->hx == x && ui->hy == y)
2057 tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
2058
2059 if (flashtime > 0 &&
2060 (flashtime <= FLASH_TIME/3 ||
2061 flashtime >= FLASH_TIME*2/3))
2062 tile |= DF_HIGHLIGHT; /* completion flash */
2063
2064 tile |= ds->errors[y*w+x];
2065
2066 if (ds->tiles[y*w+x] != tile) {
2067 ds->tiles[y*w+x] = tile;
2068 draw_tile(dr, ds, state->clues, x, y, tile,
2069 state->par.multiplication_only);
2070 }
2071 }
2072 }
2073}
2074
2075static float game_anim_length(const game_state *oldstate,
2076 const game_state *newstate, int dir, game_ui *ui)
2077{
2078 return 0.0F;
2079}
2080
2081static float game_flash_length(const game_state *oldstate,
2082 const game_state *newstate, int dir, game_ui *ui)
2083{
2084 if (!oldstate->completed && newstate->completed &&
2085 !oldstate->cheated && !newstate->cheated)
2086 return FLASH_TIME;
2087 return 0.0F;
2088}
2089
2090static int game_status(const game_state *state)
2091{
2092 return state->completed ? +1 : 0;
2093}
2094
2095static int game_timing_state(const game_state *state, game_ui *ui)
2096{
2097 if (state->completed)
2098 return FALSE;
2099 return TRUE;
2100}
2101
2102static void game_print_size(const game_params *params, float *x, float *y)
2103{
2104 int pw, ph;
2105
2106 /*
2107 * We use 9mm squares by default, like Solo.
2108 */
2109 game_compute_size(params, 900, &pw, &ph);
2110 *x = pw / 100.0F;
2111 *y = ph / 100.0F;
2112}
2113
2114/*
2115 * Subfunction to draw the thick lines between cells. In order to do
2116 * this using the line-drawing rather than rectangle-drawing API (so
2117 * as to get line thicknesses to scale correctly) and yet have
2118 * correctly mitred joins between lines, we must do this by tracing
2119 * the boundary of each sub-block and drawing it in one go as a
2120 * single polygon.
2121 */
2122static void outline_block_structure(drawing *dr, game_drawstate *ds,
2123 int w, int *dsf, int ink)
2124{
2125 int a = w*w;
2126 int *coords;
2127 int i, n;
2128 int x, y, dx, dy, sx, sy, sdx, sdy;
2129
2130 coords = snewn(4*a, int);
2131
2132 /*
2133 * Iterate over all the blocks.
2134 */
2135 for (i = 0; i < a; i++) {
2136 if (dsf_canonify(dsf, i) != i)
2137 continue;
2138
2139 /*
2140 * For each block, we need a starting square within it which
2141 * has a boundary at the left. Conveniently, we have one
2142 * right here, by construction.
2143 */
2144 x = i % w;
2145 y = i / w;
2146 dx = -1;
2147 dy = 0;
2148
2149 /*
2150 * Now begin tracing round the perimeter. At all
2151 * times, (x,y) describes some square within the
2152 * block, and (x+dx,y+dy) is some adjacent square
2153 * outside it; so the edge between those two squares
2154 * is always an edge of the block.
2155 */
2156 sx = x, sy = y, sdx = dx, sdy = dy; /* save starting position */
2157 n = 0;
2158 do {
2159 int cx, cy, tx, ty, nin;
2160
2161 /*
2162 * Advance to the next edge, by looking at the two
2163 * squares beyond it. If they're both outside the block,
2164 * we turn right (by leaving x,y the same and rotating
2165 * dx,dy clockwise); if they're both inside, we turn
2166 * left (by rotating dx,dy anticlockwise and contriving
2167 * to leave x+dx,y+dy unchanged); if one of each, we go
2168 * straight on (and may enforce by assertion that
2169 * they're one of each the _right_ way round).
2170 */
2171 nin = 0;
2172 tx = x - dy + dx;
2173 ty = y + dx + dy;
2174 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w &&
2175 dsf_canonify(dsf, ty*w+tx) == i);
2176 tx = x - dy;
2177 ty = y + dx;
2178 nin += (tx >= 0 && tx < w && ty >= 0 && ty < w &&
2179 dsf_canonify(dsf, ty*w+tx) == i);
2180 if (nin == 0) {
2181 /*
2182 * Turn right.
2183 */
2184 int tmp;
2185 tmp = dx;
2186 dx = -dy;
2187 dy = tmp;
2188 } else if (nin == 2) {
2189 /*
2190 * Turn left.
2191 */
2192 int tmp;
2193
2194 x += dx;
2195 y += dy;
2196
2197 tmp = dx;
2198 dx = dy;
2199 dy = -tmp;
2200
2201 x -= dx;
2202 y -= dy;
2203 } else {
2204 /*
2205 * Go straight on.
2206 */
2207 x -= dy;
2208 y += dx;
2209 }
2210
2211 /*
2212 * Now enforce by assertion that we ended up
2213 * somewhere sensible.
2214 */
2215 assert(x >= 0 && x < w && y >= 0 && y < w &&
2216 dsf_canonify(dsf, y*w+x) == i);
2217 assert(x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= w ||
2218 dsf_canonify(dsf, (y+dy)*w+(x+dx)) != i);
2219
2220 /*
2221 * Record the point we just went past at one end of the
2222 * edge. To do this, we translate (x,y) down and right
2223 * by half a unit (so they're describing a point in the
2224 * _centre_ of the square) and then translate back again
2225 * in a manner rotated by dy and dx.
2226 */
2227 assert(n < 2*w+2);
2228 cx = ((2*x+1) + dy + dx) / 2;
2229 cy = ((2*y+1) - dx + dy) / 2;
2230 coords[2*n+0] = BORDER + cx * TILESIZE;
2231 coords[2*n+1] = BORDER + cy * TILESIZE;
2232 n++;
2233
2234 } while (x != sx || y != sy || dx != sdx || dy != sdy);
2235
2236 /*
2237 * That's our polygon; now draw it.
2238 */
2239 draw_polygon(dr, coords, n, -1, ink);
2240 }
2241
2242 sfree(coords);
2243}
2244
2245static void game_print(drawing *dr, const game_state *state, int tilesize)
2246{
2247 int w = state->par.w;
2248 int ink = print_mono_colour(dr, 0);
2249 int x, y;
2250 char *minus_sign, *times_sign, *divide_sign;
2251
2252 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2253 game_drawstate ads, *ds = &ads;
2254 game_set_size(dr, ds, NULL, tilesize);
2255
2256 minus_sign = text_fallback(dr, minus_signs, lenof(minus_signs));
2257 times_sign = text_fallback(dr, times_signs, lenof(times_signs));
2258 divide_sign = text_fallback(dr, divide_signs, lenof(divide_signs));
2259
2260 /*
2261 * Border.
2262 */
2263 print_line_width(dr, 3 * TILESIZE / 40);
2264 draw_rect_outline(dr, BORDER, BORDER, w*TILESIZE, w*TILESIZE, ink);
2265
2266 /*
2267 * Main grid.
2268 */
2269 for (x = 1; x < w; x++) {
2270 print_line_width(dr, TILESIZE / 40);
2271 draw_line(dr, BORDER+x*TILESIZE, BORDER,
2272 BORDER+x*TILESIZE, BORDER+w*TILESIZE, ink);
2273 }
2274 for (y = 1; y < w; y++) {
2275 print_line_width(dr, TILESIZE / 40);
2276 draw_line(dr, BORDER, BORDER+y*TILESIZE,
2277 BORDER+w*TILESIZE, BORDER+y*TILESIZE, ink);
2278 }
2279
2280 /*
2281 * Thick lines between cells.
2282 */
2283 print_line_width(dr, 3 * TILESIZE / 40);
2284 outline_block_structure(dr, ds, w, state->clues->dsf, ink);
2285
2286 /*
2287 * Clues.
2288 */
2289 for (y = 0; y < w; y++)
2290 for (x = 0; x < w; x++)
2291 if (dsf_canonify(state->clues->dsf, y*w+x) == y*w+x) {
2292 long clue = state->clues->clues[y*w+x];
2293 long cluetype = clue & CMASK, clueval = clue & ~CMASK;
2294 int size = dsf_size(state->clues->dsf, y*w+x);
2295 char str[64];
2296
2297 /*
2298 * As in the drawing code, we omit the operator for
2299 * blocks of area 1.
2300 */
2301 sprintf (str, "%ld%s", clueval,
2302 (size == 1 ? "" :
2303 cluetype == C_ADD ? "+" :
2304 cluetype == C_SUB ? minus_sign :
2305 cluetype == C_MUL ? times_sign :
2306 /* cluetype == C_DIV ? */ divide_sign));
2307
2308 draw_text(dr,
2309 BORDER+x*TILESIZE + 5*TILESIZE/80,
2310 BORDER+y*TILESIZE + 20*TILESIZE/80,
2311 FONT_VARIABLE, TILESIZE/4,
2312 ALIGN_VNORMAL | ALIGN_HLEFT,
2313 ink, str);
2314 }
2315
2316 /*
2317 * Numbers for the solution, if any.
2318 */
2319 for (y = 0; y < w; y++)
2320 for (x = 0; x < w; x++)
2321 if (state->grid[y*w+x]) {
2322 char str[2];
2323 str[1] = '\0';
2324 str[0] = state->grid[y*w+x] + '0';
2325 draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2,
2326 BORDER + y*TILESIZE + TILESIZE/2,
2327 FONT_VARIABLE, TILESIZE/2,
2328 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2329 }
2330
2331 sfree(minus_sign);
2332 sfree(times_sign);
2333 sfree(divide_sign);
2334}
2335
2336#ifdef COMBINED
2337#define thegame keen
2338#endif
2339
2340const struct game thegame = {
2341 "Keen", "games.keen", "keen",
2342 default_params,
2343 game_fetch_preset, NULL,
2344 decode_params,
2345 encode_params,
2346 free_params,
2347 dup_params,
2348 TRUE, game_configure, custom_params,
2349 validate_params,
2350 new_game_desc,
2351 validate_desc,
2352 new_game,
2353 dup_game,
2354 free_game,
2355 TRUE, solve_game,
2356 FALSE, game_can_format_as_text_now, game_text_format,
2357 new_ui,
2358 free_ui,
2359 encode_ui,
2360 decode_ui,
2361 game_changed_state,
2362 interpret_move,
2363 execute_move,
2364 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2365 game_colours,
2366 game_new_drawstate,
2367 game_free_drawstate,
2368 game_redraw,
2369 game_anim_length,
2370 game_flash_length,
2371 game_status,
2372 TRUE, FALSE, game_print_size, game_print,
2373 FALSE, /* wants_statusbar */
2374 FALSE, game_timing_state,
2375 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2376};
2377
2378#ifdef STANDALONE_SOLVER
2379
2380#include <stdarg.h>
2381
2382int main(int argc, char **argv)
2383{
2384 game_params *p;
2385 game_state *s;
2386 char *id = NULL, *desc, *err;
2387 int grade = FALSE;
2388 int ret, diff, really_show_working = FALSE;
2389
2390 while (--argc > 0) {
2391 char *p = *++argv;
2392 if (!strcmp(p, "-v")) {
2393 really_show_working = TRUE;
2394 } else if (!strcmp(p, "-g")) {
2395 grade = TRUE;
2396 } else if (*p == '-') {
2397 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2398 return 1;
2399 } else {
2400 id = p;
2401 }
2402 }
2403
2404 if (!id) {
2405 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2406 return 1;
2407 }
2408
2409 desc = strchr(id, ':');
2410 if (!desc) {
2411 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2412 return 1;
2413 }
2414 *desc++ = '\0';
2415
2416 p = default_params();
2417 decode_params(p, id);
2418 err = validate_desc(p, desc);
2419 if (err) {
2420 fprintf(stderr, "%s: %s\n", argv[0], err);
2421 return 1;
2422 }
2423 s = new_game(NULL, p, desc);
2424
2425 /*
2426 * When solving an Easy puzzle, we don't want to bother the
2427 * user with Hard-level deductions. For this reason, we grade
2428 * the puzzle internally before doing anything else.
2429 */
2430 ret = -1; /* placate optimiser */
2431 solver_show_working = FALSE;
2432 for (diff = 0; diff < DIFFCOUNT; diff++) {
2433 memset(s->grid, 0, p->w * p->w);
2434 ret = solver(p->w, s->clues->dsf, s->clues->clues,
2435 s->grid, diff);
2436 if (ret <= diff)
2437 break;
2438 }
2439
2440 if (diff == DIFFCOUNT) {
2441 if (grade)
2442 printf("Difficulty rating: ambiguous\n");
2443 else
2444 printf("Unable to find a unique solution\n");
2445 } else {
2446 if (grade) {
2447 if (ret == diff_impossible)
2448 printf("Difficulty rating: impossible (no solution exists)\n");
2449 else
2450 printf("Difficulty rating: %s\n", keen_diffnames[ret]);
2451 } else {
2452 solver_show_working = really_show_working;
2453 memset(s->grid, 0, p->w * p->w);
2454 ret = solver(p->w, s->clues->dsf, s->clues->clues,
2455 s->grid, diff);
2456 if (ret != diff)
2457 printf("Puzzle is inconsistent\n");
2458 else {
2459 /*
2460 * We don't have a game_text_format for this game,
2461 * so we have to output the solution manually.
2462 */
2463 int x, y;
2464 for (y = 0; y < p->w; y++) {
2465 for (x = 0; x < p->w; x++) {
2466 printf("%s%c", x>0?" ":"", '0' + s->grid[y*p->w+x]);
2467 }
2468 putchar('\n');
2469 }
2470 }
2471 }
2472 }
2473
2474 return 0;
2475}
2476
2477#endif
2478
2479/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/keen.html b/apps/plugins/puzzles/src/keen.html
new file mode 100644
index 0000000000..062f0dd728
--- /dev/null
+++ b/apps/plugins/puzzles/src/keen.html
@@ -0,0 +1,102 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Keen</title>
7<link rel="previous" href="filling.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="towers.html">
12</head>
13<body>
14<p><a href="filling.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="towers.html">Next</a></p>
15<h1><a name="C30"></a>Chapter 30: <a name="i0"></a>Keen</h1>
16<p>
17You have a square grid; each square may contain a digit from 1 to the size of the grid. The grid is divided into blocks of varying shape and size, with arithmetic clues written in them. Your aim is to fully populate the grid with digits such that:
18</p>
19<ul><li>
20Each row contains only one occurrence of each digit
21</li>
22<li>
23Each column contains only one occurrence of each digit
24</li>
25<li>
26The digits in each block can be combined to form the number stated in the clue, using the arithmetic operation given in the clue. That is:
27<ul><li>
28An addition clue means that the sum of the digits in the block must be the given number. For example, &#8216;15+&#8217; means the contents of the block adds up to fifteen.
29</li>
30<li>
31A multiplication clue (e.g. &#8216;60&#215;&#8217;), similarly, means that the product of the digits in the block must be the given number.
32</li>
33<li>
34A subtraction clue will always be written in a block of size two, and it means that one of the digits in the block is greater than the other by the given amount. For example, &#8216;2&#8722;&#8217; means that one of the digits in the block is 2 more than the other, or equivalently that one digit minus the other one is 2. The two digits could be either way round, though.
35</li>
36<li>
37A division clue (e.g. &#8216;3&#247;&#8217;), similarly, is always in a block of size two and means that one digit divided by the other is equal to the given amount.
38</li>
39</ul>
40<p>
41Note that a block may contain the same digit more than once (provided the identical ones are not in the same row and column). This rule is precisely the opposite of the rule in Solo's &#8216;Killer&#8217; mode (see <a href="solo.html#C11">chapter 11</a>).
42</p>
43
44</li>
45</ul>
46<p>
47This puzzle appears in the Times under the name &#8216;<a name="i1"></a>KenKen&#8217;.
48</p>
49<h2><a name="S30.1"></a>30.1 <a name="i2"></a>Keen controls</h2>
50<p>
51Keen shares much of its control system with Solo (and Unequal).
52</p>
53<p>
54To play Keen, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).
55</p>
56<p>
57If you <em>right</em>-click in a square and then type a number, that number will be entered in the square as a &#8216;pencil mark&#8217;. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks.
58</p>
59<p>
60The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.
61</p>
62<p>
63To erase a single pencil mark, right-click in the square and type the same number again.
64</p>
65<p>
66All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.
67</p>
68<p>
69As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks.
70</p>
71<p>
72Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it.
73</p>
74<p>
75(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
76</p>
77<h2><a name="S30.2"></a>30.2 <a name="i3"></a>Keen parameters</h2>
78<p>
79These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
80</p>
81<dl><dt>
82<em>Grid size</em>
83</dt>
84<dd>
85Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with &#8216;digits&#8217; bigger than 9!).
86</dd>
87<dt>
88<em>Difficulty</em>
89</dt>
90<dd>
91Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack.
92</dd>
93<dt>
94<em>Multiplication only</em>
95</dt>
96<dd>
97If this is enabled, all boxes will be multiplication boxes. With this rule, the puzzle is known as &#8216;Inshi No Heya&#8217;.
98</dd>
99</dl>
100
101<hr><address></address></body>
102</html>
diff --git a/apps/plugins/puzzles/src/latin.c b/apps/plugins/puzzles/src/latin.c
new file mode 100644
index 0000000000..03d78aff1f
--- /dev/null
+++ b/apps/plugins/puzzles/src/latin.c
@@ -0,0 +1,1436 @@
1#include <assert.h>
2#include <string.h>
3#include <stdarg.h>
4
5#include "puzzles.h"
6#include "tree234.h"
7#include "maxflow.h"
8
9#ifdef STANDALONE_LATIN_TEST
10#define STANDALONE_SOLVER
11#endif
12
13#include "latin.h"
14
15/* --------------------------------------------------------
16 * Solver.
17 */
18
19static int latin_solver_top(struct latin_solver *solver, int maxdiff,
20 int diff_simple, int diff_set_0, int diff_set_1,
21 int diff_forcing, int diff_recursive,
22 usersolver_t const *usersolvers, void *ctx,
23 ctxnew_t ctxnew, ctxfree_t ctxfree);
24
25#ifdef STANDALONE_SOLVER
26int solver_show_working, solver_recurse_depth;
27#endif
28
29/*
30 * Function called when we are certain that a particular square has
31 * a particular number in it. The y-coordinate passed in here is
32 * transformed.
33 */
34void latin_solver_place(struct latin_solver *solver, int x, int y, int n)
35{
36 int i, o = solver->o;
37
38 assert(n <= o);
39 assert(cube(x,y,n));
40
41 /*
42 * Rule out all other numbers in this square.
43 */
44 for (i = 1; i <= o; i++)
45 if (i != n)
46 cube(x,y,i) = FALSE;
47
48 /*
49 * Rule out this number in all other positions in the row.
50 */
51 for (i = 0; i < o; i++)
52 if (i != y)
53 cube(x,i,n) = FALSE;
54
55 /*
56 * Rule out this number in all other positions in the column.
57 */
58 for (i = 0; i < o; i++)
59 if (i != x)
60 cube(i,y,n) = FALSE;
61
62 /*
63 * Enter the number in the result grid.
64 */
65 solver->grid[y*o+x] = n;
66
67 /*
68 * Cross out this number from the list of numbers left to place
69 * in its row, its column and its block.
70 */
71 solver->row[y*o+n-1] = solver->col[x*o+n-1] = TRUE;
72}
73
74int latin_solver_elim(struct latin_solver *solver, int start, int step
75#ifdef STANDALONE_SOLVER
76 , char *fmt, ...
77#endif
78 )
79{
80 int o = solver->o;
81#ifdef STANDALONE_SOLVER
82 char **names = solver->names;
83#endif
84 int fpos, m, i;
85
86 /*
87 * Count the number of set bits within this section of the
88 * cube.
89 */
90 m = 0;
91 fpos = -1;
92 for (i = 0; i < o; i++)
93 if (solver->cube[start+i*step]) {
94 fpos = start+i*step;
95 m++;
96 }
97
98 if (m == 1) {
99 int x, y, n;
100 assert(fpos >= 0);
101
102 n = 1 + fpos % o;
103 y = fpos / o;
104 x = y / o;
105 y %= o;
106
107 if (!solver->grid[y*o+x]) {
108#ifdef STANDALONE_SOLVER
109 if (solver_show_working) {
110 va_list ap;
111 printf("%*s", solver_recurse_depth*4, "");
112 va_start(ap, fmt);
113 vprintf(fmt, ap);
114 va_end(ap);
115 printf(":\n%*s placing %s at (%d,%d)\n",
116 solver_recurse_depth*4, "", names[n-1],
117 x+1, y+1);
118 }
119#endif
120 latin_solver_place(solver, x, y, n);
121 return +1;
122 }
123 } else if (m == 0) {
124#ifdef STANDALONE_SOLVER
125 if (solver_show_working) {
126 va_list ap;
127 printf("%*s", solver_recurse_depth*4, "");
128 va_start(ap, fmt);
129 vprintf(fmt, ap);
130 va_end(ap);
131 printf(":\n%*s no possibilities available\n",
132 solver_recurse_depth*4, "");
133 }
134#endif
135 return -1;
136 }
137
138 return 0;
139}
140
141struct latin_solver_scratch {
142 unsigned char *grid, *rowidx, *colidx, *set;
143 int *neighbours, *bfsqueue;
144#ifdef STANDALONE_SOLVER
145 int *bfsprev;
146#endif
147};
148
149int latin_solver_set(struct latin_solver *solver,
150 struct latin_solver_scratch *scratch,
151 int start, int step1, int step2
152#ifdef STANDALONE_SOLVER
153 , char *fmt, ...
154#endif
155 )
156{
157 int o = solver->o;
158#ifdef STANDALONE_SOLVER
159 char **names = solver->names;
160#endif
161 int i, j, n, count;
162 unsigned char *grid = scratch->grid;
163 unsigned char *rowidx = scratch->rowidx;
164 unsigned char *colidx = scratch->colidx;
165 unsigned char *set = scratch->set;
166
167 /*
168 * We are passed a o-by-o matrix of booleans. Our first job
169 * is to winnow it by finding any definite placements - i.e.
170 * any row with a solitary 1 - and discarding that row and the
171 * column containing the 1.
172 */
173 memset(rowidx, TRUE, o);
174 memset(colidx, TRUE, o);
175 for (i = 0; i < o; i++) {
176 int count = 0, first = -1;
177 for (j = 0; j < o; j++)
178 if (solver->cube[start+i*step1+j*step2])
179 first = j, count++;
180
181 if (count == 0) return -1;
182 if (count == 1)
183 rowidx[i] = colidx[first] = FALSE;
184 }
185
186 /*
187 * Convert each of rowidx/colidx from a list of 0s and 1s to a
188 * list of the indices of the 1s.
189 */
190 for (i = j = 0; i < o; i++)
191 if (rowidx[i])
192 rowidx[j++] = i;
193 n = j;
194 for (i = j = 0; i < o; i++)
195 if (colidx[i])
196 colidx[j++] = i;
197 assert(n == j);
198
199 /*
200 * And create the smaller matrix.
201 */
202 for (i = 0; i < n; i++)
203 for (j = 0; j < n; j++)
204 grid[i*o+j] = solver->cube[start+rowidx[i]*step1+colidx[j]*step2];
205
206 /*
207 * Having done that, we now have a matrix in which every row
208 * has at least two 1s in. Now we search to see if we can find
209 * a rectangle of zeroes (in the set-theoretic sense of
210 * `rectangle', i.e. a subset of rows crossed with a subset of
211 * columns) whose width and height add up to n.
212 */
213
214 memset(set, 0, n);
215 count = 0;
216 while (1) {
217 /*
218 * We have a candidate set. If its size is <=1 or >=n-1
219 * then we move on immediately.
220 */
221 if (count > 1 && count < n-1) {
222 /*
223 * The number of rows we need is n-count. See if we can
224 * find that many rows which each have a zero in all
225 * the positions listed in `set'.
226 */
227 int rows = 0;
228 for (i = 0; i < n; i++) {
229 int ok = TRUE;
230 for (j = 0; j < n; j++)
231 if (set[j] && grid[i*o+j]) {
232 ok = FALSE;
233 break;
234 }
235 if (ok)
236 rows++;
237 }
238
239 /*
240 * We expect never to be able to get _more_ than
241 * n-count suitable rows: this would imply that (for
242 * example) there are four numbers which between them
243 * have at most three possible positions, and hence it
244 * indicates a faulty deduction before this point or
245 * even a bogus clue.
246 */
247 if (rows > n - count) {
248#ifdef STANDALONE_SOLVER
249 if (solver_show_working) {
250 va_list ap;
251 printf("%*s", solver_recurse_depth*4,
252 "");
253 va_start(ap, fmt);
254 vprintf(fmt, ap);
255 va_end(ap);
256 printf(":\n%*s contradiction reached\n",
257 solver_recurse_depth*4, "");
258 }
259#endif
260 return -1;
261 }
262
263 if (rows >= n - count) {
264 int progress = FALSE;
265
266 /*
267 * We've got one! Now, for each row which _doesn't_
268 * satisfy the criterion, eliminate all its set
269 * bits in the positions _not_ listed in `set'.
270 * Return +1 (meaning progress has been made) if we
271 * successfully eliminated anything at all.
272 *
273 * This involves referring back through
274 * rowidx/colidx in order to work out which actual
275 * positions in the cube to meddle with.
276 */
277 for (i = 0; i < n; i++) {
278 int ok = TRUE;
279 for (j = 0; j < n; j++)
280 if (set[j] && grid[i*o+j]) {
281 ok = FALSE;
282 break;
283 }
284 if (!ok) {
285 for (j = 0; j < n; j++)
286 if (!set[j] && grid[i*o+j]) {
287 int fpos = (start+rowidx[i]*step1+
288 colidx[j]*step2);
289#ifdef STANDALONE_SOLVER
290 if (solver_show_working) {
291 int px, py, pn;
292
293 if (!progress) {
294 va_list ap;
295 printf("%*s", solver_recurse_depth*4,
296 "");
297 va_start(ap, fmt);
298 vprintf(fmt, ap);
299 va_end(ap);
300 printf(":\n");
301 }
302
303 pn = 1 + fpos % o;
304 py = fpos / o;
305 px = py / o;
306 py %= o;
307
308 printf("%*s ruling out %s at (%d,%d)\n",
309 solver_recurse_depth*4, "",
310 names[pn-1], px+1, py+1);
311 }
312#endif
313 progress = TRUE;
314 solver->cube[fpos] = FALSE;
315 }
316 }
317 }
318
319 if (progress) {
320 return +1;
321 }
322 }
323 }
324
325 /*
326 * Binary increment: change the rightmost 0 to a 1, and
327 * change all 1s to the right of it to 0s.
328 */
329 i = n;
330 while (i > 0 && set[i-1])
331 set[--i] = 0, count--;
332 if (i > 0)
333 set[--i] = 1, count++;
334 else
335 break; /* done */
336 }
337
338 return 0;
339}
340
341/*
342 * Look for forcing chains. A forcing chain is a path of
343 * pairwise-exclusive squares (i.e. each pair of adjacent squares
344 * in the path are in the same row, column or block) with the
345 * following properties:
346 *
347 * (a) Each square on the path has precisely two possible numbers.
348 *
349 * (b) Each pair of squares which are adjacent on the path share
350 * at least one possible number in common.
351 *
352 * (c) Each square in the middle of the path shares _both_ of its
353 * numbers with at least one of its neighbours (not the same
354 * one with both neighbours).
355 *
356 * These together imply that at least one of the possible number
357 * choices at one end of the path forces _all_ the rest of the
358 * numbers along the path. In order to make real use of this, we
359 * need further properties:
360 *
361 * (c) Ruling out some number N from the square at one end
362 * of the path forces the square at the other end to
363 * take number N.
364 *
365 * (d) The two end squares are both in line with some third
366 * square.
367 *
368 * (e) That third square currently has N as a possibility.
369 *
370 * If we can find all of that lot, we can deduce that at least one
371 * of the two ends of the forcing chain has number N, and that
372 * therefore the mutually adjacent third square does not.
373 *
374 * To find forcing chains, we're going to start a bfs at each
375 * suitable square, once for each of its two possible numbers.
376 */
377int latin_solver_forcing(struct latin_solver *solver,
378 struct latin_solver_scratch *scratch)
379{
380 int o = solver->o;
381#ifdef STANDALONE_SOLVER
382 char **names = solver->names;
383#endif
384 int *bfsqueue = scratch->bfsqueue;
385#ifdef STANDALONE_SOLVER
386 int *bfsprev = scratch->bfsprev;
387#endif
388 unsigned char *number = scratch->grid;
389 int *neighbours = scratch->neighbours;
390 int x, y;
391
392 for (y = 0; y < o; y++)
393 for (x = 0; x < o; x++) {
394 int count, t, n;
395
396 /*
397 * If this square doesn't have exactly two candidate
398 * numbers, don't try it.
399 *
400 * In this loop we also sum the candidate numbers,
401 * which is a nasty hack to allow us to quickly find
402 * `the other one' (since we will shortly know there
403 * are exactly two).
404 */
405 for (count = t = 0, n = 1; n <= o; n++)
406 if (cube(x, y, n))
407 count++, t += n;
408 if (count != 2)
409 continue;
410
411 /*
412 * Now attempt a bfs for each candidate.
413 */
414 for (n = 1; n <= o; n++)
415 if (cube(x, y, n)) {
416 int orign, currn, head, tail;
417
418 /*
419 * Begin a bfs.
420 */
421 orign = n;
422
423 memset(number, o+1, o*o);
424 head = tail = 0;
425 bfsqueue[tail++] = y*o+x;
426#ifdef STANDALONE_SOLVER
427 bfsprev[y*o+x] = -1;
428#endif
429 number[y*o+x] = t - n;
430
431 while (head < tail) {
432 int xx, yy, nneighbours, xt, yt, i;
433
434 xx = bfsqueue[head++];
435 yy = xx / o;
436 xx %= o;
437
438 currn = number[yy*o+xx];
439
440 /*
441 * Find neighbours of yy,xx.
442 */
443 nneighbours = 0;
444 for (yt = 0; yt < o; yt++)
445 neighbours[nneighbours++] = yt*o+xx;
446 for (xt = 0; xt < o; xt++)
447 neighbours[nneighbours++] = yy*o+xt;
448
449 /*
450 * Try visiting each of those neighbours.
451 */
452 for (i = 0; i < nneighbours; i++) {
453 int cc, tt, nn;
454
455 xt = neighbours[i] % o;
456 yt = neighbours[i] / o;
457
458 /*
459 * We need this square to not be
460 * already visited, and to include
461 * currn as a possible number.
462 */
463 if (number[yt*o+xt] <= o)
464 continue;
465 if (!cube(xt, yt, currn))
466 continue;
467
468 /*
469 * Don't visit _this_ square a second
470 * time!
471 */
472 if (xt == xx && yt == yy)
473 continue;
474
475 /*
476 * To continue with the bfs, we need
477 * this square to have exactly two
478 * possible numbers.
479 */
480 for (cc = tt = 0, nn = 1; nn <= o; nn++)
481 if (cube(xt, yt, nn))
482 cc++, tt += nn;
483 if (cc == 2) {
484 bfsqueue[tail++] = yt*o+xt;
485#ifdef STANDALONE_SOLVER
486 bfsprev[yt*o+xt] = yy*o+xx;
487#endif
488 number[yt*o+xt] = tt - currn;
489 }
490
491 /*
492 * One other possibility is that this
493 * might be the square in which we can
494 * make a real deduction: if it's
495 * adjacent to x,y, and currn is equal
496 * to the original number we ruled out.
497 */
498 if (currn == orign &&
499 (xt == x || yt == y)) {
500#ifdef STANDALONE_SOLVER
501 if (solver_show_working) {
502 char *sep = "";
503 int xl, yl;
504 printf("%*sforcing chain, %s at ends of ",
505 solver_recurse_depth*4, "",
506 names[orign-1]);
507 xl = xx;
508 yl = yy;
509 while (1) {
510 printf("%s(%d,%d)", sep, xl+1,
511 yl+1);
512 xl = bfsprev[yl*o+xl];
513 if (xl < 0)
514 break;
515 yl = xl / o;
516 xl %= o;
517 sep = "-";
518 }
519 printf("\n%*s ruling out %s at (%d,%d)\n",
520 solver_recurse_depth*4, "",
521 names[orign-1],
522 xt+1, yt+1);
523 }
524#endif
525 cube(xt, yt, orign) = FALSE;
526 return 1;
527 }
528 }
529 }
530 }
531 }
532
533 return 0;
534}
535
536struct latin_solver_scratch *latin_solver_new_scratch(struct latin_solver *solver)
537{
538 struct latin_solver_scratch *scratch = snew(struct latin_solver_scratch);
539 int o = solver->o;
540 scratch->grid = snewn(o*o, unsigned char);
541 scratch->rowidx = snewn(o, unsigned char);
542 scratch->colidx = snewn(o, unsigned char);
543 scratch->set = snewn(o, unsigned char);
544 scratch->neighbours = snewn(3*o, int);
545 scratch->bfsqueue = snewn(o*o, int);
546#ifdef STANDALONE_SOLVER
547 scratch->bfsprev = snewn(o*o, int);
548#endif
549 return scratch;
550}
551
552void latin_solver_free_scratch(struct latin_solver_scratch *scratch)
553{
554#ifdef STANDALONE_SOLVER
555 sfree(scratch->bfsprev);
556#endif
557 sfree(scratch->bfsqueue);
558 sfree(scratch->neighbours);
559 sfree(scratch->set);
560 sfree(scratch->colidx);
561 sfree(scratch->rowidx);
562 sfree(scratch->grid);
563 sfree(scratch);
564}
565
566void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o)
567{
568 int x, y;
569
570 solver->o = o;
571 solver->cube = snewn(o*o*o, unsigned char);
572 solver->grid = grid; /* write straight back to the input */
573 memset(solver->cube, TRUE, o*o*o);
574
575 solver->row = snewn(o*o, unsigned char);
576 solver->col = snewn(o*o, unsigned char);
577 memset(solver->row, FALSE, o*o);
578 memset(solver->col, FALSE, o*o);
579
580 for (x = 0; x < o; x++)
581 for (y = 0; y < o; y++)
582 if (grid[y*o+x])
583 latin_solver_place(solver, x, y, grid[y*o+x]);
584
585#ifdef STANDALONE_SOLVER
586 solver->names = NULL;
587#endif
588}
589
590void latin_solver_free(struct latin_solver *solver)
591{
592 sfree(solver->cube);
593 sfree(solver->row);
594 sfree(solver->col);
595}
596
597int latin_solver_diff_simple(struct latin_solver *solver)
598{
599 int x, y, n, ret, o = solver->o;
600#ifdef STANDALONE_SOLVER
601 char **names = solver->names;
602#endif
603
604 /*
605 * Row-wise positional elimination.
606 */
607 for (y = 0; y < o; y++)
608 for (n = 1; n <= o; n++)
609 if (!solver->row[y*o+n-1]) {
610 ret = latin_solver_elim(solver, cubepos(0,y,n), o*o
611#ifdef STANDALONE_SOLVER
612 , "positional elimination,"
613 " %s in row %d", names[n-1],
614 y+1
615#endif
616 );
617 if (ret != 0) return ret;
618 }
619 /*
620 * Column-wise positional elimination.
621 */
622 for (x = 0; x < o; x++)
623 for (n = 1; n <= o; n++)
624 if (!solver->col[x*o+n-1]) {
625 ret = latin_solver_elim(solver, cubepos(x,0,n), o
626#ifdef STANDALONE_SOLVER
627 , "positional elimination,"
628 " %s in column %d", names[n-1], x+1
629#endif
630 );
631 if (ret != 0) return ret;
632 }
633
634 /*
635 * Numeric elimination.
636 */
637 for (x = 0; x < o; x++)
638 for (y = 0; y < o; y++)
639 if (!solver->grid[y*o+x]) {
640 ret = latin_solver_elim(solver, cubepos(x,y,1), 1
641#ifdef STANDALONE_SOLVER
642 , "numeric elimination at (%d,%d)",
643 x+1, y+1
644#endif
645 );
646 if (ret != 0) return ret;
647 }
648 return 0;
649}
650
651int latin_solver_diff_set(struct latin_solver *solver,
652 struct latin_solver_scratch *scratch,
653 int extreme)
654{
655 int x, y, n, ret, o = solver->o;
656#ifdef STANDALONE_SOLVER
657 char **names = solver->names;
658#endif
659
660 if (!extreme) {
661 /*
662 * Row-wise set elimination.
663 */
664 for (y = 0; y < o; y++) {
665 ret = latin_solver_set(solver, scratch, cubepos(0,y,1), o*o, 1
666#ifdef STANDALONE_SOLVER
667 , "set elimination, row %d", y+1
668#endif
669 );
670 if (ret != 0) return ret;
671 }
672 /*
673 * Column-wise set elimination.
674 */
675 for (x = 0; x < o; x++) {
676 ret = latin_solver_set(solver, scratch, cubepos(x,0,1), o, 1
677#ifdef STANDALONE_SOLVER
678 , "set elimination, column %d", x+1
679#endif
680 );
681 if (ret != 0) return ret;
682 }
683 } else {
684 /*
685 * Row-vs-column set elimination on a single number
686 * (much tricker for a human to do!)
687 */
688 for (n = 1; n <= o; n++) {
689 ret = latin_solver_set(solver, scratch, cubepos(0,0,n), o*o, o
690#ifdef STANDALONE_SOLVER
691 , "positional set elimination on %s",
692 names[n-1]
693#endif
694 );
695 if (ret != 0) return ret;
696 }
697 }
698 return 0;
699}
700
701/*
702 * Returns:
703 * 0 for 'didn't do anything' implying it was already solved.
704 * -1 for 'impossible' (no solution)
705 * 1 for 'single solution'
706 * >1 for 'multiple solutions' (you don't get to know how many, and
707 * the first such solution found will be set.
708 *
709 * and this function may well assert if given an impossible board.
710 */
711static int latin_solver_recurse
712 (struct latin_solver *solver, int diff_simple, int diff_set_0,
713 int diff_set_1, int diff_forcing, int diff_recursive,
714 usersolver_t const *usersolvers, void *ctx,
715 ctxnew_t ctxnew, ctxfree_t ctxfree)
716{
717 int best, bestcount;
718 int o = solver->o, x, y, n;
719#ifdef STANDALONE_SOLVER
720 char **names = solver->names;
721#endif
722
723 best = -1;
724 bestcount = o+1;
725
726 for (y = 0; y < o; y++)
727 for (x = 0; x < o; x++)
728 if (!solver->grid[y*o+x]) {
729 int count;
730
731 /*
732 * An unfilled square. Count the number of
733 * possible digits in it.
734 */
735 count = 0;
736 for (n = 1; n <= o; n++)
737 if (cube(x,y,n))
738 count++;
739
740 /*
741 * We should have found any impossibilities
742 * already, so this can safely be an assert.
743 */
744 assert(count > 1);
745
746 if (count < bestcount) {
747 bestcount = count;
748 best = y*o+x;
749 }
750 }
751
752 if (best == -1)
753 /* we were complete already. */
754 return 0;
755 else {
756 int i, j;
757 digit *list, *ingrid, *outgrid;
758 int diff = diff_impossible; /* no solution found yet */
759
760 /*
761 * Attempt recursion.
762 */
763 y = best / o;
764 x = best % o;
765
766 list = snewn(o, digit);
767 ingrid = snewn(o*o, digit);
768 outgrid = snewn(o*o, digit);
769 memcpy(ingrid, solver->grid, o*o);
770
771 /* Make a list of the possible digits. */
772 for (j = 0, n = 1; n <= o; n++)
773 if (cube(x,y,n))
774 list[j++] = n;
775
776#ifdef STANDALONE_SOLVER
777 if (solver_show_working) {
778 char *sep = "";
779 printf("%*srecursing on (%d,%d) [",
780 solver_recurse_depth*4, "", x+1, y+1);
781 for (i = 0; i < j; i++) {
782 printf("%s%s", sep, names[list[i]-1]);
783 sep = " or ";
784 }
785 printf("]\n");
786 }
787#endif
788
789 /*
790 * And step along the list, recursing back into the
791 * main solver at every stage.
792 */
793 for (i = 0; i < j; i++) {
794 int ret;
795 void *newctx;
796 struct latin_solver subsolver;
797
798 memcpy(outgrid, ingrid, o*o);
799 outgrid[y*o+x] = list[i];
800
801#ifdef STANDALONE_SOLVER
802 if (solver_show_working)
803 printf("%*sguessing %s at (%d,%d)\n",
804 solver_recurse_depth*4, "", names[list[i]-1], x+1, y+1);
805 solver_recurse_depth++;
806#endif
807
808 if (ctxnew) {
809 newctx = ctxnew(ctx);
810 } else {
811 newctx = ctx;
812 }
813 latin_solver_alloc(&subsolver, outgrid, o);
814#ifdef STANDALONE_SOLVER
815 subsolver.names = solver->names;
816#endif
817 ret = latin_solver_top(&subsolver, diff_recursive,
818 diff_simple, diff_set_0, diff_set_1,
819 diff_forcing, diff_recursive,
820 usersolvers, newctx, ctxnew, ctxfree);
821 latin_solver_free(&subsolver);
822 if (ctxnew)
823 ctxfree(newctx);
824
825#ifdef STANDALONE_SOLVER
826 solver_recurse_depth--;
827 if (solver_show_working) {
828 printf("%*sretracting %s at (%d,%d)\n",
829 solver_recurse_depth*4, "", names[list[i]-1], x+1, y+1);
830 }
831#endif
832 /* we recurse as deep as we can, so we should never find
833 * find ourselves giving up on a puzzle without declaring it
834 * impossible. */
835 assert(ret != diff_unfinished);
836
837 /*
838 * If we have our first solution, copy it into the
839 * grid we will return.
840 */
841 if (diff == diff_impossible && ret != diff_impossible)
842 memcpy(solver->grid, outgrid, o*o);
843
844 if (ret == diff_ambiguous)
845 diff = diff_ambiguous;
846 else if (ret == diff_impossible)
847 /* do not change our return value */;
848 else {
849 /* the recursion turned up exactly one solution */
850 if (diff == diff_impossible)
851 diff = diff_recursive;
852 else
853 diff = diff_ambiguous;
854 }
855
856 /*
857 * As soon as we've found more than one solution,
858 * give up immediately.
859 */
860 if (diff == diff_ambiguous)
861 break;
862 }
863
864 sfree(outgrid);
865 sfree(ingrid);
866 sfree(list);
867
868 if (diff == diff_impossible)
869 return -1;
870 else if (diff == diff_ambiguous)
871 return 2;
872 else {
873 assert(diff == diff_recursive);
874 return 1;
875 }
876 }
877}
878
879static int latin_solver_top(struct latin_solver *solver, int maxdiff,
880 int diff_simple, int diff_set_0, int diff_set_1,
881 int diff_forcing, int diff_recursive,
882 usersolver_t const *usersolvers, void *ctx,
883 ctxnew_t ctxnew, ctxfree_t ctxfree)
884{
885 struct latin_solver_scratch *scratch = latin_solver_new_scratch(solver);
886 int ret, diff = diff_simple;
887
888 assert(maxdiff <= diff_recursive);
889 /*
890 * Now loop over the grid repeatedly trying all permitted modes
891 * of reasoning. The loop terminates if we complete an
892 * iteration without making any progress; we then return
893 * failure or success depending on whether the grid is full or
894 * not.
895 */
896 while (1) {
897 int i;
898
899 cont:
900
901 latin_solver_debug(solver->cube, solver->o);
902
903 for (i = 0; i <= maxdiff; i++) {
904 if (usersolvers[i])
905 ret = usersolvers[i](solver, ctx);
906 else
907 ret = 0;
908 if (ret == 0 && i == diff_simple)
909 ret = latin_solver_diff_simple(solver);
910 if (ret == 0 && i == diff_set_0)
911 ret = latin_solver_diff_set(solver, scratch, 0);
912 if (ret == 0 && i == diff_set_1)
913 ret = latin_solver_diff_set(solver, scratch, 1);
914 if (ret == 0 && i == diff_forcing)
915 ret = latin_solver_forcing(solver, scratch);
916
917 if (ret < 0) {
918 diff = diff_impossible;
919 goto got_result;
920 } else if (ret > 0) {
921 diff = max(diff, i);
922 goto cont;
923 }
924 }
925
926 /*
927 * If we reach here, we have made no deductions in this
928 * iteration, so the algorithm terminates.
929 */
930 break;
931 }
932
933 /*
934 * Last chance: if we haven't fully solved the puzzle yet, try
935 * recursing based on guesses for a particular square. We pick
936 * one of the most constrained empty squares we can find, which
937 * has the effect of pruning the search tree as much as
938 * possible.
939 */
940 if (maxdiff == diff_recursive) {
941 int nsol = latin_solver_recurse(solver,
942 diff_simple, diff_set_0, diff_set_1,
943 diff_forcing, diff_recursive,
944 usersolvers, ctx, ctxnew, ctxfree);
945 if (nsol < 0) diff = diff_impossible;
946 else if (nsol == 1) diff = diff_recursive;
947 else if (nsol > 1) diff = diff_ambiguous;
948 /* if nsol == 0 then we were complete anyway
949 * (and thus don't need to change diff) */
950 } else {
951 /*
952 * We're forbidden to use recursion, so we just see whether
953 * our grid is fully solved, and return diff_unfinished
954 * otherwise.
955 */
956 int x, y, o = solver->o;
957
958 for (y = 0; y < o; y++)
959 for (x = 0; x < o; x++)
960 if (!solver->grid[y*o+x])
961 diff = diff_unfinished;
962 }
963
964 got_result:
965
966#ifdef STANDALONE_SOLVER
967 if (solver_show_working)
968 printf("%*s%s found\n",
969 solver_recurse_depth*4, "",
970 diff == diff_impossible ? "no solution (impossible)" :
971 diff == diff_unfinished ? "no solution (unfinished)" :
972 diff == diff_ambiguous ? "multiple solutions" :
973 "one solution");
974#endif
975
976 latin_solver_free_scratch(scratch);
977
978 return diff;
979}
980
981int latin_solver_main(struct latin_solver *solver, int maxdiff,
982 int diff_simple, int diff_set_0, int diff_set_1,
983 int diff_forcing, int diff_recursive,
984 usersolver_t const *usersolvers, void *ctx,
985 ctxnew_t ctxnew, ctxfree_t ctxfree)
986{
987 int diff;
988#ifdef STANDALONE_SOLVER
989 int o = solver->o;
990 char *text = NULL, **names = NULL;
991#endif
992
993#ifdef STANDALONE_SOLVER
994 if (!solver->names) {
995 char *p;
996 int i;
997
998 text = snewn(40 * o, char);
999 p = text;
1000
1001 solver->names = snewn(o, char *);
1002
1003 for (i = 0; i < o; i++) {
1004 solver->names[i] = p;
1005 p += 1 + sprintf(p, "%d", i+1);
1006 }
1007 }
1008#endif
1009
1010 diff = latin_solver_top(solver, maxdiff,
1011 diff_simple, diff_set_0, diff_set_1,
1012 diff_forcing, diff_recursive,
1013 usersolvers, ctx, ctxnew, ctxfree);
1014
1015#ifdef STANDALONE_SOLVER
1016 sfree(names);
1017 sfree(text);
1018#endif
1019
1020 return diff;
1021}
1022
1023int latin_solver(digit *grid, int o, int maxdiff,
1024 int diff_simple, int diff_set_0, int diff_set_1,
1025 int diff_forcing, int diff_recursive,
1026 usersolver_t const *usersolvers, void *ctx,
1027 ctxnew_t ctxnew, ctxfree_t ctxfree)
1028{
1029 struct latin_solver solver;
1030 int diff;
1031
1032 latin_solver_alloc(&solver, grid, o);
1033 diff = latin_solver_main(&solver, maxdiff,
1034 diff_simple, diff_set_0, diff_set_1,
1035 diff_forcing, diff_recursive,
1036 usersolvers, ctx, ctxnew, ctxfree);
1037 latin_solver_free(&solver);
1038 return diff;
1039}
1040
1041void latin_solver_debug(unsigned char *cube, int o)
1042{
1043#ifdef STANDALONE_SOLVER
1044 if (solver_show_working > 1) {
1045 struct latin_solver ls, *solver = &ls;
1046 char *dbg;
1047 int x, y, i, c = 0;
1048
1049 ls.cube = cube; ls.o = o; /* for cube() to work */
1050
1051 dbg = snewn(3*o*o*o, char);
1052 for (y = 0; y < o; y++) {
1053 for (x = 0; x < o; x++) {
1054 for (i = 1; i <= o; i++) {
1055 if (cube(x,y,i))
1056 dbg[c++] = i + '0';
1057 else
1058 dbg[c++] = '.';
1059 }
1060 dbg[c++] = ' ';
1061 }
1062 dbg[c++] = '\n';
1063 }
1064 dbg[c++] = '\n';
1065 dbg[c++] = '\0';
1066
1067 printf("%s", dbg);
1068 sfree(dbg);
1069 }
1070#endif
1071}
1072
1073void latin_debug(digit *sq, int o)
1074{
1075#ifdef STANDALONE_SOLVER
1076 if (solver_show_working) {
1077 int x, y;
1078
1079 for (y = 0; y < o; y++) {
1080 for (x = 0; x < o; x++) {
1081 printf("%2d ", sq[y*o+x]);
1082 }
1083 printf("\n");
1084 }
1085 printf("\n");
1086 }
1087#endif
1088}
1089
1090/* --------------------------------------------------------
1091 * Generation.
1092 */
1093
1094digit *latin_generate(int o, random_state *rs)
1095{
1096 digit *sq;
1097 int *edges, *backedges, *capacity, *flow;
1098 void *scratch;
1099 int ne, scratchsize;
1100 int i, j, k;
1101 digit *row, *col, *numinv, *num;
1102
1103 /*
1104 * To efficiently generate a latin square in such a way that
1105 * all possible squares are possible outputs from the function,
1106 * we make use of a theorem which states that any r x n latin
1107 * rectangle, with r < n, can be extended into an (r+1) x n
1108 * latin rectangle. In other words, we can reliably generate a
1109 * latin square row by row, by at every stage writing down any
1110 * row at all which doesn't conflict with previous rows, and
1111 * the theorem guarantees that we will never have to backtrack.
1112 *
1113 * To find a viable row at each stage, we can make use of the
1114 * support functions in maxflow.c.
1115 */
1116
1117 sq = snewn(o*o, digit);
1118
1119 /*
1120 * In case this method of generation introduces a really subtle
1121 * top-to-bottom directional bias, we'll generate the rows in
1122 * random order.
1123 */
1124 row = snewn(o, digit);
1125 col = snewn(o, digit);
1126 numinv = snewn(o, digit);
1127 num = snewn(o, digit);
1128 for (i = 0; i < o; i++)
1129 row[i] = i;
1130 shuffle(row, i, sizeof(*row), rs);
1131
1132 /*
1133 * Set up the infrastructure for the maxflow algorithm.
1134 */
1135 scratchsize = maxflow_scratch_size(o * 2 + 2);
1136 scratch = smalloc(scratchsize);
1137 backedges = snewn(o*o + 2*o, int);
1138 edges = snewn((o*o + 2*o) * 2, int);
1139 capacity = snewn(o*o + 2*o, int);
1140 flow = snewn(o*o + 2*o, int);
1141 /* Set up the edge array, and the initial capacities. */
1142 ne = 0;
1143 for (i = 0; i < o; i++) {
1144 /* Each LHS vertex is connected to all RHS vertices. */
1145 for (j = 0; j < o; j++) {
1146 edges[ne*2] = i;
1147 edges[ne*2+1] = j+o;
1148 /* capacity for this edge is set later on */
1149 ne++;
1150 }
1151 }
1152 for (i = 0; i < o; i++) {
1153 /* Each RHS vertex is connected to the distinguished sink vertex. */
1154 edges[ne*2] = i+o;
1155 edges[ne*2+1] = o*2+1;
1156 capacity[ne] = 1;
1157 ne++;
1158 }
1159 for (i = 0; i < o; i++) {
1160 /* And the distinguished source vertex connects to each LHS vertex. */
1161 edges[ne*2] = o*2;
1162 edges[ne*2+1] = i;
1163 capacity[ne] = 1;
1164 ne++;
1165 }
1166 assert(ne == o*o + 2*o);
1167 /* Now set up backedges. */
1168 maxflow_setup_backedges(ne, edges, backedges);
1169
1170 /*
1171 * Now generate each row of the latin square.
1172 */
1173 for (i = 0; i < o; i++) {
1174 /*
1175 * To prevent maxflow from behaving deterministically, we
1176 * separately permute the columns and the digits for the
1177 * purposes of the algorithm, differently for every row.
1178 */
1179 for (j = 0; j < o; j++)
1180 col[j] = num[j] = j;
1181 shuffle(col, j, sizeof(*col), rs);
1182 shuffle(num, j, sizeof(*num), rs);
1183 /* We need the num permutation in both forward and inverse forms. */
1184 for (j = 0; j < o; j++)
1185 numinv[num[j]] = j;
1186
1187 /*
1188 * Set up the capacities for the maxflow run, by examining
1189 * the existing latin square.
1190 */
1191 for (j = 0; j < o*o; j++)
1192 capacity[j] = 1;
1193 for (j = 0; j < i; j++)
1194 for (k = 0; k < o; k++) {
1195 int n = num[sq[row[j]*o + col[k]] - 1];
1196 capacity[k*o+n] = 0;
1197 }
1198
1199 /*
1200 * Run maxflow.
1201 */
1202 j = maxflow_with_scratch(scratch, o*2+2, 2*o, 2*o+1, ne,
1203 edges, backedges, capacity, flow, NULL);
1204 assert(j == o); /* by the above theorem, this must have succeeded */
1205
1206 /*
1207 * And examine the flow array to pick out the new row of
1208 * the latin square.
1209 */
1210 for (j = 0; j < o; j++) {
1211 for (k = 0; k < o; k++) {
1212 if (flow[j*o+k])
1213 break;
1214 }
1215 assert(k < o);
1216 sq[row[i]*o + col[j]] = numinv[k] + 1;
1217 }
1218 }
1219
1220 /*
1221 * Done. Free our internal workspaces...
1222 */
1223 sfree(flow);
1224 sfree(capacity);
1225 sfree(edges);
1226 sfree(backedges);
1227 sfree(scratch);
1228 sfree(numinv);
1229 sfree(num);
1230 sfree(col);
1231 sfree(row);
1232
1233 /*
1234 * ... and return our completed latin square.
1235 */
1236 return sq;
1237}
1238
1239digit *latin_generate_rect(int w, int h, random_state *rs)
1240{
1241 int o = max(w, h), x, y;
1242 digit *latin, *latin_rect;
1243
1244 latin = latin_generate(o, rs);
1245 latin_rect = snewn(w*h, digit);
1246
1247 for (x = 0; x < w; x++) {
1248 for (y = 0; y < h; y++) {
1249 latin_rect[y*w + x] = latin[y*o + x];
1250 }
1251 }
1252
1253 sfree(latin);
1254 return latin_rect;
1255}
1256
1257/* --------------------------------------------------------
1258 * Checking.
1259 */
1260
1261typedef struct lcparams {
1262 digit elt;
1263 int count;
1264} lcparams;
1265
1266static int latin_check_cmp(void *v1, void *v2)
1267{
1268 lcparams *lc1 = (lcparams *)v1;
1269 lcparams *lc2 = (lcparams *)v2;
1270
1271 if (lc1->elt < lc2->elt) return -1;
1272 if (lc1->elt > lc2->elt) return 1;
1273 return 0;
1274}
1275
1276#define ELT(sq,x,y) (sq[((y)*order)+(x)])
1277
1278/* returns non-zero if sq is not a latin square. */
1279int latin_check(digit *sq, int order)
1280{
1281 tree234 *dict = newtree234(latin_check_cmp);
1282 int c, r;
1283 int ret = 0;
1284 lcparams *lcp, lc, *aret;
1285
1286 /* Use a tree234 as a simple hash table, go through the square
1287 * adding elements as we go or incrementing their counts. */
1288 for (c = 0; c < order; c++) {
1289 for (r = 0; r < order; r++) {
1290 lc.elt = ELT(sq, c, r); lc.count = 0;
1291 lcp = find234(dict, &lc, NULL);
1292 if (!lcp) {
1293 lcp = snew(lcparams);
1294 lcp->elt = ELT(sq, c, r);
1295 lcp->count = 1;
1296 aret = add234(dict, lcp);
1297 assert(aret == lcp);
1298 } else {
1299 lcp->count++;
1300 }
1301 }
1302 }
1303
1304 /* There should be precisely 'order' letters in the alphabet,
1305 * each occurring 'order' times (making the OxO tree) */
1306 if (count234(dict) != order) ret = 1;
1307 else {
1308 for (c = 0; (lcp = index234(dict, c)) != NULL; c++) {
1309 if (lcp->count != order) ret = 1;
1310 }
1311 }
1312 for (c = 0; (lcp = index234(dict, c)) != NULL; c++)
1313 sfree(lcp);
1314 freetree234(dict);
1315
1316 return ret;
1317}
1318
1319
1320/* --------------------------------------------------------
1321 * Testing (and printing).
1322 */
1323
1324#ifdef STANDALONE_LATIN_TEST
1325
1326#include <stdio.h>
1327#include <time.h>
1328
1329const char *quis;
1330
1331static void latin_print(digit *sq, int order)
1332{
1333 int x, y;
1334
1335 for (y = 0; y < order; y++) {
1336 for (x = 0; x < order; x++) {
1337 printf("%2u ", ELT(sq, x, y));
1338 }
1339 printf("\n");
1340 }
1341 printf("\n");
1342}
1343
1344static void gen(int order, random_state *rs, int debug)
1345{
1346 digit *sq;
1347
1348 solver_show_working = debug;
1349
1350 sq = latin_generate(order, rs);
1351 latin_print(sq, order);
1352 if (latin_check(sq, order)) {
1353 fprintf(stderr, "Square is not a latin square!");
1354 exit(1);
1355 }
1356
1357 sfree(sq);
1358}
1359
1360void test_soak(int order, random_state *rs)
1361{
1362 digit *sq;
1363 int n = 0;
1364 time_t tt_start, tt_now, tt_last;
1365
1366 solver_show_working = 0;
1367 tt_now = tt_start = time(NULL);
1368
1369 while(1) {
1370 sq = latin_generate(order, rs);
1371 sfree(sq);
1372 n++;
1373
1374 tt_last = time(NULL);
1375 if (tt_last > tt_now) {
1376 tt_now = tt_last;
1377 printf("%d total, %3.1f/s\n", n,
1378 (double)n / (double)(tt_now - tt_start));
1379 }
1380 }
1381}
1382
1383void usage_exit(const char *msg)
1384{
1385 if (msg)
1386 fprintf(stderr, "%s: %s\n", quis, msg);
1387 fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
1388 exit(1);
1389}
1390
1391int main(int argc, char *argv[])
1392{
1393 int i, soak = 0;
1394 random_state *rs;
1395 time_t seed = time(NULL);
1396
1397 quis = argv[0];
1398 while (--argc > 0) {
1399 const char *p = *++argv;
1400 if (!strcmp(p, "--soak"))
1401 soak = 1;
1402 else if (!strcmp(p, "--seed")) {
1403 if (argc == 0)
1404 usage_exit("--seed needs an argument");
1405 seed = (time_t)atoi(*++argv);
1406 argc--;
1407 } else if (*p == '-')
1408 usage_exit("unrecognised option");
1409 else
1410 break; /* finished options */
1411 }
1412
1413 rs = random_new((void*)&seed, sizeof(time_t));
1414
1415 if (soak == 1) {
1416 if (argc != 1) usage_exit("only one argument for --soak");
1417 test_soak(atoi(*argv), rs);
1418 } else {
1419 if (argc > 0) {
1420 for (i = 0; i < argc; i++) {
1421 gen(atoi(*argv++), rs, 1);
1422 }
1423 } else {
1424 while (1) {
1425 i = random_upto(rs, 20) + 1;
1426 gen(i, rs, 0);
1427 }
1428 }
1429 }
1430 random_free(rs);
1431 return 0;
1432}
1433
1434#endif
1435
1436/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/latin.h b/apps/plugins/puzzles/src/latin.h
new file mode 100644
index 0000000000..4b09f16ce1
--- /dev/null
+++ b/apps/plugins/puzzles/src/latin.h
@@ -0,0 +1,122 @@
1#ifndef LATIN_H
2#define LATIN_H
3
4#include "puzzles.h"
5
6typedef unsigned char digit;
7
8/* --- Solver structures, definitions --- */
9
10#ifdef STANDALONE_SOLVER
11extern int solver_show_working, solver_recurse_depth;
12#endif
13
14struct latin_solver {
15 int o; /* order of latin square */
16 unsigned char *cube; /* o^3, indexed by x, y, and digit:
17 TRUE in that position indicates a possibility */
18 digit *grid; /* o^2, indexed by x and y: for final deductions */
19
20 unsigned char *row; /* o^2: row[y*cr+n-1] TRUE if n is in row y */
21 unsigned char *col; /* o^2: col[x*cr+n-1] TRUE if n is in col x */
22
23#ifdef STANDALONE_SOLVER
24 char **names; /* o: names[n-1] gives name of 'digit' n */
25#endif
26};
27#define cubepos(x,y,n) (((x)*solver->o+(y))*solver->o+(n)-1)
28#define cube(x,y,n) (solver->cube[cubepos(x,y,n)])
29
30#define gridpos(x,y) ((y)*solver->o+(x))
31#define grid(x,y) (solver->grid[gridpos(x,y)])
32
33
34/* --- Solver individual strategies --- */
35
36/* Place a value at a specific location. */
37void latin_solver_place(struct latin_solver *solver, int x, int y, int n);
38
39/* Positional elimination. */
40int latin_solver_elim(struct latin_solver *solver, int start, int step
41#ifdef STANDALONE_SOLVER
42 , char *fmt, ...
43#endif
44 );
45
46struct latin_solver_scratch; /* private to latin.c */
47/* Set elimination */
48int latin_solver_set(struct latin_solver *solver,
49 struct latin_solver_scratch *scratch,
50 int start, int step1, int step2
51#ifdef STANDALONE_SOLVER
52 , char *fmt, ...
53#endif
54 );
55
56/* Forcing chains */
57int latin_solver_forcing(struct latin_solver *solver,
58 struct latin_solver_scratch *scratch);
59
60
61/* --- Solver allocation --- */
62
63/* Fills in (and allocates members for) a latin_solver struct.
64 * Will allocate members of snew, but not snew itself
65 * (allowing 'struct latin_solver' to be the first element in a larger
66 * struct, for example). */
67void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o);
68void latin_solver_free(struct latin_solver *solver);
69
70/* Allocates scratch space (for _set and _forcing) */
71struct latin_solver_scratch *
72 latin_solver_new_scratch(struct latin_solver *solver);
73void latin_solver_free_scratch(struct latin_solver_scratch *scratch);
74
75
76/* --- Solver guts --- */
77
78/* Looped positional elimination */
79int latin_solver_diff_simple(struct latin_solver *solver);
80
81/* Looped set elimination; *extreme is set if it used
82 * the more difficult single-number elimination. */
83int latin_solver_diff_set(struct latin_solver *solver,
84 struct latin_solver_scratch *scratch,
85 int extreme);
86
87typedef int (*usersolver_t)(struct latin_solver *solver, void *ctx);
88typedef void *(*ctxnew_t)(void *ctx);
89typedef void (*ctxfree_t)(void *ctx);
90
91/* Individual puzzles should use their enumerations for their
92 * own difficulty levels, ensuring they don't clash with these. */
93enum { diff_impossible = 10, diff_ambiguous, diff_unfinished };
94
95/* Externally callable function that allocates and frees a latin_solver */
96int latin_solver(digit *grid, int o, int maxdiff,
97 int diff_simple, int diff_set_0, int diff_set_1,
98 int diff_forcing, int diff_recursive,
99 usersolver_t const *usersolvers, void *ctx,
100 ctxnew_t ctxnew, ctxfree_t ctxfree);
101
102/* Version you can call if you want to alloc and free latin_solver yourself */
103int latin_solver_main(struct latin_solver *solver, int maxdiff,
104 int diff_simple, int diff_set_0, int diff_set_1,
105 int diff_forcing, int diff_recursive,
106 usersolver_t const *usersolvers, void *ctx,
107 ctxnew_t ctxnew, ctxfree_t ctxfree);
108
109void latin_solver_debug(unsigned char *cube, int o);
110
111/* --- Generation and checking --- */
112
113digit *latin_generate(int o, random_state *rs);
114
115/* The order of the latin rectangle is max(w,h). */
116digit *latin_generate_rect(int w, int h, random_state *rs);
117
118int latin_check(digit *sq, int order); /* !0 => not a latin square */
119
120void latin_debug(digit *sq, int order);
121
122#endif
diff --git a/apps/plugins/puzzles/src/laydomino.c b/apps/plugins/puzzles/src/laydomino.c
new file mode 100644
index 0000000000..cead5d5a7f
--- /dev/null
+++ b/apps/plugins/puzzles/src/laydomino.c
@@ -0,0 +1,291 @@
1/*
2 * laydomino.c: code for performing a domino (2x1 tile) layout of
3 * a given area of code.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <assert.h>
9
10#include "puzzles.h"
11
12/*
13 * This function returns an array size w x h representing a grid:
14 * each grid[i] = j, where j is the other end of a 2x1 domino.
15 * If w*h is odd, one square will remain referring to itself.
16 */
17
18int *domino_layout(int w, int h, random_state *rs)
19{
20 int *grid, *grid2, *list;
21 int wh = w*h;
22
23 /*
24 * Allocate space in which to lay the grid out.
25 */
26 grid = snewn(wh, int);
27 grid2 = snewn(wh, int);
28 list = snewn(2*wh, int);
29
30 domino_layout_prealloc(w, h, rs, grid, grid2, list);
31
32 sfree(grid2);
33 sfree(list);
34
35 return grid;
36}
37
38/*
39 * As for domino_layout, but with preallocated buffers.
40 * grid and grid2 should be size w*h, and list size 2*w*h.
41 */
42void domino_layout_prealloc(int w, int h, random_state *rs,
43 int *grid, int *grid2, int *list)
44{
45 int i, j, k, m, wh = w*h, todo, done;
46
47 /*
48 * To begin with, set grid[i] = i for all i to indicate
49 * that all squares are currently singletons. Later we'll
50 * set grid[i] to be the index of the other end of the
51 * domino on i.
52 */
53 for (i = 0; i < wh; i++)
54 grid[i] = i;
55
56 /*
57 * Now prepare a list of the possible domino locations. There
58 * are w*(h-1) possible vertical locations, and (w-1)*h
59 * horizontal ones, for a total of 2*wh - h - w.
60 *
61 * I'm going to denote the vertical domino placement with
62 * its top in square i as 2*i, and the horizontal one with
63 * its left half in square i as 2*i+1.
64 */
65 k = 0;
66 for (j = 0; j < h-1; j++)
67 for (i = 0; i < w; i++)
68 list[k++] = 2 * (j*w+i); /* vertical positions */
69 for (j = 0; j < h; j++)
70 for (i = 0; i < w-1; i++)
71 list[k++] = 2 * (j*w+i) + 1; /* horizontal positions */
72 assert(k == 2*wh - h - w);
73
74 /*
75 * Shuffle the list.
76 */
77 shuffle(list, k, sizeof(*list), rs);
78
79 /*
80 * Work down the shuffled list, placing a domino everywhere
81 * we can.
82 */
83 for (i = 0; i < k; i++) {
84 int horiz, xy, xy2;
85
86 horiz = list[i] % 2;
87 xy = list[i] / 2;
88 xy2 = xy + (horiz ? 1 : w);
89
90 if (grid[xy] == xy && grid[xy2] == xy2) {
91 /*
92 * We can place this domino. Do so.
93 */
94 grid[xy] = xy2;
95 grid[xy2] = xy;
96 }
97 }
98
99#ifdef GENERATION_DIAGNOSTICS
100 printf("generated initial layout\n");
101#endif
102
103 /*
104 * Now we've placed as many dominoes as we can immediately
105 * manage. There will be squares remaining, but they'll be
106 * singletons. So loop round and deal with the singletons
107 * two by two.
108 */
109 while (1) {
110#ifdef GENERATION_DIAGNOSTICS
111 for (j = 0; j < h; j++) {
112 for (i = 0; i < w; i++) {
113 int xy = j*w+i;
114 int v = grid[xy];
115 int c = (v == xy+1 ? '[' : v == xy-1 ? ']' :
116 v == xy+w ? 'n' : v == xy-w ? 'U' : '.');
117 putchar(c);
118 }
119 putchar('\n');
120 }
121 putchar('\n');
122#endif
123
124 /*
125 * Our strategy is:
126 *
127 * First find a singleton square.
128 *
129 * Then breadth-first search out from the starting
130 * square. From that square (and any others we reach on
131 * the way), examine all four neighbours of the square.
132 * If one is an end of a domino, we move to the _other_
133 * end of that domino before looking at neighbours
134 * again. When we encounter another singleton on this
135 * search, stop.
136 *
137 * This will give us a path of adjacent squares such
138 * that all but the two ends are covered in dominoes.
139 * So we can now shuffle every domino on the path up by
140 * one.
141 *
142 * (Chessboard colours are mathematically important
143 * here: we always end up pairing each singleton with a
144 * singleton of the other colour. However, we never
145 * have to track this manually, since it's
146 * automatically taken care of by the fact that we
147 * always make an even number of orthogonal moves.)
148 */
149 k = 0;
150 for (j = 0; j < wh; j++) {
151 if (grid[j] == j) {
152 k++;
153 i = j; /* start BFS here. */
154 }
155 }
156 if (k == (wh % 2))
157 break; /* if area is even, we have no more singletons;
158 if area is odd, we have one singleton.
159 either way, we're done. */
160
161#ifdef GENERATION_DIAGNOSTICS
162 printf("starting b.f.s. at singleton %d\n", i);
163#endif
164 /*
165 * Set grid2 to -1 everywhere. It will hold our
166 * distance-from-start values, and also our
167 * backtracking data, during the b.f.s.
168 */
169 for (j = 0; j < wh; j++)
170 grid2[j] = -1;
171 grid2[i] = 0; /* starting square has distance zero */
172
173 /*
174 * Start our to-do list of squares. It'll live in
175 * `list'; since the b.f.s can cover every square at
176 * most once there is no need for it to be circular.
177 * We'll just have two counters tracking the end of the
178 * list and the squares we've already dealt with.
179 */
180 done = 0;
181 todo = 1;
182 list[0] = i;
183
184 /*
185 * Now begin the b.f.s. loop.
186 */
187 while (done < todo) {
188 int d[4], nd, x, y;
189
190 i = list[done++];
191
192#ifdef GENERATION_DIAGNOSTICS
193 printf("b.f.s. iteration from %d\n", i);
194#endif
195 x = i % w;
196 y = i / w;
197 nd = 0;
198 if (x > 0)
199 d[nd++] = i - 1;
200 if (x+1 < w)
201 d[nd++] = i + 1;
202 if (y > 0)
203 d[nd++] = i - w;
204 if (y+1 < h)
205 d[nd++] = i + w;
206 /*
207 * To avoid directional bias, process the
208 * neighbours of this square in a random order.
209 */
210 shuffle(d, nd, sizeof(*d), rs);
211
212 for (j = 0; j < nd; j++) {
213 k = d[j];
214 if (grid[k] == k) {
215#ifdef GENERATION_DIAGNOSTICS
216 printf("found neighbouring singleton %d\n", k);
217#endif
218 grid2[k] = i;
219 break; /* found a target singleton! */
220 }
221
222 /*
223 * We're moving through a domino here, so we
224 * have two entries in grid2 to fill with
225 * useful data. In grid[k] - the square
226 * adjacent to where we came from - I'm going
227 * to put the address _of_ the square we came
228 * from. In the other end of the domino - the
229 * square from which we will continue the
230 * search - I'm going to put the distance.
231 */
232 m = grid[k];
233
234 if (grid2[m] < 0 || grid2[m] > grid2[i]+1) {
235#ifdef GENERATION_DIAGNOSTICS
236 printf("found neighbouring domino %d/%d\n", k, m);
237#endif
238 grid2[m] = grid2[i]+1;
239 grid2[k] = i;
240 /*
241 * And since we've now visited a new
242 * domino, add m to the to-do list.
243 */
244 assert(todo < wh);
245 list[todo++] = m;
246 }
247 }
248
249 if (j < nd) {
250 i = k;
251#ifdef GENERATION_DIAGNOSTICS
252 printf("terminating b.f.s. loop, i = %d\n", i);
253#endif
254 break;
255 }
256
257 i = -1; /* just in case the loop terminates */
258 }
259
260 /*
261 * We expect this b.f.s. to have found us a target
262 * square.
263 */
264 assert(i >= 0);
265
266 /*
267 * Now we can follow the trail back to our starting
268 * singleton, re-laying dominoes as we go.
269 */
270 while (1) {
271 j = grid2[i];
272 assert(j >= 0 && j < wh);
273 k = grid[j];
274
275 grid[i] = j;
276 grid[j] = i;
277#ifdef GENERATION_DIAGNOSTICS
278 printf("filling in domino %d/%d (next %d)\n", i, j, k);
279#endif
280 if (j == k)
281 break; /* we've reached the other singleton */
282 i = k;
283 }
284#ifdef GENERATION_DIAGNOSTICS
285 printf("fixup path completed\n");
286#endif
287 }
288}
289
290/* vim: set shiftwidth=4 :set textwidth=80: */
291
diff --git a/apps/plugins/puzzles/src/licence.html b/apps/plugins/puzzles/src/licence.html
new file mode 100644
index 0000000000..17501348e4
--- /dev/null
+++ b/apps/plugins/puzzles/src/licence.html
@@ -0,0 +1,33 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Licence</title>
7<link rel="previous" href="palisade.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="docindex.html">
12</head>
13<body>
14<p><a href="palisade.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="docindex.html">Next</a></p>
15<h1><a name="AA"></a>Appendix A: <a name="i0"></a><a name="i1"></a>Licence</h1>
16<p>
17This software is <a name="i2"></a>copyright 2004-2014 Simon Tatham.
18</p>
19<p>
20Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas K&#246;lker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens.
21</p>
22<p>
23Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#8216;Software&#8217;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
24</p>
25<p>
26The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
27</p>
28<p>
29THE SOFTWARE IS PROVIDED &#8216;AS IS&#8217;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30</p>
31
32<hr><address></address></body>
33</html>
diff --git a/apps/plugins/puzzles/src/lightup.R b/apps/plugins/puzzles/src/lightup.R
new file mode 100644
index 0000000000..a474de815d
--- /dev/null
+++ b/apps/plugins/puzzles/src/lightup.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3LIGHTUP_EXTRA = combi
4
5lightup : [X] GTK COMMON lightup LIGHTUP_EXTRA lightup-icon|no-icon
6
7lightup : [G] WINDOWS COMMON lightup LIGHTUP_EXTRA lightup.res|noicon.res
8
9lightupsolver : [U] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE
10lightupsolver : [C] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE
11
12ALL += lightup[COMBINED] LIGHTUP_EXTRA
13
14!begin am gtk
15GAMES += lightup
16!end
17
18!begin >list.c
19 A(lightup) \
20!end
21
22!begin >gamedesc.txt
23lightup:lightup.exe:Light Up:Light-bulb placing puzzle:Place bulbs to light up all the squares.
24!end
diff --git a/apps/plugins/puzzles/src/lightup.c b/apps/plugins/puzzles/src/lightup.c
new file mode 100644
index 0000000000..4dd46c8392
--- /dev/null
+++ b/apps/plugins/puzzles/src/lightup.c
@@ -0,0 +1,2405 @@
1/*
2 * lightup.c: Implementation of the Nikoli game 'Light Up'.
3 *
4 * Possible future solver enhancements:
5 *
6 * - In a situation where two clues are diagonally adjacent, you can
7 * deduce bounds on the number of lights shared between them. For
8 * instance, suppose a 3 clue is diagonally adjacent to a 1 clue:
9 * of the two squares adjacent to both clues, at least one must be
10 * a light (or the 3 would be unsatisfiable) and yet at most one
11 * must be a light (or the 1 would be overcommitted), so in fact
12 * _exactly_ one must be a light, and hence the other two squares
13 * adjacent to the 3 must also be lights and the other two adjacent
14 * to the 1 must not. Likewise if the 3 is replaced with a 2 but
15 * one of its other two squares is known not to be a light, and so
16 * on.
17 *
18 * - In a situation where two clues are orthogonally separated (not
19 * necessarily directly adjacent), you may be able to deduce
20 * something about the squares that align with each other. For
21 * instance, suppose two clues are vertically adjacent. Consider
22 * the pair of squares A,B horizontally adjacent to the top clue,
23 * and the pair C,D horizontally adjacent to the bottom clue.
24 * Assuming no intervening obstacles, A and C align with each other
25 * and hence at most one of them can be a light, and B and D
26 * likewise, so we must have at most two lights between the four
27 * squares. So if the clues indicate that there are at _least_ two
28 * lights in those four squares because the top clue requires at
29 * least one of AB to be a light and the bottom one requires at
30 * least one of CD, then we can in fact deduce that there are
31 * _exactly_ two lights between the four squares, and fill in the
32 * other squares adjacent to each clue accordingly. For instance,
33 * if both clues are 3s, then we instantly deduce that all four of
34 * the squares _vertically_ adjacent to the two clues must be
35 * lights. (For that to happen, of course, there'd also have to be
36 * a black square in between the clues, so the two inner lights
37 * don't light each other.)
38 *
39 * - I haven't thought it through carefully, but there's always the
40 * possibility that both of the above deductions are special cases
41 * of some more general pattern which can be made computationally
42 * feasible...
43 */
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <assert.h>
49#include <ctype.h>
50#include <math.h>
51
52#include "puzzles.h"
53
54/*
55 * In standalone solver mode, `verbose' is a variable which can be
56 * set by command-line option; in debugging mode it's simply always
57 * true.
58 */
59#if defined STANDALONE_SOLVER
60#define SOLVER_DIAGNOSTICS
61int verbose = 0;
62#undef debug
63#define debug(x) printf x
64#elif defined SOLVER_DIAGNOSTICS
65#define verbose 2
66#endif
67
68/* --- Constants, structure definitions, etc. --- */
69
70#define PREFERRED_TILE_SIZE 32
71#define TILE_SIZE (ds->tilesize)
72#define BORDER (TILE_SIZE / 2)
73#define TILE_RADIUS (ds->crad)
74
75#define COORD(x) ( (x) * TILE_SIZE + BORDER )
76#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
77
78#define FLASH_TIME 0.30F
79
80enum {
81 COL_BACKGROUND,
82 COL_GRID,
83 COL_BLACK, /* black */
84 COL_LIGHT, /* white */
85 COL_LIT, /* yellow */
86 COL_ERROR, /* red */
87 COL_CURSOR,
88 NCOLOURS
89};
90
91enum { SYMM_NONE, SYMM_REF2, SYMM_ROT2, SYMM_REF4, SYMM_ROT4, SYMM_MAX };
92
93#define DIFFCOUNT 2
94
95struct game_params {
96 int w, h;
97 int blackpc; /* %age of black squares */
98 int symm;
99 int difficulty; /* 0 to DIFFCOUNT */
100};
101
102#define F_BLACK 1
103
104/* flags for black squares */
105#define F_NUMBERED 2 /* it has a number attached */
106#define F_NUMBERUSED 4 /* this number was useful for solving */
107
108/* flags for non-black squares */
109#define F_IMPOSSIBLE 8 /* can't put a light here */
110#define F_LIGHT 16
111
112#define F_MARK 32
113
114struct game_state {
115 int w, h, nlights;
116 int *lights; /* For black squares, (optionally) the number
117 of surrounding lights. For non-black squares,
118 the number of times it's lit. size h*w*/
119 unsigned int *flags; /* size h*w */
120 int completed, used_solve;
121};
122
123#define GRID(gs,grid,x,y) (gs->grid[(y)*((gs)->w) + (x)])
124
125/* A ll_data holds information about which lights would be lit by
126 * a particular grid location's light (or conversely, which locations
127 * could light a specific other location). */
128/* most things should consider this struct opaque. */
129typedef struct {
130 int ox,oy;
131 int minx, maxx, miny, maxy;
132 int include_origin;
133} ll_data;
134
135/* Macro that executes 'block' once per light in lld, including
136 * the origin if include_origin is specified. 'block' can use
137 * lx and ly as the coords. */
138#define FOREACHLIT(lld,block) do { \
139 int lx,ly; \
140 ly = (lld)->oy; \
141 for (lx = (lld)->minx; lx <= (lld)->maxx; lx++) { \
142 if (lx == (lld)->ox) continue; \
143 block \
144 } \
145 lx = (lld)->ox; \
146 for (ly = (lld)->miny; ly <= (lld)->maxy; ly++) { \
147 if (!(lld)->include_origin && ly == (lld)->oy) continue; \
148 block \
149 } \
150} while(0)
151
152
153typedef struct {
154 struct { int x, y; unsigned int f; } points[4];
155 int npoints;
156} surrounds;
157
158/* Fills in (doesn't allocate) a surrounds structure with the grid locations
159 * around a given square, taking account of the edges. */
160static void get_surrounds(const game_state *state, int ox, int oy,
161 surrounds *s)
162{
163 assert(ox >= 0 && ox < state->w && oy >= 0 && oy < state->h);
164 s->npoints = 0;
165#define ADDPOINT(cond,nx,ny) do {\
166 if (cond) { \
167 s->points[s->npoints].x = (nx); \
168 s->points[s->npoints].y = (ny); \
169 s->points[s->npoints].f = 0; \
170 s->npoints++; \
171 } } while(0)
172 ADDPOINT(ox > 0, ox-1, oy);
173 ADDPOINT(ox < (state->w-1), ox+1, oy);
174 ADDPOINT(oy > 0, ox, oy-1);
175 ADDPOINT(oy < (state->h-1), ox, oy+1);
176}
177
178/* --- Game parameter functions --- */
179
180#define DEFAULT_PRESET 0
181
182const struct game_params lightup_presets[] = {
183 { 7, 7, 20, SYMM_ROT4, 0 },
184 { 7, 7, 20, SYMM_ROT4, 1 },
185 { 7, 7, 20, SYMM_ROT4, 2 },
186 { 10, 10, 20, SYMM_ROT2, 0 },
187 { 10, 10, 20, SYMM_ROT2, 1 },
188#ifdef SLOW_SYSTEM
189 { 12, 12, 20, SYMM_ROT2, 0 },
190 { 12, 12, 20, SYMM_ROT2, 1 },
191#else
192 { 10, 10, 20, SYMM_ROT2, 2 },
193 { 14, 14, 20, SYMM_ROT2, 0 },
194 { 14, 14, 20, SYMM_ROT2, 1 },
195 { 14, 14, 20, SYMM_ROT2, 2 }
196#endif
197};
198
199static game_params *default_params(void)
200{
201 game_params *ret = snew(game_params);
202 *ret = lightup_presets[DEFAULT_PRESET];
203
204 return ret;
205}
206
207static int game_fetch_preset(int i, char **name, game_params **params)
208{
209 game_params *ret;
210 char buf[80];
211
212 if (i < 0 || i >= lenof(lightup_presets))
213 return FALSE;
214
215 ret = default_params();
216 *ret = lightup_presets[i];
217 *params = ret;
218
219 sprintf(buf, "%dx%d %s",
220 ret->w, ret->h,
221 ret->difficulty == 2 ? "hard" :
222 ret->difficulty == 1 ? "tricky" : "easy");
223 *name = dupstr(buf);
224
225 return TRUE;
226}
227
228static void free_params(game_params *params)
229{
230 sfree(params);
231}
232
233static game_params *dup_params(const game_params *params)
234{
235 game_params *ret = snew(game_params);
236 *ret = *params; /* structure copy */
237 return ret;
238}
239
240#define EATNUM(x) do { \
241 (x) = atoi(string); \
242 while (*string && isdigit((unsigned char)*string)) string++; \
243} while(0)
244
245static void decode_params(game_params *params, char const *string)
246{
247 EATNUM(params->w);
248 if (*string == 'x') {
249 string++;
250 EATNUM(params->h);
251 }
252 if (*string == 'b') {
253 string++;
254 EATNUM(params->blackpc);
255 }
256 if (*string == 's') {
257 string++;
258 EATNUM(params->symm);
259 } else {
260 /* cope with user input such as '18x10' by ensuring symmetry
261 * is not selected by default to be incompatible with dimensions */
262 if (params->symm == SYMM_ROT4 && params->w != params->h)
263 params->symm = SYMM_ROT2;
264 }
265 params->difficulty = 0;
266 /* cope with old params */
267 if (*string == 'r') {
268 params->difficulty = 2;
269 string++;
270 }
271 if (*string == 'd') {
272 string++;
273 EATNUM(params->difficulty);
274 }
275}
276
277static char *encode_params(const game_params *params, int full)
278{
279 char buf[80];
280
281 if (full) {
282 sprintf(buf, "%dx%db%ds%dd%d",
283 params->w, params->h, params->blackpc,
284 params->symm,
285 params->difficulty);
286 } else {
287 sprintf(buf, "%dx%d", params->w, params->h);
288 }
289 return dupstr(buf);
290}
291
292static config_item *game_configure(const game_params *params)
293{
294 config_item *ret;
295 char buf[80];
296
297 ret = snewn(6, config_item);
298
299 ret[0].name = "Width";
300 ret[0].type = C_STRING;
301 sprintf(buf, "%d", params->w);
302 ret[0].sval = dupstr(buf);
303 ret[0].ival = 0;
304
305 ret[1].name = "Height";
306 ret[1].type = C_STRING;
307 sprintf(buf, "%d", params->h);
308 ret[1].sval = dupstr(buf);
309 ret[1].ival = 0;
310
311 ret[2].name = "%age of black squares";
312 ret[2].type = C_STRING;
313 sprintf(buf, "%d", params->blackpc);
314 ret[2].sval = dupstr(buf);
315 ret[2].ival = 0;
316
317 ret[3].name = "Symmetry";
318 ret[3].type = C_CHOICES;
319 ret[3].sval = ":None"
320 ":2-way mirror:2-way rotational"
321 ":4-way mirror:4-way rotational";
322 ret[3].ival = params->symm;
323
324 ret[4].name = "Difficulty";
325 ret[4].type = C_CHOICES;
326 ret[4].sval = ":Easy:Tricky:Hard";
327 ret[4].ival = params->difficulty;
328
329 ret[5].name = NULL;
330 ret[5].type = C_END;
331 ret[5].sval = NULL;
332 ret[5].ival = 0;
333
334 return ret;
335}
336
337static game_params *custom_params(const config_item *cfg)
338{
339 game_params *ret = snew(game_params);
340
341 ret->w = atoi(cfg[0].sval);
342 ret->h = atoi(cfg[1].sval);
343 ret->blackpc = atoi(cfg[2].sval);
344 ret->symm = cfg[3].ival;
345 ret->difficulty = cfg[4].ival;
346
347 return ret;
348}
349
350static char *validate_params(const game_params *params, int full)
351{
352 if (params->w < 2 || params->h < 2)
353 return "Width and height must be at least 2";
354 if (full) {
355 if (params->blackpc < 5 || params->blackpc > 100)
356 return "Percentage of black squares must be between 5% and 100%";
357 if (params->w != params->h) {
358 if (params->symm == SYMM_ROT4)
359 return "4-fold symmetry is only available with square grids";
360 }
361 if (params->symm < 0 || params->symm >= SYMM_MAX)
362 return "Unknown symmetry type";
363 if (params->difficulty < 0 || params->difficulty > DIFFCOUNT)
364 return "Unknown difficulty level";
365 }
366 return NULL;
367}
368
369/* --- Game state construction/freeing helper functions --- */
370
371static game_state *new_state(const game_params *params)
372{
373 game_state *ret = snew(game_state);
374
375 ret->w = params->w;
376 ret->h = params->h;
377 ret->lights = snewn(ret->w * ret->h, int);
378 ret->nlights = 0;
379 memset(ret->lights, 0, ret->w * ret->h * sizeof(int));
380 ret->flags = snewn(ret->w * ret->h, unsigned int);
381 memset(ret->flags, 0, ret->w * ret->h * sizeof(unsigned int));
382 ret->completed = ret->used_solve = 0;
383 return ret;
384}
385
386static game_state *dup_game(const game_state *state)
387{
388 game_state *ret = snew(game_state);
389
390 ret->w = state->w;
391 ret->h = state->h;
392
393 ret->lights = snewn(ret->w * ret->h, int);
394 memcpy(ret->lights, state->lights, ret->w * ret->h * sizeof(int));
395 ret->nlights = state->nlights;
396
397 ret->flags = snewn(ret->w * ret->h, unsigned int);
398 memcpy(ret->flags, state->flags, ret->w * ret->h * sizeof(unsigned int));
399
400 ret->completed = state->completed;
401 ret->used_solve = state->used_solve;
402
403 return ret;
404}
405
406static void free_game(game_state *state)
407{
408 sfree(state->lights);
409 sfree(state->flags);
410 sfree(state);
411}
412
413static void debug_state(game_state *state)
414{
415 int x, y;
416 char c = '?';
417
418 for (y = 0; y < state->h; y++) {
419 for (x = 0; x < state->w; x++) {
420 c = '.';
421 if (GRID(state, flags, x, y) & F_BLACK) {
422 if (GRID(state, flags, x, y) & F_NUMBERED)
423 c = GRID(state, lights, x, y) + '0';
424 else
425 c = '#';
426 } else {
427 if (GRID(state, flags, x, y) & F_LIGHT)
428 c = 'O';
429 else if (GRID(state, flags, x, y) & F_IMPOSSIBLE)
430 c = 'X';
431 }
432 debug(("%c", (int)c));
433 }
434 debug((" "));
435 for (x = 0; x < state->w; x++) {
436 if (GRID(state, flags, x, y) & F_BLACK)
437 c = '#';
438 else {
439 c = (GRID(state, flags, x, y) & F_LIGHT) ? 'A' : 'a';
440 c += GRID(state, lights, x, y);
441 }
442 debug(("%c", (int)c));
443 }
444 debug(("\n"));
445 }
446}
447
448/* --- Game completion test routines. --- */
449
450/* These are split up because occasionally functions are only
451 * interested in one particular aspect. */
452
453/* Returns non-zero if all grid spaces are lit. */
454static int grid_lit(game_state *state)
455{
456 int x, y;
457
458 for (x = 0; x < state->w; x++) {
459 for (y = 0; y < state->h; y++) {
460 if (GRID(state,flags,x,y) & F_BLACK) continue;
461 if (GRID(state,lights,x,y) == 0)
462 return 0;
463 }
464 }
465 return 1;
466}
467
468/* Returns non-zero if any lights are lit by other lights. */
469static int grid_overlap(game_state *state)
470{
471 int x, y;
472
473 for (x = 0; x < state->w; x++) {
474 for (y = 0; y < state->h; y++) {
475 if (!(GRID(state, flags, x, y) & F_LIGHT)) continue;
476 if (GRID(state, lights, x, y) > 1)
477 return 1;
478 }
479 }
480 return 0;
481}
482
483static int number_wrong(const game_state *state, int x, int y)
484{
485 surrounds s;
486 int i, n, empty, lights = GRID(state, lights, x, y);
487
488 /*
489 * This function computes the display hint for a number: we
490 * turn the number red if it is definitely wrong. This means
491 * that either
492 *
493 * (a) it has too many lights around it, or
494 * (b) it would have too few lights around it even if all the
495 * plausible squares (not black, lit or F_IMPOSSIBLE) were
496 * filled with lights.
497 */
498
499 assert(GRID(state, flags, x, y) & F_NUMBERED);
500 get_surrounds(state, x, y, &s);
501
502 empty = n = 0;
503 for (i = 0; i < s.npoints; i++) {
504 if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_LIGHT) {
505 n++;
506 continue;
507 }
508 if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_BLACK)
509 continue;
510 if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_IMPOSSIBLE)
511 continue;
512 if (GRID(state,lights,s.points[i].x,s.points[i].y))
513 continue;
514 empty++;
515 }
516 return (n > lights || (n + empty < lights));
517}
518
519static int number_correct(game_state *state, int x, int y)
520{
521 surrounds s;
522 int n = 0, i, lights = GRID(state, lights, x, y);
523
524 assert(GRID(state, flags, x, y) & F_NUMBERED);
525 get_surrounds(state, x, y, &s);
526 for (i = 0; i < s.npoints; i++) {
527 if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_LIGHT)
528 n++;
529 }
530 return (n == lights) ? 1 : 0;
531}
532
533/* Returns non-zero if any numbers add up incorrectly. */
534static int grid_addsup(game_state *state)
535{
536 int x, y;
537
538 for (x = 0; x < state->w; x++) {
539 for (y = 0; y < state->h; y++) {
540 if (!(GRID(state, flags, x, y) & F_NUMBERED)) continue;
541 if (!number_correct(state, x, y)) return 0;
542 }
543 }
544 return 1;
545}
546
547static int grid_correct(game_state *state)
548{
549 if (grid_lit(state) &&
550 !grid_overlap(state) &&
551 grid_addsup(state)) return 1;
552 return 0;
553}
554
555/* --- Board initial setup (blacks, lights, numbers) --- */
556
557static void clean_board(game_state *state, int leave_blacks)
558{
559 int x,y;
560 for (x = 0; x < state->w; x++) {
561 for (y = 0; y < state->h; y++) {
562 if (leave_blacks)
563 GRID(state, flags, x, y) &= F_BLACK;
564 else
565 GRID(state, flags, x, y) = 0;
566 GRID(state, lights, x, y) = 0;
567 }
568 }
569 state->nlights = 0;
570}
571
572static void set_blacks(game_state *state, const game_params *params,
573 random_state *rs)
574{
575 int x, y, degree = 0, rotate = 0, nblack;
576 int rh, rw, i;
577 int wodd = (state->w % 2) ? 1 : 0;
578 int hodd = (state->h % 2) ? 1 : 0;
579 int xs[4], ys[4];
580
581 switch (params->symm) {
582 case SYMM_NONE: degree = 1; rotate = 0; break;
583 case SYMM_ROT2: degree = 2; rotate = 1; break;
584 case SYMM_REF2: degree = 2; rotate = 0; break;
585 case SYMM_ROT4: degree = 4; rotate = 1; break;
586 case SYMM_REF4: degree = 4; rotate = 0; break;
587 default: assert(!"Unknown symmetry type");
588 }
589 if (params->symm == SYMM_ROT4 && (state->h != state->w))
590 assert(!"4-fold symmetry unavailable without square grid");
591
592 if (degree == 4) {
593 rw = state->w/2;
594 rh = state->h/2;
595 if (!rotate) rw += wodd; /* ... but see below. */
596 rh += hodd;
597 } else if (degree == 2) {
598 rw = state->w;
599 rh = state->h/2;
600 rh += hodd;
601 } else {
602 rw = state->w;
603 rh = state->h;
604 }
605
606 /* clear, then randomise, required region. */
607 clean_board(state, 0);
608 nblack = (rw * rh * params->blackpc) / 100;
609 for (i = 0; i < nblack; i++) {
610 do {
611 x = random_upto(rs,rw);
612 y = random_upto(rs,rh);
613 } while (GRID(state,flags,x,y) & F_BLACK);
614 GRID(state, flags, x, y) |= F_BLACK;
615 }
616
617 /* Copy required region. */
618 if (params->symm == SYMM_NONE) return;
619
620 for (x = 0; x < rw; x++) {
621 for (y = 0; y < rh; y++) {
622 if (degree == 4) {
623 xs[0] = x;
624 ys[0] = y;
625 xs[1] = state->w - 1 - (rotate ? y : x);
626 ys[1] = rotate ? x : y;
627 xs[2] = rotate ? (state->w - 1 - x) : x;
628 ys[2] = state->h - 1 - y;
629 xs[3] = rotate ? y : (state->w - 1 - x);
630 ys[3] = state->h - 1 - (rotate ? x : y);
631 } else {
632 xs[0] = x;
633 ys[0] = y;
634 xs[1] = rotate ? (state->w - 1 - x) : x;
635 ys[1] = state->h - 1 - y;
636 }
637 for (i = 1; i < degree; i++) {
638 GRID(state, flags, xs[i], ys[i]) =
639 GRID(state, flags, xs[0], ys[0]);
640 }
641 }
642 }
643 /* SYMM_ROT4 misses the middle square above; fix that here. */
644 if (degree == 4 && rotate && wodd &&
645 (random_upto(rs,100) <= (unsigned int)params->blackpc))
646 GRID(state,flags,
647 state->w/2 + wodd - 1, state->h/2 + hodd - 1) |= F_BLACK;
648
649#ifdef SOLVER_DIAGNOSTICS
650 if (verbose) debug_state(state);
651#endif
652}
653
654/* Fills in (does not allocate) a ll_data with all the tiles that would
655 * be illuminated by a light at point (ox,oy). If origin=1 then the
656 * origin is included in this list. */
657static void list_lights(game_state *state, int ox, int oy, int origin,
658 ll_data *lld)
659{
660 int x,y;
661
662 lld->ox = lld->minx = lld->maxx = ox;
663 lld->oy = lld->miny = lld->maxy = oy;
664 lld->include_origin = origin;
665
666 y = oy;
667 for (x = ox-1; x >= 0; x--) {
668 if (GRID(state, flags, x, y) & F_BLACK) break;
669 if (x < lld->minx) lld->minx = x;
670 }
671 for (x = ox+1; x < state->w; x++) {
672 if (GRID(state, flags, x, y) & F_BLACK) break;
673 if (x > lld->maxx) lld->maxx = x;
674 }
675
676 x = ox;
677 for (y = oy-1; y >= 0; y--) {
678 if (GRID(state, flags, x, y) & F_BLACK) break;
679 if (y < lld->miny) lld->miny = y;
680 }
681 for (y = oy+1; y < state->h; y++) {
682 if (GRID(state, flags, x, y) & F_BLACK) break;
683 if (y > lld->maxy) lld->maxy = y;
684 }
685}
686
687/* Makes sure a light is the given state, editing the lights table to suit the
688 * new state if necessary. */
689static void set_light(game_state *state, int ox, int oy, int on)
690{
691 ll_data lld;
692 int diff = 0;
693
694 assert(!(GRID(state,flags,ox,oy) & F_BLACK));
695
696 if (!on && GRID(state,flags,ox,oy) & F_LIGHT) {
697 diff = -1;
698 GRID(state,flags,ox,oy) &= ~F_LIGHT;
699 state->nlights--;
700 } else if (on && !(GRID(state,flags,ox,oy) & F_LIGHT)) {
701 diff = 1;
702 GRID(state,flags,ox,oy) |= F_LIGHT;
703 state->nlights++;
704 }
705
706 if (diff != 0) {
707 list_lights(state,ox,oy,1,&lld);
708 FOREACHLIT(&lld, GRID(state,lights,lx,ly) += diff; );
709 }
710}
711
712/* Returns 1 if removing a light at (x,y) would cause a square to go dark. */
713static int check_dark(game_state *state, int x, int y)
714{
715 ll_data lld;
716
717 list_lights(state, x, y, 1, &lld);
718 FOREACHLIT(&lld, if (GRID(state,lights,lx,ly) == 1) { return 1; } );
719 return 0;
720}
721
722/* Sets up an initial random correct position (i.e. every
723 * space lit, and no lights lit by other lights) by filling the
724 * grid with lights and then removing lights one by one at random. */
725static void place_lights(game_state *state, random_state *rs)
726{
727 int i, x, y, n, *numindices, wh = state->w*state->h;
728 ll_data lld;
729
730 numindices = snewn(wh, int);
731 for (i = 0; i < wh; i++) numindices[i] = i;
732 shuffle(numindices, wh, sizeof(*numindices), rs);
733
734 /* Place a light on all grid squares without lights. */
735 for (x = 0; x < state->w; x++) {
736 for (y = 0; y < state->h; y++) {
737 GRID(state, flags, x, y) &= ~F_MARK; /* we use this later. */
738 if (GRID(state, flags, x, y) & F_BLACK) continue;
739 set_light(state, x, y, 1);
740 }
741 }
742
743 for (i = 0; i < wh; i++) {
744 y = numindices[i] / state->w;
745 x = numindices[i] % state->w;
746 if (!(GRID(state, flags, x, y) & F_LIGHT)) continue;
747 if (GRID(state, flags, x, y) & F_MARK) continue;
748 list_lights(state, x, y, 0, &lld);
749
750 /* If we're not lighting any lights ourself, don't remove anything. */
751 n = 0;
752 FOREACHLIT(&lld, if (GRID(state,flags,lx,ly) & F_LIGHT) { n += 1; } );
753 if (n == 0) continue; /* [1] */
754
755 /* Check whether removing lights we're lighting would cause anything
756 * to go dark. */
757 n = 0;
758 FOREACHLIT(&lld, if (GRID(state,flags,lx,ly) & F_LIGHT) { n += check_dark(state,lx,ly); } );
759 if (n == 0) {
760 /* No, it wouldn't, so we can remove them all. */
761 FOREACHLIT(&lld, set_light(state,lx,ly, 0); );
762 GRID(state,flags,x,y) |= F_MARK;
763 }
764
765 if (!grid_overlap(state)) {
766 sfree(numindices);
767 return; /* we're done. */
768 }
769 assert(grid_lit(state));
770 }
771 /* could get here if the line at [1] continue'd out of the loop. */
772 if (grid_overlap(state)) {
773 debug_state(state);
774 assert(!"place_lights failed to resolve overlapping lights!");
775 }
776 sfree(numindices);
777}
778
779/* Fills in all black squares with numbers of adjacent lights. */
780static void place_numbers(game_state *state)
781{
782 int x, y, i, n;
783 surrounds s;
784
785 for (x = 0; x < state->w; x++) {
786 for (y = 0; y < state->h; y++) {
787 if (!(GRID(state,flags,x,y) & F_BLACK)) continue;
788 get_surrounds(state, x, y, &s);
789 n = 0;
790 for (i = 0; i < s.npoints; i++) {
791 if (GRID(state,flags,s.points[i].x, s.points[i].y) & F_LIGHT)
792 n++;
793 }
794 GRID(state,flags,x,y) |= F_NUMBERED;
795 GRID(state,lights,x,y) = n;
796 }
797 }
798}
799
800/* --- Actual solver, with helper subroutines. --- */
801
802static void tsl_callback(game_state *state,
803 int lx, int ly, int *x, int *y, int *n)
804{
805 if (GRID(state,flags,lx,ly) & F_IMPOSSIBLE) return;
806 if (GRID(state,lights,lx,ly) > 0) return;
807 *x = lx; *y = ly; (*n)++;
808}
809
810static int try_solve_light(game_state *state, int ox, int oy,
811 unsigned int flags, int lights)
812{
813 ll_data lld;
814 int sx = 0, sy = 0, n = 0;
815
816 if (lights > 0) return 0;
817 if (flags & F_BLACK) return 0;
818
819 /* We have an unlit square; count how many ways there are left to
820 * place a light that lights us (including this square); if only
821 * one, we must put a light there. Squares that could light us
822 * are, of course, the same as the squares we would light... */
823 list_lights(state, ox, oy, 1, &lld);
824 FOREACHLIT(&lld, { tsl_callback(state, lx, ly, &sx, &sy, &n); });
825 if (n == 1) {
826 set_light(state, sx, sy, 1);
827#ifdef SOLVER_DIAGNOSTICS
828 debug(("(%d,%d) can only be lit from (%d,%d); setting to LIGHT\n",
829 ox,oy,sx,sy));
830 if (verbose) debug_state(state);
831#endif
832 return 1;
833 }
834
835 return 0;
836}
837
838static int could_place_light(unsigned int flags, int lights)
839{
840 if (flags & (F_BLACK | F_IMPOSSIBLE)) return 0;
841 return (lights > 0) ? 0 : 1;
842}
843
844static int could_place_light_xy(game_state *state, int x, int y)
845{
846 int lights = GRID(state,lights,x,y);
847 unsigned int flags = GRID(state,flags,x,y);
848 return (could_place_light(flags, lights)) ? 1 : 0;
849}
850
851/* For a given number square, determine whether we have enough info
852 * to unambiguously place its lights. */
853static int try_solve_number(game_state *state, int nx, int ny,
854 unsigned int nflags, int nlights)
855{
856 surrounds s;
857 int x, y, nl, ns, i, ret = 0, lights;
858 unsigned int flags;
859
860 if (!(nflags & F_NUMBERED)) return 0;
861 nl = nlights;
862 get_surrounds(state,nx,ny,&s);
863 ns = s.npoints;
864
865 /* nl is no. of lights we need to place, ns is no. of spaces we
866 * have to place them in. Try and narrow these down, and mark
867 * points we can ignore later. */
868 for (i = 0; i < s.npoints; i++) {
869 x = s.points[i].x; y = s.points[i].y;
870 flags = GRID(state,flags,x,y);
871 lights = GRID(state,lights,x,y);
872 if (flags & F_LIGHT) {
873 /* light here already; one less light for one less place. */
874 nl--; ns--;
875 s.points[i].f |= F_MARK;
876 } else if (!could_place_light(flags, lights)) {
877 ns--;
878 s.points[i].f |= F_MARK;
879 }
880 }
881 if (ns == 0) return 0; /* nowhere to put anything. */
882 if (nl == 0) {
883 /* we have placed all lights we need to around here; all remaining
884 * surrounds are therefore IMPOSSIBLE. */
885 GRID(state,flags,nx,ny) |= F_NUMBERUSED;
886 for (i = 0; i < s.npoints; i++) {
887 if (!(s.points[i].f & F_MARK)) {
888 GRID(state,flags,s.points[i].x,s.points[i].y) |= F_IMPOSSIBLE;
889 ret = 1;
890 }
891 }
892#ifdef SOLVER_DIAGNOSTICS
893 printf("Clue at (%d,%d) full; setting unlit to IMPOSSIBLE.\n",
894 nx,ny);
895 if (verbose) debug_state(state);
896#endif
897 } else if (nl == ns) {
898 /* we have as many lights to place as spaces; fill them all. */
899 GRID(state,flags,nx,ny) |= F_NUMBERUSED;
900 for (i = 0; i < s.npoints; i++) {
901 if (!(s.points[i].f & F_MARK)) {
902 set_light(state, s.points[i].x,s.points[i].y, 1);
903 ret = 1;
904 }
905 }
906#ifdef SOLVER_DIAGNOSTICS
907 printf("Clue at (%d,%d) trivial; setting unlit to LIGHT.\n",
908 nx,ny);
909 if (verbose) debug_state(state);
910#endif
911 }
912 return ret;
913}
914
915struct setscratch {
916 int x, y;
917 int n;
918};
919
920#define SCRATCHSZ (state->w+state->h)
921
922/* New solver algorithm: overlapping sets can add IMPOSSIBLE flags.
923 * Algorithm thanks to Simon:
924 *
925 * (a) Any square where you can place a light has a set of squares
926 * which would become non-lights as a result. (This includes
927 * squares lit by the first square, and can also include squares
928 * adjacent to the same clue square if the new light is the last
929 * one around that clue.) Call this MAKESDARK(x,y) with (x,y) being
930 * the square you place a light.
931
932 * (b) Any unlit square has a set of squares on which you could place
933 * a light to illuminate it. (Possibly including itself, of
934 * course.) This set of squares has the property that _at least
935 * one_ of them must contain a light. Sets of this type also arise
936 * from clue squares. Call this MAKESLIGHT(x,y), again with (x,y)
937 * the square you would place a light.
938
939 * (c) If there exists (dx,dy) and (lx,ly) such that MAKESDARK(dx,dy) is
940 * a superset of MAKESLIGHT(lx,ly), this implies that placing a light at
941 * (dx,dy) would either leave no remaining way to illuminate a certain
942 * square, or would leave no remaining way to fulfill a certain clue
943 * (at lx,ly). In either case, a light can be ruled out at that position.
944 *
945 * So, we construct all possible MAKESLIGHT sets, both from unlit squares
946 * and clue squares, and then we look for plausible MAKESDARK sets that include
947 * our (lx,ly) to see if we can find a (dx,dy) to rule out. By the time we have
948 * constructed the MAKESLIGHT set we don't care about (lx,ly), just the set
949 * members.
950 *
951 * Once we have such a set, Simon came up with a Cunning Plan to find
952 * the most sensible MAKESDARK candidate:
953 *
954 * (a) for each square S in your set X, find all the squares which _would_
955 * rule it out. That means any square which would light S, plus
956 * any square adjacent to the same clue square as S (provided
957 * that clue square has only one remaining light to be placed).
958 * It's not hard to make this list. Don't do anything with this
959 * data at the moment except _count_ the squares.
960
961 * (b) Find the square S_min in the original set which has the
962 * _smallest_ number of other squares which would rule it out.
963
964 * (c) Find all the squares that rule out S_min (it's probably
965 * better to recompute this than to have stored it during step
966 * (a), since the CPU requirement is modest but the storage
967 * cost would get ugly.) For each of these squares, see if it
968 * rules out everything else in the set X. Any which does can
969 * be marked as not-a-light.
970 *
971 */
972
973typedef void (*trl_cb)(game_state *state, int dx, int dy,
974 struct setscratch *scratch, int n, void *ctx);
975
976static void try_rule_out(game_state *state, int x, int y,
977 struct setscratch *scratch, int n,
978 trl_cb cb, void *ctx);
979
980static void trl_callback_search(game_state *state, int dx, int dy,
981 struct setscratch *scratch, int n, void *ignored)
982{
983 int i;
984
985#ifdef SOLVER_DIAGNOSTICS
986 if (verbose) debug(("discount cb: light at (%d,%d)\n", dx, dy));
987#endif
988
989 for (i = 0; i < n; i++) {
990 if (dx == scratch[i].x && dy == scratch[i].y) {
991 scratch[i].n = 1;
992 return;
993 }
994 }
995}
996
997static void trl_callback_discount(game_state *state, int dx, int dy,
998 struct setscratch *scratch, int n, void *ctx)
999{
1000 int *didsth = (int *)ctx;
1001 int i;
1002
1003 if (GRID(state,flags,dx,dy) & F_IMPOSSIBLE) {
1004#ifdef SOLVER_DIAGNOSTICS
1005 debug(("Square at (%d,%d) already impossible.\n", dx,dy));
1006#endif
1007 return;
1008 }
1009
1010 /* Check whether a light at (dx,dy) rules out everything
1011 * in scratch, and mark (dx,dy) as IMPOSSIBLE if it does.
1012 * We can use try_rule_out for this as well, as the set of
1013 * squares which would rule out (x,y) is the same as the
1014 * set of squares which (x,y) would rule out. */
1015
1016#ifdef SOLVER_DIAGNOSTICS
1017 if (verbose) debug(("Checking whether light at (%d,%d) rules out everything in scratch.\n", dx, dy));
1018#endif
1019
1020 for (i = 0; i < n; i++)
1021 scratch[i].n = 0;
1022 try_rule_out(state, dx, dy, scratch, n, trl_callback_search, NULL);
1023 for (i = 0; i < n; i++) {
1024 if (scratch[i].n == 0) return;
1025 }
1026 /* The light ruled out everything in scratch. Yay. */
1027 GRID(state,flags,dx,dy) |= F_IMPOSSIBLE;
1028#ifdef SOLVER_DIAGNOSTICS
1029 debug(("Set reduction discounted square at (%d,%d):\n", dx,dy));
1030 if (verbose) debug_state(state);
1031#endif
1032
1033 *didsth = 1;
1034}
1035
1036static void trl_callback_incn(game_state *state, int dx, int dy,
1037 struct setscratch *scratch, int n, void *ctx)
1038{
1039 struct setscratch *s = (struct setscratch *)ctx;
1040 s->n++;
1041}
1042
1043static void try_rule_out(game_state *state, int x, int y,
1044 struct setscratch *scratch, int n,
1045 trl_cb cb, void *ctx)
1046{
1047 /* XXX Find all the squares which would rule out (x,y); anything
1048 * that would light it as well as squares adjacent to same clues
1049 * as X assuming that clue only has one remaining light.
1050 * Call the callback with each square. */
1051 ll_data lld;
1052 surrounds s, ss;
1053 int i, j, curr_lights, tot_lights;
1054
1055 /* Find all squares that would rule out a light at (x,y) and call trl_cb
1056 * with them: anything that would light (x,y)... */
1057
1058 list_lights(state, x, y, 0, &lld);
1059 FOREACHLIT(&lld, { if (could_place_light_xy(state, lx, ly)) { cb(state, lx, ly, scratch, n, ctx); } });
1060
1061 /* ... as well as any empty space (that isn't x,y) next to any clue square
1062 * next to (x,y) that only has one light left to place. */
1063
1064 get_surrounds(state, x, y, &s);
1065 for (i = 0; i < s.npoints; i++) {
1066 if (!(GRID(state,flags,s.points[i].x,s.points[i].y) & F_NUMBERED))
1067 continue;
1068 /* we have an adjacent clue square; find /its/ surrounds
1069 * and count the remaining lights it needs. */
1070 get_surrounds(state,s.points[i].x,s.points[i].y,&ss);
1071 curr_lights = 0;
1072 for (j = 0; j < ss.npoints; j++) {
1073 if (GRID(state,flags,ss.points[j].x,ss.points[j].y) & F_LIGHT)
1074 curr_lights++;
1075 }
1076 tot_lights = GRID(state, lights, s.points[i].x, s.points[i].y);
1077 /* We have a clue with tot_lights to fill, and curr_lights currently
1078 * around it. If adding a light at (x,y) fills up the clue (i.e.
1079 * curr_lights + 1 = tot_lights) then we need to discount all other
1080 * unlit squares around the clue. */
1081 if ((curr_lights + 1) == tot_lights) {
1082 for (j = 0; j < ss.npoints; j++) {
1083 int lx = ss.points[j].x, ly = ss.points[j].y;
1084 if (lx == x && ly == y) continue;
1085 if (could_place_light_xy(state, lx, ly))
1086 cb(state, lx, ly, scratch, n, ctx);
1087 }
1088 }
1089 }
1090}
1091
1092#ifdef SOLVER_DIAGNOSTICS
1093static void debug_scratch(const char *msg, struct setscratch *scratch, int n)
1094{
1095 int i;
1096 debug(("%s scratch (%d elements):\n", msg, n));
1097 for (i = 0; i < n; i++) {
1098 debug((" (%d,%d) n%d\n", scratch[i].x, scratch[i].y, scratch[i].n));
1099 }
1100}
1101#endif
1102
1103static int discount_set(game_state *state,
1104 struct setscratch *scratch, int n)
1105{
1106 int i, besti, bestn, didsth = 0;
1107
1108#ifdef SOLVER_DIAGNOSTICS
1109 if (verbose > 1) debug_scratch("discount_set", scratch, n);
1110#endif
1111 if (n == 0) return 0;
1112
1113 for (i = 0; i < n; i++) {
1114 try_rule_out(state, scratch[i].x, scratch[i].y, scratch, n,
1115 trl_callback_incn, (void*)&(scratch[i]));
1116 }
1117#ifdef SOLVER_DIAGNOSTICS
1118 if (verbose > 1) debug_scratch("discount_set after count", scratch, n);
1119#endif
1120
1121 besti = -1; bestn = SCRATCHSZ;
1122 for (i = 0; i < n; i++) {
1123 if (scratch[i].n < bestn) {
1124 bestn = scratch[i].n;
1125 besti = i;
1126 }
1127 }
1128#ifdef SOLVER_DIAGNOSTICS
1129 if (verbose > 1) debug(("best square (%d,%d) with n%d.\n",
1130 scratch[besti].x, scratch[besti].y, scratch[besti].n));
1131#endif
1132 try_rule_out(state, scratch[besti].x, scratch[besti].y, scratch, n,
1133 trl_callback_discount, (void*)&didsth);
1134#ifdef SOLVER_DIAGNOSTICS
1135 if (didsth) debug((" [from square (%d,%d)]\n",
1136 scratch[besti].x, scratch[besti].y));
1137#endif
1138
1139 return didsth;
1140}
1141
1142static void discount_clear(game_state *state, struct setscratch *scratch, int *n)
1143{
1144 *n = 0;
1145 memset(scratch, 0, SCRATCHSZ * sizeof(struct setscratch));
1146}
1147
1148static void unlit_cb(game_state *state, int lx, int ly,
1149 struct setscratch *scratch, int *n)
1150{
1151 if (could_place_light_xy(state, lx, ly)) {
1152 scratch[*n].x = lx; scratch[*n].y = ly; (*n)++;
1153 }
1154}
1155
1156/* Construct a MAKESLIGHT set from an unlit square. */
1157static int discount_unlit(game_state *state, int x, int y,
1158 struct setscratch *scratch)
1159{
1160 ll_data lld;
1161 int n, didsth;
1162
1163#ifdef SOLVER_DIAGNOSTICS
1164 if (verbose) debug(("Trying to discount for unlit square at (%d,%d).\n", x, y));
1165 if (verbose > 1) debug_state(state);
1166#endif
1167
1168 discount_clear(state, scratch, &n);
1169
1170 list_lights(state, x, y, 1, &lld);
1171 FOREACHLIT(&lld, { unlit_cb(state, lx, ly, scratch, &n); });
1172 didsth = discount_set(state, scratch, n);
1173#ifdef SOLVER_DIAGNOSTICS
1174 if (didsth) debug((" [from unlit square at (%d,%d)].\n", x, y));
1175#endif
1176 return didsth;
1177
1178}
1179
1180/* Construct a series of MAKESLIGHT sets from a clue square.
1181 * for a clue square with N remaining spaces that must contain M lights, every
1182 * subset of size N-M+1 of those N spaces forms such a set.
1183 */
1184
1185static int discount_clue(game_state *state, int x, int y,
1186 struct setscratch *scratch)
1187{
1188 int slen, m = GRID(state, lights, x, y), n, i, didsth = 0, lights;
1189 unsigned int flags;
1190 surrounds s, sempty;
1191 combi_ctx *combi;
1192
1193 if (m == 0) return 0;
1194
1195#ifdef SOLVER_DIAGNOSTICS
1196 if (verbose) debug(("Trying to discount for sets at clue (%d,%d).\n", x, y));
1197 if (verbose > 1) debug_state(state);
1198#endif
1199
1200 /* m is no. of lights still to place; starts off at the clue value
1201 * and decreases when we find a light already down.
1202 * n is no. of spaces left; starts off at 0 and goes up when we find
1203 * a plausible space. */
1204
1205 get_surrounds(state, x, y, &s);
1206 memset(&sempty, 0, sizeof(surrounds));
1207 for (i = 0; i < s.npoints; i++) {
1208 int lx = s.points[i].x, ly = s.points[i].y;
1209 flags = GRID(state,flags,lx,ly);
1210 lights = GRID(state,lights,lx,ly);
1211
1212 if (flags & F_LIGHT) m--;
1213
1214 if (could_place_light(flags, lights)) {
1215 sempty.points[sempty.npoints].x = lx;
1216 sempty.points[sempty.npoints].y = ly;
1217 sempty.npoints++;
1218 }
1219 }
1220 n = sempty.npoints; /* sempty is now a surrounds of only blank squares. */
1221 if (n == 0) return 0; /* clue is full already. */
1222
1223 if (m < 0 || m > n) return 0; /* become impossible. */
1224
1225 combi = new_combi(n - m + 1, n);
1226 while (next_combi(combi)) {
1227 discount_clear(state, scratch, &slen);
1228 for (i = 0; i < combi->r; i++) {
1229 scratch[slen].x = sempty.points[combi->a[i]].x;
1230 scratch[slen].y = sempty.points[combi->a[i]].y;
1231 slen++;
1232 }
1233 if (discount_set(state, scratch, slen)) didsth = 1;
1234 }
1235 free_combi(combi);
1236#ifdef SOLVER_DIAGNOSTICS
1237 if (didsth) debug((" [from clue at (%d,%d)].\n", x, y));
1238#endif
1239 return didsth;
1240}
1241
1242#define F_SOLVE_FORCEUNIQUE 1
1243#define F_SOLVE_DISCOUNTSETS 2
1244#define F_SOLVE_ALLOWRECURSE 4
1245
1246static unsigned int flags_from_difficulty(int difficulty)
1247{
1248 unsigned int sflags = F_SOLVE_FORCEUNIQUE;
1249 assert(difficulty <= DIFFCOUNT);
1250 if (difficulty >= 1) sflags |= F_SOLVE_DISCOUNTSETS;
1251 if (difficulty >= 2) sflags |= F_SOLVE_ALLOWRECURSE;
1252 return sflags;
1253}
1254
1255#define MAXRECURSE 5
1256
1257static int solve_sub(game_state *state,
1258 unsigned int solve_flags, int depth,
1259 int *maxdepth)
1260{
1261 unsigned int flags;
1262 int x, y, didstuff, ncanplace, lights;
1263 int bestx, besty, n, bestn, copy_soluble, self_soluble, ret, maxrecurse = 0;
1264 game_state *scopy;
1265 ll_data lld;
1266 struct setscratch *sscratch = NULL;
1267
1268#ifdef SOLVER_DIAGNOSTICS
1269 printf("solve_sub: depth = %d\n", depth);
1270#endif
1271 if (maxdepth && *maxdepth < depth) *maxdepth = depth;
1272 if (solve_flags & F_SOLVE_ALLOWRECURSE) maxrecurse = MAXRECURSE;
1273
1274 while (1) {
1275 if (grid_overlap(state)) {
1276 /* Our own solver, from scratch, should never cause this to happen
1277 * (assuming a soluble grid). However, if we're trying to solve
1278 * from a half-completed *incorrect* grid this might occur; we
1279 * just return the 'no solutions' code in this case. */
1280 ret = 0; goto done;
1281 }
1282
1283 if (grid_correct(state)) { ret = 1; goto done; }
1284
1285 ncanplace = 0;
1286 didstuff = 0;
1287 /* These 2 loops, and the functions they call, are the critical loops
1288 * for timing; any optimisations should look here first. */
1289 for (x = 0; x < state->w; x++) {
1290 for (y = 0; y < state->h; y++) {
1291 flags = GRID(state,flags,x,y);
1292 lights = GRID(state,lights,x,y);
1293 ncanplace += could_place_light(flags, lights);
1294
1295 if (try_solve_light(state, x, y, flags, lights)) didstuff = 1;
1296 if (try_solve_number(state, x, y, flags, lights)) didstuff = 1;
1297 }
1298 }
1299 if (didstuff) continue;
1300 if (!ncanplace) {
1301 /* nowhere to put a light, puzzle is unsoluble. */
1302 ret = 0; goto done;
1303 }
1304
1305 if (solve_flags & F_SOLVE_DISCOUNTSETS) {
1306 if (!sscratch) sscratch = snewn(SCRATCHSZ, struct setscratch);
1307 /* Try a more cunning (and more involved) way... more details above. */
1308 for (x = 0; x < state->w; x++) {
1309 for (y = 0; y < state->h; y++) {
1310 flags = GRID(state,flags,x,y);
1311 lights = GRID(state,lights,x,y);
1312
1313 if (!(flags & F_BLACK) && lights == 0) {
1314 if (discount_unlit(state, x, y, sscratch)) {
1315 didstuff = 1;
1316 goto reduction_success;
1317 }
1318 } else if (flags & F_NUMBERED) {
1319 if (discount_clue(state, x, y, sscratch)) {
1320 didstuff = 1;
1321 goto reduction_success;
1322 }
1323 }
1324 }
1325 }
1326 }
1327reduction_success:
1328 if (didstuff) continue;
1329
1330 /* We now have to make a guess; we have places to put lights but
1331 * no definite idea about where they can go. */
1332 if (depth >= maxrecurse) {
1333 /* mustn't delve any deeper. */
1334 ret = -1; goto done;
1335 }
1336 /* Of all the squares that we could place a light, pick the one
1337 * that would light the most currently unlit squares. */
1338 /* This heuristic was just plucked from the air; there may well be
1339 * a more efficient way of choosing a square to flip to minimise
1340 * recursion. */
1341 bestn = 0;
1342 bestx = besty = -1; /* suyb */
1343 for (x = 0; x < state->w; x++) {
1344 for (y = 0; y < state->h; y++) {
1345 flags = GRID(state,flags,x,y);
1346 lights = GRID(state,lights,x,y);
1347 if (!could_place_light(flags, lights)) continue;
1348
1349 n = 0;
1350 list_lights(state, x, y, 1, &lld);
1351 FOREACHLIT(&lld, { if (GRID(state,lights,lx,ly) == 0) n++; });
1352 if (n > bestn) {
1353 bestn = n; bestx = x; besty = y;
1354 }
1355 }
1356 }
1357 assert(bestn > 0);
1358 assert(bestx >= 0 && besty >= 0);
1359
1360 /* Now we've chosen a plausible (x,y), try to solve it once as 'lit'
1361 * and once as 'impossible'; we need to make one copy to do this. */
1362
1363 scopy = dup_game(state);
1364#ifdef SOLVER_DIAGNOSTICS
1365 debug(("Recursing #1: trying (%d,%d) as IMPOSSIBLE\n", bestx, besty));
1366#endif
1367 GRID(state,flags,bestx,besty) |= F_IMPOSSIBLE;
1368 self_soluble = solve_sub(state, solve_flags, depth+1, maxdepth);
1369
1370 if (!(solve_flags & F_SOLVE_FORCEUNIQUE) && self_soluble > 0) {
1371 /* we didn't care about finding all solutions, and we just
1372 * found one; return with it immediately. */
1373 free_game(scopy);
1374 ret = self_soluble;
1375 goto done;
1376 }
1377
1378#ifdef SOLVER_DIAGNOSTICS
1379 debug(("Recursing #2: trying (%d,%d) as LIGHT\n", bestx, besty));
1380#endif
1381 set_light(scopy, bestx, besty, 1);
1382 copy_soluble = solve_sub(scopy, solve_flags, depth+1, maxdepth);
1383
1384 /* If we wanted a unique solution but we hit our recursion limit
1385 * (on either branch) then we have to assume we didn't find possible
1386 * extra solutions, and return 'not soluble'. */
1387 if ((solve_flags & F_SOLVE_FORCEUNIQUE) &&
1388 ((copy_soluble < 0) || (self_soluble < 0))) {
1389 ret = -1;
1390 /* Make sure that whether or not it was self or copy (or both) that
1391 * were soluble, that we return a solved state in self. */
1392 } else if (copy_soluble <= 0) {
1393 /* copy wasn't soluble; keep self state and return that result. */
1394 ret = self_soluble;
1395 } else if (self_soluble <= 0) {
1396 /* copy solved and we didn't, so copy in copy's (now solved)
1397 * flags and light state. */
1398 memcpy(state->lights, scopy->lights,
1399 scopy->w * scopy->h * sizeof(int));
1400 memcpy(state->flags, scopy->flags,
1401 scopy->w * scopy->h * sizeof(unsigned int));
1402 ret = copy_soluble;
1403 } else {
1404 ret = copy_soluble + self_soluble;
1405 }
1406 free_game(scopy);
1407 goto done;
1408 }
1409done:
1410 if (sscratch) sfree(sscratch);
1411#ifdef SOLVER_DIAGNOSTICS
1412 if (ret < 0)
1413 debug(("solve_sub: depth = %d returning, ran out of recursion.\n",
1414 depth));
1415 else
1416 debug(("solve_sub: depth = %d returning, %d solutions.\n",
1417 depth, ret));
1418#endif
1419 return ret;
1420}
1421
1422/* Fills in the (possibly partially-complete) game_state as far as it can,
1423 * returning the number of possible solutions. If it returns >0 then the
1424 * game_state will be in a solved state, but you won't know which one. */
1425static int dosolve(game_state *state, int solve_flags, int *maxdepth)
1426{
1427 int x, y, nsol;
1428
1429 for (x = 0; x < state->w; x++) {
1430 for (y = 0; y < state->h; y++) {
1431 GRID(state,flags,x,y) &= ~F_NUMBERUSED;
1432 }
1433 }
1434 nsol = solve_sub(state, solve_flags, 0, maxdepth);
1435 return nsol;
1436}
1437
1438static int strip_unused_nums(game_state *state)
1439{
1440 int x,y,n=0;
1441 for (x = 0; x < state->w; x++) {
1442 for (y = 0; y < state->h; y++) {
1443 if ((GRID(state,flags,x,y) & F_NUMBERED) &&
1444 !(GRID(state,flags,x,y) & F_NUMBERUSED)) {
1445 GRID(state,flags,x,y) &= ~F_NUMBERED;
1446 GRID(state,lights,x,y) = 0;
1447 n++;
1448 }
1449 }
1450 }
1451 debug(("Stripped %d unused numbers.\n", n));
1452 return n;
1453}
1454
1455static void unplace_lights(game_state *state)
1456{
1457 int x,y;
1458 for (x = 0; x < state->w; x++) {
1459 for (y = 0; y < state->h; y++) {
1460 if (GRID(state,flags,x,y) & F_LIGHT)
1461 set_light(state,x,y,0);
1462 GRID(state,flags,x,y) &= ~F_IMPOSSIBLE;
1463 GRID(state,flags,x,y) &= ~F_NUMBERUSED;
1464 }
1465 }
1466}
1467
1468static int puzzle_is_good(game_state *state, int difficulty)
1469{
1470 int nsol, mdepth = 0;
1471 unsigned int sflags = flags_from_difficulty(difficulty);
1472
1473 unplace_lights(state);
1474
1475#ifdef SOLVER_DIAGNOSTICS
1476 debug(("Trying to solve with difficulty %d (0x%x):\n",
1477 difficulty, sflags));
1478 if (verbose) debug_state(state);
1479#endif
1480
1481 nsol = dosolve(state, sflags, &mdepth);
1482 /* if we wanted an easy puzzle, make sure we didn't need recursion. */
1483 if (!(sflags & F_SOLVE_ALLOWRECURSE) && mdepth > 0) {
1484 debug(("Ignoring recursive puzzle.\n"));
1485 return 0;
1486 }
1487
1488 debug(("%d solutions found.\n", nsol));
1489 if (nsol <= 0) return 0;
1490 if (nsol > 1) return 0;
1491 return 1;
1492}
1493
1494/* --- New game creation and user input code. --- */
1495
1496/* The basic algorithm here is to generate the most complex grid possible
1497 * while honouring two restrictions:
1498 *
1499 * * we require a unique solution, and
1500 * * either we require solubility with no recursion (!params->recurse)
1501 * * or we require some recursion. (params->recurse).
1502 *
1503 * The solver helpfully keeps track of the numbers it needed to use to
1504 * get its solution, so we use that to remove an initial set of numbers
1505 * and check we still satsify our requirements (on uniqueness and
1506 * non-recursiveness, if applicable; we don't check explicit recursiveness
1507 * until the end).
1508 *
1509 * Then we try to remove all numbers in a random order, and see if we
1510 * still satisfy requirements (putting them back if we didn't).
1511 *
1512 * Removing numbers will always, in general terms, make a puzzle require
1513 * more recursion but it may also mean a puzzle becomes non-unique.
1514 *
1515 * Once we're done, if we wanted a recursive puzzle but the most difficult
1516 * puzzle we could come up with was non-recursive, we give up and try a new
1517 * grid. */
1518
1519#define MAX_GRIDGEN_TRIES 20
1520
1521static char *new_game_desc(const game_params *params_in, random_state *rs,
1522 char **aux, int interactive)
1523{
1524 game_params params_copy = *params_in; /* structure copy */
1525 game_params *params = &params_copy;
1526 game_state *news = new_state(params), *copys;
1527 int i, j, run, x, y, wh = params->w*params->h, num;
1528 char *ret, *p;
1529 int *numindices;
1530
1531 /* Construct a shuffled list of grid positions; we only
1532 * do this once, because if it gets used more than once it'll
1533 * be on a different grid layout. */
1534 numindices = snewn(wh, int);
1535 for (j = 0; j < wh; j++) numindices[j] = j;
1536 shuffle(numindices, wh, sizeof(*numindices), rs);
1537
1538 while (1) {
1539 for (i = 0; i < MAX_GRIDGEN_TRIES; i++) {
1540 set_blacks(news, params, rs); /* also cleans board. */
1541
1542 /* set up lights and then the numbers, and remove the lights */
1543 place_lights(news, rs);
1544 debug(("Generating initial grid.\n"));
1545 place_numbers(news);
1546 if (!puzzle_is_good(news, params->difficulty)) continue;
1547
1548 /* Take a copy, remove numbers we didn't use and check there's
1549 * still a unique solution; if so, use the copy subsequently. */
1550 copys = dup_game(news);
1551 strip_unused_nums(copys);
1552 if (!puzzle_is_good(copys, params->difficulty)) {
1553 debug(("Stripped grid is not good, reverting.\n"));
1554 free_game(copys);
1555 } else {
1556 free_game(news);
1557 news = copys;
1558 }
1559
1560 /* Go through grid removing numbers at random one-by-one and
1561 * trying to solve again; if it ceases to be good put the number back. */
1562 for (j = 0; j < wh; j++) {
1563 y = numindices[j] / params->w;
1564 x = numindices[j] % params->w;
1565 if (!(GRID(news, flags, x, y) & F_NUMBERED)) continue;
1566 num = GRID(news, lights, x, y);
1567 GRID(news, lights, x, y) = 0;
1568 GRID(news, flags, x, y) &= ~F_NUMBERED;
1569 if (!puzzle_is_good(news, params->difficulty)) {
1570 GRID(news, lights, x, y) = num;
1571 GRID(news, flags, x, y) |= F_NUMBERED;
1572 } else
1573 debug(("Removed (%d,%d) still soluble.\n", x, y));
1574 }
1575 if (params->difficulty > 0) {
1576 /* Was the maximally-difficult puzzle difficult enough?
1577 * Check we can't solve it with a more simplistic solver. */
1578 if (puzzle_is_good(news, params->difficulty-1)) {
1579 debug(("Maximally-hard puzzle still not hard enough, skipping.\n"));
1580 continue;
1581 }
1582 }
1583
1584 goto goodpuzzle;
1585 }
1586 /* Couldn't generate a good puzzle in however many goes. Ramp up the
1587 * %age of black squares (if we didn't already have lots; in which case
1588 * why couldn't we generate a puzzle?) and try again. */
1589 if (params->blackpc < 90) params->blackpc += 5;
1590 debug(("New black layout %d%%.\n", params->blackpc));
1591 }
1592goodpuzzle:
1593 /* Game is encoded as a long string one character per square;
1594 * 'S' is a space
1595 * 'B' is a black square with no number
1596 * '0', '1', '2', '3', '4' is a black square with a number. */
1597 ret = snewn((params->w * params->h) + 1, char);
1598 p = ret;
1599 run = 0;
1600 for (y = 0; y < params->h; y++) {
1601 for (x = 0; x < params->w; x++) {
1602 if (GRID(news,flags,x,y) & F_BLACK) {
1603 if (run) {
1604 *p++ = ('a'-1) + run;
1605 run = 0;
1606 }
1607 if (GRID(news,flags,x,y) & F_NUMBERED)
1608 *p++ = '0' + GRID(news,lights,x,y);
1609 else
1610 *p++ = 'B';
1611 } else {
1612 if (run == 26) {
1613 *p++ = ('a'-1) + run;
1614 run = 0;
1615 }
1616 run++;
1617 }
1618 }
1619 }
1620 if (run) {
1621 *p++ = ('a'-1) + run;
1622 run = 0;
1623 }
1624 *p = '\0';
1625 assert(p - ret <= params->w * params->h);
1626 free_game(news);
1627 sfree(numindices);
1628
1629 return ret;
1630}
1631
1632static char *validate_desc(const game_params *params, const char *desc)
1633{
1634 int i;
1635 for (i = 0; i < params->w*params->h; i++) {
1636 if (*desc >= '0' && *desc <= '4')
1637 /* OK */;
1638 else if (*desc == 'B')
1639 /* OK */;
1640 else if (*desc >= 'a' && *desc <= 'z')
1641 i += *desc - 'a'; /* and the i++ will add another one */
1642 else if (!*desc)
1643 return "Game description shorter than expected";
1644 else
1645 return "Game description contained unexpected character";
1646 desc++;
1647 }
1648 if (*desc || i > params->w*params->h)
1649 return "Game description longer than expected";
1650
1651 return NULL;
1652}
1653
1654static game_state *new_game(midend *me, const game_params *params,
1655 const char *desc)
1656{
1657 game_state *ret = new_state(params);
1658 int x,y;
1659 int run = 0;
1660
1661 for (y = 0; y < params->h; y++) {
1662 for (x = 0; x < params->w; x++) {
1663 char c = '\0';
1664
1665 if (run == 0) {
1666 c = *desc++;
1667 assert(c != 'S');
1668 if (c >= 'a' && c <= 'z')
1669 run = c - 'a' + 1;
1670 }
1671
1672 if (run > 0) {
1673 c = 'S';
1674 run--;
1675 }
1676
1677 switch (c) {
1678 case '0': case '1': case '2': case '3': case '4':
1679 GRID(ret,flags,x,y) |= F_NUMBERED;
1680 GRID(ret,lights,x,y) = (c - '0');
1681 /* run-on... */
1682
1683 case 'B':
1684 GRID(ret,flags,x,y) |= F_BLACK;
1685 break;
1686
1687 case 'S':
1688 /* empty square */
1689 break;
1690
1691 default:
1692 assert(!"Malformed desc.");
1693 break;
1694 }
1695 }
1696 }
1697 if (*desc) assert(!"Over-long desc.");
1698
1699 return ret;
1700}
1701
1702static char *solve_game(const game_state *state, const game_state *currstate,
1703 const char *aux, char **error)
1704{
1705 game_state *solved;
1706 char *move = NULL, buf[80];
1707 int movelen, movesize, x, y, len;
1708 unsigned int oldflags, solvedflags, sflags;
1709
1710 /* We don't care here about non-unique puzzles; if the
1711 * user entered one themself then I doubt they care. */
1712
1713 sflags = F_SOLVE_ALLOWRECURSE | F_SOLVE_DISCOUNTSETS;
1714
1715 /* Try and solve from where we are now (for non-unique
1716 * puzzles this may produce a different answer). */
1717 solved = dup_game(currstate);
1718 if (dosolve(solved, sflags, NULL) > 0) goto solved;
1719 free_game(solved);
1720
1721 /* That didn't work; try solving from the clean puzzle. */
1722 solved = dup_game(state);
1723 if (dosolve(solved, sflags, NULL) > 0) goto solved;
1724 *error = "Unable to find a solution to this puzzle.";
1725 goto done;
1726
1727solved:
1728 movesize = 256;
1729 move = snewn(movesize, char);
1730 movelen = 0;
1731 move[movelen++] = 'S';
1732 move[movelen] = '\0';
1733 for (x = 0; x < currstate->w; x++) {
1734 for (y = 0; y < currstate->h; y++) {
1735 len = 0;
1736 oldflags = GRID(currstate, flags, x, y);
1737 solvedflags = GRID(solved, flags, x, y);
1738 if ((oldflags & F_LIGHT) != (solvedflags & F_LIGHT))
1739 len = sprintf(buf, ";L%d,%d", x, y);
1740 else if ((oldflags & F_IMPOSSIBLE) != (solvedflags & F_IMPOSSIBLE))
1741 len = sprintf(buf, ";I%d,%d", x, y);
1742 if (len) {
1743 if (movelen + len >= movesize) {
1744 movesize = movelen + len + 256;
1745 move = sresize(move, movesize, char);
1746 }
1747 strcpy(move + movelen, buf);
1748 movelen += len;
1749 }
1750 }
1751 }
1752
1753done:
1754 free_game(solved);
1755 return move;
1756}
1757
1758static int game_can_format_as_text_now(const game_params *params)
1759{
1760 return TRUE;
1761}
1762
1763/* 'borrowed' from slant.c, mainly. I could have printed it one
1764 * character per cell (like debug_state) but that comes out tiny.
1765 * 'L' is used for 'light here' because 'O' looks too much like '0'
1766 * (black square with no surrounding lights). */
1767static char *game_text_format(const game_state *state)
1768{
1769 int w = state->w, h = state->h, W = w+1, H = h+1;
1770 int x, y, len, lights;
1771 unsigned int flags;
1772 char *ret, *p;
1773
1774 len = (h+H) * (w+W+1) + 1;
1775 ret = snewn(len, char);
1776 p = ret;
1777
1778 for (y = 0; y < H; y++) {
1779 for (x = 0; x < W; x++) {
1780 *p++ = '+';
1781 if (x < w)
1782 *p++ = '-';
1783 }
1784 *p++ = '\n';
1785 if (y < h) {
1786 for (x = 0; x < W; x++) {
1787 *p++ = '|';
1788 if (x < w) {
1789 /* actual interesting bit. */
1790 flags = GRID(state, flags, x, y);
1791 lights = GRID(state, lights, x, y);
1792 if (flags & F_BLACK) {
1793 if (flags & F_NUMBERED)
1794 *p++ = '0' + lights;
1795 else
1796 *p++ = '#';
1797 } else {
1798 if (flags & F_LIGHT)
1799 *p++ = 'L';
1800 else if (flags & F_IMPOSSIBLE)
1801 *p++ = 'x';
1802 else if (lights > 0)
1803 *p++ = '.';
1804 else
1805 *p++ = ' ';
1806 }
1807 }
1808 }
1809 *p++ = '\n';
1810 }
1811 }
1812 *p++ = '\0';
1813
1814 assert(p - ret == len);
1815 return ret;
1816}
1817
1818struct game_ui {
1819 int cur_x, cur_y, cur_visible;
1820};
1821
1822static game_ui *new_ui(const game_state *state)
1823{
1824 game_ui *ui = snew(game_ui);
1825 ui->cur_x = ui->cur_y = ui->cur_visible = 0;
1826 return ui;
1827}
1828
1829static void free_ui(game_ui *ui)
1830{
1831 sfree(ui);
1832}
1833
1834static char *encode_ui(const game_ui *ui)
1835{
1836 /* nothing to encode. */
1837 return NULL;
1838}
1839
1840static void decode_ui(game_ui *ui, const char *encoding)
1841{
1842 /* nothing to decode. */
1843}
1844
1845static void game_changed_state(game_ui *ui, const game_state *oldstate,
1846 const game_state *newstate)
1847{
1848 if (newstate->completed)
1849 ui->cur_visible = 0;
1850}
1851
1852#define DF_BLACK 1 /* black square */
1853#define DF_NUMBERED 2 /* black square with number */
1854#define DF_LIT 4 /* display (white) square lit up */
1855#define DF_LIGHT 8 /* display light in square */
1856#define DF_OVERLAP 16 /* display light as overlapped */
1857#define DF_CURSOR 32 /* display cursor */
1858#define DF_NUMBERWRONG 64 /* display black numbered square as error. */
1859#define DF_FLASH 128 /* background flash is on. */
1860#define DF_IMPOSSIBLE 256 /* display non-light little square */
1861
1862struct game_drawstate {
1863 int tilesize, crad;
1864 int w, h;
1865 unsigned int *flags; /* width * height */
1866 int started;
1867};
1868
1869
1870/* Believe it or not, this empty = "" hack is needed to get around a bug in
1871 * the prc-tools gcc when optimisation is turned on; before, it produced:
1872 lightup-sect.c: In function `interpret_move':
1873 lightup-sect.c:1416: internal error--unrecognizable insn:
1874 (insn 582 580 583 (set (reg:SI 134)
1875 (pc)) -1 (nil)
1876 (nil))
1877 */
1878static char *interpret_move(const game_state *state, game_ui *ui,
1879 const game_drawstate *ds,
1880 int x, int y, int button)
1881{
1882 enum { NONE, FLIP_LIGHT, FLIP_IMPOSSIBLE } action = NONE;
1883 int cx = -1, cy = -1;
1884 unsigned int flags;
1885 char buf[80], *nullret = NULL, *empty = "", c;
1886
1887 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1888 if (ui->cur_visible)
1889 nullret = empty;
1890 ui->cur_visible = 0;
1891 cx = FROMCOORD(x);
1892 cy = FROMCOORD(y);
1893 action = (button == LEFT_BUTTON) ? FLIP_LIGHT : FLIP_IMPOSSIBLE;
1894 } else if (IS_CURSOR_SELECT(button) ||
1895 button == 'i' || button == 'I' ||
1896 button == ' ' || button == '\r' || button == '\n') {
1897 if (ui->cur_visible) {
1898 /* Only allow cursor-effect operations if the cursor is visible
1899 * (otherwise you have no idea which square it might be affecting) */
1900 cx = ui->cur_x;
1901 cy = ui->cur_y;
1902 action = (button == 'i' || button == 'I' || button == CURSOR_SELECT2) ?
1903 FLIP_IMPOSSIBLE : FLIP_LIGHT;
1904 }
1905 ui->cur_visible = 1;
1906 } else if (IS_CURSOR_MOVE(button)) {
1907 move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, 0);
1908 ui->cur_visible = 1;
1909 nullret = empty;
1910 } else
1911 return NULL;
1912
1913 switch (action) {
1914 case FLIP_LIGHT:
1915 case FLIP_IMPOSSIBLE:
1916 if (cx < 0 || cy < 0 || cx >= state->w || cy >= state->h)
1917 return nullret;
1918 flags = GRID(state, flags, cx, cy);
1919 if (flags & F_BLACK)
1920 return nullret;
1921 if (action == FLIP_LIGHT) {
1922#ifdef STYLUS_BASED
1923 if (flags & F_IMPOSSIBLE || flags & F_LIGHT) c = 'I'; else c = 'L';
1924#else
1925 if (flags & F_IMPOSSIBLE) return nullret;
1926 c = 'L';
1927#endif
1928 } else {
1929#ifdef STYLUS_BASED
1930 if (flags & F_IMPOSSIBLE || flags & F_LIGHT) c = 'L'; else c = 'I';
1931#else
1932 if (flags & F_LIGHT) return nullret;
1933 c = 'I';
1934#endif
1935 }
1936 sprintf(buf, "%c%d,%d", (int)c, cx, cy);
1937 break;
1938
1939 case NONE:
1940 return nullret;
1941
1942 default:
1943 assert(!"Shouldn't get here!");
1944 }
1945 return dupstr(buf);
1946}
1947
1948static game_state *execute_move(const game_state *state, const char *move)
1949{
1950 game_state *ret = dup_game(state);
1951 int x, y, n, flags;
1952 char c;
1953
1954 if (!*move) goto badmove;
1955
1956 while (*move) {
1957 c = *move;
1958 if (c == 'S') {
1959 ret->used_solve = TRUE;
1960 move++;
1961 } else if (c == 'L' || c == 'I') {
1962 move++;
1963 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
1964 x < 0 || y < 0 || x >= ret->w || y >= ret->h)
1965 goto badmove;
1966
1967 flags = GRID(ret, flags, x, y);
1968 if (flags & F_BLACK) goto badmove;
1969
1970 /* LIGHT and IMPOSSIBLE are mutually exclusive. */
1971 if (c == 'L') {
1972 GRID(ret, flags, x, y) &= ~F_IMPOSSIBLE;
1973 set_light(ret, x, y, (flags & F_LIGHT) ? 0 : 1);
1974 } else {
1975 set_light(ret, x, y, 0);
1976 GRID(ret, flags, x, y) ^= F_IMPOSSIBLE;
1977 }
1978 move += n;
1979 } else goto badmove;
1980
1981 if (*move == ';')
1982 move++;
1983 else if (*move) goto badmove;
1984 }
1985 if (grid_correct(ret)) ret->completed = 1;
1986 return ret;
1987
1988badmove:
1989 free_game(ret);
1990 return NULL;
1991}
1992
1993/* ----------------------------------------------------------------------
1994 * Drawing routines.
1995 */
1996
1997/* XXX entirely cloned from fifteen.c; separate out? */
1998static void game_compute_size(const game_params *params, int tilesize,
1999 int *x, int *y)
2000{
2001 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2002 struct { int tilesize; } ads, *ds = &ads;
2003 ads.tilesize = tilesize;
2004
2005 *x = TILE_SIZE * params->w + 2 * BORDER;
2006 *y = TILE_SIZE * params->h + 2 * BORDER;
2007}
2008
2009static void game_set_size(drawing *dr, game_drawstate *ds,
2010 const game_params *params, int tilesize)
2011{
2012 ds->tilesize = tilesize;
2013 ds->crad = 3*(tilesize-1)/8;
2014}
2015
2016static float *game_colours(frontend *fe, int *ncolours)
2017{
2018 float *ret = snewn(3 * NCOLOURS, float);
2019 int i;
2020
2021 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2022
2023 for (i = 0; i < 3; i++) {
2024 ret[COL_BLACK * 3 + i] = 0.0F;
2025 ret[COL_LIGHT * 3 + i] = 1.0F;
2026 ret[COL_CURSOR * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 2.0F;
2027 ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 1.5F;
2028
2029 }
2030
2031 ret[COL_ERROR * 3 + 0] = 1.0F;
2032 ret[COL_ERROR * 3 + 1] = 0.25F;
2033 ret[COL_ERROR * 3 + 2] = 0.25F;
2034
2035 ret[COL_LIT * 3 + 0] = 1.0F;
2036 ret[COL_LIT * 3 + 1] = 1.0F;
2037 ret[COL_LIT * 3 + 2] = 0.0F;
2038
2039 *ncolours = NCOLOURS;
2040 return ret;
2041}
2042
2043static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2044{
2045 struct game_drawstate *ds = snew(struct game_drawstate);
2046 int i;
2047
2048 ds->tilesize = ds->crad = 0;
2049 ds->w = state->w; ds->h = state->h;
2050
2051 ds->flags = snewn(ds->w*ds->h, unsigned int);
2052 for (i = 0; i < ds->w*ds->h; i++)
2053 ds->flags[i] = -1;
2054
2055 ds->started = 0;
2056
2057 return ds;
2058}
2059
2060static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2061{
2062 sfree(ds->flags);
2063 sfree(ds);
2064}
2065
2066/* At some stage we should put these into a real options struct.
2067 * Note that tile_redraw has no #ifdeffery; it relies on tile_flags not
2068 * to put those flags in. */
2069#define HINT_LIGHTS
2070#define HINT_OVERLAPS
2071#define HINT_NUMBERS
2072
2073static unsigned int tile_flags(game_drawstate *ds, const game_state *state,
2074 const game_ui *ui, int x, int y, int flashing)
2075{
2076 unsigned int flags = GRID(state, flags, x, y);
2077 int lights = GRID(state, lights, x, y);
2078 unsigned int ret = 0;
2079
2080 if (flashing) ret |= DF_FLASH;
2081 if (ui && ui->cur_visible && x == ui->cur_x && y == ui->cur_y)
2082 ret |= DF_CURSOR;
2083
2084 if (flags & F_BLACK) {
2085 ret |= DF_BLACK;
2086 if (flags & F_NUMBERED) {
2087#ifdef HINT_NUMBERS
2088 if (number_wrong(state, x, y))
2089 ret |= DF_NUMBERWRONG;
2090#endif
2091 ret |= DF_NUMBERED;
2092 }
2093 } else {
2094#ifdef HINT_LIGHTS
2095 if (lights > 0) ret |= DF_LIT;
2096#endif
2097 if (flags & F_LIGHT) {
2098 ret |= DF_LIGHT;
2099#ifdef HINT_OVERLAPS
2100 if (lights > 1) ret |= DF_OVERLAP;
2101#endif
2102 }
2103 if (flags & F_IMPOSSIBLE) ret |= DF_IMPOSSIBLE;
2104 }
2105 return ret;
2106}
2107
2108static void tile_redraw(drawing *dr, game_drawstate *ds,
2109 const game_state *state, int x, int y)
2110{
2111 unsigned int ds_flags = GRID(ds, flags, x, y);
2112 int dx = COORD(x), dy = COORD(y);
2113 int lit = (ds_flags & DF_FLASH) ? COL_GRID : COL_LIT;
2114
2115 if (ds_flags & DF_BLACK) {
2116 draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_BLACK);
2117 if (ds_flags & DF_NUMBERED) {
2118 int ccol = (ds_flags & DF_NUMBERWRONG) ? COL_ERROR : COL_LIGHT;
2119 char str[32];
2120
2121 /* We know that this won't change over the course of the game
2122 * so it's OK to ignore this when calculating whether or not
2123 * to redraw the tile. */
2124 sprintf(str, "%d", GRID(state, lights, x, y));
2125 draw_text(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2,
2126 FONT_VARIABLE, TILE_SIZE*3/5,
2127 ALIGN_VCENTRE | ALIGN_HCENTRE, ccol, str);
2128 }
2129 } else {
2130 draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE,
2131 (ds_flags & DF_LIT) ? lit : COL_BACKGROUND);
2132 draw_rect_outline(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_GRID);
2133 if (ds_flags & DF_LIGHT) {
2134 int lcol = (ds_flags & DF_OVERLAP) ? COL_ERROR : COL_LIGHT;
2135 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, TILE_RADIUS,
2136 lcol, COL_BLACK);
2137 } else if ((ds_flags & DF_IMPOSSIBLE)) {
2138 static int draw_blobs_when_lit = -1;
2139 if (draw_blobs_when_lit < 0) {
2140 char *env = getenv("LIGHTUP_LIT_BLOBS");
2141 draw_blobs_when_lit = (!env || (env[0] == 'y' ||
2142 env[0] == 'Y'));
2143 }
2144 if (!(ds_flags & DF_LIT) || draw_blobs_when_lit) {
2145 int rlen = TILE_SIZE / 4;
2146 draw_rect(dr, dx + TILE_SIZE/2 - rlen/2,
2147 dy + TILE_SIZE/2 - rlen/2,
2148 rlen, rlen, COL_BLACK);
2149 }
2150 }
2151 }
2152
2153 if (ds_flags & DF_CURSOR) {
2154 int coff = TILE_SIZE/8;
2155 draw_rect_outline(dr, dx + coff, dy + coff,
2156 TILE_SIZE - coff*2, TILE_SIZE - coff*2, COL_CURSOR);
2157 }
2158
2159 draw_update(dr, dx, dy, TILE_SIZE, TILE_SIZE);
2160}
2161
2162static void game_redraw(drawing *dr, game_drawstate *ds,
2163 const game_state *oldstate, const game_state *state,
2164 int dir, const game_ui *ui,
2165 float animtime, float flashtime)
2166{
2167 int flashing = FALSE;
2168 int x,y;
2169
2170 if (flashtime) flashing = (int)(flashtime * 3 / FLASH_TIME) != 1;
2171
2172 if (!ds->started) {
2173 draw_rect(dr, 0, 0,
2174 TILE_SIZE * ds->w + 2 * BORDER,
2175 TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND);
2176
2177 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1,
2178 TILE_SIZE * ds->w + 2,
2179 TILE_SIZE * ds->h + 2,
2180 COL_GRID);
2181
2182 draw_update(dr, 0, 0,
2183 TILE_SIZE * ds->w + 2 * BORDER,
2184 TILE_SIZE * ds->h + 2 * BORDER);
2185 ds->started = 1;
2186 }
2187
2188 for (x = 0; x < ds->w; x++) {
2189 for (y = 0; y < ds->h; y++) {
2190 unsigned int ds_flags = tile_flags(ds, state, ui, x, y, flashing);
2191 if (ds_flags != GRID(ds, flags, x, y)) {
2192 GRID(ds, flags, x, y) = ds_flags;
2193 tile_redraw(dr, ds, state, x, y);
2194 }
2195 }
2196 }
2197}
2198
2199static float game_anim_length(const game_state *oldstate,
2200 const game_state *newstate, int dir, game_ui *ui)
2201{
2202 return 0.0F;
2203}
2204
2205static float game_flash_length(const game_state *oldstate,
2206 const game_state *newstate, int dir, game_ui *ui)
2207{
2208 if (!oldstate->completed && newstate->completed &&
2209 !oldstate->used_solve && !newstate->used_solve)
2210 return FLASH_TIME;
2211 return 0.0F;
2212}
2213
2214static int game_status(const game_state *state)
2215{
2216 return state->completed ? +1 : 0;
2217}
2218
2219static int game_timing_state(const game_state *state, game_ui *ui)
2220{
2221 return TRUE;
2222}
2223
2224static void game_print_size(const game_params *params, float *x, float *y)
2225{
2226 int pw, ph;
2227
2228 /*
2229 * I'll use 6mm squares by default.
2230 */
2231 game_compute_size(params, 600, &pw, &ph);
2232 *x = pw / 100.0F;
2233 *y = ph / 100.0F;
2234}
2235
2236static void game_print(drawing *dr, const game_state *state, int tilesize)
2237{
2238 int w = state->w, h = state->h;
2239 int ink = print_mono_colour(dr, 0);
2240 int paper = print_mono_colour(dr, 1);
2241 int x, y;
2242
2243 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2244 game_drawstate ads, *ds = &ads;
2245 game_set_size(dr, ds, NULL, tilesize);
2246
2247 /*
2248 * Border.
2249 */
2250 print_line_width(dr, TILE_SIZE / 16);
2251 draw_rect_outline(dr, COORD(0), COORD(0),
2252 TILE_SIZE * w, TILE_SIZE * h, ink);
2253
2254 /*
2255 * Grid.
2256 */
2257 print_line_width(dr, TILE_SIZE / 24);
2258 for (x = 1; x < w; x++)
2259 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink);
2260 for (y = 1; y < h; y++)
2261 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink);
2262
2263 /*
2264 * Grid contents.
2265 */
2266 for (y = 0; y < h; y++)
2267 for (x = 0; x < w; x++) {
2268 unsigned int ds_flags = tile_flags(ds, state, NULL, x, y, FALSE);
2269 int dx = COORD(x), dy = COORD(y);
2270 if (ds_flags & DF_BLACK) {
2271 draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, ink);
2272 if (ds_flags & DF_NUMBERED) {
2273 char str[32];
2274 sprintf(str, "%d", GRID(state, lights, x, y));
2275 draw_text(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2,
2276 FONT_VARIABLE, TILE_SIZE*3/5,
2277 ALIGN_VCENTRE | ALIGN_HCENTRE, paper, str);
2278 }
2279 } else if (ds_flags & DF_LIGHT) {
2280 draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2,
2281 TILE_RADIUS, -1, ink);
2282 }
2283 }
2284}
2285
2286#ifdef COMBINED
2287#define thegame lightup
2288#endif
2289
2290const struct game thegame = {
2291 "Light Up", "games.lightup", "lightup",
2292 default_params,
2293 game_fetch_preset, NULL,
2294 decode_params,
2295 encode_params,
2296 free_params,
2297 dup_params,
2298 TRUE, game_configure, custom_params,
2299 validate_params,
2300 new_game_desc,
2301 validate_desc,
2302 new_game,
2303 dup_game,
2304 free_game,
2305 TRUE, solve_game,
2306 TRUE, game_can_format_as_text_now, game_text_format,
2307 new_ui,
2308 free_ui,
2309 encode_ui,
2310 decode_ui,
2311 game_changed_state,
2312 interpret_move,
2313 execute_move,
2314 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2315 game_colours,
2316 game_new_drawstate,
2317 game_free_drawstate,
2318 game_redraw,
2319 game_anim_length,
2320 game_flash_length,
2321 game_status,
2322 TRUE, FALSE, game_print_size, game_print,
2323 FALSE, /* wants_statusbar */
2324 FALSE, game_timing_state,
2325 0, /* flags */
2326};
2327
2328#ifdef STANDALONE_SOLVER
2329
2330int main(int argc, char **argv)
2331{
2332 game_params *p;
2333 game_state *s;
2334 char *id = NULL, *desc, *err, *result;
2335 int nsol, diff, really_verbose = 0;
2336 unsigned int sflags;
2337
2338 while (--argc > 0) {
2339 char *p = *++argv;
2340 if (!strcmp(p, "-v")) {
2341 really_verbose++;
2342 } else if (*p == '-') {
2343 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2344 return 1;
2345 } else {
2346 id = p;
2347 }
2348 }
2349
2350 if (!id) {
2351 fprintf(stderr, "usage: %s [-v] <game_id>\n", argv[0]);
2352 return 1;
2353 }
2354
2355 desc = strchr(id, ':');
2356 if (!desc) {
2357 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2358 return 1;
2359 }
2360 *desc++ = '\0';
2361
2362 p = default_params();
2363 decode_params(p, id);
2364 err = validate_desc(p, desc);
2365 if (err) {
2366 fprintf(stderr, "%s: %s\n", argv[0], err);
2367 return 1;
2368 }
2369 s = new_game(NULL, p, desc);
2370
2371 /* Run the solvers easiest to hardest until we find one that
2372 * can solve our puzzle. If it's soluble we know that the
2373 * hardest (recursive) solver will always find the solution. */
2374 nsol = sflags = 0;
2375 for (diff = 0; diff <= DIFFCOUNT; diff++) {
2376 printf("\nSolving with difficulty %d.\n", diff);
2377 sflags = flags_from_difficulty(diff);
2378 unplace_lights(s);
2379 nsol = dosolve(s, sflags, NULL);
2380 if (nsol == 1) break;
2381 }
2382
2383 printf("\n");
2384 if (nsol == 0) {
2385 printf("Puzzle has no solution.\n");
2386 } else if (nsol < 0) {
2387 printf("Unable to find a unique solution.\n");
2388 } else if (nsol > 1) {
2389 printf("Puzzle has multiple solutions.\n");
2390 } else {
2391 verbose = really_verbose;
2392 unplace_lights(s);
2393 printf("Puzzle has difficulty %d: solving...\n", diff);
2394 dosolve(s, sflags, NULL); /* sflags from last successful solve */
2395 result = game_text_format(s);
2396 printf("%s", result);
2397 sfree(result);
2398 }
2399
2400 return 0;
2401}
2402
2403#endif
2404
2405/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/lightup.html b/apps/plugins/puzzles/src/lightup.html
new file mode 100644
index 0000000000..8cf1befa40
--- /dev/null
+++ b/apps/plugins/puzzles/src/lightup.html
@@ -0,0 +1,98 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Light Up</title>
7<link rel="previous" href="slant.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="map.html">
12</head>
13<body>
14<p><a href="slant.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="map.html">Next</a></p>
15<h1><a name="C21"></a>Chapter 21: <a name="i0"></a>Light Up</h1>
16<p>
17You have a grid of squares. Some are filled in black; some of the black squares are numbered. Your aim is to &#8216;light up&#8217; all the empty squares by placing light bulbs in some of them.
18</p>
19<p>
20Each light bulb illuminates the square it is on, plus all squares in line with it horizontally or vertically unless a black square is blocking the way.
21</p>
22<p>
23To win the game, you must satisfy the following conditions:
24</p>
25<ul><li>
26All non-black squares are lit.
27</li>
28<li>
29No light is lit by another light.
30</li>
31<li>
32All numbered black squares have exactly that number of lights adjacent to them (in the four squares above, below, and to the side).
33</li>
34</ul>
35<p>
36Non-numbered black squares may have any number of lights adjacent to them.
37</p>
38<p>
39Credit for this puzzle goes to <a name="i1"></a>Nikoli <a href="#p0">[9]</a>.
40</p>
41<p>
42Light Up was contributed to this collection by James Harvey.
43</p>
44<p><a name="p0"></a>
45[9] <a href="http://www.nikoli.co.jp/en/puzzles/akari.html"><code>http://www.nikoli.co.jp/en/puzzles/akari.html</code></a> (beware of Flash)
46</p>
47<h2><a name="S21.1"></a>21.1 <a name="i2"></a>Light Up controls</h2>
48<p>
49Left-clicking in a non-black square will toggle the presence of a light in that square. Right-clicking in a non-black square toggles a mark there to aid solving; it can be used to highlight squares that cannot be lit, for example.
50</p>
51<p>
52You may not place a light in a marked square, nor place a mark in a lit square.
53</p>
54<p>
55The game will highlight obvious errors in red. Lights lit by other lights are highlighted in this way, as are numbered squares which do not (or cannot) have the right number of lights next to them.
56</p>
57<p>
58Thus, the grid is solved when all non-black squares have yellow highlights and there are no red lights.
59</p>
60<p>
61(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
62</p>
63<h2><a name="S21.2"></a>21.2 <a name="i3"></a>Light Up parameters</h2>
64<p>
65These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
66</p>
67<dl><dt>
68<em>Width</em>, <em>Height</em>
69</dt>
70<dd>
71Size of grid in squares.
72</dd>
73<dt>
74<em>%age of black squares</em>
75</dt>
76<dd>
77Rough percentage of black squares in the grid.
78<p>
79This is a hint rather than an instruction. If the grid generator is unable to generate a puzzle to this precise specification, it will increase the proportion of black squares until it can.
80</p>
81
82</dd>
83<dt>
84<em>Symmetry</em>
85</dt>
86<dd>
87Allows you to specify the required symmetry of the black squares in the grid. (This does not affect the difficulty of the puzzles noticeably.)
88</dd>
89<dt>
90<em>Difficulty</em>
91</dt>
92<dd>
93&#8216;Easy&#8217; means that the puzzles should be soluble without backtracking or guessing, &#8216;Hard&#8217; means that some guesses will probably be necessary.
94</dd>
95</dl>
96
97<hr><address></address></body>
98</html>
diff --git a/apps/plugins/puzzles/src/list.c b/apps/plugins/puzzles/src/list.c
new file mode 100644
index 0000000000..ec019c31b2
--- /dev/null
+++ b/apps/plugins/puzzles/src/list.c
@@ -0,0 +1,55 @@
1/*
2 * list.c: List of pointers to puzzle structures, for monolithic
3 * platforms.
4 *
5 * This file is automatically generated by mkfiles.pl. Do not edit
6 * it directly, or the changes will be lost next time mkfiles.pl runs.
7 * Instead, edit Recipe and/or its *.R subfiles.
8 */
9#include "puzzles.h"
10#define GAMELIST(A) \
11 A(blackbox) \
12 A(bridges) \
13 A(cube) \
14 A(dominosa) \
15 A(fifteen) \
16 A(filling) \
17 A(flip) \
18 A(flood) \
19 A(galaxies) \
20 A(guess) \
21 A(inertia) \
22 A(keen) \
23 A(lightup) \
24 A(loopy) \
25 A(magnets) \
26 A(map) \
27 A(mines) \
28 A(net) \
29 A(netslide) \
30 A(palisade) \
31 A(pattern) \
32 A(pearl) \
33 A(pegs) \
34 A(range) \
35 A(rect) \
36 A(samegame) \
37 A(signpost) \
38 A(singles) \
39 A(sixteen) \
40 A(slant) \
41 A(solo) \
42 A(tents) \
43 A(towers) \
44 A(tracks) \
45 A(twiddle) \
46 A(undead) \
47 A(unequal) \
48 A(unruly) \
49 A(untangle) \
50
51#define DECL(x) extern const game x;
52#define REF(x) &x,
53GAMELIST(DECL)
54const game *gamelist[] = { GAMELIST(REF) };
55const int gamecount = lenof(gamelist);
diff --git a/apps/plugins/puzzles/src/loopgen.c b/apps/plugins/puzzles/src/loopgen.c
new file mode 100644
index 0000000000..0b6990455f
--- /dev/null
+++ b/apps/plugins/puzzles/src/loopgen.c
@@ -0,0 +1,536 @@
1/*
2 * loopgen.c: loop generation functions for grid.[ch].
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <stddef.h>
8#include <string.h>
9#include <assert.h>
10#include <ctype.h>
11#include <math.h>
12
13#include "puzzles.h"
14#include "tree234.h"
15#include "grid.h"
16#include "loopgen.h"
17
18
19/* We're going to store lists of current candidate faces for colouring black
20 * or white.
21 * Each face gets a 'score', which tells us how adding that face right
22 * now would affect the curliness of the solution loop. We're trying to
23 * maximise that quantity so will bias our random selection of faces to
24 * colour those with high scores */
25struct face_score {
26 int white_score;
27 int black_score;
28 unsigned long random;
29 /* No need to store a grid_face* here. The 'face_scores' array will
30 * be a list of 'face_score' objects, one for each face of the grid, so
31 * the position (index) within the 'face_scores' array will determine
32 * which face corresponds to a particular face_score.
33 * Having a single 'face_scores' array for all faces simplifies memory
34 * management, and probably improves performance, because we don't have to
35 * malloc/free each individual face_score, and we don't have to maintain
36 * a mapping from grid_face* pointers to face_score* pointers.
37 */
38};
39
40static int generic_sort_cmpfn(void *v1, void *v2, size_t offset)
41{
42 struct face_score *f1 = v1;
43 struct face_score *f2 = v2;
44 int r;
45
46 r = *(int *)((char *)f2 + offset) - *(int *)((char *)f1 + offset);
47 if (r) {
48 return r;
49 }
50
51 if (f1->random < f2->random)
52 return -1;
53 else if (f1->random > f2->random)
54 return 1;
55
56 /*
57 * It's _just_ possible that two faces might have been given
58 * the same random value. In that situation, fall back to
59 * comparing based on the positions within the face_scores list.
60 * This introduces a tiny directional bias, but not a significant one.
61 */
62 return f1 - f2;
63}
64
65static int white_sort_cmpfn(void *v1, void *v2)
66{
67 return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,white_score));
68}
69
70static int black_sort_cmpfn(void *v1, void *v2)
71{
72 return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,black_score));
73}
74
75/* 'board' is an array of enum face_colour, indicating which faces are
76 * currently black/white/grey. 'colour' is FACE_WHITE or FACE_BLACK.
77 * Returns whether it's legal to colour the given face with this colour. */
78static int can_colour_face(grid *g, char* board, int face_index,
79 enum face_colour colour)
80{
81 int i, j;
82 grid_face *test_face = g->faces + face_index;
83 grid_face *starting_face, *current_face;
84 grid_dot *starting_dot;
85 int transitions;
86 int current_state, s; /* booleans: equal or not-equal to 'colour' */
87 int found_same_coloured_neighbour = FALSE;
88 assert(board[face_index] != colour);
89
90 /* Can only consider a face for colouring if it's adjacent to a face
91 * with the same colour. */
92 for (i = 0; i < test_face->order; i++) {
93 grid_edge *e = test_face->edges[i];
94 grid_face *f = (e->face1 == test_face) ? e->face2 : e->face1;
95 if (FACE_COLOUR(f) == colour) {
96 found_same_coloured_neighbour = TRUE;
97 break;
98 }
99 }
100 if (!found_same_coloured_neighbour)
101 return FALSE;
102
103 /* Need to avoid creating a loop of faces of this colour around some
104 * differently-coloured faces.
105 * Also need to avoid meeting a same-coloured face at a corner, with
106 * other-coloured faces in between. Here's a simple test that (I believe)
107 * takes care of both these conditions:
108 *
109 * Take the circular path formed by this face's edges, and inflate it
110 * slightly outwards. Imagine walking around this path and consider
111 * the faces that you visit in sequence. This will include all faces
112 * touching the given face, either along an edge or just at a corner.
113 * Count the number of 'colour'/not-'colour' transitions you encounter, as
114 * you walk along the complete loop. This will obviously turn out to be
115 * an even number.
116 * If 0, we're either in the middle of an "island" of this colour (should
117 * be impossible as we're not supposed to create black or white loops),
118 * or we're about to start a new island - also not allowed.
119 * If 4 or greater, there are too many separate coloured regions touching
120 * this face, and colouring it would create a loop or a corner-violation.
121 * The only allowed case is when the count is exactly 2. */
122
123 /* i points to a dot around the test face.
124 * j points to a face around the i^th dot.
125 * The current face will always be:
126 * test_face->dots[i]->faces[j]
127 * We assume dots go clockwise around the test face,
128 * and faces go clockwise around dots. */
129
130 /*
131 * The end condition is slightly fiddly. In sufficiently strange
132 * degenerate grids, our test face may be adjacent to the same
133 * other face multiple times (typically if it's the exterior
134 * face). Consider this, in particular:
135 *
136 * +--+
137 * | |
138 * +--+--+
139 * | | |
140 * +--+--+
141 *
142 * The bottom left face there is adjacent to the exterior face
143 * twice, so we can't just terminate our iteration when we reach
144 * the same _face_ we started at. Furthermore, we can't
145 * condition on having the same (i,j) pair either, because
146 * several (i,j) pairs identify the bottom left contiguity with
147 * the exterior face! We canonicalise the (i,j) pair by taking
148 * one step around before we set the termination tracking.
149 */
150
151 i = j = 0;
152 current_face = test_face->dots[0]->faces[0];
153 if (current_face == test_face) {
154 j = 1;
155 current_face = test_face->dots[0]->faces[1];
156 }
157 transitions = 0;
158 current_state = (FACE_COLOUR(current_face) == colour);
159 starting_dot = NULL;
160 starting_face = NULL;
161 while (TRUE) {
162 /* Advance to next face.
163 * Need to loop here because it might take several goes to
164 * find it. */
165 while (TRUE) {
166 j++;
167 if (j == test_face->dots[i]->order)
168 j = 0;
169
170 if (test_face->dots[i]->faces[j] == test_face) {
171 /* Advance to next dot round test_face, then
172 * find current_face around new dot
173 * and advance to the next face clockwise */
174 i++;
175 if (i == test_face->order)
176 i = 0;
177 for (j = 0; j < test_face->dots[i]->order; j++) {
178 if (test_face->dots[i]->faces[j] == current_face)
179 break;
180 }
181 /* Must actually find current_face around new dot,
182 * or else something's wrong with the grid. */
183 assert(j != test_face->dots[i]->order);
184 /* Found, so advance to next face and try again */
185 } else {
186 break;
187 }
188 }
189 /* (i,j) are now advanced to next face */
190 current_face = test_face->dots[i]->faces[j];
191 s = (FACE_COLOUR(current_face) == colour);
192 if (!starting_dot) {
193 starting_dot = test_face->dots[i];
194 starting_face = current_face;
195 current_state = s;
196 } else {
197 if (s != current_state) {
198 ++transitions;
199 current_state = s;
200 if (transitions > 2)
201 break;
202 }
203 if (test_face->dots[i] == starting_dot &&
204 current_face == starting_face)
205 break;
206 }
207 }
208
209 return (transitions == 2) ? TRUE : FALSE;
210}
211
212/* Count the number of neighbours of 'face', having colour 'colour' */
213static int face_num_neighbours(grid *g, char *board, grid_face *face,
214 enum face_colour colour)
215{
216 int colour_count = 0;
217 int i;
218 grid_face *f;
219 grid_edge *e;
220 for (i = 0; i < face->order; i++) {
221 e = face->edges[i];
222 f = (e->face1 == face) ? e->face2 : e->face1;
223 if (FACE_COLOUR(f) == colour)
224 ++colour_count;
225 }
226 return colour_count;
227}
228
229/* The 'score' of a face reflects its current desirability for selection
230 * as the next face to colour white or black. We want to encourage moving
231 * into grey areas and increasing loopiness, so we give scores according to
232 * how many of the face's neighbours are currently coloured the same as the
233 * proposed colour. */
234static int face_score(grid *g, char *board, grid_face *face,
235 enum face_colour colour)
236{
237 /* Simple formula: score = 0 - num. same-coloured neighbours,
238 * so a higher score means fewer same-coloured neighbours. */
239 return -face_num_neighbours(g, board, face, colour);
240}
241
242/*
243 * Generate a new complete random closed loop for the given grid.
244 *
245 * The method is to generate a WHITE/BLACK colouring of all the faces,
246 * such that the WHITE faces will define the inside of the path, and the
247 * BLACK faces define the outside.
248 * To do this, we initially colour all faces GREY. The infinite space outside
249 * the grid is coloured BLACK, and we choose a random face to colour WHITE.
250 * Then we gradually grow the BLACK and the WHITE regions, eliminating GREY
251 * faces, until the grid is filled with BLACK/WHITE. As we grow the regions,
252 * we avoid creating loops of a single colour, to preserve the topological
253 * shape of the WHITE and BLACK regions.
254 * We also try to make the boundary as loopy and twisty as possible, to avoid
255 * generating paths that are uninteresting.
256 * The algorithm works by choosing a BLACK/WHITE colour, then choosing a GREY
257 * face that can be coloured with that colour (without violating the
258 * topological shape of that region). It's not obvious, but I think this
259 * algorithm is guaranteed to terminate without leaving any GREY faces behind.
260 * Indeed, if there are any GREY faces at all, both the WHITE and BLACK
261 * regions can be grown.
262 * This is checked using assert()ions, and I haven't seen any failures yet.
263 *
264 * Hand-wavy proof: imagine what can go wrong...
265 *
266 * Could the white faces get completely cut off by the black faces, and still
267 * leave some grey faces remaining?
268 * No, because then the black faces would form a loop around both the white
269 * faces and the grey faces, which is disallowed because we continually
270 * maintain the correct topological shape of the black region.
271 * Similarly, the black faces can never get cut off by the white faces. That
272 * means both the WHITE and BLACK regions always have some room to grow into
273 * the GREY regions.
274 * Could it be that we can't colour some GREY face, because there are too many
275 * WHITE/BLACK transitions as we walk round the face? (see the
276 * can_colour_face() function for details)
277 * No. Imagine otherwise, and we see WHITE/BLACK/WHITE/BLACK as we walk
278 * around the face. The two WHITE faces would be connected by a WHITE path,
279 * and the BLACK faces would be connected by a BLACK path. These paths would
280 * have to cross, which is impossible.
281 * Another thing that could go wrong: perhaps we can't find any GREY face to
282 * colour WHITE, because it would create a loop-violation or a corner-violation
283 * with the other WHITE faces?
284 * This is a little bit tricky to prove impossible. Imagine you have such a
285 * GREY face (that is, if you coloured it WHITE, you would create a WHITE loop
286 * or corner violation).
287 * That would cut all the non-white area into two blobs. One of those blobs
288 * must be free of BLACK faces (because the BLACK stuff is a connected blob).
289 * So we have a connected GREY area, completely surrounded by WHITE
290 * (including the GREY face we've tentatively coloured WHITE).
291 * A well-known result in graph theory says that you can always find a GREY
292 * face whose removal leaves the remaining GREY area connected. And it says
293 * there are at least two such faces, so we can always choose the one that
294 * isn't the "tentative" GREY face. Colouring that face WHITE leaves
295 * everything nice and connected, including that "tentative" GREY face which
296 * acts as a gateway to the rest of the non-WHITE grid.
297 */
298void generate_loop(grid *g, char *board, random_state *rs,
299 loopgen_bias_fn_t bias, void *biasctx)
300{
301 int i, j;
302 int num_faces = g->num_faces;
303 struct face_score *face_scores; /* Array of face_score objects */
304 struct face_score *fs; /* Points somewhere in the above list */
305 struct grid_face *cur_face;
306 tree234 *lightable_faces_sorted;
307 tree234 *darkable_faces_sorted;
308 int *face_list;
309 int do_random_pass;
310
311 /* Make a board */
312 memset(board, FACE_GREY, num_faces);
313
314 /* Create and initialise the list of face_scores */
315 face_scores = snewn(num_faces, struct face_score);
316 for (i = 0; i < num_faces; i++) {
317 face_scores[i].random = random_bits(rs, 31);
318 face_scores[i].black_score = face_scores[i].white_score = 0;
319 }
320
321 /* Colour a random, finite face white. The infinite face is implicitly
322 * coloured black. Together, they will seed the random growth process
323 * for the black and white areas. */
324 i = random_upto(rs, num_faces);
325 board[i] = FACE_WHITE;
326
327 /* We need a way of favouring faces that will increase our loopiness.
328 * We do this by maintaining a list of all candidate faces sorted by
329 * their score and choose randomly from that with appropriate skew.
330 * In order to avoid consistently biasing towards particular faces, we
331 * need the sort order _within_ each group of scores to be completely
332 * random. But it would be abusing the hospitality of the tree234 data
333 * structure if our comparison function were nondeterministic :-). So with
334 * each face we associate a random number that does not change during a
335 * particular run of the generator, and use that as a secondary sort key.
336 * Yes, this means we will be biased towards particular random faces in
337 * any one run but that doesn't actually matter. */
338
339 lightable_faces_sorted = newtree234(white_sort_cmpfn);
340 darkable_faces_sorted = newtree234(black_sort_cmpfn);
341
342 /* Initialise the lists of lightable and darkable faces. This is
343 * slightly different from the code inside the while-loop, because we need
344 * to check every face of the board (the grid structure does not keep a
345 * list of the infinite face's neighbours). */
346 for (i = 0; i < num_faces; i++) {
347 grid_face *f = g->faces + i;
348 struct face_score *fs = face_scores + i;
349 if (board[i] != FACE_GREY) continue;
350 /* We need the full colourability check here, it's not enough simply
351 * to check neighbourhood. On some grids, a neighbour of the infinite
352 * face is not necessarily darkable. */
353 if (can_colour_face(g, board, i, FACE_BLACK)) {
354 fs->black_score = face_score(g, board, f, FACE_BLACK);
355 add234(darkable_faces_sorted, fs);
356 }
357 if (can_colour_face(g, board, i, FACE_WHITE)) {
358 fs->white_score = face_score(g, board, f, FACE_WHITE);
359 add234(lightable_faces_sorted, fs);
360 }
361 }
362
363 /* Colour faces one at a time until no more faces are colourable. */
364 while (TRUE)
365 {
366 enum face_colour colour;
367 tree234 *faces_to_pick;
368 int c_lightable = count234(lightable_faces_sorted);
369 int c_darkable = count234(darkable_faces_sorted);
370 if (c_lightable == 0 && c_darkable == 0) {
371 /* No more faces we can use at all. */
372 break;
373 }
374 assert(c_lightable != 0 && c_darkable != 0);
375
376 /* Choose a colour, and colour the best available face
377 * with that colour. */
378 colour = random_upto(rs, 2) ? FACE_WHITE : FACE_BLACK;
379
380 if (colour == FACE_WHITE)
381 faces_to_pick = lightable_faces_sorted;
382 else
383 faces_to_pick = darkable_faces_sorted;
384 if (bias) {
385 /*
386 * Go through all the candidate faces and pick the one the
387 * bias function likes best, breaking ties using the
388 * ordering in our tree234 (which is why we replace only
389 * if score > bestscore, not >=).
390 */
391 int j, k;
392 struct face_score *best = NULL;
393 int score, bestscore = 0;
394
395 for (j = 0;
396 (fs = (struct face_score *)index234(faces_to_pick, j))!=NULL;
397 j++) {
398
399 assert(fs);
400 k = fs - face_scores;
401 assert(board[k] == FACE_GREY);
402 board[k] = colour;
403 score = bias(biasctx, board, k);
404 board[k] = FACE_GREY;
405 bias(biasctx, board, k); /* let bias know we put it back */
406
407 if (!best || score > bestscore) {
408 bestscore = score;
409 best = fs;
410 }
411 }
412 fs = best;
413 } else {
414 fs = (struct face_score *)index234(faces_to_pick, 0);
415 }
416 assert(fs);
417 i = fs - face_scores;
418 assert(board[i] == FACE_GREY);
419 board[i] = colour;
420 if (bias)
421 bias(biasctx, board, i); /* notify bias function of the change */
422
423 /* Remove this newly-coloured face from the lists. These lists should
424 * only contain grey faces. */
425 del234(lightable_faces_sorted, fs);
426 del234(darkable_faces_sorted, fs);
427
428 /* Remember which face we've just coloured */
429 cur_face = g->faces + i;
430
431 /* The face we've just coloured potentially affects the colourability
432 * and the scores of any neighbouring faces (touching at a corner or
433 * edge). So the search needs to be conducted around all faces
434 * touching the one we've just lit. Iterate over its corners, then
435 * over each corner's faces. For each such face, we remove it from
436 * the lists, recalculate any scores, then add it back to the lists
437 * (depending on whether it is lightable, darkable or both). */
438 for (i = 0; i < cur_face->order; i++) {
439 grid_dot *d = cur_face->dots[i];
440 for (j = 0; j < d->order; j++) {
441 grid_face *f = d->faces[j];
442 int fi; /* face index of f */
443
444 if (f == NULL)
445 continue;
446 if (f == cur_face)
447 continue;
448
449 /* If the face is already coloured, it won't be on our
450 * lightable/darkable lists anyway, so we can skip it without
451 * bothering with the removal step. */
452 if (FACE_COLOUR(f) != FACE_GREY) continue;
453
454 /* Find the face index and face_score* corresponding to f */
455 fi = f - g->faces;
456 fs = face_scores + fi;
457
458 /* Remove from lightable list if it's in there. We do this,
459 * even if it is still lightable, because the score might
460 * be different, and we need to remove-then-add to maintain
461 * correct sort order. */
462 del234(lightable_faces_sorted, fs);
463 if (can_colour_face(g, board, fi, FACE_WHITE)) {
464 fs->white_score = face_score(g, board, f, FACE_WHITE);
465 add234(lightable_faces_sorted, fs);
466 }
467 /* Do the same for darkable list. */
468 del234(darkable_faces_sorted, fs);
469 if (can_colour_face(g, board, fi, FACE_BLACK)) {
470 fs->black_score = face_score(g, board, f, FACE_BLACK);
471 add234(darkable_faces_sorted, fs);
472 }
473 }
474 }
475 }
476
477 /* Clean up */
478 freetree234(lightable_faces_sorted);
479 freetree234(darkable_faces_sorted);
480 sfree(face_scores);
481
482 /* The next step requires a shuffled list of all faces */
483 face_list = snewn(num_faces, int);
484 for (i = 0; i < num_faces; ++i) {
485 face_list[i] = i;
486 }
487 shuffle(face_list, num_faces, sizeof(int), rs);
488
489 /* The above loop-generation algorithm can often leave large clumps
490 * of faces of one colour. In extreme cases, the resulting path can be
491 * degenerate and not very satisfying to solve.
492 * This next step alleviates this problem:
493 * Go through the shuffled list, and flip the colour of any face we can
494 * legally flip, and which is adjacent to only one face of the opposite
495 * colour - this tends to grow 'tendrils' into any clumps.
496 * Repeat until we can find no more faces to flip. This will
497 * eventually terminate, because each flip increases the loop's
498 * perimeter, which cannot increase for ever.
499 * The resulting path will have maximal loopiness (in the sense that it
500 * cannot be improved "locally". Unfortunately, this allows a player to
501 * make some illicit deductions. To combat this (and make the path more
502 * interesting), we do one final pass making random flips. */
503
504 /* Set to TRUE for final pass */
505 do_random_pass = FALSE;
506
507 while (TRUE) {
508 /* Remember whether a flip occurred during this pass */
509 int flipped = FALSE;
510
511 for (i = 0; i < num_faces; ++i) {
512 int j = face_list[i];
513 enum face_colour opp =
514 (board[j] == FACE_WHITE) ? FACE_BLACK : FACE_WHITE;
515 if (can_colour_face(g, board, j, opp)) {
516 grid_face *face = g->faces +j;
517 if (do_random_pass) {
518 /* final random pass */
519 if (!random_upto(rs, 10))
520 board[j] = opp;
521 } else {
522 /* normal pass - flip when neighbour count is 1 */
523 if (face_num_neighbours(g, board, face, opp) == 1) {
524 board[j] = opp;
525 flipped = TRUE;
526 }
527 }
528 }
529 }
530
531 if (do_random_pass) break;
532 if (!flipped) do_random_pass = TRUE;
533 }
534
535 sfree(face_list);
536}
diff --git a/apps/plugins/puzzles/src/loopgen.h b/apps/plugins/puzzles/src/loopgen.h
new file mode 100644
index 0000000000..079c87c576
--- /dev/null
+++ b/apps/plugins/puzzles/src/loopgen.h
@@ -0,0 +1,35 @@
1/*
2 * loopgen.h: interface file for loop generation functions for grid.[ch].
3 */
4
5#ifndef _LOOPGEN_H
6#define _LOOPGEN_H
7
8#include "puzzles.h"
9#include "grid.h"
10
11enum face_colour { FACE_WHITE, FACE_GREY, FACE_BLACK };
12
13/* face should be of type grid_face* here. */
14#define FACE_COLOUR(face) \
15 ( (face) == NULL ? FACE_BLACK : \
16 board[(face) - g->faces] )
17
18typedef int (*loopgen_bias_fn_t)(void *ctx, char *board, int face);
19
20/* 'board' should be a char array whose length is the same as
21 * g->num_faces: this will be filled in with FACE_WHITE or FACE_BLACK
22 * after loop generation.
23 *
24 * If 'bias' is non-null, it should be a user-provided function which
25 * rates a half-finished board (i.e. may include some FACE_GREYs) for
26 * desirability; this will cause the loop generator to bias in favour
27 * of loops with a high return value from that function. The 'face'
28 * parameter to the bias function indicates which face of the grid has
29 * been modified since the last call; it is guaranteed that only one
30 * will have been (so that bias functions can work incrementally
31 * rather than re-scanning the whole grid on every call). */
32extern void generate_loop(grid *g, char *board, random_state *rs,
33 loopgen_bias_fn_t bias, void *biasctx);
34
35#endif
diff --git a/apps/plugins/puzzles/src/loopy.R b/apps/plugins/puzzles/src/loopy.R
new file mode 100644
index 0000000000..f44560095d
--- /dev/null
+++ b/apps/plugins/puzzles/src/loopy.R
@@ -0,0 +1,31 @@
1# -*- makefile -*-
2
3LOOPY_EXTRA = tree234 dsf grid penrose loopgen
4
5loopy : [X] GTK COMMON loopy LOOPY_EXTRA loopy-icon|no-icon
6
7loopy : [G] WINDOWS COMMON loopy LOOPY_EXTRA loopy.res|noicon.res
8
9loopysolver : [U] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE m.lib
10loopysolver : [C] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE
11
12#penrose : [U] penrose[TEST_PENROSE] STANDALONE m.lib
13#penrose : [C] penrose[TEST_PENROSE] STANDALONE
14
15#test-basis : [U] penrose[TEST_VECTORS] tree234 STANDALONE m.lib
16#test-basis : [C] penrose[TEST_VECTORS] tree234 STANDALONE
17
18
19ALL += loopy[COMBINED] LOOPY_EXTRA
20
21!begin am gtk
22GAMES += loopy
23!end
24
25!begin >list.c
26 A(loopy) \
27!end
28
29!begin >gamedesc.txt
30loopy:loopy.exe:Loopy:Loop-drawing puzzle:Draw a single closed loop, given clues about number of adjacent edges.
31!end
diff --git a/apps/plugins/puzzles/src/loopy.c b/apps/plugins/puzzles/src/loopy.c
new file mode 100644
index 0000000000..652b9ecc09
--- /dev/null
+++ b/apps/plugins/puzzles/src/loopy.c
@@ -0,0 +1,3786 @@
1/*
2 * loopy.c:
3 *
4 * An implementation of the Nikoli game 'Loop the loop'.
5 * (c) Mike Pinna, 2005, 2006
6 * Substantially rewritten to allowing for more general types of grid.
7 * (c) Lambros Lambrou 2008
8 *
9 * vim: set shiftwidth=4 :set textwidth=80:
10 */
11
12/*
13 * Possible future solver enhancements:
14 *
15 * - There's an interesting deductive technique which makes use
16 * of topology rather than just graph theory. Each _face_ in
17 * the grid is either inside or outside the loop; you can tell
18 * that two faces are on the same side of the loop if they're
19 * separated by a LINE_NO (or, more generally, by a path
20 * crossing no LINE_UNKNOWNs and an even number of LINE_YESes),
21 * and on the opposite side of the loop if they're separated by
22 * a LINE_YES (or an odd number of LINE_YESes and no
23 * LINE_UNKNOWNs). Oh, and any face separated from the outside
24 * of the grid by a LINE_YES or a LINE_NO is on the inside or
25 * outside respectively. So if you can track this for all
26 * faces, you figure out the state of the line between a pair
27 * once their relative insideness is known.
28 * + The way I envisage this working is simply to keep an edsf
29 * of all _faces_, which indicates whether they're on
30 * opposite sides of the loop from one another. We also
31 * include a special entry in the edsf for the infinite
32 * exterior "face".
33 * + So, the simple way to do this is to just go through the
34 * edges: every time we see an edge in a state other than
35 * LINE_UNKNOWN which separates two faces that aren't in the
36 * same edsf class, we can rectify that by merging the
37 * classes. Then, conversely, an edge in LINE_UNKNOWN state
38 * which separates two faces that _are_ in the same edsf
39 * class can immediately have its state determined.
40 * + But you can go one better, if you're prepared to loop
41 * over all _pairs_ of edges. Suppose we have edges A and B,
42 * which respectively separate faces A1,A2 and B1,B2.
43 * Suppose that A,B are in the same edge-edsf class and that
44 * A1,B1 (wlog) are in the same face-edsf class; then we can
45 * immediately place A2,B2 into the same face-edsf class (as
46 * each other, not as A1 and A2) one way round or the other.
47 * And conversely again, if A1,B1 are in the same face-edsf
48 * class and so are A2,B2, then we can put A,B into the same
49 * face-edsf class.
50 * * Of course, this deduction requires a quadratic-time
51 * loop over all pairs of edges in the grid, so it should
52 * be reserved until there's nothing easier left to be
53 * done.
54 *
55 * - The generalised grid support has made me (SGT) notice a
56 * possible extension to the loop-avoidance code. When you have
57 * a path of connected edges such that no other edges at all
58 * are incident on any vertex in the middle of the path - or,
59 * alternatively, such that any such edges are already known to
60 * be LINE_NO - then you know those edges are either all
61 * LINE_YES or all LINE_NO. Hence you can mentally merge the
62 * entire path into a single long curly edge for the purposes
63 * of loop avoidance, and look directly at whether or not the
64 * extreme endpoints of the path are connected by some other
65 * route. I find this coming up fairly often when I play on the
66 * octagonal grid setting, so it might be worth implementing in
67 * the solver.
68 *
69 * - (Just a speed optimisation.) Consider some todo list queue where every
70 * time we modify something we mark it for consideration by other bits of
71 * the solver, to save iteration over things that have already been done.
72 */
73
74#include <stdio.h>
75#include <stdlib.h>
76#include <stddef.h>
77#include <string.h>
78#include <assert.h>
79#include <ctype.h>
80#include <math.h>
81
82#include "puzzles.h"
83#include "tree234.h"
84#include "grid.h"
85#include "loopgen.h"
86
87/* Debugging options */
88
89/*
90#define DEBUG_CACHES
91#define SHOW_WORKING
92#define DEBUG_DLINES
93*/
94
95/* ----------------------------------------------------------------------
96 * Struct, enum and function declarations
97 */
98
99enum {
100 COL_BACKGROUND,
101 COL_FOREGROUND,
102 COL_LINEUNKNOWN,
103 COL_HIGHLIGHT,
104 COL_MISTAKE,
105 COL_SATISFIED,
106 COL_FAINT,
107 NCOLOURS
108};
109
110struct game_state {
111 grid *game_grid; /* ref-counted (internally) */
112
113 /* Put -1 in a face that doesn't get a clue */
114 signed char *clues;
115
116 /* Array of line states, to store whether each line is
117 * YES, NO or UNKNOWN */
118 char *lines;
119
120 unsigned char *line_errors;
121 int exactly_one_loop;
122
123 int solved;
124 int cheated;
125
126 /* Used in game_text_format(), so that it knows what type of
127 * grid it's trying to render as ASCII text. */
128 int grid_type;
129};
130
131enum solver_status {
132 SOLVER_SOLVED, /* This is the only solution the solver could find */
133 SOLVER_MISTAKE, /* This is definitely not a solution */
134 SOLVER_AMBIGUOUS, /* This _might_ be an ambiguous solution */
135 SOLVER_INCOMPLETE /* This may be a partial solution */
136};
137
138/* ------ Solver state ------ */
139typedef struct solver_state {
140 game_state *state;
141 enum solver_status solver_status;
142 /* NB looplen is the number of dots that are joined together at a point, ie a
143 * looplen of 1 means there are no lines to a particular dot */
144 int *looplen;
145
146 /* Difficulty level of solver. Used by solver functions that want to
147 * vary their behaviour depending on the requested difficulty level. */
148 int diff;
149
150 /* caches */
151 char *dot_yes_count;
152 char *dot_no_count;
153 char *face_yes_count;
154 char *face_no_count;
155 char *dot_solved, *face_solved;
156 int *dotdsf;
157
158 /* Information for Normal level deductions:
159 * For each dline, store a bitmask for whether we know:
160 * (bit 0) at least one is YES
161 * (bit 1) at most one is YES */
162 char *dlines;
163
164 /* Hard level information */
165 int *linedsf;
166} solver_state;
167
168/*
169 * Difficulty levels. I do some macro ickery here to ensure that my
170 * enum and the various forms of my name list always match up.
171 */
172
173#define DIFFLIST(A) \
174 A(EASY,Easy,e) \
175 A(NORMAL,Normal,n) \
176 A(TRICKY,Tricky,t) \
177 A(HARD,Hard,h)
178#define ENUM(upper,title,lower) DIFF_ ## upper,
179#define TITLE(upper,title,lower) #title,
180#define ENCODE(upper,title,lower) #lower
181#define CONFIG(upper,title,lower) ":" #title
182enum { DIFFLIST(ENUM) DIFF_MAX };
183static char const *const diffnames[] = { DIFFLIST(TITLE) };
184static char const diffchars[] = DIFFLIST(ENCODE);
185#define DIFFCONFIG DIFFLIST(CONFIG)
186
187/*
188 * Solver routines, sorted roughly in order of computational cost.
189 * The solver will run the faster deductions first, and slower deductions are
190 * only invoked when the faster deductions are unable to make progress.
191 * Each function is associated with a difficulty level, so that the generated
192 * puzzles are solvable by applying only the functions with the chosen
193 * difficulty level or lower.
194 */
195#define SOLVERLIST(A) \
196 A(trivial_deductions, DIFF_EASY) \
197 A(dline_deductions, DIFF_NORMAL) \
198 A(linedsf_deductions, DIFF_HARD) \
199 A(loop_deductions, DIFF_EASY)
200#define SOLVER_FN_DECL(fn,diff) static int fn(solver_state *);
201#define SOLVER_FN(fn,diff) &fn,
202#define SOLVER_DIFF(fn,diff) diff,
203SOLVERLIST(SOLVER_FN_DECL)
204static int (*(solver_fns[]))(solver_state *) = { SOLVERLIST(SOLVER_FN) };
205static int const solver_diffs[] = { SOLVERLIST(SOLVER_DIFF) };
206static const int NUM_SOLVERS = sizeof(solver_diffs)/sizeof(*solver_diffs);
207
208struct game_params {
209 int w, h;
210 int diff;
211 int type;
212};
213
214/* line_drawstate is the same as line_state, but with the extra ERROR
215 * possibility. The drawing code copies line_state to line_drawstate,
216 * except in the case that the line is an error. */
217enum line_state { LINE_YES, LINE_UNKNOWN, LINE_NO };
218enum line_drawstate { DS_LINE_YES, DS_LINE_UNKNOWN,
219 DS_LINE_NO, DS_LINE_ERROR };
220
221#define OPP(line_state) \
222 (2 - line_state)
223
224
225struct game_drawstate {
226 int started;
227 int tilesize;
228 int flashing;
229 int *textx, *texty;
230 char *lines;
231 char *clue_error;
232 char *clue_satisfied;
233};
234
235static char *validate_desc(const game_params *params, const char *desc);
236static int dot_order(const game_state* state, int i, char line_type);
237static int face_order(const game_state* state, int i, char line_type);
238static solver_state *solve_game_rec(const solver_state *sstate);
239
240#ifdef DEBUG_CACHES
241static void check_caches(const solver_state* sstate);
242#else
243#define check_caches(s)
244#endif
245
246/*
247 * Grid type config options available in Loopy.
248 *
249 * Annoyingly, we have to use an enum here which doesn't match up
250 * exactly to the grid-type enum in grid.h. Values in params->types
251 * are given by names such as LOOPY_GRID_SQUARE, which shouldn't be
252 * confused with GRID_SQUARE which is the value you pass to grid_new()
253 * and friends. So beware!
254 *
255 * (This is partly for historical reasons - Loopy's version of the
256 * enum is encoded in game parameter strings, so we keep it for
257 * backwards compatibility. But also, we need to store additional data
258 * here alongside each enum value, such as names for the presets menu,
259 * which isn't stored in grid.h; so we have to have our own list macro
260 * here anyway, and C doesn't make it easy to enforce that that lines
261 * up exactly with grid.h.)
262 *
263 * Do not add values to this list _except_ at the end, or old game ids
264 * will stop working!
265 */
266#define GRIDLIST(A) \
267 A("Squares",SQUARE,3,3) \
268 A("Triangular",TRIANGULAR,3,3) \
269 A("Honeycomb",HONEYCOMB,3,3) \
270 A("Snub-Square",SNUBSQUARE,3,3) \
271 A("Cairo",CAIRO,3,4) \
272 A("Great-Hexagonal",GREATHEXAGONAL,3,3) \
273 A("Octagonal",OCTAGONAL,3,3) \
274 A("Kites",KITE,3,3) \
275 A("Floret",FLORET,1,2) \
276 A("Dodecagonal",DODECAGONAL,2,2) \
277 A("Great-Dodecagonal",GREATDODECAGONAL,2,2) \
278 A("Penrose (kite/dart)",PENROSE_P2,3,3) \
279 A("Penrose (rhombs)",PENROSE_P3,3,3) \
280 A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \
281 /* end of list */
282
283#define GRID_NAME(title,type,amin,omin) title,
284#define GRID_CONFIG(title,type,amin,omin) ":" title
285#define GRID_LOOPYTYPE(title,type,amin,omin) LOOPY_GRID_ ## type,
286#define GRID_GRIDTYPE(title,type,amin,omin) GRID_ ## type,
287#define GRID_SIZES(title,type,amin,omin) \
288 {amin, omin, \
289 "Width and height for this grid type must both be at least " #amin, \
290 "At least one of width and height for this grid type must be at least " #omin,},
291enum { GRIDLIST(GRID_LOOPYTYPE) };
292static char const *const gridnames[] = { GRIDLIST(GRID_NAME) };
293#define GRID_CONFIGS GRIDLIST(GRID_CONFIG)
294static grid_type grid_types[] = { GRIDLIST(GRID_GRIDTYPE) };
295#define NUM_GRID_TYPES (sizeof(grid_types) / sizeof(grid_types[0]))
296static const struct {
297 int amin, omin;
298 char *aerr, *oerr;
299} grid_size_limits[] = { GRIDLIST(GRID_SIZES) };
300
301/* Generates a (dynamically allocated) new grid, according to the
302 * type and size requested in params. Does nothing if the grid is already
303 * generated. */
304static grid *loopy_generate_grid(const game_params *params,
305 const char *grid_desc)
306{
307 return grid_new(grid_types[params->type], params->w, params->h, grid_desc);
308}
309
310/* ----------------------------------------------------------------------
311 * Preprocessor magic
312 */
313
314/* General constants */
315#define PREFERRED_TILE_SIZE 32
316#define BORDER(tilesize) ((tilesize) / 2)
317#define FLASH_TIME 0.5F
318
319#define BIT_SET(field, bit) ((field) & (1<<(bit)))
320
321#define SET_BIT(field, bit) (BIT_SET(field, bit) ? FALSE : \
322 ((field) |= (1<<(bit)), TRUE))
323
324#define CLEAR_BIT(field, bit) (BIT_SET(field, bit) ? \
325 ((field) &= ~(1<<(bit)), TRUE) : FALSE)
326
327#define CLUE2CHAR(c) \
328 ((c < 0) ? ' ' : c < 10 ? c + '0' : c - 10 + 'A')
329
330/* ----------------------------------------------------------------------
331 * General struct manipulation and other straightforward code
332 */
333
334static game_state *dup_game(const game_state *state)
335{
336 game_state *ret = snew(game_state);
337
338 ret->game_grid = state->game_grid;
339 ret->game_grid->refcount++;
340
341 ret->solved = state->solved;
342 ret->cheated = state->cheated;
343
344 ret->clues = snewn(state->game_grid->num_faces, signed char);
345 memcpy(ret->clues, state->clues, state->game_grid->num_faces);
346
347 ret->lines = snewn(state->game_grid->num_edges, char);
348 memcpy(ret->lines, state->lines, state->game_grid->num_edges);
349
350 ret->line_errors = snewn(state->game_grid->num_edges, unsigned char);
351 memcpy(ret->line_errors, state->line_errors, state->game_grid->num_edges);
352 ret->exactly_one_loop = state->exactly_one_loop;
353
354 ret->grid_type = state->grid_type;
355 return ret;
356}
357
358static void free_game(game_state *state)
359{
360 if (state) {
361 grid_free(state->game_grid);
362 sfree(state->clues);
363 sfree(state->lines);
364 sfree(state->line_errors);
365 sfree(state);
366 }
367}
368
369static solver_state *new_solver_state(const game_state *state, int diff) {
370 int i;
371 int num_dots = state->game_grid->num_dots;
372 int num_faces = state->game_grid->num_faces;
373 int num_edges = state->game_grid->num_edges;
374 solver_state *ret = snew(solver_state);
375
376 ret->state = dup_game(state);
377
378 ret->solver_status = SOLVER_INCOMPLETE;
379 ret->diff = diff;
380
381 ret->dotdsf = snew_dsf(num_dots);
382 ret->looplen = snewn(num_dots, int);
383
384 for (i = 0; i < num_dots; i++) {
385 ret->looplen[i] = 1;
386 }
387
388 ret->dot_solved = snewn(num_dots, char);
389 ret->face_solved = snewn(num_faces, char);
390 memset(ret->dot_solved, FALSE, num_dots);
391 memset(ret->face_solved, FALSE, num_faces);
392
393 ret->dot_yes_count = snewn(num_dots, char);
394 memset(ret->dot_yes_count, 0, num_dots);
395 ret->dot_no_count = snewn(num_dots, char);
396 memset(ret->dot_no_count, 0, num_dots);
397 ret->face_yes_count = snewn(num_faces, char);
398 memset(ret->face_yes_count, 0, num_faces);
399 ret->face_no_count = snewn(num_faces, char);
400 memset(ret->face_no_count, 0, num_faces);
401
402 if (diff < DIFF_NORMAL) {
403 ret->dlines = NULL;
404 } else {
405 ret->dlines = snewn(2*num_edges, char);
406 memset(ret->dlines, 0, 2*num_edges);
407 }
408
409 if (diff < DIFF_HARD) {
410 ret->linedsf = NULL;
411 } else {
412 ret->linedsf = snew_dsf(state->game_grid->num_edges);
413 }
414
415 return ret;
416}
417
418static void free_solver_state(solver_state *sstate) {
419 if (sstate) {
420 free_game(sstate->state);
421 sfree(sstate->dotdsf);
422 sfree(sstate->looplen);
423 sfree(sstate->dot_solved);
424 sfree(sstate->face_solved);
425 sfree(sstate->dot_yes_count);
426 sfree(sstate->dot_no_count);
427 sfree(sstate->face_yes_count);
428 sfree(sstate->face_no_count);
429
430 /* OK, because sfree(NULL) is a no-op */
431 sfree(sstate->dlines);
432 sfree(sstate->linedsf);
433
434 sfree(sstate);
435 }
436}
437
438static solver_state *dup_solver_state(const solver_state *sstate) {
439 game_state *state = sstate->state;
440 int num_dots = state->game_grid->num_dots;
441 int num_faces = state->game_grid->num_faces;
442 int num_edges = state->game_grid->num_edges;
443 solver_state *ret = snew(solver_state);
444
445 ret->state = state = dup_game(sstate->state);
446
447 ret->solver_status = sstate->solver_status;
448 ret->diff = sstate->diff;
449
450 ret->dotdsf = snewn(num_dots, int);
451 ret->looplen = snewn(num_dots, int);
452 memcpy(ret->dotdsf, sstate->dotdsf,
453 num_dots * sizeof(int));
454 memcpy(ret->looplen, sstate->looplen,
455 num_dots * sizeof(int));
456
457 ret->dot_solved = snewn(num_dots, char);
458 ret->face_solved = snewn(num_faces, char);
459 memcpy(ret->dot_solved, sstate->dot_solved, num_dots);
460 memcpy(ret->face_solved, sstate->face_solved, num_faces);
461
462 ret->dot_yes_count = snewn(num_dots, char);
463 memcpy(ret->dot_yes_count, sstate->dot_yes_count, num_dots);
464 ret->dot_no_count = snewn(num_dots, char);
465 memcpy(ret->dot_no_count, sstate->dot_no_count, num_dots);
466
467 ret->face_yes_count = snewn(num_faces, char);
468 memcpy(ret->face_yes_count, sstate->face_yes_count, num_faces);
469 ret->face_no_count = snewn(num_faces, char);
470 memcpy(ret->face_no_count, sstate->face_no_count, num_faces);
471
472 if (sstate->dlines) {
473 ret->dlines = snewn(2*num_edges, char);
474 memcpy(ret->dlines, sstate->dlines,
475 2*num_edges);
476 } else {
477 ret->dlines = NULL;
478 }
479
480 if (sstate->linedsf) {
481 ret->linedsf = snewn(num_edges, int);
482 memcpy(ret->linedsf, sstate->linedsf,
483 num_edges * sizeof(int));
484 } else {
485 ret->linedsf = NULL;
486 }
487
488 return ret;
489}
490
491static game_params *default_params(void)
492{
493 game_params *ret = snew(game_params);
494
495#ifdef SLOW_SYSTEM
496 ret->h = 7;
497 ret->w = 7;
498#else
499 ret->h = 10;
500 ret->w = 10;
501#endif
502 ret->diff = DIFF_EASY;
503 ret->type = 0;
504
505 return ret;
506}
507
508static game_params *dup_params(const game_params *params)
509{
510 game_params *ret = snew(game_params);
511
512 *ret = *params; /* structure copy */
513 return ret;
514}
515
516static const game_params loopy_presets_top[] = {
517#ifdef SMALL_SCREEN
518 { 7, 7, DIFF_EASY, LOOPY_GRID_SQUARE },
519 { 7, 7, DIFF_NORMAL, LOOPY_GRID_SQUARE },
520 { 7, 7, DIFF_HARD, LOOPY_GRID_SQUARE },
521 { 7, 7, DIFF_HARD, LOOPY_GRID_TRIANGULAR },
522 { 5, 5, DIFF_HARD, LOOPY_GRID_SNUBSQUARE },
523 { 7, 7, DIFF_HARD, LOOPY_GRID_CAIRO },
524 { 5, 5, DIFF_HARD, LOOPY_GRID_KITE },
525 { 6, 6, DIFF_HARD, LOOPY_GRID_PENROSE_P2 },
526 { 6, 6, DIFF_HARD, LOOPY_GRID_PENROSE_P3 },
527#else
528 { 7, 7, DIFF_EASY, LOOPY_GRID_SQUARE },
529 { 10, 10, DIFF_EASY, LOOPY_GRID_SQUARE },
530 { 7, 7, DIFF_NORMAL, LOOPY_GRID_SQUARE },
531 { 10, 10, DIFF_NORMAL, LOOPY_GRID_SQUARE },
532 { 7, 7, DIFF_HARD, LOOPY_GRID_SQUARE },
533 { 10, 10, DIFF_HARD, LOOPY_GRID_SQUARE },
534 { 12, 10, DIFF_HARD, LOOPY_GRID_TRIANGULAR },
535 { 7, 7, DIFF_HARD, LOOPY_GRID_SNUBSQUARE },
536 { 9, 9, DIFF_HARD, LOOPY_GRID_CAIRO },
537 { 5, 5, DIFF_HARD, LOOPY_GRID_KITE },
538 { 10, 10, DIFF_HARD, LOOPY_GRID_PENROSE_P2 },
539 { 10, 10, DIFF_HARD, LOOPY_GRID_PENROSE_P3 },
540#endif
541};
542
543static const game_params loopy_presets_more[] = {
544#ifdef SMALL_SCREEN
545 { 7, 7, DIFF_HARD, LOOPY_GRID_HONEYCOMB },
546 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL },
547 { 5, 5, DIFF_HARD, LOOPY_GRID_OCTAGONAL },
548 { 3, 3, DIFF_HARD, LOOPY_GRID_FLORET },
549 { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
550 { 3, 3, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL },
551 { 3, 2, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL },
552#else
553 { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB },
554 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL },
555 { 7, 7, DIFF_HARD, LOOPY_GRID_OCTAGONAL },
556 { 5, 5, DIFF_HARD, LOOPY_GRID_FLORET },
557 { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
558 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL },
559 { 5, 3, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL },
560#endif
561};
562
563static void preset_menu_add_preset_with_title(struct preset_menu *menu,
564 const game_params *params)
565{
566 char buf[80];
567 game_params *dup_params;
568
569 sprintf(buf, "%dx%d %s - %s", params->h, params->w,
570 gridnames[params->type], diffnames[params->diff]);
571
572 dup_params = snew(game_params);
573 *dup_params = *params;
574
575 preset_menu_add_preset(menu, dupstr(buf), dup_params);
576}
577
578static struct preset_menu *game_preset_menu(void)
579{
580 struct preset_menu *top, *more;
581 int i;
582
583 top = preset_menu_new();
584 for (i = 0; i < lenof(loopy_presets_top); i++)
585 preset_menu_add_preset_with_title(top, &loopy_presets_top[i]);
586
587 more = preset_menu_add_submenu(top, dupstr("More..."));
588 for (i = 0; i < lenof(loopy_presets_more); i++)
589 preset_menu_add_preset_with_title(more, &loopy_presets_more[i]);
590
591 return top;
592}
593
594static void free_params(game_params *params)
595{
596 sfree(params);
597}
598
599static void decode_params(game_params *params, char const *string)
600{
601 params->h = params->w = atoi(string);
602 params->diff = DIFF_EASY;
603 while (*string && isdigit((unsigned char)*string)) string++;
604 if (*string == 'x') {
605 string++;
606 params->h = atoi(string);
607 while (*string && isdigit((unsigned char)*string)) string++;
608 }
609 if (*string == 't') {
610 string++;
611 params->type = atoi(string);
612 while (*string && isdigit((unsigned char)*string)) string++;
613 }
614 if (*string == 'd') {
615 int i;
616 string++;
617 for (i = 0; i < DIFF_MAX; i++)
618 if (*string == diffchars[i])
619 params->diff = i;
620 if (*string) string++;
621 }
622}
623
624static char *encode_params(const game_params *params, int full)
625{
626 char str[80];
627 sprintf(str, "%dx%dt%d", params->w, params->h, params->type);
628 if (full)
629 sprintf(str + strlen(str), "d%c", diffchars[params->diff]);
630 return dupstr(str);
631}
632
633static config_item *game_configure(const game_params *params)
634{
635 config_item *ret;
636 char buf[80];
637
638 ret = snewn(5, config_item);
639
640 ret[0].name = "Width";
641 ret[0].type = C_STRING;
642 sprintf(buf, "%d", params->w);
643 ret[0].sval = dupstr(buf);
644 ret[0].ival = 0;
645
646 ret[1].name = "Height";
647 ret[1].type = C_STRING;
648 sprintf(buf, "%d", params->h);
649 ret[1].sval = dupstr(buf);
650 ret[1].ival = 0;
651
652 ret[2].name = "Grid type";
653 ret[2].type = C_CHOICES;
654 ret[2].sval = GRID_CONFIGS;
655 ret[2].ival = params->type;
656
657 ret[3].name = "Difficulty";
658 ret[3].type = C_CHOICES;
659 ret[3].sval = DIFFCONFIG;
660 ret[3].ival = params->diff;
661
662 ret[4].name = NULL;
663 ret[4].type = C_END;
664 ret[4].sval = NULL;
665 ret[4].ival = 0;
666
667 return ret;
668}
669
670static game_params *custom_params(const config_item *cfg)
671{
672 game_params *ret = snew(game_params);
673
674 ret->w = atoi(cfg[0].sval);
675 ret->h = atoi(cfg[1].sval);
676 ret->type = cfg[2].ival;
677 ret->diff = cfg[3].ival;
678
679 return ret;
680}
681
682static char *validate_params(const game_params *params, int full)
683{
684 if (params->type < 0 || params->type >= NUM_GRID_TYPES)
685 return "Illegal grid type";
686 if (params->w < grid_size_limits[params->type].amin ||
687 params->h < grid_size_limits[params->type].amin)
688 return grid_size_limits[params->type].aerr;
689 if (params->w < grid_size_limits[params->type].omin &&
690 params->h < grid_size_limits[params->type].omin)
691 return grid_size_limits[params->type].oerr;
692
693 /*
694 * This shouldn't be able to happen at all, since decode_params
695 * and custom_params will never generate anything that isn't
696 * within range.
697 */
698 assert(params->diff < DIFF_MAX);
699
700 return NULL;
701}
702
703/* Returns a newly allocated string describing the current puzzle */
704static char *state_to_text(const game_state *state)
705{
706 grid *g = state->game_grid;
707 char *retval;
708 int num_faces = g->num_faces;
709 char *description = snewn(num_faces + 1, char);
710 char *dp = description;
711 int empty_count = 0;
712 int i;
713
714 for (i = 0; i < num_faces; i++) {
715 if (state->clues[i] < 0) {
716 if (empty_count > 25) {
717 dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1));
718 empty_count = 0;
719 }
720 empty_count++;
721 } else {
722 if (empty_count) {
723 dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1));
724 empty_count = 0;
725 }
726 dp += sprintf(dp, "%c", (int)CLUE2CHAR(state->clues[i]));
727 }
728 }
729
730 if (empty_count)
731 dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1));
732
733 retval = dupstr(description);
734 sfree(description);
735
736 return retval;
737}
738
739#define GRID_DESC_SEP '_'
740
741/* Splits up a (optional) grid_desc from the game desc. Returns the
742 * grid_desc (which needs freeing) and updates the desc pointer to
743 * start of real desc, or returns NULL if no desc. */
744static char *extract_grid_desc(const char **desc)
745{
746 char *sep = strchr(*desc, GRID_DESC_SEP), *gd;
747 int gd_len;
748
749 if (!sep) return NULL;
750
751 gd_len = sep - (*desc);
752 gd = snewn(gd_len+1, char);
753 memcpy(gd, *desc, gd_len);
754 gd[gd_len] = '\0';
755
756 *desc = sep+1;
757
758 return gd;
759}
760
761/* We require that the params pass the test in validate_params and that the
762 * description fills the entire game area */
763static char *validate_desc(const game_params *params, const char *desc)
764{
765 int count = 0;
766 grid *g;
767 char *grid_desc, *ret;
768
769 /* It's pretty inefficient to do this just for validation. All we need to
770 * know is the precise number of faces. */
771 grid_desc = extract_grid_desc(&desc);
772 ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc);
773 if (ret) return ret;
774
775 g = loopy_generate_grid(params, grid_desc);
776 if (grid_desc) sfree(grid_desc);
777
778 for (; *desc; ++desc) {
779 if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) {
780 count++;
781 continue;
782 }
783 if (*desc >= 'a') {
784 count += *desc - 'a' + 1;
785 continue;
786 }
787 return "Unknown character in description";
788 }
789
790 if (count < g->num_faces)
791 return "Description too short for board size";
792 if (count > g->num_faces)
793 return "Description too long for board size";
794
795 grid_free(g);
796
797 return NULL;
798}
799
800/* Sums the lengths of the numbers in range [0,n) */
801/* See equivalent function in solo.c for justification of this. */
802static int len_0_to_n(int n)
803{
804 int len = 1; /* Counting 0 as a bit of a special case */
805 int i;
806
807 for (i = 1; i < n; i *= 10) {
808 len += max(n - i, 0);
809 }
810
811 return len;
812}
813
814static char *encode_solve_move(const game_state *state)
815{
816 int len;
817 char *ret, *p;
818 int i;
819 int num_edges = state->game_grid->num_edges;
820
821 /* This is going to return a string representing the moves needed to set
822 * every line in a grid to be the same as the ones in 'state'. The exact
823 * length of this string is predictable. */
824
825 len = 1; /* Count the 'S' prefix */
826 /* Numbers in all lines */
827 len += len_0_to_n(num_edges);
828 /* For each line we also have a letter */
829 len += num_edges;
830
831 ret = snewn(len + 1, char);
832 p = ret;
833
834 p += sprintf(p, "S");
835
836 for (i = 0; i < num_edges; i++) {
837 switch (state->lines[i]) {
838 case LINE_YES:
839 p += sprintf(p, "%dy", i);
840 break;
841 case LINE_NO:
842 p += sprintf(p, "%dn", i);
843 break;
844 }
845 }
846
847 /* No point in doing sums like that if they're going to be wrong */
848 assert(strlen(ret) <= (size_t)len);
849 return ret;
850}
851
852static game_ui *new_ui(const game_state *state)
853{
854 return NULL;
855}
856
857static void free_ui(game_ui *ui)
858{
859}
860
861static char *encode_ui(const game_ui *ui)
862{
863 return NULL;
864}
865
866static void decode_ui(game_ui *ui, const char *encoding)
867{
868}
869
870static void game_changed_state(game_ui *ui, const game_state *oldstate,
871 const game_state *newstate)
872{
873}
874
875static void game_compute_size(const game_params *params, int tilesize,
876 int *x, int *y)
877{
878 int grid_width, grid_height, rendered_width, rendered_height;
879 int g_tilesize;
880
881 grid_compute_size(grid_types[params->type], params->w, params->h,
882 &g_tilesize, &grid_width, &grid_height);
883
884 /* multiply first to minimise rounding error on integer division */
885 rendered_width = grid_width * tilesize / g_tilesize;
886 rendered_height = grid_height * tilesize / g_tilesize;
887 *x = rendered_width + 2 * BORDER(tilesize) + 1;
888 *y = rendered_height + 2 * BORDER(tilesize) + 1;
889}
890
891static void game_set_size(drawing *dr, game_drawstate *ds,
892 const game_params *params, int tilesize)
893{
894 ds->tilesize = tilesize;
895}
896
897static float *game_colours(frontend *fe, int *ncolours)
898{
899 float *ret = snewn(3 * NCOLOURS, float);
900
901 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
902
903 ret[COL_FOREGROUND * 3 + 0] = 0.0F;
904 ret[COL_FOREGROUND * 3 + 1] = 0.0F;
905 ret[COL_FOREGROUND * 3 + 2] = 0.0F;
906
907 /*
908 * We want COL_LINEUNKNOWN to be a yellow which is a bit darker
909 * than the background. (I previously set it to 0.8,0.8,0, but
910 * found that this went badly with the 0.8,0.8,0.8 favoured as a
911 * background by the Java frontend.)
912 */
913 ret[COL_LINEUNKNOWN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
914 ret[COL_LINEUNKNOWN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F;
915 ret[COL_LINEUNKNOWN * 3 + 2] = 0.0F;
916
917 ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
918 ret[COL_HIGHLIGHT * 3 + 1] = 1.0F;
919 ret[COL_HIGHLIGHT * 3 + 2] = 1.0F;
920
921 ret[COL_MISTAKE * 3 + 0] = 1.0F;
922 ret[COL_MISTAKE * 3 + 1] = 0.0F;
923 ret[COL_MISTAKE * 3 + 2] = 0.0F;
924
925 ret[COL_SATISFIED * 3 + 0] = 0.0F;
926 ret[COL_SATISFIED * 3 + 1] = 0.0F;
927 ret[COL_SATISFIED * 3 + 2] = 0.0F;
928
929 /* We want the faint lines to be a bit darker than the background.
930 * Except if the background is pretty dark already; then it ought to be a
931 * bit lighter. Oy vey.
932 */
933 ret[COL_FAINT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
934 ret[COL_FAINT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F;
935 ret[COL_FAINT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.9F;
936
937 *ncolours = NCOLOURS;
938 return ret;
939}
940
941static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
942{
943 struct game_drawstate *ds = snew(struct game_drawstate);
944 int num_faces = state->game_grid->num_faces;
945 int num_edges = state->game_grid->num_edges;
946 int i;
947
948 ds->tilesize = 0;
949 ds->started = 0;
950 ds->lines = snewn(num_edges, char);
951 ds->clue_error = snewn(num_faces, char);
952 ds->clue_satisfied = snewn(num_faces, char);
953 ds->textx = snewn(num_faces, int);
954 ds->texty = snewn(num_faces, int);
955 ds->flashing = 0;
956
957 memset(ds->lines, LINE_UNKNOWN, num_edges);
958 memset(ds->clue_error, 0, num_faces);
959 memset(ds->clue_satisfied, 0, num_faces);
960 for (i = 0; i < num_faces; i++)
961 ds->textx[i] = ds->texty[i] = -1;
962
963 return ds;
964}
965
966static void game_free_drawstate(drawing *dr, game_drawstate *ds)
967{
968 sfree(ds->textx);
969 sfree(ds->texty);
970 sfree(ds->clue_error);
971 sfree(ds->clue_satisfied);
972 sfree(ds->lines);
973 sfree(ds);
974}
975
976static int game_timing_state(const game_state *state, game_ui *ui)
977{
978 return TRUE;
979}
980
981static float game_anim_length(const game_state *oldstate,
982 const game_state *newstate, int dir, game_ui *ui)
983{
984 return 0.0F;
985}
986
987static int game_can_format_as_text_now(const game_params *params)
988{
989 if (params->type != 0)
990 return FALSE;
991 return TRUE;
992}
993
994static char *game_text_format(const game_state *state)
995{
996 int w, h, W, H;
997 int x, y, i;
998 int cell_size;
999 char *ret;
1000 grid *g = state->game_grid;
1001 grid_face *f;
1002
1003 assert(state->grid_type == 0);
1004
1005 /* Work out the basic size unit */
1006 f = g->faces; /* first face */
1007 assert(f->order == 4);
1008 /* The dots are ordered clockwise, so the two opposite
1009 * corners are guaranteed to span the square */
1010 cell_size = abs(f->dots[0]->x - f->dots[2]->x);
1011
1012 w = (g->highest_x - g->lowest_x) / cell_size;
1013 h = (g->highest_y - g->lowest_y) / cell_size;
1014
1015 /* Create a blank "canvas" to "draw" on */
1016 W = 2 * w + 2;
1017 H = 2 * h + 1;
1018 ret = snewn(W * H + 1, char);
1019 for (y = 0; y < H; y++) {
1020 for (x = 0; x < W-1; x++) {
1021 ret[y*W + x] = ' ';
1022 }
1023 ret[y*W + W-1] = '\n';
1024 }
1025 ret[H*W] = '\0';
1026
1027 /* Fill in edge info */
1028 for (i = 0; i < g->num_edges; i++) {
1029 grid_edge *e = g->edges + i;
1030 /* Cell coordinates, from (0,0) to (w-1,h-1) */
1031 int x1 = (e->dot1->x - g->lowest_x) / cell_size;
1032 int x2 = (e->dot2->x - g->lowest_x) / cell_size;
1033 int y1 = (e->dot1->y - g->lowest_y) / cell_size;
1034 int y2 = (e->dot2->y - g->lowest_y) / cell_size;
1035 /* Midpoint, in canvas coordinates (canvas coordinates are just twice
1036 * cell coordinates) */
1037 x = x1 + x2;
1038 y = y1 + y2;
1039 switch (state->lines[i]) {
1040 case LINE_YES:
1041 ret[y*W + x] = (y1 == y2) ? '-' : '|';
1042 break;
1043 case LINE_NO:
1044 ret[y*W + x] = 'x';
1045 break;
1046 case LINE_UNKNOWN:
1047 break; /* already a space */
1048 default:
1049 assert(!"Illegal line state");
1050 }
1051 }
1052
1053 /* Fill in clues */
1054 for (i = 0; i < g->num_faces; i++) {
1055 int x1, x2, y1, y2;
1056
1057 f = g->faces + i;
1058 assert(f->order == 4);
1059 /* Cell coordinates, from (0,0) to (w-1,h-1) */
1060 x1 = (f->dots[0]->x - g->lowest_x) / cell_size;
1061 x2 = (f->dots[2]->x - g->lowest_x) / cell_size;
1062 y1 = (f->dots[0]->y - g->lowest_y) / cell_size;
1063 y2 = (f->dots[2]->y - g->lowest_y) / cell_size;
1064 /* Midpoint, in canvas coordinates */
1065 x = x1 + x2;
1066 y = y1 + y2;
1067 ret[y*W + x] = CLUE2CHAR(state->clues[i]);
1068 }
1069 return ret;
1070}
1071
1072/* ----------------------------------------------------------------------
1073 * Debug code
1074 */
1075
1076#ifdef DEBUG_CACHES
1077static void check_caches(const solver_state* sstate)
1078{
1079 int i;
1080 const game_state *state = sstate->state;
1081 const grid *g = state->game_grid;
1082
1083 for (i = 0; i < g->num_dots; i++) {
1084 assert(dot_order(state, i, LINE_YES) == sstate->dot_yes_count[i]);
1085 assert(dot_order(state, i, LINE_NO) == sstate->dot_no_count[i]);
1086 }
1087
1088 for (i = 0; i < g->num_faces; i++) {
1089 assert(face_order(state, i, LINE_YES) == sstate->face_yes_count[i]);
1090 assert(face_order(state, i, LINE_NO) == sstate->face_no_count[i]);
1091 }
1092}
1093
1094#if 0
1095#define check_caches(s) \
1096 do { \
1097 fprintf(stderr, "check_caches at line %d\n", __LINE__); \
1098 check_caches(s); \
1099 } while (0)
1100#endif
1101#endif /* DEBUG_CACHES */
1102
1103/* ----------------------------------------------------------------------
1104 * Solver utility functions
1105 */
1106
1107/* Sets the line (with index i) to the new state 'line_new', and updates
1108 * the cached counts of any affected faces and dots.
1109 * Returns TRUE if this actually changed the line's state. */
1110static int solver_set_line(solver_state *sstate, int i,
1111 enum line_state line_new
1112#ifdef SHOW_WORKING
1113 , const char *reason
1114#endif
1115 )
1116{
1117 game_state *state = sstate->state;
1118 grid *g;
1119 grid_edge *e;
1120
1121 assert(line_new != LINE_UNKNOWN);
1122
1123 check_caches(sstate);
1124
1125 if (state->lines[i] == line_new) {
1126 return FALSE; /* nothing changed */
1127 }
1128 state->lines[i] = line_new;
1129
1130#ifdef SHOW_WORKING
1131 fprintf(stderr, "solver: set line [%d] to %s (%s)\n",
1132 i, line_new == LINE_YES ? "YES" : "NO",
1133 reason);
1134#endif
1135
1136 g = state->game_grid;
1137 e = g->edges + i;
1138
1139 /* Update the cache for both dots and both faces affected by this. */
1140 if (line_new == LINE_YES) {
1141 sstate->dot_yes_count[e->dot1 - g->dots]++;
1142 sstate->dot_yes_count[e->dot2 - g->dots]++;
1143 if (e->face1) {
1144 sstate->face_yes_count[e->face1 - g->faces]++;
1145 }
1146 if (e->face2) {
1147 sstate->face_yes_count[e->face2 - g->faces]++;
1148 }
1149 } else {
1150 sstate->dot_no_count[e->dot1 - g->dots]++;
1151 sstate->dot_no_count[e->dot2 - g->dots]++;
1152 if (e->face1) {
1153 sstate->face_no_count[e->face1 - g->faces]++;
1154 }
1155 if (e->face2) {
1156 sstate->face_no_count[e->face2 - g->faces]++;
1157 }
1158 }
1159
1160 check_caches(sstate);
1161 return TRUE;
1162}
1163
1164#ifdef SHOW_WORKING
1165#define solver_set_line(a, b, c) \
1166 solver_set_line(a, b, c, __FUNCTION__)
1167#endif
1168
1169/*
1170 * Merge two dots due to the existence of an edge between them.
1171 * Updates the dsf tracking equivalence classes, and keeps track of
1172 * the length of path each dot is currently a part of.
1173 * Returns TRUE if the dots were already linked, ie if they are part of a
1174 * closed loop, and false otherwise.
1175 */
1176static int merge_dots(solver_state *sstate, int edge_index)
1177{
1178 int i, j, len;
1179 grid *g = sstate->state->game_grid;
1180 grid_edge *e = g->edges + edge_index;
1181
1182 i = e->dot1 - g->dots;
1183 j = e->dot2 - g->dots;
1184
1185 i = dsf_canonify(sstate->dotdsf, i);
1186 j = dsf_canonify(sstate->dotdsf, j);
1187
1188 if (i == j) {
1189 return TRUE;
1190 } else {
1191 len = sstate->looplen[i] + sstate->looplen[j];
1192 dsf_merge(sstate->dotdsf, i, j);
1193 i = dsf_canonify(sstate->dotdsf, i);
1194 sstate->looplen[i] = len;
1195 return FALSE;
1196 }
1197}
1198
1199/* Merge two lines because the solver has deduced that they must be either
1200 * identical or opposite. Returns TRUE if this is new information, otherwise
1201 * FALSE. */
1202static int merge_lines(solver_state *sstate, int i, int j, int inverse
1203#ifdef SHOW_WORKING
1204 , const char *reason
1205#endif
1206 )
1207{
1208 int inv_tmp;
1209
1210 assert(i < sstate->state->game_grid->num_edges);
1211 assert(j < sstate->state->game_grid->num_edges);
1212
1213 i = edsf_canonify(sstate->linedsf, i, &inv_tmp);
1214 inverse ^= inv_tmp;
1215 j = edsf_canonify(sstate->linedsf, j, &inv_tmp);
1216 inverse ^= inv_tmp;
1217
1218 edsf_merge(sstate->linedsf, i, j, inverse);
1219
1220#ifdef SHOW_WORKING
1221 if (i != j) {
1222 fprintf(stderr, "%s [%d] [%d] %s(%s)\n",
1223 __FUNCTION__, i, j,
1224 inverse ? "inverse " : "", reason);
1225 }
1226#endif
1227 return (i != j);
1228}
1229
1230#ifdef SHOW_WORKING
1231#define merge_lines(a, b, c, d) \
1232 merge_lines(a, b, c, d, __FUNCTION__)
1233#endif
1234
1235/* Count the number of lines of a particular type currently going into the
1236 * given dot. */
1237static int dot_order(const game_state* state, int dot, char line_type)
1238{
1239 int n = 0;
1240 grid *g = state->game_grid;
1241 grid_dot *d = g->dots + dot;
1242 int i;
1243
1244 for (i = 0; i < d->order; i++) {
1245 grid_edge *e = d->edges[i];
1246 if (state->lines[e - g->edges] == line_type)
1247 ++n;
1248 }
1249 return n;
1250}
1251
1252/* Count the number of lines of a particular type currently surrounding the
1253 * given face */
1254static int face_order(const game_state* state, int face, char line_type)
1255{
1256 int n = 0;
1257 grid *g = state->game_grid;
1258 grid_face *f = g->faces + face;
1259 int i;
1260
1261 for (i = 0; i < f->order; i++) {
1262 grid_edge *e = f->edges[i];
1263 if (state->lines[e - g->edges] == line_type)
1264 ++n;
1265 }
1266 return n;
1267}
1268
1269/* Set all lines bordering a dot of type old_type to type new_type
1270 * Return value tells caller whether this function actually did anything */
1271static int dot_setall(solver_state *sstate, int dot,
1272 char old_type, char new_type)
1273{
1274 int retval = FALSE, r;
1275 game_state *state = sstate->state;
1276 grid *g;
1277 grid_dot *d;
1278 int i;
1279
1280 if (old_type == new_type)
1281 return FALSE;
1282
1283 g = state->game_grid;
1284 d = g->dots + dot;
1285
1286 for (i = 0; i < d->order; i++) {
1287 int line_index = d->edges[i] - g->edges;
1288 if (state->lines[line_index] == old_type) {
1289 r = solver_set_line(sstate, line_index, new_type);
1290 assert(r == TRUE);
1291 retval = TRUE;
1292 }
1293 }
1294 return retval;
1295}
1296
1297/* Set all lines bordering a face of type old_type to type new_type */
1298static int face_setall(solver_state *sstate, int face,
1299 char old_type, char new_type)
1300{
1301 int retval = FALSE, r;
1302 game_state *state = sstate->state;
1303 grid *g;
1304 grid_face *f;
1305 int i;
1306
1307 if (old_type == new_type)
1308 return FALSE;
1309
1310 g = state->game_grid;
1311 f = g->faces + face;
1312
1313 for (i = 0; i < f->order; i++) {
1314 int line_index = f->edges[i] - g->edges;
1315 if (state->lines[line_index] == old_type) {
1316 r = solver_set_line(sstate, line_index, new_type);
1317 assert(r == TRUE);
1318 retval = TRUE;
1319 }
1320 }
1321 return retval;
1322}
1323
1324/* ----------------------------------------------------------------------
1325 * Loop generation and clue removal
1326 */
1327
1328static void add_full_clues(game_state *state, random_state *rs)
1329{
1330 signed char *clues = state->clues;
1331 grid *g = state->game_grid;
1332 char *board = snewn(g->num_faces, char);
1333 int i;
1334
1335 generate_loop(g, board, rs, NULL, NULL);
1336
1337 /* Fill out all the clues by initialising to 0, then iterating over
1338 * all edges and incrementing each clue as we find edges that border
1339 * between BLACK/WHITE faces. While we're at it, we verify that the
1340 * algorithm does work, and there aren't any GREY faces still there. */
1341 memset(clues, 0, g->num_faces);
1342 for (i = 0; i < g->num_edges; i++) {
1343 grid_edge *e = g->edges + i;
1344 grid_face *f1 = e->face1;
1345 grid_face *f2 = e->face2;
1346 enum face_colour c1 = FACE_COLOUR(f1);
1347 enum face_colour c2 = FACE_COLOUR(f2);
1348 assert(c1 != FACE_GREY);
1349 assert(c2 != FACE_GREY);
1350 if (c1 != c2) {
1351 if (f1) clues[f1 - g->faces]++;
1352 if (f2) clues[f2 - g->faces]++;
1353 }
1354 }
1355 sfree(board);
1356}
1357
1358
1359static int game_has_unique_soln(const game_state *state, int diff)
1360{
1361 int ret;
1362 solver_state *sstate_new;
1363 solver_state *sstate = new_solver_state((game_state *)state, diff);
1364
1365 sstate_new = solve_game_rec(sstate);
1366
1367 assert(sstate_new->solver_status != SOLVER_MISTAKE);
1368 ret = (sstate_new->solver_status == SOLVER_SOLVED);
1369
1370 free_solver_state(sstate_new);
1371 free_solver_state(sstate);
1372
1373 return ret;
1374}
1375
1376
1377/* Remove clues one at a time at random. */
1378static game_state *remove_clues(game_state *state, random_state *rs,
1379 int diff)
1380{
1381 int *face_list;
1382 int num_faces = state->game_grid->num_faces;
1383 game_state *ret = dup_game(state), *saved_ret;
1384 int n;
1385
1386 /* We need to remove some clues. We'll do this by forming a list of all
1387 * available clues, shuffling it, then going along one at a
1388 * time clearing each clue in turn for which doing so doesn't render the
1389 * board unsolvable. */
1390 face_list = snewn(num_faces, int);
1391 for (n = 0; n < num_faces; ++n) {
1392 face_list[n] = n;
1393 }
1394
1395 shuffle(face_list, num_faces, sizeof(int), rs);
1396
1397 for (n = 0; n < num_faces; ++n) {
1398 saved_ret = dup_game(ret);
1399 ret->clues[face_list[n]] = -1;
1400
1401 if (game_has_unique_soln(ret, diff)) {
1402 free_game(saved_ret);
1403 } else {
1404 free_game(ret);
1405 ret = saved_ret;
1406 }
1407 }
1408 sfree(face_list);
1409
1410 return ret;
1411}
1412
1413
1414static char *new_game_desc(const game_params *params, random_state *rs,
1415 char **aux, int interactive)
1416{
1417 /* solution and description both use run-length encoding in obvious ways */
1418 char *retval, *game_desc, *grid_desc;
1419 grid *g;
1420 game_state *state = snew(game_state);
1421 game_state *state_new;
1422
1423 grid_desc = grid_new_desc(grid_types[params->type], params->w, params->h, rs);
1424 state->game_grid = g = loopy_generate_grid(params, grid_desc);
1425
1426 state->clues = snewn(g->num_faces, signed char);
1427 state->lines = snewn(g->num_edges, char);
1428 state->line_errors = snewn(g->num_edges, unsigned char);
1429 state->exactly_one_loop = FALSE;
1430
1431 state->grid_type = params->type;
1432
1433 newboard_please:
1434
1435 memset(state->lines, LINE_UNKNOWN, g->num_edges);
1436 memset(state->line_errors, 0, g->num_edges);
1437
1438 state->solved = state->cheated = FALSE;
1439
1440 /* Get a new random solvable board with all its clues filled in. Yes, this
1441 * can loop for ever if the params are suitably unfavourable, but
1442 * preventing games smaller than 4x4 seems to stop this happening */
1443 do {
1444 add_full_clues(state, rs);
1445 } while (!game_has_unique_soln(state, params->diff));
1446
1447 state_new = remove_clues(state, rs, params->diff);
1448 free_game(state);
1449 state = state_new;
1450
1451
1452 if (params->diff > 0 && game_has_unique_soln(state, params->diff-1)) {
1453#ifdef SHOW_WORKING
1454 fprintf(stderr, "Rejecting board, it is too easy\n");
1455#endif
1456 goto newboard_please;
1457 }
1458
1459 game_desc = state_to_text(state);
1460
1461 free_game(state);
1462
1463 if (grid_desc) {
1464 retval = snewn(strlen(grid_desc) + 1 + strlen(game_desc) + 1, char);
1465 sprintf(retval, "%s%c%s", grid_desc, (int)GRID_DESC_SEP, game_desc);
1466 sfree(grid_desc);
1467 sfree(game_desc);
1468 } else {
1469 retval = game_desc;
1470 }
1471
1472 assert(!validate_desc(params, retval));
1473
1474 return retval;
1475}
1476
1477static game_state *new_game(midend *me, const game_params *params,
1478 const char *desc)
1479{
1480 int i;
1481 game_state *state = snew(game_state);
1482 int empties_to_make = 0;
1483 int n,n2;
1484 const char *dp;
1485 char *grid_desc;
1486 grid *g;
1487 int num_faces, num_edges;
1488
1489 grid_desc = extract_grid_desc(&desc);
1490 state->game_grid = g = loopy_generate_grid(params, grid_desc);
1491 if (grid_desc) sfree(grid_desc);
1492
1493 dp = desc;
1494
1495 num_faces = g->num_faces;
1496 num_edges = g->num_edges;
1497
1498 state->clues = snewn(num_faces, signed char);
1499 state->lines = snewn(num_edges, char);
1500 state->line_errors = snewn(num_edges, unsigned char);
1501 state->exactly_one_loop = FALSE;
1502
1503 state->solved = state->cheated = FALSE;
1504
1505 state->grid_type = params->type;
1506
1507 for (i = 0; i < num_faces; i++) {
1508 if (empties_to_make) {
1509 empties_to_make--;
1510 state->clues[i] = -1;
1511 continue;
1512 }
1513
1514 assert(*dp);
1515 n = *dp - '0';
1516 n2 = *dp - 'A' + 10;
1517 if (n >= 0 && n < 10) {
1518 state->clues[i] = n;
1519 } else if (n2 >= 10 && n2 < 36) {
1520 state->clues[i] = n2;
1521 } else {
1522 n = *dp - 'a' + 1;
1523 assert(n > 0);
1524 state->clues[i] = -1;
1525 empties_to_make = n - 1;
1526 }
1527 ++dp;
1528 }
1529
1530 memset(state->lines, LINE_UNKNOWN, num_edges);
1531 memset(state->line_errors, 0, num_edges);
1532 return state;
1533}
1534
1535/* Calculates the line_errors data, and checks if the current state is a
1536 * solution */
1537static int check_completion(game_state *state)
1538{
1539 grid *g = state->game_grid;
1540 int i, ret;
1541 int *dsf, *component_state;
1542 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
1543 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
1544
1545 memset(state->line_errors, 0, g->num_edges);
1546
1547 /*
1548 * Find loops in the grid, and determine whether the puzzle is
1549 * solved.
1550 *
1551 * Loopy is a bit more complicated than most puzzles that care
1552 * about loop detection. In most of them, loops are simply
1553 * _forbidden_; so the obviously right way to do
1554 * error-highlighting during play is to light up a graph edge red
1555 * iff it is part of a loop, which is exactly what the centralised
1556 * findloop.c makes easy.
1557 *
1558 * But Loopy is unusual in that you're _supposed_ to be making a
1559 * loop - and yet _some_ loops are not the right loop. So we need
1560 * to be more discriminating, by identifying loops one by one and
1561 * then thinking about which ones to highlight, and so findloop.c
1562 * isn't quite the right tool for the job in this case.
1563 *
1564 * Worse still, consider situations in which the grid contains a
1565 * loop and also some non-loop edges: there are some cases like
1566 * this in which the user's intuitive expectation would be to
1567 * highlight the loop (if you're only about half way through the
1568 * puzzle and have accidentally made a little loop in some corner
1569 * of the grid), and others in which they'd be more likely to
1570 * expect you to highlight the non-loop edges (if you've just
1571 * closed off a whole loop that you thought was the entire
1572 * solution, but forgot some disconnected edges in a corner
1573 * somewhere). So while it's easy enough to check whether the
1574 * solution is _right_, highlighting the wrong parts is a tricky
1575 * problem for this puzzle!
1576 *
1577 * I'd quite like, in some situations, to identify the largest
1578 * loop among the player's YES edges, and then light up everything
1579 * other than that. But finding the longest cycle in a graph is an
1580 * NP-complete problem (because, in particular, it must return a
1581 * Hamilton cycle if one exists).
1582 *
1583 * However, I think we can make the problem tractable by
1584 * exercising the Puzzles principle that it isn't absolutely
1585 * necessary to highlight _all_ errors: the key point is that by
1586 * the time the user has filled in the whole grid, they should
1587 * either have seen a completion flash, or have _some_ error
1588 * highlight showing them why the solution isn't right. So in
1589 * principle it would be *just about* good enough to highlight
1590 * just one error in the whole grid, if there was really no better
1591 * way. But we'd like to highlight as many errors as possible.
1592 *
1593 * In this case, I think the simple approach is to make use of the
1594 * fact that no vertex may have degree > 2, and that's really
1595 * simple to detect. So the plan goes like this:
1596 *
1597 * - Form the dsf of connected components of the graph vertices.
1598 *
1599 * - Highlight an error at any vertex with degree > 2. (It so
1600 * happens that we do this by lighting up all the edges
1601 * incident to that vertex, but that's an output detail.)
1602 *
1603 * - Any component that contains such a vertex is now excluded
1604 * from further consideration, because it already has a
1605 * highlight.
1606 *
1607 * - The remaining components have no vertex with degree > 2, and
1608 * hence they all consist of either a simple loop, or a simple
1609 * path with two endpoints.
1610 *
1611 * - For these purposes, group together all the paths and imagine
1612 * them to be a single component (because in most normal
1613 * situations the player will gradually build up the solution
1614 * _not_ all in one connected segment, but as lots of separate
1615 * little path pieces that gradually connect to each other).
1616 *
1617 * - After doing that, if there is exactly one (sensible)
1618 * component - be it a collection of paths or a loop - then
1619 * highlight no further edge errors. (The former case is normal
1620 * during play, and the latter is a potentially solved puzzle.)
1621 *
1622 * - Otherwise, find the largest of the sensible components,
1623 * leave that one unhighlighted, and light the rest up in red.
1624 */
1625
1626 dsf = snew_dsf(g->num_dots);
1627
1628 /* Build the dsf. */
1629 for (i = 0; i < g->num_edges; i++) {
1630 if (state->lines[i] == LINE_YES) {
1631 grid_edge *e = g->edges + i;
1632 int d1 = e->dot1 - g->dots, d2 = e->dot2 - g->dots;
1633 dsf_merge(dsf, d1, d2);
1634 }
1635 }
1636
1637 /* Initialise a state variable for each connected component. */
1638 component_state = snewn(g->num_dots, int);
1639 for (i = 0; i < g->num_dots; i++) {
1640 if (dsf_canonify(dsf, i) == i)
1641 component_state[i] = COMP_LOOP;
1642 else
1643 component_state[i] = COMP_NONE;
1644 }
1645
1646 /* Check for dots with degree > 3. Here we also spot dots of
1647 * degree 1 in which the user has marked all the non-edges as
1648 * LINE_NO, because those are also clear vertex-level errors, so
1649 * we give them the same treatment of excluding their connected
1650 * component from the subsequent loop analysis. */
1651 for (i = 0; i < g->num_dots; i++) {
1652 int comp = dsf_canonify(dsf, i);
1653 int yes = dot_order(state, i, LINE_YES);
1654 int unknown = dot_order(state, i, LINE_UNKNOWN);
1655 if ((yes == 1 && unknown == 0) || (yes >= 3)) {
1656 /* violation, so mark all YES edges as errors */
1657 grid_dot *d = g->dots + i;
1658 int j;
1659 for (j = 0; j < d->order; j++) {
1660 int e = d->edges[j] - g->edges;
1661 if (state->lines[e] == LINE_YES)
1662 state->line_errors[e] = TRUE;
1663 }
1664 /* And mark this component as not worthy of further
1665 * consideration. */
1666 component_state[comp] = COMP_SILLY;
1667
1668 } else if (yes == 0) {
1669 /* A completely isolated dot must also be excluded it from
1670 * the subsequent loop highlighting pass, but we tag it
1671 * with a different enum value to avoid it counting
1672 * towards the components that inhibit returning a win
1673 * status. */
1674 component_state[comp] = COMP_EMPTY;
1675 } else if (yes == 1) {
1676 /* A dot with degree 1 that didn't fall into the 'clearly
1677 * erroneous' case above indicates that this connected
1678 * component will be a path rather than a loop - unless
1679 * something worse elsewhere in the component has
1680 * classified it as silly. */
1681 if (component_state[comp] != COMP_SILLY)
1682 component_state[comp] = COMP_PATH;
1683 }
1684 }
1685
1686 /* Count up the components. Also, find the largest sensible
1687 * component. (Tie-breaking condition is derived from the order of
1688 * vertices in the grid data structure, which is fairly arbitrary
1689 * but at least stays stable throughout the game.) */
1690 nsilly = nloop = npath = 0;
1691 total_pathsize = 0;
1692 largest_comp = largest_size = -1;
1693 for (i = 0; i < g->num_dots; i++) {
1694 if (component_state[i] == COMP_SILLY) {
1695 nsilly++;
1696 } else if (component_state[i] == COMP_PATH) {
1697 total_pathsize += dsf_size(dsf, i);
1698 npath = 1;
1699 } else if (component_state[i] == COMP_LOOP) {
1700 int this_size;
1701
1702 nloop++;
1703
1704 if ((this_size = dsf_size(dsf, i)) > largest_size) {
1705 largest_comp = i;
1706 largest_size = this_size;
1707 }
1708 }
1709 }
1710 if (largest_size < total_pathsize) {
1711 largest_comp = -1; /* means the paths */
1712 largest_size = total_pathsize;
1713 }
1714
1715 if (nloop > 0 && nloop + npath > 1) {
1716 /*
1717 * If there are at least two sensible components including at
1718 * least one loop, highlight all edges in every sensible
1719 * component that is not the largest one.
1720 */
1721 for (i = 0; i < g->num_edges; i++) {
1722 if (state->lines[i] == LINE_YES) {
1723 grid_edge *e = g->edges + i;
1724 int d1 = e->dot1 - g->dots; /* either endpoint is good enough */
1725 int comp = dsf_canonify(dsf, d1);
1726 if ((component_state[comp] == COMP_PATH &&
1727 -1 != largest_comp) ||
1728 (component_state[comp] == COMP_LOOP &&
1729 comp != largest_comp))
1730 state->line_errors[i] = TRUE;
1731 }
1732 }
1733 }
1734
1735 if (nloop == 1 && npath == 0 && nsilly == 0) {
1736 /*
1737 * If there is exactly one component and it is a loop, then
1738 * the puzzle is potentially complete, so check the clues.
1739 */
1740 ret = TRUE;
1741
1742 for (i = 0; i < g->num_faces; i++) {
1743 int c = state->clues[i];
1744 if (c >= 0 && face_order(state, i, LINE_YES) != c) {
1745 ret = FALSE;
1746 break;
1747 }
1748 }
1749
1750 /*
1751 * Also, whether or not the puzzle is actually complete, set
1752 * the flag that says this game_state has exactly one loop and
1753 * nothing else, which will be used to vary the semantics of
1754 * clue highlighting at display time.
1755 */
1756 state->exactly_one_loop = TRUE;
1757 } else {
1758 ret = FALSE;
1759 state->exactly_one_loop = FALSE;
1760 }
1761
1762 sfree(component_state);
1763 sfree(dsf);
1764
1765 return ret;
1766}
1767
1768/* ----------------------------------------------------------------------
1769 * Solver logic
1770 *
1771 * Our solver modes operate as follows. Each mode also uses the modes above it.
1772 *
1773 * Easy Mode
1774 * Just implement the rules of the game.
1775 *
1776 * Normal and Tricky Modes
1777 * For each (adjacent) pair of lines through each dot we store a bit for
1778 * whether at least one of them is on and whether at most one is on. (If we
1779 * know both or neither is on that's already stored more directly.)
1780 *
1781 * Advanced Mode
1782 * Use edsf data structure to make equivalence classes of lines that are
1783 * known identical to or opposite to one another.
1784 */
1785
1786
1787/* DLines:
1788 * For general grids, we consider "dlines" to be pairs of lines joined
1789 * at a dot. The lines must be adjacent around the dot, so we can think of
1790 * a dline as being a dot+face combination. Or, a dot+edge combination where
1791 * the second edge is taken to be the next clockwise edge from the dot.
1792 * Original loopy code didn't have this extra restriction of the lines being
1793 * adjacent. From my tests with square grids, this extra restriction seems to
1794 * take little, if anything, away from the quality of the puzzles.
1795 * A dline can be uniquely identified by an edge/dot combination, given that
1796 * a dline-pair always goes clockwise around its common dot. The edge/dot
1797 * combination can be represented by an edge/bool combination - if bool is
1798 * TRUE, use edge->dot1 else use edge->dot2. So the total number of dlines is
1799 * exactly twice the number of edges in the grid - although the dlines
1800 * spanning the infinite face are not all that useful to the solver.
1801 * Note that, by convention, a dline goes clockwise around its common dot,
1802 * which means the dline goes anti-clockwise around its common face.
1803 */
1804
1805/* Helper functions for obtaining an index into an array of dlines, given
1806 * various information. We assume the grid layout conventions about how
1807 * the various lists are interleaved - see grid_make_consistent() for
1808 * details. */
1809
1810/* i points to the first edge of the dline pair, reading clockwise around
1811 * the dot. */
1812static int dline_index_from_dot(grid *g, grid_dot *d, int i)
1813{
1814 grid_edge *e = d->edges[i];
1815 int ret;
1816#ifdef DEBUG_DLINES
1817 grid_edge *e2;
1818 int i2 = i+1;
1819 if (i2 == d->order) i2 = 0;
1820 e2 = d->edges[i2];
1821#endif
1822 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0);
1823#ifdef DEBUG_DLINES
1824 printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n",
1825 (int)(d - g->dots), i, (int)(e - g->edges),
1826 (int)(e2 - g->edges), ret);
1827#endif
1828 return ret;
1829}
1830/* i points to the second edge of the dline pair, reading clockwise around
1831 * the face. That is, the edges of the dline, starting at edge{i}, read
1832 * anti-clockwise around the face. By layout conventions, the common dot
1833 * of the dline will be f->dots[i] */
1834static int dline_index_from_face(grid *g, grid_face *f, int i)
1835{
1836 grid_edge *e = f->edges[i];
1837 grid_dot *d = f->dots[i];
1838 int ret;
1839#ifdef DEBUG_DLINES
1840 grid_edge *e2;
1841 int i2 = i - 1;
1842 if (i2 < 0) i2 += f->order;
1843 e2 = f->edges[i2];
1844#endif
1845 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0);
1846#ifdef DEBUG_DLINES
1847 printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n",
1848 (int)(f - g->faces), i, (int)(e - g->edges),
1849 (int)(e2 - g->edges), ret);
1850#endif
1851 return ret;
1852}
1853static int is_atleastone(const char *dline_array, int index)
1854{
1855 return BIT_SET(dline_array[index], 0);
1856}
1857static int set_atleastone(char *dline_array, int index)
1858{
1859 return SET_BIT(dline_array[index], 0);
1860}
1861static int is_atmostone(const char *dline_array, int index)
1862{
1863 return BIT_SET(dline_array[index], 1);
1864}
1865static int set_atmostone(char *dline_array, int index)
1866{
1867 return SET_BIT(dline_array[index], 1);
1868}
1869
1870static void array_setall(char *array, char from, char to, int len)
1871{
1872 char *p = array, *p_old = p;
1873 int len_remaining = len;
1874
1875 while ((p = memchr(p, from, len_remaining))) {
1876 *p = to;
1877 len_remaining -= p - p_old;
1878 p_old = p;
1879 }
1880}
1881
1882/* Helper, called when doing dline dot deductions, in the case where we
1883 * have 4 UNKNOWNs, and two of them (adjacent) have *exactly* one YES between
1884 * them (because of dline atmostone/atleastone).
1885 * On entry, edge points to the first of these two UNKNOWNs. This function
1886 * will find the opposite UNKNOWNS (if they are adjacent to one another)
1887 * and set their corresponding dline to atleastone. (Setting atmostone
1888 * already happens in earlier dline deductions) */
1889static int dline_set_opp_atleastone(solver_state *sstate,
1890 grid_dot *d, int edge)
1891{
1892 game_state *state = sstate->state;
1893 grid *g = state->game_grid;
1894 int N = d->order;
1895 int opp, opp2;
1896 for (opp = 0; opp < N; opp++) {
1897 int opp_dline_index;
1898 if (opp == edge || opp == edge+1 || opp == edge-1)
1899 continue;
1900 if (opp == 0 && edge == N-1)
1901 continue;
1902 if (opp == N-1 && edge == 0)
1903 continue;
1904 opp2 = opp + 1;
1905 if (opp2 == N) opp2 = 0;
1906 /* Check if opp, opp2 point to LINE_UNKNOWNs */
1907 if (state->lines[d->edges[opp] - g->edges] != LINE_UNKNOWN)
1908 continue;
1909 if (state->lines[d->edges[opp2] - g->edges] != LINE_UNKNOWN)
1910 continue;
1911 /* Found opposite UNKNOWNS and they're next to each other */
1912 opp_dline_index = dline_index_from_dot(g, d, opp);
1913 return set_atleastone(sstate->dlines, opp_dline_index);
1914 }
1915 return FALSE;
1916}
1917
1918
1919/* Set pairs of lines around this face which are known to be identical, to
1920 * the given line_state */
1921static int face_setall_identical(solver_state *sstate, int face_index,
1922 enum line_state line_new)
1923{
1924 /* can[dir] contains the canonical line associated with the line in
1925 * direction dir from the square in question. Similarly inv[dir] is
1926 * whether or not the line in question is inverse to its canonical
1927 * element. */
1928 int retval = FALSE;
1929 game_state *state = sstate->state;
1930 grid *g = state->game_grid;
1931 grid_face *f = g->faces + face_index;
1932 int N = f->order;
1933 int i, j;
1934 int can1, can2, inv1, inv2;
1935
1936 for (i = 0; i < N; i++) {
1937 int line1_index = f->edges[i] - g->edges;
1938 if (state->lines[line1_index] != LINE_UNKNOWN)
1939 continue;
1940 for (j = i + 1; j < N; j++) {
1941 int line2_index = f->edges[j] - g->edges;
1942 if (state->lines[line2_index] != LINE_UNKNOWN)
1943 continue;
1944
1945 /* Found two UNKNOWNS */
1946 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1);
1947 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2);
1948 if (can1 == can2 && inv1 == inv2) {
1949 solver_set_line(sstate, line1_index, line_new);
1950 solver_set_line(sstate, line2_index, line_new);
1951 }
1952 }
1953 }
1954 return retval;
1955}
1956
1957/* Given a dot or face, and a count of LINE_UNKNOWNs, find them and
1958 * return the edge indices into e. */
1959static void find_unknowns(game_state *state,
1960 grid_edge **edge_list, /* Edge list to search (from a face or a dot) */
1961 int expected_count, /* Number of UNKNOWNs (comes from solver's cache) */
1962 int *e /* Returned edge indices */)
1963{
1964 int c = 0;
1965 grid *g = state->game_grid;
1966 while (c < expected_count) {
1967 int line_index = *edge_list - g->edges;
1968 if (state->lines[line_index] == LINE_UNKNOWN) {
1969 e[c] = line_index;
1970 c++;
1971 }
1972 ++edge_list;
1973 }
1974}
1975
1976/* If we have a list of edges, and we know whether the number of YESs should
1977 * be odd or even, and there are only a few UNKNOWNs, we can do some simple
1978 * linedsf deductions. This can be used for both face and dot deductions.
1979 * Returns the difficulty level of the next solver that should be used,
1980 * or DIFF_MAX if no progress was made. */
1981static int parity_deductions(solver_state *sstate,
1982 grid_edge **edge_list, /* Edge list (from a face or a dot) */
1983 int total_parity, /* Expected number of YESs modulo 2 (either 0 or 1) */
1984 int unknown_count)
1985{
1986 game_state *state = sstate->state;
1987 int diff = DIFF_MAX;
1988 int *linedsf = sstate->linedsf;
1989
1990 if (unknown_count == 2) {
1991 /* Lines are known alike/opposite, depending on inv. */
1992 int e[2];
1993 find_unknowns(state, edge_list, 2, e);
1994 if (merge_lines(sstate, e[0], e[1], total_parity))
1995 diff = min(diff, DIFF_HARD);
1996 } else if (unknown_count == 3) {
1997 int e[3];
1998 int can[3]; /* canonical edges */
1999 int inv[3]; /* whether can[x] is inverse to e[x] */
2000 find_unknowns(state, edge_list, 3, e);
2001 can[0] = edsf_canonify(linedsf, e[0], inv);
2002 can[1] = edsf_canonify(linedsf, e[1], inv+1);
2003 can[2] = edsf_canonify(linedsf, e[2], inv+2);
2004 if (can[0] == can[1]) {
2005 if (solver_set_line(sstate, e[2], (total_parity^inv[0]^inv[1]) ?
2006 LINE_YES : LINE_NO))
2007 diff = min(diff, DIFF_EASY);
2008 }
2009 if (can[0] == can[2]) {
2010 if (solver_set_line(sstate, e[1], (total_parity^inv[0]^inv[2]) ?
2011 LINE_YES : LINE_NO))
2012 diff = min(diff, DIFF_EASY);
2013 }
2014 if (can[1] == can[2]) {
2015 if (solver_set_line(sstate, e[0], (total_parity^inv[1]^inv[2]) ?
2016 LINE_YES : LINE_NO))
2017 diff = min(diff, DIFF_EASY);
2018 }
2019 } else if (unknown_count == 4) {
2020 int e[4];
2021 int can[4]; /* canonical edges */
2022 int inv[4]; /* whether can[x] is inverse to e[x] */
2023 find_unknowns(state, edge_list, 4, e);
2024 can[0] = edsf_canonify(linedsf, e[0], inv);
2025 can[1] = edsf_canonify(linedsf, e[1], inv+1);
2026 can[2] = edsf_canonify(linedsf, e[2], inv+2);
2027 can[3] = edsf_canonify(linedsf, e[3], inv+3);
2028 if (can[0] == can[1]) {
2029 if (merge_lines(sstate, e[2], e[3], total_parity^inv[0]^inv[1]))
2030 diff = min(diff, DIFF_HARD);
2031 } else if (can[0] == can[2]) {
2032 if (merge_lines(sstate, e[1], e[3], total_parity^inv[0]^inv[2]))
2033 diff = min(diff, DIFF_HARD);
2034 } else if (can[0] == can[3]) {
2035 if (merge_lines(sstate, e[1], e[2], total_parity^inv[0]^inv[3]))
2036 diff = min(diff, DIFF_HARD);
2037 } else if (can[1] == can[2]) {
2038 if (merge_lines(sstate, e[0], e[3], total_parity^inv[1]^inv[2]))
2039 diff = min(diff, DIFF_HARD);
2040 } else if (can[1] == can[3]) {
2041 if (merge_lines(sstate, e[0], e[2], total_parity^inv[1]^inv[3]))
2042 diff = min(diff, DIFF_HARD);
2043 } else if (can[2] == can[3]) {
2044 if (merge_lines(sstate, e[0], e[1], total_parity^inv[2]^inv[3]))
2045 diff = min(diff, DIFF_HARD);
2046 }
2047 }
2048 return diff;
2049}
2050
2051
2052/*
2053 * These are the main solver functions.
2054 *
2055 * Their return values are diff values corresponding to the lowest mode solver
2056 * that would notice the work that they have done. For example if the normal
2057 * mode solver adds actual lines or crosses, it will return DIFF_EASY as the
2058 * easy mode solver might be able to make progress using that. It doesn't make
2059 * sense for one of them to return a diff value higher than that of the
2060 * function itself.
2061 *
2062 * Each function returns the lowest value it can, as early as possible, in
2063 * order to try and pass as much work as possible back to the lower level
2064 * solvers which progress more quickly.
2065 */
2066
2067/* PROPOSED NEW DESIGN:
2068 * We have a work queue consisting of 'events' notifying us that something has
2069 * happened that a particular solver mode might be interested in. For example
2070 * the hard mode solver might do something that helps the normal mode solver at
2071 * dot [x,y] in which case it will enqueue an event recording this fact. Then
2072 * we pull events off the work queue, and hand each in turn to the solver that
2073 * is interested in them. If a solver reports that it failed we pass the same
2074 * event on to progressively more advanced solvers and the loop detector. Once
2075 * we've exhausted an event, or it has helped us progress, we drop it and
2076 * continue to the next one. The events are sorted first in order of solver
2077 * complexity (easy first) then order of insertion (oldest first).
2078 * Once we run out of events we loop over each permitted solver in turn
2079 * (easiest first) until either a deduction is made (and an event therefore
2080 * emerges) or no further deductions can be made (in which case we've failed).
2081 *
2082 * QUESTIONS:
2083 * * How do we 'loop over' a solver when both dots and squares are concerned.
2084 * Answer: first all squares then all dots.
2085 */
2086
2087static int trivial_deductions(solver_state *sstate)
2088{
2089 int i, current_yes, current_no;
2090 game_state *state = sstate->state;
2091 grid *g = state->game_grid;
2092 int diff = DIFF_MAX;
2093
2094 /* Per-face deductions */
2095 for (i = 0; i < g->num_faces; i++) {
2096 grid_face *f = g->faces + i;
2097
2098 if (sstate->face_solved[i])
2099 continue;
2100
2101 current_yes = sstate->face_yes_count[i];
2102 current_no = sstate->face_no_count[i];
2103
2104 if (current_yes + current_no == f->order) {
2105 sstate->face_solved[i] = TRUE;
2106 continue;
2107 }
2108
2109 if (state->clues[i] < 0)
2110 continue;
2111
2112 /*
2113 * This code checks whether the numeric clue on a face is so
2114 * large as to permit all its remaining LINE_UNKNOWNs to be
2115 * filled in as LINE_YES, or alternatively so small as to
2116 * permit them all to be filled in as LINE_NO.
2117 */
2118
2119 if (state->clues[i] < current_yes) {
2120 sstate->solver_status = SOLVER_MISTAKE;
2121 return DIFF_EASY;
2122 }
2123 if (state->clues[i] == current_yes) {
2124 if (face_setall(sstate, i, LINE_UNKNOWN, LINE_NO))
2125 diff = min(diff, DIFF_EASY);
2126 sstate->face_solved[i] = TRUE;
2127 continue;
2128 }
2129
2130 if (f->order - state->clues[i] < current_no) {
2131 sstate->solver_status = SOLVER_MISTAKE;
2132 return DIFF_EASY;
2133 }
2134 if (f->order - state->clues[i] == current_no) {
2135 if (face_setall(sstate, i, LINE_UNKNOWN, LINE_YES))
2136 diff = min(diff, DIFF_EASY);
2137 sstate->face_solved[i] = TRUE;
2138 continue;
2139 }
2140
2141 if (f->order - state->clues[i] == current_no + 1 &&
2142 f->order - current_yes - current_no > 2) {
2143 /*
2144 * One small refinement to the above: we also look for any
2145 * adjacent pair of LINE_UNKNOWNs around the face with
2146 * some LINE_YES incident on it from elsewhere. If we find
2147 * one, then we know that pair of LINE_UNKNOWNs can't
2148 * _both_ be LINE_YES, and hence that pushes us one line
2149 * closer to being able to determine all the rest.
2150 */
2151 int j, k, e1, e2, e, d;
2152
2153 for (j = 0; j < f->order; j++) {
2154 e1 = f->edges[j] - g->edges;
2155 e2 = f->edges[j+1 < f->order ? j+1 : 0] - g->edges;
2156
2157 if (g->edges[e1].dot1 == g->edges[e2].dot1 ||
2158 g->edges[e1].dot1 == g->edges[e2].dot2) {
2159 d = g->edges[e1].dot1 - g->dots;
2160 } else {
2161 assert(g->edges[e1].dot2 == g->edges[e2].dot1 ||
2162 g->edges[e1].dot2 == g->edges[e2].dot2);
2163 d = g->edges[e1].dot2 - g->dots;
2164 }
2165
2166 if (state->lines[e1] == LINE_UNKNOWN &&
2167 state->lines[e2] == LINE_UNKNOWN) {
2168 for (k = 0; k < g->dots[d].order; k++) {
2169 int e = g->dots[d].edges[k] - g->edges;
2170 if (state->lines[e] == LINE_YES)
2171 goto found; /* multi-level break */
2172 }
2173 }
2174 }
2175 continue;
2176
2177 found:
2178 /*
2179 * If we get here, we've found such a pair of edges, and
2180 * they're e1 and e2.
2181 */
2182 for (j = 0; j < f->order; j++) {
2183 e = f->edges[j] - g->edges;
2184 if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) {
2185 int r = solver_set_line(sstate, e, LINE_YES);
2186 assert(r);
2187 diff = min(diff, DIFF_EASY);
2188 }
2189 }
2190 }
2191 }
2192
2193 check_caches(sstate);
2194
2195 /* Per-dot deductions */
2196 for (i = 0; i < g->num_dots; i++) {
2197 grid_dot *d = g->dots + i;
2198 int yes, no, unknown;
2199
2200 if (sstate->dot_solved[i])
2201 continue;
2202
2203 yes = sstate->dot_yes_count[i];
2204 no = sstate->dot_no_count[i];
2205 unknown = d->order - yes - no;
2206
2207 if (yes == 0) {
2208 if (unknown == 0) {
2209 sstate->dot_solved[i] = TRUE;
2210 } else if (unknown == 1) {
2211 dot_setall(sstate, i, LINE_UNKNOWN, LINE_NO);
2212 diff = min(diff, DIFF_EASY);
2213 sstate->dot_solved[i] = TRUE;
2214 }
2215 } else if (yes == 1) {
2216 if (unknown == 0) {
2217 sstate->solver_status = SOLVER_MISTAKE;
2218 return DIFF_EASY;
2219 } else if (unknown == 1) {
2220 dot_setall(sstate, i, LINE_UNKNOWN, LINE_YES);
2221 diff = min(diff, DIFF_EASY);
2222 }
2223 } else if (yes == 2) {
2224 if (unknown > 0) {
2225 dot_setall(sstate, i, LINE_UNKNOWN, LINE_NO);
2226 diff = min(diff, DIFF_EASY);
2227 }
2228 sstate->dot_solved[i] = TRUE;
2229 } else {
2230 sstate->solver_status = SOLVER_MISTAKE;
2231 return DIFF_EASY;
2232 }
2233 }
2234
2235 check_caches(sstate);
2236
2237 return diff;
2238}
2239
2240static int dline_deductions(solver_state *sstate)
2241{
2242 game_state *state = sstate->state;
2243 grid *g = state->game_grid;
2244 char *dlines = sstate->dlines;
2245 int i;
2246 int diff = DIFF_MAX;
2247
2248 /* ------ Face deductions ------ */
2249
2250 /* Given a set of dline atmostone/atleastone constraints, need to figure
2251 * out if we can deduce any further info. For more general faces than
2252 * squares, this turns out to be a tricky problem.
2253 * The approach taken here is to define (per face) NxN matrices:
2254 * "maxs" and "mins".
2255 * The entries maxs(j,k) and mins(j,k) define the upper and lower limits
2256 * for the possible number of edges that are YES between positions j and k
2257 * going clockwise around the face. Can think of j and k as marking dots
2258 * around the face (recall the labelling scheme: edge0 joins dot0 to dot1,
2259 * edge1 joins dot1 to dot2 etc).
2260 * Trivially, mins(j,j) = maxs(j,j) = 0, and we don't even bother storing
2261 * these. mins(j,j+1) and maxs(j,j+1) are determined by whether edge{j}
2262 * is YES, NO or UNKNOWN. mins(j,j+2) and maxs(j,j+2) are related to
2263 * the dline atmostone/atleastone status for edges j and j+1.
2264 *
2265 * Then we calculate the remaining entries recursively. We definitely
2266 * know that
2267 * mins(j,k) >= { mins(j,u) + mins(u,k) } for any u between j and k.
2268 * This is because any valid placement of YESs between j and k must give
2269 * a valid placement between j and u, and also between u and k.
2270 * I believe it's sufficient to use just the two values of u:
2271 * j+1 and j+2. Seems to work well in practice - the bounds we compute
2272 * are rigorous, even if they might not be best-possible.
2273 *
2274 * Once we have maxs and mins calculated, we can make inferences about
2275 * each dline{j,j+1} by looking at the possible complementary edge-counts
2276 * mins(j+2,j) and maxs(j+2,j) and comparing these with the face clue.
2277 * As well as dlines, we can make similar inferences about single edges.
2278 * For example, consider a pentagon with clue 3, and we know at most one
2279 * of (edge0, edge1) is YES, and at most one of (edge2, edge3) is YES.
2280 * We could then deduce edge4 is YES, because maxs(0,4) would be 2, so
2281 * that final edge would have to be YES to make the count up to 3.
2282 */
2283
2284 /* Much quicker to allocate arrays on the stack than the heap, so
2285 * define the largest possible face size, and base our array allocations
2286 * on that. We check this with an assertion, in case someone decides to
2287 * make a grid which has larger faces than this. Note, this algorithm
2288 * could get quite expensive if there are many large faces. */
2289#define MAX_FACE_SIZE 12
2290
2291 for (i = 0; i < g->num_faces; i++) {
2292 int maxs[MAX_FACE_SIZE][MAX_FACE_SIZE];
2293 int mins[MAX_FACE_SIZE][MAX_FACE_SIZE];
2294 grid_face *f = g->faces + i;
2295 int N = f->order;
2296 int j,m;
2297 int clue = state->clues[i];
2298 assert(N <= MAX_FACE_SIZE);
2299 if (sstate->face_solved[i])
2300 continue;
2301 if (clue < 0) continue;
2302
2303 /* Calculate the (j,j+1) entries */
2304 for (j = 0; j < N; j++) {
2305 int edge_index = f->edges[j] - g->edges;
2306 int dline_index;
2307 enum line_state line1 = state->lines[edge_index];
2308 enum line_state line2;
2309 int tmp;
2310 int k = j + 1;
2311 if (k >= N) k = 0;
2312 maxs[j][k] = (line1 == LINE_NO) ? 0 : 1;
2313 mins[j][k] = (line1 == LINE_YES) ? 1 : 0;
2314 /* Calculate the (j,j+2) entries */
2315 dline_index = dline_index_from_face(g, f, k);
2316 edge_index = f->edges[k] - g->edges;
2317 line2 = state->lines[edge_index];
2318 k++;
2319 if (k >= N) k = 0;
2320
2321 /* max */
2322 tmp = 2;
2323 if (line1 == LINE_NO) tmp--;
2324 if (line2 == LINE_NO) tmp--;
2325 if (tmp == 2 && is_atmostone(dlines, dline_index))
2326 tmp = 1;
2327 maxs[j][k] = tmp;
2328
2329 /* min */
2330 tmp = 0;
2331 if (line1 == LINE_YES) tmp++;
2332 if (line2 == LINE_YES) tmp++;
2333 if (tmp == 0 && is_atleastone(dlines, dline_index))
2334 tmp = 1;
2335 mins[j][k] = tmp;
2336 }
2337
2338 /* Calculate the (j,j+m) entries for m between 3 and N-1 */
2339 for (m = 3; m < N; m++) {
2340 for (j = 0; j < N; j++) {
2341 int k = j + m;
2342 int u = j + 1;
2343 int v = j + 2;
2344 int tmp;
2345 if (k >= N) k -= N;
2346 if (u >= N) u -= N;
2347 if (v >= N) v -= N;
2348 maxs[j][k] = maxs[j][u] + maxs[u][k];
2349 mins[j][k] = mins[j][u] + mins[u][k];
2350 tmp = maxs[j][v] + maxs[v][k];
2351 maxs[j][k] = min(maxs[j][k], tmp);
2352 tmp = mins[j][v] + mins[v][k];
2353 mins[j][k] = max(mins[j][k], tmp);
2354 }
2355 }
2356
2357 /* See if we can make any deductions */
2358 for (j = 0; j < N; j++) {
2359 int k;
2360 grid_edge *e = f->edges[j];
2361 int line_index = e - g->edges;
2362 int dline_index;
2363
2364 if (state->lines[line_index] != LINE_UNKNOWN)
2365 continue;
2366 k = j + 1;
2367 if (k >= N) k = 0;
2368
2369 /* minimum YESs in the complement of this edge */
2370 if (mins[k][j] > clue) {
2371 sstate->solver_status = SOLVER_MISTAKE;
2372 return DIFF_EASY;
2373 }
2374 if (mins[k][j] == clue) {
2375 /* setting this edge to YES would make at least
2376 * (clue+1) edges - contradiction */
2377 solver_set_line(sstate, line_index, LINE_NO);
2378 diff = min(diff, DIFF_EASY);
2379 }
2380 if (maxs[k][j] < clue - 1) {
2381 sstate->solver_status = SOLVER_MISTAKE;
2382 return DIFF_EASY;
2383 }
2384 if (maxs[k][j] == clue - 1) {
2385 /* Only way to satisfy the clue is to set edge{j} as YES */
2386 solver_set_line(sstate, line_index, LINE_YES);
2387 diff = min(diff, DIFF_EASY);
2388 }
2389
2390 /* More advanced deduction that allows propagation along diagonal
2391 * chains of faces connected by dots, for example, 3-2-...-2-3
2392 * in square grids. */
2393 if (sstate->diff >= DIFF_TRICKY) {
2394 /* Now see if we can make dline deduction for edges{j,j+1} */
2395 e = f->edges[k];
2396 if (state->lines[e - g->edges] != LINE_UNKNOWN)
2397 /* Only worth doing this for an UNKNOWN,UNKNOWN pair.
2398 * Dlines where one of the edges is known, are handled in the
2399 * dot-deductions */
2400 continue;
2401
2402 dline_index = dline_index_from_face(g, f, k);
2403 k++;
2404 if (k >= N) k = 0;
2405
2406 /* minimum YESs in the complement of this dline */
2407 if (mins[k][j] > clue - 2) {
2408 /* Adding 2 YESs would break the clue */
2409 if (set_atmostone(dlines, dline_index))
2410 diff = min(diff, DIFF_NORMAL);
2411 }
2412 /* maximum YESs in the complement of this dline */
2413 if (maxs[k][j] < clue) {
2414 /* Adding 2 NOs would mean not enough YESs */
2415 if (set_atleastone(dlines, dline_index))
2416 diff = min(diff, DIFF_NORMAL);
2417 }
2418 }
2419 }
2420 }
2421
2422 if (diff < DIFF_NORMAL)
2423 return diff;
2424
2425 /* ------ Dot deductions ------ */
2426
2427 for (i = 0; i < g->num_dots; i++) {
2428 grid_dot *d = g->dots + i;
2429 int N = d->order;
2430 int yes, no, unknown;
2431 int j;
2432 if (sstate->dot_solved[i])
2433 continue;
2434 yes = sstate->dot_yes_count[i];
2435 no = sstate->dot_no_count[i];
2436 unknown = N - yes - no;
2437
2438 for (j = 0; j < N; j++) {
2439 int k;
2440 int dline_index;
2441 int line1_index, line2_index;
2442 enum line_state line1, line2;
2443 k = j + 1;
2444 if (k >= N) k = 0;
2445 dline_index = dline_index_from_dot(g, d, j);
2446 line1_index = d->edges[j] - g->edges;
2447 line2_index = d->edges[k] - g->edges;
2448 line1 = state->lines[line1_index];
2449 line2 = state->lines[line2_index];
2450
2451 /* Infer dline state from line state */
2452 if (line1 == LINE_NO || line2 == LINE_NO) {
2453 if (set_atmostone(dlines, dline_index))
2454 diff = min(diff, DIFF_NORMAL);
2455 }
2456 if (line1 == LINE_YES || line2 == LINE_YES) {
2457 if (set_atleastone(dlines, dline_index))
2458 diff = min(diff, DIFF_NORMAL);
2459 }
2460 /* Infer line state from dline state */
2461 if (is_atmostone(dlines, dline_index)) {
2462 if (line1 == LINE_YES && line2 == LINE_UNKNOWN) {
2463 solver_set_line(sstate, line2_index, LINE_NO);
2464 diff = min(diff, DIFF_EASY);
2465 }
2466 if (line2 == LINE_YES && line1 == LINE_UNKNOWN) {
2467 solver_set_line(sstate, line1_index, LINE_NO);
2468 diff = min(diff, DIFF_EASY);
2469 }
2470 }
2471 if (is_atleastone(dlines, dline_index)) {
2472 if (line1 == LINE_NO && line2 == LINE_UNKNOWN) {
2473 solver_set_line(sstate, line2_index, LINE_YES);
2474 diff = min(diff, DIFF_EASY);
2475 }
2476 if (line2 == LINE_NO && line1 == LINE_UNKNOWN) {
2477 solver_set_line(sstate, line1_index, LINE_YES);
2478 diff = min(diff, DIFF_EASY);
2479 }
2480 }
2481 /* Deductions that depend on the numbers of lines.
2482 * Only bother if both lines are UNKNOWN, otherwise the
2483 * easy-mode solver (or deductions above) would have taken
2484 * care of it. */
2485 if (line1 != LINE_UNKNOWN || line2 != LINE_UNKNOWN)
2486 continue;
2487
2488 if (yes == 0 && unknown == 2) {
2489 /* Both these unknowns must be identical. If we know
2490 * atmostone or atleastone, we can make progress. */
2491 if (is_atmostone(dlines, dline_index)) {
2492 solver_set_line(sstate, line1_index, LINE_NO);
2493 solver_set_line(sstate, line2_index, LINE_NO);
2494 diff = min(diff, DIFF_EASY);
2495 }
2496 if (is_atleastone(dlines, dline_index)) {
2497 solver_set_line(sstate, line1_index, LINE_YES);
2498 solver_set_line(sstate, line2_index, LINE_YES);
2499 diff = min(diff, DIFF_EASY);
2500 }
2501 }
2502 if (yes == 1) {
2503 if (set_atmostone(dlines, dline_index))
2504 diff = min(diff, DIFF_NORMAL);
2505 if (unknown == 2) {
2506 if (set_atleastone(dlines, dline_index))
2507 diff = min(diff, DIFF_NORMAL);
2508 }
2509 }
2510
2511 /* More advanced deduction that allows propagation along diagonal
2512 * chains of faces connected by dots, for example: 3-2-...-2-3
2513 * in square grids. */
2514 if (sstate->diff >= DIFF_TRICKY) {
2515 /* If we have atleastone set for this dline, infer
2516 * atmostone for each "opposite" dline (that is, each
2517 * dline without edges in common with this one).
2518 * Again, this test is only worth doing if both these
2519 * lines are UNKNOWN. For if one of these lines were YES,
2520 * the (yes == 1) test above would kick in instead. */
2521 if (is_atleastone(dlines, dline_index)) {
2522 int opp;
2523 for (opp = 0; opp < N; opp++) {
2524 int opp_dline_index;
2525 if (opp == j || opp == j+1 || opp == j-1)
2526 continue;
2527 if (j == 0 && opp == N-1)
2528 continue;
2529 if (j == N-1 && opp == 0)
2530 continue;
2531 opp_dline_index = dline_index_from_dot(g, d, opp);
2532 if (set_atmostone(dlines, opp_dline_index))
2533 diff = min(diff, DIFF_NORMAL);
2534 }
2535 if (yes == 0 && is_atmostone(dlines, dline_index)) {
2536 /* This dline has *exactly* one YES and there are no
2537 * other YESs. This allows more deductions. */
2538 if (unknown == 3) {
2539 /* Third unknown must be YES */
2540 for (opp = 0; opp < N; opp++) {
2541 int opp_index;
2542 if (opp == j || opp == k)
2543 continue;
2544 opp_index = d->edges[opp] - g->edges;
2545 if (state->lines[opp_index] == LINE_UNKNOWN) {
2546 solver_set_line(sstate, opp_index,
2547 LINE_YES);
2548 diff = min(diff, DIFF_EASY);
2549 }
2550 }
2551 } else if (unknown == 4) {
2552 /* Exactly one of opposite UNKNOWNS is YES. We've
2553 * already set atmostone, so set atleastone as
2554 * well.
2555 */
2556 if (dline_set_opp_atleastone(sstate, d, j))
2557 diff = min(diff, DIFF_NORMAL);
2558 }
2559 }
2560 }
2561 }
2562 }
2563 }
2564 return diff;
2565}
2566
2567static int linedsf_deductions(solver_state *sstate)
2568{
2569 game_state *state = sstate->state;
2570 grid *g = state->game_grid;
2571 char *dlines = sstate->dlines;
2572 int i;
2573 int diff = DIFF_MAX;
2574 int diff_tmp;
2575
2576 /* ------ Face deductions ------ */
2577
2578 /* A fully-general linedsf deduction seems overly complicated
2579 * (I suspect the problem is NP-complete, though in practice it might just
2580 * be doable because faces are limited in size).
2581 * For simplicity, we only consider *pairs* of LINE_UNKNOWNS that are
2582 * known to be identical. If setting them both to YES (or NO) would break
2583 * the clue, set them to NO (or YES). */
2584
2585 for (i = 0; i < g->num_faces; i++) {
2586 int N, yes, no, unknown;
2587 int clue;
2588
2589 if (sstate->face_solved[i])
2590 continue;
2591 clue = state->clues[i];
2592 if (clue < 0)
2593 continue;
2594
2595 N = g->faces[i].order;
2596 yes = sstate->face_yes_count[i];
2597 if (yes + 1 == clue) {
2598 if (face_setall_identical(sstate, i, LINE_NO))
2599 diff = min(diff, DIFF_EASY);
2600 }
2601 no = sstate->face_no_count[i];
2602 if (no + 1 == N - clue) {
2603 if (face_setall_identical(sstate, i, LINE_YES))
2604 diff = min(diff, DIFF_EASY);
2605 }
2606
2607 /* Reload YES count, it might have changed */
2608 yes = sstate->face_yes_count[i];
2609 unknown = N - no - yes;
2610
2611 /* Deductions with small number of LINE_UNKNOWNs, based on overall
2612 * parity of lines. */
2613 diff_tmp = parity_deductions(sstate, g->faces[i].edges,
2614 (clue - yes) % 2, unknown);
2615 diff = min(diff, diff_tmp);
2616 }
2617
2618 /* ------ Dot deductions ------ */
2619 for (i = 0; i < g->num_dots; i++) {
2620 grid_dot *d = g->dots + i;
2621 int N = d->order;
2622 int j;
2623 int yes, no, unknown;
2624 /* Go through dlines, and do any dline<->linedsf deductions wherever
2625 * we find two UNKNOWNS. */
2626 for (j = 0; j < N; j++) {
2627 int dline_index = dline_index_from_dot(g, d, j);
2628 int line1_index;
2629 int line2_index;
2630 int can1, can2, inv1, inv2;
2631 int j2;
2632 line1_index = d->edges[j] - g->edges;
2633 if (state->lines[line1_index] != LINE_UNKNOWN)
2634 continue;
2635 j2 = j + 1;
2636 if (j2 == N) j2 = 0;
2637 line2_index = d->edges[j2] - g->edges;
2638 if (state->lines[line2_index] != LINE_UNKNOWN)
2639 continue;
2640 /* Infer dline flags from linedsf */
2641 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1);
2642 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2);
2643 if (can1 == can2 && inv1 != inv2) {
2644 /* These are opposites, so set dline atmostone/atleastone */
2645 if (set_atmostone(dlines, dline_index))
2646 diff = min(diff, DIFF_NORMAL);
2647 if (set_atleastone(dlines, dline_index))
2648 diff = min(diff, DIFF_NORMAL);
2649 continue;
2650 }
2651 /* Infer linedsf from dline flags */
2652 if (is_atmostone(dlines, dline_index)
2653 && is_atleastone(dlines, dline_index)) {
2654 if (merge_lines(sstate, line1_index, line2_index, 1))
2655 diff = min(diff, DIFF_HARD);
2656 }
2657 }
2658
2659 /* Deductions with small number of LINE_UNKNOWNs, based on overall
2660 * parity of lines. */
2661 yes = sstate->dot_yes_count[i];
2662 no = sstate->dot_no_count[i];
2663 unknown = N - yes - no;
2664 diff_tmp = parity_deductions(sstate, d->edges,
2665 yes % 2, unknown);
2666 diff = min(diff, diff_tmp);
2667 }
2668
2669 /* ------ Edge dsf deductions ------ */
2670
2671 /* If the state of a line is known, deduce the state of its canonical line
2672 * too, and vice versa. */
2673 for (i = 0; i < g->num_edges; i++) {
2674 int can, inv;
2675 enum line_state s;
2676 can = edsf_canonify(sstate->linedsf, i, &inv);
2677 if (can == i)
2678 continue;
2679 s = sstate->state->lines[can];
2680 if (s != LINE_UNKNOWN) {
2681 if (solver_set_line(sstate, i, inv ? OPP(s) : s))
2682 diff = min(diff, DIFF_EASY);
2683 } else {
2684 s = sstate->state->lines[i];
2685 if (s != LINE_UNKNOWN) {
2686 if (solver_set_line(sstate, can, inv ? OPP(s) : s))
2687 diff = min(diff, DIFF_EASY);
2688 }
2689 }
2690 }
2691
2692 return diff;
2693}
2694
2695static int loop_deductions(solver_state *sstate)
2696{
2697 int edgecount = 0, clues = 0, satclues = 0, sm1clues = 0;
2698 game_state *state = sstate->state;
2699 grid *g = state->game_grid;
2700 int shortest_chainlen = g->num_dots;
2701 int loop_found = FALSE;
2702 int dots_connected;
2703 int progress = FALSE;
2704 int i;
2705
2706 /*
2707 * Go through the grid and update for all the new edges.
2708 * Since merge_dots() is idempotent, the simplest way to
2709 * do this is just to update for _all_ the edges.
2710 * Also, while we're here, we count the edges.
2711 */
2712 for (i = 0; i < g->num_edges; i++) {
2713 if (state->lines[i] == LINE_YES) {
2714 loop_found |= merge_dots(sstate, i);
2715 edgecount++;
2716 }
2717 }
2718
2719 /*
2720 * Count the clues, count the satisfied clues, and count the
2721 * satisfied-minus-one clues.
2722 */
2723 for (i = 0; i < g->num_faces; i++) {
2724 int c = state->clues[i];
2725 if (c >= 0) {
2726 int o = sstate->face_yes_count[i];
2727 if (o == c)
2728 satclues++;
2729 else if (o == c-1)
2730 sm1clues++;
2731 clues++;
2732 }
2733 }
2734
2735 for (i = 0; i < g->num_dots; ++i) {
2736 dots_connected =
2737 sstate->looplen[dsf_canonify(sstate->dotdsf, i)];
2738 if (dots_connected > 1)
2739 shortest_chainlen = min(shortest_chainlen, dots_connected);
2740 }
2741
2742 assert(sstate->solver_status == SOLVER_INCOMPLETE);
2743
2744 if (satclues == clues && shortest_chainlen == edgecount) {
2745 sstate->solver_status = SOLVER_SOLVED;
2746 /* This discovery clearly counts as progress, even if we haven't
2747 * just added any lines or anything */
2748 progress = TRUE;
2749 goto finished_loop_deductionsing;
2750 }
2751
2752 /*
2753 * Now go through looking for LINE_UNKNOWN edges which
2754 * connect two dots that are already in the same
2755 * equivalence class. If we find one, test to see if the
2756 * loop it would create is a solution.
2757 */
2758 for (i = 0; i < g->num_edges; i++) {
2759 grid_edge *e = g->edges + i;
2760 int d1 = e->dot1 - g->dots;
2761 int d2 = e->dot2 - g->dots;
2762 int eqclass, val;
2763 if (state->lines[i] != LINE_UNKNOWN)
2764 continue;
2765
2766 eqclass = dsf_canonify(sstate->dotdsf, d1);
2767 if (eqclass != dsf_canonify(sstate->dotdsf, d2))
2768 continue;
2769
2770 val = LINE_NO; /* loop is bad until proven otherwise */
2771
2772 /*
2773 * This edge would form a loop. Next
2774 * question: how long would the loop be?
2775 * Would it equal the total number of edges
2776 * (plus the one we'd be adding if we added
2777 * it)?
2778 */
2779 if (sstate->looplen[eqclass] == edgecount + 1) {
2780 int sm1_nearby;
2781
2782 /*
2783 * This edge would form a loop which
2784 * took in all the edges in the entire
2785 * grid. So now we need to work out
2786 * whether it would be a valid solution
2787 * to the puzzle, which means we have to
2788 * check if it satisfies all the clues.
2789 * This means that every clue must be
2790 * either satisfied or satisfied-minus-
2791 * 1, and also that the number of
2792 * satisfied-minus-1 clues must be at
2793 * most two and they must lie on either
2794 * side of this edge.
2795 */
2796 sm1_nearby = 0;
2797 if (e->face1) {
2798 int f = e->face1 - g->faces;
2799 int c = state->clues[f];
2800 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2801 sm1_nearby++;
2802 }
2803 if (e->face2) {
2804 int f = e->face2 - g->faces;
2805 int c = state->clues[f];
2806 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2807 sm1_nearby++;
2808 }
2809 if (sm1clues == sm1_nearby &&
2810 sm1clues + satclues == clues) {
2811 val = LINE_YES; /* loop is good! */
2812 }
2813 }
2814
2815 /*
2816 * Right. Now we know that adding this edge
2817 * would form a loop, and we know whether
2818 * that loop would be a viable solution or
2819 * not.
2820 *
2821 * If adding this edge produces a solution,
2822 * then we know we've found _a_ solution but
2823 * we don't know that it's _the_ solution -
2824 * if it were provably the solution then
2825 * we'd have deduced this edge some time ago
2826 * without the need to do loop detection. So
2827 * in this state we return SOLVER_AMBIGUOUS,
2828 * which has the effect that hitting Solve
2829 * on a user-provided puzzle will fill in a
2830 * solution but using the solver to
2831 * construct new puzzles won't consider this
2832 * a reasonable deduction for the user to
2833 * make.
2834 */
2835 progress = solver_set_line(sstate, i, val);
2836 assert(progress == TRUE);
2837 if (val == LINE_YES) {
2838 sstate->solver_status = SOLVER_AMBIGUOUS;
2839 goto finished_loop_deductionsing;
2840 }
2841 }
2842
2843 finished_loop_deductionsing:
2844 return progress ? DIFF_EASY : DIFF_MAX;
2845}
2846
2847/* This will return a dynamically allocated solver_state containing the (more)
2848 * solved grid */
2849static solver_state *solve_game_rec(const solver_state *sstate_start)
2850{
2851 solver_state *sstate;
2852
2853 /* Index of the solver we should call next. */
2854 int i = 0;
2855
2856 /* As a speed-optimisation, we avoid re-running solvers that we know
2857 * won't make any progress. This happens when a high-difficulty
2858 * solver makes a deduction that can only help other high-difficulty
2859 * solvers.
2860 * For example: if a new 'dline' flag is set by dline_deductions, the
2861 * trivial_deductions solver cannot do anything with this information.
2862 * If we've already run the trivial_deductions solver (because it's
2863 * earlier in the list), there's no point running it again.
2864 *
2865 * Therefore: if a solver is earlier in the list than "threshold_index",
2866 * we don't bother running it if it's difficulty level is less than
2867 * "threshold_diff".
2868 */
2869 int threshold_diff = 0;
2870 int threshold_index = 0;
2871
2872 sstate = dup_solver_state(sstate_start);
2873
2874 check_caches(sstate);
2875
2876 while (i < NUM_SOLVERS) {
2877 if (sstate->solver_status == SOLVER_MISTAKE)
2878 return sstate;
2879 if (sstate->solver_status == SOLVER_SOLVED ||
2880 sstate->solver_status == SOLVER_AMBIGUOUS) {
2881 /* solver finished */
2882 break;
2883 }
2884
2885 if ((solver_diffs[i] >= threshold_diff || i >= threshold_index)
2886 && solver_diffs[i] <= sstate->diff) {
2887 /* current_solver is eligible, so use it */
2888 int next_diff = solver_fns[i](sstate);
2889 if (next_diff != DIFF_MAX) {
2890 /* solver made progress, so use new thresholds and
2891 * start again at top of list. */
2892 threshold_diff = next_diff;
2893 threshold_index = i;
2894 i = 0;
2895 continue;
2896 }
2897 }
2898 /* current_solver is ineligible, or failed to make progress, so
2899 * go to the next solver in the list */
2900 i++;
2901 }
2902
2903 if (sstate->solver_status == SOLVER_SOLVED ||
2904 sstate->solver_status == SOLVER_AMBIGUOUS) {
2905 /* s/LINE_UNKNOWN/LINE_NO/g */
2906 array_setall(sstate->state->lines, LINE_UNKNOWN, LINE_NO,
2907 sstate->state->game_grid->num_edges);
2908 return sstate;
2909 }
2910
2911 return sstate;
2912}
2913
2914static char *solve_game(const game_state *state, const game_state *currstate,
2915 const char *aux, char **error)
2916{
2917 char *soln = NULL;
2918 solver_state *sstate, *new_sstate;
2919
2920 sstate = new_solver_state(state, DIFF_MAX);
2921 new_sstate = solve_game_rec(sstate);
2922
2923 if (new_sstate->solver_status == SOLVER_SOLVED) {
2924 soln = encode_solve_move(new_sstate->state);
2925 } else if (new_sstate->solver_status == SOLVER_AMBIGUOUS) {
2926 soln = encode_solve_move(new_sstate->state);
2927 /**error = "Solver found ambiguous solutions"; */
2928 } else {
2929 soln = encode_solve_move(new_sstate->state);
2930 /**error = "Solver failed"; */
2931 }
2932
2933 free_solver_state(new_sstate);
2934 free_solver_state(sstate);
2935
2936 return soln;
2937}
2938
2939/* ----------------------------------------------------------------------
2940 * Drawing and mouse-handling
2941 */
2942
2943static char *interpret_move(const game_state *state, game_ui *ui,
2944 const game_drawstate *ds,
2945 int x, int y, int button)
2946{
2947 grid *g = state->game_grid;
2948 grid_edge *e;
2949 int i;
2950 char *movebuf;
2951 int movelen, movesize;
2952 char button_char = ' ';
2953 enum line_state old_state;
2954
2955 button &= ~MOD_MASK;
2956
2957 /* Convert mouse-click (x,y) to grid coordinates */
2958 x -= BORDER(ds->tilesize);
2959 y -= BORDER(ds->tilesize);
2960 x = x * g->tilesize / ds->tilesize;
2961 y = y * g->tilesize / ds->tilesize;
2962 x += g->lowest_x;
2963 y += g->lowest_y;
2964
2965 e = grid_nearest_edge(g, x, y);
2966 if (e == NULL)
2967 return NULL;
2968
2969 i = e - g->edges;
2970
2971 /* I think it's only possible to play this game with mouse clicks, sorry */
2972 /* Maybe will add mouse drag support some time */
2973 old_state = state->lines[i];
2974
2975 switch (button) {
2976 case LEFT_BUTTON:
2977 switch (old_state) {
2978 case LINE_UNKNOWN:
2979 button_char = 'y';
2980 break;
2981 case LINE_YES:
2982#ifdef STYLUS_BASED
2983 button_char = 'n';
2984 break;
2985#endif
2986 case LINE_NO:
2987 button_char = 'u';
2988 break;
2989 }
2990 break;
2991 case MIDDLE_BUTTON:
2992 button_char = 'u';
2993 break;
2994 case RIGHT_BUTTON:
2995 switch (old_state) {
2996 case LINE_UNKNOWN:
2997 button_char = 'n';
2998 break;
2999 case LINE_NO:
3000#ifdef STYLUS_BASED
3001 button_char = 'y';
3002 break;
3003#endif
3004 case LINE_YES:
3005 button_char = 'u';
3006 break;
3007 }
3008 break;
3009 default:
3010 return NULL;
3011 }
3012
3013 movelen = 0;
3014 movesize = 80;
3015 movebuf = snewn(movesize, char);
3016 movelen = sprintf(movebuf, "%d%c", i, (int)button_char);
3017 {
3018 static enum { OFF, FIXED, ADAPTIVE, DUNNO } autofollow = DUNNO;
3019 if (autofollow == DUNNO) {
3020 const char *env = getenv("LOOPY_AUTOFOLLOW");
3021 if (env && !strcmp(env, "off"))
3022 autofollow = OFF;
3023 else if (env && !strcmp(env, "fixed"))
3024 autofollow = FIXED;
3025 else if (env && !strcmp(env, "adaptive"))
3026 autofollow = ADAPTIVE;
3027 else
3028 autofollow = OFF;
3029 }
3030
3031 if (autofollow != OFF) {
3032 int dotid;
3033 for (dotid = 0; dotid < 2; dotid++) {
3034 grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2);
3035 grid_edge *e_this = e;
3036
3037 while (1) {
3038 int j, n_found;
3039 grid_edge *e_next = NULL;
3040
3041 for (j = n_found = 0; j < dot->order; j++) {
3042 grid_edge *e_candidate = dot->edges[j];
3043 int i_candidate = e_candidate - g->edges;
3044 if (e_candidate != e_this &&
3045 (autofollow == FIXED ||
3046 state->lines[i] == LINE_NO ||
3047 state->lines[i_candidate] != LINE_NO)) {
3048 e_next = e_candidate;
3049 n_found++;
3050 }
3051 }
3052
3053 if (n_found != 1 ||
3054 state->lines[e_next - g->edges] != state->lines[i])
3055 break;
3056
3057 dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2);
3058 if (movelen > movesize - 40) {
3059 movesize = movesize * 5 / 4 + 128;
3060 movebuf = sresize(movebuf, movesize, char);
3061 }
3062 e_this = e_next;
3063 movelen += sprintf(movebuf+movelen, "%d%c",
3064 (int)(e_this - g->edges), button_char);
3065 }
3066 }
3067 }
3068 }
3069
3070 return sresize(movebuf, movelen+1, char);
3071}
3072
3073static game_state *execute_move(const game_state *state, const char *move)
3074{
3075 int i;
3076 game_state *newstate = dup_game(state);
3077
3078 if (move[0] == 'S') {
3079 move++;
3080 newstate->cheated = TRUE;
3081 }
3082
3083 while (*move) {
3084 i = atoi(move);
3085 if (i < 0 || i >= newstate->game_grid->num_edges)
3086 goto fail;
3087 move += strspn(move, "1234567890");
3088 switch (*(move++)) {
3089 case 'y':
3090 newstate->lines[i] = LINE_YES;
3091 break;
3092 case 'n':
3093 newstate->lines[i] = LINE_NO;
3094 break;
3095 case 'u':
3096 newstate->lines[i] = LINE_UNKNOWN;
3097 break;
3098 default:
3099 goto fail;
3100 }
3101 }
3102
3103 /*
3104 * Check for completion.
3105 */
3106 if (check_completion(newstate))
3107 newstate->solved = TRUE;
3108
3109 return newstate;
3110
3111 fail:
3112 free_game(newstate);
3113 return NULL;
3114}
3115
3116/* ----------------------------------------------------------------------
3117 * Drawing routines.
3118 */
3119
3120/* Convert from grid coordinates to screen coordinates */
3121static void grid_to_screen(const game_drawstate *ds, const grid *g,
3122 int grid_x, int grid_y, int *x, int *y)
3123{
3124 *x = grid_x - g->lowest_x;
3125 *y = grid_y - g->lowest_y;
3126 *x = *x * ds->tilesize / g->tilesize;
3127 *y = *y * ds->tilesize / g->tilesize;
3128 *x += BORDER(ds->tilesize);
3129 *y += BORDER(ds->tilesize);
3130}
3131
3132/* Returns (into x,y) position of centre of face for rendering the text clue.
3133 */
3134static void face_text_pos(const game_drawstate *ds, const grid *g,
3135 grid_face *f, int *xret, int *yret)
3136{
3137 int faceindex = f - g->faces;
3138
3139 /*
3140 * Return the cached position for this face, if we've already
3141 * worked it out.
3142 */
3143 if (ds->textx[faceindex] >= 0) {
3144 *xret = ds->textx[faceindex];
3145 *yret = ds->texty[faceindex];
3146 return;
3147 }
3148
3149 /*
3150 * Otherwise, use the incentre computed by grid.c and convert it
3151 * to screen coordinates.
3152 */
3153 grid_find_incentre(f);
3154 grid_to_screen(ds, g, f->ix, f->iy,
3155 &ds->textx[faceindex], &ds->texty[faceindex]);
3156
3157 *xret = ds->textx[faceindex];
3158 *yret = ds->texty[faceindex];
3159}
3160
3161static void face_text_bbox(game_drawstate *ds, grid *g, grid_face *f,
3162 int *x, int *y, int *w, int *h)
3163{
3164 int xx, yy;
3165 face_text_pos(ds, g, f, &xx, &yy);
3166
3167 /* There seems to be a certain amount of trial-and-error involved
3168 * in working out the correct bounding-box for the text. */
3169
3170 *x = xx - ds->tilesize/4 - 1;
3171 *y = yy - ds->tilesize/4 - 3;
3172 *w = ds->tilesize/2 + 2;
3173 *h = ds->tilesize/2 + 5;
3174}
3175
3176static void game_redraw_clue(drawing *dr, game_drawstate *ds,
3177 const game_state *state, int i)
3178{
3179 grid *g = state->game_grid;
3180 grid_face *f = g->faces + i;
3181 int x, y;
3182 char c[20];
3183
3184 sprintf(c, "%d", state->clues[i]);
3185
3186 face_text_pos(ds, g, f, &x, &y);
3187 draw_text(dr, x, y,
3188 FONT_VARIABLE, ds->tilesize/2,
3189 ALIGN_VCENTRE | ALIGN_HCENTRE,
3190 ds->clue_error[i] ? COL_MISTAKE :
3191 ds->clue_satisfied[i] ? COL_SATISFIED : COL_FOREGROUND, c);
3192}
3193
3194static void edge_bbox(game_drawstate *ds, grid *g, grid_edge *e,
3195 int *x, int *y, int *w, int *h)
3196{
3197 int x1 = e->dot1->x;
3198 int y1 = e->dot1->y;
3199 int x2 = e->dot2->x;
3200 int y2 = e->dot2->y;
3201 int xmin, xmax, ymin, ymax;
3202
3203 grid_to_screen(ds, g, x1, y1, &x1, &y1);
3204 grid_to_screen(ds, g, x2, y2, &x2, &y2);
3205 /* Allow extra margin for dots, and thickness of lines */
3206 xmin = min(x1, x2) - 2;
3207 xmax = max(x1, x2) + 2;
3208 ymin = min(y1, y2) - 2;
3209 ymax = max(y1, y2) + 2;
3210
3211 *x = xmin;
3212 *y = ymin;
3213 *w = xmax - xmin + 1;
3214 *h = ymax - ymin + 1;
3215}
3216
3217static void dot_bbox(game_drawstate *ds, grid *g, grid_dot *d,
3218 int *x, int *y, int *w, int *h)
3219{
3220 int x1, y1;
3221
3222 grid_to_screen(ds, g, d->x, d->y, &x1, &y1);
3223
3224 *x = x1 - 2;
3225 *y = y1 - 2;
3226 *w = 5;
3227 *h = 5;
3228}
3229
3230static const int loopy_line_redraw_phases[] = {
3231 COL_FAINT, COL_LINEUNKNOWN, COL_FOREGROUND, COL_HIGHLIGHT, COL_MISTAKE
3232};
3233#define NPHASES lenof(loopy_line_redraw_phases)
3234
3235static void game_redraw_line(drawing *dr, game_drawstate *ds,
3236 const game_state *state, int i, int phase)
3237{
3238 grid *g = state->game_grid;
3239 grid_edge *e = g->edges + i;
3240 int x1, x2, y1, y2;
3241 int line_colour;
3242
3243 if (state->line_errors[i])
3244 line_colour = COL_MISTAKE;
3245 else if (state->lines[i] == LINE_UNKNOWN)
3246 line_colour = COL_LINEUNKNOWN;
3247 else if (state->lines[i] == LINE_NO)
3248 line_colour = COL_FAINT;
3249 else if (ds->flashing)
3250 line_colour = COL_HIGHLIGHT;
3251 else
3252 line_colour = COL_FOREGROUND;
3253 if (line_colour != loopy_line_redraw_phases[phase])
3254 return;
3255
3256 /* Convert from grid to screen coordinates */
3257 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1);
3258 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
3259
3260 if (line_colour == COL_FAINT) {
3261 static int draw_faint_lines = -1;
3262 if (draw_faint_lines < 0) {
3263 char *env = getenv("LOOPY_FAINT_LINES");
3264 draw_faint_lines = (!env || (env[0] == 'y' ||
3265 env[0] == 'Y'));
3266 }
3267 if (draw_faint_lines)
3268 draw_line(dr, x1, y1, x2, y2, line_colour);
3269 } else {
3270 draw_thick_line(dr, 3.0,
3271 x1 + 0.5, y1 + 0.5,
3272 x2 + 0.5, y2 + 0.5,
3273 line_colour);
3274 }
3275}
3276
3277static void game_redraw_dot(drawing *dr, game_drawstate *ds,
3278 const game_state *state, int i)
3279{
3280 grid *g = state->game_grid;
3281 grid_dot *d = g->dots + i;
3282 int x, y;
3283
3284 grid_to_screen(ds, g, d->x, d->y, &x, &y);
3285 draw_circle(dr, x, y, 2, COL_FOREGROUND, COL_FOREGROUND);
3286}
3287
3288static int boxes_intersect(int x0, int y0, int w0, int h0,
3289 int x1, int y1, int w1, int h1)
3290{
3291 /*
3292 * Two intervals intersect iff neither is wholly on one side of
3293 * the other. Two boxes intersect iff their horizontal and
3294 * vertical intervals both intersect.
3295 */
3296 return (x0 < x1+w1 && x1 < x0+w0 && y0 < y1+h1 && y1 < y0+h0);
3297}
3298
3299static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
3300 const game_state *state,
3301 int x, int y, int w, int h)
3302{
3303 grid *g = state->game_grid;
3304 int i, phase;
3305 int bx, by, bw, bh;
3306
3307 clip(dr, x, y, w, h);
3308 draw_rect(dr, x, y, w, h, COL_BACKGROUND);
3309
3310 for (i = 0; i < g->num_faces; i++) {
3311 if (state->clues[i] >= 0) {
3312 face_text_bbox(ds, g, &g->faces[i], &bx, &by, &bw, &bh);
3313 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3314 game_redraw_clue(dr, ds, state, i);
3315 }
3316 }
3317 for (phase = 0; phase < NPHASES; phase++) {
3318 for (i = 0; i < g->num_edges; i++) {
3319 edge_bbox(ds, g, &g->edges[i], &bx, &by, &bw, &bh);
3320 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3321 game_redraw_line(dr, ds, state, i, phase);
3322 }
3323 }
3324 for (i = 0; i < g->num_dots; i++) {
3325 dot_bbox(ds, g, &g->dots[i], &bx, &by, &bw, &bh);
3326 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3327 game_redraw_dot(dr, ds, state, i);
3328 }
3329
3330 unclip(dr);
3331 draw_update(dr, x, y, w, h);
3332}
3333
3334static void game_redraw(drawing *dr, game_drawstate *ds,
3335 const game_state *oldstate, const game_state *state,
3336 int dir, const game_ui *ui,
3337 float animtime, float flashtime)
3338{
3339#define REDRAW_OBJECTS_LIMIT 16 /* Somewhat arbitrary tradeoff */
3340
3341 grid *g = state->game_grid;
3342 int border = BORDER(ds->tilesize);
3343 int i;
3344 int flash_changed;
3345 int redraw_everything = FALSE;
3346
3347 int edges[REDRAW_OBJECTS_LIMIT], nedges = 0;
3348 int faces[REDRAW_OBJECTS_LIMIT], nfaces = 0;
3349
3350 /* Redrawing is somewhat involved.
3351 *
3352 * An update can theoretically affect an arbitrary number of edges
3353 * (consider, for example, completing or breaking a cycle which doesn't
3354 * satisfy all the clues -- we'll switch many edges between error and
3355 * normal states). On the other hand, redrawing the whole grid takes a
3356 * while, making the game feel sluggish, and many updates are actually
3357 * quite well localized.
3358 *
3359 * This redraw algorithm attempts to cope with both situations gracefully
3360 * and correctly. For localized changes, we set a clip rectangle, fill
3361 * it with background, and then redraw (a plausible but conservative
3362 * guess at) the objects which intersect the rectangle; if several
3363 * objects need redrawing, we'll do them individually. However, if lots
3364 * of objects are affected, we'll just redraw everything.
3365 *
3366 * The reason for all of this is that it's just not safe to do the redraw
3367 * piecemeal. If you try to draw an antialiased diagonal line over
3368 * itself, you get a slightly thicker antialiased diagonal line, which
3369 * looks rather ugly after a while.
3370 *
3371 * So, we take two passes over the grid. The first attempts to work out
3372 * what needs doing, and the second actually does it.
3373 */
3374
3375 if (!ds->started) {
3376 redraw_everything = TRUE;
3377 /*
3378 * But we must still go through the upcoming loops, so that we
3379 * set up stuff in ds correctly for the initial redraw.
3380 */
3381 }
3382
3383 /* First, trundle through the faces. */
3384 for (i = 0; i < g->num_faces; i++) {
3385 grid_face *f = g->faces + i;
3386 int sides = f->order;
3387 int yes_order, no_order;
3388 int clue_mistake;
3389 int clue_satisfied;
3390 int n = state->clues[i];
3391 if (n < 0)
3392 continue;
3393
3394 yes_order = face_order(state, i, LINE_YES);
3395 if (state->exactly_one_loop) {
3396 /*
3397 * Special case: if the set of LINE_YES edges in the grid
3398 * consists of exactly one loop and nothing else, then we
3399 * switch to treating LINE_UNKNOWN the same as LINE_NO for
3400 * purposes of clue checking.
3401 *
3402 * This is because some people like to play Loopy without
3403 * using the right-click, i.e. never setting anything to
3404 * LINE_NO. Without this special case, if a person playing
3405 * in that style fills in what they think is a correct
3406 * solution loop but in fact it has an underfilled clue,
3407 * then we will display no victory flash and also no error
3408 * highlight explaining why not. With this special case,
3409 * we light up underfilled clues at the instant the loop
3410 * is closed. (Of course, *overfilled* clues are fine
3411 * either way.)
3412 *
3413 * (It might still be considered unfortunate that we can't
3414 * warn this style of player any earlier, if they make a
3415 * mistake very near the beginning which doesn't show up
3416 * until they close the last edge of the loop. One other
3417 * thing we _could_ do here is to treat any LINE_UNKNOWN
3418 * as LINE_NO if either of its endpoints has yes-degree 2,
3419 * reflecting the fact that setting that line to YES would
3420 * be an obvious error. But I don't think even that could
3421 * catch _all_ clue errors in a timely manner; I think
3422 * there are some that won't be displayed until the loop
3423 * is filled in, even so, and there's no way to avoid that
3424 * with complete reliability except to switch to being a
3425 * player who sets things to LINE_NO.)
3426 */
3427 no_order = sides - yes_order;
3428 } else {
3429 no_order = face_order(state, i, LINE_NO);
3430 }
3431
3432 clue_mistake = (yes_order > n || no_order > (sides-n));
3433 clue_satisfied = (yes_order == n && no_order == (sides-n));
3434
3435 if (clue_mistake != ds->clue_error[i] ||
3436 clue_satisfied != ds->clue_satisfied[i]) {
3437 ds->clue_error[i] = clue_mistake;
3438 ds->clue_satisfied[i] = clue_satisfied;
3439 if (nfaces == REDRAW_OBJECTS_LIMIT)
3440 redraw_everything = TRUE;
3441 else
3442 faces[nfaces++] = i;
3443 }
3444 }
3445
3446 /* Work out what the flash state needs to be. */
3447 if (flashtime > 0 &&
3448 (flashtime <= FLASH_TIME/3 ||
3449 flashtime >= FLASH_TIME*2/3)) {
3450 flash_changed = !ds->flashing;
3451 ds->flashing = TRUE;
3452 } else {
3453 flash_changed = ds->flashing;
3454 ds->flashing = FALSE;
3455 }
3456
3457 /* Now, trundle through the edges. */
3458 for (i = 0; i < g->num_edges; i++) {
3459 char new_ds =
3460 state->line_errors[i] ? DS_LINE_ERROR : state->lines[i];
3461 if (new_ds != ds->lines[i] ||
3462 (flash_changed && state->lines[i] == LINE_YES)) {
3463 ds->lines[i] = new_ds;
3464 if (nedges == REDRAW_OBJECTS_LIMIT)
3465 redraw_everything = TRUE;
3466 else
3467 edges[nedges++] = i;
3468 }
3469 }
3470
3471 /* Pass one is now done. Now we do the actual drawing. */
3472 if (redraw_everything) {
3473 int grid_width = g->highest_x - g->lowest_x;
3474 int grid_height = g->highest_y - g->lowest_y;
3475 int w = grid_width * ds->tilesize / g->tilesize;
3476 int h = grid_height * ds->tilesize / g->tilesize;
3477
3478 game_redraw_in_rect(dr, ds, state,
3479 0, 0, w + 2*border + 1, h + 2*border + 1);
3480 } else {
3481
3482 /* Right. Now we roll up our sleeves. */
3483
3484 for (i = 0; i < nfaces; i++) {
3485 grid_face *f = g->faces + faces[i];
3486 int x, y, w, h;
3487
3488 face_text_bbox(ds, g, f, &x, &y, &w, &h);
3489 game_redraw_in_rect(dr, ds, state, x, y, w, h);
3490 }
3491
3492 for (i = 0; i < nedges; i++) {
3493 grid_edge *e = g->edges + edges[i];
3494 int x, y, w, h;
3495
3496 edge_bbox(ds, g, e, &x, &y, &w, &h);
3497 game_redraw_in_rect(dr, ds, state, x, y, w, h);
3498 }
3499 }
3500
3501 ds->started = TRUE;
3502}
3503
3504static float game_flash_length(const game_state *oldstate,
3505 const game_state *newstate, int dir, game_ui *ui)
3506{
3507 if (!oldstate->solved && newstate->solved &&
3508 !oldstate->cheated && !newstate->cheated) {
3509 return FLASH_TIME;
3510 }
3511
3512 return 0.0F;
3513}
3514
3515static int game_status(const game_state *state)
3516{
3517 return state->solved ? +1 : 0;
3518}
3519
3520static void game_print_size(const game_params *params, float *x, float *y)
3521{
3522 int pw, ph;
3523
3524 /*
3525 * I'll use 7mm "squares" by default.
3526 */
3527 game_compute_size(params, 700, &pw, &ph);
3528 *x = pw / 100.0F;
3529 *y = ph / 100.0F;
3530}
3531
3532static void game_print(drawing *dr, const game_state *state, int tilesize)
3533{
3534 int ink = print_mono_colour(dr, 0);
3535 int i;
3536 game_drawstate ads, *ds = &ads;
3537 grid *g = state->game_grid;
3538
3539 ds->tilesize = tilesize;
3540 ds->textx = snewn(g->num_faces, int);
3541 ds->texty = snewn(g->num_faces, int);
3542 for (i = 0; i < g->num_faces; i++)
3543 ds->textx[i] = ds->texty[i] = -1;
3544
3545 for (i = 0; i < g->num_dots; i++) {
3546 int x, y;
3547 grid_to_screen(ds, g, g->dots[i].x, g->dots[i].y, &x, &y);
3548 draw_circle(dr, x, y, ds->tilesize / 15, ink, ink);
3549 }
3550
3551 /*
3552 * Clues.
3553 */
3554 for (i = 0; i < g->num_faces; i++) {
3555 grid_face *f = g->faces + i;
3556 int clue = state->clues[i];
3557 if (clue >= 0) {
3558 char c[20];
3559 int x, y;
3560 sprintf(c, "%d", state->clues[i]);
3561 face_text_pos(ds, g, f, &x, &y);
3562 draw_text(dr, x, y,
3563 FONT_VARIABLE, ds->tilesize / 2,
3564 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, c);
3565 }
3566 }
3567
3568 /*
3569 * Lines.
3570 */
3571 for (i = 0; i < g->num_edges; i++) {
3572 int thickness = (state->lines[i] == LINE_YES) ? 30 : 150;
3573 grid_edge *e = g->edges + i;
3574 int x1, y1, x2, y2;
3575 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1);
3576 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
3577 if (state->lines[i] == LINE_YES)
3578 {
3579 /* (dx, dy) points from (x1, y1) to (x2, y2).
3580 * The line is then "fattened" in a perpendicular
3581 * direction to create a thin rectangle. */
3582 double d = sqrt(SQ((double)x1 - x2) + SQ((double)y1 - y2));
3583 double dx = (x2 - x1) / d;
3584 double dy = (y2 - y1) / d;
3585 int points[8];
3586
3587 dx = (dx * ds->tilesize) / thickness;
3588 dy = (dy * ds->tilesize) / thickness;
3589 points[0] = x1 + (int)dy;
3590 points[1] = y1 - (int)dx;
3591 points[2] = x1 - (int)dy;
3592 points[3] = y1 + (int)dx;
3593 points[4] = x2 - (int)dy;
3594 points[5] = y2 + (int)dx;
3595 points[6] = x2 + (int)dy;
3596 points[7] = y2 - (int)dx;
3597 draw_polygon(dr, points, 4, ink, ink);
3598 }
3599 else
3600 {
3601 /* Draw a dotted line */
3602 int divisions = 6;
3603 int j;
3604 for (j = 1; j < divisions; j++) {
3605 /* Weighted average */
3606 int x = (x1 * (divisions -j) + x2 * j) / divisions;
3607 int y = (y1 * (divisions -j) + y2 * j) / divisions;
3608 draw_circle(dr, x, y, ds->tilesize / thickness, ink, ink);
3609 }
3610 }
3611 }
3612
3613 sfree(ds->textx);
3614 sfree(ds->texty);
3615}
3616
3617#ifdef COMBINED
3618#define thegame loopy
3619#endif
3620
3621const struct game thegame = {
3622 "Loopy", "games.loopy", "loopy",
3623 default_params,
3624 NULL, game_preset_menu,
3625 decode_params,
3626 encode_params,
3627 free_params,
3628 dup_params,
3629 TRUE, game_configure, custom_params,
3630 validate_params,
3631 new_game_desc,
3632 validate_desc,
3633 new_game,
3634 dup_game,
3635 free_game,
3636 1, solve_game,
3637 TRUE, game_can_format_as_text_now, game_text_format,
3638 new_ui,
3639 free_ui,
3640 encode_ui,
3641 decode_ui,
3642 game_changed_state,
3643 interpret_move,
3644 execute_move,
3645 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3646 game_colours,
3647 game_new_drawstate,
3648 game_free_drawstate,
3649 game_redraw,
3650 game_anim_length,
3651 game_flash_length,
3652 game_status,
3653 TRUE, FALSE, game_print_size, game_print,
3654 FALSE /* wants_statusbar */,
3655 FALSE, game_timing_state,
3656 0, /* mouse_priorities */
3657};
3658
3659#ifdef STANDALONE_SOLVER
3660
3661/*
3662 * Half-hearted standalone solver. It can't output the solution to
3663 * anything but a square puzzle, and it can't log the deductions
3664 * it makes either. But it can solve square puzzles, and more
3665 * importantly it can use its solver to grade the difficulty of
3666 * any puzzle you give it.
3667 */
3668
3669#include <stdarg.h>
3670
3671int main(int argc, char **argv)
3672{
3673 game_params *p;
3674 game_state *s;
3675 char *id = NULL, *desc, *err;
3676 int grade = FALSE;
3677 int ret, diff;
3678#if 0 /* verbose solver not supported here (yet) */
3679 int really_verbose = FALSE;
3680#endif
3681
3682 while (--argc > 0) {
3683 char *p = *++argv;
3684#if 0 /* verbose solver not supported here (yet) */
3685 if (!strcmp(p, "-v")) {
3686 really_verbose = TRUE;
3687 } else
3688#endif
3689 if (!strcmp(p, "-g")) {
3690 grade = TRUE;
3691 } else if (*p == '-') {
3692 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
3693 return 1;
3694 } else {
3695 id = p;
3696 }
3697 }
3698
3699 if (!id) {
3700 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
3701 return 1;
3702 }
3703
3704 desc = strchr(id, ':');
3705 if (!desc) {
3706 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
3707 return 1;
3708 }
3709 *desc++ = '\0';
3710
3711 p = default_params();
3712 decode_params(p, id);
3713 err = validate_desc(p, desc);
3714 if (err) {
3715 fprintf(stderr, "%s: %s\n", argv[0], err);
3716 return 1;
3717 }
3718 s = new_game(NULL, p, desc);
3719
3720 /*
3721 * When solving an Easy puzzle, we don't want to bother the
3722 * user with Hard-level deductions. For this reason, we grade
3723 * the puzzle internally before doing anything else.
3724 */
3725 ret = -1; /* placate optimiser */
3726 for (diff = 0; diff < DIFF_MAX; diff++) {
3727 solver_state *sstate_new;
3728 solver_state *sstate = new_solver_state((game_state *)s, diff);
3729
3730 sstate_new = solve_game_rec(sstate);
3731
3732 if (sstate_new->solver_status == SOLVER_MISTAKE)
3733 ret = 0;
3734 else if (sstate_new->solver_status == SOLVER_SOLVED)
3735 ret = 1;
3736 else
3737 ret = 2;
3738
3739 free_solver_state(sstate_new);
3740 free_solver_state(sstate);
3741
3742 if (ret < 2)
3743 break;
3744 }
3745
3746 if (diff == DIFF_MAX) {
3747 if (grade)
3748 printf("Difficulty rating: harder than Hard, or ambiguous\n");
3749 else
3750 printf("Unable to find a unique solution\n");
3751 } else {
3752 if (grade) {
3753 if (ret == 0)
3754 printf("Difficulty rating: impossible (no solution exists)\n");
3755 else if (ret == 1)
3756 printf("Difficulty rating: %s\n", diffnames[diff]);
3757 } else {
3758 solver_state *sstate_new;
3759 solver_state *sstate = new_solver_state((game_state *)s, diff);
3760
3761 /* If we supported a verbose solver, we'd set verbosity here */
3762
3763 sstate_new = solve_game_rec(sstate);
3764
3765 if (sstate_new->solver_status == SOLVER_MISTAKE)
3766 printf("Puzzle is inconsistent\n");
3767 else {
3768 assert(sstate_new->solver_status == SOLVER_SOLVED);
3769 if (s->grid_type == 0) {
3770 fputs(game_text_format(sstate_new->state), stdout);
3771 } else {
3772 printf("Unable to output non-square grids\n");
3773 }
3774 }
3775
3776 free_solver_state(sstate_new);
3777 free_solver_state(sstate);
3778 }
3779 }
3780
3781 return 0;
3782}
3783
3784#endif
3785
3786/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/loopy.html b/apps/plugins/puzzles/src/loopy.html
new file mode 100644
index 0000000000..6730e15664
--- /dev/null
+++ b/apps/plugins/puzzles/src/loopy.html
@@ -0,0 +1,69 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Loopy</title>
7<link rel="previous" href="map.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="inertia.html">
12</head>
13<body>
14<p><a href="map.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="inertia.html">Next</a></p>
15<h1><a name="C23"></a>Chapter 23: <a name="i0"></a>Loopy</h1>
16<p>
17You are given a grid of dots, marked with yellow lines to indicate which dots you are allowed to connect directly together. Your aim is to use some subset of those yellow lines to draw a single unbroken loop from dot to dot within the grid.
18</p>
19<p>
20Some of the spaces between the lines contain numbers. These numbers indicate how many of the lines around that space form part of the loop. The loop you draw must correctly satisfy all of these clues to be considered a correct solution.
21</p>
22<p>
23In the default mode, the dots are arranged in a grid of squares; however, you can also play on triangular or hexagonal grids, or even more exotic ones.
24</p>
25<p>
26Credit for the basic puzzle idea goes to <a name="i1"></a>Nikoli <a href="#p0">[10]</a>.
27</p>
28<p>
29Loopy was originally contributed to this collection by Mike Pinna, and subsequently enhanced to handle various types of non-square grid by Lambros Lambrou.
30</p>
31<p><a name="p0"></a>
32[10] <a href="http://www.nikoli.co.jp/en/puzzles/slitherlink.html"><code>http://www.nikoli.co.jp/en/puzzles/slitherlink.html</code></a> (beware of Flash)
33</p>
34<h2><a name="S23.1"></a>23.1 <a name="i2"></a>Loopy controls</h2>
35<p>
36Click the left mouse button on a yellow line to turn it black, indicating that you think it is part of the loop. Click again to turn the line yellow again (meaning you aren't sure yet).
37</p>
38<p>
39If you are sure that a particular line segment is <em>not</em> part of the loop, you can click the right mouse button to remove it completely. Again, clicking a second time will turn the line back to yellow.
40</p>
41<p>
42(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
43</p>
44<h2><a name="S23.2"></a>23.2 <a name="i3"></a>Loopy parameters</h2>
45<p>
46These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
47</p>
48<dl><dt>
49<em>Width</em>, <em>Height</em>
50</dt>
51<dd>
52Size of grid, measured in number of regions across and down. For square grids, it's clear how this is counted; for other types of grid you may have to think a bit to see how the dimensions are measured.
53</dd>
54<dt>
55<em>Grid type</em>
56</dt>
57<dd>
58Allows you to choose between a selection of types of tiling. Some have all the faces the same but may have multiple different types of vertex (e.g. the <em>Cairo</em> or <em>Kites</em> mode); others have all the vertices the same but may have different types of face (e.g. the <em>Great Hexagonal</em>). The square, triangular and honeycomb grids are fully regular, and have all their vertices <em>and</em> faces the same; this makes them the least confusing to play.
59</dd>
60<dt>
61<em>Difficulty</em>
62</dt>
63<dd>
64Controls the difficulty of the generated puzzle.
65</dd>
66</dl>
67
68<hr><address></address></body>
69</html>
diff --git a/apps/plugins/puzzles/src/magnets.R b/apps/plugins/puzzles/src/magnets.R
new file mode 100644
index 0000000000..e55e4746ee
--- /dev/null
+++ b/apps/plugins/puzzles/src/magnets.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3MAGNETS_EXTRA = laydomino
4
5magnets : [X] GTK COMMON magnets MAGNETS_EXTRA magnets-icon|no-icon
6
7magnets : [G] WINDOWS COMMON magnets MAGNETS_EXTRA magnets.res|noicon.res
8
9magnetssolver : [U] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE m.lib
10magnetssolver : [C] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE
11
12ALL += magnets[COMBINED] MAGNETS_EXTRA
13
14!begin am gtk
15GAMES += magnets
16!end
17
18!begin >list.c
19 A(magnets) \
20!end
21
22!begin >gamedesc.txt
23magnets:magnets.exe:Magnets:Magnet-placing puzzle:Place magnets to satisfy the clues and avoid like poles touching.
24!end
diff --git a/apps/plugins/puzzles/src/magnets.c b/apps/plugins/puzzles/src/magnets.c
new file mode 100644
index 0000000000..553ca0d0da
--- /dev/null
+++ b/apps/plugins/puzzles/src/magnets.c
@@ -0,0 +1,2641 @@
1/*
2 * magnets.c: implementation of janko.at 'magnets puzzle' game.
3 *
4 * http://64.233.179.104/translate_c?hl=en&u=http://www.janko.at/Raetsel/Magnete/Beispiel.htm
5 *
6 * Puzzle definition is just the size, and then the list of + (across then
7 * down) and - (across then down) present, then domino edges.
8 *
9 * An example:
10 *
11 * + 2 0 1
12 * +-----+
13 * 1|+ -| |1
14 * |-+-+ |
15 * 0|-|#| |1
16 * | +-+-|
17 * 2|+|- +|1
18 * +-----+
19 * 1 2 0 -
20 *
21 * 3x3:201,102,120,111,LRTT*BBLR
22 *
23 * 'Zotmeister' examples:
24 * 5x5:.2..1,3..1.,.2..2,2..2.,LRLRTTLRTBBT*BTTBLRBBLRLR
25 * 9x9:3.51...33,.2..23.13,..33.33.2,12...5.3.,**TLRTLR*,*TBLRBTLR,TBLRLRBTT,BLRTLRTBB,LRTB*TBLR,LRBLRBLRT,TTTLRLRTB,BBBTLRTB*,*LRBLRB**
26 *
27 * Janko 6x6 with solution:
28 * 6x6:322223,323132,232223,232223,LRTLRTTTBLRBBBTTLRLRBBLRTTLRTTBBLRBB
29 *
30 * janko 8x8:
31 * 8x8:34131323,23131334,43122323,21332243,LRTLRLRT,LRBTTTTB,LRTBBBBT,TTBTLRTB,BBTBTTBT,TTBTBBTB,BBTBLRBT,LRBLRLRB
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <assert.h>
38#include <ctype.h>
39#include <math.h>
40
41#include "puzzles.h"
42
43#ifdef STANDALONE_SOLVER
44int verbose = 0;
45#endif
46
47enum {
48 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
49 COL_TEXT, COL_ERROR, COL_CURSOR, COL_DONE,
50 COL_NEUTRAL, COL_NEGATIVE, COL_POSITIVE, COL_NOT,
51 NCOLOURS
52};
53
54/* Cell states. */
55enum { EMPTY = 0, NEUTRAL = EMPTY, POSITIVE = 1, NEGATIVE = 2 };
56
57#if defined DEBUGGING || defined STANDALONE_SOLVER
58static const char *cellnames[3] = { "neutral", "positive", "negative" };
59#define NAME(w) ( ((w) < 0 || (w) > 2) ? "(out of range)" : cellnames[(w)] )
60#endif
61
62#define GRID2CHAR(g) ( ((g) >= 0 && (g) <= 2) ? ".+-"[(g)] : '?' )
63#define CHAR2GRID(c) ( (c) == '+' ? POSITIVE : (c) == '-' ? NEGATIVE : NEUTRAL )
64
65#define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
66
67#define OPPOSITE(x) ( ((x)*2) % 3 ) /* 0 --> 0,
68 1 --> 2,
69 2 --> 4 --> 1 */
70
71#define FLASH_TIME 0.7F
72
73/* Macro ickery copied from slant.c */
74#define DIFFLIST(A) \
75 A(EASY,Easy,e) \
76 A(TRICKY,Tricky,t)
77#define ENUM(upper,title,lower) DIFF_ ## upper,
78#define TITLE(upper,title,lower) #title,
79#define ENCODE(upper,title,lower) #lower
80#define CONFIG(upper,title,lower) ":" #title
81enum { DIFFLIST(ENUM) DIFFCOUNT };
82static char const *const magnets_diffnames[] = { DIFFLIST(TITLE) "(count)" };
83static char const magnets_diffchars[] = DIFFLIST(ENCODE);
84#define DIFFCONFIG DIFFLIST(CONFIG)
85
86
87/* --------------------------------------------------------------- */
88/* Game parameter functions. */
89
90struct game_params {
91 int w, h, diff, stripclues;
92};
93
94#define DEFAULT_PRESET 2
95
96static const struct game_params magnets_presets[] = {
97 {6, 5, DIFF_EASY, 0},
98 {6, 5, DIFF_TRICKY, 0},
99 {6, 5, DIFF_TRICKY, 1},
100 {8, 7, DIFF_EASY, 0},
101 {8, 7, DIFF_TRICKY, 0},
102 {8, 7, DIFF_TRICKY, 1},
103 {10, 9, DIFF_TRICKY, 0},
104 {10, 9, DIFF_TRICKY, 1}
105};
106
107static game_params *default_params(void)
108{
109 game_params *ret = snew(game_params);
110
111 *ret = magnets_presets[DEFAULT_PRESET];
112
113 return ret;
114}
115
116static int game_fetch_preset(int i, char **name, game_params **params)
117{
118 game_params *ret;
119 char buf[64];
120
121 if (i < 0 || i >= lenof(magnets_presets)) return FALSE;
122
123 ret = default_params();
124 *ret = magnets_presets[i]; /* struct copy */
125 *params = ret;
126
127 sprintf(buf, "%dx%d %s%s",
128 magnets_presets[i].w, magnets_presets[i].h,
129 magnets_diffnames[magnets_presets[i].diff],
130 magnets_presets[i].stripclues ? ", strip clues" : "");
131 *name = dupstr(buf);
132
133 return TRUE;
134}
135
136static void free_params(game_params *params)
137{
138 sfree(params);
139}
140
141static game_params *dup_params(const game_params *params)
142{
143 game_params *ret = snew(game_params);
144 *ret = *params; /* structure copy */
145 return ret;
146}
147
148static void decode_params(game_params *ret, char const *string)
149{
150 ret->w = ret->h = atoi(string);
151 while (*string && isdigit((unsigned char) *string)) ++string;
152 if (*string == 'x') {
153 string++;
154 ret->h = atoi(string);
155 while (*string && isdigit((unsigned char)*string)) string++;
156 }
157
158 ret->diff = DIFF_EASY;
159 if (*string == 'd') {
160 int i;
161 string++;
162 for (i = 0; i < DIFFCOUNT; i++)
163 if (*string == magnets_diffchars[i])
164 ret->diff = i;
165 if (*string) string++;
166 }
167
168 ret->stripclues = 0;
169 if (*string == 'S') {
170 string++;
171 ret->stripclues = 1;
172 }
173}
174
175static char *encode_params(const game_params *params, int full)
176{
177 char buf[256];
178 sprintf(buf, "%dx%d", params->w, params->h);
179 if (full)
180 sprintf(buf + strlen(buf), "d%c%s",
181 magnets_diffchars[params->diff],
182 params->stripclues ? "S" : "");
183 return dupstr(buf);
184}
185
186static config_item *game_configure(const game_params *params)
187{
188 config_item *ret;
189 char buf[64];
190
191 ret = snewn(5, config_item);
192
193 ret[0].name = "Width";
194 ret[0].type = C_STRING;
195 sprintf(buf, "%d", params->w);
196 ret[0].sval = dupstr(buf);
197 ret[0].ival = 0;
198
199 ret[1].name = "Height";
200 ret[1].type = C_STRING;
201 sprintf(buf, "%d", params->h);
202 ret[1].sval = dupstr(buf);
203 ret[1].ival = 0;
204
205 ret[2].name = "Difficulty";
206 ret[2].type = C_CHOICES;
207 ret[2].sval = DIFFCONFIG;
208 ret[2].ival = params->diff;
209
210 ret[3].name = "Strip clues";
211 ret[3].type = C_BOOLEAN;
212 ret[3].sval = NULL;
213 ret[3].ival = params->stripclues;
214
215 ret[4].name = NULL;
216 ret[4].type = C_END;
217 ret[4].sval = NULL;
218 ret[4].ival = 0;
219
220 return ret;
221}
222
223static game_params *custom_params(const config_item *cfg)
224{
225 game_params *ret = snew(game_params);
226
227 ret->w = atoi(cfg[0].sval);
228 ret->h = atoi(cfg[1].sval);
229 ret->diff = cfg[2].ival;
230 ret->stripclues = cfg[3].ival;
231
232 return ret;
233}
234
235static char *validate_params(const game_params *params, int full)
236{
237 if (params->w < 2) return "Width must be at least one";
238 if (params->h < 2) return "Height must be at least one";
239 if (params->diff < 0 || params->diff >= DIFFCOUNT)
240 return "Unknown difficulty level";
241
242 return NULL;
243}
244
245/* --------------------------------------------------------------- */
246/* Game state allocation, deallocation. */
247
248struct game_common {
249 int *dominoes; /* size w*h, dominoes[i] points to other end of domino. */
250 int *rowcount; /* size 3*h, array of [plus, minus, neutral] counts */
251 int *colcount; /* size 3*w, ditto */
252 int refcount;
253};
254
255#define GS_ERROR 1
256#define GS_SET 2
257#define GS_NOTPOSITIVE 4
258#define GS_NOTNEGATIVE 8
259#define GS_NOTNEUTRAL 16
260#define GS_MARK 32
261
262#define GS_NOTMASK (GS_NOTPOSITIVE|GS_NOTNEGATIVE|GS_NOTNEUTRAL)
263
264#define NOTFLAG(w) ( (w) == NEUTRAL ? GS_NOTNEUTRAL : \
265 (w) == POSITIVE ? GS_NOTPOSITIVE : \
266 (w) == NEGATIVE ? GS_NOTNEGATIVE : \
267 0 )
268
269#define POSSIBLE(f,w) (!(state->flags[(f)] & NOTFLAG(w)))
270
271struct game_state {
272 int w, h, wh;
273 int *grid; /* size w*h, for cell state (pos/neg) */
274 unsigned int *flags; /* size w*h */
275 int solved, completed, numbered;
276 unsigned char *counts_done;
277
278 struct game_common *common; /* domino layout never changes. */
279};
280
281static void clear_state(game_state *ret)
282{
283 int i;
284
285 ret->solved = ret->completed = ret->numbered = 0;
286
287 memset(ret->common->rowcount, 0, ret->h*3*sizeof(int));
288 memset(ret->common->colcount, 0, ret->w*3*sizeof(int));
289 memset(ret->counts_done, 0, (ret->h + ret->w) * 2 * sizeof(unsigned char));
290
291 for (i = 0; i < ret->wh; i++) {
292 ret->grid[i] = EMPTY;
293 ret->flags[i] = 0;
294 ret->common->dominoes[i] = i;
295 }
296}
297
298static game_state *new_state(int w, int h)
299{
300 game_state *ret = snew(game_state);
301
302 memset(ret, 0, sizeof(game_state));
303 ret->w = w;
304 ret->h = h;
305 ret->wh = w*h;
306
307 ret->grid = snewn(ret->wh, int);
308 ret->flags = snewn(ret->wh, unsigned int);
309 ret->counts_done = snewn((ret->h + ret->w) * 2, unsigned char);
310
311 ret->common = snew(struct game_common);
312 ret->common->refcount = 1;
313
314 ret->common->dominoes = snewn(ret->wh, int);
315 ret->common->rowcount = snewn(ret->h*3, int);
316 ret->common->colcount = snewn(ret->w*3, int);
317
318 clear_state(ret);
319
320 return ret;
321}
322
323static game_state *dup_game(const game_state *src)
324{
325 game_state *dest = snew(game_state);
326
327 dest->w = src->w;
328 dest->h = src->h;
329 dest->wh = src->wh;
330
331 dest->solved = src->solved;
332 dest->completed = src->completed;
333 dest->numbered = src->numbered;
334
335 dest->common = src->common;
336 dest->common->refcount++;
337
338 dest->grid = snewn(dest->wh, int);
339 memcpy(dest->grid, src->grid, dest->wh*sizeof(int));
340
341 dest->counts_done = snewn((dest->h + dest->w) * 2, unsigned char);
342 memcpy(dest->counts_done, src->counts_done,
343 (dest->h + dest->w) * 2 * sizeof(unsigned char));
344
345 dest->flags = snewn(dest->wh, unsigned int);
346 memcpy(dest->flags, src->flags, dest->wh*sizeof(unsigned int));
347
348 return dest;
349}
350
351static void free_game(game_state *state)
352{
353 state->common->refcount--;
354 if (state->common->refcount == 0) {
355 sfree(state->common->dominoes);
356 sfree(state->common->rowcount);
357 sfree(state->common->colcount);
358 sfree(state->common);
359 }
360 sfree(state->counts_done);
361 sfree(state->flags);
362 sfree(state->grid);
363 sfree(state);
364}
365
366/* --------------------------------------------------------------- */
367/* Game generation and reading. */
368
369/* For a game of size w*h the game description is:
370 * w-sized string of column + numbers (L-R), or '.' for none
371 * semicolon
372 * h-sized string of row + numbers (T-B), or '.'
373 * semicolon
374 * w-sized string of column - numbers (L-R), or '.'
375 * semicolon
376 * h-sized string of row - numbers (T-B), or '.'
377 * semicolon
378 * w*h-sized string of 'L', 'R', 'U', 'D' for domino associations,
379 * or '*' for a black singleton square.
380 *
381 * for a total length of 2w + 2h + wh + 4.
382 */
383
384static char n2c(int num) { /* XXX cloned from singles.c */
385 if (num == -1)
386 return '.';
387 if (num < 10)
388 return '0' + num;
389 else if (num < 10+26)
390 return 'a' + num - 10;
391 else
392 return 'A' + num - 10 - 26;
393 return '?';
394}
395
396static int c2n(char c) { /* XXX cloned from singles.c */
397 if (isdigit((unsigned char)c))
398 return (int)(c - '0');
399 else if (c >= 'a' && c <= 'z')
400 return (int)(c - 'a' + 10);
401 else if (c >= 'A' && c <= 'Z')
402 return (int)(c - 'A' + 10 + 26);
403 return -1;
404}
405
406static const char *readrow(const char *desc, int n, int *array, int off,
407 const char **prob)
408{
409 int i, num;
410 char c;
411
412 for (i = 0; i < n; i++) {
413 c = *desc++;
414 if (c == 0) goto badchar;
415 if (c == '.')
416 num = -1;
417 else {
418 num = c2n(c);
419 if (num < 0) goto badchar;
420 }
421 array[i*3+off] = num;
422 }
423 c = *desc++;
424 if (c != ',') goto badchar;
425 return desc;
426
427badchar:
428 *prob = (c == 0) ?
429 "Game description too short" :
430 "Game description contained unexpected characters";
431 return NULL;
432}
433
434static game_state *new_game_int(const game_params *params, const char *desc,
435 const char **prob)
436{
437 game_state *state = new_state(params->w, params->h);
438 int x, y, idx, *count;
439 char c;
440
441 *prob = NULL;
442
443 /* top row, left-to-right */
444 desc = readrow(desc, state->w, state->common->colcount, POSITIVE, prob);
445 if (*prob) goto done;
446
447 /* left column, top-to-bottom */
448 desc = readrow(desc, state->h, state->common->rowcount, POSITIVE, prob);
449 if (*prob) goto done;
450
451 /* bottom row, left-to-right */
452 desc = readrow(desc, state->w, state->common->colcount, NEGATIVE, prob);
453 if (*prob) goto done;
454
455 /* right column, top-to-bottom */
456 desc = readrow(desc, state->h, state->common->rowcount, NEGATIVE, prob);
457 if (*prob) goto done;
458
459 /* Add neutral counts (== size - pos - neg) to columns and rows.
460 * Any singleton cells will just be treated as permanently neutral. */
461 count = state->common->colcount;
462 for (x = 0; x < state->w; x++) {
463 if (count[x*3+POSITIVE] < 0 || count[x*3+NEGATIVE] < 0)
464 count[x*3+NEUTRAL] = -1;
465 else {
466 count[x*3+NEUTRAL] =
467 state->h - count[x*3+POSITIVE] - count[x*3+NEGATIVE];
468 if (count[x*3+NEUTRAL] < 0) {
469 *prob = "Column counts inconsistent";
470 goto done;
471 }
472 }
473 }
474 count = state->common->rowcount;
475 for (y = 0; y < state->h; y++) {
476 if (count[y*3+POSITIVE] < 0 || count[y*3+NEGATIVE] < 0)
477 count[y*3+NEUTRAL] = -1;
478 else {
479 count[y*3+NEUTRAL] =
480 state->w - count[y*3+POSITIVE] - count[y*3+NEGATIVE];
481 if (count[y*3+NEUTRAL] < 0) {
482 *prob = "Row counts inconsistent";
483 goto done;
484 }
485 }
486 }
487
488
489 for (y = 0; y < state->h; y++) {
490 for (x = 0; x < state->w; x++) {
491 idx = y*state->w + x;
492nextchar:
493 c = *desc++;
494
495 if (c == 'L') /* this square is LHS of a domino */
496 state->common->dominoes[idx] = idx+1;
497 else if (c == 'R') /* ... RHS of a domino */
498 state->common->dominoes[idx] = idx-1;
499 else if (c == 'T') /* ... top of a domino */
500 state->common->dominoes[idx] = idx+state->w;
501 else if (c == 'B') /* ... bottom of a domino */
502 state->common->dominoes[idx] = idx-state->w;
503 else if (c == '*') /* singleton */
504 state->common->dominoes[idx] = idx;
505 else if (c == ',') /* spacer, ignore */
506 goto nextchar;
507 else goto badchar;
508 }
509 }
510
511 /* Check dominoes as input are sensibly consistent
512 * (i.e. each end points to the other) */
513 for (idx = 0; idx < state->wh; idx++) {
514 if (state->common->dominoes[idx] < 0 ||
515 state->common->dominoes[idx] > state->wh ||
516 state->common->dominoes[state->common->dominoes[idx]] != idx) {
517 *prob = "Domino descriptions inconsistent";
518 goto done;
519 }
520 if (state->common->dominoes[idx] == idx) {
521 state->grid[idx] = NEUTRAL;
522 state->flags[idx] |= GS_SET;
523 }
524 }
525 /* Success. */
526 state->numbered = 1;
527 goto done;
528
529badchar:
530 *prob = (c == 0) ?
531 "Game description too short" :
532 "Game description contained unexpected characters";
533
534done:
535 if (*prob) {
536 free_game(state);
537 return NULL;
538 }
539 return state;
540}
541
542static char *validate_desc(const game_params *params, const char *desc)
543{
544 const char *prob;
545 game_state *st = new_game_int(params, desc, &prob);
546 if (!st) return (char*)prob;
547 free_game(st);
548 return NULL;
549}
550
551static game_state *new_game(midend *me, const game_params *params,
552 const char *desc)
553{
554 const char *prob;
555 game_state *st = new_game_int(params, desc, &prob);
556 assert(st);
557 return st;
558}
559
560static char *generate_desc(game_state *new)
561{
562 int x, y, idx, other, w = new->w, h = new->h;
563 char *desc = snewn(new->wh + 2*(w + h) + 5, char), *p = desc;
564
565 for (x = 0; x < w; x++) *p++ = n2c(new->common->colcount[x*3+POSITIVE]);
566 *p++ = ',';
567 for (y = 0; y < h; y++) *p++ = n2c(new->common->rowcount[y*3+POSITIVE]);
568 *p++ = ',';
569
570 for (x = 0; x < w; x++) *p++ = n2c(new->common->colcount[x*3+NEGATIVE]);
571 *p++ = ',';
572 for (y = 0; y < h; y++) *p++ = n2c(new->common->rowcount[y*3+NEGATIVE]);
573 *p++ = ',';
574
575 for (y = 0; y < h; y++) {
576 for (x = 0; x < w; x++) {
577 idx = y*w + x;
578 other = new->common->dominoes[idx];
579
580 if (other == idx) *p++ = '*';
581 else if (other == idx+1) *p++ = 'L';
582 else if (other == idx-1) *p++ = 'R';
583 else if (other == idx+w) *p++ = 'T';
584 else if (other == idx-w) *p++ = 'B';
585 else assert(!"mad domino orientation");
586 }
587 }
588 *p = '\0';
589
590 return desc;
591}
592
593static void game_text_hborder(const game_state *state, char **p_r)
594{
595 char *p = *p_r;
596 int x;
597
598 *p++ = ' ';
599 *p++ = '+';
600 for (x = 0; x < state->w*2-1; x++) *p++ = '-';
601 *p++ = '+';
602 *p++ = '\n';
603
604 *p_r = p;
605}
606
607static int game_can_format_as_text_now(const game_params *params)
608{
609 return TRUE;
610}
611
612static char *game_text_format(const game_state *state)
613{
614 int len, x, y, i;
615 char *ret, *p;
616
617 len = ((state->w*2)+4) * ((state->h*2)+4) + 2;
618 p = ret = snewn(len, char);
619
620 /* top row: '+' then column totals for plus. */
621 *p++ = '+';
622 for (x = 0; x < state->w; x++) {
623 *p++ = ' ';
624 *p++ = n2c(state->common->colcount[x*3+POSITIVE]);
625 }
626 *p++ = '\n';
627
628 /* top border. */
629 game_text_hborder(state, &p);
630
631 for (y = 0; y < state->h; y++) {
632 *p++ = n2c(state->common->rowcount[y*3+POSITIVE]);
633 *p++ = '|';
634 for (x = 0; x < state->w; x++) {
635 i = y*state->w+x;
636 *p++ = state->common->dominoes[i] == i ? '#' :
637 state->grid[i] == POSITIVE ? '+' :
638 state->grid[i] == NEGATIVE ? '-' :
639 state->flags[i] & GS_SET ? '*' : ' ';
640 if (x < (state->w-1))
641 *p++ = state->common->dominoes[i] == i+1 ? ' ' : '|';
642 }
643 *p++ = '|';
644 *p++ = n2c(state->common->rowcount[y*3+NEGATIVE]);
645 *p++ = '\n';
646
647 if (y < (state->h-1)) {
648 *p++ = ' ';
649 *p++ = '|';
650 for (x = 0; x < state->w; x++) {
651 i = y*state->w+x;
652 *p++ = state->common->dominoes[i] == i+state->w ? ' ' : '-';
653 if (x < (state->w-1))
654 *p++ = '+';
655 }
656 *p++ = '|';
657 *p++ = '\n';
658 }
659 }
660
661 /* bottom border. */
662 game_text_hborder(state, &p);
663
664 /* bottom row: column totals for minus then '-'. */
665 *p++ = ' ';
666 for (x = 0; x < state->w; x++) {
667 *p++ = ' ';
668 *p++ = n2c(state->common->colcount[x*3+NEGATIVE]);
669 }
670 *p++ = ' ';
671 *p++ = '-';
672 *p++ = '\n';
673 *p++ = '\0';
674
675 return ret;
676}
677
678static void game_debug(game_state *state, const char *desc)
679{
680 char *fmt = game_text_format(state);
681 debug(("%s:\n%s\n", desc, fmt));
682 sfree(fmt);
683}
684
685enum { ROW, COLUMN };
686
687typedef struct rowcol {
688 int i, di, n, roworcol, num;
689 int *targets;
690 const char *name;
691} rowcol;
692
693static rowcol mkrowcol(const game_state *state, int num, int roworcol)
694{
695 rowcol rc;
696
697 rc.roworcol = roworcol;
698 rc.num = num;
699
700 if (roworcol == ROW) {
701 rc.i = num * state->w;
702 rc.di = 1;
703 rc.n = state->w;
704 rc.targets = &(state->common->rowcount[num*3]);
705 rc.name = "row";
706 } else if (roworcol == COLUMN) {
707 rc.i = num;
708 rc.di = state->w;
709 rc.n = state->h;
710 rc.targets = &(state->common->colcount[num*3]);
711 rc.name = "column";
712 } else {
713 assert(!"unknown roworcol");
714 }
715 return rc;
716}
717
718static int count_rowcol(const game_state *state, int num, int roworcol,
719 int which)
720{
721 int i, count = 0;
722 rowcol rc = mkrowcol(state, num, roworcol);
723
724 for (i = 0; i < rc.n; i++, rc.i += rc.di) {
725 if (which < 0) {
726 if (state->grid[rc.i] == EMPTY &&
727 !(state->flags[rc.i] & GS_SET))
728 count++;
729 } else if (state->grid[rc.i] == which)
730 count++;
731 }
732 return count;
733}
734
735static void check_rowcol(game_state *state, int num, int roworcol, int which,
736 int *wrong, int *incomplete)
737{
738 int count, target = mkrowcol(state, num, roworcol).targets[which];
739
740 if (target == -1) return; /* no number to check against. */
741
742 count = count_rowcol(state, num, roworcol, which);
743 if (count < target) *incomplete = 1;
744 if (count > target) *wrong = 1;
745}
746
747static int check_completion(game_state *state)
748{
749 int i, j, x, y, idx, w = state->w, h = state->h;
750 int which = POSITIVE, wrong = 0, incomplete = 0;
751
752 /* Check row and column counts for magnets. */
753 for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) {
754 for (i = 0; i < w; i++)
755 check_rowcol(state, i, COLUMN, which, &wrong, &incomplete);
756
757 for (i = 0; i < h; i++)
758 check_rowcol(state, i, ROW, which, &wrong, &incomplete);
759 }
760 /* Check each domino has been filled, and that we don't have
761 * touching identical terminals. */
762 for (i = 0; i < state->wh; i++) state->flags[i] &= ~GS_ERROR;
763 for (x = 0; x < w; x++) {
764 for (y = 0; y < h; y++) {
765 idx = y*w + x;
766 if (state->common->dominoes[idx] == idx)
767 continue; /* no domino here */
768
769 if (!(state->flags[idx] & GS_SET))
770 incomplete = 1;
771
772 which = state->grid[idx];
773 if (which != NEUTRAL) {
774#define CHECK(xx,yy) do { \
775 if (INGRID(state,xx,yy) && \
776 (state->grid[(yy)*w+(xx)] == which)) { \
777 wrong = 1; \
778 state->flags[(yy)*w+(xx)] |= GS_ERROR; \
779 state->flags[y*w+x] |= GS_ERROR; \
780 } \
781} while(0)
782 CHECK(x,y-1);
783 CHECK(x,y+1);
784 CHECK(x-1,y);
785 CHECK(x+1,y);
786#undef CHECK
787 }
788 }
789 }
790 return wrong ? -1 : incomplete ? 0 : 1;
791}
792
793static const int dx[4] = {-1, 1, 0, 0};
794static const int dy[4] = {0, 0, -1, 1};
795
796static void solve_clearflags(game_state *state)
797{
798 int i;
799
800 for (i = 0; i < state->wh; i++) {
801 state->flags[i] &= ~GS_NOTMASK;
802 if (state->common->dominoes[i] != i)
803 state->flags[i] &= ~GS_SET;
804 }
805}
806
807/* Knowing a given cell cannot be a certain colour also tells us
808 * something about the other cell in that domino. */
809static int solve_unflag(game_state *state, int i, int which,
810 const char *why, rowcol *rc)
811{
812 int ii, ret = 0;
813#if defined DEBUGGING || defined STANDALONE_SOLVER
814 int w = state->w;
815#endif
816
817 assert(i >= 0 && i < state->wh);
818 ii = state->common->dominoes[i];
819 if (ii == i) return 0;
820
821 if (rc)
822 debug(("solve_unflag: (%d,%d) for %s %d", i%w, i/w, rc->name, rc->num));
823
824 if ((state->flags[i] & GS_SET) && (state->grid[i] == which)) {
825 debug(("solve_unflag: (%d,%d) already %s, cannot unflag (for %s).",
826 i%w, i/w, NAME(which), why));
827 return -1;
828 }
829 if ((state->flags[ii] & GS_SET) && (state->grid[ii] == OPPOSITE(which))) {
830 debug(("solve_unflag: (%d,%d) opposite already %s, cannot unflag (for %s).",
831 ii%w, ii/w, NAME(OPPOSITE(which)), why));
832 return -1;
833 }
834 if (POSSIBLE(i, which)) {
835 state->flags[i] |= NOTFLAG(which);
836 ret++;
837 debug(("solve_unflag: (%d,%d) CANNOT be %s (%s)",
838 i%w, i/w, NAME(which), why));
839 }
840 if (POSSIBLE(ii, OPPOSITE(which))) {
841 state->flags[ii] |= NOTFLAG(OPPOSITE(which));
842 ret++;
843 debug(("solve_unflag: (%d,%d) CANNOT be %s (%s, other half)",
844 ii%w, ii/w, NAME(OPPOSITE(which)), why));
845 }
846#ifdef STANDALONE_SOLVER
847 if (verbose && ret) {
848 printf("(%d,%d)", i%w, i/w);
849 if (rc) printf(" in %s %d", rc->name, rc->num);
850 printf(" cannot be %s (%s); opposite (%d,%d) not %s.\n",
851 NAME(which), why, ii%w, ii/w, NAME(OPPOSITE(which)));
852 }
853#endif
854 return ret;
855}
856
857static int solve_unflag_surrounds(game_state *state, int i, int which)
858{
859 int x = i%state->w, y = i/state->w, xx, yy, j, ii;
860
861 assert(INGRID(state, x, y));
862
863 for (j = 0; j < 4; j++) {
864 xx = x+dx[j]; yy = y+dy[j];
865 if (!INGRID(state, xx, yy)) continue;
866
867 ii = yy*state->w+xx;
868 if (solve_unflag(state, ii, which, "adjacent to set cell", NULL) < 0)
869 return -1;
870 }
871 return 0;
872}
873
874/* Sets a cell to a particular colour, and also perform other
875 * housekeeping around that. */
876static int solve_set(game_state *state, int i, int which,
877 const char *why, rowcol *rc)
878{
879 int ii;
880#if defined DEBUGGING || defined STANDALONE_SOLVER
881 int w = state->w;
882#endif
883
884 ii = state->common->dominoes[i];
885
886 if (state->flags[i] & GS_SET) {
887 if (state->grid[i] == which) {
888 return 0; /* was already set and held, do nothing. */
889 } else {
890 debug(("solve_set: (%d,%d) is held and %s, cannot set to %s",
891 i%w, i/w, NAME(state->grid[i]), NAME(which)));
892 return -1;
893 }
894 }
895 if ((state->flags[ii] & GS_SET) && state->grid[ii] != OPPOSITE(which)) {
896 debug(("solve_set: (%d,%d) opposite is held and %s, cannot set to %s",
897 ii%w, ii/w, NAME(state->grid[ii]), NAME(OPPOSITE(which))));
898 return -1;
899 }
900 if (!POSSIBLE(i, which)) {
901 debug(("solve_set: (%d,%d) NOT %s, cannot set.", i%w, i/w, NAME(which)));
902 return -1;
903 }
904 if (!POSSIBLE(ii, OPPOSITE(which))) {
905 debug(("solve_set: (%d,%d) NOT %s, cannot set (%d,%d).",
906 ii%w, ii/w, NAME(OPPOSITE(which)), i%w, i/w));
907 return -1;
908 }
909
910#ifdef STANDALONE_SOLVER
911 if (verbose) {
912 printf("(%d,%d)", i%w, i/w);
913 if (rc) printf(" in %s %d", rc->name, rc->num);
914 printf(" set to %s (%s), opposite (%d,%d) set to %s.\n",
915 NAME(which), why, ii%w, ii/w, NAME(OPPOSITE(which)));
916 }
917#endif
918 if (rc)
919 debug(("solve_set: (%d,%d) for %s %d", i%w, i/w, rc->name, rc->num));
920 debug(("solve_set: (%d,%d) setting to %s (%s), surrounds first:",
921 i%w, i/w, NAME(which), why));
922
923 if (which != NEUTRAL) {
924 if (solve_unflag_surrounds(state, i, which) < 0)
925 return -1;
926 if (solve_unflag_surrounds(state, ii, OPPOSITE(which)) < 0)
927 return -1;
928 }
929
930 state->grid[i] = which;
931 state->grid[ii] = OPPOSITE(which);
932
933 state->flags[i] |= GS_SET;
934 state->flags[ii] |= GS_SET;
935
936 debug(("solve_set: (%d,%d) set to %s (%s)", i%w, i/w, NAME(which), why));
937
938 return 1;
939}
940
941/* counts should be int[4]. */
942static void solve_counts(game_state *state, rowcol rc, int *counts, int *unset)
943{
944 int i, j, which;
945
946 assert(counts);
947 for (i = 0; i < 4; i++) {
948 counts[i] = 0;
949 if (unset) unset[i] = 0;
950 }
951
952 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
953 if (state->flags[i] & GS_SET) {
954 assert(state->grid[i] < 3);
955 counts[state->grid[i]]++;
956 } else if (unset) {
957 for (which = 0; which <= 2; which++) {
958 if (POSSIBLE(i, which))
959 unset[which]++;
960 }
961 }
962 }
963}
964
965static int solve_checkfull(game_state *state, rowcol rc, int *counts)
966{
967 int starti = rc.i, j, which, didsth = 0, target;
968 int unset[4];
969
970 assert(state->numbered); /* only useful (should only be called) if numbered. */
971
972 solve_counts(state, rc, counts, unset);
973
974 for (which = 0; which <= 2; which++) {
975 target = rc.targets[which];
976 if (target == -1) continue;
977
978 /*debug(("%s %d for %s: target %d, count %d, unset %d",
979 rc.name, rc.num, NAME(which),
980 target, counts[which], unset[which]));*/
981
982 if (target < counts[which]) {
983 debug(("%s %d has too many (%d) %s squares (target %d), impossible!",
984 rc.name, rc.num, counts[which], NAME(which), target));
985 return -1;
986 }
987 if (target == counts[which]) {
988 /* We have the correct no. of the colour in this row/column
989 * already; unflag all the rest. */
990 for (rc.i = starti, j = 0; j < rc.n; rc.i += rc.di, j++) {
991 if (state->flags[rc.i] & GS_SET) continue;
992 if (!POSSIBLE(rc.i, which)) continue;
993
994 if (solve_unflag(state, rc.i, which, "row/col full", &rc) < 0)
995 return -1;
996 didsth = 1;
997 }
998 } else if ((target - counts[which]) == unset[which]) {
999 /* We need all the remaining unset squares for this colour;
1000 * set them all. */
1001 for (rc.i = starti, j = 0; j < rc.n; rc.i += rc.di, j++) {
1002 if (state->flags[rc.i] & GS_SET) continue;
1003 if (!POSSIBLE(rc.i, which)) continue;
1004
1005 if (solve_set(state, rc.i, which, "row/col needs all unset", &rc) < 0)
1006 return -1;
1007 didsth = 1;
1008 }
1009 }
1010 }
1011 return didsth;
1012}
1013
1014static int solve_startflags(game_state *state)
1015{
1016 int x, y, i;
1017
1018 for (x = 0; x < state->w; x++) {
1019 for (y = 0; y < state->h; y++) {
1020 i = y*state->w+x;
1021 if (state->common->dominoes[i] == i) continue;
1022 if (state->grid[i] != NEUTRAL ||
1023 state->flags[i] & GS_SET) {
1024 if (solve_set(state, i, state->grid[i], "initial set-and-hold", NULL) < 0)
1025 return -1;
1026 }
1027 }
1028 }
1029 return 0;
1030}
1031
1032typedef int (*rowcolfn)(game_state *state, rowcol rc, int *counts);
1033
1034static int solve_rowcols(game_state *state, rowcolfn fn)
1035{
1036 int x, y, didsth = 0, ret;
1037 rowcol rc;
1038 int counts[4];
1039
1040 for (x = 0; x < state->w; x++) {
1041 rc = mkrowcol(state, x, COLUMN);
1042 solve_counts(state, rc, counts, NULL);
1043
1044 ret = fn(state, rc, counts);
1045 if (ret < 0) return ret;
1046 didsth += ret;
1047 }
1048 for (y = 0; y < state->h; y++) {
1049 rc = mkrowcol(state, y, ROW);
1050 solve_counts(state, rc, counts, NULL);
1051
1052 ret = fn(state, rc, counts);
1053 if (ret < 0) return ret;
1054 didsth += ret;
1055 }
1056 return didsth;
1057}
1058
1059static int solve_force(game_state *state)
1060{
1061 int i, which, didsth = 0;
1062 unsigned long f;
1063
1064 for (i = 0; i < state->wh; i++) {
1065 if (state->flags[i] & GS_SET) continue;
1066 if (state->common->dominoes[i] == i) continue;
1067
1068 f = state->flags[i] & GS_NOTMASK;
1069 which = -1;
1070 if (f == (GS_NOTPOSITIVE|GS_NOTNEGATIVE))
1071 which = NEUTRAL;
1072 if (f == (GS_NOTPOSITIVE|GS_NOTNEUTRAL))
1073 which = NEGATIVE;
1074 if (f == (GS_NOTNEGATIVE|GS_NOTNEUTRAL))
1075 which = POSITIVE;
1076 if (which != -1) {
1077 if (solve_set(state, i, which, "forced by flags", NULL) < 0)
1078 return -1;
1079 didsth = 1;
1080 }
1081 }
1082 return didsth;
1083}
1084
1085static int solve_neither(game_state *state)
1086{
1087 int i, j, didsth = 0;
1088
1089 for (i = 0; i < state->wh; i++) {
1090 if (state->flags[i] & GS_SET) continue;
1091 j = state->common->dominoes[i];
1092 if (i == j) continue;
1093
1094 if (((state->flags[i] & GS_NOTPOSITIVE) &&
1095 (state->flags[j] & GS_NOTPOSITIVE)) ||
1096 ((state->flags[i] & GS_NOTNEGATIVE) &&
1097 (state->flags[j] & GS_NOTNEGATIVE))) {
1098 if (solve_set(state, i, NEUTRAL, "neither tile magnet", NULL) < 0)
1099 return -1;
1100 didsth = 1;
1101 }
1102 }
1103 return didsth;
1104}
1105
1106static int solve_advancedfull(game_state *state, rowcol rc, int *counts)
1107{
1108 int i, j, nfound = 0, clearpos = 0, clearneg = 0, ret = 0;
1109
1110 /* For this row/col, look for a domino entirely within the row where
1111 * both ends can only be + or - (but isn't held).
1112 * The +/- counts can thus be decremented by 1 each, and the 'unset'
1113 * count by 2.
1114 *
1115 * Once that's done for all such dominoes (and they're marked), try
1116 * and made usual deductions about rest of the row based on new totals. */
1117
1118 if (rc.targets[POSITIVE] == -1 && rc.targets[NEGATIVE] == -1)
1119 return 0; /* don't have a target for either colour, nothing to do. */
1120 if ((rc.targets[POSITIVE] >= 0 && counts[POSITIVE] == rc.targets[POSITIVE]) &&
1121 (rc.targets[NEGATIVE] >= 0 && counts[NEGATIVE] == rc.targets[NEGATIVE]))
1122 return 0; /* both colours are full up already, nothing to do. */
1123
1124 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++)
1125 state->flags[i] &= ~GS_MARK;
1126
1127 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1128 if (state->flags[i] & GS_SET) continue;
1129
1130 /* We're looking for a domino in our row/col, thus if
1131 * dominoes[i] -> i+di we've found one. */
1132 if (state->common->dominoes[i] != i+rc.di) continue;
1133
1134 /* We need both squares of this domino to be either + or -
1135 * (i.e. both NOTNEUTRAL only). */
1136 if (((state->flags[i] & GS_NOTMASK) != GS_NOTNEUTRAL) ||
1137 ((state->flags[i+rc.di] & GS_NOTMASK) != GS_NOTNEUTRAL))
1138 continue;
1139
1140 debug(("Domino in %s %d at (%d,%d) must be polarised.",
1141 rc.name, rc.num, i%state->w, i/state->w));
1142 state->flags[i] |= GS_MARK;
1143 state->flags[i+rc.di] |= GS_MARK;
1144 nfound++;
1145 }
1146 if (nfound == 0) return 0;
1147
1148 /* nfound is #dominoes we matched, which will all be marked. */
1149 counts[POSITIVE] += nfound;
1150 counts[NEGATIVE] += nfound;
1151
1152 if (rc.targets[POSITIVE] >= 0 && counts[POSITIVE] == rc.targets[POSITIVE]) {
1153 debug(("%s %d has now filled POSITIVE:", rc.name, rc.num));
1154 clearpos = 1;
1155 }
1156 if (rc.targets[NEGATIVE] >= 0 && counts[NEGATIVE] == rc.targets[NEGATIVE]) {
1157 debug(("%s %d has now filled NEGATIVE:", rc.name, rc.num));
1158 clearneg = 1;
1159 }
1160
1161 if (!clearpos && !clearneg) return 0;
1162
1163 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1164 if (state->flags[i] & GS_SET) continue;
1165 if (state->flags[i] & GS_MARK) continue;
1166
1167 if (clearpos && !(state->flags[i] & GS_NOTPOSITIVE)) {
1168 if (solve_unflag(state, i, POSITIVE, "row/col full (+ve) [tricky]", &rc) < 0)
1169 return -1;
1170 ret++;
1171 }
1172 if (clearneg && !(state->flags[i] & GS_NOTNEGATIVE)) {
1173 if (solve_unflag(state, i, NEGATIVE, "row/col full (-ve) [tricky]", &rc) < 0)
1174 return -1;
1175 ret++;
1176 }
1177 }
1178
1179 return ret;
1180}
1181
1182/* If we only have one neutral still to place on a row/column then no
1183 dominoes entirely in that row/column can be neutral. */
1184static int solve_nonneutral(game_state *state, rowcol rc, int *counts)
1185{
1186 int i, j, ret = 0;
1187
1188 if (rc.targets[NEUTRAL] != counts[NEUTRAL]+1)
1189 return 0;
1190
1191 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1192 if (state->flags[i] & GS_SET) continue;
1193 if (state->common->dominoes[i] != i+rc.di) continue;
1194
1195 if (!(state->flags[i] & GS_NOTNEUTRAL)) {
1196 if (solve_unflag(state, i, NEUTRAL, "single neutral in row/col [tricky]", &rc) < 0)
1197 return -1;
1198 ret++;
1199 }
1200 }
1201 return ret;
1202}
1203
1204/* If we need to fill all unfilled cells with +-, and we need 1 more of
1205 * one than the other, and we have a single odd-numbered region of unfilled
1206 * cells, that odd-numbered region must start and end with the extra number. */
1207static int solve_oddlength(game_state *state, rowcol rc, int *counts)
1208{
1209 int i, j, ret = 0, extra, tpos, tneg;
1210 int start = -1, length = 0, inempty = 0, startodd = -1;
1211
1212 /* need zero neutral cells still to find... */
1213 if (rc.targets[NEUTRAL] != counts[NEUTRAL])
1214 return 0;
1215
1216 /* ...and #positive and #negative to differ by one. */
1217 tpos = rc.targets[POSITIVE] - counts[POSITIVE];
1218 tneg = rc.targets[NEGATIVE] - counts[NEGATIVE];
1219 if (tpos == tneg+1)
1220 extra = POSITIVE;
1221 else if (tneg == tpos+1)
1222 extra = NEGATIVE;
1223 else return 0;
1224
1225 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1226 if (state->flags[i] & GS_SET) {
1227 if (inempty) {
1228 if (length % 2) {
1229 /* we've just finished an odd-length section. */
1230 if (startodd != -1) goto twoodd;
1231 startodd = start;
1232 }
1233 inempty = 0;
1234 }
1235 } else {
1236 if (inempty)
1237 length++;
1238 else {
1239 start = i;
1240 length = 1;
1241 inempty = 1;
1242 }
1243 }
1244 }
1245 if (inempty && (length % 2)) {
1246 if (startodd != -1) goto twoodd;
1247 startodd = start;
1248 }
1249 if (startodd != -1)
1250 ret = solve_set(state, startodd, extra, "odd-length section start", &rc);
1251
1252 return ret;
1253
1254twoodd:
1255 debug(("%s %d has >1 odd-length sections, starting at %d,%d and %d,%d.",
1256 rc.name, rc.num,
1257 startodd%state->w, startodd/state->w,
1258 start%state->w, start/state->w));
1259 return 0;
1260}
1261
1262/* Count the number of remaining empty dominoes in any row/col.
1263 * If that number is equal to the #remaining positive,
1264 * or to the #remaining negative, no empty cells can be neutral. */
1265static int solve_countdominoes_neutral(game_state *state, rowcol rc, int *counts)
1266{
1267 int i, j, ndom = 0, nonn = 0, ret = 0;
1268
1269 if ((rc.targets[POSITIVE] == -1) && (rc.targets[NEGATIVE] == -1))
1270 return 0; /* need at least one target to compare. */
1271
1272 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1273 if (state->flags[i] & GS_SET) continue;
1274 assert(state->grid[i] == EMPTY);
1275
1276 /* Skip solo cells, or second cell in domino. */
1277 if ((state->common->dominoes[i] == i) ||
1278 (state->common->dominoes[i] == i-rc.di))
1279 continue;
1280
1281 ndom++;
1282 }
1283
1284 if ((rc.targets[POSITIVE] != -1) &&
1285 (rc.targets[POSITIVE]-counts[POSITIVE] == ndom))
1286 nonn = 1;
1287 if ((rc.targets[NEGATIVE] != -1) &&
1288 (rc.targets[NEGATIVE]-counts[NEGATIVE] == ndom))
1289 nonn = 1;
1290
1291 if (!nonn) return 0;
1292
1293 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1294 if (state->flags[i] & GS_SET) continue;
1295
1296 if (!(state->flags[i] & GS_NOTNEUTRAL)) {
1297 if (solve_unflag(state, i, NEUTRAL, "all dominoes +/- [tricky]", &rc) < 0)
1298 return -1;
1299 ret++;
1300 }
1301 }
1302 return ret;
1303}
1304
1305static int solve_domino_count(game_state *state, rowcol rc, int i, int which)
1306{
1307 int nposs = 0;
1308
1309 /* Skip solo cells or 2nd in domino. */
1310 if ((state->common->dominoes[i] == i) ||
1311 (state->common->dominoes[i] == i-rc.di))
1312 return 0;
1313
1314 if (state->flags[i] & GS_SET)
1315 return 0;
1316
1317 if (POSSIBLE(i, which))
1318 nposs++;
1319
1320 if (state->common->dominoes[i] == i+rc.di) {
1321 /* second cell of domino is on our row: test that too. */
1322 if (POSSIBLE(i+rc.di, which))
1323 nposs++;
1324 }
1325 return nposs;
1326}
1327
1328/* Count number of dominoes we could put each of + and - into. If it is equal
1329 * to the #left, any domino we can only put + or - in one cell of must have it. */
1330static int solve_countdominoes_nonneutral(game_state *state, rowcol rc, int *counts)
1331{
1332 int which, w, i, j, ndom = 0, didsth = 0, toset;
1333
1334 for (which = POSITIVE, w = 0; w < 2; which = OPPOSITE(which), w++) {
1335 if (rc.targets[which] == -1) continue;
1336
1337 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1338 if (solve_domino_count(state, rc, i, which) > 0)
1339 ndom++;
1340 }
1341
1342 if ((rc.targets[which] - counts[which]) != ndom)
1343 continue;
1344
1345 for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) {
1346 if (solve_domino_count(state, rc, i, which) == 1) {
1347 if (POSSIBLE(i, which))
1348 toset = i;
1349 else {
1350 /* paranoia, should have been checked by solve_domino_count. */
1351 assert(state->common->dominoes[i] == i+rc.di);
1352 assert(POSSIBLE(i+rc.di, which));
1353 toset = i+rc.di;
1354 }
1355 if (solve_set(state, toset, which, "all empty dominoes need +/- [tricky]", &rc) < 0)
1356 return -1;
1357 didsth++;
1358 }
1359 }
1360 }
1361 return didsth;
1362}
1363
1364/* danger, evil macro. can't use the do { ... } while(0) trick because
1365 * the continue breaks. */
1366#define SOLVE_FOR_ROWCOLS(fn) \
1367 ret = solve_rowcols(state, fn); \
1368 if (ret < 0) { debug(("%s said impossible, cannot solve", #fn)); return -1; } \
1369 if (ret > 0) continue
1370
1371static int solve_state(game_state *state, int diff)
1372{
1373 int ret;
1374
1375 debug(("solve_state, difficulty %s", magnets_diffnames[diff]));
1376
1377 solve_clearflags(state);
1378 if (solve_startflags(state) < 0) return -1;
1379
1380 while (1) {
1381 ret = solve_force(state);
1382 if (ret > 0) continue;
1383 if (ret < 0) return -1;
1384
1385 ret = solve_neither(state);
1386 if (ret > 0) continue;
1387 if (ret < 0) return -1;
1388
1389 SOLVE_FOR_ROWCOLS(solve_checkfull);
1390 SOLVE_FOR_ROWCOLS(solve_oddlength);
1391
1392 if (diff < DIFF_TRICKY) break;
1393
1394 SOLVE_FOR_ROWCOLS(solve_advancedfull);
1395 SOLVE_FOR_ROWCOLS(solve_nonneutral);
1396 SOLVE_FOR_ROWCOLS(solve_countdominoes_neutral);
1397 SOLVE_FOR_ROWCOLS(solve_countdominoes_nonneutral);
1398
1399 /* more ... */
1400
1401 break;
1402 }
1403 return check_completion(state);
1404}
1405
1406
1407static char *game_state_diff(const game_state *src, const game_state *dst,
1408 int issolve)
1409{
1410 char *ret = NULL, buf[80], c;
1411 int retlen = 0, x, y, i, k;
1412
1413 assert(src->w == dst->w && src->h == dst->h);
1414
1415 if (issolve) {
1416 ret = sresize(ret, 3, char);
1417 ret[0] = 'S'; ret[1] = ';'; ret[2] = '\0';
1418 retlen += 2;
1419 }
1420 for (x = 0; x < dst->w; x++) {
1421 for (y = 0; y < dst->h; y++) {
1422 i = y*dst->w+x;
1423
1424 if (src->common->dominoes[i] == i) continue;
1425
1426#define APPEND do { \
1427 ret = sresize(ret, retlen + k + 1, char); \
1428 strcpy(ret + retlen, buf); \
1429 retlen += k; \
1430} while(0)
1431
1432 if ((src->grid[i] != dst->grid[i]) ||
1433 ((src->flags[i] & GS_SET) != (dst->flags[i] & GS_SET))) {
1434 if (dst->grid[i] == EMPTY && !(dst->flags[i] & GS_SET))
1435 c = ' ';
1436 else
1437 c = GRID2CHAR(dst->grid[i]);
1438 k = sprintf(buf, "%c%d,%d;", (int)c, x, y);
1439 APPEND;
1440 }
1441 }
1442 }
1443 debug(("game_state_diff returns %s", ret));
1444 return ret;
1445}
1446
1447static void solve_from_aux(const game_state *state, const char *aux)
1448{
1449 int i;
1450 assert(strlen(aux) == state->wh);
1451 for (i = 0; i < state->wh; i++) {
1452 state->grid[i] = CHAR2GRID(aux[i]);
1453 state->flags[i] |= GS_SET;
1454 }
1455}
1456
1457static char *solve_game(const game_state *state, const game_state *currstate,
1458 const char *aux, char **error)
1459{
1460 game_state *solved = dup_game(currstate);
1461 char *move = NULL;
1462 int ret;
1463
1464 if (aux && strlen(aux) == state->wh) {
1465 solve_from_aux(solved, aux);
1466 goto solved;
1467 }
1468
1469 if (solve_state(solved, DIFFCOUNT) > 0) goto solved;
1470 free_game(solved);
1471
1472 solved = dup_game(state);
1473 ret = solve_state(solved, DIFFCOUNT);
1474 if (ret > 0) goto solved;
1475 free_game(solved);
1476
1477 *error = (ret < 0) ? "Puzzle is impossible." : "Unable to solve puzzle.";
1478 return NULL;
1479
1480solved:
1481 move = game_state_diff(currstate, solved, 1);
1482 free_game(solved);
1483 return move;
1484}
1485
1486static int solve_unnumbered(game_state *state)
1487{
1488 int i, ret;
1489 while (1) {
1490 ret = solve_force(state);
1491 if (ret > 0) continue;
1492 if (ret < 0) return -1;
1493
1494 ret = solve_neither(state);
1495 if (ret > 0) continue;
1496 if (ret < 0) return -1;
1497
1498 break;
1499 }
1500 for (i = 0; i < state->wh; i++) {
1501 if (!(state->flags[i] & GS_SET)) return 0;
1502 }
1503 return 1;
1504}
1505
1506static int lay_dominoes(game_state *state, random_state *rs, int *scratch)
1507{
1508 int n, i, ret = 0, nlaid = 0, n_initial_neutral;
1509
1510 for (i = 0; i < state->wh; i++) {
1511 scratch[i] = i;
1512 state->grid[i] = EMPTY;
1513 state->flags[i] = (state->common->dominoes[i] == i) ? GS_SET : 0;
1514 }
1515 shuffle(scratch, state->wh, sizeof(int), rs);
1516
1517 n_initial_neutral = (state->wh > 100) ? 5 : (state->wh / 10);
1518
1519 for (n = 0; n < state->wh; n++) {
1520 /* Find a space ... */
1521
1522 i = scratch[n];
1523 if (state->flags[i] & GS_SET) continue; /* already laid here. */
1524
1525 /* ...and lay a domino if we can. */
1526
1527 debug(("Laying domino at i:%d, (%d,%d)\n", i, i%state->w, i/state->w));
1528
1529 /* The choice of which type of domino to lay here leads to subtle differences
1530 * in the sorts of boards that get produced. Too much bias towards magnets
1531 * leads to games that are too easy.
1532 *
1533 * Currently, it lays a small set of dominoes at random as neutral, and
1534 * then lays the rest preferring to be magnets -- however, if the
1535 * current layout is such that a magnet won't go there, then it lays
1536 * another neutral.
1537 *
1538 * The number of initially neutral dominoes is limited as grids get bigger:
1539 * too many neutral dominoes invariably ends up with insoluble puzzle at
1540 * this size, and the positioning process means it'll always end up laying
1541 * more than the initial 5 anyway.
1542 */
1543
1544 /* We should always be able to lay a neutral anywhere. */
1545 assert(!(state->flags[i] & GS_NOTNEUTRAL));
1546
1547 if (n < n_initial_neutral) {
1548 debug((" ...laying neutral\n"));
1549 ret = solve_set(state, i, NEUTRAL, "layout initial neutral", NULL);
1550 } else {
1551 debug((" ... preferring magnet\n"));
1552 if (!(state->flags[i] & GS_NOTPOSITIVE))
1553 ret = solve_set(state, i, POSITIVE, "layout", NULL);
1554 else if (!(state->flags[i] & GS_NOTNEGATIVE))
1555 ret = solve_set(state, i, NEGATIVE, "layout", NULL);
1556 else
1557 ret = solve_set(state, i, NEUTRAL, "layout", NULL);
1558 }
1559 if (!ret) {
1560 debug(("Unable to lay anything at (%d,%d), giving up.",
1561 i%state->w, i/state->w));
1562 ret = -1;
1563 break;
1564 }
1565
1566 nlaid++;
1567 ret = solve_unnumbered(state);
1568 if (ret == -1)
1569 debug(("solve_unnumbered decided impossible.\n"));
1570 if (ret != 0)
1571 break;
1572 }
1573
1574 debug(("Laid %d dominoes, total %d dominoes.\n", nlaid, state->wh/2));
1575 game_debug(state, "Final layout");
1576 return ret;
1577}
1578
1579static void gen_game(game_state *new, random_state *rs)
1580{
1581 int ret, x, y, val;
1582 int *scratch = snewn(new->wh, int);
1583
1584#ifdef STANDALONE_SOLVER
1585 if (verbose) printf("Generating new game...\n");
1586#endif
1587
1588 clear_state(new);
1589 sfree(new->common->dominoes); /* bit grotty. */
1590 new->common->dominoes = domino_layout(new->w, new->h, rs);
1591
1592 do {
1593 ret = lay_dominoes(new, rs, scratch);
1594 } while(ret == -1);
1595
1596 /* for each cell, update colcount/rowcount as appropriate. */
1597 memset(new->common->colcount, 0, new->w*3*sizeof(int));
1598 memset(new->common->rowcount, 0, new->h*3*sizeof(int));
1599 for (x = 0; x < new->w; x++) {
1600 for (y = 0; y < new->h; y++) {
1601 val = new->grid[y*new->w+x];
1602 new->common->colcount[x*3+val]++;
1603 new->common->rowcount[y*3+val]++;
1604 }
1605 }
1606 new->numbered = 1;
1607
1608 sfree(scratch);
1609}
1610
1611static void generate_aux(game_state *new, char *aux)
1612{
1613 int i;
1614 for (i = 0; i < new->wh; i++)
1615 aux[i] = GRID2CHAR(new->grid[i]);
1616 aux[new->wh] = '\0';
1617}
1618
1619static int check_difficulty(const game_params *params, game_state *new,
1620 random_state *rs)
1621{
1622 int *scratch, *grid_correct, slen, i;
1623
1624 memset(new->grid, EMPTY, new->wh*sizeof(int));
1625
1626 if (params->diff > DIFF_EASY) {
1627 /* If this is too easy, return. */
1628 if (solve_state(new, params->diff-1) > 0) {
1629 debug(("Puzzle is too easy."));
1630 return -1;
1631 }
1632 }
1633 if (solve_state(new, params->diff) <= 0) {
1634 debug(("Puzzle is not soluble at requested difficulty."));
1635 return -1;
1636 }
1637 if (!params->stripclues) return 0;
1638
1639 /* Copy the correct grid away. */
1640 grid_correct = snewn(new->wh, int);
1641 memcpy(grid_correct, new->grid, new->wh*sizeof(int));
1642
1643 /* Create shuffled array of side-clue locations. */
1644 slen = new->w*2 + new->h*2;
1645 scratch = snewn(slen, int);
1646 for (i = 0; i < slen; i++) scratch[i] = i;
1647 shuffle(scratch, slen, sizeof(int), rs);
1648
1649 /* For each clue, check whether removing it makes the puzzle unsoluble;
1650 * put it back if so. */
1651 for (i = 0; i < slen; i++) {
1652 int num = scratch[i], which, roworcol, target, targetn, ret;
1653 rowcol rc;
1654
1655 /* work out which clue we meant. */
1656 if (num < new->w+new->h) { which = POSITIVE; }
1657 else { which = NEGATIVE; num -= new->w+new->h; }
1658
1659 if (num < new->w) { roworcol = COLUMN; }
1660 else { roworcol = ROW; num -= new->w; }
1661
1662 /* num is now the row/column index in question. */
1663 rc = mkrowcol(new, num, roworcol);
1664
1665 /* Remove clue, storing original... */
1666 target = rc.targets[which];
1667 targetn = rc.targets[NEUTRAL];
1668 rc.targets[which] = -1;
1669 rc.targets[NEUTRAL] = -1;
1670
1671 /* ...and see if we can still solve it. */
1672 game_debug(new, "removed clue, new board:");
1673 memset(new->grid, EMPTY, new->wh * sizeof(int));
1674 ret = solve_state(new, params->diff);
1675 assert(ret != -1);
1676
1677 if (ret == 0 ||
1678 memcmp(new->grid, grid_correct, new->wh*sizeof(int)) != 0) {
1679 /* We made it ambiguous: put clue back. */
1680 debug(("...now impossible/different, put clue back."));
1681 rc.targets[which] = target;
1682 rc.targets[NEUTRAL] = targetn;
1683 }
1684 }
1685 sfree(scratch);
1686 sfree(grid_correct);
1687
1688 return 0;
1689}
1690
1691static char *new_game_desc(const game_params *params, random_state *rs,
1692 char **aux_r, int interactive)
1693{
1694 game_state *new = new_state(params->w, params->h);
1695 char *desc, *aux = snewn(new->wh+1, char);
1696
1697 do {
1698 gen_game(new, rs);
1699 generate_aux(new, aux);
1700 } while (check_difficulty(params, new, rs) < 0);
1701
1702 /* now we're complete, generate the description string
1703 * and an aux_info for the completed game. */
1704 desc = generate_desc(new);
1705
1706 free_game(new);
1707
1708 *aux_r = aux;
1709 return desc;
1710}
1711
1712struct game_ui {
1713 int cur_x, cur_y, cur_visible;
1714};
1715
1716static game_ui *new_ui(const game_state *state)
1717{
1718 game_ui *ui = snew(game_ui);
1719 ui->cur_x = ui->cur_y = 0;
1720 ui->cur_visible = 0;
1721 return ui;
1722}
1723
1724static void free_ui(game_ui *ui)
1725{
1726 sfree(ui);
1727}
1728
1729static char *encode_ui(const game_ui *ui)
1730{
1731 return NULL;
1732}
1733
1734static void decode_ui(game_ui *ui, const char *encoding)
1735{
1736}
1737
1738static void game_changed_state(game_ui *ui, const game_state *oldstate,
1739 const game_state *newstate)
1740{
1741 if (!oldstate->completed && newstate->completed)
1742 ui->cur_visible = 0;
1743}
1744
1745struct game_drawstate {
1746 int tilesize, started, solved;
1747 int w, h;
1748 unsigned long *what; /* size w*h */
1749 unsigned long *colwhat, *rowwhat; /* size 3*w, 3*h */
1750};
1751
1752#define DS_WHICH_MASK 0xf
1753
1754#define DS_ERROR 0x10
1755#define DS_CURSOR 0x20
1756#define DS_SET 0x40
1757#define DS_NOTPOS 0x80
1758#define DS_NOTNEG 0x100
1759#define DS_NOTNEU 0x200
1760#define DS_FLASH 0x400
1761
1762#define PREFERRED_TILE_SIZE 32
1763#define TILE_SIZE (ds->tilesize)
1764#define BORDER (TILE_SIZE / 8)
1765
1766#define COORD(x) ( (x+1) * TILE_SIZE + BORDER )
1767#define FROMCOORD(x) ( (x - BORDER) / TILE_SIZE - 1 )
1768
1769static int is_clue(const game_state *state, int x, int y)
1770{
1771 int h = state->h, w = state->w;
1772
1773 if (((x == -1 || x == w) && y >= 0 && y < h) ||
1774 ((y == -1 || y == h) && x >= 0 && x < w))
1775 return TRUE;
1776
1777 return FALSE;
1778}
1779
1780static int clue_index(const game_state *state, int x, int y)
1781{
1782 int h = state->h, w = state->w;
1783
1784 if (y == -1)
1785 return x;
1786 else if (x == w)
1787 return w + y;
1788 else if (y == h)
1789 return 2 * w + h - x - 1;
1790 else if (x == -1)
1791 return 2 * (w + h) - y - 1;
1792
1793 return -1;
1794}
1795
1796static char *interpret_move(const game_state *state, game_ui *ui,
1797 const game_drawstate *ds,
1798 int x, int y, int button)
1799{
1800 int gx = FROMCOORD(x), gy = FROMCOORD(y), idx, curr;
1801 char *nullret = NULL, buf[80], movech;
1802 enum { CYCLE_MAGNET, CYCLE_NEUTRAL } action;
1803
1804 if (IS_CURSOR_MOVE(button)) {
1805 move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, 0);
1806 ui->cur_visible = 1;
1807 return "";
1808 } else if (IS_CURSOR_SELECT(button)) {
1809 if (!ui->cur_visible) {
1810 ui->cur_visible = 1;
1811 return "";
1812 }
1813 action = (button == CURSOR_SELECT) ? CYCLE_MAGNET : CYCLE_NEUTRAL;
1814 gx = ui->cur_x;
1815 gy = ui->cur_y;
1816 } else if (INGRID(state, gx, gy) &&
1817 (button == LEFT_BUTTON || button == RIGHT_BUTTON)) {
1818 if (ui->cur_visible) {
1819 ui->cur_visible = 0;
1820 nullret = "";
1821 }
1822 action = (button == LEFT_BUTTON) ? CYCLE_MAGNET : CYCLE_NEUTRAL;
1823 } else if (button == LEFT_BUTTON && is_clue(state, gx, gy)) {
1824 sprintf(buf, "D%d,%d", gx, gy);
1825 return dupstr(buf);
1826 } else
1827 return NULL;
1828
1829 idx = gy * state->w + gx;
1830 if (state->common->dominoes[idx] == idx) return nullret;
1831 curr = state->grid[idx];
1832
1833 if (action == CYCLE_MAGNET) {
1834 /* ... empty --> positive --> negative --> empty ... */
1835
1836 if (state->grid[idx] == NEUTRAL && state->flags[idx] & GS_SET)
1837 return nullret; /* can't cycle a magnet from a neutral. */
1838 movech = (curr == EMPTY) ? '+' : (curr == POSITIVE) ? '-' : ' ';
1839 } else if (action == CYCLE_NEUTRAL) {
1840 /* ... empty -> neutral -> !neutral --> empty ... */
1841
1842 if (state->grid[idx] != NEUTRAL)
1843 return nullret; /* can't cycle through neutral from a magnet. */
1844
1845 /* All of these are grid == EMPTY == NEUTRAL; it twiddles
1846 * combinations of flags. */
1847 if (state->flags[idx] & GS_SET) /* neutral */
1848 movech = '?';
1849 else if (state->flags[idx] & GS_NOTNEUTRAL) /* !neutral */
1850 movech = ' ';
1851 else
1852 movech = '.';
1853 } else {
1854 assert(!"unknown action");
1855 movech = 0; /* placate optimiser */
1856 }
1857
1858 sprintf(buf, "%c%d,%d", movech, gx, gy);
1859
1860 return dupstr(buf);
1861}
1862
1863static game_state *execute_move(const game_state *state, const char *move)
1864{
1865 game_state *ret = dup_game(state);
1866 int x, y, n, idx, idx2;
1867 char c;
1868
1869 if (!*move) goto badmove;
1870 while (*move) {
1871 c = *move++;
1872 if (c == 'S') {
1873 ret->solved = TRUE;
1874 n = 0;
1875 } else if (c == '+' || c == '-' ||
1876 c == '.' || c == ' ' || c == '?') {
1877 if ((sscanf(move, "%d,%d%n", &x, &y, &n) != 2) ||
1878 !INGRID(state, x, y)) goto badmove;
1879
1880 idx = y*state->w + x;
1881 idx2 = state->common->dominoes[idx];
1882 if (idx == idx2) goto badmove;
1883
1884 ret->flags[idx] &= ~GS_NOTMASK;
1885 ret->flags[idx2] &= ~GS_NOTMASK;
1886
1887 if (c == ' ' || c == '?') {
1888 ret->grid[idx] = EMPTY;
1889 ret->grid[idx2] = EMPTY;
1890 ret->flags[idx] &= ~GS_SET;
1891 ret->flags[idx2] &= ~GS_SET;
1892 if (c == '?') {
1893 ret->flags[idx] |= GS_NOTNEUTRAL;
1894 ret->flags[idx2] |= GS_NOTNEUTRAL;
1895 }
1896 } else {
1897 ret->grid[idx] = CHAR2GRID(c);
1898 ret->grid[idx2] = OPPOSITE(CHAR2GRID(c));
1899 ret->flags[idx] |= GS_SET;
1900 ret->flags[idx2] |= GS_SET;
1901 }
1902 } else if (c == 'D' && sscanf(move, "%d,%d%n", &x, &y, &n) == 2 &&
1903 is_clue(ret, x, y)) {
1904 ret->counts_done[clue_index(ret, x, y)] ^= 1;
1905 } else
1906 goto badmove;
1907
1908 move += n;
1909 if (*move == ';') move++;
1910 else if (*move) goto badmove;
1911 }
1912 if (check_completion(ret) == 1)
1913 ret->completed = 1;
1914
1915 return ret;
1916
1917badmove:
1918 free_game(ret);
1919 return NULL;
1920}
1921
1922/* ----------------------------------------------------------------------
1923 * Drawing routines.
1924 */
1925
1926static void game_compute_size(const game_params *params, int tilesize,
1927 int *x, int *y)
1928{
1929 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1930 struct { int tilesize; } ads, *ds = &ads;
1931 ads.tilesize = tilesize;
1932
1933 *x = TILE_SIZE * (params->w+2) + 2 * BORDER;
1934 *y = TILE_SIZE * (params->h+2) + 2 * BORDER;
1935}
1936
1937static void game_set_size(drawing *dr, game_drawstate *ds,
1938 const game_params *params, int tilesize)
1939{
1940 ds->tilesize = tilesize;
1941}
1942
1943static float *game_colours(frontend *fe, int *ncolours)
1944{
1945 float *ret = snewn(3 * NCOLOURS, float);
1946 int i;
1947
1948 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1949
1950 for (i = 0; i < 3; i++) {
1951 ret[COL_TEXT * 3 + i] = 0.0F;
1952 ret[COL_NEGATIVE * 3 + i] = 0.0F;
1953 ret[COL_CURSOR * 3 + i] = 0.9F;
1954 ret[COL_DONE * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 1.5F;
1955 }
1956
1957 ret[COL_POSITIVE * 3 + 0] = 0.8F;
1958 ret[COL_POSITIVE * 3 + 1] = 0.0F;
1959 ret[COL_POSITIVE * 3 + 2] = 0.0F;
1960
1961 ret[COL_NEUTRAL * 3 + 0] = 0.10F;
1962 ret[COL_NEUTRAL * 3 + 1] = 0.60F;
1963 ret[COL_NEUTRAL * 3 + 2] = 0.10F;
1964
1965 ret[COL_ERROR * 3 + 0] = 1.0F;
1966 ret[COL_ERROR * 3 + 1] = 0.0F;
1967 ret[COL_ERROR * 3 + 2] = 0.0F;
1968
1969 ret[COL_NOT * 3 + 0] = 0.2F;
1970 ret[COL_NOT * 3 + 1] = 0.2F;
1971 ret[COL_NOT * 3 + 2] = 1.0F;
1972
1973 *ncolours = NCOLOURS;
1974 return ret;
1975}
1976
1977static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1978{
1979 struct game_drawstate *ds = snew(struct game_drawstate);
1980
1981 ds->tilesize = ds->started = ds->solved = 0;
1982 ds->w = state->w;
1983 ds->h = state->h;
1984
1985 ds->what = snewn(state->wh, unsigned long);
1986 memset(ds->what, 0, state->wh*sizeof(unsigned long));
1987
1988 ds->colwhat = snewn(state->w*3, unsigned long);
1989 memset(ds->colwhat, 0, state->w*3*sizeof(unsigned long));
1990 ds->rowwhat = snewn(state->h*3, unsigned long);
1991 memset(ds->rowwhat, 0, state->h*3*sizeof(unsigned long));
1992
1993 return ds;
1994}
1995
1996static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1997{
1998 sfree(ds->colwhat);
1999 sfree(ds->rowwhat);
2000 sfree(ds->what);
2001 sfree(ds);
2002}
2003
2004static void draw_num(drawing *dr, game_drawstate *ds, int rowcol, int which,
2005 int idx, int colbg, int col, int num)
2006{
2007 char buf[32];
2008 int cx, cy, tsz;
2009
2010 if (num < 0) return;
2011
2012 sprintf(buf, "%d", num);
2013 tsz = (strlen(buf) == 1) ? (7*TILE_SIZE/10) : (9*TILE_SIZE/10)/strlen(buf);
2014
2015 if (rowcol == ROW) {
2016 cx = BORDER;
2017 if (which == NEGATIVE) cx += TILE_SIZE * (ds->w+1);
2018 cy = BORDER + TILE_SIZE * (idx+1);
2019 } else {
2020 cx = BORDER + TILE_SIZE * (idx+1);
2021 cy = BORDER;
2022 if (which == NEGATIVE) cy += TILE_SIZE * (ds->h+1);
2023 }
2024
2025 draw_rect(dr, cx, cy, TILE_SIZE, TILE_SIZE, colbg);
2026 draw_text(dr, cx + TILE_SIZE/2, cy + TILE_SIZE/2, FONT_VARIABLE, tsz,
2027 ALIGN_VCENTRE | ALIGN_HCENTRE, col, buf);
2028
2029 draw_update(dr, cx, cy, TILE_SIZE, TILE_SIZE);
2030}
2031
2032static void draw_sym(drawing *dr, game_drawstate *ds, int x, int y, int which, int col)
2033{
2034 int cx = COORD(x), cy = COORD(y);
2035 int ccx = cx + TILE_SIZE/2, ccy = cy + TILE_SIZE/2;
2036 int roff = TILE_SIZE/4, rsz = 2*roff+1;
2037 int soff = TILE_SIZE/16, ssz = 2*soff+1;
2038
2039 if (which == POSITIVE || which == NEGATIVE) {
2040 draw_rect(dr, ccx - roff, ccy - soff, rsz, ssz, col);
2041 if (which == POSITIVE)
2042 draw_rect(dr, ccx - soff, ccy - roff, ssz, rsz, col);
2043 } else if (col == COL_NOT) {
2044 /* not-a-neutral is a blue question mark. */
2045 char qu[2] = { '?', 0 };
2046 draw_text(dr, ccx, ccy, FONT_VARIABLE, 7*TILE_SIZE/10,
2047 ALIGN_VCENTRE | ALIGN_HCENTRE, col, qu);
2048 } else {
2049 draw_line(dr, ccx - roff, ccy - roff, ccx + roff, ccy + roff, col);
2050 draw_line(dr, ccx + roff, ccy - roff, ccx - roff, ccy + roff, col);
2051 }
2052}
2053
2054enum {
2055 TYPE_L,
2056 TYPE_R,
2057 TYPE_T,
2058 TYPE_B,
2059 TYPE_BLANK
2060};
2061
2062/* NOT responsible for redrawing background or updating. */
2063static void draw_tile_col(drawing *dr, game_drawstate *ds, int *dominoes,
2064 int x, int y, int which, int bg, int fg, int perc)
2065{
2066 int cx = COORD(x), cy = COORD(y), i, other, type = TYPE_BLANK;
2067 int gutter, radius, coffset;
2068
2069 /* gutter is TSZ/16 for 100%, 8*TSZ/16 (TSZ/2) for 0% */
2070 gutter = (TILE_SIZE / 16) + ((100 - perc) * (7*TILE_SIZE / 16))/100;
2071 radius = (perc * (TILE_SIZE / 8)) / 100;
2072 coffset = gutter + radius;
2073
2074 i = y*ds->w + x;
2075 other = dominoes[i];
2076
2077 if (other == i) return;
2078 else if (other == i+1) type = TYPE_L;
2079 else if (other == i-1) type = TYPE_R;
2080 else if (other == i+ds->w) type = TYPE_T;
2081 else if (other == i-ds->w) type = TYPE_B;
2082 else assert(!"mad domino orientation");
2083
2084 /* domino drawing shamelessly stolen from dominosa.c. */
2085 if (type == TYPE_L || type == TYPE_T)
2086 draw_circle(dr, cx+coffset, cy+coffset,
2087 radius, bg, bg);
2088 if (type == TYPE_R || type == TYPE_T)
2089 draw_circle(dr, cx+TILE_SIZE-1-coffset, cy+coffset,
2090 radius, bg, bg);
2091 if (type == TYPE_L || type == TYPE_B)
2092 draw_circle(dr, cx+coffset, cy+TILE_SIZE-1-coffset,
2093 radius, bg, bg);
2094 if (type == TYPE_R || type == TYPE_B)
2095 draw_circle(dr, cx+TILE_SIZE-1-coffset,
2096 cy+TILE_SIZE-1-coffset,
2097 radius, bg, bg);
2098
2099 for (i = 0; i < 2; i++) {
2100 int x1, y1, x2, y2;
2101
2102 x1 = cx + (i ? gutter : coffset);
2103 y1 = cy + (i ? coffset : gutter);
2104 x2 = cx + TILE_SIZE-1 - (i ? gutter : coffset);
2105 y2 = cy + TILE_SIZE-1 - (i ? coffset : gutter);
2106 if (type == TYPE_L)
2107 x2 = cx + TILE_SIZE;
2108 else if (type == TYPE_R)
2109 x1 = cx;
2110 else if (type == TYPE_T)
2111 y2 = cy + TILE_SIZE ;
2112 else if (type == TYPE_B)
2113 y1 = cy;
2114
2115 draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg);
2116 }
2117
2118 if (fg != -1) draw_sym(dr, ds, x, y, which, fg);
2119}
2120
2121static void draw_tile(drawing *dr, game_drawstate *ds, int *dominoes,
2122 int x, int y, unsigned long flags)
2123{
2124 int cx = COORD(x), cy = COORD(y), bg, fg, perc = 100;
2125 int which = flags & DS_WHICH_MASK;
2126
2127 flags &= ~DS_WHICH_MASK;
2128
2129 draw_rect(dr, cx, cy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
2130
2131 if (flags & DS_CURSOR)
2132 bg = COL_CURSOR; /* off-white white for cursor */
2133 else if (which == POSITIVE)
2134 bg = COL_POSITIVE;
2135 else if (which == NEGATIVE)
2136 bg = COL_NEGATIVE;
2137 else if (flags & DS_SET)
2138 bg = COL_NEUTRAL; /* green inner for neutral cells */
2139 else
2140 bg = COL_LOWLIGHT; /* light grey for empty cells. */
2141
2142 if (which == EMPTY && !(flags & DS_SET)) {
2143 int notwhich = -1;
2144 fg = -1; /* don't draw cross unless actually set as neutral. */
2145
2146 if (flags & DS_NOTPOS) notwhich = POSITIVE;
2147 if (flags & DS_NOTNEG) notwhich = NEGATIVE;
2148 if (flags & DS_NOTNEU) notwhich = NEUTRAL;
2149 if (notwhich != -1) {
2150 which = notwhich;
2151 fg = COL_NOT;
2152 }
2153 } else
2154 fg = (flags & DS_ERROR) ? COL_ERROR :
2155 (flags & DS_CURSOR) ? COL_TEXT : COL_BACKGROUND;
2156
2157 draw_rect(dr, cx, cy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
2158
2159 if (flags & DS_FLASH) {
2160 int bordercol = COL_HIGHLIGHT;
2161 draw_tile_col(dr, ds, dominoes, x, y, which, bordercol, -1, perc);
2162 perc = 3*perc/4;
2163 }
2164 draw_tile_col(dr, ds, dominoes, x, y, which, bg, fg, perc);
2165
2166 draw_update(dr, cx, cy, TILE_SIZE, TILE_SIZE);
2167}
2168
2169static int get_count_color(const game_state *state, int rowcol, int which,
2170 int index, int target)
2171{
2172 int idx;
2173 int count = count_rowcol(state, index, rowcol, which);
2174
2175 if ((count > target) ||
2176 (count < target && !count_rowcol(state, index, rowcol, -1))) {
2177 return COL_ERROR;
2178 } else if (rowcol == COLUMN) {
2179 idx = clue_index(state, index, which == POSITIVE ? -1 : state->h);
2180 } else {
2181 idx = clue_index(state, which == POSITIVE ? -1 : state->w, index);
2182 }
2183
2184 if (state->counts_done[idx]) {
2185 return COL_DONE;
2186 }
2187
2188 return COL_TEXT;
2189}
2190
2191static void game_redraw(drawing *dr, game_drawstate *ds,
2192 const game_state *oldstate, const game_state *state,
2193 int dir, const game_ui *ui,
2194 float animtime, float flashtime)
2195{
2196 int x, y, w = state->w, h = state->h, which, i, j, flash;
2197
2198 flash = (int)(flashtime * 5 / FLASH_TIME) % 2;
2199
2200 if (!ds->started) {
2201 /* draw background, corner +-. */
2202 draw_rect(dr, 0, 0,
2203 TILE_SIZE * (w+2) + 2 * BORDER,
2204 TILE_SIZE * (h+2) + 2 * BORDER,
2205 COL_BACKGROUND);
2206
2207 draw_sym(dr, ds, -1, -1, POSITIVE, COL_TEXT);
2208 draw_sym(dr, ds, state->w, state->h, NEGATIVE, COL_TEXT);
2209
2210 draw_update(dr, 0, 0,
2211 TILE_SIZE * (ds->w+2) + 2 * BORDER,
2212 TILE_SIZE * (ds->h+2) + 2 * BORDER);
2213 }
2214
2215 /* Draw grid */
2216 for (y = 0; y < h; y++) {
2217 for (x = 0; x < w; x++) {
2218 int idx = y*w+x;
2219 unsigned long c = state->grid[idx];
2220
2221 if (state->flags[idx] & GS_ERROR)
2222 c |= DS_ERROR;
2223 if (state->flags[idx] & GS_SET)
2224 c |= DS_SET;
2225
2226 if (x == ui->cur_x && y == ui->cur_y && ui->cur_visible)
2227 c |= DS_CURSOR;
2228
2229 if (flash)
2230 c |= DS_FLASH;
2231
2232 if (state->flags[idx] & GS_NOTPOSITIVE)
2233 c |= DS_NOTPOS;
2234 if (state->flags[idx] & GS_NOTNEGATIVE)
2235 c |= DS_NOTNEG;
2236 if (state->flags[idx] & GS_NOTNEUTRAL)
2237 c |= DS_NOTNEU;
2238
2239 if (ds->what[idx] != c || !ds->started) {
2240 draw_tile(dr, ds, state->common->dominoes, x, y, c);
2241 ds->what[idx] = c;
2242 }
2243 }
2244 }
2245 /* Draw counts around side */
2246 for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) {
2247 for (i = 0; i < w; i++) {
2248 int index = i * 3 + which;
2249 int target = state->common->colcount[index];
2250 int color = get_count_color(state, COLUMN, which, i, target);
2251
2252 if (color != ds->colwhat[index] || !ds->started) {
2253 draw_num(dr, ds, COLUMN, which, i, COL_BACKGROUND, color, target);
2254 ds->colwhat[index] = color;
2255 }
2256 }
2257 for (i = 0; i < h; i++) {
2258 int index = i * 3 + which;
2259 int target = state->common->rowcount[index];
2260 int color = get_count_color(state, ROW, which, i, target);
2261
2262 if (color != ds->rowwhat[index] || !ds->started) {
2263 draw_num(dr, ds, ROW, which, i, COL_BACKGROUND, color, target);
2264 ds->rowwhat[index] = color;
2265 }
2266 }
2267 }
2268
2269 ds->started = 1;
2270}
2271
2272static float game_anim_length(const game_state *oldstate,
2273 const game_state *newstate, int dir, game_ui *ui)
2274{
2275 return 0.0F;
2276}
2277
2278static float game_flash_length(const game_state *oldstate,
2279 const game_state *newstate, int dir, game_ui *ui)
2280{
2281 if (!oldstate->completed && newstate->completed &&
2282 !oldstate->solved && !newstate->solved)
2283 return FLASH_TIME;
2284 return 0.0F;
2285}
2286
2287static int game_status(const game_state *state)
2288{
2289 return state->completed ? +1 : 0;
2290}
2291
2292static int game_timing_state(const game_state *state, game_ui *ui)
2293{
2294 return TRUE;
2295}
2296
2297static void game_print_size(const game_params *params, float *x, float *y)
2298{
2299 int pw, ph;
2300
2301 /*
2302 * I'll use 6mm squares by default.
2303 */
2304 game_compute_size(params, 600, &pw, &ph);
2305 *x = pw / 100.0F;
2306 *y = ph / 100.0F;
2307}
2308
2309static void game_print(drawing *dr, const game_state *state, int tilesize)
2310{
2311 int w = state->w, h = state->h;
2312 int ink = print_mono_colour(dr, 0);
2313 int paper = print_mono_colour(dr, 1);
2314 int x, y, which, i, j;
2315
2316 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2317 game_drawstate ads, *ds = &ads;
2318 game_set_size(dr, ds, NULL, tilesize);
2319 ds->w = w; ds->h = h;
2320
2321 /* Border. */
2322 print_line_width(dr, TILE_SIZE/12);
2323
2324 /* Numbers and +/- for corners. */
2325 draw_sym(dr, ds, -1, -1, POSITIVE, ink);
2326 draw_sym(dr, ds, state->w, state->h, NEGATIVE, ink);
2327 for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) {
2328 for (i = 0; i < w; i++) {
2329 draw_num(dr, ds, COLUMN, which, i, paper, ink,
2330 state->common->colcount[i*3+which]);
2331 }
2332 for (i = 0; i < h; i++) {
2333 draw_num(dr, ds, ROW, which, i, paper, ink,
2334 state->common->rowcount[i*3+which]);
2335 }
2336 }
2337
2338 /* Dominoes. */
2339 for (x = 0; x < w; x++) {
2340 for (y = 0; y < h; y++) {
2341 i = y*state->w + x;
2342 if (state->common->dominoes[i] == i+1 ||
2343 state->common->dominoes[i] == i+w) {
2344 int dx = state->common->dominoes[i] == i+1 ? 2 : 1;
2345 int dy = 3 - dx;
2346 int xx, yy;
2347 int cx = COORD(x), cy = COORD(y);
2348
2349 print_line_width(dr, 0);
2350
2351 /* Ink the domino */
2352 for (yy = 0; yy < 2; yy++)
2353 for (xx = 0; xx < 2; xx++)
2354 draw_circle(dr,
2355 cx+xx*dx*TILE_SIZE+(1-2*xx)*3*TILE_SIZE/16,
2356 cy+yy*dy*TILE_SIZE+(1-2*yy)*3*TILE_SIZE/16,
2357 TILE_SIZE/8, ink, ink);
2358 draw_rect(dr, cx + TILE_SIZE/16, cy + 3*TILE_SIZE/16,
2359 dx*TILE_SIZE - 2*(TILE_SIZE/16),
2360 dy*TILE_SIZE - 6*(TILE_SIZE/16), ink);
2361 draw_rect(dr, cx + 3*TILE_SIZE/16, cy + TILE_SIZE/16,
2362 dx*TILE_SIZE - 6*(TILE_SIZE/16),
2363 dy*TILE_SIZE - 2*(TILE_SIZE/16), ink);
2364
2365 /* Un-ink the domino interior */
2366 for (yy = 0; yy < 2; yy++)
2367 for (xx = 0; xx < 2; xx++)
2368 draw_circle(dr,
2369 cx+xx*dx*TILE_SIZE+(1-2*xx)*3*TILE_SIZE/16,
2370 cy+yy*dy*TILE_SIZE+(1-2*yy)*3*TILE_SIZE/16,
2371 3*TILE_SIZE/32, paper, paper);
2372 draw_rect(dr, cx + 3*TILE_SIZE/32, cy + 3*TILE_SIZE/16,
2373 dx*TILE_SIZE - 2*(3*TILE_SIZE/32),
2374 dy*TILE_SIZE - 6*(TILE_SIZE/16), paper);
2375 draw_rect(dr, cx + 3*TILE_SIZE/16, cy + 3*TILE_SIZE/32,
2376 dx*TILE_SIZE - 6*(TILE_SIZE/16),
2377 dy*TILE_SIZE - 2*(3*TILE_SIZE/32), paper);
2378 }
2379 }
2380 }
2381
2382 /* Grid symbols (solution). */
2383 for (x = 0; x < w; x++) {
2384 for (y = 0; y < h; y++) {
2385 i = y*state->w + x;
2386 if ((state->grid[i] != NEUTRAL) || (state->flags[i] & GS_SET))
2387 draw_sym(dr, ds, x, y, state->grid[i], ink);
2388 }
2389 }
2390}
2391
2392#ifdef COMBINED
2393#define thegame magnets
2394#endif
2395
2396const struct game thegame = {
2397 "Magnets", "games.magnets", "magnets",
2398 default_params,
2399 game_fetch_preset, NULL,
2400 decode_params,
2401 encode_params,
2402 free_params,
2403 dup_params,
2404 TRUE, game_configure, custom_params,
2405 validate_params,
2406 new_game_desc,
2407 validate_desc,
2408 new_game,
2409 dup_game,
2410 free_game,
2411 TRUE, solve_game,
2412 TRUE, game_can_format_as_text_now, game_text_format,
2413 new_ui,
2414 free_ui,
2415 encode_ui,
2416 decode_ui,
2417 game_changed_state,
2418 interpret_move,
2419 execute_move,
2420 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2421 game_colours,
2422 game_new_drawstate,
2423 game_free_drawstate,
2424 game_redraw,
2425 game_anim_length,
2426 game_flash_length,
2427 game_status,
2428 TRUE, FALSE, game_print_size, game_print,
2429 FALSE, /* wants_statusbar */
2430 FALSE, game_timing_state,
2431 REQUIRE_RBUTTON, /* flags */
2432};
2433
2434#ifdef STANDALONE_SOLVER
2435
2436#include <time.h>
2437#include <stdarg.h>
2438
2439const char *quis = NULL;
2440int csv = 0;
2441
2442void usage(FILE *out) {
2443 fprintf(out, "usage: %s [-v] [--print] <params>|<game id>\n", quis);
2444}
2445
2446void doprint(game_state *state)
2447{
2448 char *fmt = game_text_format(state);
2449 printf("%s", fmt);
2450 sfree(fmt);
2451}
2452
2453static void pnum(int n, int ntot, const char *desc)
2454{
2455 printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc);
2456}
2457
2458static void start_soak(game_params *p, random_state *rs)
2459{
2460 time_t tt_start, tt_now, tt_last;
2461 char *aux;
2462 game_state *s, *s2;
2463 int n = 0, nsolved = 0, nimpossible = 0, ntricky = 0, ret, i;
2464 long nn, nn_total = 0, nn_solved = 0, nn_tricky = 0;
2465
2466 tt_start = tt_now = time(NULL);
2467
2468 if (csv)
2469 printf("time, w, h, #generated, #solved, #tricky, #impossible, "
2470 "#neutral, #neutral/solved, #neutral/tricky\n");
2471 else
2472 printf("Soak-testing a %dx%d grid.\n", p->w, p->h);
2473
2474 s = new_state(p->w, p->h);
2475 aux = snewn(s->wh+1, char);
2476
2477 while (1) {
2478 gen_game(s, rs);
2479
2480 nn = 0;
2481 for (i = 0; i < s->wh; i++) {
2482 if (s->grid[i] == NEUTRAL) nn++;
2483 }
2484
2485 generate_aux(s, aux);
2486 memset(s->grid, EMPTY, s->wh * sizeof(int));
2487 s2 = dup_game(s);
2488
2489 ret = solve_state(s, DIFFCOUNT);
2490
2491 n++;
2492 nn_total += nn;
2493 if (ret > 0) {
2494 nsolved++;
2495 nn_solved += nn;
2496 if (solve_state(s2, DIFF_EASY) <= 0) {
2497 ntricky++;
2498 nn_tricky += nn;
2499 }
2500 } else if (ret < 0) {
2501 char *desc = generate_desc(s);
2502 solve_from_aux(s, aux);
2503 printf("Game considered impossible:\n %dx%d:%s\n",
2504 p->w, p->h, desc);
2505 sfree(desc);
2506 doprint(s);
2507 nimpossible++;
2508 }
2509
2510 free_game(s2);
2511
2512 tt_last = time(NULL);
2513 if (tt_last > tt_now) {
2514 tt_now = tt_last;
2515 if (csv) {
2516 printf("%d,%d,%d, %d,%d,%d,%d, %ld,%ld,%ld\n",
2517 (int)(tt_now - tt_start), p->w, p->h,
2518 n, nsolved, ntricky, nimpossible,
2519 nn_total, nn_solved, nn_tricky);
2520 } else {
2521 printf("%d total, %3.1f/s, ",
2522 n, (double)n / ((double)tt_now - tt_start));
2523 pnum(nsolved, n, "solved"); printf(", ");
2524 pnum(ntricky, n, "tricky");
2525 if (nimpossible > 0)
2526 pnum(nimpossible, n, "impossible");
2527 printf("\n");
2528
2529 printf(" overall %3.1f%% neutral (%3.1f%% for solved, %3.1f%% for tricky)\n",
2530 (double)(nn_total * 100) / (double)(p->w * p->h * n),
2531 (double)(nn_solved * 100) / (double)(p->w * p->h * nsolved),
2532 (double)(nn_tricky * 100) / (double)(p->w * p->h * ntricky));
2533 }
2534 }
2535 }
2536 free_game(s);
2537 sfree(aux);
2538}
2539
2540int main(int argc, const char *argv[])
2541{
2542 int print = 0, soak = 0, solved = 0, ret;
2543 char *id = NULL, *desc, *desc_gen = NULL, *err, *aux = NULL;
2544 game_state *s = NULL;
2545 game_params *p = NULL;
2546 random_state *rs = NULL;
2547 time_t seed = time(NULL);
2548
2549 setvbuf(stdout, NULL, _IONBF, 0);
2550
2551 quis = argv[0];
2552 while (--argc > 0) {
2553 char *p = (char*)(*++argv);
2554 if (!strcmp(p, "-v") || !strcmp(p, "--verbose")) {
2555 verbose = 1;
2556 } else if (!strcmp(p, "--csv")) {
2557 csv = 1;
2558 } else if (!strcmp(p, "-e") || !strcmp(p, "--seed")) {
2559 seed = atoi(*++argv);
2560 argc--;
2561 } else if (!strcmp(p, "-p") || !strcmp(p, "--print")) {
2562 print = 1;
2563 } else if (!strcmp(p, "-s") || !strcmp(p, "--soak")) {
2564 soak = 1;
2565 } else if (*p == '-') {
2566 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2567 usage(stderr);
2568 exit(1);
2569 } else {
2570 id = p;
2571 }
2572 }
2573
2574 rs = random_new((void*)&seed, sizeof(time_t));
2575
2576 if (!id) {
2577 fprintf(stderr, "usage: %s [-v] [--soak] <params> | <game_id>\n", argv[0]);
2578 goto done;
2579 }
2580 desc = strchr(id, ':');
2581 if (desc) *desc++ = '\0';
2582
2583 p = default_params();
2584 decode_params(p, id);
2585 err = validate_params(p, 1);
2586 if (err) {
2587 fprintf(stderr, "%s: %s", argv[0], err);
2588 goto done;
2589 }
2590
2591 if (soak) {
2592 if (desc) {
2593 fprintf(stderr, "%s: --soak needs parameters, not description.\n", quis);
2594 goto done;
2595 }
2596 start_soak(p, rs);
2597 goto done;
2598 }
2599
2600 if (!desc)
2601 desc = desc_gen = new_game_desc(p, rs, &aux, 0);
2602
2603 err = validate_desc(p, desc);
2604 if (err) {
2605 fprintf(stderr, "%s: %s\nDescription: %s\n", quis, err, desc);
2606 goto done;
2607 }
2608 s = new_game(NULL, p, desc);
2609 printf("%s:%s (seed %ld)\n", id, desc, (long)seed);
2610 if (aux) {
2611 /* We just generated this ourself. */
2612 if (verbose || print) {
2613 doprint(s);
2614 solve_from_aux(s, aux);
2615 solved = 1;
2616 }
2617 } else {
2618 doprint(s);
2619 verbose = 1;
2620 ret = solve_state(s, DIFFCOUNT);
2621 if (ret < 0) printf("Puzzle is impossible.\n");
2622 else if (ret == 0) printf("Puzzle is ambiguous.\n");
2623 else printf("Puzzle was solved.\n");
2624 verbose = 0;
2625 solved = 1;
2626 }
2627 if (solved) doprint(s);
2628
2629done:
2630 if (desc_gen) sfree(desc_gen);
2631 if (p) free_params(p);
2632 if (s) free_game(s);
2633 if (rs) random_free(rs);
2634 if (aux) sfree(aux);
2635
2636 return 0;
2637}
2638
2639#endif
2640
2641/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/magnets.html b/apps/plugins/puzzles/src/magnets.html
new file mode 100644
index 0000000000..0e756f2555
--- /dev/null
+++ b/apps/plugins/puzzles/src/magnets.html
@@ -0,0 +1,76 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Magnets</title>
7<link rel="previous" href="singles.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="signpost.html">
12</head>
13<body>
14<p><a href="singles.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="signpost.html">Next</a></p>
15<h1><a name="C33"></a>Chapter 33: <a name="i0"></a>Magnets</h1>
16<p>
17A rectangular grid has been filled with a mixture of magnets (that is, dominoes with one positive end and one negative end) and blank dominoes (that is, dominoes with two neutral poles). These dominoes are initially only seen in silhouette. Around the grid are placed a number of clues indicating the number of positive and negative poles contained in certain columns and rows.
18</p>
19<p>
20Your aim is to correctly place the magnets and blank dominoes such that all the clues are satisfied, with the additional constraint that no two similar magnetic poles may be orthogonally adjacent (since they repel). Neutral poles do not repel, and can be adjacent to any other pole.
21</p>
22<p>
23Credit for this puzzle goes to <a name="i1"></a>Janko <a href="#p0">[16]</a>.
24</p>
25<p>
26Magnets was contributed to this collection by James Harvey.
27</p>
28<p><a name="p0"></a>
29[16] <a href="http://www.janko.at/Raetsel/Magnete/index.htm"><code>http://www.janko.at/Raetsel/Magnete/index.htm</code></a>
30</p>
31<h2><a name="S33.1"></a>33.1 <a name="i2"></a>Magnets controls</h2>
32<p>
33Left-clicking on an empty square places a magnet at that position with the positive pole on the square and the negative pole on the other half of the magnet; left-clicking again reverses the polarity, and a third click removes the magnet.
34</p>
35<p>
36Right-clicking on an empty square places a blank domino there. Right-clicking again places two question marks on the domino, signifying &#8216;this cannot be blank&#8217; (which can be useful to note deductions while solving), and right-clicking again empties the domino.
37</p>
38<p>
39Left-clicking a clue will mark it as done (grey it out), or unmark it if it is already marked.
40</p>
41<p>
42You can also use the cursor keys to move a cursor around the grid. Pressing the return key will lay a domino with a positive pole at that position; pressing again reverses the polarity and then removes the domino, as with left-clicking. Using the space bar allows placement of blank dominoes and cannot-be-blank hints, as for right-clicking.
43</p>
44<p>
45(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
46</p>
47<h2><a name="S33.2"></a>33.2 <a name="i3"></a>Magnets parameters</h2>
48<p>
49These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
50</p>
51<dl><dt>
52<em>Width</em>, <em>Height</em>
53</dt>
54<dd>
55Size of grid in squares. There will be half <em>Width</em> &#215; <em>Height</em> dominoes in the grid: if this number is odd then one square will be blank.
56<p>
57(Grids with at least one odd dimension tend to be easier to solve.)
58</p>
59
60</dd>
61<dt>
62<em>Difficulty</em>
63</dt>
64<dd>
65Controls the difficulty of the generated puzzle. At Tricky level, you are required to make more deductions about empty dominoes and row/column counts.
66</dd>
67<dt>
68<em>Strip clues</em>
69</dt>
70<dd>
71If true, some of the clues around the grid are removed at generation time, making the puzzle more difficult.
72</dd>
73</dl>
74
75<hr><address></address></body>
76</html>
diff --git a/apps/plugins/puzzles/src/makedist.sh b/apps/plugins/puzzles/src/makedist.sh
new file mode 100755
index 0000000000..22b4f5d0ae
--- /dev/null
+++ b/apps/plugins/puzzles/src/makedist.sh
@@ -0,0 +1,47 @@
1#!/bin/sh
2
3# Build a Unix source distribution from the Puzzles SVN area.
4#
5# Pass a version number argument to have the archive tagged with that
6# version number. Otherwise, the script will not version-tag the
7# archive at all.
8
9version="$1"
10
11if test "x$version" != "x"; then
12 arcsuffix="-$version"
13 ver="-DVER=$version"
14else
15 arcsuffix=
16 ver=
17fi
18
19perl mkfiles.pl
20./mkauto.sh
21
22mkdir tmp.$$
23mkdir tmp.$$/puzzles$arcsuffix
24mkdir tmp.$$/puzzles$arcsuffix/icons
25
26# Build Windows Help and text versions of the manual for convenience.
27halibut --winhelp=puzzles.hlp --text=puzzles.txt puzzles.but
28
29# Build a text version of the HACKING document.
30halibut --text=HACKING devel.but
31
32for i in *.c *.m *.h *.R *.rc *.but *.plist *.icns LICENCE README Recipe \
33 *.rc2 mkfiles.pl Makefile Makefile.* \
34 HACKING puzzles.txt puzzles.hlp puzzles.cnt puzzles.chm \
35 icons/Makefile icons/*.sav icons/*.pl icons/*.sh icons/win16pal.xpm \
36 icons/*.png icons/*.ico icons/*.rc icons/*.c \
37 configure.ac mkauto.sh aclocal.m4 \
38 configure depcomp install-sh missing compile; do
39 case $i in
40 */*) ln -s ../../../$i tmp.$$/puzzles$arcsuffix/$i;;
41 *) ln -s ../../$i tmp.$$/puzzles$arcsuffix/$i;;
42 esac
43done
44
45tar -C tmp.$$ -chzf - puzzles$arcsuffix > ../puzzles$arcsuffix.tar.gz
46
47rm -rf tmp.$$
diff --git a/apps/plugins/puzzles/src/malloc.c b/apps/plugins/puzzles/src/malloc.c
new file mode 100644
index 0000000000..a7fa7c5adc
--- /dev/null
+++ b/apps/plugins/puzzles/src/malloc.c
@@ -0,0 +1,53 @@
1/*
2 * malloc.c: safe wrappers around malloc, realloc, free, strdup
3 */
4
5#include <stdlib.h>
6#include <string.h>
7#include "puzzles.h"
8
9/*
10 * smalloc should guarantee to return a useful pointer - Halibut
11 * can do nothing except die when it's out of memory anyway.
12 */
13void *smalloc(size_t size) {
14 void *p;
15 p = malloc(size);
16 if (!p)
17 fatal("out of memory");
18 return p;
19}
20
21/*
22 * sfree should guaranteeably deal gracefully with freeing NULL
23 */
24void sfree(void *p) {
25 if (p) {
26 free(p);
27 }
28}
29
30/*
31 * srealloc should guaranteeably be able to realloc NULL
32 */
33void *srealloc(void *p, size_t size) {
34 void *q;
35 if (p) {
36 q = realloc(p, size);
37 } else {
38 q = malloc(size);
39 }
40 if (!q)
41 fatal("out of memory");
42 return q;
43}
44
45/*
46 * dupstr is like strdup, but with the never-return-NULL property
47 * of smalloc (and also reliably defined in all environments :-)
48 */
49char *dupstr(const char *s) {
50 char *r = smalloc(1+strlen(s));
51 strcpy(r,s);
52 return r;
53}
diff --git a/apps/plugins/puzzles/src/map.R b/apps/plugins/puzzles/src/map.R
new file mode 100644
index 0000000000..1e702aa19d
--- /dev/null
+++ b/apps/plugins/puzzles/src/map.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3MAP_EXTRA = dsf
4
5map : [X] GTK COMMON map MAP_EXTRA map-icon|no-icon
6
7map : [G] WINDOWS COMMON map MAP_EXTRA map.res|noicon.res
8
9mapsolver : [U] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE m.lib
10mapsolver : [C] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE
11
12ALL += map[COMBINED] MAP_EXTRA
13
14!begin am gtk
15GAMES += map
16!end
17
18!begin >list.c
19 A(map) \
20!end
21
22!begin >gamedesc.txt
23map:map.exe:Map:Map-colouring puzzle:Colour the map so that adjacent regions are never the same colour.
24!end
diff --git a/apps/plugins/puzzles/src/map.c b/apps/plugins/puzzles/src/map.c
new file mode 100644
index 0000000000..f1af38ba5e
--- /dev/null
+++ b/apps/plugins/puzzles/src/map.c
@@ -0,0 +1,3340 @@
1/*
2 * map.c: Game involving four-colouring a map.
3 */
4
5/*
6 * TODO:
7 *
8 * - clue marking
9 * - better four-colouring algorithm?
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <assert.h>
16#include <ctype.h>
17#include <math.h>
18
19#include "puzzles.h"
20
21/*
22 * In standalone solver mode, `verbose' is a variable which can be
23 * set by command-line option; in debugging mode it's simply always
24 * true.
25 */
26#if defined STANDALONE_SOLVER
27#define SOLVER_DIAGNOSTICS
28int verbose = FALSE;
29#elif defined SOLVER_DIAGNOSTICS
30#define verbose TRUE
31#endif
32
33/*
34 * I don't seriously anticipate wanting to change the number of
35 * colours used in this game, but it doesn't cost much to use a
36 * #define just in case :-)
37 */
38#define FOUR 4
39#define THREE (FOUR-1)
40#define FIVE (FOUR+1)
41#define SIX (FOUR+2)
42
43/*
44 * Ghastly run-time configuration option, just for Gareth (again).
45 */
46static int flash_type = -1;
47static float flash_length;
48
49/*
50 * Difficulty levels. I do some macro ickery here to ensure that my
51 * enum and the various forms of my name list always match up.
52 */
53#define DIFFLIST(A) \
54 A(EASY,Easy,e) \
55 A(NORMAL,Normal,n) \
56 A(HARD,Hard,h) \
57 A(RECURSE,Unreasonable,u)
58#define ENUM(upper,title,lower) DIFF_ ## upper,
59#define TITLE(upper,title,lower) #title,
60#define ENCODE(upper,title,lower) #lower
61#define CONFIG(upper,title,lower) ":" #title
62enum { DIFFLIST(ENUM) DIFFCOUNT };
63static char const *const map_diffnames[] = { DIFFLIST(TITLE) };
64static char const map_diffchars[] = DIFFLIST(ENCODE);
65#define DIFFCONFIG DIFFLIST(CONFIG)
66
67enum { TE, BE, LE, RE }; /* top/bottom/left/right edges */
68
69enum {
70 COL_BACKGROUND,
71 COL_GRID,
72 COL_0, COL_1, COL_2, COL_3,
73 COL_ERROR, COL_ERRTEXT,
74 NCOLOURS
75};
76
77struct game_params {
78 int w, h, n, diff;
79};
80
81struct map {
82 int refcount;
83 int *map;
84 int *graph;
85 int n;
86 int ngraph;
87 int *immutable;
88 int *edgex, *edgey; /* position of a point on each edge */
89 int *regionx, *regiony; /* position of a point in each region */
90};
91
92struct game_state {
93 game_params p;
94 struct map *map;
95 int *colouring, *pencil;
96 int completed, cheated;
97};
98
99static game_params *default_params(void)
100{
101 game_params *ret = snew(game_params);
102
103#ifdef PORTRAIT_SCREEN
104 ret->w = 16;
105 ret->h = 18;
106#else
107 ret->w = 20;
108 ret->h = 15;
109#endif
110 ret->n = 30;
111 ret->diff = DIFF_NORMAL;
112
113 return ret;
114}
115
116static const struct game_params map_presets[] = {
117#ifdef PORTRAIT_SCREEN
118 {16, 18, 30, DIFF_EASY},
119 {16, 18, 30, DIFF_NORMAL},
120 {16, 18, 30, DIFF_HARD},
121 {16, 18, 30, DIFF_RECURSE},
122 {25, 30, 75, DIFF_NORMAL},
123 {25, 30, 75, DIFF_HARD},
124#else
125 {20, 15, 30, DIFF_EASY},
126 {20, 15, 30, DIFF_NORMAL},
127 {20, 15, 30, DIFF_HARD},
128 {20, 15, 30, DIFF_RECURSE},
129 {30, 25, 75, DIFF_NORMAL},
130 {30, 25, 75, DIFF_HARD},
131#endif
132};
133
134static int game_fetch_preset(int i, char **name, game_params **params)
135{
136 game_params *ret;
137 char str[80];
138
139 if (i < 0 || i >= lenof(map_presets))
140 return FALSE;
141
142 ret = snew(game_params);
143 *ret = map_presets[i];
144
145 sprintf(str, "%dx%d, %d regions, %s", ret->w, ret->h, ret->n,
146 map_diffnames[ret->diff]);
147
148 *name = dupstr(str);
149 *params = ret;
150 return TRUE;
151}
152
153static void free_params(game_params *params)
154{
155 sfree(params);
156}
157
158static game_params *dup_params(const game_params *params)
159{
160 game_params *ret = snew(game_params);
161 *ret = *params; /* structure copy */
162 return ret;
163}
164
165static void decode_params(game_params *params, char const *string)
166{
167 char const *p = string;
168
169 params->w = atoi(p);
170 while (*p && isdigit((unsigned char)*p)) p++;
171 if (*p == 'x') {
172 p++;
173 params->h = atoi(p);
174 while (*p && isdigit((unsigned char)*p)) p++;
175 } else {
176 params->h = params->w;
177 }
178 if (*p == 'n') {
179 p++;
180 params->n = atoi(p);
181 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
182 } else {
183 params->n = params->w * params->h / 8;
184 }
185 if (*p == 'd') {
186 int i;
187 p++;
188 for (i = 0; i < DIFFCOUNT; i++)
189 if (*p == map_diffchars[i])
190 params->diff = i;
191 if (*p) p++;
192 }
193}
194
195static char *encode_params(const game_params *params, int full)
196{
197 char ret[400];
198
199 sprintf(ret, "%dx%dn%d", params->w, params->h, params->n);
200 if (full)
201 sprintf(ret + strlen(ret), "d%c", map_diffchars[params->diff]);
202
203 return dupstr(ret);
204}
205
206static config_item *game_configure(const game_params *params)
207{
208 config_item *ret;
209 char buf[80];
210
211 ret = snewn(5, config_item);
212
213 ret[0].name = "Width";
214 ret[0].type = C_STRING;
215 sprintf(buf, "%d", params->w);
216 ret[0].sval = dupstr(buf);
217 ret[0].ival = 0;
218
219 ret[1].name = "Height";
220 ret[1].type = C_STRING;
221 sprintf(buf, "%d", params->h);
222 ret[1].sval = dupstr(buf);
223 ret[1].ival = 0;
224
225 ret[2].name = "Regions";
226 ret[2].type = C_STRING;
227 sprintf(buf, "%d", params->n);
228 ret[2].sval = dupstr(buf);
229 ret[2].ival = 0;
230
231 ret[3].name = "Difficulty";
232 ret[3].type = C_CHOICES;
233 ret[3].sval = DIFFCONFIG;
234 ret[3].ival = params->diff;
235
236 ret[4].name = NULL;
237 ret[4].type = C_END;
238 ret[4].sval = NULL;
239 ret[4].ival = 0;
240
241 return ret;
242}
243
244static game_params *custom_params(const config_item *cfg)
245{
246 game_params *ret = snew(game_params);
247
248 ret->w = atoi(cfg[0].sval);
249 ret->h = atoi(cfg[1].sval);
250 ret->n = atoi(cfg[2].sval);
251 ret->diff = cfg[3].ival;
252
253 return ret;
254}
255
256static char *validate_params(const game_params *params, int full)
257{
258 if (params->w < 2 || params->h < 2)
259 return "Width and height must be at least two";
260 if (params->n < 5)
261 return "Must have at least five regions";
262 if (params->n > params->w * params->h)
263 return "Too many regions to fit in grid";
264 return NULL;
265}
266
267/* ----------------------------------------------------------------------
268 * Cumulative frequency table functions.
269 */
270
271/*
272 * Initialise a cumulative frequency table. (Hardly worth writing
273 * this function; all it does is to initialise everything in the
274 * array to zero.)
275 */
276static void cf_init(int *table, int n)
277{
278 int i;
279
280 for (i = 0; i < n; i++)
281 table[i] = 0;
282}
283
284/*
285 * Increment the count of symbol `sym' by `count'.
286 */
287static void cf_add(int *table, int n, int sym, int count)
288{
289 int bit;
290
291 bit = 1;
292 while (sym != 0) {
293 if (sym & bit) {
294 table[sym] += count;
295 sym &= ~bit;
296 }
297 bit <<= 1;
298 }
299
300 table[0] += count;
301}
302
303/*
304 * Cumulative frequency lookup: return the total count of symbols
305 * with value less than `sym'.
306 */
307static int cf_clookup(int *table, int n, int sym)
308{
309 int bit, index, limit, count;
310
311 if (sym == 0)
312 return 0;
313
314 assert(0 < sym && sym <= n);
315
316 count = table[0]; /* start with the whole table size */
317
318 bit = 1;
319 while (bit < n)
320 bit <<= 1;
321
322 limit = n;
323
324 while (bit > 0) {
325 /*
326 * Find the least number with its lowest set bit in this
327 * position which is greater than or equal to sym.
328 */
329 index = ((sym + bit - 1) &~ (bit * 2 - 1)) + bit;
330
331 if (index < limit) {
332 count -= table[index];
333 limit = index;
334 }
335
336 bit >>= 1;
337 }
338
339 return count;
340}
341
342/*
343 * Single frequency lookup: return the count of symbol `sym'.
344 */
345static int cf_slookup(int *table, int n, int sym)
346{
347 int count, bit;
348
349 assert(0 <= sym && sym < n);
350
351 count = table[sym];
352
353 for (bit = 1; sym+bit < n && !(sym & bit); bit <<= 1)
354 count -= table[sym+bit];
355
356 return count;
357}
358
359/*
360 * Return the largest symbol index such that the cumulative
361 * frequency up to that symbol is less than _or equal to_ count.
362 */
363static int cf_whichsym(int *table, int n, int count) {
364 int bit, sym, top;
365
366 assert(count >= 0 && count < table[0]);
367
368 bit = 1;
369 while (bit < n)
370 bit <<= 1;
371
372 sym = 0;
373 top = table[0];
374
375 while (bit > 0) {
376 if (sym+bit < n) {
377 if (count >= top - table[sym+bit])
378 sym += bit;
379 else
380 top -= table[sym+bit];
381 }
382
383 bit >>= 1;
384 }
385
386 return sym;
387}
388
389/* ----------------------------------------------------------------------
390 * Map generation.
391 *
392 * FIXME: this isn't entirely optimal at present, because it
393 * inherently prioritises growing the largest region since there
394 * are more squares adjacent to it. This acts as a destabilising
395 * influence leading to a few large regions and mostly small ones.
396 * It might be better to do it some other way.
397 */
398
399#define WEIGHT_INCREASED 2 /* for increased perimeter */
400#define WEIGHT_DECREASED 4 /* for decreased perimeter */
401#define WEIGHT_UNCHANGED 3 /* for unchanged perimeter */
402
403/*
404 * Look at a square and decide which colours can be extended into
405 * it.
406 *
407 * If called with index < 0, it adds together one of
408 * WEIGHT_INCREASED, WEIGHT_DECREASED or WEIGHT_UNCHANGED for each
409 * colour that has a valid extension (according to the effect that
410 * it would have on the perimeter of the region being extended) and
411 * returns the overall total.
412 *
413 * If called with index >= 0, it returns one of the possible
414 * colours depending on the value of index, in such a way that the
415 * number of possible inputs which would give rise to a given
416 * return value correspond to the weight of that value.
417 */
418static int extend_options(int w, int h, int n, int *map,
419 int x, int y, int index)
420{
421 int c, i, dx, dy;
422 int col[8];
423 int total = 0;
424
425 if (map[y*w+x] >= 0) {
426 assert(index < 0);
427 return 0; /* can't do this square at all */
428 }
429
430 /*
431 * Fetch the eight neighbours of this square, in order around
432 * the square.
433 */
434 for (dy = -1; dy <= +1; dy++)
435 for (dx = -1; dx <= +1; dx++) {
436 int index = (dy < 0 ? 6-dx : dy > 0 ? 2+dx : 2*(1+dx));
437 if (x+dx >= 0 && x+dx < w && y+dy >= 0 && y+dy < h)
438 col[index] = map[(y+dy)*w+(x+dx)];
439 else
440 col[index] = -1;
441 }
442
443 /*
444 * Iterate over each colour that might be feasible.
445 *
446 * FIXME: this routine currently has O(n) running time. We
447 * could turn it into O(FOUR) by only bothering to iterate over
448 * the colours mentioned in the four neighbouring squares.
449 */
450
451 for (c = 0; c < n; c++) {
452 int count, neighbours, runs;
453
454 /*
455 * One of the even indices of col (representing the
456 * orthogonal neighbours of this square) must be equal to
457 * c, or else this square is not adjacent to region c and
458 * obviously cannot become an extension of it at this time.
459 */
460 neighbours = 0;
461 for (i = 0; i < 8; i += 2)
462 if (col[i] == c)
463 neighbours++;
464 if (!neighbours)
465 continue;
466
467 /*
468 * Now we know this square is adjacent to region c. The
469 * next question is, would extending it cause the region to
470 * become non-simply-connected? If so, we mustn't do it.
471 *
472 * We determine this by looking around col to see if we can
473 * find more than one separate run of colour c.
474 */
475 runs = 0;
476 for (i = 0; i < 8; i++)
477 if (col[i] == c && col[(i+1) & 7] != c)
478 runs++;
479 if (runs > 1)
480 continue;
481
482 assert(runs == 1);
483
484 /*
485 * This square is a possibility. Determine its effect on
486 * the region's perimeter (computed from the number of
487 * orthogonal neighbours - 1 means a perimeter increase, 3
488 * a decrease, 2 no change; 4 is impossible because the
489 * region would already not be simply connected) and we're
490 * done.
491 */
492 assert(neighbours > 0 && neighbours < 4);
493 count = (neighbours == 1 ? WEIGHT_INCREASED :
494 neighbours == 2 ? WEIGHT_UNCHANGED : WEIGHT_DECREASED);
495
496 total += count;
497 if (index >= 0 && index < count)
498 return c;
499 else
500 index -= count;
501 }
502
503 assert(index < 0);
504
505 return total;
506}
507
508static void genmap(int w, int h, int n, int *map, random_state *rs)
509{
510 int wh = w*h;
511 int x, y, i, k;
512 int *tmp;
513
514 assert(n <= wh);
515 tmp = snewn(wh, int);
516
517 /*
518 * Clear the map, and set up `tmp' as a list of grid indices.
519 */
520 for (i = 0; i < wh; i++) {
521 map[i] = -1;
522 tmp[i] = i;
523 }
524
525 /*
526 * Place the region seeds by selecting n members from `tmp'.
527 */
528 k = wh;
529 for (i = 0; i < n; i++) {
530 int j = random_upto(rs, k);
531 map[tmp[j]] = i;
532 tmp[j] = tmp[--k];
533 }
534
535 /*
536 * Re-initialise `tmp' as a cumulative frequency table. This
537 * will store the number of possible region colours we can
538 * extend into each square.
539 */
540 cf_init(tmp, wh);
541
542 /*
543 * Go through the grid and set up the initial cumulative
544 * frequencies.
545 */
546 for (y = 0; y < h; y++)
547 for (x = 0; x < w; x++)
548 cf_add(tmp, wh, y*w+x,
549 extend_options(w, h, n, map, x, y, -1));
550
551 /*
552 * Now repeatedly choose a square we can extend a region into,
553 * and do so.
554 */
555 while (tmp[0] > 0) {
556 int k = random_upto(rs, tmp[0]);
557 int sq;
558 int colour;
559 int xx, yy;
560
561 sq = cf_whichsym(tmp, wh, k);
562 k -= cf_clookup(tmp, wh, sq);
563 x = sq % w;
564 y = sq / w;
565 colour = extend_options(w, h, n, map, x, y, k);
566
567 map[sq] = colour;
568
569 /*
570 * Re-scan the nine cells around the one we've just
571 * modified.
572 */
573 for (yy = max(y-1, 0); yy < min(y+2, h); yy++)
574 for (xx = max(x-1, 0); xx < min(x+2, w); xx++) {
575 cf_add(tmp, wh, yy*w+xx,
576 -cf_slookup(tmp, wh, yy*w+xx) +
577 extend_options(w, h, n, map, xx, yy, -1));
578 }
579 }
580
581 /*
582 * Finally, go through and normalise the region labels into
583 * order, meaning that indistinguishable maps are actually
584 * identical.
585 */
586 for (i = 0; i < n; i++)
587 tmp[i] = -1;
588 k = 0;
589 for (i = 0; i < wh; i++) {
590 assert(map[i] >= 0);
591 if (tmp[map[i]] < 0)
592 tmp[map[i]] = k++;
593 map[i] = tmp[map[i]];
594 }
595
596 sfree(tmp);
597}
598
599/* ----------------------------------------------------------------------
600 * Functions to handle graphs.
601 */
602
603/*
604 * Having got a map in a square grid, convert it into a graph
605 * representation.
606 */
607static int gengraph(int w, int h, int n, int *map, int *graph)
608{
609 int i, j, x, y;
610
611 /*
612 * Start by setting the graph up as an adjacency matrix. We'll
613 * turn it into a list later.
614 */
615 for (i = 0; i < n*n; i++)
616 graph[i] = 0;
617
618 /*
619 * Iterate over the map looking for all adjacencies.
620 */
621 for (y = 0; y < h; y++)
622 for (x = 0; x < w; x++) {
623 int v, vx, vy;
624 v = map[y*w+x];
625 if (x+1 < w && (vx = map[y*w+(x+1)]) != v)
626 graph[v*n+vx] = graph[vx*n+v] = 1;
627 if (y+1 < h && (vy = map[(y+1)*w+x]) != v)
628 graph[v*n+vy] = graph[vy*n+v] = 1;
629 }
630
631 /*
632 * Turn the matrix into a list.
633 */
634 for (i = j = 0; i < n*n; i++)
635 if (graph[i])
636 graph[j++] = i;
637
638 return j;
639}
640
641static int graph_edge_index(int *graph, int n, int ngraph, int i, int j)
642{
643 int v = i*n+j;
644 int top, bot, mid;
645
646 bot = -1;
647 top = ngraph;
648 while (top - bot > 1) {
649 mid = (top + bot) / 2;
650 if (graph[mid] == v)
651 return mid;
652 else if (graph[mid] < v)
653 bot = mid;
654 else
655 top = mid;
656 }
657 return -1;
658}
659
660#define graph_adjacent(graph, n, ngraph, i, j) \
661 (graph_edge_index((graph), (n), (ngraph), (i), (j)) >= 0)
662
663static int graph_vertex_start(int *graph, int n, int ngraph, int i)
664{
665 int v = i*n;
666 int top, bot, mid;
667
668 bot = -1;
669 top = ngraph;
670 while (top - bot > 1) {
671 mid = (top + bot) / 2;
672 if (graph[mid] < v)
673 bot = mid;
674 else
675 top = mid;
676 }
677 return top;
678}
679
680/* ----------------------------------------------------------------------
681 * Generate a four-colouring of a graph.
682 *
683 * FIXME: it would be nice if we could convert this recursion into
684 * pseudo-recursion using some sort of explicit stack array, for
685 * the sake of the Palm port and its limited stack.
686 */
687
688static int fourcolour_recurse(int *graph, int n, int ngraph,
689 int *colouring, int *scratch, random_state *rs)
690{
691 int nfree, nvert, start, i, j, k, c, ci;
692 int cs[FOUR];
693
694 /*
695 * Find the smallest number of free colours in any uncoloured
696 * vertex, and count the number of such vertices.
697 */
698
699 nfree = FIVE; /* start off bigger than FOUR! */
700 nvert = 0;
701 for (i = 0; i < n; i++)
702 if (colouring[i] < 0 && scratch[i*FIVE+FOUR] <= nfree) {
703 if (nfree > scratch[i*FIVE+FOUR]) {
704 nfree = scratch[i*FIVE+FOUR];
705 nvert = 0;
706 }
707 nvert++;
708 }
709
710 /*
711 * If there aren't any uncoloured vertices at all, we're done.
712 */
713 if (nvert == 0)
714 return TRUE; /* we've got a colouring! */
715
716 /*
717 * Pick a random vertex in that set.
718 */
719 j = random_upto(rs, nvert);
720 for (i = 0; i < n; i++)
721 if (colouring[i] < 0 && scratch[i*FIVE+FOUR] == nfree)
722 if (j-- == 0)
723 break;
724 assert(i < n);
725 start = graph_vertex_start(graph, n, ngraph, i);
726
727 /*
728 * Loop over the possible colours for i, and recurse for each
729 * one.
730 */
731 ci = 0;
732 for (c = 0; c < FOUR; c++)
733 if (scratch[i*FIVE+c] == 0)
734 cs[ci++] = c;
735 shuffle(cs, ci, sizeof(*cs), rs);
736
737 while (ci-- > 0) {
738 c = cs[ci];
739
740 /*
741 * Fill in this colour.
742 */
743 colouring[i] = c;
744
745 /*
746 * Update the scratch space to reflect a new neighbour
747 * of this colour for each neighbour of vertex i.
748 */
749 for (j = start; j < ngraph && graph[j] < n*(i+1); j++) {
750 k = graph[j] - i*n;
751 if (scratch[k*FIVE+c] == 0)
752 scratch[k*FIVE+FOUR]--;
753 scratch[k*FIVE+c]++;
754 }
755
756 /*
757 * Recurse.
758 */
759 if (fourcolour_recurse(graph, n, ngraph, colouring, scratch, rs))
760 return TRUE; /* got one! */
761
762 /*
763 * If that didn't work, clean up and try again with a
764 * different colour.
765 */
766 for (j = start; j < ngraph && graph[j] < n*(i+1); j++) {
767 k = graph[j] - i*n;
768 scratch[k*FIVE+c]--;
769 if (scratch[k*FIVE+c] == 0)
770 scratch[k*FIVE+FOUR]++;
771 }
772 colouring[i] = -1;
773 }
774
775 /*
776 * If we reach here, we were unable to find a colouring at all.
777 * (This doesn't necessarily mean the Four Colour Theorem is
778 * violated; it might just mean we've gone down a dead end and
779 * need to back up and look somewhere else. It's only an FCT
780 * violation if we get all the way back up to the top level and
781 * still fail.)
782 */
783 return FALSE;
784}
785
786static void fourcolour(int *graph, int n, int ngraph, int *colouring,
787 random_state *rs)
788{
789 int *scratch;
790 int i;
791
792 /*
793 * For each vertex and each colour, we store the number of
794 * neighbours that have that colour. Also, we store the number
795 * of free colours for the vertex.
796 */
797 scratch = snewn(n * FIVE, int);
798 for (i = 0; i < n * FIVE; i++)
799 scratch[i] = (i % FIVE == FOUR ? FOUR : 0);
800
801 /*
802 * Clear the colouring to start with.
803 */
804 for (i = 0; i < n; i++)
805 colouring[i] = -1;
806
807 i = fourcolour_recurse(graph, n, ngraph, colouring, scratch, rs);
808 assert(i); /* by the Four Colour Theorem :-) */
809
810 sfree(scratch);
811}
812
813/* ----------------------------------------------------------------------
814 * Non-recursive solver.
815 */
816
817struct solver_scratch {
818 unsigned char *possible; /* bitmap of colours for each region */
819
820 int *graph;
821 int n;
822 int ngraph;
823
824 int *bfsqueue;
825 int *bfscolour;
826#ifdef SOLVER_DIAGNOSTICS
827 int *bfsprev;
828#endif
829
830 int depth;
831};
832
833static struct solver_scratch *new_scratch(int *graph, int n, int ngraph)
834{
835 struct solver_scratch *sc;
836
837 sc = snew(struct solver_scratch);
838 sc->graph = graph;
839 sc->n = n;
840 sc->ngraph = ngraph;
841 sc->possible = snewn(n, unsigned char);
842 sc->depth = 0;
843 sc->bfsqueue = snewn(n, int);
844 sc->bfscolour = snewn(n, int);
845#ifdef SOLVER_DIAGNOSTICS
846 sc->bfsprev = snewn(n, int);
847#endif
848
849 return sc;
850}
851
852static void free_scratch(struct solver_scratch *sc)
853{
854 sfree(sc->possible);
855 sfree(sc->bfsqueue);
856 sfree(sc->bfscolour);
857#ifdef SOLVER_DIAGNOSTICS
858 sfree(sc->bfsprev);
859#endif
860 sfree(sc);
861}
862
863/*
864 * Count the bits in a word. Only needs to cope with FOUR bits.
865 */
866static int bitcount(int word)
867{
868 assert(FOUR <= 4); /* or this needs changing */
869 word = ((word & 0xA) >> 1) + (word & 0x5);
870 word = ((word & 0xC) >> 2) + (word & 0x3);
871 return word;
872}
873
874#ifdef SOLVER_DIAGNOSTICS
875static const char colnames[FOUR] = { 'R', 'Y', 'G', 'B' };
876#endif
877
878static int place_colour(struct solver_scratch *sc,
879 int *colouring, int index, int colour
880#ifdef SOLVER_DIAGNOSTICS
881 , char *verb
882#endif
883 )
884{
885 int *graph = sc->graph, n = sc->n, ngraph = sc->ngraph;
886 int j, k;
887
888 if (!(sc->possible[index] & (1 << colour))) {
889#ifdef SOLVER_DIAGNOSTICS
890 if (verbose)
891 printf("%*scannot place %c in region %d\n", 2*sc->depth, "",
892 colnames[colour], index);
893#endif
894 return FALSE; /* can't do it */
895 }
896
897 sc->possible[index] = 1 << colour;
898 colouring[index] = colour;
899
900#ifdef SOLVER_DIAGNOSTICS
901 if (verbose)
902 printf("%*s%s %c in region %d\n", 2*sc->depth, "",
903 verb, colnames[colour], index);
904#endif
905
906 /*
907 * Rule out this colour from all the region's neighbours.
908 */
909 for (j = graph_vertex_start(graph, n, ngraph, index);
910 j < ngraph && graph[j] < n*(index+1); j++) {
911 k = graph[j] - index*n;
912#ifdef SOLVER_DIAGNOSTICS
913 if (verbose && (sc->possible[k] & (1 << colour)))
914 printf("%*s ruling out %c in region %d\n", 2*sc->depth, "",
915 colnames[colour], k);
916#endif
917 sc->possible[k] &= ~(1 << colour);
918 }
919
920 return TRUE;
921}
922
923#ifdef SOLVER_DIAGNOSTICS
924static char *colourset(char *buf, int set)
925{
926 int i;
927 char *p = buf;
928 char *sep = "";
929
930 for (i = 0; i < FOUR; i++)
931 if (set & (1 << i)) {
932 p += sprintf(p, "%s%c", sep, colnames[i]);
933 sep = ",";
934 }
935
936 return buf;
937}
938#endif
939
940/*
941 * Returns 0 for impossible, 1 for success, 2 for failure to
942 * converge (i.e. puzzle is either ambiguous or just too
943 * difficult).
944 */
945static int map_solver(struct solver_scratch *sc,
946 int *graph, int n, int ngraph, int *colouring,
947 int difficulty)
948{
949 int i;
950
951 if (sc->depth == 0) {
952 /*
953 * Initialise scratch space.
954 */
955 for (i = 0; i < n; i++)
956 sc->possible[i] = (1 << FOUR) - 1;
957
958 /*
959 * Place clues.
960 */
961 for (i = 0; i < n; i++)
962 if (colouring[i] >= 0) {
963 if (!place_colour(sc, colouring, i, colouring[i]
964#ifdef SOLVER_DIAGNOSTICS
965 , "initial clue:"
966#endif
967 )) {
968#ifdef SOLVER_DIAGNOSTICS
969 if (verbose)
970 printf("%*sinitial clue set is inconsistent\n",
971 2*sc->depth, "");
972#endif
973 return 0; /* the clues aren't even consistent! */
974 }
975 }
976 }
977
978 /*
979 * Now repeatedly loop until we find nothing further to do.
980 */
981 while (1) {
982 int done_something = FALSE;
983
984 if (difficulty < DIFF_EASY)
985 break; /* can't do anything at all! */
986
987 /*
988 * Simplest possible deduction: find a region with only one
989 * possible colour.
990 */
991 for (i = 0; i < n; i++) if (colouring[i] < 0) {
992 int p = sc->possible[i];
993
994 if (p == 0) {
995#ifdef SOLVER_DIAGNOSTICS
996 if (verbose)
997 printf("%*sregion %d has no possible colours left\n",
998 2*sc->depth, "", i);
999#endif
1000 return 0; /* puzzle is inconsistent */
1001 }
1002
1003 if ((p & (p-1)) == 0) { /* p is a power of two */
1004 int c, ret;
1005 for (c = 0; c < FOUR; c++)
1006 if (p == (1 << c))
1007 break;
1008 assert(c < FOUR);
1009 ret = place_colour(sc, colouring, i, c
1010#ifdef SOLVER_DIAGNOSTICS
1011 , "placing"
1012#endif
1013 );
1014 /*
1015 * place_colour() can only fail if colour c was not
1016 * even a _possibility_ for region i, and we're
1017 * pretty sure it was because we checked before
1018 * calling place_colour(). So we can safely assert
1019 * here rather than having to return a nice
1020 * friendly error code.
1021 */
1022 assert(ret);
1023 done_something = TRUE;
1024 }
1025 }
1026
1027 if (done_something)
1028 continue;
1029
1030 if (difficulty < DIFF_NORMAL)
1031 break; /* can't do anything harder */
1032
1033 /*
1034 * Failing that, go up one level. Look for pairs of regions
1035 * which (a) both have the same pair of possible colours,
1036 * (b) are adjacent to one another, (c) are adjacent to the
1037 * same region, and (d) that region still thinks it has one
1038 * or both of those possible colours.
1039 *
1040 * Simplest way to do this is by going through the graph
1041 * edge by edge, so that we start with property (b) and
1042 * then look for (a) and finally (c) and (d).
1043 */
1044 for (i = 0; i < ngraph; i++) {
1045 int j1 = graph[i] / n, j2 = graph[i] % n;
1046 int j, k, v, v2;
1047#ifdef SOLVER_DIAGNOSTICS
1048 int started = FALSE;
1049#endif
1050
1051 if (j1 > j2)
1052 continue; /* done it already, other way round */
1053
1054 if (colouring[j1] >= 0 || colouring[j2] >= 0)
1055 continue; /* they're not undecided */
1056
1057 if (sc->possible[j1] != sc->possible[j2])
1058 continue; /* they don't have the same possibles */
1059
1060 v = sc->possible[j1];
1061 /*
1062 * See if v contains exactly two set bits.
1063 */
1064 v2 = v & -v; /* find lowest set bit */
1065 v2 = v & ~v2; /* clear it */
1066 if (v2 == 0 || (v2 & (v2-1)) != 0) /* not power of 2 */
1067 continue;
1068
1069 /*
1070 * We've found regions j1 and j2 satisfying properties
1071 * (a) and (b): they have two possible colours between
1072 * them, and since they're adjacent to one another they
1073 * must use _both_ those colours between them.
1074 * Therefore, if they are both adjacent to any other
1075 * region then that region cannot be either colour.
1076 *
1077 * Go through the neighbours of j1 and see if any are
1078 * shared with j2.
1079 */
1080 for (j = graph_vertex_start(graph, n, ngraph, j1);
1081 j < ngraph && graph[j] < n*(j1+1); j++) {
1082 k = graph[j] - j1*n;
1083 if (graph_adjacent(graph, n, ngraph, k, j2) &&
1084 (sc->possible[k] & v)) {
1085#ifdef SOLVER_DIAGNOSTICS
1086 if (verbose) {
1087 char buf[80];
1088 if (!started)
1089 printf("%*sadjacent regions %d,%d share colours"
1090 " %s\n", 2*sc->depth, "", j1, j2,
1091 colourset(buf, v));
1092 started = TRUE;
1093 printf("%*s ruling out %s in region %d\n",2*sc->depth,
1094 "", colourset(buf, sc->possible[k] & v), k);
1095 }
1096#endif
1097 sc->possible[k] &= ~v;
1098 done_something = TRUE;
1099 }
1100 }
1101 }
1102
1103 if (done_something)
1104 continue;
1105
1106 if (difficulty < DIFF_HARD)
1107 break; /* can't do anything harder */
1108
1109 /*
1110 * Right; now we get creative. Now we're going to look for
1111 * `forcing chains'. A forcing chain is a path through the
1112 * graph with the following properties:
1113 *
1114 * (a) Each vertex on the path has precisely two possible
1115 * colours.
1116 *
1117 * (b) Each pair of vertices which are adjacent on the
1118 * path share at least one possible colour in common.
1119 *
1120 * (c) Each vertex in the middle of the path shares _both_
1121 * of its colours with at least one of its neighbours
1122 * (not the same one with both neighbours).
1123 *
1124 * These together imply that at least one of the possible
1125 * colour choices at one end of the path forces _all_ the
1126 * rest of the colours along the path. In order to make
1127 * real use of this, we need further properties:
1128 *
1129 * (c) Ruling out some colour C from the vertex at one end
1130 * of the path forces the vertex at the other end to
1131 * take colour C.
1132 *
1133 * (d) The two end vertices are mutually adjacent to some
1134 * third vertex.
1135 *
1136 * (e) That third vertex currently has C as a possibility.
1137 *
1138 * If we can find all of that lot, we can deduce that at
1139 * least one of the two ends of the forcing chain has
1140 * colour C, and that therefore the mutually adjacent third
1141 * vertex does not.
1142 *
1143 * To find forcing chains, we're going to start a bfs at
1144 * each suitable vertex of the graph, once for each of its
1145 * two possible colours.
1146 */
1147 for (i = 0; i < n; i++) {
1148 int c;
1149
1150 if (colouring[i] >= 0 || bitcount(sc->possible[i]) != 2)
1151 continue;
1152
1153 for (c = 0; c < FOUR; c++)
1154 if (sc->possible[i] & (1 << c)) {
1155 int j, k, gi, origc, currc, head, tail;
1156 /*
1157 * Try a bfs from this vertex, ruling out
1158 * colour c.
1159 *
1160 * Within this loop, we work in colour bitmaps
1161 * rather than actual colours, because
1162 * converting back and forth is a needless
1163 * computational expense.
1164 */
1165
1166 origc = 1 << c;
1167
1168 for (j = 0; j < n; j++) {
1169 sc->bfscolour[j] = -1;
1170#ifdef SOLVER_DIAGNOSTICS
1171 sc->bfsprev[j] = -1;
1172#endif
1173 }
1174 head = tail = 0;
1175 sc->bfsqueue[tail++] = i;
1176 sc->bfscolour[i] = sc->possible[i] &~ origc;
1177
1178 while (head < tail) {
1179 j = sc->bfsqueue[head++];
1180 currc = sc->bfscolour[j];
1181
1182 /*
1183 * Try neighbours of j.
1184 */
1185 for (gi = graph_vertex_start(graph, n, ngraph, j);
1186 gi < ngraph && graph[gi] < n*(j+1); gi++) {
1187 k = graph[gi] - j*n;
1188
1189 /*
1190 * To continue with the bfs in vertex
1191 * k, we need k to be
1192 * (a) not already visited
1193 * (b) have two possible colours
1194 * (c) those colours include currc.
1195 */
1196
1197 if (sc->bfscolour[k] < 0 &&
1198 colouring[k] < 0 &&
1199 bitcount(sc->possible[k]) == 2 &&
1200 (sc->possible[k] & currc)) {
1201 sc->bfsqueue[tail++] = k;
1202 sc->bfscolour[k] =
1203 sc->possible[k] &~ currc;
1204#ifdef SOLVER_DIAGNOSTICS
1205 sc->bfsprev[k] = j;
1206#endif
1207 }
1208
1209 /*
1210 * One other possibility is that k
1211 * might be the region in which we can
1212 * make a real deduction: if it's
1213 * adjacent to i, contains currc as a
1214 * possibility, and currc is equal to
1215 * the original colour we ruled out.
1216 */
1217 if (currc == origc &&
1218 graph_adjacent(graph, n, ngraph, k, i) &&
1219 (sc->possible[k] & currc)) {
1220#ifdef SOLVER_DIAGNOSTICS
1221 if (verbose) {
1222 char buf[80], *sep = "";
1223 int r;
1224
1225 printf("%*sforcing chain, colour %s, ",
1226 2*sc->depth, "",
1227 colourset(buf, origc));
1228 for (r = j; r != -1; r = sc->bfsprev[r]) {
1229 printf("%s%d", sep, r);
1230 sep = "-";
1231 }
1232 printf("\n%*s ruling out %s in region"
1233 " %d\n", 2*sc->depth, "",
1234 colourset(buf, origc), k);
1235 }
1236#endif
1237 sc->possible[k] &= ~origc;
1238 done_something = TRUE;
1239 }
1240 }
1241 }
1242
1243 assert(tail <= n);
1244 }
1245 }
1246
1247 if (!done_something)
1248 break;
1249 }
1250
1251 /*
1252 * See if we've got a complete solution, and return if so.
1253 */
1254 for (i = 0; i < n; i++)
1255 if (colouring[i] < 0)
1256 break;
1257 if (i == n) {
1258#ifdef SOLVER_DIAGNOSTICS
1259 if (verbose)
1260 printf("%*sone solution found\n", 2*sc->depth, "");
1261#endif
1262 return 1; /* success! */
1263 }
1264
1265 /*
1266 * If recursion is not permissible, we now give up.
1267 */
1268 if (difficulty < DIFF_RECURSE) {
1269#ifdef SOLVER_DIAGNOSTICS
1270 if (verbose)
1271 printf("%*sunable to proceed further without recursion\n",
1272 2*sc->depth, "");
1273#endif
1274 return 2; /* unable to complete */
1275 }
1276
1277 /*
1278 * Now we've got to do something recursive. So first hunt for a
1279 * currently-most-constrained region.
1280 */
1281 {
1282 int best, bestc;
1283 struct solver_scratch *rsc;
1284 int *subcolouring, *origcolouring;
1285 int ret, subret;
1286 int we_already_got_one;
1287
1288 best = -1;
1289 bestc = FIVE;
1290
1291 for (i = 0; i < n; i++) if (colouring[i] < 0) {
1292 int p = sc->possible[i];
1293 enum { compile_time_assertion = 1 / (FOUR <= 4) };
1294 int c;
1295
1296 /* Count the set bits. */
1297 c = (p & 5) + ((p >> 1) & 5);
1298 c = (c & 3) + ((c >> 2) & 3);
1299 assert(c > 1); /* or colouring[i] would be >= 0 */
1300
1301 if (c < bestc) {
1302 best = i;
1303 bestc = c;
1304 }
1305 }
1306
1307 assert(best >= 0); /* or we'd be solved already */
1308
1309#ifdef SOLVER_DIAGNOSTICS
1310 if (verbose)
1311 printf("%*srecursing on region %d\n", 2*sc->depth, "", best);
1312#endif
1313
1314 /*
1315 * Now iterate over the possible colours for this region.
1316 */
1317 rsc = new_scratch(graph, n, ngraph);
1318 rsc->depth = sc->depth + 1;
1319 origcolouring = snewn(n, int);
1320 memcpy(origcolouring, colouring, n * sizeof(int));
1321 subcolouring = snewn(n, int);
1322 we_already_got_one = FALSE;
1323 ret = 0;
1324
1325 for (i = 0; i < FOUR; i++) {
1326 if (!(sc->possible[best] & (1 << i)))
1327 continue;
1328
1329 memcpy(rsc->possible, sc->possible, n);
1330 memcpy(subcolouring, origcolouring, n * sizeof(int));
1331
1332 place_colour(rsc, subcolouring, best, i
1333#ifdef SOLVER_DIAGNOSTICS
1334 , "trying"
1335#endif
1336 );
1337
1338 subret = map_solver(rsc, graph, n, ngraph,
1339 subcolouring, difficulty);
1340
1341#ifdef SOLVER_DIAGNOSTICS
1342 if (verbose) {
1343 printf("%*sretracting %c in region %d; found %s\n",
1344 2*sc->depth, "", colnames[i], best,
1345 subret == 0 ? "no solutions" :
1346 subret == 1 ? "one solution" : "multiple solutions");
1347 }
1348#endif
1349
1350 /*
1351 * If this possibility turned up more than one valid
1352 * solution, or if it turned up one and we already had
1353 * one, we're definitely ambiguous.
1354 */
1355 if (subret == 2 || (subret == 1 && we_already_got_one)) {
1356 ret = 2;
1357 break;
1358 }
1359
1360 /*
1361 * If this possibility turned up one valid solution and
1362 * it's the first we've seen, copy it into the output.
1363 */
1364 if (subret == 1) {
1365 memcpy(colouring, subcolouring, n * sizeof(int));
1366 we_already_got_one = TRUE;
1367 ret = 1;
1368 }
1369
1370 /*
1371 * Otherwise, this guess led to a contradiction, so we
1372 * do nothing.
1373 */
1374 }
1375
1376 sfree(origcolouring);
1377 sfree(subcolouring);
1378 free_scratch(rsc);
1379
1380#ifdef SOLVER_DIAGNOSTICS
1381 if (verbose && sc->depth == 0) {
1382 printf("%*s%s found\n",
1383 2*sc->depth, "",
1384 ret == 0 ? "no solutions" :
1385 ret == 1 ? "one solution" : "multiple solutions");
1386 }
1387#endif
1388 return ret;
1389 }
1390}
1391
1392/* ----------------------------------------------------------------------
1393 * Game generation main function.
1394 */
1395
1396static char *new_game_desc(const game_params *params, random_state *rs,
1397 char **aux, int interactive)
1398{
1399 struct solver_scratch *sc = NULL;
1400 int *map, *graph, ngraph, *colouring, *colouring2, *regions;
1401 int i, j, w, h, n, solveret, cfreq[FOUR];
1402 int wh;
1403 int mindiff, tries;
1404#ifdef GENERATION_DIAGNOSTICS
1405 int x, y;
1406#endif
1407 char *ret, buf[80];
1408 int retlen, retsize;
1409
1410 w = params->w;
1411 h = params->h;
1412 n = params->n;
1413 wh = w*h;
1414
1415 *aux = NULL;
1416
1417 map = snewn(wh, int);
1418 graph = snewn(n*n, int);
1419 colouring = snewn(n, int);
1420 colouring2 = snewn(n, int);
1421 regions = snewn(n, int);
1422
1423 /*
1424 * This is the minimum difficulty below which we'll completely
1425 * reject a map design. Normally we set this to one below the
1426 * requested difficulty, ensuring that we have the right
1427 * result. However, for particularly dense maps or maps with
1428 * particularly few regions it might not be possible to get the
1429 * desired difficulty, so we will eventually drop this down to
1430 * -1 to indicate that any old map will do.
1431 */
1432 mindiff = params->diff;
1433 tries = 50;
1434
1435 while (1) {
1436
1437 /*
1438 * Create the map.
1439 */
1440 genmap(w, h, n, map, rs);
1441
1442#ifdef GENERATION_DIAGNOSTICS
1443 for (y = 0; y < h; y++) {
1444 for (x = 0; x < w; x++) {
1445 int v = map[y*w+x];
1446 if (v >= 62)
1447 putchar('!');
1448 else if (v >= 36)
1449 putchar('a' + v-36);
1450 else if (v >= 10)
1451 putchar('A' + v-10);
1452 else
1453 putchar('0' + v);
1454 }
1455 putchar('\n');
1456 }
1457#endif
1458
1459 /*
1460 * Convert the map into a graph.
1461 */
1462 ngraph = gengraph(w, h, n, map, graph);
1463
1464#ifdef GENERATION_DIAGNOSTICS
1465 for (i = 0; i < ngraph; i++)
1466 printf("%d-%d\n", graph[i]/n, graph[i]%n);
1467#endif
1468
1469 /*
1470 * Colour the map.
1471 */
1472 fourcolour(graph, n, ngraph, colouring, rs);
1473
1474#ifdef GENERATION_DIAGNOSTICS
1475 for (i = 0; i < n; i++)
1476 printf("%d: %d\n", i, colouring[i]);
1477
1478 for (y = 0; y < h; y++) {
1479 for (x = 0; x < w; x++) {
1480 int v = colouring[map[y*w+x]];
1481 if (v >= 36)
1482 putchar('a' + v-36);
1483 else if (v >= 10)
1484 putchar('A' + v-10);
1485 else
1486 putchar('0' + v);
1487 }
1488 putchar('\n');
1489 }
1490#endif
1491
1492 /*
1493 * Encode the solution as an aux string.
1494 */
1495 if (*aux) /* in case we've come round again */
1496 sfree(*aux);
1497 retlen = retsize = 0;
1498 ret = NULL;
1499 for (i = 0; i < n; i++) {
1500 int len;
1501
1502 if (colouring[i] < 0)
1503 continue;
1504
1505 len = sprintf(buf, "%s%d:%d", i ? ";" : "S;", colouring[i], i);
1506 if (retlen + len >= retsize) {
1507 retsize = retlen + len + 256;
1508 ret = sresize(ret, retsize, char);
1509 }
1510 strcpy(ret + retlen, buf);
1511 retlen += len;
1512 }
1513 *aux = ret;
1514
1515 /*
1516 * Remove the region colours one by one, keeping
1517 * solubility. Also ensure that there always remains at
1518 * least one region of every colour, so that the user can
1519 * drag from somewhere.
1520 */
1521 for (i = 0; i < FOUR; i++)
1522 cfreq[i] = 0;
1523 for (i = 0; i < n; i++) {
1524 regions[i] = i;
1525 cfreq[colouring[i]]++;
1526 }
1527 for (i = 0; i < FOUR; i++)
1528 if (cfreq[i] == 0)
1529 continue;
1530
1531 shuffle(regions, n, sizeof(*regions), rs);
1532
1533 if (sc) free_scratch(sc);
1534 sc = new_scratch(graph, n, ngraph);
1535
1536 for (i = 0; i < n; i++) {
1537 j = regions[i];
1538
1539 if (cfreq[colouring[j]] == 1)
1540 continue; /* can't remove last region of colour */
1541
1542 memcpy(colouring2, colouring, n*sizeof(int));
1543 colouring2[j] = -1;
1544 solveret = map_solver(sc, graph, n, ngraph, colouring2,
1545 params->diff);
1546 assert(solveret >= 0); /* mustn't be impossible! */
1547 if (solveret == 1) {
1548 cfreq[colouring[j]]--;
1549 colouring[j] = -1;
1550 }
1551 }
1552
1553#ifdef GENERATION_DIAGNOSTICS
1554 for (i = 0; i < n; i++)
1555 if (colouring[i] >= 0) {
1556 if (i >= 62)
1557 putchar('!');
1558 else if (i >= 36)
1559 putchar('a' + i-36);
1560 else if (i >= 10)
1561 putchar('A' + i-10);
1562 else
1563 putchar('0' + i);
1564 printf(": %d\n", colouring[i]);
1565 }
1566#endif
1567
1568 /*
1569 * Finally, check that the puzzle is _at least_ as hard as
1570 * required, and indeed that it isn't already solved.
1571 * (Calling map_solver with negative difficulty ensures the
1572 * latter - if a solver which _does nothing_ can solve it,
1573 * it's too easy!)
1574 */
1575 memcpy(colouring2, colouring, n*sizeof(int));
1576 if (map_solver(sc, graph, n, ngraph, colouring2,
1577 mindiff - 1) == 1) {
1578 /*
1579 * Drop minimum difficulty if necessary.
1580 */
1581 if (mindiff > 0 && (n < 9 || n > 2*wh/3)) {
1582 if (tries-- <= 0)
1583 mindiff = 0; /* give up and go for Easy */
1584 }
1585 continue;
1586 }
1587
1588 break;
1589 }
1590
1591 /*
1592 * Encode as a game ID. We do this by:
1593 *
1594 * - first going along the horizontal edges row by row, and
1595 * then the vertical edges column by column
1596 * - encoding the lengths of runs of edges and runs of
1597 * non-edges
1598 * - the decoder will reconstitute the region boundaries from
1599 * this and automatically number them the same way we did
1600 * - then we encode the initial region colours in a Slant-like
1601 * fashion (digits 0-3 interspersed with letters giving
1602 * lengths of runs of empty spaces).
1603 */
1604 retlen = retsize = 0;
1605 ret = NULL;
1606
1607 {
1608 int run, pv;
1609
1610 /*
1611 * Start with a notional non-edge, so that there'll be an
1612 * explicit `a' to distinguish the case where we start with
1613 * an edge.
1614 */
1615 run = 1;
1616 pv = 0;
1617
1618 for (i = 0; i < w*(h-1) + (w-1)*h; i++) {
1619 int x, y, dx, dy, v;
1620
1621 if (i < w*(h-1)) {
1622 /* Horizontal edge. */
1623 y = i / w;
1624 x = i % w;
1625 dx = 0;
1626 dy = 1;
1627 } else {
1628 /* Vertical edge. */
1629 x = (i - w*(h-1)) / h;
1630 y = (i - w*(h-1)) % h;
1631 dx = 1;
1632 dy = 0;
1633 }
1634
1635 if (retlen + 10 >= retsize) {
1636 retsize = retlen + 256;
1637 ret = sresize(ret, retsize, char);
1638 }
1639
1640 v = (map[y*w+x] != map[(y+dy)*w+(x+dx)]);
1641
1642 if (pv != v) {
1643 ret[retlen++] = 'a'-1 + run;
1644 run = 1;
1645 pv = v;
1646 } else {
1647 /*
1648 * 'z' is a special case in this encoding. Rather
1649 * than meaning a run of 26 and a state switch, it
1650 * means a run of 25 and _no_ state switch, because
1651 * otherwise there'd be no way to encode runs of
1652 * more than 26.
1653 */
1654 if (run == 25) {
1655 ret[retlen++] = 'z';
1656 run = 0;
1657 }
1658 run++;
1659 }
1660 }
1661
1662 ret[retlen++] = 'a'-1 + run;
1663 ret[retlen++] = ',';
1664
1665 run = 0;
1666 for (i = 0; i < n; i++) {
1667 if (retlen + 10 >= retsize) {
1668 retsize = retlen + 256;
1669 ret = sresize(ret, retsize, char);
1670 }
1671
1672 if (colouring[i] < 0) {
1673 /*
1674 * In _this_ encoding, 'z' is a run of 26, since
1675 * there's no implicit state switch after each run.
1676 * Confusingly different, but more compact.
1677 */
1678 if (run == 26) {
1679 ret[retlen++] = 'z';
1680 run = 0;
1681 }
1682 run++;
1683 } else {
1684 if (run > 0)
1685 ret[retlen++] = 'a'-1 + run;
1686 ret[retlen++] = '0' + colouring[i];
1687 run = 0;
1688 }
1689 }
1690 if (run > 0)
1691 ret[retlen++] = 'a'-1 + run;
1692 ret[retlen] = '\0';
1693
1694 assert(retlen < retsize);
1695 }
1696
1697 free_scratch(sc);
1698 sfree(regions);
1699 sfree(colouring2);
1700 sfree(colouring);
1701 sfree(graph);
1702 sfree(map);
1703
1704 return ret;
1705}
1706
1707static char *parse_edge_list(const game_params *params, const char **desc,
1708 int *map)
1709{
1710 int w = params->w, h = params->h, wh = w*h, n = params->n;
1711 int i, k, pos, state;
1712 const char *p = *desc;
1713
1714 dsf_init(map+wh, wh);
1715
1716 pos = -1;
1717 state = 0;
1718
1719 /*
1720 * Parse the game description to get the list of edges, and
1721 * build up a disjoint set forest as we go (by identifying
1722 * pairs of squares whenever the edge list shows a non-edge).
1723 */
1724 while (*p && *p != ',') {
1725 if (*p < 'a' || *p > 'z')
1726 return "Unexpected character in edge list";
1727 if (*p == 'z')
1728 k = 25;
1729 else
1730 k = *p - 'a' + 1;
1731 while (k-- > 0) {
1732 int x, y, dx, dy;
1733
1734 if (pos < 0) {
1735 pos++;
1736 continue;
1737 } else if (pos < w*(h-1)) {
1738 /* Horizontal edge. */
1739 y = pos / w;
1740 x = pos % w;
1741 dx = 0;
1742 dy = 1;
1743 } else if (pos < 2*wh-w-h) {
1744 /* Vertical edge. */
1745 x = (pos - w*(h-1)) / h;
1746 y = (pos - w*(h-1)) % h;
1747 dx = 1;
1748 dy = 0;
1749 } else
1750 return "Too much data in edge list";
1751 if (!state)
1752 dsf_merge(map+wh, y*w+x, (y+dy)*w+(x+dx));
1753
1754 pos++;
1755 }
1756 if (*p != 'z')
1757 state = !state;
1758 p++;
1759 }
1760 assert(pos <= 2*wh-w-h);
1761 if (pos < 2*wh-w-h)
1762 return "Too little data in edge list";
1763
1764 /*
1765 * Now go through again and allocate region numbers.
1766 */
1767 pos = 0;
1768 for (i = 0; i < wh; i++)
1769 map[i] = -1;
1770 for (i = 0; i < wh; i++) {
1771 k = dsf_canonify(map+wh, i);
1772 if (map[k] < 0)
1773 map[k] = pos++;
1774 map[i] = map[k];
1775 }
1776 if (pos != n)
1777 return "Edge list defines the wrong number of regions";
1778
1779 *desc = p;
1780
1781 return NULL;
1782}
1783
1784static char *validate_desc(const game_params *params, const char *desc)
1785{
1786 int w = params->w, h = params->h, wh = w*h, n = params->n;
1787 int area;
1788 int *map;
1789 char *ret;
1790
1791 map = snewn(2*wh, int);
1792 ret = parse_edge_list(params, &desc, map);
1793 sfree(map);
1794 if (ret)
1795 return ret;
1796
1797 if (*desc != ',')
1798 return "Expected comma before clue list";
1799 desc++; /* eat comma */
1800
1801 area = 0;
1802 while (*desc) {
1803 if (*desc >= '0' && *desc < '0'+FOUR)
1804 area++;
1805 else if (*desc >= 'a' && *desc <= 'z')
1806 area += *desc - 'a' + 1;
1807 else
1808 return "Unexpected character in clue list";
1809 desc++;
1810 }
1811 if (area < n)
1812 return "Too little data in clue list";
1813 else if (area > n)
1814 return "Too much data in clue list";
1815
1816 return NULL;
1817}
1818
1819static game_state *new_game(midend *me, const game_params *params,
1820 const char *desc)
1821{
1822 int w = params->w, h = params->h, wh = w*h, n = params->n;
1823 int i, pos;
1824 const char *p;
1825 game_state *state = snew(game_state);
1826
1827 state->p = *params;
1828 state->colouring = snewn(n, int);
1829 for (i = 0; i < n; i++)
1830 state->colouring[i] = -1;
1831 state->pencil = snewn(n, int);
1832 for (i = 0; i < n; i++)
1833 state->pencil[i] = 0;
1834
1835 state->completed = state->cheated = FALSE;
1836
1837 state->map = snew(struct map);
1838 state->map->refcount = 1;
1839 state->map->map = snewn(wh*4, int);
1840 state->map->graph = snewn(n*n, int);
1841 state->map->n = n;
1842 state->map->immutable = snewn(n, int);
1843 for (i = 0; i < n; i++)
1844 state->map->immutable[i] = FALSE;
1845
1846 p = desc;
1847
1848 {
1849 char *ret;
1850 ret = parse_edge_list(params, &p, state->map->map);
1851 assert(!ret);
1852 }
1853
1854 /*
1855 * Set up the other three quadrants in `map'.
1856 */
1857 for (i = wh; i < 4*wh; i++)
1858 state->map->map[i] = state->map->map[i % wh];
1859
1860 assert(*p == ',');
1861 p++;
1862
1863 /*
1864 * Now process the clue list.
1865 */
1866 pos = 0;
1867 while (*p) {
1868 if (*p >= '0' && *p < '0'+FOUR) {
1869 state->colouring[pos] = *p - '0';
1870 state->map->immutable[pos] = TRUE;
1871 pos++;
1872 } else {
1873 assert(*p >= 'a' && *p <= 'z');
1874 pos += *p - 'a' + 1;
1875 }
1876 p++;
1877 }
1878 assert(pos == n);
1879
1880 state->map->ngraph = gengraph(w, h, n, state->map->map, state->map->graph);
1881
1882 /*
1883 * Attempt to smooth out some of the more jagged region
1884 * outlines by the judicious use of diagonally divided squares.
1885 */
1886 {
1887 random_state *rs = random_new(desc, strlen(desc));
1888 int *squares = snewn(wh, int);
1889 int done_something;
1890
1891 for (i = 0; i < wh; i++)
1892 squares[i] = i;
1893 shuffle(squares, wh, sizeof(*squares), rs);
1894
1895 do {
1896 done_something = FALSE;
1897 for (i = 0; i < wh; i++) {
1898 int y = squares[i] / w, x = squares[i] % w;
1899 int c = state->map->map[y*w+x];
1900 int tc, bc, lc, rc;
1901
1902 if (x == 0 || x == w-1 || y == 0 || y == h-1)
1903 continue;
1904
1905 if (state->map->map[TE * wh + y*w+x] !=
1906 state->map->map[BE * wh + y*w+x])
1907 continue;
1908
1909 tc = state->map->map[BE * wh + (y-1)*w+x];
1910 bc = state->map->map[TE * wh + (y+1)*w+x];
1911 lc = state->map->map[RE * wh + y*w+(x-1)];
1912 rc = state->map->map[LE * wh + y*w+(x+1)];
1913
1914 /*
1915 * If this square is adjacent on two sides to one
1916 * region and on the other two sides to the other
1917 * region, and is itself one of the two regions, we can
1918 * adjust it so that it's a diagonal.
1919 */
1920 if (tc != bc && (tc == c || bc == c)) {
1921 if ((lc == tc && rc == bc) ||
1922 (lc == bc && rc == tc)) {
1923 state->map->map[TE * wh + y*w+x] = tc;
1924 state->map->map[BE * wh + y*w+x] = bc;
1925 state->map->map[LE * wh + y*w+x] = lc;
1926 state->map->map[RE * wh + y*w+x] = rc;
1927 done_something = TRUE;
1928 }
1929 }
1930 }
1931 } while (done_something);
1932 sfree(squares);
1933 random_free(rs);
1934 }
1935
1936 /*
1937 * Analyse the map to find a canonical line segment
1938 * corresponding to each edge, and a canonical point
1939 * corresponding to each region. The former are where we'll
1940 * eventually put error markers; the latter are where we'll put
1941 * per-region flags such as numbers (when in diagnostic mode).
1942 */
1943 {
1944 int *bestx, *besty, *an, pass;
1945 float *ax, *ay, *best;
1946
1947 ax = snewn(state->map->ngraph + n, float);
1948 ay = snewn(state->map->ngraph + n, float);
1949 an = snewn(state->map->ngraph + n, int);
1950 bestx = snewn(state->map->ngraph + n, int);
1951 besty = snewn(state->map->ngraph + n, int);
1952 best = snewn(state->map->ngraph + n, float);
1953
1954 for (i = 0; i < state->map->ngraph + n; i++) {
1955 bestx[i] = besty[i] = -1;
1956 best[i] = (float)(2*(w+h)+1);
1957 ax[i] = ay[i] = 0.0F;
1958 an[i] = 0;
1959 }
1960
1961 /*
1962 * We make two passes over the map, finding all the line
1963 * segments separating regions and all the suitable points
1964 * within regions. In the first pass, we compute the
1965 * _average_ x and y coordinate of all the points in a
1966 * given class; in the second pass, for each such average
1967 * point, we find the candidate closest to it and call that
1968 * canonical.
1969 *
1970 * Line segments are considered to have coordinates in
1971 * their centre. Thus, at least one coordinate for any line
1972 * segment is always something-and-a-half; so we store our
1973 * coordinates as twice their normal value.
1974 */
1975 for (pass = 0; pass < 2; pass++) {
1976 int x, y;
1977
1978 for (y = 0; y < h; y++)
1979 for (x = 0; x < w; x++) {
1980 int ex[4], ey[4], ea[4], eb[4], en = 0;
1981
1982 /*
1983 * Look for an edge to the right of this
1984 * square, an edge below it, and an edge in the
1985 * middle of it. Also look to see if the point
1986 * at the bottom right of this square is on an
1987 * edge (and isn't a place where more than two
1988 * regions meet).
1989 */
1990 if (x+1 < w) {
1991 /* right edge */
1992 ea[en] = state->map->map[RE * wh + y*w+x];
1993 eb[en] = state->map->map[LE * wh + y*w+(x+1)];
1994 ex[en] = (x+1)*2;
1995 ey[en] = y*2+1;
1996 en++;
1997 }
1998 if (y+1 < h) {
1999 /* bottom edge */
2000 ea[en] = state->map->map[BE * wh + y*w+x];
2001 eb[en] = state->map->map[TE * wh + (y+1)*w+x];
2002 ex[en] = x*2+1;
2003 ey[en] = (y+1)*2;
2004 en++;
2005 }
2006 /* diagonal edge */
2007 ea[en] = state->map->map[TE * wh + y*w+x];
2008 eb[en] = state->map->map[BE * wh + y*w+x];
2009 ex[en] = x*2+1;
2010 ey[en] = y*2+1;
2011 en++;
2012
2013 if (x+1 < w && y+1 < h) {
2014 /* bottom right corner */
2015 int oct[8], othercol, nchanges;
2016 oct[0] = state->map->map[RE * wh + y*w+x];
2017 oct[1] = state->map->map[LE * wh + y*w+(x+1)];
2018 oct[2] = state->map->map[BE * wh + y*w+(x+1)];
2019 oct[3] = state->map->map[TE * wh + (y+1)*w+(x+1)];
2020 oct[4] = state->map->map[LE * wh + (y+1)*w+(x+1)];
2021 oct[5] = state->map->map[RE * wh + (y+1)*w+x];
2022 oct[6] = state->map->map[TE * wh + (y+1)*w+x];
2023 oct[7] = state->map->map[BE * wh + y*w+x];
2024
2025 othercol = -1;
2026 nchanges = 0;
2027 for (i = 0; i < 8; i++) {
2028 if (oct[i] != oct[0]) {
2029 if (othercol < 0)
2030 othercol = oct[i];
2031 else if (othercol != oct[i])
2032 break; /* three colours at this point */
2033 }
2034 if (oct[i] != oct[(i+1) & 7])
2035 nchanges++;
2036 }
2037
2038 /*
2039 * Now if there are exactly two regions at
2040 * this point (not one, and not three or
2041 * more), and only two changes around the
2042 * loop, then this is a valid place to put
2043 * an error marker.
2044 */
2045 if (i == 8 && othercol >= 0 && nchanges == 2) {
2046 ea[en] = oct[0];
2047 eb[en] = othercol;
2048 ex[en] = (x+1)*2;
2049 ey[en] = (y+1)*2;
2050 en++;
2051 }
2052
2053 /*
2054 * If there's exactly _one_ region at this
2055 * point, on the other hand, it's a valid
2056 * place to put a region centre.
2057 */
2058 if (othercol < 0) {
2059 ea[en] = eb[en] = oct[0];
2060 ex[en] = (x+1)*2;
2061 ey[en] = (y+1)*2;
2062 en++;
2063 }
2064 }
2065
2066 /*
2067 * Now process the points we've found, one by
2068 * one.
2069 */
2070 for (i = 0; i < en; i++) {
2071 int emin = min(ea[i], eb[i]);
2072 int emax = max(ea[i], eb[i]);
2073 int gindex;
2074
2075 if (emin != emax) {
2076 /* Graph edge */
2077 gindex =
2078 graph_edge_index(state->map->graph, n,
2079 state->map->ngraph, emin,
2080 emax);
2081 } else {
2082 /* Region number */
2083 gindex = state->map->ngraph + emin;
2084 }
2085
2086 assert(gindex >= 0);
2087
2088 if (pass == 0) {
2089 /*
2090 * In pass 0, accumulate the values
2091 * we'll use to compute the average
2092 * positions.
2093 */
2094 ax[gindex] += ex[i];
2095 ay[gindex] += ey[i];
2096 an[gindex] += 1;
2097 } else {
2098 /*
2099 * In pass 1, work out whether this
2100 * point is closer to the average than
2101 * the last one we've seen.
2102 */
2103 float dx, dy, d;
2104
2105 assert(an[gindex] > 0);
2106 dx = ex[i] - ax[gindex];
2107 dy = ey[i] - ay[gindex];
2108 d = (float)sqrt(dx*dx + dy*dy);
2109 if (d < best[gindex]) {
2110 best[gindex] = d;
2111 bestx[gindex] = ex[i];
2112 besty[gindex] = ey[i];
2113 }
2114 }
2115 }
2116 }
2117
2118 if (pass == 0) {
2119 for (i = 0; i < state->map->ngraph + n; i++)
2120 if (an[i] > 0) {
2121 ax[i] /= an[i];
2122 ay[i] /= an[i];
2123 }
2124 }
2125 }
2126
2127 state->map->edgex = snewn(state->map->ngraph, int);
2128 state->map->edgey = snewn(state->map->ngraph, int);
2129 memcpy(state->map->edgex, bestx, state->map->ngraph * sizeof(int));
2130 memcpy(state->map->edgey, besty, state->map->ngraph * sizeof(int));
2131
2132 state->map->regionx = snewn(n, int);
2133 state->map->regiony = snewn(n, int);
2134 memcpy(state->map->regionx, bestx + state->map->ngraph, n*sizeof(int));
2135 memcpy(state->map->regiony, besty + state->map->ngraph, n*sizeof(int));
2136
2137 for (i = 0; i < state->map->ngraph; i++)
2138 if (state->map->edgex[i] < 0) {
2139 /* Find the other representation of this edge. */
2140 int e = state->map->graph[i];
2141 int iprime = graph_edge_index(state->map->graph, n,
2142 state->map->ngraph, e%n, e/n);
2143 assert(state->map->edgex[iprime] >= 0);
2144 state->map->edgex[i] = state->map->edgex[iprime];
2145 state->map->edgey[i] = state->map->edgey[iprime];
2146 }
2147
2148 sfree(ax);
2149 sfree(ay);
2150 sfree(an);
2151 sfree(best);
2152 sfree(bestx);
2153 sfree(besty);
2154 }
2155
2156 return state;
2157}
2158
2159static game_state *dup_game(const game_state *state)
2160{
2161 game_state *ret = snew(game_state);
2162
2163 ret->p = state->p;
2164 ret->colouring = snewn(state->p.n, int);
2165 memcpy(ret->colouring, state->colouring, state->p.n * sizeof(int));
2166 ret->pencil = snewn(state->p.n, int);
2167 memcpy(ret->pencil, state->pencil, state->p.n * sizeof(int));
2168 ret->map = state->map;
2169 ret->map->refcount++;
2170 ret->completed = state->completed;
2171 ret->cheated = state->cheated;
2172
2173 return ret;
2174}
2175
2176static void free_game(game_state *state)
2177{
2178 if (--state->map->refcount <= 0) {
2179 sfree(state->map->map);
2180 sfree(state->map->graph);
2181 sfree(state->map->immutable);
2182 sfree(state->map->edgex);
2183 sfree(state->map->edgey);
2184 sfree(state->map->regionx);
2185 sfree(state->map->regiony);
2186 sfree(state->map);
2187 }
2188 sfree(state->pencil);
2189 sfree(state->colouring);
2190 sfree(state);
2191}
2192
2193static char *solve_game(const game_state *state, const game_state *currstate,
2194 const char *aux, char **error)
2195{
2196 if (!aux) {
2197 /*
2198 * Use the solver.
2199 */
2200 int *colouring;
2201 struct solver_scratch *sc;
2202 int sret;
2203 int i;
2204 char *ret, buf[80];
2205 int retlen, retsize;
2206
2207 colouring = snewn(state->map->n, int);
2208 memcpy(colouring, state->colouring, state->map->n * sizeof(int));
2209
2210 sc = new_scratch(state->map->graph, state->map->n, state->map->ngraph);
2211 sret = map_solver(sc, state->map->graph, state->map->n,
2212 state->map->ngraph, colouring, DIFFCOUNT-1);
2213 free_scratch(sc);
2214
2215 if (sret != 1) {
2216 sfree(colouring);
2217 if (sret == 0)
2218 *error = "Puzzle is inconsistent";
2219 else
2220 *error = "Unable to find a unique solution for this puzzle";
2221 return NULL;
2222 }
2223
2224 retsize = 64;
2225 ret = snewn(retsize, char);
2226 strcpy(ret, "S");
2227 retlen = 1;
2228
2229 for (i = 0; i < state->map->n; i++) {
2230 int len;
2231
2232 assert(colouring[i] >= 0);
2233 if (colouring[i] == currstate->colouring[i])
2234 continue;
2235 assert(!state->map->immutable[i]);
2236
2237 len = sprintf(buf, ";%d:%d", colouring[i], i);
2238 if (retlen + len >= retsize) {
2239 retsize = retlen + len + 256;
2240 ret = sresize(ret, retsize, char);
2241 }
2242 strcpy(ret + retlen, buf);
2243 retlen += len;
2244 }
2245
2246 sfree(colouring);
2247
2248 return ret;
2249 }
2250 return dupstr(aux);
2251}
2252
2253static int game_can_format_as_text_now(const game_params *params)
2254{
2255 return TRUE;
2256}
2257
2258static char *game_text_format(const game_state *state)
2259{
2260 return NULL;
2261}
2262
2263struct game_ui {
2264 /*
2265 * drag_colour:
2266 *
2267 * - -2 means no drag currently active.
2268 * - >=0 means we're dragging a solid colour.
2269 * - -1 means we're dragging a blank space, and drag_pencil
2270 * might or might not add some pencil-mark stipples to that.
2271 */
2272 int drag_colour;
2273 int drag_pencil;
2274 int dragx, dragy;
2275 int show_numbers;
2276
2277 int cur_x, cur_y, cur_visible, cur_moved, cur_lastmove;
2278};
2279
2280static game_ui *new_ui(const game_state *state)
2281{
2282 game_ui *ui = snew(game_ui);
2283 ui->dragx = ui->dragy = -1;
2284 ui->drag_colour = -2;
2285 ui->drag_pencil = 0;
2286 ui->show_numbers = FALSE;
2287 ui->cur_x = ui->cur_y = ui->cur_visible = ui->cur_moved = 0;
2288 ui->cur_lastmove = 0;
2289 return ui;
2290}
2291
2292static void free_ui(game_ui *ui)
2293{
2294 sfree(ui);
2295}
2296
2297static char *encode_ui(const game_ui *ui)
2298{
2299 return NULL;
2300}
2301
2302static void decode_ui(game_ui *ui, const char *encoding)
2303{
2304}
2305
2306static void game_changed_state(game_ui *ui, const game_state *oldstate,
2307 const game_state *newstate)
2308{
2309}
2310
2311struct game_drawstate {
2312 int tilesize;
2313 unsigned long *drawn, *todraw;
2314 int started;
2315 int dragx, dragy, drag_visible;
2316 blitter *bl;
2317};
2318
2319/* Flags in `drawn'. */
2320#define ERR_BASE 0x00800000L
2321#define ERR_MASK 0xFF800000L
2322#define PENCIL_T_BASE 0x00080000L
2323#define PENCIL_T_MASK 0x00780000L
2324#define PENCIL_B_BASE 0x00008000L
2325#define PENCIL_B_MASK 0x00078000L
2326#define PENCIL_MASK 0x007F8000L
2327#define SHOW_NUMBERS 0x00004000L
2328
2329#define TILESIZE (ds->tilesize)
2330#define BORDER (TILESIZE)
2331#define COORD(x) ( (x) * TILESIZE + BORDER )
2332#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
2333
2334 /*
2335 * EPSILON_FOO are epsilons added to absolute cursor position by
2336 * cursor movement, such that in pathological cases (e.g. a very
2337 * small diamond-shaped area) it's relatively easy to select the
2338 * region you wanted.
2339 */
2340
2341#define EPSILON_X(button) (((button) == CURSOR_RIGHT) ? +1 : \
2342 ((button) == CURSOR_LEFT) ? -1 : 0)
2343#define EPSILON_Y(button) (((button) == CURSOR_DOWN) ? +1 : \
2344 ((button) == CURSOR_UP) ? -1 : 0)
2345
2346
2347static int region_from_coords(const game_state *state,
2348 const game_drawstate *ds, int x, int y)
2349{
2350 int w = state->p.w, h = state->p.h, wh = w*h /*, n = state->p.n */;
2351 int tx = FROMCOORD(x), ty = FROMCOORD(y);
2352 int dx = x - COORD(tx), dy = y - COORD(ty);
2353 int quadrant;
2354
2355 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
2356 return -1; /* border */
2357
2358 quadrant = 2 * (dx > dy) + (TILESIZE - dx > dy);
2359 quadrant = (quadrant == 0 ? BE :
2360 quadrant == 1 ? LE :
2361 quadrant == 2 ? RE : TE);
2362
2363 return state->map->map[quadrant * wh + ty*w+tx];
2364}
2365
2366static char *interpret_move(const game_state *state, game_ui *ui,
2367 const game_drawstate *ds,
2368 int x, int y, int button)
2369{
2370 char *bufp, buf[256];
2371 int alt_button;
2372
2373 /*
2374 * Enable or disable numeric labels on regions.
2375 */
2376 if (button == 'l' || button == 'L') {
2377 ui->show_numbers = !ui->show_numbers;
2378 return "";
2379 }
2380
2381 if (IS_CURSOR_MOVE(button)) {
2382 move_cursor(button, &ui->cur_x, &ui->cur_y, state->p.w, state->p.h, 0);
2383 ui->cur_visible = 1;
2384 ui->cur_moved = 1;
2385 ui->cur_lastmove = button;
2386 ui->dragx = COORD(ui->cur_x) + TILESIZE/2 + EPSILON_X(button);
2387 ui->dragy = COORD(ui->cur_y) + TILESIZE/2 + EPSILON_Y(button);
2388 return "";
2389 }
2390 if (IS_CURSOR_SELECT(button)) {
2391 if (!ui->cur_visible) {
2392 ui->dragx = COORD(ui->cur_x) + TILESIZE/2 + EPSILON_X(ui->cur_lastmove);
2393 ui->dragy = COORD(ui->cur_y) + TILESIZE/2 + EPSILON_Y(ui->cur_lastmove);
2394 ui->cur_visible = 1;
2395 return "";
2396 }
2397 if (ui->drag_colour == -2) { /* not currently cursor-dragging, start. */
2398 int r = region_from_coords(state, ds, ui->dragx, ui->dragy);
2399 if (r >= 0) {
2400 ui->drag_colour = state->colouring[r];
2401 ui->drag_pencil = (ui->drag_colour >= 0) ? 0 : state->pencil[r];
2402 } else {
2403 ui->drag_colour = -1;
2404 ui->drag_pencil = 0;
2405 }
2406 ui->cur_moved = 0;
2407 return "";
2408 } else { /* currently cursor-dragging; drop the colour in the new region. */
2409 x = COORD(ui->cur_x) + TILESIZE/2 + EPSILON_X(ui->cur_lastmove);
2410 y = COORD(ui->cur_y) + TILESIZE/2 + EPSILON_Y(ui->cur_lastmove);
2411 alt_button = (button == CURSOR_SELECT2) ? 1 : 0;
2412 /* Double-select removes current colour. */
2413 if (!ui->cur_moved) ui->drag_colour = -1;
2414 goto drag_dropped;
2415 }
2416 }
2417
2418 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2419 int r = region_from_coords(state, ds, x, y);
2420
2421 if (r >= 0) {
2422 ui->drag_colour = state->colouring[r];
2423 ui->drag_pencil = state->pencil[r];
2424 if (ui->drag_colour >= 0)
2425 ui->drag_pencil = 0; /* should be already, but double-check */
2426 } else {
2427 ui->drag_colour = -1;
2428 ui->drag_pencil = 0;
2429 }
2430 ui->dragx = x;
2431 ui->dragy = y;
2432 ui->cur_visible = 0;
2433 return "";
2434 }
2435
2436 if ((button == LEFT_DRAG || button == RIGHT_DRAG) &&
2437 ui->drag_colour > -2) {
2438 ui->dragx = x;
2439 ui->dragy = y;
2440 return "";
2441 }
2442
2443 if ((button == LEFT_RELEASE || button == RIGHT_RELEASE) &&
2444 ui->drag_colour > -2) {
2445 alt_button = (button == RIGHT_RELEASE) ? 1 : 0;
2446 goto drag_dropped;
2447 }
2448
2449 return NULL;
2450
2451drag_dropped:
2452 {
2453 int r = region_from_coords(state, ds, x, y);
2454 int c = ui->drag_colour;
2455 int p = ui->drag_pencil;
2456 int oldp;
2457
2458 /*
2459 * Cancel the drag, whatever happens.
2460 */
2461 ui->drag_colour = -2;
2462
2463 if (r < 0)
2464 return ""; /* drag into border; do nothing else */
2465
2466 if (state->map->immutable[r])
2467 return ""; /* can't change this region */
2468
2469 if (state->colouring[r] == c && state->pencil[r] == p)
2470 return ""; /* don't _need_ to change this region */
2471
2472 if (alt_button) {
2473 if (state->colouring[r] >= 0) {
2474 /* Can't pencil on a coloured region */
2475 return "";
2476 } else if (c >= 0) {
2477 /* Right-dragging from colour to blank toggles one pencil */
2478 p = state->pencil[r] ^ (1 << c);
2479 c = -1;
2480 }
2481 /* Otherwise, right-dragging from blank to blank is equivalent
2482 * to left-dragging. */
2483 }
2484
2485 bufp = buf;
2486 oldp = state->pencil[r];
2487 if (c != state->colouring[r]) {
2488 bufp += sprintf(bufp, ";%c:%d", (int)(c < 0 ? 'C' : '0' + c), r);
2489 if (c >= 0)
2490 oldp = 0;
2491 }
2492 if (p != oldp) {
2493 int i;
2494 for (i = 0; i < FOUR; i++)
2495 if ((oldp ^ p) & (1 << i))
2496 bufp += sprintf(bufp, ";p%c:%d", (int)('0' + i), r);
2497 }
2498
2499 return dupstr(buf+1); /* ignore first semicolon */
2500 }
2501}
2502
2503static game_state *execute_move(const game_state *state, const char *move)
2504{
2505 int n = state->p.n;
2506 game_state *ret = dup_game(state);
2507 int c, k, adv, i;
2508
2509 while (*move) {
2510 int pencil = FALSE;
2511
2512 c = *move;
2513 if (c == 'p') {
2514 pencil = TRUE;
2515 c = *++move;
2516 }
2517 if ((c == 'C' || (c >= '0' && c < '0'+FOUR)) &&
2518 sscanf(move+1, ":%d%n", &k, &adv) == 1 &&
2519 k >= 0 && k < state->p.n) {
2520 move += 1 + adv;
2521 if (pencil) {
2522 if (ret->colouring[k] >= 0) {
2523 free_game(ret);
2524 return NULL;
2525 }
2526 if (c == 'C')
2527 ret->pencil[k] = 0;
2528 else
2529 ret->pencil[k] ^= 1 << (c - '0');
2530 } else {
2531 ret->colouring[k] = (c == 'C' ? -1 : c - '0');
2532 ret->pencil[k] = 0;
2533 }
2534 } else if (*move == 'S') {
2535 move++;
2536 ret->cheated = TRUE;
2537 } else {
2538 free_game(ret);
2539 return NULL;
2540 }
2541
2542 if (*move && *move != ';') {
2543 free_game(ret);
2544 return NULL;
2545 }
2546 if (*move)
2547 move++;
2548 }
2549
2550 /*
2551 * Check for completion.
2552 */
2553 if (!ret->completed) {
2554 int ok = TRUE;
2555
2556 for (i = 0; i < n; i++)
2557 if (ret->colouring[i] < 0) {
2558 ok = FALSE;
2559 break;
2560 }
2561
2562 if (ok) {
2563 for (i = 0; i < ret->map->ngraph; i++) {
2564 int j = ret->map->graph[i] / n;
2565 int k = ret->map->graph[i] % n;
2566 if (ret->colouring[j] == ret->colouring[k]) {
2567 ok = FALSE;
2568 break;
2569 }
2570 }
2571 }
2572
2573 if (ok)
2574 ret->completed = TRUE;
2575 }
2576
2577 return ret;
2578}
2579
2580/* ----------------------------------------------------------------------
2581 * Drawing routines.
2582 */
2583
2584static void game_compute_size(const game_params *params, int tilesize,
2585 int *x, int *y)
2586{
2587 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2588 struct { int tilesize; } ads, *ds = &ads;
2589 ads.tilesize = tilesize;
2590
2591 *x = params->w * TILESIZE + 2 * BORDER + 1;
2592 *y = params->h * TILESIZE + 2 * BORDER + 1;
2593}
2594
2595static void game_set_size(drawing *dr, game_drawstate *ds,
2596 const game_params *params, int tilesize)
2597{
2598 ds->tilesize = tilesize;
2599
2600 assert(!ds->bl); /* set_size is never called twice */
2601 ds->bl = blitter_new(dr, TILESIZE+3, TILESIZE+3);
2602}
2603
2604const float map_colours[FOUR][3] = {
2605#ifdef VIVID_COLOURS
2606 /* Use more vivid colours (e.g. on the Pocket PC) */
2607 {0.75F, 0.25F, 0.25F},
2608 {0.3F, 0.7F, 0.3F},
2609 {0.3F, 0.3F, 0.7F},
2610 {0.85F, 0.85F, 0.1F},
2611#else
2612 {0.7F, 0.5F, 0.4F},
2613 {0.8F, 0.7F, 0.4F},
2614 {0.5F, 0.6F, 0.4F},
2615 {0.55F, 0.45F, 0.35F},
2616#endif
2617};
2618const int map_hatching[FOUR] = {
2619 HATCH_VERT, HATCH_SLASH, HATCH_HORIZ, HATCH_BACKSLASH
2620};
2621
2622static float *game_colours(frontend *fe, int *ncolours)
2623{
2624 float *ret = snewn(3 * NCOLOURS, float);
2625
2626 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2627
2628 ret[COL_GRID * 3 + 0] = 0.0F;
2629 ret[COL_GRID * 3 + 1] = 0.0F;
2630 ret[COL_GRID * 3 + 2] = 0.0F;
2631
2632 memcpy(ret + COL_0 * 3, map_colours[0], 3 * sizeof(float));
2633 memcpy(ret + COL_1 * 3, map_colours[1], 3 * sizeof(float));
2634 memcpy(ret + COL_2 * 3, map_colours[2], 3 * sizeof(float));
2635 memcpy(ret + COL_3 * 3, map_colours[3], 3 * sizeof(float));
2636
2637 ret[COL_ERROR * 3 + 0] = 1.0F;
2638 ret[COL_ERROR * 3 + 1] = 0.0F;
2639 ret[COL_ERROR * 3 + 2] = 0.0F;
2640
2641 ret[COL_ERRTEXT * 3 + 0] = 1.0F;
2642 ret[COL_ERRTEXT * 3 + 1] = 1.0F;
2643 ret[COL_ERRTEXT * 3 + 2] = 1.0F;
2644
2645 *ncolours = NCOLOURS;
2646 return ret;
2647}
2648
2649static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2650{
2651 struct game_drawstate *ds = snew(struct game_drawstate);
2652 int i;
2653
2654 ds->tilesize = 0;
2655 ds->drawn = snewn(state->p.w * state->p.h, unsigned long);
2656 for (i = 0; i < state->p.w * state->p.h; i++)
2657 ds->drawn[i] = 0xFFFFL;
2658 ds->todraw = snewn(state->p.w * state->p.h, unsigned long);
2659 ds->started = FALSE;
2660 ds->bl = NULL;
2661 ds->drag_visible = FALSE;
2662 ds->dragx = ds->dragy = -1;
2663
2664 return ds;
2665}
2666
2667static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2668{
2669 sfree(ds->drawn);
2670 sfree(ds->todraw);
2671 if (ds->bl)
2672 blitter_free(dr, ds->bl);
2673 sfree(ds);
2674}
2675
2676static void draw_error(drawing *dr, game_drawstate *ds, int x, int y)
2677{
2678 int coords[8];
2679 int yext, xext;
2680
2681 /*
2682 * Draw a diamond.
2683 */
2684 coords[0] = x - TILESIZE*2/5;
2685 coords[1] = y;
2686 coords[2] = x;
2687 coords[3] = y - TILESIZE*2/5;
2688 coords[4] = x + TILESIZE*2/5;
2689 coords[5] = y;
2690 coords[6] = x;
2691 coords[7] = y + TILESIZE*2/5;
2692 draw_polygon(dr, coords, 4, COL_ERROR, COL_GRID);
2693
2694 /*
2695 * Draw an exclamation mark in the diamond. This turns out to
2696 * look unpleasantly off-centre if done via draw_text, so I do
2697 * it by hand on the basis that exclamation marks aren't that
2698 * difficult to draw...
2699 */
2700 xext = TILESIZE/16;
2701 yext = TILESIZE*2/5 - (xext*2+2);
2702 draw_rect(dr, x-xext, y-yext, xext*2+1, yext*2+1 - (xext*3),
2703 COL_ERRTEXT);
2704 draw_rect(dr, x-xext, y+yext-xext*2+1, xext*2+1, xext*2, COL_ERRTEXT);
2705}
2706
2707static void draw_square(drawing *dr, game_drawstate *ds,
2708 const game_params *params, struct map *map,
2709 int x, int y, unsigned long v)
2710{
2711 int w = params->w, h = params->h, wh = w*h;
2712 int tv, bv, xo, yo, i, j, oldj;
2713 unsigned long errs, pencil, show_numbers;
2714
2715 errs = v & ERR_MASK;
2716 v &= ~ERR_MASK;
2717 pencil = v & PENCIL_MASK;
2718 v &= ~PENCIL_MASK;
2719 show_numbers = v & SHOW_NUMBERS;
2720 v &= ~SHOW_NUMBERS;
2721 tv = v / FIVE;
2722 bv = v % FIVE;
2723
2724 clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
2725
2726 /*
2727 * Draw the region colour.
2728 */
2729 draw_rect(dr, COORD(x), COORD(y), TILESIZE, TILESIZE,
2730 (tv == FOUR ? COL_BACKGROUND : COL_0 + tv));
2731 /*
2732 * Draw the second region colour, if this is a diagonally
2733 * divided square.
2734 */
2735 if (map->map[TE * wh + y*w+x] != map->map[BE * wh + y*w+x]) {
2736 int coords[6];
2737 coords[0] = COORD(x)-1;
2738 coords[1] = COORD(y+1)+1;
2739 if (map->map[LE * wh + y*w+x] == map->map[TE * wh + y*w+x])
2740 coords[2] = COORD(x+1)+1;
2741 else
2742 coords[2] = COORD(x)-1;
2743 coords[3] = COORD(y)-1;
2744 coords[4] = COORD(x+1)+1;
2745 coords[5] = COORD(y+1)+1;
2746 draw_polygon(dr, coords, 3,
2747 (bv == FOUR ? COL_BACKGROUND : COL_0 + bv), COL_GRID);
2748 }
2749
2750 /*
2751 * Draw `pencil marks'. Currently we arrange these in a square
2752 * formation, which means we may be in trouble if the value of
2753 * FOUR changes later...
2754 */
2755 assert(FOUR == 4);
2756 for (yo = 0; yo < 4; yo++)
2757 for (xo = 0; xo < 4; xo++) {
2758 int te = map->map[TE * wh + y*w+x];
2759 int e, ee, c;
2760
2761 e = (yo < xo && yo < 3-xo ? TE :
2762 yo > xo && yo > 3-xo ? BE :
2763 xo < 2 ? LE : RE);
2764 ee = map->map[e * wh + y*w+x];
2765
2766 if (xo != (yo * 2 + 1) % 5)
2767 continue;
2768 c = yo;
2769
2770 if (!(pencil & ((ee == te ? PENCIL_T_BASE : PENCIL_B_BASE) << c)))
2771 continue;
2772
2773 if (yo == xo &&
2774 (map->map[TE * wh + y*w+x] != map->map[LE * wh + y*w+x]))
2775 continue; /* avoid TL-BR diagonal line */
2776 if (yo == 3-xo &&
2777 (map->map[TE * wh + y*w+x] != map->map[RE * wh + y*w+x]))
2778 continue; /* avoid BL-TR diagonal line */
2779
2780 draw_circle(dr, COORD(x) + (xo+1)*TILESIZE/5,
2781 COORD(y) + (yo+1)*TILESIZE/5,
2782 TILESIZE/7, COL_0 + c, COL_0 + c);
2783 }
2784
2785 /*
2786 * Draw the grid lines, if required.
2787 */
2788 if (x <= 0 || map->map[RE*wh+y*w+(x-1)] != map->map[LE*wh+y*w+x])
2789 draw_rect(dr, COORD(x), COORD(y), 1, TILESIZE, COL_GRID);
2790 if (y <= 0 || map->map[BE*wh+(y-1)*w+x] != map->map[TE*wh+y*w+x])
2791 draw_rect(dr, COORD(x), COORD(y), TILESIZE, 1, COL_GRID);
2792 if (x <= 0 || y <= 0 ||
2793 map->map[RE*wh+(y-1)*w+(x-1)] != map->map[TE*wh+y*w+x] ||
2794 map->map[BE*wh+(y-1)*w+(x-1)] != map->map[LE*wh+y*w+x])
2795 draw_rect(dr, COORD(x), COORD(y), 1, 1, COL_GRID);
2796
2797 /*
2798 * Draw error markers.
2799 */
2800 for (yo = 0; yo < 3; yo++)
2801 for (xo = 0; xo < 3; xo++)
2802 if (errs & (ERR_BASE << (yo*3+xo)))
2803 draw_error(dr, ds,
2804 (COORD(x)*2+TILESIZE*xo)/2,
2805 (COORD(y)*2+TILESIZE*yo)/2);
2806
2807 /*
2808 * Draw region numbers, if desired.
2809 */
2810 if (show_numbers) {
2811 oldj = -1;
2812 for (i = 0; i < 2; i++) {
2813 j = map->map[(i?BE:TE)*wh+y*w+x];
2814 if (oldj == j)
2815 continue;
2816 oldj = j;
2817
2818 xo = map->regionx[j] - 2*x;
2819 yo = map->regiony[j] - 2*y;
2820 if (xo >= 0 && xo <= 2 && yo >= 0 && yo <= 2) {
2821 char buf[80];
2822 sprintf(buf, "%d", j);
2823 draw_text(dr, (COORD(x)*2+TILESIZE*xo)/2,
2824 (COORD(y)*2+TILESIZE*yo)/2,
2825 FONT_VARIABLE, 3*TILESIZE/5,
2826 ALIGN_HCENTRE|ALIGN_VCENTRE,
2827 COL_GRID, buf);
2828 }
2829 }
2830 }
2831
2832 unclip(dr);
2833
2834 draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
2835}
2836
2837static void game_redraw(drawing *dr, game_drawstate *ds,
2838 const game_state *oldstate, const game_state *state,
2839 int dir, const game_ui *ui,
2840 float animtime, float flashtime)
2841{
2842 int w = state->p.w, h = state->p.h, wh = w*h, n = state->p.n;
2843 int x, y, i;
2844 int flash;
2845
2846 if (ds->drag_visible) {
2847 blitter_load(dr, ds->bl, ds->dragx, ds->dragy);
2848 draw_update(dr, ds->dragx, ds->dragy, TILESIZE + 3, TILESIZE + 3);
2849 ds->drag_visible = FALSE;
2850 }
2851
2852 /*
2853 * The initial contents of the window are not guaranteed and
2854 * can vary with front ends. To be on the safe side, all games
2855 * should start by drawing a big background-colour rectangle
2856 * covering the whole window.
2857 */
2858 if (!ds->started) {
2859 int ww, wh;
2860
2861 game_compute_size(&state->p, TILESIZE, &ww, &wh);
2862 draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
2863 draw_rect(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1,
2864 COL_GRID);
2865
2866 draw_update(dr, 0, 0, ww, wh);
2867 ds->started = TRUE;
2868 }
2869
2870 if (flashtime) {
2871 if (flash_type == 1)
2872 flash = (int)(flashtime * FOUR / flash_length);
2873 else
2874 flash = 1 + (int)(flashtime * THREE / flash_length);
2875 } else
2876 flash = -1;
2877
2878 /*
2879 * Set up the `todraw' array.
2880 */
2881 for (y = 0; y < h; y++)
2882 for (x = 0; x < w; x++) {
2883 int tv = state->colouring[state->map->map[TE * wh + y*w+x]];
2884 int bv = state->colouring[state->map->map[BE * wh + y*w+x]];
2885 unsigned long v;
2886
2887 if (tv < 0)
2888 tv = FOUR;
2889 if (bv < 0)
2890 bv = FOUR;
2891
2892 if (flash >= 0) {
2893 if (flash_type == 1) {
2894 if (tv == flash)
2895 tv = FOUR;
2896 if (bv == flash)
2897 bv = FOUR;
2898 } else if (flash_type == 2) {
2899 if (flash % 2)
2900 tv = bv = FOUR;
2901 } else {
2902 if (tv != FOUR)
2903 tv = (tv + flash) % FOUR;
2904 if (bv != FOUR)
2905 bv = (bv + flash) % FOUR;
2906 }
2907 }
2908
2909 v = tv * FIVE + bv;
2910
2911 /*
2912 * Add pencil marks.
2913 */
2914 for (i = 0; i < FOUR; i++) {
2915 if (state->colouring[state->map->map[TE * wh + y*w+x]] < 0 &&
2916 (state->pencil[state->map->map[TE * wh + y*w+x]] & (1<<i)))
2917 v |= PENCIL_T_BASE << i;
2918 if (state->colouring[state->map->map[BE * wh + y*w+x]] < 0 &&
2919 (state->pencil[state->map->map[BE * wh + y*w+x]] & (1<<i)))
2920 v |= PENCIL_B_BASE << i;
2921 }
2922
2923 if (ui->show_numbers)
2924 v |= SHOW_NUMBERS;
2925
2926 ds->todraw[y*w+x] = v;
2927 }
2928
2929 /*
2930 * Add error markers to the `todraw' array.
2931 */
2932 for (i = 0; i < state->map->ngraph; i++) {
2933 int v1 = state->map->graph[i] / n;
2934 int v2 = state->map->graph[i] % n;
2935 int xo, yo;
2936
2937 if (state->colouring[v1] < 0 || state->colouring[v2] < 0)
2938 continue;
2939 if (state->colouring[v1] != state->colouring[v2])
2940 continue;
2941
2942 x = state->map->edgex[i];
2943 y = state->map->edgey[i];
2944
2945 xo = x % 2; x /= 2;
2946 yo = y % 2; y /= 2;
2947
2948 ds->todraw[y*w+x] |= ERR_BASE << (yo*3+xo);
2949 if (xo == 0) {
2950 assert(x > 0);
2951 ds->todraw[y*w+(x-1)] |= ERR_BASE << (yo*3+2);
2952 }
2953 if (yo == 0) {
2954 assert(y > 0);
2955 ds->todraw[(y-1)*w+x] |= ERR_BASE << (2*3+xo);
2956 }
2957 if (xo == 0 && yo == 0) {
2958 assert(x > 0 && y > 0);
2959 ds->todraw[(y-1)*w+(x-1)] |= ERR_BASE << (2*3+2);
2960 }
2961 }
2962
2963 /*
2964 * Now actually draw everything.
2965 */
2966 for (y = 0; y < h; y++)
2967 for (x = 0; x < w; x++) {
2968 unsigned long v = ds->todraw[y*w+x];
2969 if (ds->drawn[y*w+x] != v) {
2970 draw_square(dr, ds, &state->p, state->map, x, y, v);
2971 ds->drawn[y*w+x] = v;
2972 }
2973 }
2974
2975 /*
2976 * Draw the dragged colour blob if any.
2977 */
2978 if ((ui->drag_colour > -2) || ui->cur_visible) {
2979 int bg, iscur = 0;
2980 if (ui->drag_colour >= 0)
2981 bg = COL_0 + ui->drag_colour;
2982 else if (ui->drag_colour == -1) {
2983 bg = COL_BACKGROUND;
2984 } else {
2985 int r = region_from_coords(state, ds, ui->dragx, ui->dragy);
2986 int c = (r < 0) ? -1 : state->colouring[r];
2987 assert(ui->cur_visible);
2988 /*bg = COL_GRID;*/
2989 bg = (c < 0) ? COL_BACKGROUND : COL_0 + c;
2990 iscur = 1;
2991 }
2992
2993 ds->dragx = ui->dragx - TILESIZE/2 - 2;
2994 ds->dragy = ui->dragy - TILESIZE/2 - 2;
2995 blitter_save(dr, ds->bl, ds->dragx, ds->dragy);
2996 draw_circle(dr, ui->dragx, ui->dragy,
2997 iscur ? TILESIZE/4 : TILESIZE/2, bg, COL_GRID);
2998 for (i = 0; i < FOUR; i++)
2999 if (ui->drag_pencil & (1 << i))
3000 draw_circle(dr, ui->dragx + ((i*4+2)%10-3) * TILESIZE/10,
3001 ui->dragy + (i*2-3) * TILESIZE/10,
3002 TILESIZE/8, COL_0 + i, COL_0 + i);
3003 draw_update(dr, ds->dragx, ds->dragy, TILESIZE + 3, TILESIZE + 3);
3004 ds->drag_visible = TRUE;
3005 }
3006}
3007
3008static float game_anim_length(const game_state *oldstate,
3009 const game_state *newstate, int dir, game_ui *ui)
3010{
3011 return 0.0F;
3012}
3013
3014static float game_flash_length(const game_state *oldstate,
3015 const game_state *newstate, int dir, game_ui *ui)
3016{
3017 if (!oldstate->completed && newstate->completed &&
3018 !oldstate->cheated && !newstate->cheated) {
3019 if (flash_type < 0) {
3020 char *env = getenv("MAP_ALTERNATIVE_FLASH");
3021 if (env)
3022 flash_type = atoi(env);
3023 else
3024 flash_type = 0;
3025 flash_length = (flash_type == 1 ? 0.50F : 0.30F);
3026 }
3027 return flash_length;
3028 } else
3029 return 0.0F;
3030}
3031
3032static int game_status(const game_state *state)
3033{
3034 return state->completed ? +1 : 0;
3035}
3036
3037static int game_timing_state(const game_state *state, game_ui *ui)
3038{
3039 return TRUE;
3040}
3041
3042static void game_print_size(const game_params *params, float *x, float *y)
3043{
3044 int pw, ph;
3045
3046 /*
3047 * I'll use 4mm squares by default, I think. Simplest way to
3048 * compute this size is to compute the pixel puzzle size at a
3049 * given tile size and then scale.
3050 */
3051 game_compute_size(params, 400, &pw, &ph);
3052 *x = pw / 100.0F;
3053 *y = ph / 100.0F;
3054}
3055
3056static void game_print(drawing *dr, const game_state *state, int tilesize)
3057{
3058 int w = state->p.w, h = state->p.h, wh = w*h, n = state->p.n;
3059 int ink, c[FOUR], i;
3060 int x, y, r;
3061 int *coords, ncoords, coordsize;
3062
3063 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3064 struct { int tilesize; } ads, *ds = &ads;
3065 /* We can't call game_set_size() here because we don't want a blitter */
3066 ads.tilesize = tilesize;
3067
3068 ink = print_mono_colour(dr, 0);
3069 for (i = 0; i < FOUR; i++)
3070 c[i] = print_rgb_hatched_colour(dr, map_colours[i][0],
3071 map_colours[i][1], map_colours[i][2],
3072 map_hatching[i]);
3073
3074 coordsize = 0;
3075 coords = NULL;
3076
3077 print_line_width(dr, TILESIZE / 16);
3078
3079 /*
3080 * Draw a single filled polygon around each region.
3081 */
3082 for (r = 0; r < n; r++) {
3083 int octants[8], lastdir, d1, d2, ox, oy;
3084
3085 /*
3086 * Start by finding a point on the region boundary. Any
3087 * point will do. To do this, we'll search for a square
3088 * containing the region and then decide which corner of it
3089 * to use.
3090 */
3091 x = w;
3092 for (y = 0; y < h; y++) {
3093 for (x = 0; x < w; x++) {
3094 if (state->map->map[wh*0+y*w+x] == r ||
3095 state->map->map[wh*1+y*w+x] == r ||
3096 state->map->map[wh*2+y*w+x] == r ||
3097 state->map->map[wh*3+y*w+x] == r)
3098 break;
3099 }
3100 if (x < w)
3101 break;
3102 }
3103 assert(y < h && x < w); /* we must have found one somewhere */
3104 /*
3105 * This is the first square in lexicographic order which
3106 * contains part of this region. Therefore, one of the top
3107 * two corners of the square must be what we're after. The
3108 * only case in which it isn't the top left one is if the
3109 * square is diagonally divided and the region is in the
3110 * bottom right half.
3111 */
3112 if (state->map->map[wh*TE+y*w+x] != r &&
3113 state->map->map[wh*LE+y*w+x] != r)
3114 x++; /* could just as well have done y++ */
3115
3116 /*
3117 * Now we have a point on the region boundary. Trace around
3118 * the region until we come back to this point,
3119 * accumulating coordinates for a polygon draw operation as
3120 * we go.
3121 */
3122 lastdir = -1;
3123 ox = x;
3124 oy = y;
3125 ncoords = 0;
3126
3127 do {
3128 /*
3129 * There are eight possible directions we could head in
3130 * from here. We identify them by octant numbers, and
3131 * we also use octant numbers to identify the spaces
3132 * between them:
3133 *
3134 * 6 7 0
3135 * \ 7|0 /
3136 * \ | /
3137 * 6 \|/ 1
3138 * 5-----+-----1
3139 * 5 /|\ 2
3140 * / | \
3141 * / 4|3 \
3142 * 4 3 2
3143 */
3144 octants[0] = x<w && y>0 ? state->map->map[wh*LE+(y-1)*w+x] : -1;
3145 octants[1] = x<w && y>0 ? state->map->map[wh*BE+(y-1)*w+x] : -1;
3146 octants[2] = x<w && y<h ? state->map->map[wh*TE+y*w+x] : -1;
3147 octants[3] = x<w && y<h ? state->map->map[wh*LE+y*w+x] : -1;
3148 octants[4] = x>0 && y<h ? state->map->map[wh*RE+y*w+(x-1)] : -1;
3149 octants[5] = x>0 && y<h ? state->map->map[wh*TE+y*w+(x-1)] : -1;
3150 octants[6] = x>0 && y>0 ? state->map->map[wh*BE+(y-1)*w+(x-1)] :-1;
3151 octants[7] = x>0 && y>0 ? state->map->map[wh*RE+(y-1)*w+(x-1)] :-1;
3152
3153 d1 = d2 = -1;
3154 for (i = 0; i < 8; i++)
3155 if ((octants[i] == r) ^ (octants[(i+1)%8] == r)) {
3156 assert(d2 == -1);
3157 if (d1 == -1)
3158 d1 = i;
3159 else
3160 d2 = i;
3161 }
3162
3163 assert(d1 != -1 && d2 != -1);
3164 if (d1 == lastdir)
3165 d1 = d2;
3166
3167 /*
3168 * Now we're heading in direction d1. Save the current
3169 * coordinates.
3170 */
3171 if (ncoords + 2 > coordsize) {
3172 coordsize += 128;
3173 coords = sresize(coords, coordsize, int);
3174 }
3175 coords[ncoords++] = COORD(x);
3176 coords[ncoords++] = COORD(y);
3177
3178 /*
3179 * Compute the new coordinates.
3180 */
3181 x += (d1 % 4 == 3 ? 0 : d1 < 4 ? +1 : -1);
3182 y += (d1 % 4 == 1 ? 0 : d1 > 1 && d1 < 5 ? +1 : -1);
3183 assert(x >= 0 && x <= w && y >= 0 && y <= h);
3184
3185 lastdir = d1 ^ 4;
3186 } while (x != ox || y != oy);
3187
3188 draw_polygon(dr, coords, ncoords/2,
3189 state->colouring[r] >= 0 ?
3190 c[state->colouring[r]] : -1, ink);
3191 }
3192 sfree(coords);
3193}
3194
3195#ifdef COMBINED
3196#define thegame map
3197#endif
3198
3199const struct game thegame = {
3200 "Map", "games.map", "map",
3201 default_params,
3202 game_fetch_preset, NULL,
3203 decode_params,
3204 encode_params,
3205 free_params,
3206 dup_params,
3207 TRUE, game_configure, custom_params,
3208 validate_params,
3209 new_game_desc,
3210 validate_desc,
3211 new_game,
3212 dup_game,
3213 free_game,
3214 TRUE, solve_game,
3215 FALSE, game_can_format_as_text_now, game_text_format,
3216 new_ui,
3217 free_ui,
3218 encode_ui,
3219 decode_ui,
3220 game_changed_state,
3221 interpret_move,
3222 execute_move,
3223 20, game_compute_size, game_set_size,
3224 game_colours,
3225 game_new_drawstate,
3226 game_free_drawstate,
3227 game_redraw,
3228 game_anim_length,
3229 game_flash_length,
3230 game_status,
3231 TRUE, TRUE, game_print_size, game_print,
3232 FALSE, /* wants_statusbar */
3233 FALSE, game_timing_state,
3234 0, /* flags */
3235};
3236
3237#ifdef STANDALONE_SOLVER
3238
3239int main(int argc, char **argv)
3240{
3241 game_params *p;
3242 game_state *s;
3243 char *id = NULL, *desc, *err;
3244 int grade = FALSE;
3245 int ret, diff, really_verbose = FALSE;
3246 struct solver_scratch *sc;
3247 int i;
3248
3249 while (--argc > 0) {
3250 char *p = *++argv;
3251 if (!strcmp(p, "-v")) {
3252 really_verbose = TRUE;
3253 } else if (!strcmp(p, "-g")) {
3254 grade = TRUE;
3255 } else if (*p == '-') {
3256 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
3257 return 1;
3258 } else {
3259 id = p;
3260 }
3261 }
3262
3263 if (!id) {
3264 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
3265 return 1;
3266 }
3267
3268 desc = strchr(id, ':');
3269 if (!desc) {
3270 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
3271 return 1;
3272 }
3273 *desc++ = '\0';
3274
3275 p = default_params();
3276 decode_params(p, id);
3277 err = validate_desc(p, desc);
3278 if (err) {
3279 fprintf(stderr, "%s: %s\n", argv[0], err);
3280 return 1;
3281 }
3282 s = new_game(NULL, p, desc);
3283
3284 sc = new_scratch(s->map->graph, s->map->n, s->map->ngraph);
3285
3286 /*
3287 * When solving an Easy puzzle, we don't want to bother the
3288 * user with Hard-level deductions. For this reason, we grade
3289 * the puzzle internally before doing anything else.
3290 */
3291 ret = -1; /* placate optimiser */
3292 for (diff = 0; diff < DIFFCOUNT; diff++) {
3293 for (i = 0; i < s->map->n; i++)
3294 if (!s->map->immutable[i])
3295 s->colouring[i] = -1;
3296 ret = map_solver(sc, s->map->graph, s->map->n, s->map->ngraph,
3297 s->colouring, diff);
3298 if (ret < 2)
3299 break;
3300 }
3301
3302 if (diff == DIFFCOUNT) {
3303 if (grade)
3304 printf("Difficulty rating: harder than Hard, or ambiguous\n");
3305 else
3306 printf("Unable to find a unique solution\n");
3307 } else {
3308 if (grade) {
3309 if (ret == 0)
3310 printf("Difficulty rating: impossible (no solution exists)\n");
3311 else if (ret == 1)
3312 printf("Difficulty rating: %s\n", map_diffnames[diff]);
3313 } else {
3314 verbose = really_verbose;
3315 for (i = 0; i < s->map->n; i++)
3316 if (!s->map->immutable[i])
3317 s->colouring[i] = -1;
3318 ret = map_solver(sc, s->map->graph, s->map->n, s->map->ngraph,
3319 s->colouring, diff);
3320 if (ret == 0)
3321 printf("Puzzle is inconsistent\n");
3322 else {
3323 int col = 0;
3324
3325 for (i = 0; i < s->map->n; i++) {
3326 printf("%5d <- %c%c", i, colnames[s->colouring[i]],
3327 (col < 6 && i+1 < s->map->n ? ' ' : '\n'));
3328 if (++col == 7)
3329 col = 0;
3330 }
3331 }
3332 }
3333 }
3334
3335 return 0;
3336}
3337
3338#endif
3339
3340/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/map.html b/apps/plugins/puzzles/src/map.html
new file mode 100644
index 0000000000..d3aac337d8
--- /dev/null
+++ b/apps/plugins/puzzles/src/map.html
@@ -0,0 +1,76 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Map</title>
7<link rel="previous" href="lightup.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="loopy.html">
12</head>
13<body>
14<p><a href="lightup.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="loopy.html">Next</a></p>
15<h1><a name="C22"></a>Chapter 22: <a name="i0"></a>Map</h1>
16<p>
17You are given a map consisting of a number of regions. Your task is to colour each region with one of four colours, in such a way that no two regions sharing a boundary have the same colour. You are provided with some regions already coloured, sufficient to make the remainder of the solution unique.
18</p>
19<p>
20Only regions which share a length of border are required to be different colours. Two regions which meet at only one <em>point</em> (i.e. are diagonally separated) may be the same colour.
21</p>
22<p>
23I believe this puzzle is original; I've never seen an implementation of it anywhere else. The concept of a <a name="i1"></a>four-colouring puzzle was suggested by Owen Dunn; credit must also go to Nikoli and to Verity Allan for inspiring the train of thought that led to me realising Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor for many detailed suggestions.
24</p>
25<h2><a name="S22.1"></a>22.1 <a name="i2"></a>Map controls</h2>
26<p>
27To colour a region, click the left mouse button on an existing region of the desired colour and drag that colour into the new region.
28</p>
29<p>
30(The program will always ensure the starting puzzle has at least one region of each colour, so that this is always possible!)
31</p>
32<p>
33If you need to clear a region, you can drag from an empty region, or from the puzzle boundary if there are no empty regions left.
34</p>
35<p>
36Dragging a colour using the <em>right</em> mouse button will stipple the region in that colour, which you can use as a note to yourself that you think the region <em>might</em> be that colour. A region can contain stipples in multiple colours at once. (This is often useful at the harder difficulty levels.)
37</p>
38<p>
39You can also use the cursor keys to move around the map: the colour of the cursor indicates the position of the colour you would drag (which is not obvious if you're on a region's boundary, since it depends on the direction from which you approached the boundary). Pressing the return key starts a drag of that colour, as above, which you control with the cursor keys; pressing the return key again finishes the drag. The space bar can be used similarly to create a stippled region. Double-pressing the return key (without moving the cursor) will clear the region, as a drag from an empty region does: this is useful with the cursor mode if you have filled the entire map in but need to correct the layout.
40</p>
41<p>
42If you press L during play, the game will toggle display of a number in each region of the map. This is useful if you want to discuss a particular puzzle instance with a friend &#8211; having an unambiguous name for each region is much easier than trying to refer to them all by names such as &#8216;the one down and right of the brown one on the top border&#8217;.
43</p>
44<p>
45(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
46</p>
47<h2><a name="S22.2"></a>22.2 <a name="i3"></a>Map parameters</h2>
48<p>
49These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
50</p>
51<dl><dt>
52<em>Width</em>, <em>Height</em>
53</dt>
54<dd>
55Size of grid in squares.
56</dd>
57<dt>
58<em>Regions</em>
59</dt>
60<dd>
61Number of regions in the generated map.
62</dd>
63<dt>
64<em>Difficulty</em>
65</dt>
66<dd>
67In &#8216;Easy&#8217; mode, there should always be at least one region whose colour can be determined trivially. In &#8216;Normal&#8217; and &#8216;Hard&#8217; modes, you will have to use increasingly complex logic to deduce the colour of some regions. However, it will always be possible without having to guess or backtrack.
68<p>
69In &#8216;Unreasonable&#8217; mode, the program will feel free to generate puzzles which are as hard as it can possibly make them: the only constraint is that they should still have a unique solution. Solving Unreasonable puzzles may require guessing and backtracking.
70</p>
71
72</dd>
73</dl>
74
75<hr><address></address></body>
76</html>
diff --git a/apps/plugins/puzzles/src/maxflow.c b/apps/plugins/puzzles/src/maxflow.c
new file mode 100644
index 0000000000..028946b9bd
--- /dev/null
+++ b/apps/plugins/puzzles/src/maxflow.c
@@ -0,0 +1,461 @@
1/*
2 * Edmonds-Karp algorithm for finding a maximum flow and minimum
3 * cut in a network. Almost identical to the Ford-Fulkerson
4 * algorithm, but apparently using breadth-first search to find the
5 * _shortest_ augmenting path is a good way to guarantee
6 * termination and ensure the time complexity is not dependent on
7 * the actual value of the maximum flow. I don't understand why
8 * that should be, but it's claimed on the Internet that it's been
9 * proved, and that's good enough for me. I prefer BFS to DFS
10 * anyway :-)
11 */
12
13#include <assert.h>
14#include <stdlib.h>
15#include <stdio.h>
16
17#include "maxflow.h"
18
19#include "puzzles.h" /* for snewn/sfree */
20
21int maxflow_with_scratch(void *scratch, int nv, int source, int sink,
22 int ne, const int *edges, const int *backedges,
23 const int *capacity, int *flow, int *cut)
24{
25 int *todo = (int *)scratch;
26 int *prev = todo + nv;
27 int *firstedge = todo + 2*nv;
28 int *firstbackedge = todo + 3*nv;
29 int i, j, head, tail, from, to;
30 int totalflow;
31
32 /*
33 * Scan the edges array to find the index of the first edge
34 * from each node.
35 */
36 j = 0;
37 for (i = 0; i < ne; i++)
38 while (j <= edges[2*i])
39 firstedge[j++] = i;
40 while (j < nv)
41 firstedge[j++] = ne;
42 assert(j == nv);
43
44 /*
45 * Scan the backedges array to find the index of the first edge
46 * _to_ each node.
47 */
48 j = 0;
49 for (i = 0; i < ne; i++)
50 while (j <= edges[2*backedges[i]+1])
51 firstbackedge[j++] = i;
52 while (j < nv)
53 firstbackedge[j++] = ne;
54 assert(j == nv);
55
56 /*
57 * Start the flow off at zero on every edge.
58 */
59 for (i = 0; i < ne; i++)
60 flow[i] = 0;
61 totalflow = 0;
62
63 /*
64 * Repeatedly look for an augmenting path, and follow it.
65 */
66 while (1) {
67
68 /*
69 * Set up the prev array.
70 */
71 for (i = 0; i < nv; i++)
72 prev[i] = -1;
73
74 /*
75 * Initialise the to-do list for BFS.
76 */
77 head = tail = 0;
78 todo[tail++] = source;
79
80 /*
81 * Now do the BFS loop.
82 */
83 while (head < tail && prev[sink] <= 0) {
84 from = todo[head++];
85
86 /*
87 * Try all the forward edges out of node `from'. For a
88 * forward edge to be valid, it must have flow
89 * currently less than its capacity.
90 */
91 for (i = firstedge[from]; i < ne && edges[2*i] == from; i++) {
92 to = edges[2*i+1];
93 if (to == source || prev[to] >= 0)
94 continue;
95 if (capacity[i] >= 0 && flow[i] >= capacity[i])
96 continue;
97 /*
98 * This is a valid augmenting edge. Visit node `to'.
99 */
100 prev[to] = 2*i;
101 todo[tail++] = to;
102 }
103
104 /*
105 * Try all the backward edges into node `from'. For a
106 * backward edge to be valid, it must have flow
107 * currently greater than zero.
108 */
109 for (i = firstbackedge[from];
110 j = backedges[i], i < ne && edges[2*j+1]==from; i++) {
111 to = edges[2*j];
112 if (to == source || prev[to] >= 0)
113 continue;
114 if (flow[j] <= 0)
115 continue;
116 /*
117 * This is a valid augmenting edge. Visit node `to'.
118 */
119 prev[to] = 2*j+1;
120 todo[tail++] = to;
121 }
122 }
123
124 /*
125 * If prev[sink] is non-null, we have found an augmenting
126 * path.
127 */
128 if (prev[sink] >= 0) {
129 int max;
130
131 /*
132 * Work backwards along the path figuring out the
133 * maximum flow we can add.
134 */
135 to = sink;
136 max = -1;
137 while (to != source) {
138 int spare;
139
140 /*
141 * Find the edge we're currently moving along.
142 */
143 i = prev[to];
144 from = edges[i];
145 assert(from != to);
146
147 /*
148 * Determine the spare capacity of this edge.
149 */
150 if (i & 1)
151 spare = flow[i / 2]; /* backward edge */
152 else if (capacity[i / 2] >= 0)
153 spare = capacity[i / 2] - flow[i / 2]; /* forward edge */
154 else
155 spare = -1; /* unlimited forward edge */
156
157 assert(spare != 0);
158
159 if (max < 0 || (spare >= 0 && spare < max))
160 max = spare;
161
162 to = from;
163 }
164 /*
165 * Fail an assertion if max is still < 0, i.e. there is
166 * an entirely unlimited path from source to sink. Also
167 * max should not _be_ zero, because by construction
168 * this _should_ be an augmenting path.
169 */
170 assert(max > 0);
171
172 /*
173 * Now work backwards along the path again, this time
174 * actually adjusting the flow.
175 */
176 to = sink;
177 while (to != source) {
178 /*
179 * Find the edge we're currently moving along.
180 */
181 i = prev[to];
182 from = edges[i];
183 assert(from != to);
184
185 /*
186 * Adjust the edge.
187 */
188 if (i & 1)
189 flow[i / 2] -= max; /* backward edge */
190 else
191 flow[i / 2] += max; /* forward edge */
192
193 to = from;
194 }
195
196 /*
197 * And adjust the overall flow counter.
198 */
199 totalflow += max;
200
201 continue;
202 }
203
204 /*
205 * If we reach here, we have failed to find an augmenting
206 * path, which means we're done. Output the `cut' array if
207 * required, and leave.
208 */
209 if (cut) {
210 for (i = 0; i < nv; i++) {
211 if (i == source || prev[i] >= 0)
212 cut[i] = 0;
213 else
214 cut[i] = 1;
215 }
216 }
217 return totalflow;
218 }
219}
220
221int maxflow_scratch_size(int nv)
222{
223 return (nv * 4) * sizeof(int);
224}
225
226void maxflow_setup_backedges(int ne, const int *edges, int *backedges)
227{
228 int i, n;
229
230 for (i = 0; i < ne; i++)
231 backedges[i] = i;
232
233 /*
234 * We actually can't use the C qsort() function, because we'd
235 * need to pass `edges' as a context parameter to its
236 * comparator function. So instead I'm forced to implement my
237 * own sorting algorithm internally, which is a pest. I'll use
238 * heapsort, because I like it.
239 */
240
241#define LESS(i,j) ( (edges[2*(i)+1] < edges[2*(j)+1]) || \
242 (edges[2*(i)+1] == edges[2*(j)+1] && \
243 edges[2*(i)] < edges[2*(j)]) )
244#define PARENT(n) ( ((n)-1)/2 )
245#define LCHILD(n) ( 2*(n)+1 )
246#define RCHILD(n) ( 2*(n)+2 )
247#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
248
249 /*
250 * Phase 1: build the heap. We want the _largest_ element at
251 * the top.
252 */
253 n = 0;
254 while (n < ne) {
255 n++;
256
257 /*
258 * Swap element n with its parent repeatedly to preserve
259 * the heap property.
260 */
261 i = n-1;
262
263 while (i > 0) {
264 int p = PARENT(i);
265
266 if (LESS(backedges[p], backedges[i])) {
267 SWAP(backedges[p], backedges[i]);
268 i = p;
269 } else
270 break;
271 }
272 }
273
274 /*
275 * Phase 2: repeatedly remove the largest element and stick it
276 * at the top of the array.
277 */
278 while (n > 0) {
279 /*
280 * The largest element is at position 0. Put it at the top,
281 * and swap the arbitrary element from that position into
282 * position 0.
283 */
284 n--;
285 SWAP(backedges[0], backedges[n]);
286
287 /*
288 * Now repeatedly move that arbitrary element down the heap
289 * by swapping it with the more suitable of its children.
290 */
291 i = 0;
292 while (1) {
293 int lc, rc;
294
295 lc = LCHILD(i);
296 rc = RCHILD(i);
297
298 if (lc >= n)
299 break; /* we've hit bottom */
300
301 if (rc >= n) {
302 /*
303 * Special case: there is only one child to check.
304 */
305 if (LESS(backedges[i], backedges[lc]))
306 SWAP(backedges[i], backedges[lc]);
307
308 /* _Now_ we've hit bottom. */
309 break;
310 } else {
311 /*
312 * The common case: there are two children and we
313 * must check them both.
314 */
315 if (LESS(backedges[i], backedges[lc]) ||
316 LESS(backedges[i], backedges[rc])) {
317 /*
318 * Pick the more appropriate child to swap with
319 * (i.e. the one which would want to be the
320 * parent if one were above the other - as one
321 * is about to be).
322 */
323 if (LESS(backedges[lc], backedges[rc])) {
324 SWAP(backedges[i], backedges[rc]);
325 i = rc;
326 } else {
327 SWAP(backedges[i], backedges[lc]);
328 i = lc;
329 }
330 } else {
331 /* This element is in the right place; we're done. */
332 break;
333 }
334 }
335 }
336 }
337
338#undef LESS
339#undef PARENT
340#undef LCHILD
341#undef RCHILD
342#undef SWAP
343
344}
345
346int maxflow(int nv, int source, int sink,
347 int ne, const int *edges, const int *capacity,
348 int *flow, int *cut)
349{
350 void *scratch;
351 int *backedges;
352 int size;
353 int ret;
354
355 /*
356 * Allocate the space.
357 */
358 size = ne * sizeof(int) + maxflow_scratch_size(nv);
359 backedges = smalloc(size);
360 if (!backedges)
361 return -1;
362 scratch = backedges + ne;
363
364 /*
365 * Set up the backedges array.
366 */
367 maxflow_setup_backedges(ne, edges, backedges);
368
369 /*
370 * Call the main function.
371 */
372 ret = maxflow_with_scratch(scratch, nv, source, sink, ne, edges,
373 backedges, capacity, flow, cut);
374
375 /*
376 * Free the scratch space.
377 */
378 sfree(backedges);
379
380 /*
381 * And we're done.
382 */
383 return ret;
384}
385
386#ifdef TESTMODE
387
388#define MAXEDGES 256
389#define MAXVERTICES 128
390#define ADDEDGE(i,j) do{edges[ne*2] = (i); edges[ne*2+1] = (j); ne++;}while(0)
391
392int compare_edge(const void *av, const void *bv)
393{
394 const int *a = (const int *)av;
395 const int *b = (const int *)bv;
396
397 if (a[0] < b[0])
398 return -1;
399 else if (a[0] > b[0])
400 return +1;
401 else if (a[1] < b[1])
402 return -1;
403 else if (a[1] > b[1])
404 return +1;
405 else
406 return 0;
407}
408
409int main(void)
410{
411 int edges[MAXEDGES*2], ne, nv;
412 int capacity[MAXEDGES], flow[MAXEDGES], cut[MAXVERTICES];
413 int source, sink, p, q, i, j, ret;
414
415 /*
416 * Use this algorithm to find a maximal complete matching in a
417 * bipartite graph.
418 */
419 ne = 0;
420 nv = 0;
421 source = nv++;
422 p = nv;
423 nv += 5;
424 q = nv;
425 nv += 5;
426 sink = nv++;
427 for (i = 0; i < 5; i++) {
428 capacity[ne] = 1;
429 ADDEDGE(source, p+i);
430 }
431 for (i = 0; i < 5; i++) {
432 capacity[ne] = 1;
433 ADDEDGE(q+i, sink);
434 }
435 j = ne;
436 capacity[ne] = 1; ADDEDGE(p+0,q+0);
437 capacity[ne] = 1; ADDEDGE(p+1,q+0);
438 capacity[ne] = 1; ADDEDGE(p+1,q+1);
439 capacity[ne] = 1; ADDEDGE(p+2,q+1);
440 capacity[ne] = 1; ADDEDGE(p+2,q+2);
441 capacity[ne] = 1; ADDEDGE(p+3,q+2);
442 capacity[ne] = 1; ADDEDGE(p+3,q+3);
443 capacity[ne] = 1; ADDEDGE(p+4,q+3);
444 /* capacity[ne] = 1; ADDEDGE(p+2,q+4); */
445 qsort(edges, ne, 2*sizeof(int), compare_edge);
446
447 ret = maxflow(nv, source, sink, ne, edges, capacity, flow, cut);
448
449 printf("ret = %d\n", ret);
450
451 for (i = 0; i < ne; i++)
452 printf("flow %d: %d -> %d\n", flow[i], edges[2*i], edges[2*i+1]);
453
454 for (i = 0; i < nv; i++)
455 if (cut[i] == 0)
456 printf("difficult set includes %d\n", i);
457
458 return 0;
459}
460
461#endif
diff --git a/apps/plugins/puzzles/src/maxflow.h b/apps/plugins/puzzles/src/maxflow.h
new file mode 100644
index 0000000000..d490f45421
--- /dev/null
+++ b/apps/plugins/puzzles/src/maxflow.h
@@ -0,0 +1,95 @@
1/*
2 * Edmonds-Karp algorithm for finding a maximum flow and minimum
3 * cut in a network. Almost identical to the Ford-Fulkerson
4 * algorithm, but apparently using breadth-first search to find the
5 * _shortest_ augmenting path is a good way to guarantee
6 * termination and ensure the time complexity is not dependent on
7 * the actual value of the maximum flow. I don't understand why
8 * that should be, but it's claimed on the Internet that it's been
9 * proved, and that's good enough for me. I prefer BFS to DFS
10 * anyway :-)
11 */
12
13#ifndef MAXFLOW_MAXFLOW_H
14#define MAXFLOW_MAXFLOW_H
15
16/*
17 * The actual algorithm.
18 *
19 * Inputs:
20 *
21 * - `scratch' is previously allocated scratch space of a size
22 * previously determined by calling `maxflow_scratch_size'.
23 *
24 * - `nv' is the number of vertices. Vertices are assumed to be
25 * numbered from 0 to nv-1.
26 *
27 * - `source' and `sink' are the distinguished source and sink
28 * vertices.
29 *
30 * - `ne' is the number of edges in the graph.
31 *
32 * - `edges' is an array of 2*ne integers, giving a (source, dest)
33 * pair for each network edge. Edge pairs are expected to be
34 * sorted in lexicographic order.
35 *
36 * - `backedges' is an array of `ne' integers, each a distinct
37 * index into `edges'. The edges in `edges', if permuted as
38 * specified by this array, should end up sorted in the _other_
39 * lexicographic order, i.e. dest taking priority over source.
40 *
41 * - `capacity' is an array of `ne' integers, giving a maximum
42 * flow capacity for each edge. A negative value is taken to
43 * indicate unlimited capacity on that edge, but note that there
44 * may not be any unlimited-capacity _path_ from source to sink
45 * or an assertion will be failed.
46 *
47 * Output:
48 *
49 * - `flow' must be non-NULL. It is an array of `ne' integers,
50 * each giving the final flow along each edge.
51 *
52 * - `cut' may be NULL. If non-NULL, it is an array of `nv'
53 * integers, which will be set to zero or one on output, in such
54 * a way that:
55 * + the set of zero vertices includes the source
56 * + the set of one vertices includes the sink
57 * + the maximum flow capacity between the zero and one vertex
58 * sets is achieved (i.e. all edges from a zero vertex to a
59 * one vertex are at full capacity, while all edges from a
60 * one vertex to a zero vertex have no flow at all).
61 *
62 * - the returned value from the function is the total flow
63 * achieved.
64 */
65int maxflow_with_scratch(void *scratch, int nv, int source, int sink,
66 int ne, const int *edges, const int *backedges,
67 const int *capacity, int *flow, int *cut);
68
69/*
70 * The above function expects its `scratch' and `backedges'
71 * parameters to have already been set up. This allows you to set
72 * them up once and use them in multiple invocates of the
73 * algorithm. Now I provide functions to actually do the setting
74 * up.
75 */
76int maxflow_scratch_size(int nv);
77void maxflow_setup_backedges(int ne, const int *edges, int *backedges);
78
79/*
80 * Simplified version of the above function. All parameters are the
81 * same, except that `scratch' and `backedges' are constructed
82 * internally. This is the simplest way to call the algorithm as a
83 * one-off; however, if you need to call it multiple times on the
84 * same network, it is probably better to call the above version
85 * directly so that you only construct `scratch' and `backedges'
86 * once.
87 *
88 * Additional return value is now -1, meaning that scratch space
89 * could not be allocated.
90 */
91int maxflow(int nv, int source, int sink,
92 int ne, const int *edges, const int *capacity,
93 int *flow, int *cut);
94
95#endif /* MAXFLOW_MAXFLOW_H */
diff --git a/apps/plugins/puzzles/src/midend.c b/apps/plugins/puzzles/src/midend.c
new file mode 100644
index 0000000000..2eb5ee93e8
--- /dev/null
+++ b/apps/plugins/puzzles/src/midend.c
@@ -0,0 +1,2237 @@
1/*
2 * midend.c: general middle fragment sitting between the
3 * platform-specific front end and game-specific back end.
4 * Maintains a move list, takes care of Undo and Redo commands, and
5 * processes standard keystrokes for undo/redo/new/quit.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <assert.h>
11#include <stdlib.h>
12#include <ctype.h>
13
14#include "puzzles.h"
15
16enum { DEF_PARAMS, DEF_SEED, DEF_DESC }; /* for midend_game_id_int */
17
18enum { NEWGAME, MOVE, SOLVE, RESTART };/* for midend_state_entry.movetype */
19
20#define special(type) ( (type) != MOVE )
21
22struct midend_state_entry {
23 game_state *state;
24 char *movestr;
25 int movetype;
26};
27
28struct midend {
29 frontend *frontend;
30 random_state *random;
31 const game *ourgame;
32
33 struct preset_menu *preset_menu;
34 char **encoded_presets; /* for midend_which_preset to check against */
35 int n_encoded_presets;
36
37 /*
38 * `desc' and `privdesc' deserve a comment.
39 *
40 * `desc' is the game description as presented to the user when
41 * they ask for Game -> Specific. `privdesc', if non-NULL, is a
42 * different game description used to reconstruct the initial
43 * game_state when de-serialising. If privdesc is NULL, `desc'
44 * is used for both.
45 *
46 * For almost all games, `privdesc' is NULL and never used. The
47 * exception (as usual) is Mines: the initial game state has no
48 * squares open at all, but after the first click `desc' is
49 * rewritten to describe a game state with an initial click and
50 * thus a bunch of squares open. If we used that desc to
51 * serialise and deserialise, then the initial game state after
52 * deserialisation would look unlike the initial game state
53 * beforehand, and worse still execute_move() might fail on the
54 * attempted first click. So `privdesc' is also used in this
55 * case, to provide a game description describing the same
56 * fixed mine layout _but_ no initial click. (These game IDs
57 * may also be typed directly into Mines if you like.)
58 */
59 char *desc, *privdesc, *seedstr;
60 char *aux_info;
61 enum { GOT_SEED, GOT_DESC, GOT_NOTHING } genmode;
62
63 int nstates, statesize, statepos;
64 struct midend_state_entry *states;
65
66 game_params *params, *curparams;
67 game_drawstate *drawstate;
68 game_ui *ui;
69
70 game_state *oldstate;
71 float anim_time, anim_pos;
72 float flash_time, flash_pos;
73 int dir;
74
75 int timing;
76 float elapsed;
77 char *laststatus;
78
79 drawing *drawing;
80
81 int pressed_mouse_button;
82
83 int preferred_tilesize, tilesize, winwidth, winheight;
84
85 void (*game_id_change_notify_function)(void *);
86 void *game_id_change_notify_ctx;
87};
88
89#define ensure(me) do { \
90 if ((me)->nstates >= (me)->statesize) { \
91 (me)->statesize = (me)->nstates + 128; \
92 (me)->states = sresize((me)->states, (me)->statesize, \
93 struct midend_state_entry); \
94 } \
95} while (0)
96
97void midend_reset_tilesize(midend *me)
98{
99 me->preferred_tilesize = me->ourgame->preferred_tilesize;
100 {
101 /*
102 * Allow an environment-based override for the default tile
103 * size by defining a variable along the lines of
104 * `NET_TILESIZE=15'.
105 */
106
107 char buf[80], *e;
108 int j, k, ts;
109
110 sprintf(buf, "%s_TILESIZE", me->ourgame->name);
111 for (j = k = 0; buf[j]; j++)
112 if (!isspace((unsigned char)buf[j]))
113 buf[k++] = toupper((unsigned char)buf[j]);
114 buf[k] = '\0';
115 if ((e = getenv(buf)) != NULL && sscanf(e, "%d", &ts) == 1 && ts > 0)
116 me->preferred_tilesize = ts;
117 }
118}
119
120midend *midend_new(frontend *fe, const game *ourgame,
121 const drawing_api *drapi, void *drhandle)
122{
123 midend *me = snew(midend);
124 void *randseed;
125 int randseedsize;
126
127 get_random_seed(&randseed, &randseedsize);
128
129 me->frontend = fe;
130 me->ourgame = ourgame;
131 me->random = random_new(randseed, randseedsize);
132 me->nstates = me->statesize = me->statepos = 0;
133 me->states = NULL;
134 me->params = ourgame->default_params();
135 me->game_id_change_notify_function = NULL;
136 me->game_id_change_notify_ctx = NULL;
137
138 /*
139 * Allow environment-based changing of the default settings by
140 * defining a variable along the lines of `NET_DEFAULT=25x25w'
141 * in which the value is an encoded parameter string.
142 */
143 {
144 char buf[80], *e;
145 int j, k;
146 sprintf(buf, "%s_DEFAULT", me->ourgame->name);
147 for (j = k = 0; buf[j]; j++)
148 if (!isspace((unsigned char)buf[j]))
149 buf[k++] = toupper((unsigned char)buf[j]);
150 buf[k] = '\0';
151 if ((e = getenv(buf)) != NULL)
152 me->ourgame->decode_params(me->params, e);
153 }
154 me->curparams = NULL;
155 me->desc = me->privdesc = NULL;
156 me->seedstr = NULL;
157 me->aux_info = NULL;
158 me->genmode = GOT_NOTHING;
159 me->drawstate = NULL;
160 me->oldstate = NULL;
161 me->preset_menu = NULL;
162 me->anim_time = me->anim_pos = 0.0F;
163 me->flash_time = me->flash_pos = 0.0F;
164 me->dir = 0;
165 me->ui = NULL;
166 me->pressed_mouse_button = 0;
167 me->laststatus = NULL;
168 me->timing = FALSE;
169 me->elapsed = 0.0F;
170 me->tilesize = me->winwidth = me->winheight = 0;
171 if (drapi)
172 me->drawing = drawing_new(drapi, me, drhandle);
173 else
174 me->drawing = NULL;
175
176 midend_reset_tilesize(me);
177
178 sfree(randseed);
179
180 return me;
181}
182
183const game *midend_which_game(midend *me)
184{
185 return me->ourgame;
186}
187
188static void midend_purge_states(midend *me)
189{
190 while (me->nstates > me->statepos) {
191 me->ourgame->free_game(me->states[--me->nstates].state);
192 if (me->states[me->nstates].movestr)
193 sfree(me->states[me->nstates].movestr);
194 }
195}
196
197static void midend_free_game(midend *me)
198{
199 while (me->nstates > 0) {
200 me->nstates--;
201 me->ourgame->free_game(me->states[me->nstates].state);
202 sfree(me->states[me->nstates].movestr);
203 }
204
205 if (me->drawstate)
206 me->ourgame->free_drawstate(me->drawing, me->drawstate);
207}
208
209static void midend_free_preset_menu(midend *me, struct preset_menu *menu)
210{
211 if (menu) {
212 int i;
213 for (i = 0; i < menu->n_entries; i++) {
214 sfree(menu->entries[i].title);
215 if (menu->entries[i].params)
216 me->ourgame->free_params(menu->entries[i].params);
217 midend_free_preset_menu(me, menu->entries[i].submenu);
218 }
219 sfree(menu->entries);
220 sfree(menu);
221 }
222}
223
224void midend_free(midend *me)
225{
226 midend_free_game(me);
227
228 if (me->drawing)
229 drawing_free(me->drawing);
230 random_free(me->random);
231 sfree(me->states);
232 sfree(me->desc);
233 sfree(me->privdesc);
234 sfree(me->seedstr);
235 sfree(me->aux_info);
236 me->ourgame->free_params(me->params);
237 midend_free_preset_menu(me, me->preset_menu);
238 if (me->ui)
239 me->ourgame->free_ui(me->ui);
240 if (me->curparams)
241 me->ourgame->free_params(me->curparams);
242 sfree(me->laststatus);
243 sfree(me);
244}
245
246static void midend_size_new_drawstate(midend *me)
247{
248 /*
249 * Don't even bother, if we haven't worked out our tile size
250 * anyway yet.
251 */
252 if (me->tilesize > 0) {
253 me->ourgame->compute_size(me->params, me->tilesize,
254 &me->winwidth, &me->winheight);
255 me->ourgame->set_size(me->drawing, me->drawstate,
256 me->params, me->tilesize);
257 }
258}
259
260void midend_size(midend *me, int *x, int *y, int user_size)
261{
262 int min, max;
263 int rx, ry;
264
265 /*
266 * We can't set the size on the same drawstate twice. So if
267 * we've already sized one drawstate, we must throw it away and
268 * create a new one.
269 */
270 if (me->drawstate && me->tilesize > 0) {
271 me->ourgame->free_drawstate(me->drawing, me->drawstate);
272 me->drawstate = me->ourgame->new_drawstate(me->drawing,
273 me->states[0].state);
274 }
275
276 /*
277 * Find the tile size that best fits within the given space. If
278 * `user_size' is TRUE, we must actually find the _largest_ such
279 * tile size, in order to get as close to the user's explicit
280 * request as possible; otherwise, we bound above at the game's
281 * preferred tile size, so that the game gets what it wants
282 * provided that this doesn't break the constraint from the
283 * front-end (which is likely to be a screen size or similar).
284 */
285 if (user_size) {
286 max = 1;
287 do {
288 max *= 2;
289 me->ourgame->compute_size(me->params, max, &rx, &ry);
290 } while (rx <= *x && ry <= *y);
291 } else
292 max = me->preferred_tilesize + 1;
293 min = 1;
294
295 /*
296 * Now binary-search between min and max. We're looking for a
297 * boundary rather than a value: the point at which tile sizes
298 * stop fitting within the given dimensions. Thus, we stop when
299 * max and min differ by exactly 1.
300 */
301 while (max - min > 1) {
302 int mid = (max + min) / 2;
303 me->ourgame->compute_size(me->params, mid, &rx, &ry);
304 if (rx <= *x && ry <= *y)
305 min = mid;
306 else
307 max = mid;
308 }
309
310 /*
311 * Now `min' is a valid size, and `max' isn't. So use `min'.
312 */
313
314 me->tilesize = min;
315 if (user_size)
316 /* If the user requested a change in size, make it permanent. */
317 me->preferred_tilesize = me->tilesize;
318 midend_size_new_drawstate(me);
319 *x = me->winwidth;
320 *y = me->winheight;
321}
322
323int midend_tilesize(midend *me) { return me->tilesize; }
324
325void midend_set_params(midend *me, game_params *params)
326{
327 me->ourgame->free_params(me->params);
328 me->params = me->ourgame->dup_params(params);
329}
330
331game_params *midend_get_params(midend *me)
332{
333 return me->ourgame->dup_params(me->params);
334}
335
336static void midend_set_timer(midend *me)
337{
338 me->timing = (me->ourgame->is_timed &&
339 me->ourgame->timing_state(me->states[me->statepos-1].state,
340 me->ui));
341 if (me->timing || me->flash_time || me->anim_time)
342 activate_timer(me->frontend);
343 else
344 deactivate_timer(me->frontend);
345}
346
347void midend_force_redraw(midend *me)
348{
349 if (me->drawstate)
350 me->ourgame->free_drawstate(me->drawing, me->drawstate);
351 me->drawstate = me->ourgame->new_drawstate(me->drawing,
352 me->states[0].state);
353 midend_size_new_drawstate(me);
354 midend_redraw(me);
355}
356
357void midend_new_game(midend *me)
358{
359 midend_stop_anim(me);
360 midend_free_game(me);
361
362 assert(me->nstates == 0);
363
364 if (me->genmode == GOT_DESC) {
365 me->genmode = GOT_NOTHING;
366 } else {
367 random_state *rs;
368
369 if (me->genmode == GOT_SEED) {
370 me->genmode = GOT_NOTHING;
371 } else {
372 /*
373 * Generate a new random seed. 15 digits comes to about
374 * 48 bits, which should be more than enough.
375 *
376 * I'll avoid putting a leading zero on the number,
377 * just in case it confuses anybody who thinks it's
378 * processed as an integer rather than a string.
379 */
380 char newseed[16];
381 int i;
382 newseed[15] = '\0';
383 newseed[0] = '1' + (char)random_upto(me->random, 9);
384 for (i = 1; i < 15; i++)
385 newseed[i] = '0' + (char)random_upto(me->random, 10);
386 sfree(me->seedstr);
387 me->seedstr = dupstr(newseed);
388
389 if (me->curparams)
390 me->ourgame->free_params(me->curparams);
391 me->curparams = me->ourgame->dup_params(me->params);
392 }
393
394 sfree(me->desc);
395 sfree(me->privdesc);
396 sfree(me->aux_info);
397 me->aux_info = NULL;
398
399 rs = random_new(me->seedstr, strlen(me->seedstr));
400 /*
401 * If this midend has been instantiated without providing a
402 * drawing API, it is non-interactive. This means that it's
403 * being used for bulk game generation, and hence we should
404 * pass the non-interactive flag to new_desc.
405 */
406 me->desc = me->ourgame->new_desc(me->curparams, rs,
407 &me->aux_info, (me->drawing != NULL));
408 me->privdesc = NULL;
409 random_free(rs);
410 }
411
412 ensure(me);
413
414 /*
415 * It might seem a bit odd that we're using me->params to
416 * create the initial game state, rather than me->curparams
417 * which is better tailored to this specific game and which we
418 * always know.
419 *
420 * It's supposed to be an invariant in the midend that
421 * me->params and me->curparams differ in no aspect that is
422 * important after generation (i.e. after new_desc()). By
423 * deliberately passing the _less_ specific of these two
424 * parameter sets, we provoke play-time misbehaviour in the
425 * case where a game has failed to encode a play-time parameter
426 * in the non-full version of encode_params().
427 */
428 me->states[me->nstates].state =
429 me->ourgame->new_game(me, me->params, me->desc);
430
431 /*
432 * As part of our commitment to self-testing, test the aux
433 * string to make sure nothing ghastly went wrong.
434 */
435 if (me->ourgame->can_solve && me->aux_info) {
436 game_state *s;
437 char *msg, *movestr;
438
439 msg = NULL;
440 movestr = me->ourgame->solve(me->states[0].state,
441 me->states[0].state,
442 me->aux_info, &msg);
443 assert(movestr && !msg);
444 s = me->ourgame->execute_move(me->states[0].state, movestr);
445 assert(s);
446 me->ourgame->free_game(s);
447 sfree(movestr);
448 }
449
450 me->states[me->nstates].movestr = NULL;
451 me->states[me->nstates].movetype = NEWGAME;
452 me->nstates++;
453 me->statepos = 1;
454 me->drawstate = me->ourgame->new_drawstate(me->drawing,
455 me->states[0].state);
456 midend_size_new_drawstate(me);
457 me->elapsed = 0.0F;
458 me->flash_pos = me->flash_time = 0.0F;
459 me->anim_pos = me->anim_time = 0.0F;
460 if (me->ui)
461 me->ourgame->free_ui(me->ui);
462 me->ui = me->ourgame->new_ui(me->states[0].state);
463 midend_set_timer(me);
464 me->pressed_mouse_button = 0;
465
466 if (me->game_id_change_notify_function)
467 me->game_id_change_notify_function(me->game_id_change_notify_ctx);
468}
469
470int midend_can_undo(midend *me)
471{
472 return (me->statepos > 1);
473}
474
475int midend_can_redo(midend *me)
476{
477 return (me->statepos < me->nstates);
478}
479
480static int midend_undo(midend *me)
481{
482 if (me->statepos > 1) {
483 if (me->ui)
484 me->ourgame->changed_state(me->ui,
485 me->states[me->statepos-1].state,
486 me->states[me->statepos-2].state);
487 me->statepos--;
488 me->dir = -1;
489 return 1;
490 } else
491 return 0;
492}
493
494static int midend_redo(midend *me)
495{
496 if (me->statepos < me->nstates) {
497 if (me->ui)
498 me->ourgame->changed_state(me->ui,
499 me->states[me->statepos-1].state,
500 me->states[me->statepos].state);
501 me->statepos++;
502 me->dir = +1;
503 return 1;
504 } else
505 return 0;
506}
507
508static void midend_finish_move(midend *me)
509{
510 float flashtime;
511
512 /*
513 * We do not flash if the later of the two states is special.
514 * This covers both forward Solve moves and backward (undone)
515 * Restart moves.
516 */
517 if ((me->oldstate || me->statepos > 1) &&
518 ((me->dir > 0 && !special(me->states[me->statepos-1].movetype)) ||
519 (me->dir < 0 && me->statepos < me->nstates &&
520 !special(me->states[me->statepos].movetype)))) {
521 flashtime = me->ourgame->flash_length(me->oldstate ? me->oldstate :
522 me->states[me->statepos-2].state,
523 me->states[me->statepos-1].state,
524 me->oldstate ? me->dir : +1,
525 me->ui);
526 if (flashtime > 0) {
527 me->flash_pos = 0.0F;
528 me->flash_time = flashtime;
529 }
530 }
531
532 if (me->oldstate)
533 me->ourgame->free_game(me->oldstate);
534 me->oldstate = NULL;
535 me->anim_pos = me->anim_time = 0;
536 me->dir = 0;
537
538 midend_set_timer(me);
539}
540
541void midend_stop_anim(midend *me)
542{
543 if (me->oldstate || me->anim_time != 0) {
544 midend_finish_move(me);
545 midend_redraw(me);
546 }
547}
548
549void midend_restart_game(midend *me)
550{
551 game_state *s;
552
553 assert(me->statepos >= 1);
554 if (me->statepos == 1)
555 return; /* no point doing anything at all! */
556
557 /*
558 * During restart, we reconstruct the game from the (public)
559 * game description rather than from states[0], because that
560 * way Mines gets slightly more sensible behaviour (restart
561 * goes to _after_ the first click so you don't have to
562 * remember where you clicked).
563 */
564 s = me->ourgame->new_game(me, me->params, me->desc);
565
566 /*
567 * Now enter the restarted state as the next move.
568 */
569 midend_stop_anim(me);
570 midend_purge_states(me);
571 ensure(me);
572 me->states[me->nstates].state = s;
573 me->states[me->nstates].movestr = dupstr(me->desc);
574 me->states[me->nstates].movetype = RESTART;
575 me->statepos = ++me->nstates;
576 if (me->ui)
577 me->ourgame->changed_state(me->ui,
578 me->states[me->statepos-2].state,
579 me->states[me->statepos-1].state);
580 me->flash_pos = me->flash_time = 0.0F;
581 midend_finish_move(me);
582 midend_redraw(me);
583 midend_set_timer(me);
584}
585
586static int midend_really_process_key(midend *me, int x, int y, int button)
587{
588 game_state *oldstate =
589 me->ourgame->dup_game(me->states[me->statepos - 1].state);
590 int type = MOVE, gottype = FALSE, ret = 1;
591 float anim_time;
592 game_state *s;
593 char *movestr;
594
595 movestr =
596 me->ourgame->interpret_move(me->states[me->statepos-1].state,
597 me->ui, me->drawstate, x, y, button);
598
599 if (!movestr) {
600 if (button == 'n' || button == 'N' || button == '\x0E') {
601 midend_new_game(me);
602 midend_redraw(me);
603 goto done; /* never animate */
604 } else if (button == 'u' || button == 'U' ||
605 button == '\x1A' || button == '\x1F') {
606 midend_stop_anim(me);
607 type = me->states[me->statepos-1].movetype;
608 gottype = TRUE;
609 if (!midend_undo(me))
610 goto done;
611 } else if (button == 'r' || button == 'R' ||
612 button == '\x12' || button == '\x19') {
613 midend_stop_anim(me);
614 if (!midend_redo(me))
615 goto done;
616 } else if (button == '\x13' && me->ourgame->can_solve) {
617 if (midend_solve(me))
618 goto done;
619 } else if (button == 'q' || button == 'Q' || button == '\x11') {
620 ret = 0;
621 goto done;
622 } else
623 goto done;
624 } else {
625 if (!*movestr)
626 s = me->states[me->statepos-1].state;
627 else {
628 s = me->ourgame->execute_move(me->states[me->statepos-1].state,
629 movestr);
630 assert(s != NULL);
631 }
632
633 if (s == me->states[me->statepos-1].state) {
634 /*
635 * make_move() is allowed to return its input state to
636 * indicate that although no move has been made, the UI
637 * state has been updated and a redraw is called for.
638 */
639 midend_redraw(me);
640 midend_set_timer(me);
641 goto done;
642 } else if (s) {
643 midend_stop_anim(me);
644 midend_purge_states(me);
645 ensure(me);
646 assert(movestr != NULL);
647 me->states[me->nstates].state = s;
648 me->states[me->nstates].movestr = movestr;
649 me->states[me->nstates].movetype = MOVE;
650 me->statepos = ++me->nstates;
651 me->dir = +1;
652 if (me->ui)
653 me->ourgame->changed_state(me->ui,
654 me->states[me->statepos-2].state,
655 me->states[me->statepos-1].state);
656 } else {
657 goto done;
658 }
659 }
660
661 if (!gottype)
662 type = me->states[me->statepos-1].movetype;
663
664 /*
665 * See if this move requires an animation.
666 */
667 if (special(type) && !(type == SOLVE &&
668 (me->ourgame->flags & SOLVE_ANIMATES))) {
669 anim_time = 0;
670 } else {
671 anim_time = me->ourgame->anim_length(oldstate,
672 me->states[me->statepos-1].state,
673 me->dir, me->ui);
674 }
675
676 me->oldstate = oldstate; oldstate = NULL;
677 if (anim_time > 0) {
678 me->anim_time = anim_time;
679 } else {
680 me->anim_time = 0.0;
681 midend_finish_move(me);
682 }
683 me->anim_pos = 0.0;
684
685 midend_redraw(me);
686
687 midend_set_timer(me);
688
689 done:
690 if (oldstate) me->ourgame->free_game(oldstate);
691 return ret;
692}
693
694int midend_process_key(midend *me, int x, int y, int button)
695{
696 int ret = 1;
697
698 /*
699 * Harmonise mouse drag and release messages.
700 *
701 * Some front ends might accidentally switch from sending, say,
702 * RIGHT_DRAG messages to sending LEFT_DRAG, half way through a
703 * drag. (This can happen on the Mac, for example, since
704 * RIGHT_DRAG is usually done using Command+drag, and if the
705 * user accidentally releases Command half way through the drag
706 * then there will be trouble.)
707 *
708 * It would be an O(number of front ends) annoyance to fix this
709 * in the front ends, but an O(number of back ends) annoyance
710 * to have each game capable of dealing with it. Therefore, we
711 * fix it _here_ in the common midend code so that it only has
712 * to be done once.
713 *
714 * The possible ways in which things can go screwy in the front
715 * end are:
716 *
717 * - in a system containing multiple physical buttons button
718 * presses can inadvertently overlap. We can see ABab (caps
719 * meaning button-down and lowercase meaning button-up) when
720 * the user had semantically intended AaBb.
721 *
722 * - in a system where one button is simulated by means of a
723 * modifier key and another button, buttons can mutate
724 * between press and release (possibly during drag). So we
725 * can see Ab instead of Aa.
726 *
727 * Definite requirements are:
728 *
729 * - button _presses_ must never be invented or destroyed. If
730 * the user presses two buttons in succession, the button
731 * presses must be transferred to the backend unchanged. So
732 * if we see AaBb , that's fine; if we see ABab (the button
733 * presses inadvertently overlapped) we must somehow
734 * `correct' it to AaBb.
735 *
736 * - every mouse action must end up looking like a press, zero
737 * or more drags, then a release. This allows back ends to
738 * make the _assumption_ that incoming mouse data will be
739 * sane in this regard, and not worry about the details.
740 *
741 * So my policy will be:
742 *
743 * - treat any button-up as a button-up for the currently
744 * pressed button, or ignore it if there is no currently
745 * pressed button.
746 *
747 * - treat any drag as a drag for the currently pressed
748 * button, or ignore it if there is no currently pressed
749 * button.
750 *
751 * - if we see a button-down while another button is currently
752 * pressed, invent a button-up for the first one and then
753 * pass the button-down through as before.
754 *
755 * 2005-05-31: An addendum to the above. Some games might want
756 * a `priority order' among buttons, such that if one button is
757 * pressed while another is down then a fixed one of the
758 * buttons takes priority no matter what order they're pressed
759 * in. Mines, in particular, wants to treat a left+right click
760 * like a left click for the benefit of users of other
761 * implementations. So the last of the above points is modified
762 * in the presence of an (optional) button priority order.
763 *
764 * A further addition: we translate certain keyboard presses to
765 * cursor key 'select' buttons, so that a) frontends don't have
766 * to translate these themselves (like they do for CURSOR_UP etc),
767 * and b) individual games don't have to hard-code button presses
768 * of '\n' etc for keyboard-based cursors. The choice of buttons
769 * here could eventually be controlled by a runtime configuration
770 * option.
771 */
772 if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
773 if (me->pressed_mouse_button) {
774 if (IS_MOUSE_DRAG(button)) {
775 button = me->pressed_mouse_button +
776 (LEFT_DRAG - LEFT_BUTTON);
777 } else {
778 button = me->pressed_mouse_button +
779 (LEFT_RELEASE - LEFT_BUTTON);
780 }
781 } else
782 return ret; /* ignore it */
783 } else if (IS_MOUSE_DOWN(button) && me->pressed_mouse_button) {
784 /*
785 * If the new button has lower priority than the old one,
786 * don't bother doing this.
787 */
788 if (me->ourgame->flags &
789 BUTTON_BEATS(me->pressed_mouse_button, button))
790 return ret; /* just ignore it */
791
792 /*
793 * Fabricate a button-up for the previously pressed button.
794 */
795 ret = ret && midend_really_process_key
796 (me, x, y, (me->pressed_mouse_button +
797 (LEFT_RELEASE - LEFT_BUTTON)));
798 }
799
800 /*
801 * Translate keyboard presses to cursor selection.
802 */
803 if (button == '\n' || button == '\r')
804 button = CURSOR_SELECT;
805 if (button == ' ')
806 button = CURSOR_SELECT2;
807
808 /*
809 * Normalise both backspace characters (8 and 127) to \b. Easier
810 * to do this once, here, than to require all front ends to
811 * carefully generate the same one - now each front end can
812 * generate whichever is easiest.
813 */
814 if (button == '\177')
815 button = '\b';
816
817 /*
818 * Now send on the event we originally received.
819 */
820 ret = ret && midend_really_process_key(me, x, y, button);
821
822 /*
823 * And update the currently pressed button.
824 */
825 if (IS_MOUSE_RELEASE(button))
826 me->pressed_mouse_button = 0;
827 else if (IS_MOUSE_DOWN(button))
828 me->pressed_mouse_button = button;
829
830 return ret;
831}
832
833void midend_redraw(midend *me)
834{
835 assert(me->drawing);
836
837 if (me->statepos > 0 && me->drawstate) {
838 start_draw(me->drawing);
839 if (me->oldstate && me->anim_time > 0 &&
840 me->anim_pos < me->anim_time) {
841 assert(me->dir != 0);
842 me->ourgame->redraw(me->drawing, me->drawstate, me->oldstate,
843 me->states[me->statepos-1].state, me->dir,
844 me->ui, me->anim_pos, me->flash_pos);
845 } else {
846 me->ourgame->redraw(me->drawing, me->drawstate, NULL,
847 me->states[me->statepos-1].state, +1 /*shrug*/,
848 me->ui, 0.0, me->flash_pos);
849 }
850 end_draw(me->drawing);
851 }
852}
853
854/*
855 * Nasty hacky function used to implement the --redo option in
856 * gtk.c. Only used for generating the puzzles' icons.
857 */
858void midend_freeze_timer(midend *me, float tprop)
859{
860 me->anim_pos = me->anim_time * tprop;
861 midend_redraw(me);
862 deactivate_timer(me->frontend);
863}
864
865void midend_timer(midend *me, float tplus)
866{
867 int need_redraw = (me->anim_time > 0 || me->flash_time > 0);
868
869 me->anim_pos += tplus;
870 if (me->anim_pos >= me->anim_time ||
871 me->anim_time == 0 || !me->oldstate) {
872 if (me->anim_time > 0)
873 midend_finish_move(me);
874 }
875
876 me->flash_pos += tplus;
877 if (me->flash_pos >= me->flash_time || me->flash_time == 0) {
878 me->flash_pos = me->flash_time = 0;
879 }
880
881 if (need_redraw)
882 midend_redraw(me);
883
884 if (me->timing) {
885 float oldelapsed = me->elapsed;
886 me->elapsed += tplus;
887 if ((int)oldelapsed != (int)me->elapsed)
888 status_bar(me->drawing, me->laststatus ? me->laststatus : "");
889 }
890
891 midend_set_timer(me);
892}
893
894float *midend_colours(midend *me, int *ncolours)
895{
896 float *ret;
897
898 ret = me->ourgame->colours(me->frontend, ncolours);
899
900 {
901 int i;
902
903 /*
904 * Allow environment-based overrides for the standard
905 * colours by defining variables along the lines of
906 * `NET_COLOUR_4=6000c0'.
907 */
908
909 for (i = 0; i < *ncolours; i++) {
910 char buf[80], *e;
911 unsigned int r, g, b;
912 int j, k;
913
914 sprintf(buf, "%s_COLOUR_%d", me->ourgame->name, i);
915 for (j = k = 0; buf[j]; j++)
916 if (!isspace((unsigned char)buf[j]))
917 buf[k++] = toupper((unsigned char)buf[j]);
918 buf[k] = '\0';
919 if ((e = getenv(buf)) != NULL &&
920 sscanf(e, "%2x%2x%2x", &r, &g, &b) == 3) {
921 ret[i*3 + 0] = r / 255.0F;
922 ret[i*3 + 1] = g / 255.0F;
923 ret[i*3 + 2] = b / 255.0F;
924 }
925 }
926 }
927
928 return ret;
929}
930
931struct preset_menu *preset_menu_new(void)
932{
933 struct preset_menu *menu = snew(struct preset_menu);
934 menu->n_entries = 0;
935 menu->entries_size = 0;
936 menu->entries = NULL;
937 return menu;
938}
939
940static struct preset_menu_entry *preset_menu_add(struct preset_menu *menu,
941 char *title)
942{
943 struct preset_menu_entry *toret;
944 if (menu->n_entries >= menu->entries_size) {
945 menu->entries_size = menu->n_entries * 5 / 4 + 10;
946 menu->entries = sresize(menu->entries, menu->entries_size,
947 struct preset_menu_entry);
948 }
949 toret = &menu->entries[menu->n_entries++];
950 toret->title = title;
951 toret->params = NULL;
952 toret->submenu = NULL;
953 return toret;
954}
955
956struct preset_menu *preset_menu_add_submenu(struct preset_menu *parent,
957 char *title)
958{
959 struct preset_menu_entry *entry = preset_menu_add(parent, title);
960 entry->submenu = preset_menu_new();
961 return entry->submenu;
962}
963
964void preset_menu_add_preset(struct preset_menu *parent,
965 char *title, game_params *params)
966{
967 struct preset_menu_entry *entry = preset_menu_add(parent, title);
968 entry->params = params;
969}
970
971game_params *preset_menu_lookup_by_id(struct preset_menu *menu, int id)
972{
973 int i;
974 game_params *retd;
975
976 for (i = 0; i < menu->n_entries; i++) {
977 if (id == menu->entries[i].id)
978 return menu->entries[i].params;
979 if (menu->entries[i].submenu &&
980 (retd = preset_menu_lookup_by_id(
981 menu->entries[i].submenu, id)) != NULL)
982 return retd;
983 }
984
985 return NULL;
986}
987
988static char *preset_menu_add_from_user_env(
989 midend *me, struct preset_menu *menu, char *p, int top_level)
990{
991 while (*p) {
992 char *name, *val;
993 game_params *preset;
994
995 name = p;
996 while (*p && *p != ':') p++;
997 if (*p) *p++ = '\0';
998 val = p;
999 while (*p && *p != ':') p++;
1000 if (*p) *p++ = '\0';
1001
1002 if (!strcmp(val, "#")) {
1003 /*
1004 * Special case: either open a new submenu with the given
1005 * title, or terminate the current submenu.
1006 */
1007 if (*name) {
1008 struct preset_menu *submenu =
1009 preset_menu_add_submenu(menu, dupstr(name));
1010 p = preset_menu_add_from_user_env(me, submenu, p, FALSE);
1011 } else {
1012 /*
1013 * If we get a 'close submenu' indication at the top
1014 * level, there's not much we can do but quietly
1015 * ignore it.
1016 */
1017 if (!top_level)
1018 return p;
1019 }
1020 continue;
1021 }
1022
1023 preset = me->ourgame->default_params();
1024 me->ourgame->decode_params(preset, val);
1025
1026 if (me->ourgame->validate_params(preset, TRUE)) {
1027 /* Drop this one from the list. */
1028 me->ourgame->free_params(preset);
1029 continue;
1030 }
1031
1032 preset_menu_add_preset(menu, dupstr(name), preset);
1033 }
1034
1035 return p;
1036}
1037
1038static void preset_menu_alloc_ids(midend *me, struct preset_menu *menu)
1039{
1040 int i;
1041
1042 for (i = 0; i < menu->n_entries; i++)
1043 menu->entries[i].id = me->n_encoded_presets++;
1044
1045 for (i = 0; i < menu->n_entries; i++)
1046 if (menu->entries[i].submenu)
1047 preset_menu_alloc_ids(me, menu->entries[i].submenu);
1048}
1049
1050static void preset_menu_encode_params(midend *me, struct preset_menu *menu)
1051{
1052 int i;
1053
1054 for (i = 0; i < menu->n_entries; i++) {
1055 if (menu->entries[i].params) {
1056 me->encoded_presets[menu->entries[i].id] =
1057 me->ourgame->encode_params(menu->entries[i].params, TRUE);
1058 } else {
1059 preset_menu_encode_params(me, menu->entries[i].submenu);
1060 }
1061 }
1062}
1063
1064struct preset_menu *midend_get_presets(midend *me, int *id_limit)
1065{
1066 int i;
1067
1068 if (me->preset_menu)
1069 return me->preset_menu;
1070
1071#if 0
1072 /* Expect the game to implement exactly one of the two preset APIs */
1073 assert(me->ourgame->fetch_preset || me->ourgame->preset_menu);
1074 assert(!(me->ourgame->fetch_preset && me->ourgame->preset_menu));
1075#endif
1076
1077 if (me->ourgame->fetch_preset) {
1078 char *name;
1079 game_params *preset;
1080
1081 /* Simple one-level menu */
1082 assert(!me->ourgame->preset_menu);
1083 me->preset_menu = preset_menu_new();
1084 for (i = 0; me->ourgame->fetch_preset(i, &name, &preset); i++)
1085 preset_menu_add_preset(me->preset_menu, name, preset);
1086
1087 } else {
1088 /* Hierarchical menu provided by the game backend */
1089 me->preset_menu = me->ourgame->preset_menu();
1090 }
1091
1092 {
1093 /*
1094 * Allow user extensions to the preset list by defining an
1095 * environment variable <gamename>_PRESETS whose value is a
1096 * colon-separated list of items, alternating between textual
1097 * titles in the menu and encoded parameter strings. For
1098 * example, "SOLO_PRESETS=2x3 Advanced:2x3da" would define
1099 * just one additional preset for Solo.
1100 */
1101 char buf[80], *e;
1102 int j, k;
1103
1104 sprintf(buf, "%s_PRESETS", me->ourgame->name);
1105 for (j = k = 0; buf[j]; j++)
1106 if (!isspace((unsigned char)buf[j]))
1107 buf[k++] = toupper((unsigned char)buf[j]);
1108 buf[k] = '\0';
1109
1110 if ((e = getenv(buf)) != NULL) {
1111 e = dupstr(e);
1112 preset_menu_add_from_user_env(me, me->preset_menu, e, TRUE);
1113 sfree(e);
1114 }
1115 }
1116
1117 /*
1118 * Finalise the menu: allocate an integer id to each entry, and
1119 * store string encodings of the presets' parameters in
1120 * me->encoded_presets.
1121 */
1122 me->n_encoded_presets = 0;
1123 preset_menu_alloc_ids(me, me->preset_menu);
1124 me->encoded_presets = snewn(me->n_encoded_presets, char *);
1125 for (i = 0; i < me->n_encoded_presets; i++)
1126 me->encoded_presets[i] = NULL;
1127 preset_menu_encode_params(me, me->preset_menu);
1128
1129 if (id_limit)
1130 *id_limit = me->n_encoded_presets;
1131 return me->preset_menu;
1132}
1133
1134int midend_which_preset(midend *me)
1135{
1136 char *encoding = me->ourgame->encode_params(me->params, TRUE);
1137 int i, ret;
1138
1139 ret = -1;
1140 for (i = 0; i < me->n_encoded_presets; i++)
1141 if (me->encoded_presets[i] &&
1142 !strcmp(encoding, me->encoded_presets[i])) {
1143 ret = i;
1144 break;
1145 }
1146
1147 sfree(encoding);
1148 return ret;
1149}
1150
1151int midend_wants_statusbar(midend *me)
1152{
1153 return me->ourgame->wants_statusbar;
1154}
1155
1156void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx)
1157{
1158 me->game_id_change_notify_function = notify;
1159 me->game_id_change_notify_ctx = ctx;
1160}
1161
1162void midend_supersede_game_desc(midend *me, char *desc, char *privdesc)
1163{
1164 sfree(me->desc);
1165 sfree(me->privdesc);
1166 me->desc = dupstr(desc);
1167 me->privdesc = privdesc ? dupstr(privdesc) : NULL;
1168 if (me->game_id_change_notify_function)
1169 me->game_id_change_notify_function(me->game_id_change_notify_ctx);
1170}
1171
1172config_item *midend_get_config(midend *me, int which, char **wintitle)
1173{
1174 char *titlebuf, *parstr, *rest;
1175 config_item *ret;
1176 char sep;
1177
1178 assert(wintitle);
1179 titlebuf = snewn(40 + strlen(me->ourgame->name), char);
1180
1181 switch (which) {
1182 case CFG_SETTINGS:
1183 sprintf(titlebuf, "%s configuration", me->ourgame->name);
1184 *wintitle = titlebuf;
1185 return me->ourgame->configure(me->params);
1186 case CFG_SEED:
1187 case CFG_DESC:
1188 if (!me->curparams) {
1189 sfree(titlebuf);
1190 return NULL;
1191 }
1192 sprintf(titlebuf, "%s %s selection", me->ourgame->name,
1193 which == CFG_SEED ? "random" : "game");
1194 *wintitle = titlebuf;
1195
1196 ret = snewn(2, config_item);
1197
1198 ret[0].type = C_STRING;
1199 if (which == CFG_SEED)
1200 ret[0].name = "Game random seed";
1201 else
1202 ret[0].name = "Game ID";
1203 ret[0].ival = 0;
1204 /*
1205 * For CFG_DESC the text going in here will be a string
1206 * encoding of the restricted parameters, plus a colon,
1207 * plus the game description. For CFG_SEED it will be the
1208 * full parameters, plus a hash, plus the random seed data.
1209 * Either of these is a valid full game ID (although only
1210 * the former is likely to persist across many code
1211 * changes).
1212 */
1213 parstr = me->ourgame->encode_params(me->curparams, which == CFG_SEED);
1214 assert(parstr);
1215 if (which == CFG_DESC) {
1216 rest = me->desc ? me->desc : "";
1217 sep = ':';
1218 } else {
1219 rest = me->seedstr ? me->seedstr : "";
1220 sep = '#';
1221 }
1222 ret[0].sval = snewn(strlen(parstr) + strlen(rest) + 2, char);
1223 sprintf(ret[0].sval, "%s%c%s", parstr, sep, rest);
1224 sfree(parstr);
1225
1226 ret[1].type = C_END;
1227 ret[1].name = ret[1].sval = NULL;
1228 ret[1].ival = 0;
1229
1230 return ret;
1231 }
1232
1233 assert(!"We shouldn't be here");
1234 return NULL;
1235}
1236
1237static char *midend_game_id_int(midend *me, char *id, int defmode)
1238{
1239 char *error, *par, *desc, *seed;
1240 game_params *newcurparams, *newparams, *oldparams1, *oldparams2;
1241 int free_params;
1242
1243 seed = strchr(id, '#');
1244 desc = strchr(id, ':');
1245
1246 if (desc && (!seed || desc < seed)) {
1247 /*
1248 * We have a colon separating parameters from game
1249 * description. So `par' now points to the parameters
1250 * string, and `desc' to the description string.
1251 */
1252 *desc++ = '\0';
1253 par = id;
1254 seed = NULL;
1255 } else if (seed && (!desc || seed < desc)) {
1256 /*
1257 * We have a hash separating parameters from random seed.
1258 * So `par' now points to the parameters string, and `seed'
1259 * to the seed string.
1260 */
1261 *seed++ = '\0';
1262 par = id;
1263 desc = NULL;
1264 } else {
1265 /*
1266 * We only have one string. Depending on `defmode', we take
1267 * it to be either parameters, seed or description.
1268 */
1269 if (defmode == DEF_SEED) {
1270 seed = id;
1271 par = desc = NULL;
1272 } else if (defmode == DEF_DESC) {
1273 desc = id;
1274 par = seed = NULL;
1275 } else {
1276 par = id;
1277 seed = desc = NULL;
1278 }
1279 }
1280
1281 /*
1282 * We must be reasonably careful here not to modify anything in
1283 * `me' until we have finished validating things. This function
1284 * must either return an error and do nothing to the midend, or
1285 * return success and do everything; nothing in between is
1286 * acceptable.
1287 */
1288 newcurparams = newparams = oldparams1 = oldparams2 = NULL;
1289
1290 if (par) {
1291 /*
1292 * The params string may underspecify the game parameters, so
1293 * we must first initialise newcurparams with a full set of
1294 * params from somewhere else before we decode_params the
1295 * input string over the top.
1296 *
1297 * But which set? It depends on what other data we have.
1298 *
1299 * If we've been given a _descriptive_ game id, then that may
1300 * well underspecify by design, e.g. Solo game descriptions
1301 * often start just '3x3:' without specifying one of Solo's
1302 * difficulty settings, because it isn't necessary once a game
1303 * has been generated (and you might not even know it, if
1304 * you're manually transcribing a game description). In that
1305 * situation, I've always felt that the best thing to set the
1306 * difficulty to (for use if the user hits 'New Game' after
1307 * pasting in that game id) is whatever it was previously set
1308 * to. That is, we use whatever is already in me->params as
1309 * the basis for our decoding of this input string.
1310 *
1311 * A random-seed based game id, however, should use the real,
1312 * built-in default params, and not even check the
1313 * <game>_DEFAULT environment setting, because when people
1314 * paste each other random seeds - whether it's two users
1315 * arranging to generate the same game at the same time to
1316 * race solving them, or a user sending a bug report upstream
1317 * - the whole point is for the random game id to always be
1318 * interpreted the same way, even if it does underspecify.
1319 *
1320 * A parameter string typed in on its own, with no seed _or_
1321 * description, gets treated the same way as a random seed,
1322 * because again I think the most likely reason for doing that
1323 * is to have a portable representation of a set of params.
1324 */
1325 if (desc) {
1326 newcurparams = me->ourgame->dup_params(me->params);
1327 } else {
1328 newcurparams = me->ourgame->default_params();
1329 }
1330 me->ourgame->decode_params(newcurparams, par);
1331 error = me->ourgame->validate_params(newcurparams, desc == NULL);
1332 if (error) {
1333 me->ourgame->free_params(newcurparams);
1334 return error;
1335 }
1336 oldparams1 = me->curparams;
1337
1338 /*
1339 * Now filter only the persistent parts of this state into
1340 * the long-term params structure, unless we've _only_
1341 * received a params string in which case the whole lot is
1342 * persistent.
1343 */
1344 oldparams2 = me->params;
1345 if (seed || desc) {
1346 char *tmpstr;
1347
1348 newparams = me->ourgame->dup_params(me->params);
1349
1350 tmpstr = me->ourgame->encode_params(newcurparams, FALSE);
1351 me->ourgame->decode_params(newparams, tmpstr);
1352
1353 sfree(tmpstr);
1354 } else {
1355 newparams = me->ourgame->dup_params(newcurparams);
1356 }
1357 free_params = TRUE;
1358 } else {
1359 newcurparams = me->curparams;
1360 newparams = me->params;
1361 free_params = FALSE;
1362 }
1363
1364 if (desc) {
1365 error = me->ourgame->validate_desc(newparams, desc);
1366 if (error) {
1367 if (free_params) {
1368 if (newcurparams)
1369 me->ourgame->free_params(newcurparams);
1370 if (newparams)
1371 me->ourgame->free_params(newparams);
1372 }
1373 return error;
1374 }
1375 }
1376
1377 /*
1378 * Now we've got past all possible error points. Update the
1379 * midend itself.
1380 */
1381 me->params = newparams;
1382 me->curparams = newcurparams;
1383 if (oldparams1)
1384 me->ourgame->free_params(oldparams1);
1385 if (oldparams2)
1386 me->ourgame->free_params(oldparams2);
1387
1388 sfree(me->desc);
1389 sfree(me->privdesc);
1390 me->desc = me->privdesc = NULL;
1391 sfree(me->seedstr);
1392 me->seedstr = NULL;
1393
1394 if (desc) {
1395 me->desc = dupstr(desc);
1396 me->genmode = GOT_DESC;
1397 sfree(me->aux_info);
1398 me->aux_info = NULL;
1399 }
1400
1401 if (seed) {
1402 me->seedstr = dupstr(seed);
1403 me->genmode = GOT_SEED;
1404 }
1405
1406 return NULL;
1407}
1408
1409char *midend_game_id(midend *me, char *id)
1410{
1411 return midend_game_id_int(me, id, DEF_PARAMS);
1412}
1413
1414char *midend_get_game_id(midend *me)
1415{
1416 char *parstr, *ret;
1417
1418 parstr = me->ourgame->encode_params(me->curparams, FALSE);
1419 assert(parstr);
1420 assert(me->desc);
1421 ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char);
1422 sprintf(ret, "%s:%s", parstr, me->desc);
1423 sfree(parstr);
1424 return ret;
1425}
1426
1427char *midend_get_random_seed(midend *me)
1428{
1429 char *parstr, *ret;
1430
1431 if (!me->seedstr)
1432 return NULL;
1433
1434 parstr = me->ourgame->encode_params(me->curparams, TRUE);
1435 assert(parstr);
1436 ret = snewn(strlen(parstr) + strlen(me->seedstr) + 2, char);
1437 sprintf(ret, "%s#%s", parstr, me->seedstr);
1438 sfree(parstr);
1439 return ret;
1440}
1441
1442char *midend_set_config(midend *me, int which, config_item *cfg)
1443{
1444 char *error;
1445 game_params *params;
1446
1447 switch (which) {
1448 case CFG_SETTINGS:
1449 params = me->ourgame->custom_params(cfg);
1450 error = me->ourgame->validate_params(params, TRUE);
1451
1452 if (error) {
1453 me->ourgame->free_params(params);
1454 return error;
1455 }
1456
1457 me->ourgame->free_params(me->params);
1458 me->params = params;
1459 break;
1460
1461 case CFG_SEED:
1462 case CFG_DESC:
1463 error = midend_game_id_int(me, cfg[0].sval,
1464 (which == CFG_SEED ? DEF_SEED : DEF_DESC));
1465 if (error)
1466 return error;
1467 break;
1468 }
1469
1470 return NULL;
1471}
1472
1473int midend_can_format_as_text_now(midend *me)
1474{
1475 if (me->ourgame->can_format_as_text_ever)
1476 return me->ourgame->can_format_as_text_now(me->params);
1477 else
1478 return FALSE;
1479}
1480
1481char *midend_text_format(midend *me)
1482{
1483 if (me->ourgame->can_format_as_text_ever && me->statepos > 0 &&
1484 me->ourgame->can_format_as_text_now(me->params))
1485 return me->ourgame->text_format(me->states[me->statepos-1].state);
1486 else
1487 return NULL;
1488}
1489
1490char *midend_solve(midend *me)
1491{
1492 game_state *s;
1493 char *msg, *movestr;
1494
1495 if (!me->ourgame->can_solve)
1496 return "This game does not support the Solve operation";
1497
1498 if (me->statepos < 1)
1499 return "No game set up to solve"; /* _shouldn't_ happen! */
1500
1501 msg = NULL;
1502 movestr = me->ourgame->solve(me->states[0].state,
1503 me->states[me->statepos-1].state,
1504 me->aux_info, &msg);
1505 if (!movestr) {
1506 if (!msg)
1507 msg = "Solve operation failed"; /* _shouldn't_ happen, but can */
1508 return msg;
1509 }
1510 s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr);
1511 assert(s);
1512
1513 /*
1514 * Now enter the solved state as the next move.
1515 */
1516 midend_stop_anim(me);
1517 midend_purge_states(me);
1518 ensure(me);
1519 me->states[me->nstates].state = s;
1520 me->states[me->nstates].movestr = movestr;
1521 me->states[me->nstates].movetype = SOLVE;
1522 me->statepos = ++me->nstates;
1523 if (me->ui)
1524 me->ourgame->changed_state(me->ui,
1525 me->states[me->statepos-2].state,
1526 me->states[me->statepos-1].state);
1527 me->dir = +1;
1528 if (me->ourgame->flags & SOLVE_ANIMATES) {
1529 me->oldstate = me->ourgame->dup_game(me->states[me->statepos-2].state);
1530 me->anim_time =
1531 me->ourgame->anim_length(me->states[me->statepos-2].state,
1532 me->states[me->statepos-1].state,
1533 +1, me->ui);
1534 me->anim_pos = 0.0;
1535 } else {
1536 me->anim_time = 0.0;
1537 midend_finish_move(me);
1538 }
1539 if (me->drawing)
1540 midend_redraw(me);
1541 midend_set_timer(me);
1542 return NULL;
1543}
1544
1545int midend_status(midend *me)
1546{
1547 /*
1548 * We should probably never be called when the state stack has no
1549 * states on it at all - ideally, midends should never be left in
1550 * that state for long enough to get put down and forgotten about.
1551 * But if we are, I think we return _true_ - pedantically speaking
1552 * a midend in that state is 'vacuously solved', and more
1553 * practically, a user whose midend has been left in that state
1554 * probably _does_ want the 'new game' option to be prominent.
1555 */
1556 if (me->statepos == 0)
1557 return +1;
1558
1559 return me->ourgame->status(me->states[me->statepos-1].state);
1560}
1561
1562char *midend_rewrite_statusbar(midend *me, char *text)
1563{
1564 /*
1565 * An important special case is that we are occasionally called
1566 * with our own laststatus, to update the timer.
1567 */
1568 if (me->laststatus != text) {
1569 sfree(me->laststatus);
1570 me->laststatus = dupstr(text);
1571 }
1572
1573 if (me->ourgame->is_timed) {
1574 char timebuf[100], *ret;
1575 int min, sec;
1576
1577 sec = (int)me->elapsed;
1578 min = sec / 60;
1579 sec %= 60;
1580 sprintf(timebuf, "[%d:%02d] ", min, sec);
1581
1582 ret = snewn(strlen(timebuf) + strlen(text) + 1, char);
1583 strcpy(ret, timebuf);
1584 strcat(ret, text);
1585 return ret;
1586
1587 } else {
1588 return dupstr(text);
1589 }
1590}
1591
1592#define SERIALISE_MAGIC "Simon Tatham's Portable Puzzle Collection"
1593#define SERIALISE_VERSION "1"
1594
1595void midend_serialise(midend *me,
1596 void (*write)(void *ctx, void *buf, int len),
1597 void *wctx)
1598{
1599 int i;
1600
1601 /*
1602 * Each line of the save file contains three components. First
1603 * exactly 8 characters of header word indicating what type of
1604 * data is contained on the line; then a colon followed by a
1605 * decimal integer giving the length of the main string on the
1606 * line; then a colon followed by the string itself (exactly as
1607 * many bytes as previously specified, no matter what they
1608 * contain). Then a newline (of reasonably flexible form).
1609 */
1610#define wr(h,s) do { \
1611 char hbuf[80]; \
1612 char *str = (s); \
1613 char lbuf[9]; \
1614 copy_left_justified(lbuf, sizeof(lbuf), h); \
1615 sprintf(hbuf, "%s:%d:", lbuf, (int)strlen(str)); \
1616 write(wctx, hbuf, strlen(hbuf)); \
1617 write(wctx, str, strlen(str)); \
1618 write(wctx, "\n", 1); \
1619} while (0)
1620
1621 /*
1622 * Magic string identifying the file, and version number of the
1623 * file format.
1624 */
1625 wr("SAVEFILE", SERIALISE_MAGIC);
1626 wr("VERSION", SERIALISE_VERSION);
1627
1628 /*
1629 * The game name. (Copied locally to avoid const annoyance.)
1630 */
1631 {
1632 char *s = dupstr(me->ourgame->name);
1633 wr("GAME", s);
1634 sfree(s);
1635 }
1636
1637 /*
1638 * The current long-term parameters structure, in full.
1639 */
1640 if (me->params) {
1641 char *s = me->ourgame->encode_params(me->params, TRUE);
1642 wr("PARAMS", s);
1643 sfree(s);
1644 }
1645
1646 /*
1647 * The current short-term parameters structure, in full.
1648 */
1649 if (me->curparams) {
1650 char *s = me->ourgame->encode_params(me->curparams, TRUE);
1651 wr("CPARAMS", s);
1652 sfree(s);
1653 }
1654
1655 /*
1656 * The current game description, the privdesc, and the random seed.
1657 */
1658 if (me->seedstr)
1659 wr("SEED", me->seedstr);
1660 if (me->desc)
1661 wr("DESC", me->desc);
1662 if (me->privdesc)
1663 wr("PRIVDESC", me->privdesc);
1664
1665 /*
1666 * The game's aux_info. We obfuscate this to prevent spoilers
1667 * (people are likely to run `head' or similar on a saved game
1668 * file simply to find out what it is, and don't necessarily
1669 * want to be told the answer to the puzzle!)
1670 */
1671 if (me->aux_info) {
1672 unsigned char *s1;
1673 char *s2;
1674 int len;
1675
1676 len = strlen(me->aux_info);
1677 s1 = snewn(len, unsigned char);
1678 memcpy(s1, me->aux_info, len);
1679 obfuscate_bitmap(s1, len*8, FALSE);
1680 s2 = bin2hex(s1, len);
1681
1682 wr("AUXINFO", s2);
1683
1684 sfree(s2);
1685 sfree(s1);
1686 }
1687
1688 /*
1689 * Any required serialisation of the game_ui.
1690 */
1691 if (me->ui) {
1692 char *s = me->ourgame->encode_ui(me->ui);
1693 if (s) {
1694 wr("UI", s);
1695 sfree(s);
1696 }
1697 }
1698
1699 /*
1700 * The game time, if it's a timed game.
1701 */
1702 if (me->ourgame->is_timed) {
1703 char buf[80];
1704 sprintf(buf, "%g", me->elapsed);
1705 wr("TIME", buf);
1706 }
1707
1708 /*
1709 * The length of, and position in, the states list.
1710 */
1711 {
1712 char buf[80];
1713 sprintf(buf, "%d", me->nstates);
1714 wr("NSTATES", buf);
1715 sprintf(buf, "%d", me->statepos);
1716 wr("STATEPOS", buf);
1717 }
1718
1719 /*
1720 * For each state after the initial one (which we know is
1721 * constructed from either privdesc or desc), enough
1722 * information for execute_move() to reconstruct it from the
1723 * previous one.
1724 */
1725 for (i = 1; i < me->nstates; i++) {
1726 assert(me->states[i].movetype != NEWGAME); /* only state 0 */
1727 switch (me->states[i].movetype) {
1728 case MOVE:
1729 wr("MOVE", me->states[i].movestr);
1730 break;
1731 case SOLVE:
1732 wr("SOLVE", me->states[i].movestr);
1733 break;
1734 case RESTART:
1735 wr("RESTART", me->states[i].movestr);
1736 break;
1737 }
1738 }
1739
1740#undef wr
1741}
1742
1743/*
1744 * This function returns NULL on success, or an error message.
1745 */
1746char *midend_deserialise(midend *me,
1747 int (*read)(void *ctx, void *buf, int len),
1748 void *rctx)
1749{
1750 int nstates = 0, statepos = -1, gotstates = 0;
1751 int started = FALSE;
1752 int i;
1753
1754 char *val = NULL;
1755 /* Initially all errors give the same report */
1756 char *ret = "Data does not appear to be a saved game file";
1757
1758 /*
1759 * We construct all the new state in local variables while we
1760 * check its sanity. Only once we have finished reading the
1761 * serialised data and detected no errors at all do we start
1762 * modifying stuff in the midend passed in.
1763 */
1764 char *seed = NULL, *parstr = NULL, *desc = NULL, *privdesc = NULL;
1765 char *auxinfo = NULL, *uistr = NULL, *cparstr = NULL;
1766 float elapsed = 0.0F;
1767 game_params *params = NULL, *cparams = NULL;
1768 game_ui *ui = NULL;
1769 struct midend_state_entry *states = NULL;
1770
1771 /*
1772 * Loop round and round reading one key/value pair at a time
1773 * from the serialised stream, until we have enough game states
1774 * to finish.
1775 */
1776 while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) {
1777 char key[9], c;
1778 int len;
1779
1780 do {
1781 if (!read(rctx, key, 1)) {
1782 /* unexpected EOF */
1783 goto cleanup;
1784 }
1785 } while (key[0] == '\r' || key[0] == '\n');
1786
1787 if (!read(rctx, key+1, 8)) {
1788 /* unexpected EOF */
1789 goto cleanup;
1790 }
1791
1792 if (key[8] != ':') {
1793 if (started)
1794 ret = "Data was incorrectly formatted for a saved game file";
1795 goto cleanup;
1796 }
1797 len = strcspn(key, ": ");
1798 assert(len <= 8);
1799 key[len] = '\0';
1800
1801 len = 0;
1802 while (1) {
1803 if (!read(rctx, &c, 1)) {
1804 /* unexpected EOF */
1805 goto cleanup;
1806 }
1807
1808 if (c == ':') {
1809 break;
1810 } else if (c >= '0' && c <= '9') {
1811 len = (len * 10) + (c - '0');
1812 } else {
1813 if (started)
1814 ret = "Data was incorrectly formatted for a"
1815 " saved game file";
1816 goto cleanup;
1817 }
1818 }
1819
1820 val = snewn(len+1, char);
1821 if (!read(rctx, val, len)) {
1822 if (started)
1823 goto cleanup;
1824 }
1825 val[len] = '\0';
1826
1827 if (!started) {
1828 if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
1829 /* ret already has the right message in it */
1830 goto cleanup;
1831 }
1832 /* Now most errors are this one, unless otherwise specified */
1833 ret = "Saved data ended unexpectedly";
1834 started = TRUE;
1835 } else {
1836 if (!strcmp(key, "VERSION")) {
1837 if (strcmp(val, SERIALISE_VERSION)) {
1838 ret = "Cannot handle this version of the saved game"
1839 " file format";
1840 goto cleanup;
1841 }
1842 } else if (!strcmp(key, "GAME")) {
1843 if (strcmp(val, me->ourgame->name)) {
1844 ret = "Save file is from a different game";
1845 goto cleanup;
1846 }
1847 } else if (!strcmp(key, "PARAMS")) {
1848 sfree(parstr);
1849 parstr = val;
1850 val = NULL;
1851 } else if (!strcmp(key, "CPARAMS")) {
1852 sfree(cparstr);
1853 cparstr = val;
1854 val = NULL;
1855 } else if (!strcmp(key, "SEED")) {
1856 sfree(seed);
1857 seed = val;
1858 val = NULL;
1859 } else if (!strcmp(key, "DESC")) {
1860 sfree(desc);
1861 desc = val;
1862 val = NULL;
1863 } else if (!strcmp(key, "PRIVDESC")) {
1864 sfree(privdesc);
1865 privdesc = val;
1866 val = NULL;
1867 } else if (!strcmp(key, "AUXINFO")) {
1868 unsigned char *tmp;
1869 int len = strlen(val) / 2; /* length in bytes */
1870 tmp = hex2bin(val, len);
1871 obfuscate_bitmap(tmp, len*8, TRUE);
1872
1873 sfree(auxinfo);
1874 auxinfo = snewn(len + 1, char);
1875 memcpy(auxinfo, tmp, len);
1876 auxinfo[len] = '\0';
1877 sfree(tmp);
1878 } else if (!strcmp(key, "UI")) {
1879 sfree(uistr);
1880 uistr = val;
1881 val = NULL;
1882 } else if (!strcmp(key, "TIME")) {
1883 elapsed = (float)atof(val);
1884 } else if (!strcmp(key, "NSTATES")) {
1885 nstates = atoi(val);
1886 if (nstates <= 0) {
1887 ret = "Number of states in save file was negative";
1888 goto cleanup;
1889 }
1890 if (states) {
1891 ret = "Two state counts provided in save file";
1892 goto cleanup;
1893 }
1894 states = snewn(nstates, struct midend_state_entry);
1895 for (i = 0; i < nstates; i++) {
1896 states[i].state = NULL;
1897 states[i].movestr = NULL;
1898 states[i].movetype = NEWGAME;
1899 }
1900 } else if (!strcmp(key, "STATEPOS")) {
1901 statepos = atoi(val);
1902 } else if (!strcmp(key, "MOVE")) {
1903 gotstates++;
1904 states[gotstates].movetype = MOVE;
1905 states[gotstates].movestr = val;
1906 val = NULL;
1907 } else if (!strcmp(key, "SOLVE")) {
1908 gotstates++;
1909 states[gotstates].movetype = SOLVE;
1910 states[gotstates].movestr = val;
1911 val = NULL;
1912 } else if (!strcmp(key, "RESTART")) {
1913 gotstates++;
1914 states[gotstates].movetype = RESTART;
1915 states[gotstates].movestr = val;
1916 val = NULL;
1917 }
1918 }
1919
1920 sfree(val);
1921 val = NULL;
1922 }
1923
1924 params = me->ourgame->default_params();
1925 me->ourgame->decode_params(params, parstr);
1926 if (me->ourgame->validate_params(params, TRUE)) {
1927 ret = "Long-term parameters in save file are invalid";
1928 goto cleanup;
1929 }
1930 cparams = me->ourgame->default_params();
1931 me->ourgame->decode_params(cparams, cparstr);
1932 if (me->ourgame->validate_params(cparams, FALSE)) {
1933 ret = "Short-term parameters in save file are invalid";
1934 goto cleanup;
1935 }
1936 if (seed && me->ourgame->validate_params(cparams, TRUE)) {
1937 /*
1938 * The seed's no use with this version, but we can perfectly
1939 * well use the rest of the data.
1940 */
1941 sfree(seed);
1942 seed = NULL;
1943 }
1944 if (!desc) {
1945 ret = "Game description in save file is missing";
1946 goto cleanup;
1947 } else if (me->ourgame->validate_desc(params, desc)) {
1948 ret = "Game description in save file is invalid";
1949 goto cleanup;
1950 }
1951 if (privdesc && me->ourgame->validate_desc(params, privdesc)) {
1952 ret = "Game private description in save file is invalid";
1953 goto cleanup;
1954 }
1955 if (statepos < 0 || statepos >= nstates) {
1956 ret = "Game position in save file is out of range";
1957 }
1958
1959 states[0].state = me->ourgame->new_game(me, params,
1960 privdesc ? privdesc : desc);
1961 for (i = 1; i < nstates; i++) {
1962 assert(states[i].movetype != NEWGAME);
1963 switch (states[i].movetype) {
1964 case MOVE:
1965 case SOLVE:
1966 states[i].state = me->ourgame->execute_move(states[i-1].state,
1967 states[i].movestr);
1968 if (states[i].state == NULL) {
1969 ret = "Save file contained an invalid move";
1970 goto cleanup;
1971 }
1972 break;
1973 case RESTART:
1974 if (me->ourgame->validate_desc(params, states[i].movestr)) {
1975 ret = "Save file contained an invalid restart move";
1976 goto cleanup;
1977 }
1978 states[i].state = me->ourgame->new_game(me, params,
1979 states[i].movestr);
1980 break;
1981 }
1982 }
1983
1984 ui = me->ourgame->new_ui(states[0].state);
1985 me->ourgame->decode_ui(ui, uistr);
1986
1987 /*
1988 * Now we've run out of possible error conditions, so we're
1989 * ready to start overwriting the real data in the current
1990 * midend. We'll do this by swapping things with the local
1991 * variables, so that the same cleanup code will free the old
1992 * stuff.
1993 */
1994 {
1995 char *tmp;
1996
1997 tmp = me->desc;
1998 me->desc = desc;
1999 desc = tmp;
2000
2001 tmp = me->privdesc;
2002 me->privdesc = privdesc;
2003 privdesc = tmp;
2004
2005 tmp = me->seedstr;
2006 me->seedstr = seed;
2007 seed = tmp;
2008
2009 tmp = me->aux_info;
2010 me->aux_info = auxinfo;
2011 auxinfo = tmp;
2012 }
2013
2014 me->genmode = GOT_NOTHING;
2015
2016 me->statesize = nstates;
2017 nstates = me->nstates;
2018 me->nstates = me->statesize;
2019 {
2020 struct midend_state_entry *tmp;
2021 tmp = me->states;
2022 me->states = states;
2023 states = tmp;
2024 }
2025 me->statepos = statepos;
2026
2027 {
2028 game_params *tmp;
2029
2030 tmp = me->params;
2031 me->params = params;
2032 params = tmp;
2033
2034 tmp = me->curparams;
2035 me->curparams = cparams;
2036 cparams = tmp;
2037 }
2038
2039 me->oldstate = NULL;
2040 me->anim_time = me->anim_pos = me->flash_time = me->flash_pos = 0.0F;
2041 me->dir = 0;
2042
2043 {
2044 game_ui *tmp;
2045
2046 tmp = me->ui;
2047 me->ui = ui;
2048 ui = tmp;
2049 }
2050
2051 me->elapsed = elapsed;
2052 me->pressed_mouse_button = 0;
2053
2054 midend_set_timer(me);
2055
2056 if (me->drawstate)
2057 me->ourgame->free_drawstate(me->drawing, me->drawstate);
2058 me->drawstate =
2059 me->ourgame->new_drawstate(me->drawing,
2060 me->states[me->statepos-1].state);
2061 midend_size_new_drawstate(me);
2062
2063 ret = NULL; /* success! */
2064
2065 cleanup:
2066 sfree(val);
2067 sfree(seed);
2068 sfree(parstr);
2069 sfree(cparstr);
2070 sfree(desc);
2071 sfree(privdesc);
2072 sfree(auxinfo);
2073 sfree(uistr);
2074 if (params)
2075 me->ourgame->free_params(params);
2076 if (cparams)
2077 me->ourgame->free_params(cparams);
2078 if (ui)
2079 me->ourgame->free_ui(ui);
2080 if (states) {
2081 int i;
2082
2083 for (i = 0; i < nstates; i++) {
2084 if (states[i].state)
2085 me->ourgame->free_game(states[i].state);
2086 sfree(states[i].movestr);
2087 }
2088 sfree(states);
2089 }
2090
2091 return ret;
2092}
2093
2094/*
2095 * This function examines a saved game file just far enough to
2096 * determine which game type it contains. It returns NULL on success
2097 * and the game name string in 'name' (which will be dynamically
2098 * allocated and should be caller-freed), or an error message on
2099 * failure.
2100 */
2101char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len),
2102 void *rctx)
2103{
2104 int nstates = 0, statepos = -1, gotstates = 0;
2105 int started = FALSE;
2106
2107 char *val = NULL;
2108 /* Initially all errors give the same report */
2109 char *ret = "Data does not appear to be a saved game file";
2110
2111 *name = NULL;
2112
2113 /*
2114 * Loop round and round reading one key/value pair at a time from
2115 * the serialised stream, until we've found the game name.
2116 */
2117 while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) {
2118 char key[9], c;
2119 int len;
2120
2121 do {
2122 if (!read(rctx, key, 1)) {
2123 /* unexpected EOF */
2124 goto cleanup;
2125 }
2126 } while (key[0] == '\r' || key[0] == '\n');
2127
2128 if (!read(rctx, key+1, 8)) {
2129 /* unexpected EOF */
2130 goto cleanup;
2131 }
2132
2133 if (key[8] != ':') {
2134 if (started)
2135 ret = "Data was incorrectly formatted for a saved game file";
2136 goto cleanup;
2137 }
2138 len = strcspn(key, ": ");
2139 assert(len <= 8);
2140 key[len] = '\0';
2141
2142 len = 0;
2143 while (1) {
2144 if (!read(rctx, &c, 1)) {
2145 /* unexpected EOF */
2146 goto cleanup;
2147 }
2148
2149 if (c == ':') {
2150 break;
2151 } else if (c >= '0' && c <= '9') {
2152 len = (len * 10) + (c - '0');
2153 } else {
2154 if (started)
2155 ret = "Data was incorrectly formatted for a"
2156 " saved game file";
2157 goto cleanup;
2158 }
2159 }
2160
2161 val = snewn(len+1, char);
2162 if (!read(rctx, val, len)) {
2163 if (started)
2164 goto cleanup;
2165 }
2166 val[len] = '\0';
2167
2168 if (!started) {
2169 if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
2170 /* ret already has the right message in it */
2171 goto cleanup;
2172 }
2173 /* Now most errors are this one, unless otherwise specified */
2174 ret = "Saved data ended unexpectedly";
2175 started = TRUE;
2176 } else {
2177 if (!strcmp(key, "VERSION")) {
2178 if (strcmp(val, SERIALISE_VERSION)) {
2179 ret = "Cannot handle this version of the saved game"
2180 " file format";
2181 goto cleanup;
2182 }
2183 } else if (!strcmp(key, "GAME")) {
2184 *name = dupstr(val);
2185 ret = NULL;
2186 goto cleanup;
2187 }
2188 }
2189
2190 sfree(val);
2191 val = NULL;
2192 }
2193
2194 cleanup:
2195 sfree(val);
2196 return ret;
2197}
2198
2199char *midend_print_puzzle(midend *me, document *doc, int with_soln)
2200{
2201 game_state *soln = NULL;
2202
2203 if (me->statepos < 1)
2204 return "No game set up to print";/* _shouldn't_ happen! */
2205
2206 if (with_soln) {
2207 char *msg, *movestr;
2208
2209 if (!me->ourgame->can_solve)
2210 return "This game does not support the Solve operation";
2211
2212 msg = "Solve operation failed";/* game _should_ overwrite on error */
2213 movestr = me->ourgame->solve(me->states[0].state,
2214 me->states[me->statepos-1].state,
2215 me->aux_info, &msg);
2216 if (!movestr)
2217 return msg;
2218 soln = me->ourgame->execute_move(me->states[me->statepos-1].state,
2219 movestr);
2220 assert(soln);
2221
2222 sfree(movestr);
2223 } else
2224 soln = NULL;
2225
2226 /*
2227 * This call passes over ownership of the two game_states and
2228 * the game_params. Hence we duplicate the ones we want to
2229 * keep, and we don't have to bother freeing soln if it was
2230 * non-NULL.
2231 */
2232 document_add_puzzle(doc, me->ourgame,
2233 me->ourgame->dup_params(me->curparams),
2234 me->ourgame->dup_game(me->states[0].state), soln);
2235
2236 return NULL;
2237}
diff --git a/apps/plugins/puzzles/src/mines.R b/apps/plugins/puzzles/src/mines.R
new file mode 100644
index 0000000000..275c76c7ad
--- /dev/null
+++ b/apps/plugins/puzzles/src/mines.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3MINES_EXTRA = tree234
4
5mines : [X] GTK COMMON mines MINES_EXTRA mines-icon|no-icon
6
7mines : [G] WINDOWS COMMON mines MINES_EXTRA mines.res|noicon.res
8
9mineobfusc : [U] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE
10mineobfusc : [C] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE
11
12ALL += mines[COMBINED] MINES_EXTRA
13
14!begin am gtk
15GAMES += mines
16!end
17
18!begin >list.c
19 A(mines) \
20!end
21
22!begin >gamedesc.txt
23mines:mines.exe:Mines:Mine-finding puzzle:Find all the mines without treading on any of them.
24!end
diff --git a/apps/plugins/puzzles/src/mines.c b/apps/plugins/puzzles/src/mines.c
new file mode 100644
index 0000000000..6a5ce029a0
--- /dev/null
+++ b/apps/plugins/puzzles/src/mines.c
@@ -0,0 +1,3250 @@
1/*
2 * mines.c: Minesweeper clone with sophisticated grid generation.
3 *
4 * Still TODO:
5 *
6 * - think about configurably supporting question marks. Once,
7 * that is, we've thought about configurability in general!
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <assert.h>
14#include <ctype.h>
15#include <math.h>
16
17#include "tree234.h"
18#include "puzzles.h"
19
20enum {
21 COL_BACKGROUND, COL_BACKGROUND2,
22 COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8,
23 COL_MINE, COL_BANG, COL_CROSS, COL_FLAG, COL_FLAGBASE, COL_QUERY,
24 COL_HIGHLIGHT, COL_LOWLIGHT,
25 COL_WRONGNUMBER,
26 COL_CURSOR,
27 NCOLOURS
28};
29
30#define PREFERRED_TILE_SIZE 20
31#define TILE_SIZE (ds->tilesize)
32#ifdef SMALL_SCREEN
33#define BORDER 8
34#else
35#define BORDER (TILE_SIZE * 3 / 2)
36#endif
37#define HIGHLIGHT_WIDTH (TILE_SIZE / 10)
38#define OUTER_HIGHLIGHT_WIDTH (BORDER / 10)
39#define COORD(x) ( (x) * TILE_SIZE + BORDER )
40#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
41
42#define FLASH_FRAME 0.13F
43
44struct game_params {
45 int w, h, n;
46 int unique;
47};
48
49struct mine_layout {
50 /*
51 * This structure is shared between all the game_states for a
52 * given instance of the puzzle, so we reference-count it.
53 */
54 int refcount;
55 char *mines;
56 /*
57 * If we haven't yet actually generated the mine layout, here's
58 * all the data we will need to do so.
59 */
60 int n, unique;
61 random_state *rs;
62 midend *me; /* to give back the new game desc */
63};
64
65struct game_state {
66 int w, h, n, dead, won;
67 int used_solve;
68 struct mine_layout *layout; /* real mine positions */
69 signed char *grid; /* player knowledge */
70 /*
71 * Each item in the `grid' array is one of the following values:
72 *
73 * - 0 to 8 mean the square is open and has a surrounding mine
74 * count.
75 *
76 * - -1 means the square is marked as a mine.
77 *
78 * - -2 means the square is unknown.
79 *
80 * - -3 means the square is marked with a question mark
81 * (FIXME: do we even want to bother with this?).
82 *
83 * - 64 means the square has had a mine revealed when the game
84 * was lost.
85 *
86 * - 65 means the square had a mine revealed and this was the
87 * one the player hits.
88 *
89 * - 66 means the square has a crossed-out mine because the
90 * player had incorrectly marked it.
91 */
92};
93
94static game_params *default_params(void)
95{
96 game_params *ret = snew(game_params);
97
98 ret->w = ret->h = 9;
99 ret->n = 10;
100 ret->unique = TRUE;
101
102 return ret;
103}
104
105static const struct game_params mines_presets[] = {
106 {9, 9, 10, TRUE},
107 {9, 9, 35, TRUE},
108 {16, 16, 40, TRUE},
109 {16, 16, 99, TRUE},
110#ifndef SMALL_SCREEN
111 {30, 16, 99, TRUE},
112 {30, 16, 170, TRUE},
113#endif
114};
115
116static int game_fetch_preset(int i, char **name, game_params **params)
117{
118 game_params *ret;
119 char str[80];
120
121 if (i < 0 || i >= lenof(mines_presets))
122 return FALSE;
123
124 ret = snew(game_params);
125 *ret = mines_presets[i];
126
127 sprintf(str, "%dx%d, %d mines", ret->w, ret->h, ret->n);
128
129 *name = dupstr(str);
130 *params = ret;
131 return TRUE;
132}
133
134static void free_params(game_params *params)
135{
136 sfree(params);
137}
138
139static game_params *dup_params(const game_params *params)
140{
141 game_params *ret = snew(game_params);
142 *ret = *params; /* structure copy */
143 return ret;
144}
145
146static void decode_params(game_params *params, char const *string)
147{
148 char const *p = string;
149
150 params->w = atoi(p);
151 while (*p && isdigit((unsigned char)*p)) p++;
152 if (*p == 'x') {
153 p++;
154 params->h = atoi(p);
155 while (*p && isdigit((unsigned char)*p)) p++;
156 } else {
157 params->h = params->w;
158 }
159 if (*p == 'n') {
160 p++;
161 params->n = atoi(p);
162 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
163 } else {
164 params->n = params->w * params->h / 10;
165 }
166
167 while (*p) {
168 if (*p == 'a') {
169 p++;
170 params->unique = FALSE;
171 } else
172 p++; /* skip any other gunk */
173 }
174}
175
176static char *encode_params(const game_params *params, int full)
177{
178 char ret[400];
179 int len;
180
181 len = sprintf(ret, "%dx%d", params->w, params->h);
182 /*
183 * Mine count is a generation-time parameter, since it can be
184 * deduced from the mine bitmap!
185 */
186 if (full)
187 len += sprintf(ret+len, "n%d", params->n);
188 if (full && !params->unique)
189 ret[len++] = 'a';
190 assert(len < lenof(ret));
191 ret[len] = '\0';
192
193 return dupstr(ret);
194}
195
196static config_item *game_configure(const game_params *params)
197{
198 config_item *ret;
199 char buf[80];
200
201 ret = snewn(5, config_item);
202
203 ret[0].name = "Width";
204 ret[0].type = C_STRING;
205 sprintf(buf, "%d", params->w);
206 ret[0].sval = dupstr(buf);
207 ret[0].ival = 0;
208
209 ret[1].name = "Height";
210 ret[1].type = C_STRING;
211 sprintf(buf, "%d", params->h);
212 ret[1].sval = dupstr(buf);
213 ret[1].ival = 0;
214
215 ret[2].name = "Mines";
216 ret[2].type = C_STRING;
217 sprintf(buf, "%d", params->n);
218 ret[2].sval = dupstr(buf);
219 ret[2].ival = 0;
220
221 ret[3].name = "Ensure solubility";
222 ret[3].type = C_BOOLEAN;
223 ret[3].sval = NULL;
224 ret[3].ival = params->unique;
225
226 ret[4].name = NULL;
227 ret[4].type = C_END;
228 ret[4].sval = NULL;
229 ret[4].ival = 0;
230
231 return ret;
232}
233
234static game_params *custom_params(const config_item *cfg)
235{
236 game_params *ret = snew(game_params);
237
238 ret->w = atoi(cfg[0].sval);
239 ret->h = atoi(cfg[1].sval);
240 ret->n = atoi(cfg[2].sval);
241 if (strchr(cfg[2].sval, '%'))
242 ret->n = ret->n * (ret->w * ret->h) / 100;
243 ret->unique = cfg[3].ival;
244
245 return ret;
246}
247
248static char *validate_params(const game_params *params, int full)
249{
250 /*
251 * Lower limit on grid size: each dimension must be at least 3.
252 * 1 is theoretically workable if rather boring, but 2 is a
253 * real problem: there is often _no_ way to generate a uniquely
254 * solvable 2xn Mines grid. You either run into two mines
255 * blocking the way and no idea what's behind them, or one mine
256 * and no way to know which of the two rows it's in. If the
257 * mine count is even you can create a soluble grid by packing
258 * all the mines at one end (so what when you hit a two-mine
259 * wall there are only as many covered squares left as there
260 * are mines); but if it's odd, you are doomed, because you
261 * _have_ to have a gap somewhere which you can't determine the
262 * position of.
263 */
264 if (full && params->unique && (params->w <= 2 || params->h <= 2))
265 return "Width and height must both be greater than two";
266 if (params->n > params->w * params->h - 9)
267 return "Too many mines for grid size";
268
269 /*
270 * FIXME: Need more constraints here. Not sure what the
271 * sensible limits for Minesweeper actually are. The limits
272 * probably ought to change, however, depending on uniqueness.
273 */
274
275 return NULL;
276}
277
278/* ----------------------------------------------------------------------
279 * Minesweeper solver, used to ensure the generated grids are
280 * solvable without having to take risks.
281 */
282
283/*
284 * Count the bits in a word. Only needs to cope with 16 bits.
285 */
286static int bitcount16(int inword)
287{
288 unsigned int word = inword;
289
290 word = ((word & 0xAAAA) >> 1) + (word & 0x5555);
291 word = ((word & 0xCCCC) >> 2) + (word & 0x3333);
292 word = ((word & 0xF0F0) >> 4) + (word & 0x0F0F);
293 word = ((word & 0xFF00) >> 8) + (word & 0x00FF);
294
295 return (int)word;
296}
297
298/*
299 * We use a tree234 to store a large number of small localised
300 * sets, each with a mine count. We also keep some of those sets
301 * linked together into a to-do list.
302 */
303struct set {
304 short x, y, mask, mines;
305 int todo;
306 struct set *prev, *next;
307};
308
309static int setcmp(void *av, void *bv)
310{
311 struct set *a = (struct set *)av;
312 struct set *b = (struct set *)bv;
313
314 if (a->y < b->y)
315 return -1;
316 else if (a->y > b->y)
317 return +1;
318 else if (a->x < b->x)
319 return -1;
320 else if (a->x > b->x)
321 return +1;
322 else if (a->mask < b->mask)
323 return -1;
324 else if (a->mask > b->mask)
325 return +1;
326 else
327 return 0;
328}
329
330struct setstore {
331 tree234 *sets;
332 struct set *todo_head, *todo_tail;
333};
334
335static struct setstore *ss_new(void)
336{
337 struct setstore *ss = snew(struct setstore);
338 ss->sets = newtree234(setcmp);
339 ss->todo_head = ss->todo_tail = NULL;
340 return ss;
341}
342
343/*
344 * Take two input sets, in the form (x,y,mask). Munge the first by
345 * taking either its intersection with the second or its difference
346 * with the second. Return the new mask part of the first set.
347 */
348static int setmunge(int x1, int y1, int mask1, int x2, int y2, int mask2,
349 int diff)
350{
351 /*
352 * Adjust the second set so that it has the same x,y
353 * coordinates as the first.
354 */
355 if (abs(x2-x1) >= 3 || abs(y2-y1) >= 3) {
356 mask2 = 0;
357 } else {
358 while (x2 > x1) {
359 mask2 &= ~(4|32|256);
360 mask2 <<= 1;
361 x2--;
362 }
363 while (x2 < x1) {
364 mask2 &= ~(1|8|64);
365 mask2 >>= 1;
366 x2++;
367 }
368 while (y2 > y1) {
369 mask2 &= ~(64|128|256);
370 mask2 <<= 3;
371 y2--;
372 }
373 while (y2 < y1) {
374 mask2 &= ~(1|2|4);
375 mask2 >>= 3;
376 y2++;
377 }
378 }
379
380 /*
381 * Invert the second set if `diff' is set (we're after A &~ B
382 * rather than A & B).
383 */
384 if (diff)
385 mask2 ^= 511;
386
387 /*
388 * Now all that's left is a logical AND.
389 */
390 return mask1 & mask2;
391}
392
393static void ss_add_todo(struct setstore *ss, struct set *s)
394{
395 if (s->todo)
396 return; /* already on it */
397
398#ifdef SOLVER_DIAGNOSTICS
399 printf("adding set on todo list: %d,%d %03x %d\n",
400 s->x, s->y, s->mask, s->mines);
401#endif
402
403 s->prev = ss->todo_tail;
404 if (s->prev)
405 s->prev->next = s;
406 else
407 ss->todo_head = s;
408 ss->todo_tail = s;
409 s->next = NULL;
410 s->todo = TRUE;
411}
412
413static void ss_add(struct setstore *ss, int x, int y, int mask, int mines)
414{
415 struct set *s;
416
417 assert(mask != 0);
418
419 /*
420 * Normalise so that x and y are genuinely the bounding
421 * rectangle.
422 */
423 while (!(mask & (1|8|64)))
424 mask >>= 1, x++;
425 while (!(mask & (1|2|4)))
426 mask >>= 3, y++;
427
428 /*
429 * Create a set structure and add it to the tree.
430 */
431 s = snew(struct set);
432 s->x = x;
433 s->y = y;
434 s->mask = mask;
435 s->mines = mines;
436 s->todo = FALSE;
437 if (add234(ss->sets, s) != s) {
438 /*
439 * This set already existed! Free it and return.
440 */
441 sfree(s);
442 return;
443 }
444
445 /*
446 * We've added a new set to the tree, so put it on the todo
447 * list.
448 */
449 ss_add_todo(ss, s);
450}
451
452static void ss_remove(struct setstore *ss, struct set *s)
453{
454 struct set *next = s->next, *prev = s->prev;
455
456#ifdef SOLVER_DIAGNOSTICS
457 printf("removing set %d,%d %03x\n", s->x, s->y, s->mask);
458#endif
459 /*
460 * Remove s from the todo list.
461 */
462 if (prev)
463 prev->next = next;
464 else if (s == ss->todo_head)
465 ss->todo_head = next;
466
467 if (next)
468 next->prev = prev;
469 else if (s == ss->todo_tail)
470 ss->todo_tail = prev;
471
472 s->todo = FALSE;
473
474 /*
475 * Remove s from the tree.
476 */
477 del234(ss->sets, s);
478
479 /*
480 * Destroy the actual set structure.
481 */
482 sfree(s);
483}
484
485/*
486 * Return a dynamically allocated list of all the sets which
487 * overlap a provided input set.
488 */
489static struct set **ss_overlap(struct setstore *ss, int x, int y, int mask)
490{
491 struct set **ret = NULL;
492 int nret = 0, retsize = 0;
493 int xx, yy;
494
495 for (xx = x-3; xx < x+3; xx++)
496 for (yy = y-3; yy < y+3; yy++) {
497 struct set stmp, *s;
498 int pos;
499
500 /*
501 * Find the first set with these top left coordinates.
502 */
503 stmp.x = xx;
504 stmp.y = yy;
505 stmp.mask = 0;
506
507 if (findrelpos234(ss->sets, &stmp, NULL, REL234_GE, &pos)) {
508 while ((s = index234(ss->sets, pos)) != NULL &&
509 s->x == xx && s->y == yy) {
510 /*
511 * This set potentially overlaps the input one.
512 * Compute the intersection to see if they
513 * really overlap, and add it to the list if
514 * so.
515 */
516 if (setmunge(x, y, mask, s->x, s->y, s->mask, FALSE)) {
517 /*
518 * There's an overlap.
519 */
520 if (nret >= retsize) {
521 retsize = nret + 32;
522 ret = sresize(ret, retsize, struct set *);
523 }
524 ret[nret++] = s;
525 }
526
527 pos++;
528 }
529 }
530 }
531
532 ret = sresize(ret, nret+1, struct set *);
533 ret[nret] = NULL;
534
535 return ret;
536}
537
538/*
539 * Get an element from the head of the set todo list.
540 */
541static struct set *ss_todo(struct setstore *ss)
542{
543 if (ss->todo_head) {
544 struct set *ret = ss->todo_head;
545 ss->todo_head = ret->next;
546 if (ss->todo_head)
547 ss->todo_head->prev = NULL;
548 else
549 ss->todo_tail = NULL;
550 ret->next = ret->prev = NULL;
551 ret->todo = FALSE;
552 return ret;
553 } else {
554 return NULL;
555 }
556}
557
558struct squaretodo {
559 int *next;
560 int head, tail;
561};
562
563static void std_add(struct squaretodo *std, int i)
564{
565 if (std->tail >= 0)
566 std->next[std->tail] = i;
567 else
568 std->head = i;
569 std->tail = i;
570 std->next[i] = -1;
571}
572
573typedef int (*open_cb)(void *, int, int);
574
575static void known_squares(int w, int h, struct squaretodo *std,
576 signed char *grid,
577 open_cb open, void *openctx,
578 int x, int y, int mask, int mine)
579{
580 int xx, yy, bit;
581
582 bit = 1;
583
584 for (yy = 0; yy < 3; yy++)
585 for (xx = 0; xx < 3; xx++) {
586 if (mask & bit) {
587 int i = (y + yy) * w + (x + xx);
588
589 /*
590 * It's possible that this square is _already_
591 * known, in which case we don't try to add it to
592 * the list twice.
593 */
594 if (grid[i] == -2) {
595
596 if (mine) {
597 grid[i] = -1; /* and don't open it! */
598 } else {
599 grid[i] = open(openctx, x + xx, y + yy);
600 assert(grid[i] != -1); /* *bang* */
601 }
602 std_add(std, i);
603
604 }
605 }
606 bit <<= 1;
607 }
608}
609
610/*
611 * This is data returned from the `perturb' function. It details
612 * which squares have become mines and which have become clear. The
613 * solver is (of course) expected to honourably not use that
614 * knowledge directly, but to efficently adjust its internal data
615 * structures and proceed based on only the information it
616 * legitimately has.
617 */
618struct perturbation {
619 int x, y;
620 int delta; /* +1 == become a mine; -1 == cleared */
621};
622struct perturbations {
623 int n;
624 struct perturbation *changes;
625};
626
627/*
628 * Main solver entry point. You give it a grid of existing
629 * knowledge (-1 for a square known to be a mine, 0-8 for empty
630 * squares with a given number of neighbours, -2 for completely
631 * unknown), plus a function which you can call to open new squares
632 * once you're confident of them. It fills in as much more of the
633 * grid as it can.
634 *
635 * Return value is:
636 *
637 * - -1 means deduction stalled and nothing could be done
638 * - 0 means deduction succeeded fully
639 * - >0 means deduction succeeded but some number of perturbation
640 * steps were required; the exact return value is the number of
641 * perturb calls.
642 */
643
644typedef struct perturbations *(*perturb_cb) (void *, signed char *, int, int, int);
645
646static int minesolve(int w, int h, int n, signed char *grid,
647 open_cb open,
648 perturb_cb perturb,
649 void *ctx, random_state *rs)
650{
651 struct setstore *ss = ss_new();
652 struct set **list;
653 struct squaretodo astd, *std = &astd;
654 int x, y, i, j;
655 int nperturbs = 0;
656
657 /*
658 * Set up a linked list of squares with known contents, so that
659 * we can process them one by one.
660 */
661 std->next = snewn(w*h, int);
662 std->head = std->tail = -1;
663
664 /*
665 * Initialise that list with all known squares in the input
666 * grid.
667 */
668 for (y = 0; y < h; y++) {
669 for (x = 0; x < w; x++) {
670 i = y*w+x;
671 if (grid[i] != -2)
672 std_add(std, i);
673 }
674 }
675
676 /*
677 * Main deductive loop.
678 */
679 while (1) {
680 int done_something = FALSE;
681 struct set *s;
682
683 /*
684 * If there are any known squares on the todo list, process
685 * them and construct a set for each.
686 */
687 while (std->head != -1) {
688 i = std->head;
689#ifdef SOLVER_DIAGNOSTICS
690 printf("known square at %d,%d [%d]\n", i%w, i/w, grid[i]);
691#endif
692 std->head = std->next[i];
693 if (std->head == -1)
694 std->tail = -1;
695
696 x = i % w;
697 y = i / w;
698
699 if (grid[i] >= 0) {
700 int dx, dy, mines, bit, val;
701#ifdef SOLVER_DIAGNOSTICS
702 printf("creating set around this square\n");
703#endif
704 /*
705 * Empty square. Construct the set of non-known squares
706 * around this one, and determine its mine count.
707 */
708 mines = grid[i];
709 bit = 1;
710 val = 0;
711 for (dy = -1; dy <= +1; dy++) {
712 for (dx = -1; dx <= +1; dx++) {
713#ifdef SOLVER_DIAGNOSTICS
714 printf("grid %d,%d = %d\n", x+dx, y+dy, grid[i+dy*w+dx]);
715#endif
716 if (x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= h)
717 /* ignore this one */;
718 else if (grid[i+dy*w+dx] == -1)
719 mines--;
720 else if (grid[i+dy*w+dx] == -2)
721 val |= bit;
722 bit <<= 1;
723 }
724 }
725 if (val)
726 ss_add(ss, x-1, y-1, val, mines);
727 }
728
729 /*
730 * Now, whether the square is empty or full, we must
731 * find any set which contains it and replace it with
732 * one which does not.
733 */
734 {
735#ifdef SOLVER_DIAGNOSTICS
736 printf("finding sets containing known square %d,%d\n", x, y);
737#endif
738 list = ss_overlap(ss, x, y, 1);
739
740 for (j = 0; list[j]; j++) {
741 int newmask, newmines;
742
743 s = list[j];
744
745 /*
746 * Compute the mask for this set minus the
747 * newly known square.
748 */
749 newmask = setmunge(s->x, s->y, s->mask, x, y, 1, TRUE);
750
751 /*
752 * Compute the new mine count.
753 */
754 newmines = s->mines - (grid[i] == -1);
755
756 /*
757 * Insert the new set into the collection,
758 * unless it's been whittled right down to
759 * nothing.
760 */
761 if (newmask)
762 ss_add(ss, s->x, s->y, newmask, newmines);
763
764 /*
765 * Destroy the old one; it is actually obsolete.
766 */
767 ss_remove(ss, s);
768 }
769
770 sfree(list);
771 }
772
773 /*
774 * Marking a fresh square as known certainly counts as
775 * doing something.
776 */
777 done_something = TRUE;
778 }
779
780 /*
781 * Now pick a set off the to-do list and attempt deductions
782 * based on it.
783 */
784 if ((s = ss_todo(ss)) != NULL) {
785
786#ifdef SOLVER_DIAGNOSTICS
787 printf("set to do: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines);
788#endif
789 /*
790 * Firstly, see if this set has a mine count of zero or
791 * of its own cardinality.
792 */
793 if (s->mines == 0 || s->mines == bitcount16(s->mask)) {
794 /*
795 * If so, we can immediately mark all the squares
796 * in the set as known.
797 */
798#ifdef SOLVER_DIAGNOSTICS
799 printf("easy\n");
800#endif
801 known_squares(w, h, std, grid, open, ctx,
802 s->x, s->y, s->mask, (s->mines != 0));
803
804 /*
805 * Having done that, we need do nothing further
806 * with this set; marking all the squares in it as
807 * known will eventually eliminate it, and will
808 * also permit further deductions about anything
809 * that overlaps it.
810 */
811 continue;
812 }
813
814 /*
815 * Failing that, we now search through all the sets
816 * which overlap this one.
817 */
818 list = ss_overlap(ss, s->x, s->y, s->mask);
819
820 for (j = 0; list[j]; j++) {
821 struct set *s2 = list[j];
822 int swing, s2wing, swc, s2wc;
823
824 /*
825 * Find the non-overlapping parts s2-s and s-s2,
826 * and their cardinalities.
827 *
828 * I'm going to refer to these parts as `wings'
829 * surrounding the central part common to both
830 * sets. The `s wing' is s-s2; the `s2 wing' is
831 * s2-s.
832 */
833 swing = setmunge(s->x, s->y, s->mask, s2->x, s2->y, s2->mask,
834 TRUE);
835 s2wing = setmunge(s2->x, s2->y, s2->mask, s->x, s->y, s->mask,
836 TRUE);
837 swc = bitcount16(swing);
838 s2wc = bitcount16(s2wing);
839
840 /*
841 * If one set has more mines than the other, and
842 * the number of extra mines is equal to the
843 * cardinality of that set's wing, then we can mark
844 * every square in the wing as a known mine, and
845 * every square in the other wing as known clear.
846 */
847 if (swc == s->mines - s2->mines ||
848 s2wc == s2->mines - s->mines) {
849 known_squares(w, h, std, grid, open, ctx,
850 s->x, s->y, swing,
851 (swc == s->mines - s2->mines));
852 known_squares(w, h, std, grid, open, ctx,
853 s2->x, s2->y, s2wing,
854 (s2wc == s2->mines - s->mines));
855 continue;
856 }
857
858 /*
859 * Failing that, see if one set is a subset of the
860 * other. If so, we can divide up the mine count of
861 * the larger set between the smaller set and its
862 * complement, even if neither smaller set ends up
863 * being immediately clearable.
864 */
865 if (swc == 0 && s2wc != 0) {
866 /* s is a subset of s2. */
867 assert(s2->mines > s->mines);
868 ss_add(ss, s2->x, s2->y, s2wing, s2->mines - s->mines);
869 } else if (s2wc == 0 && swc != 0) {
870 /* s2 is a subset of s. */
871 assert(s->mines > s2->mines);
872 ss_add(ss, s->x, s->y, swing, s->mines - s2->mines);
873 }
874 }
875
876 sfree(list);
877
878 /*
879 * In this situation we have definitely done
880 * _something_, even if it's only reducing the size of
881 * our to-do list.
882 */
883 done_something = TRUE;
884 } else if (n >= 0) {
885 /*
886 * We have nothing left on our todo list, which means
887 * all localised deductions have failed. Our next step
888 * is to resort to global deduction based on the total
889 * mine count. This is computationally expensive
890 * compared to any of the above deductions, which is
891 * why we only ever do it when all else fails, so that
892 * hopefully it won't have to happen too often.
893 *
894 * If you pass n<0 into this solver, that informs it
895 * that you do not know the total mine count, so it
896 * won't even attempt these deductions.
897 */
898
899 int minesleft, squaresleft;
900 int nsets, setused[10], cursor;
901
902 /*
903 * Start by scanning the current grid state to work out
904 * how many unknown squares we still have, and how many
905 * mines are to be placed in them.
906 */
907 squaresleft = 0;
908 minesleft = n;
909 for (i = 0; i < w*h; i++) {
910 if (grid[i] == -1)
911 minesleft--;
912 else if (grid[i] == -2)
913 squaresleft++;
914 }
915
916#ifdef SOLVER_DIAGNOSTICS
917 printf("global deduction time: squaresleft=%d minesleft=%d\n",
918 squaresleft, minesleft);
919 for (y = 0; y < h; y++) {
920 for (x = 0; x < w; x++) {
921 int v = grid[y*w+x];
922 if (v == -1)
923 putchar('*');
924 else if (v == -2)
925 putchar('?');
926 else if (v == 0)
927 putchar('-');
928 else
929 putchar('0' + v);
930 }
931 putchar('\n');
932 }
933#endif
934
935 /*
936 * If there _are_ no unknown squares, we have actually
937 * finished.
938 */
939 if (squaresleft == 0) {
940 assert(minesleft == 0);
941 break;
942 }
943
944 /*
945 * First really simple case: if there are no more mines
946 * left, or if there are exactly as many mines left as
947 * squares to play them in, then it's all easy.
948 */
949 if (minesleft == 0 || minesleft == squaresleft) {
950 for (i = 0; i < w*h; i++)
951 if (grid[i] == -2)
952 known_squares(w, h, std, grid, open, ctx,
953 i % w, i / w, 1, minesleft != 0);
954 continue; /* now go back to main deductive loop */
955 }
956
957 /*
958 * Failing that, we have to do some _real_ work.
959 * Ideally what we do here is to try every single
960 * combination of the currently available sets, in an
961 * attempt to find a disjoint union (i.e. a set of
962 * squares with a known mine count between them) such
963 * that the remaining unknown squares _not_ contained
964 * in that union either contain no mines or are all
965 * mines.
966 *
967 * Actually enumerating all 2^n possibilities will get
968 * a bit slow for large n, so I artificially cap this
969 * recursion at n=10 to avoid too much pain.
970 */
971 nsets = count234(ss->sets);
972 if (nsets <= lenof(setused)) {
973 /*
974 * Doing this with actual recursive function calls
975 * would get fiddly because a load of local
976 * variables from this function would have to be
977 * passed down through the recursion. So instead
978 * I'm going to use a virtual recursion within this
979 * function. The way this works is:
980 *
981 * - we have an array `setused', such that
982 * setused[n] is 0 or 1 depending on whether set
983 * n is currently in the union we are
984 * considering.
985 *
986 * - we have a value `cursor' which indicates how
987 * much of `setused' we have so far filled in.
988 * It's conceptually the recursion depth.
989 *
990 * We begin by setting `cursor' to zero. Then:
991 *
992 * - if cursor can advance, we advance it by one.
993 * We set the value in `setused' that it went
994 * past to 1 if that set is disjoint from
995 * anything else currently in `setused', or to 0
996 * otherwise.
997 *
998 * - If cursor cannot advance because it has
999 * reached the end of the setused list, then we
1000 * have a maximal disjoint union. Check to see
1001 * whether its mine count has any useful
1002 * properties. If so, mark all the squares not
1003 * in the union as known and terminate.
1004 *
1005 * - If cursor has reached the end of setused and
1006 * the algorithm _hasn't_ terminated, back
1007 * cursor up to the nearest 1, turn it into a 0
1008 * and advance cursor just past it.
1009 *
1010 * - If we attempt to back up to the nearest 1 and
1011 * there isn't one at all, then we have gone
1012 * through all disjoint unions of sets in the
1013 * list and none of them has been helpful, so we
1014 * give up.
1015 */
1016 struct set *sets[lenof(setused)];
1017 for (i = 0; i < nsets; i++)
1018 sets[i] = index234(ss->sets, i);
1019
1020 cursor = 0;
1021 while (1) {
1022
1023 if (cursor < nsets) {
1024 int ok = TRUE;
1025
1026 /* See if any existing set overlaps this one. */
1027 for (i = 0; i < cursor; i++)
1028 if (setused[i] &&
1029 setmunge(sets[cursor]->x,
1030 sets[cursor]->y,
1031 sets[cursor]->mask,
1032 sets[i]->x, sets[i]->y, sets[i]->mask,
1033 FALSE)) {
1034 ok = FALSE;
1035 break;
1036 }
1037
1038 if (ok) {
1039 /*
1040 * We're adding this set to our union,
1041 * so adjust minesleft and squaresleft
1042 * appropriately.
1043 */
1044 minesleft -= sets[cursor]->mines;
1045 squaresleft -= bitcount16(sets[cursor]->mask);
1046 }
1047
1048 setused[cursor++] = ok;
1049 } else {
1050#ifdef SOLVER_DIAGNOSTICS
1051 printf("trying a set combination with %d %d\n",
1052 squaresleft, minesleft);
1053#endif /* SOLVER_DIAGNOSTICS */
1054
1055 /*
1056 * We've reached the end. See if we've got
1057 * anything interesting.
1058 */
1059 if (squaresleft > 0 &&
1060 (minesleft == 0 || minesleft == squaresleft)) {
1061 /*
1062 * We have! There is at least one
1063 * square not contained within the set
1064 * union we've just found, and we can
1065 * deduce that either all such squares
1066 * are mines or all are not (depending
1067 * on whether minesleft==0). So now all
1068 * we have to do is actually go through
1069 * the grid, find those squares, and
1070 * mark them.
1071 */
1072 for (i = 0; i < w*h; i++)
1073 if (grid[i] == -2) {
1074 int outside = TRUE;
1075 y = i / w;
1076 x = i % w;
1077 for (j = 0; j < nsets; j++)
1078 if (setused[j] &&
1079 setmunge(sets[j]->x, sets[j]->y,
1080 sets[j]->mask, x, y, 1,
1081 FALSE)) {
1082 outside = FALSE;
1083 break;
1084 }
1085 if (outside)
1086 known_squares(w, h, std, grid,
1087 open, ctx,
1088 x, y, 1, minesleft != 0);
1089 }
1090
1091 done_something = TRUE;
1092 break; /* return to main deductive loop */
1093 }
1094
1095 /*
1096 * If we reach here, then this union hasn't
1097 * done us any good, so move on to the
1098 * next. Backtrack cursor to the nearest 1,
1099 * change it to a 0 and continue.
1100 */
1101 while (--cursor >= 0 && !setused[cursor]);
1102 if (cursor >= 0) {
1103 assert(setused[cursor]);
1104
1105 /*
1106 * We're removing this set from our
1107 * union, so re-increment minesleft and
1108 * squaresleft.
1109 */
1110 minesleft += sets[cursor]->mines;
1111 squaresleft += bitcount16(sets[cursor]->mask);
1112
1113 setused[cursor++] = 0;
1114 } else {
1115 /*
1116 * We've backtracked all the way to the
1117 * start without finding a single 1,
1118 * which means that our virtual
1119 * recursion is complete and nothing
1120 * helped.
1121 */
1122 break;
1123 }
1124 }
1125
1126 }
1127
1128 }
1129 }
1130
1131 if (done_something)
1132 continue;
1133
1134#ifdef SOLVER_DIAGNOSTICS
1135 /*
1136 * Dump the current known state of the grid.
1137 */
1138 printf("solver ran out of steam, ret=%d, grid:\n", nperturbs);
1139 for (y = 0; y < h; y++) {
1140 for (x = 0; x < w; x++) {
1141 int v = grid[y*w+x];
1142 if (v == -1)
1143 putchar('*');
1144 else if (v == -2)
1145 putchar('?');
1146 else if (v == 0)
1147 putchar('-');
1148 else
1149 putchar('0' + v);
1150 }
1151 putchar('\n');
1152 }
1153
1154 {
1155 struct set *s;
1156
1157 for (i = 0; (s = index234(ss->sets, i)) != NULL; i++)
1158 printf("remaining set: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines);
1159 }
1160#endif
1161
1162 /*
1163 * Now we really are at our wits' end as far as solving
1164 * this grid goes. Our only remaining option is to call
1165 * a perturb function and ask it to modify the grid to
1166 * make it easier.
1167 */
1168 if (perturb) {
1169 struct perturbations *ret;
1170 struct set *s;
1171
1172 nperturbs++;
1173
1174 /*
1175 * Choose a set at random from the current selection,
1176 * and ask the perturb function to either fill or empty
1177 * it.
1178 *
1179 * If we have no sets at all, we must give up.
1180 */
1181 if (count234(ss->sets) == 0) {
1182#ifdef SOLVER_DIAGNOSTICS
1183 printf("perturbing on entire unknown set\n");
1184#endif
1185 ret = perturb(ctx, grid, 0, 0, 0);
1186 } else {
1187 s = index234(ss->sets, random_upto(rs, count234(ss->sets)));
1188#ifdef SOLVER_DIAGNOSTICS
1189 printf("perturbing on set %d,%d %03x\n", s->x, s->y, s->mask);
1190#endif
1191 ret = perturb(ctx, grid, s->x, s->y, s->mask);
1192 }
1193
1194 if (ret) {
1195 assert(ret->n > 0); /* otherwise should have been NULL */
1196
1197 /*
1198 * A number of squares have been fiddled with, and
1199 * the returned structure tells us which. Adjust
1200 * the mine count in any set which overlaps one of
1201 * those squares, and put them back on the to-do
1202 * list. Also, if the square itself is marked as a
1203 * known non-mine, put it back on the squares-to-do
1204 * list.
1205 */
1206 for (i = 0; i < ret->n; i++) {
1207#ifdef SOLVER_DIAGNOSTICS
1208 printf("perturbation %s mine at %d,%d\n",
1209 ret->changes[i].delta > 0 ? "added" : "removed",
1210 ret->changes[i].x, ret->changes[i].y);
1211#endif
1212
1213 if (ret->changes[i].delta < 0 &&
1214 grid[ret->changes[i].y*w+ret->changes[i].x] != -2) {
1215 std_add(std, ret->changes[i].y*w+ret->changes[i].x);
1216 }
1217
1218 list = ss_overlap(ss,
1219 ret->changes[i].x, ret->changes[i].y, 1);
1220
1221 for (j = 0; list[j]; j++) {
1222 list[j]->mines += ret->changes[i].delta;
1223 ss_add_todo(ss, list[j]);
1224 }
1225
1226 sfree(list);
1227 }
1228
1229 /*
1230 * Now free the returned data.
1231 */
1232 sfree(ret->changes);
1233 sfree(ret);
1234
1235#ifdef SOLVER_DIAGNOSTICS
1236 /*
1237 * Dump the current known state of the grid.
1238 */
1239 printf("state after perturbation:\n");
1240 for (y = 0; y < h; y++) {
1241 for (x = 0; x < w; x++) {
1242 int v = grid[y*w+x];
1243 if (v == -1)
1244 putchar('*');
1245 else if (v == -2)
1246 putchar('?');
1247 else if (v == 0)
1248 putchar('-');
1249 else
1250 putchar('0' + v);
1251 }
1252 putchar('\n');
1253 }
1254
1255 {
1256 struct set *s;
1257
1258 for (i = 0; (s = index234(ss->sets, i)) != NULL; i++)
1259 printf("remaining set: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines);
1260 }
1261#endif
1262
1263 /*
1264 * And now we can go back round the deductive loop.
1265 */
1266 continue;
1267 }
1268 }
1269
1270 /*
1271 * If we get here, even that didn't work (either we didn't
1272 * have a perturb function or it returned failure), so we
1273 * give up entirely.
1274 */
1275 break;
1276 }
1277
1278 /*
1279 * See if we've got any unknown squares left.
1280 */
1281 for (y = 0; y < h; y++)
1282 for (x = 0; x < w; x++)
1283 if (grid[y*w+x] == -2) {
1284 nperturbs = -1; /* failed to complete */
1285 break;
1286 }
1287
1288 /*
1289 * Free the set list and square-todo list.
1290 */
1291 {
1292 struct set *s;
1293 while ((s = delpos234(ss->sets, 0)) != NULL)
1294 sfree(s);
1295 freetree234(ss->sets);
1296 sfree(ss);
1297 sfree(std->next);
1298 }
1299
1300 return nperturbs;
1301}
1302
1303/* ----------------------------------------------------------------------
1304 * Grid generator which uses the above solver.
1305 */
1306
1307struct minectx {
1308 char *grid;
1309 int w, h;
1310 int sx, sy;
1311 int allow_big_perturbs;
1312 random_state *rs;
1313};
1314
1315static int mineopen(void *vctx, int x, int y)
1316{
1317 struct minectx *ctx = (struct minectx *)vctx;
1318 int i, j, n;
1319
1320 assert(x >= 0 && x < ctx->w && y >= 0 && y < ctx->h);
1321 if (ctx->grid[y * ctx->w + x])
1322 return -1; /* *bang* */
1323
1324 n = 0;
1325 for (i = -1; i <= +1; i++) {
1326 if (x + i < 0 || x + i >= ctx->w)
1327 continue;
1328 for (j = -1; j <= +1; j++) {
1329 if (y + j < 0 || y + j >= ctx->h)
1330 continue;
1331 if (i == 0 && j == 0)
1332 continue;
1333 if (ctx->grid[(y+j) * ctx->w + (x+i)])
1334 n++;
1335 }
1336 }
1337
1338 return n;
1339}
1340
1341/* Structure used internally to mineperturb(). */
1342struct square {
1343 int x, y, type, random;
1344};
1345static int squarecmp(const void *av, const void *bv)
1346{
1347 const struct square *a = (const struct square *)av;
1348 const struct square *b = (const struct square *)bv;
1349 if (a->type < b->type)
1350 return -1;
1351 else if (a->type > b->type)
1352 return +1;
1353 else if (a->random < b->random)
1354 return -1;
1355 else if (a->random > b->random)
1356 return +1;
1357 else if (a->y < b->y)
1358 return -1;
1359 else if (a->y > b->y)
1360 return +1;
1361 else if (a->x < b->x)
1362 return -1;
1363 else if (a->x > b->x)
1364 return +1;
1365 return 0;
1366}
1367
1368/*
1369 * Normally this function is passed an (x,y,mask) set description.
1370 * On occasions, though, there is no _localised_ set being used,
1371 * and the set being perturbed is supposed to be the entirety of
1372 * the unreachable area. This is signified by the special case
1373 * mask==0: in this case, anything labelled -2 in the grid is part
1374 * of the set.
1375 *
1376 * Allowing perturbation in this special case appears to make it
1377 * guaranteeably possible to generate a workable grid for any mine
1378 * density, but they tend to be a bit boring, with mines packed
1379 * densely into far corners of the grid and the remainder being
1380 * less dense than one might like. Therefore, to improve overall
1381 * grid quality I disable this feature for the first few attempts,
1382 * and fall back to it after no useful grid has been generated.
1383 */
1384static struct perturbations *mineperturb(void *vctx, signed char *grid,
1385 int setx, int sety, int mask)
1386{
1387 struct minectx *ctx = (struct minectx *)vctx;
1388 struct square *sqlist;
1389 int x, y, dx, dy, i, n, nfull, nempty;
1390 struct square **tofill, **toempty, **todo;
1391 int ntofill, ntoempty, ntodo, dtodo, dset;
1392 struct perturbations *ret;
1393 int *setlist;
1394
1395 if (!mask && !ctx->allow_big_perturbs)
1396 return NULL;
1397
1398 /*
1399 * Make a list of all the squares in the grid which we can
1400 * possibly use. This list should be in preference order, which
1401 * means
1402 *
1403 * - first, unknown squares on the boundary of known space
1404 * - next, unknown squares beyond that boundary
1405 * - as a very last resort, known squares, but not within one
1406 * square of the starting position.
1407 *
1408 * Each of these sections needs to be shuffled independently.
1409 * We do this by preparing list of all squares and then sorting
1410 * it with a random secondary key.
1411 */
1412 sqlist = snewn(ctx->w * ctx->h, struct square);
1413 n = 0;
1414 for (y = 0; y < ctx->h; y++)
1415 for (x = 0; x < ctx->w; x++) {
1416 /*
1417 * If this square is too near the starting position,
1418 * don't put it on the list at all.
1419 */
1420 if (abs(y - ctx->sy) <= 1 && abs(x - ctx->sx) <= 1)
1421 continue;
1422
1423 /*
1424 * If this square is in the input set, also don't put
1425 * it on the list!
1426 */
1427 if ((mask == 0 && grid[y*ctx->w+x] == -2) ||
1428 (x >= setx && x < setx + 3 &&
1429 y >= sety && y < sety + 3 &&
1430 mask & (1 << ((y-sety)*3+(x-setx)))))
1431 continue;
1432
1433 sqlist[n].x = x;
1434 sqlist[n].y = y;
1435
1436 if (grid[y*ctx->w+x] != -2) {
1437 sqlist[n].type = 3; /* known square */
1438 } else {
1439 /*
1440 * Unknown square. Examine everything around it and
1441 * see if it borders on any known squares. If it
1442 * does, it's class 1, otherwise it's 2.
1443 */
1444
1445 sqlist[n].type = 2;
1446
1447 for (dy = -1; dy <= +1; dy++)
1448 for (dx = -1; dx <= +1; dx++)
1449 if (x+dx >= 0 && x+dx < ctx->w &&
1450 y+dy >= 0 && y+dy < ctx->h &&
1451 grid[(y+dy)*ctx->w+(x+dx)] != -2) {
1452 sqlist[n].type = 1;
1453 break;
1454 }
1455 }
1456
1457 /*
1458 * Finally, a random number to cause qsort to
1459 * shuffle within each group.
1460 */
1461 sqlist[n].random = random_bits(ctx->rs, 31);
1462
1463 n++;
1464 }
1465
1466 qsort(sqlist, n, sizeof(struct square), squarecmp);
1467
1468 /*
1469 * Now count up the number of full and empty squares in the set
1470 * we've been provided.
1471 */
1472 nfull = nempty = 0;
1473 if (mask) {
1474 for (dy = 0; dy < 3; dy++)
1475 for (dx = 0; dx < 3; dx++)
1476 if (mask & (1 << (dy*3+dx))) {
1477 assert(setx+dx <= ctx->w);
1478 assert(sety+dy <= ctx->h);
1479 if (ctx->grid[(sety+dy)*ctx->w+(setx+dx)])
1480 nfull++;
1481 else
1482 nempty++;
1483 }
1484 } else {
1485 for (y = 0; y < ctx->h; y++)
1486 for (x = 0; x < ctx->w; x++)
1487 if (grid[y*ctx->w+x] == -2) {
1488 if (ctx->grid[y*ctx->w+x])
1489 nfull++;
1490 else
1491 nempty++;
1492 }
1493 }
1494
1495 /*
1496 * Now go through our sorted list until we find either `nfull'
1497 * empty squares, or `nempty' full squares; these will be
1498 * swapped with the appropriate squares in the set to either
1499 * fill or empty the set while keeping the same number of mines
1500 * overall.
1501 */
1502 ntofill = ntoempty = 0;
1503 if (mask) {
1504 tofill = snewn(9, struct square *);
1505 toempty = snewn(9, struct square *);
1506 } else {
1507 tofill = snewn(ctx->w * ctx->h, struct square *);
1508 toempty = snewn(ctx->w * ctx->h, struct square *);
1509 }
1510 for (i = 0; i < n; i++) {
1511 struct square *sq = &sqlist[i];
1512 if (ctx->grid[sq->y * ctx->w + sq->x])
1513 toempty[ntoempty++] = sq;
1514 else
1515 tofill[ntofill++] = sq;
1516 if (ntofill == nfull || ntoempty == nempty)
1517 break;
1518 }
1519
1520 /*
1521 * If we haven't found enough empty squares outside the set to
1522 * empty it into _or_ enough full squares outside it to fill it
1523 * up with, we'll have to settle for doing only a partial job.
1524 * In this case we choose to always _fill_ the set (because
1525 * this case will tend to crop up when we're working with very
1526 * high mine densities and the only way to get a solvable grid
1527 * is going to be to pack most of the mines solidly around the
1528 * edges). So now our job is to make a list of the empty
1529 * squares in the set, and shuffle that list so that we fill a
1530 * random selection of them.
1531 */
1532 if (ntofill != nfull && ntoempty != nempty) {
1533 int k;
1534
1535 assert(ntoempty != 0);
1536
1537 setlist = snewn(ctx->w * ctx->h, int);
1538 i = 0;
1539 if (mask) {
1540 for (dy = 0; dy < 3; dy++)
1541 for (dx = 0; dx < 3; dx++)
1542 if (mask & (1 << (dy*3+dx))) {
1543 assert(setx+dx <= ctx->w);
1544 assert(sety+dy <= ctx->h);
1545 if (!ctx->grid[(sety+dy)*ctx->w+(setx+dx)])
1546 setlist[i++] = (sety+dy)*ctx->w+(setx+dx);
1547 }
1548 } else {
1549 for (y = 0; y < ctx->h; y++)
1550 for (x = 0; x < ctx->w; x++)
1551 if (grid[y*ctx->w+x] == -2) {
1552 if (!ctx->grid[y*ctx->w+x])
1553 setlist[i++] = y*ctx->w+x;
1554 }
1555 }
1556 assert(i > ntoempty);
1557 /*
1558 * Now pick `ntoempty' items at random from the list.
1559 */
1560 for (k = 0; k < ntoempty; k++) {
1561 int index = k + random_upto(ctx->rs, i - k);
1562 int tmp;
1563
1564 tmp = setlist[k];
1565 setlist[k] = setlist[index];
1566 setlist[index] = tmp;
1567 }
1568 } else
1569 setlist = NULL;
1570
1571 /*
1572 * Now we're pretty much there. We need to either
1573 * (a) put a mine in each of the empty squares in the set, and
1574 * take one out of each square in `toempty'
1575 * (b) take a mine out of each of the full squares in the set,
1576 * and put one in each square in `tofill'
1577 * depending on which one we've found enough squares to do.
1578 *
1579 * So we start by constructing our list of changes to return to
1580 * the solver, so that it can update its data structures
1581 * efficiently rather than having to rescan the whole grid.
1582 */
1583 ret = snew(struct perturbations);
1584 if (ntofill == nfull) {
1585 todo = tofill;
1586 ntodo = ntofill;
1587 dtodo = +1;
1588 dset = -1;
1589 sfree(toempty);
1590 } else {
1591 /*
1592 * (We also fall into this case if we've constructed a
1593 * setlist.)
1594 */
1595 todo = toempty;
1596 ntodo = ntoempty;
1597 dtodo = -1;
1598 dset = +1;
1599 sfree(tofill);
1600 }
1601 ret->n = 2 * ntodo;
1602 ret->changes = snewn(ret->n, struct perturbation);
1603 for (i = 0; i < ntodo; i++) {
1604 ret->changes[i].x = todo[i]->x;
1605 ret->changes[i].y = todo[i]->y;
1606 ret->changes[i].delta = dtodo;
1607 }
1608 /* now i == ntodo */
1609 if (setlist) {
1610 int j;
1611 assert(todo == toempty);
1612 for (j = 0; j < ntoempty; j++) {
1613 ret->changes[i].x = setlist[j] % ctx->w;
1614 ret->changes[i].y = setlist[j] / ctx->w;
1615 ret->changes[i].delta = dset;
1616 i++;
1617 }
1618 sfree(setlist);
1619 } else if (mask) {
1620 for (dy = 0; dy < 3; dy++)
1621 for (dx = 0; dx < 3; dx++)
1622 if (mask & (1 << (dy*3+dx))) {
1623 int currval = (ctx->grid[(sety+dy)*ctx->w+(setx+dx)] ? +1 : -1);
1624 if (dset == -currval) {
1625 ret->changes[i].x = setx + dx;
1626 ret->changes[i].y = sety + dy;
1627 ret->changes[i].delta = dset;
1628 i++;
1629 }
1630 }
1631 } else {
1632 for (y = 0; y < ctx->h; y++)
1633 for (x = 0; x < ctx->w; x++)
1634 if (grid[y*ctx->w+x] == -2) {
1635 int currval = (ctx->grid[y*ctx->w+x] ? +1 : -1);
1636 if (dset == -currval) {
1637 ret->changes[i].x = x;
1638 ret->changes[i].y = y;
1639 ret->changes[i].delta = dset;
1640 i++;
1641 }
1642 }
1643 }
1644 assert(i == ret->n);
1645
1646 sfree(sqlist);
1647 sfree(todo);
1648
1649 /*
1650 * Having set up the precise list of changes we're going to
1651 * make, we now simply make them and return.
1652 */
1653 for (i = 0; i < ret->n; i++) {
1654 int delta;
1655
1656 x = ret->changes[i].x;
1657 y = ret->changes[i].y;
1658 delta = ret->changes[i].delta;
1659
1660 /*
1661 * Check we're not trying to add an existing mine or remove
1662 * an absent one.
1663 */
1664 assert((delta < 0) ^ (ctx->grid[y*ctx->w+x] == 0));
1665
1666 /*
1667 * Actually make the change.
1668 */
1669 ctx->grid[y*ctx->w+x] = (delta > 0);
1670
1671 /*
1672 * Update any numbers already present in the grid.
1673 */
1674 for (dy = -1; dy <= +1; dy++)
1675 for (dx = -1; dx <= +1; dx++)
1676 if (x+dx >= 0 && x+dx < ctx->w &&
1677 y+dy >= 0 && y+dy < ctx->h &&
1678 grid[(y+dy)*ctx->w+(x+dx)] != -2) {
1679 if (dx == 0 && dy == 0) {
1680 /*
1681 * The square itself is marked as known in
1682 * the grid. Mark it as a mine if it's a
1683 * mine, or else work out its number.
1684 */
1685 if (delta > 0) {
1686 grid[y*ctx->w+x] = -1;
1687 } else {
1688 int dx2, dy2, minecount = 0;
1689 for (dy2 = -1; dy2 <= +1; dy2++)
1690 for (dx2 = -1; dx2 <= +1; dx2++)
1691 if (x+dx2 >= 0 && x+dx2 < ctx->w &&
1692 y+dy2 >= 0 && y+dy2 < ctx->h &&
1693 ctx->grid[(y+dy2)*ctx->w+(x+dx2)])
1694 minecount++;
1695 grid[y*ctx->w+x] = minecount;
1696 }
1697 } else {
1698 if (grid[(y+dy)*ctx->w+(x+dx)] >= 0)
1699 grid[(y+dy)*ctx->w+(x+dx)] += delta;
1700 }
1701 }
1702 }
1703
1704#ifdef GENERATION_DIAGNOSTICS
1705 {
1706 int yy, xx;
1707 printf("grid after perturbing:\n");
1708 for (yy = 0; yy < ctx->h; yy++) {
1709 for (xx = 0; xx < ctx->w; xx++) {
1710 int v = ctx->grid[yy*ctx->w+xx];
1711 if (yy == ctx->sy && xx == ctx->sx) {
1712 assert(!v);
1713 putchar('S');
1714 } else if (v) {
1715 putchar('*');
1716 } else {
1717 putchar('-');
1718 }
1719 }
1720 putchar('\n');
1721 }
1722 printf("\n");
1723 }
1724#endif
1725
1726 return ret;
1727}
1728
1729static char *minegen(int w, int h, int n, int x, int y, int unique,
1730 random_state *rs)
1731{
1732 char *ret = snewn(w*h, char);
1733 int success;
1734 int ntries = 0;
1735
1736 do {
1737 success = FALSE;
1738 ntries++;
1739
1740 memset(ret, 0, w*h);
1741
1742 /*
1743 * Start by placing n mines, none of which is at x,y or within
1744 * one square of it.
1745 */
1746 {
1747 int *tmp = snewn(w*h, int);
1748 int i, j, k, nn;
1749
1750 /*
1751 * Write down the list of possible mine locations.
1752 */
1753 k = 0;
1754 for (i = 0; i < h; i++)
1755 for (j = 0; j < w; j++)
1756 if (abs(i - y) > 1 || abs(j - x) > 1)
1757 tmp[k++] = i*w+j;
1758
1759 /*
1760 * Now pick n off the list at random.
1761 */
1762 nn = n;
1763 while (nn-- > 0) {
1764 i = random_upto(rs, k);
1765 ret[tmp[i]] = 1;
1766 tmp[i] = tmp[--k];
1767 }
1768
1769 sfree(tmp);
1770 }
1771
1772#ifdef GENERATION_DIAGNOSTICS
1773 {
1774 int yy, xx;
1775 printf("grid after initial generation:\n");
1776 for (yy = 0; yy < h; yy++) {
1777 for (xx = 0; xx < w; xx++) {
1778 int v = ret[yy*w+xx];
1779 if (yy == y && xx == x) {
1780 assert(!v);
1781 putchar('S');
1782 } else if (v) {
1783 putchar('*');
1784 } else {
1785 putchar('-');
1786 }
1787 }
1788 putchar('\n');
1789 }
1790 printf("\n");
1791 }
1792#endif
1793
1794 /*
1795 * Now set up a results grid to run the solver in, and a
1796 * context for the solver to open squares. Then run the solver
1797 * repeatedly; if the number of perturb steps ever goes up or
1798 * it ever returns -1, give up completely.
1799 *
1800 * We bypass this bit if we're not after a unique grid.
1801 */
1802 if (unique) {
1803 signed char *solvegrid = snewn(w*h, signed char);
1804 struct minectx actx, *ctx = &actx;
1805 int solveret, prevret = -2;
1806
1807 ctx->grid = ret;
1808 ctx->w = w;
1809 ctx->h = h;
1810 ctx->sx = x;
1811 ctx->sy = y;
1812 ctx->rs = rs;
1813 ctx->allow_big_perturbs = (ntries > 100);
1814
1815 while (1) {
1816 memset(solvegrid, -2, w*h);
1817 solvegrid[y*w+x] = mineopen(ctx, x, y);
1818 assert(solvegrid[y*w+x] == 0); /* by deliberate arrangement */
1819
1820 solveret =
1821 minesolve(w, h, n, solvegrid, mineopen, mineperturb, ctx, rs);
1822 if (solveret < 0 || (prevret >= 0 && solveret >= prevret)) {
1823 success = FALSE;
1824 break;
1825 } else if (solveret == 0) {
1826 success = TRUE;
1827 break;
1828 }
1829 }
1830
1831 sfree(solvegrid);
1832 } else {
1833 success = TRUE;
1834 }
1835
1836 } while (!success);
1837
1838 return ret;
1839}
1840
1841static char *describe_layout(char *grid, int area, int x, int y,
1842 int obfuscate)
1843{
1844 char *ret, *p;
1845 unsigned char *bmp;
1846 int i;
1847
1848 /*
1849 * Set up the mine bitmap and obfuscate it.
1850 */
1851 bmp = snewn((area + 7) / 8, unsigned char);
1852 memset(bmp, 0, (area + 7) / 8);
1853 for (i = 0; i < area; i++) {
1854 if (grid[i])
1855 bmp[i / 8] |= 0x80 >> (i % 8);
1856 }
1857 if (obfuscate)
1858 obfuscate_bitmap(bmp, area, FALSE);
1859
1860 /*
1861 * Now encode the resulting bitmap in hex. We can work to
1862 * nibble rather than byte granularity, since the obfuscation
1863 * function guarantees to return a bit string of the same
1864 * length as its input.
1865 */
1866 ret = snewn((area+3)/4 + 100, char);
1867 p = ret + sprintf(ret, "%d,%d,%s", x, y,
1868 obfuscate ? "m" : "u"); /* 'm' == masked */
1869 for (i = 0; i < (area+3)/4; i++) {
1870 int v = bmp[i/2];
1871 if (i % 2 == 0)
1872 v >>= 4;
1873 *p++ = "0123456789abcdef"[v & 0xF];
1874 }
1875 *p = '\0';
1876
1877 sfree(bmp);
1878
1879 return ret;
1880}
1881
1882static char *new_mine_layout(int w, int h, int n, int x, int y, int unique,
1883 random_state *rs, char **game_desc)
1884{
1885 char *grid;
1886
1887#ifdef TEST_OBFUSCATION
1888 static int tested_obfuscation = FALSE;
1889 if (!tested_obfuscation) {
1890 /*
1891 * A few simple test vectors for the obfuscator.
1892 *
1893 * First test: the 28-bit stream 1234567. This divides up
1894 * into 1234 and 567[0]. The SHA of 56 70 30 (appending
1895 * "0") is 15ce8ab946640340bbb99f3f48fd2c45d1a31d30. Thus,
1896 * we XOR the 16-bit string 15CE into the input 1234 to get
1897 * 07FA. Next, we SHA that with "0": the SHA of 07 FA 30 is
1898 * 3370135c5e3da4fed937adc004a79533962b6391. So we XOR the
1899 * 12-bit string 337 into the input 567 to get 650. Thus
1900 * our output is 07FA650.
1901 */
1902 {
1903 unsigned char bmp1[] = "\x12\x34\x56\x70";
1904 obfuscate_bitmap(bmp1, 28, FALSE);
1905 printf("test 1 encode: %s\n",
1906 memcmp(bmp1, "\x07\xfa\x65\x00", 4) ? "failed" : "passed");
1907 obfuscate_bitmap(bmp1, 28, TRUE);
1908 printf("test 1 decode: %s\n",
1909 memcmp(bmp1, "\x12\x34\x56\x70", 4) ? "failed" : "passed");
1910 }
1911 /*
1912 * Second test: a long string to make sure we switch from
1913 * one SHA to the next correctly. My input string this time
1914 * is simply fifty bytes of zeroes.
1915 */
1916 {
1917 unsigned char bmp2[50];
1918 unsigned char bmp2a[50];
1919 memset(bmp2, 0, 50);
1920 memset(bmp2a, 0, 50);
1921 obfuscate_bitmap(bmp2, 50 * 8, FALSE);
1922 /*
1923 * SHA of twenty-five zero bytes plus "0" is
1924 * b202c07b990c01f6ff2d544707f60e506019b671. SHA of
1925 * twenty-five zero bytes plus "1" is
1926 * fcb1d8b5a2f6b592fe6780b36aa9d65dd7aa6db9. Thus our
1927 * first half becomes
1928 * b202c07b990c01f6ff2d544707f60e506019b671fcb1d8b5a2.
1929 *
1930 * SHA of that lot plus "0" is
1931 * 10b0af913db85d37ca27f52a9f78bba3a80030db. SHA of the
1932 * same string plus "1" is
1933 * 3d01d8df78e76d382b8106f480135a1bc751d725. So the
1934 * second half becomes
1935 * 10b0af913db85d37ca27f52a9f78bba3a80030db3d01d8df78.
1936 */
1937 printf("test 2 encode: %s\n",
1938 memcmp(bmp2, "\xb2\x02\xc0\x7b\x99\x0c\x01\xf6\xff\x2d\x54"
1939 "\x47\x07\xf6\x0e\x50\x60\x19\xb6\x71\xfc\xb1\xd8"
1940 "\xb5\xa2\x10\xb0\xaf\x91\x3d\xb8\x5d\x37\xca\x27"
1941 "\xf5\x2a\x9f\x78\xbb\xa3\xa8\x00\x30\xdb\x3d\x01"
1942 "\xd8\xdf\x78", 50) ? "failed" : "passed");
1943 obfuscate_bitmap(bmp2, 50 * 8, TRUE);
1944 printf("test 2 decode: %s\n",
1945 memcmp(bmp2, bmp2a, 50) ? "failed" : "passed");
1946 }
1947 }
1948#endif
1949
1950 grid = minegen(w, h, n, x, y, unique, rs);
1951
1952 if (game_desc)
1953 *game_desc = describe_layout(grid, w * h, x, y, TRUE);
1954
1955 return grid;
1956}
1957
1958static char *new_game_desc(const game_params *params, random_state *rs,
1959 char **aux, int interactive)
1960{
1961 /*
1962 * We generate the coordinates of an initial click even if they
1963 * aren't actually used. This has the effect of harmonising the
1964 * random number usage between interactive and batch use: if
1965 * you use `mines --generate' with an explicit random seed, you
1966 * should get exactly the same results as if you type the same
1967 * random seed into the interactive game and click in the same
1968 * initial location. (Of course you won't get the same grid if
1969 * you click in a _different_ initial location, but there's
1970 * nothing to be done about that.)
1971 */
1972 int x = random_upto(rs, params->w);
1973 int y = random_upto(rs, params->h);
1974
1975 if (!interactive) {
1976 /*
1977 * For batch-generated grids, pre-open one square.
1978 */
1979 char *grid;
1980 char *desc;
1981
1982 grid = new_mine_layout(params->w, params->h, params->n,
1983 x, y, params->unique, rs, &desc);
1984 sfree(grid);
1985 return desc;
1986 } else {
1987 char *rsdesc, *desc;
1988
1989 rsdesc = random_state_encode(rs);
1990 desc = snewn(strlen(rsdesc) + 100, char);
1991 sprintf(desc, "r%d,%c,%s", params->n, (char)(params->unique ? 'u' : 'a'), rsdesc);
1992 sfree(rsdesc);
1993 return desc;
1994 }
1995}
1996
1997static char *validate_desc(const game_params *params, const char *desc)
1998{
1999 int wh = params->w * params->h;
2000 int x, y;
2001
2002 if (*desc == 'r') {
2003 desc++;
2004 if (!*desc || !isdigit((unsigned char)*desc))
2005 return "No initial mine count in game description";
2006 while (*desc && isdigit((unsigned char)*desc))
2007 desc++; /* skip over mine count */
2008 if (*desc != ',')
2009 return "No ',' after initial x-coordinate in game description";
2010 desc++;
2011 if (*desc != 'u' && *desc != 'a')
2012 return "No uniqueness specifier in game description";
2013 desc++;
2014 if (*desc != ',')
2015 return "No ',' after uniqueness specifier in game description";
2016 /* now ignore the rest */
2017 } else {
2018 if (*desc && isdigit((unsigned char)*desc)) {
2019 x = atoi(desc);
2020 if (x < 0 || x >= params->w)
2021 return "Initial x-coordinate was out of range";
2022 while (*desc && isdigit((unsigned char)*desc))
2023 desc++; /* skip over x coordinate */
2024 if (*desc != ',')
2025 return "No ',' after initial x-coordinate in game description";
2026 desc++; /* eat comma */
2027 if (!*desc || !isdigit((unsigned char)*desc))
2028 return "No initial y-coordinate in game description";
2029 y = atoi(desc);
2030 if (y < 0 || y >= params->h)
2031 return "Initial y-coordinate was out of range";
2032 while (*desc && isdigit((unsigned char)*desc))
2033 desc++; /* skip over y coordinate */
2034 if (*desc != ',')
2035 return "No ',' after initial y-coordinate in game description";
2036 desc++; /* eat comma */
2037 }
2038 /* eat `m' for `masked' or `u' for `unmasked', if present */
2039 if (*desc == 'm' || *desc == 'u')
2040 desc++;
2041 /* now just check length of remainder */
2042 if (strlen(desc) != (wh+3)/4)
2043 return "Game description is wrong length";
2044 }
2045
2046 return NULL;
2047}
2048
2049static int open_square(game_state *state, int x, int y)
2050{
2051 int w = state->w, h = state->h;
2052 int xx, yy, nmines, ncovered;
2053
2054 if (!state->layout->mines) {
2055 /*
2056 * We have a preliminary game in which the mine layout
2057 * hasn't been generated yet. Generate it based on the
2058 * initial click location.
2059 */
2060 char *desc, *privdesc;
2061 state->layout->mines = new_mine_layout(w, h, state->layout->n,
2062 x, y, state->layout->unique,
2063 state->layout->rs,
2064 &desc);
2065 /*
2066 * Find the trailing substring of the game description
2067 * corresponding to just the mine layout; we will use this
2068 * as our second `private' game ID for serialisation.
2069 */
2070 privdesc = desc;
2071 while (*privdesc && isdigit((unsigned char)*privdesc)) privdesc++;
2072 if (*privdesc == ',') privdesc++;
2073 while (*privdesc && isdigit((unsigned char)*privdesc)) privdesc++;
2074 if (*privdesc == ',') privdesc++;
2075 assert(*privdesc == 'm');
2076 midend_supersede_game_desc(state->layout->me, desc, privdesc);
2077 sfree(desc);
2078 random_free(state->layout->rs);
2079 state->layout->rs = NULL;
2080 }
2081
2082 if (state->layout->mines[y*w+x]) {
2083 /*
2084 * The player has landed on a mine. Bad luck. Expose the
2085 * mine that killed them, but not the rest (in case they
2086 * want to Undo and carry on playing).
2087 */
2088 state->dead = TRUE;
2089 state->grid[y*w+x] = 65;
2090 return -1;
2091 }
2092
2093 /*
2094 * Otherwise, the player has opened a safe square. Mark it to-do.
2095 */
2096 state->grid[y*w+x] = -10; /* `todo' value internal to this func */
2097
2098 /*
2099 * Now go through the grid finding all `todo' values and
2100 * opening them. Every time one of them turns out to have no
2101 * neighbouring mines, we add all its unopened neighbours to
2102 * the list as well.
2103 *
2104 * FIXME: We really ought to be able to do this better than
2105 * using repeated N^2 scans of the grid.
2106 */
2107 while (1) {
2108 int done_something = FALSE;
2109
2110 for (yy = 0; yy < h; yy++)
2111 for (xx = 0; xx < w; xx++)
2112 if (state->grid[yy*w+xx] == -10) {
2113 int dx, dy, v;
2114
2115 assert(!state->layout->mines[yy*w+xx]);
2116
2117 v = 0;
2118
2119 for (dx = -1; dx <= +1; dx++)
2120 for (dy = -1; dy <= +1; dy++)
2121 if (xx+dx >= 0 && xx+dx < state->w &&
2122 yy+dy >= 0 && yy+dy < state->h &&
2123 state->layout->mines[(yy+dy)*w+(xx+dx)])
2124 v++;
2125
2126 state->grid[yy*w+xx] = v;
2127
2128 if (v == 0) {
2129 for (dx = -1; dx <= +1; dx++)
2130 for (dy = -1; dy <= +1; dy++)
2131 if (xx+dx >= 0 && xx+dx < state->w &&
2132 yy+dy >= 0 && yy+dy < state->h &&
2133 state->grid[(yy+dy)*w+(xx+dx)] == -2)
2134 state->grid[(yy+dy)*w+(xx+dx)] = -10;
2135 }
2136
2137 done_something = TRUE;
2138 }
2139
2140 if (!done_something)
2141 break;
2142 }
2143
2144 /*
2145 * Finally, scan the grid and see if exactly as many squares
2146 * are still covered as there are mines. If so, set the `won'
2147 * flag and fill in mine markers on all covered squares.
2148 */
2149 nmines = ncovered = 0;
2150 for (yy = 0; yy < h; yy++)
2151 for (xx = 0; xx < w; xx++) {
2152 if (state->grid[yy*w+xx] < 0)
2153 ncovered++;
2154 if (state->layout->mines[yy*w+xx])
2155 nmines++;
2156 }
2157 assert(ncovered >= nmines);
2158 if (ncovered == nmines) {
2159 for (yy = 0; yy < h; yy++)
2160 for (xx = 0; xx < w; xx++) {
2161 if (state->grid[yy*w+xx] < 0)
2162 state->grid[yy*w+xx] = -1;
2163 }
2164 state->won = TRUE;
2165 }
2166
2167 return 0;
2168}
2169
2170static game_state *new_game(midend *me, const game_params *params,
2171 const char *desc)
2172{
2173 game_state *state = snew(game_state);
2174 int i, wh, x, y, masked;
2175 unsigned char *bmp;
2176
2177 state->w = params->w;
2178 state->h = params->h;
2179 state->n = params->n;
2180 state->dead = state->won = FALSE;
2181 state->used_solve = FALSE;
2182
2183 wh = state->w * state->h;
2184
2185 state->layout = snew(struct mine_layout);
2186 memset(state->layout, 0, sizeof(struct mine_layout));
2187 state->layout->refcount = 1;
2188
2189 state->grid = snewn(wh, signed char);
2190 memset(state->grid, -2, wh);
2191
2192 if (*desc == 'r') {
2193 desc++;
2194 state->layout->n = atoi(desc);
2195 while (*desc && isdigit((unsigned char)*desc))
2196 desc++; /* skip over mine count */
2197 if (*desc) desc++; /* eat comma */
2198 if (*desc == 'a')
2199 state->layout->unique = FALSE;
2200 else
2201 state->layout->unique = TRUE;
2202 desc++;
2203 if (*desc) desc++; /* eat comma */
2204
2205 state->layout->mines = NULL;
2206 state->layout->rs = random_state_decode(desc);
2207 state->layout->me = me;
2208
2209 } else {
2210 state->layout->rs = NULL;
2211 state->layout->me = NULL;
2212 state->layout->mines = snewn(wh, char);
2213
2214 if (*desc && isdigit((unsigned char)*desc)) {
2215 x = atoi(desc);
2216 while (*desc && isdigit((unsigned char)*desc))
2217 desc++; /* skip over x coordinate */
2218 if (*desc) desc++; /* eat comma */
2219 y = atoi(desc);
2220 while (*desc && isdigit((unsigned char)*desc))
2221 desc++; /* skip over y coordinate */
2222 if (*desc) desc++; /* eat comma */
2223 } else {
2224 x = y = -1;
2225 }
2226
2227 if (*desc == 'm') {
2228 masked = TRUE;
2229 desc++;
2230 } else {
2231 if (*desc == 'u')
2232 desc++;
2233 /*
2234 * We permit game IDs to be entered by hand without the
2235 * masking transformation.
2236 */
2237 masked = FALSE;
2238 }
2239
2240 bmp = snewn((wh + 7) / 8, unsigned char);
2241 memset(bmp, 0, (wh + 7) / 8);
2242 for (i = 0; i < (wh+3)/4; i++) {
2243 int c = desc[i];
2244 int v;
2245
2246 assert(c != 0); /* validate_desc should have caught */
2247 if (c >= '0' && c <= '9')
2248 v = c - '0';
2249 else if (c >= 'a' && c <= 'f')
2250 v = c - 'a' + 10;
2251 else if (c >= 'A' && c <= 'F')
2252 v = c - 'A' + 10;
2253 else
2254 v = 0;
2255
2256 bmp[i / 2] |= v << (4 * (1 - (i % 2)));
2257 }
2258
2259 if (masked)
2260 obfuscate_bitmap(bmp, wh, TRUE);
2261
2262 memset(state->layout->mines, 0, wh);
2263 for (i = 0; i < wh; i++) {
2264 if (bmp[i / 8] & (0x80 >> (i % 8)))
2265 state->layout->mines[i] = 1;
2266 }
2267
2268 if (x >= 0 && y >= 0)
2269 open_square(state, x, y);
2270 sfree(bmp);
2271 }
2272
2273 return state;
2274}
2275
2276static game_state *dup_game(const game_state *state)
2277{
2278 game_state *ret = snew(game_state);
2279
2280 ret->w = state->w;
2281 ret->h = state->h;
2282 ret->n = state->n;
2283 ret->dead = state->dead;
2284 ret->won = state->won;
2285 ret->used_solve = state->used_solve;
2286 ret->layout = state->layout;
2287 ret->layout->refcount++;
2288 ret->grid = snewn(ret->w * ret->h, signed char);
2289 memcpy(ret->grid, state->grid, ret->w * ret->h);
2290
2291 return ret;
2292}
2293
2294static void free_game(game_state *state)
2295{
2296 if (--state->layout->refcount <= 0) {
2297 sfree(state->layout->mines);
2298 if (state->layout->rs)
2299 random_free(state->layout->rs);
2300 sfree(state->layout);
2301 }
2302 sfree(state->grid);
2303 sfree(state);
2304}
2305
2306static char *solve_game(const game_state *state, const game_state *currstate,
2307 const char *aux, char **error)
2308{
2309 if (!state->layout->mines) {
2310 *error = "Game has not been started yet";
2311 return NULL;
2312 }
2313
2314 return dupstr("S");
2315}
2316
2317static int game_can_format_as_text_now(const game_params *params)
2318{
2319 return TRUE;
2320}
2321
2322static char *game_text_format(const game_state *state)
2323{
2324 char *ret;
2325 int x, y;
2326
2327 ret = snewn((state->w + 1) * state->h + 1, char);
2328 for (y = 0; y < state->h; y++) {
2329 for (x = 0; x < state->w; x++) {
2330 int v = state->grid[y*state->w+x];
2331 if (v == 0)
2332 v = '-';
2333 else if (v >= 1 && v <= 8)
2334 v = '0' + v;
2335 else if (v == -1)
2336 v = '*';
2337 else if (v == -2 || v == -3)
2338 v = '?';
2339 else if (v >= 64)
2340 v = '!';
2341 ret[y * (state->w+1) + x] = v;
2342 }
2343 ret[y * (state->w+1) + state->w] = '\n';
2344 }
2345 ret[(state->w + 1) * state->h] = '\0';
2346
2347 return ret;
2348}
2349
2350struct game_ui {
2351 int hx, hy, hradius; /* for mouse-down highlights */
2352 int validradius;
2353 int flash_is_death;
2354 int deaths, completed;
2355 int cur_x, cur_y, cur_visible;
2356};
2357
2358static game_ui *new_ui(const game_state *state)
2359{
2360 game_ui *ui = snew(game_ui);
2361 ui->hx = ui->hy = -1;
2362 ui->hradius = ui->validradius = 0;
2363 ui->deaths = 0;
2364 ui->completed = FALSE;
2365 ui->flash_is_death = FALSE; /* *shrug* */
2366 ui->cur_x = ui->cur_y = ui->cur_visible = 0;
2367 return ui;
2368}
2369
2370static void free_ui(game_ui *ui)
2371{
2372 sfree(ui);
2373}
2374
2375static char *encode_ui(const game_ui *ui)
2376{
2377 char buf[80];
2378 /*
2379 * The deaths counter and completion status need preserving
2380 * across a serialisation.
2381 */
2382 sprintf(buf, "D%d", ui->deaths);
2383 if (ui->completed)
2384 strcat(buf, "C");
2385 return dupstr(buf);
2386}
2387
2388static void decode_ui(game_ui *ui, const char *encoding)
2389{
2390 int p= 0;
2391 sscanf(encoding, "D%d%n", &ui->deaths, &p);
2392 if (encoding[p] == 'C')
2393 ui->completed = TRUE;
2394}
2395
2396static void game_changed_state(game_ui *ui, const game_state *oldstate,
2397 const game_state *newstate)
2398{
2399 if (newstate->won)
2400 ui->completed = TRUE;
2401}
2402
2403struct game_drawstate {
2404 int w, h, started, tilesize, bg;
2405 signed char *grid;
2406 /*
2407 * Items in this `grid' array have all the same values as in
2408 * the game_state grid, and in addition:
2409 *
2410 * - -10 means the tile was drawn `specially' as a result of a
2411 * flash, so it will always need redrawing.
2412 *
2413 * - -22 and -23 mean the tile is highlighted for a possible
2414 * click.
2415 */
2416 int cur_x, cur_y; /* -1, -1 for no cursor displayed. */
2417};
2418
2419static char *interpret_move(const game_state *from, game_ui *ui,
2420 const game_drawstate *ds,
2421 int x, int y, int button)
2422{
2423 int cx, cy;
2424 char buf[256];
2425
2426 if (from->dead || from->won)
2427 return NULL; /* no further moves permitted */
2428
2429 cx = FROMCOORD(x);
2430 cy = FROMCOORD(y);
2431
2432 if (IS_CURSOR_MOVE(button)) {
2433 move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, 0);
2434 ui->cur_visible = 1;
2435 return "";
2436 }
2437 if (IS_CURSOR_SELECT(button)) {
2438 int v = from->grid[ui->cur_y * from->w + ui->cur_x];
2439
2440 if (!ui->cur_visible) {
2441 ui->cur_visible = 1;
2442 return "";
2443 }
2444 if (button == CURSOR_SELECT2) {
2445 /* As for RIGHT_BUTTON; only works on covered square. */
2446 if (v != -2 && v != -1)
2447 return NULL;
2448 sprintf(buf, "F%d,%d", ui->cur_x, ui->cur_y);
2449 return dupstr(buf);
2450 }
2451 /* Otherwise, treat as LEFT_BUTTON, for a single square. */
2452 if (v == -2 || v == -3) {
2453 if (from->layout->mines &&
2454 from->layout->mines[ui->cur_y * from->w + ui->cur_x])
2455 ui->deaths++;
2456
2457 sprintf(buf, "O%d,%d", ui->cur_x, ui->cur_y);
2458 return dupstr(buf);
2459 }
2460 cx = ui->cur_x; cy = ui->cur_y;
2461 ui->validradius = 1;
2462 goto uncover;
2463 }
2464
2465 if (button == LEFT_BUTTON || button == LEFT_DRAG ||
2466 button == MIDDLE_BUTTON || button == MIDDLE_DRAG) {
2467 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2468 return NULL;
2469
2470 /*
2471 * Mouse-downs and mouse-drags just cause highlighting
2472 * updates.
2473 */
2474 ui->hx = cx;
2475 ui->hy = cy;
2476 ui->hradius = (from->grid[cy*from->w+cx] >= 0 ? 1 : 0);
2477 if (button == LEFT_BUTTON)
2478 ui->validradius = ui->hradius;
2479 else if (button == MIDDLE_BUTTON)
2480 ui->validradius = 1;
2481 ui->cur_visible = 0;
2482 return "";
2483 }
2484
2485 if (button == RIGHT_BUTTON) {
2486 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2487 return NULL;
2488
2489 /*
2490 * Right-clicking only works on a covered square, and it
2491 * toggles between -1 (marked as mine) and -2 (not marked
2492 * as mine).
2493 *
2494 * FIXME: question marks.
2495 */
2496 if (from->grid[cy * from->w + cx] != -2 &&
2497 from->grid[cy * from->w + cx] != -1)
2498 return NULL;
2499
2500 sprintf(buf, "F%d,%d", cx, cy);
2501 return dupstr(buf);
2502 }
2503
2504 if (button == LEFT_RELEASE || button == MIDDLE_RELEASE) {
2505 ui->hx = ui->hy = -1;
2506 ui->hradius = 0;
2507
2508 /*
2509 * At this stage we must never return NULL: we have adjusted
2510 * the ui, so at worst we return "".
2511 */
2512 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2513 return "";
2514
2515 /*
2516 * Left-clicking on a covered square opens a tile. Not
2517 * permitted if the tile is marked as a mine, for safety.
2518 * (Unmark it and _then_ open it.)
2519 */
2520 if (button == LEFT_RELEASE &&
2521 (from->grid[cy * from->w + cx] == -2 ||
2522 from->grid[cy * from->w + cx] == -3) &&
2523 ui->validradius == 0) {
2524 /* Check if you've killed yourself. */
2525 if (from->layout->mines && from->layout->mines[cy * from->w + cx])
2526 ui->deaths++;
2527
2528 sprintf(buf, "O%d,%d", cx, cy);
2529 return dupstr(buf);
2530 }
2531 goto uncover;
2532 }
2533 return NULL;
2534
2535uncover:
2536 {
2537 /*
2538 * Left-clicking or middle-clicking on an uncovered tile:
2539 * first we check to see if the number of mine markers
2540 * surrounding the tile is equal to its mine count, and if
2541 * so then we open all other surrounding squares.
2542 */
2543 if (from->grid[cy * from->w + cx] > 0 && ui->validradius == 1) {
2544 int dy, dx, n;
2545
2546 /* Count mine markers. */
2547 n = 0;
2548 for (dy = -1; dy <= +1; dy++)
2549 for (dx = -1; dx <= +1; dx++)
2550 if (cx+dx >= 0 && cx+dx < from->w &&
2551 cy+dy >= 0 && cy+dy < from->h) {
2552 if (from->grid[(cy+dy)*from->w+(cx+dx)] == -1)
2553 n++;
2554 }
2555
2556 if (n == from->grid[cy * from->w + cx]) {
2557
2558 /*
2559 * Now see if any of the squares we're clearing
2560 * contains a mine (which will happen iff you've
2561 * incorrectly marked the mines around the clicked
2562 * square). If so, we open _just_ those squares, to
2563 * reveal as little additional information as we
2564 * can.
2565 */
2566 char *p = buf;
2567 char *sep = "";
2568
2569 for (dy = -1; dy <= +1; dy++)
2570 for (dx = -1; dx <= +1; dx++)
2571 if (cx+dx >= 0 && cx+dx < from->w &&
2572 cy+dy >= 0 && cy+dy < from->h) {
2573 if (from->grid[(cy+dy)*from->w+(cx+dx)] != -1 &&
2574 from->layout->mines &&
2575 from->layout->mines[(cy+dy)*from->w+(cx+dx)]) {
2576 p += sprintf(p, "%sO%d,%d", sep, cx+dx, cy+dy);
2577 sep = ";";
2578 }
2579 }
2580
2581 if (p > buf) {
2582 ui->deaths++;
2583 } else {
2584 sprintf(buf, "C%d,%d", cx, cy);
2585 }
2586
2587 return dupstr(buf);
2588 }
2589 }
2590
2591 return "";
2592 }
2593}
2594
2595static game_state *execute_move(const game_state *from, const char *move)
2596{
2597 int cy, cx;
2598 game_state *ret;
2599
2600 if (!strcmp(move, "S")) {
2601 int yy, xx;
2602
2603 ret = dup_game(from);
2604 if (!ret->dead) {
2605 /*
2606 * If the player is still alive at the moment of pressing
2607 * Solve, expose the entire grid as if it were a completed
2608 * solution.
2609 */
2610 for (yy = 0; yy < ret->h; yy++)
2611 for (xx = 0; xx < ret->w; xx++) {
2612
2613 if (ret->layout->mines[yy*ret->w+xx]) {
2614 ret->grid[yy*ret->w+xx] = -1;
2615 } else {
2616 int dx, dy, v;
2617
2618 v = 0;
2619
2620 for (dx = -1; dx <= +1; dx++)
2621 for (dy = -1; dy <= +1; dy++)
2622 if (xx+dx >= 0 && xx+dx < ret->w &&
2623 yy+dy >= 0 && yy+dy < ret->h &&
2624 ret->layout->mines[(yy+dy)*ret->w+(xx+dx)])
2625 v++;
2626
2627 ret->grid[yy*ret->w+xx] = v;
2628 }
2629 }
2630 } else {
2631 /*
2632 * If the player pressed Solve _after dying_, show a full
2633 * corrections grid in the style of standard Minesweeper.
2634 * Players who don't like Mines's behaviour on death of
2635 * only showing the mine that killed you (so that in case
2636 * of a typo you can undo and carry on without the rest of
2637 * the grid being spoiled) can use this to get the display
2638 * that ordinary Minesweeper would have given them.
2639 */
2640 for (yy = 0; yy < ret->h; yy++)
2641 for (xx = 0; xx < ret->w; xx++) {
2642 int pos = yy*ret->w+xx;
2643 if ((ret->grid[pos] == -2 || ret->grid[pos] == -3) &&
2644 ret->layout->mines[pos]) {
2645 ret->grid[pos] = 64;
2646 } else if (ret->grid[pos] == -1 &&
2647 !ret->layout->mines[pos]) {
2648 ret->grid[pos] = 66;
2649 }
2650 }
2651 }
2652 ret->used_solve = TRUE;
2653
2654 return ret;
2655 } else {
2656 ret = dup_game(from);
2657
2658 while (*move) {
2659 if (move[0] == 'F' &&
2660 sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
2661 cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) {
2662 ret->grid[cy * from->w + cx] ^= (-2 ^ -1);
2663 } else if (move[0] == 'O' &&
2664 sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
2665 cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) {
2666 open_square(ret, cx, cy);
2667 } else if (move[0] == 'C' &&
2668 sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
2669 cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) {
2670 int dx, dy;
2671
2672 for (dy = -1; dy <= +1; dy++)
2673 for (dx = -1; dx <= +1; dx++)
2674 if (cx+dx >= 0 && cx+dx < ret->w &&
2675 cy+dy >= 0 && cy+dy < ret->h &&
2676 (ret->grid[(cy+dy)*ret->w+(cx+dx)] == -2 ||
2677 ret->grid[(cy+dy)*ret->w+(cx+dx)] == -3))
2678 open_square(ret, cx+dx, cy+dy);
2679 } else {
2680 free_game(ret);
2681 return NULL;
2682 }
2683
2684 while (*move && *move != ';') move++;
2685 if (*move) move++;
2686 }
2687
2688 return ret;
2689 }
2690}
2691
2692/* ----------------------------------------------------------------------
2693 * Drawing routines.
2694 */
2695
2696static void game_compute_size(const game_params *params, int tilesize,
2697 int *x, int *y)
2698{
2699 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2700 struct { int tilesize; } ads, *ds = &ads;
2701 ads.tilesize = tilesize;
2702
2703 *x = BORDER * 2 + TILE_SIZE * params->w;
2704 *y = BORDER * 2 + TILE_SIZE * params->h;
2705}
2706
2707static void game_set_size(drawing *dr, game_drawstate *ds,
2708 const game_params *params, int tilesize)
2709{
2710 ds->tilesize = tilesize;
2711}
2712
2713static float *game_colours(frontend *fe, int *ncolours)
2714{
2715 float *ret = snewn(3 * NCOLOURS, float);
2716
2717 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2718
2719 ret[COL_BACKGROUND2 * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 19.0F / 20.0F;
2720 ret[COL_BACKGROUND2 * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 19.0F / 20.0F;
2721 ret[COL_BACKGROUND2 * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 19.0F / 20.0F;
2722
2723 ret[COL_1 * 3 + 0] = 0.0F;
2724 ret[COL_1 * 3 + 1] = 0.0F;
2725 ret[COL_1 * 3 + 2] = 1.0F;
2726
2727 ret[COL_2 * 3 + 0] = 0.0F;
2728 ret[COL_2 * 3 + 1] = 0.5F;
2729 ret[COL_2 * 3 + 2] = 0.0F;
2730
2731 ret[COL_3 * 3 + 0] = 1.0F;
2732 ret[COL_3 * 3 + 1] = 0.0F;
2733 ret[COL_3 * 3 + 2] = 0.0F;
2734
2735 ret[COL_4 * 3 + 0] = 0.0F;
2736 ret[COL_4 * 3 + 1] = 0.0F;
2737 ret[COL_4 * 3 + 2] = 0.5F;
2738
2739 ret[COL_5 * 3 + 0] = 0.5F;
2740 ret[COL_5 * 3 + 1] = 0.0F;
2741 ret[COL_5 * 3 + 2] = 0.0F;
2742
2743 ret[COL_6 * 3 + 0] = 0.0F;
2744 ret[COL_6 * 3 + 1] = 0.5F;
2745 ret[COL_6 * 3 + 2] = 0.5F;
2746
2747 ret[COL_7 * 3 + 0] = 0.0F;
2748 ret[COL_7 * 3 + 1] = 0.0F;
2749 ret[COL_7 * 3 + 2] = 0.0F;
2750
2751 ret[COL_8 * 3 + 0] = 0.5F;
2752 ret[COL_8 * 3 + 1] = 0.5F;
2753 ret[COL_8 * 3 + 2] = 0.5F;
2754
2755 ret[COL_MINE * 3 + 0] = 0.0F;
2756 ret[COL_MINE * 3 + 1] = 0.0F;
2757 ret[COL_MINE * 3 + 2] = 0.0F;
2758
2759 ret[COL_BANG * 3 + 0] = 1.0F;
2760 ret[COL_BANG * 3 + 1] = 0.0F;
2761 ret[COL_BANG * 3 + 2] = 0.0F;
2762
2763 ret[COL_CROSS * 3 + 0] = 1.0F;
2764 ret[COL_CROSS * 3 + 1] = 0.0F;
2765 ret[COL_CROSS * 3 + 2] = 0.0F;
2766
2767 ret[COL_FLAG * 3 + 0] = 1.0F;
2768 ret[COL_FLAG * 3 + 1] = 0.0F;
2769 ret[COL_FLAG * 3 + 2] = 0.0F;
2770
2771 ret[COL_FLAGBASE * 3 + 0] = 0.0F;
2772 ret[COL_FLAGBASE * 3 + 1] = 0.0F;
2773 ret[COL_FLAGBASE * 3 + 2] = 0.0F;
2774
2775 ret[COL_QUERY * 3 + 0] = 0.0F;
2776 ret[COL_QUERY * 3 + 1] = 0.0F;
2777 ret[COL_QUERY * 3 + 2] = 0.0F;
2778
2779 ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
2780 ret[COL_HIGHLIGHT * 3 + 1] = 1.0F;
2781 ret[COL_HIGHLIGHT * 3 + 2] = 1.0F;
2782
2783 ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F;
2784 ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F;
2785 ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F;
2786
2787 ret[COL_WRONGNUMBER * 3 + 0] = 1.0F;
2788 ret[COL_WRONGNUMBER * 3 + 1] = 0.6F;
2789 ret[COL_WRONGNUMBER * 3 + 2] = 0.6F;
2790
2791 /* Red tinge to a light colour, for the cursor. */
2792 ret[COL_CURSOR * 3 + 0] = ret[COL_HIGHLIGHT * 3 + 0];
2793 ret[COL_CURSOR * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 0] / 2.0F;
2794 ret[COL_CURSOR * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 0] / 2.0F;
2795
2796 *ncolours = NCOLOURS;
2797 return ret;
2798}
2799
2800static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2801{
2802 struct game_drawstate *ds = snew(struct game_drawstate);
2803
2804 ds->w = state->w;
2805 ds->h = state->h;
2806 ds->started = FALSE;
2807 ds->tilesize = 0; /* not decided yet */
2808 ds->grid = snewn(ds->w * ds->h, signed char);
2809 ds->bg = -1;
2810 ds->cur_x = ds->cur_y = -1;
2811
2812 memset(ds->grid, -99, ds->w * ds->h);
2813
2814 return ds;
2815}
2816
2817static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2818{
2819 sfree(ds->grid);
2820 sfree(ds);
2821}
2822
2823static void draw_tile(drawing *dr, game_drawstate *ds,
2824 int x, int y, int v, int bg)
2825{
2826 if (v < 0) {
2827 int coords[12];
2828 int hl = 0;
2829
2830 if (v == -22 || v == -23) {
2831 v += 20;
2832
2833 /*
2834 * Omit the highlights in this case.
2835 */
2836 draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
2837 bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg);
2838 draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
2839 draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
2840 } else {
2841 /*
2842 * Draw highlights to indicate the square is covered.
2843 */
2844 coords[0] = x + TILE_SIZE - 1;
2845 coords[1] = y + TILE_SIZE - 1;
2846 coords[2] = x + TILE_SIZE - 1;
2847 coords[3] = y;
2848 coords[4] = x;
2849 coords[5] = y + TILE_SIZE - 1;
2850 draw_polygon(dr, coords, 3, COL_LOWLIGHT ^ hl, COL_LOWLIGHT ^ hl);
2851
2852 coords[0] = x;
2853 coords[1] = y;
2854 draw_polygon(dr, coords, 3, COL_HIGHLIGHT ^ hl,
2855 COL_HIGHLIGHT ^ hl);
2856
2857 draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
2858 TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
2859 bg);
2860 }
2861
2862 if (v == -1) {
2863 /*
2864 * Draw a flag.
2865 */
2866#define SETCOORD(n, dx, dy) do { \
2867 coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \
2868 coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \
2869} while (0)
2870 SETCOORD(0, 0.6F, 0.35F);
2871 SETCOORD(1, 0.6F, 0.7F);
2872 SETCOORD(2, 0.8F, 0.8F);
2873 SETCOORD(3, 0.25F, 0.8F);
2874 SETCOORD(4, 0.55F, 0.7F);
2875 SETCOORD(5, 0.55F, 0.35F);
2876 draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE);
2877
2878 SETCOORD(0, 0.6F, 0.2F);
2879 SETCOORD(1, 0.6F, 0.5F);
2880 SETCOORD(2, 0.2F, 0.35F);
2881 draw_polygon(dr, coords, 3, COL_FLAG, COL_FLAG);
2882#undef SETCOORD
2883
2884 } else if (v == -3) {
2885 /*
2886 * Draw a question mark.
2887 */
2888 draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
2889 FONT_VARIABLE, TILE_SIZE * 6 / 8,
2890 ALIGN_VCENTRE | ALIGN_HCENTRE,
2891 COL_QUERY, "?");
2892 }
2893 } else {
2894 /*
2895 * Clear the square to the background colour, and draw thin
2896 * grid lines along the top and left.
2897 *
2898 * Exception is that for value 65 (mine we've just trodden
2899 * on), we clear the square to COL_BANG.
2900 */
2901 if (v & 32) {
2902 bg = COL_WRONGNUMBER;
2903 v &= ~32;
2904 }
2905 draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
2906 (v == 65 ? COL_BANG :
2907 bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg));
2908 draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT);
2909 draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT);
2910
2911 if (v > 0 && v <= 8) {
2912 /*
2913 * Mark a number.
2914 */
2915 char str[2];
2916 str[0] = v + '0';
2917 str[1] = '\0';
2918 draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2,
2919 FONT_VARIABLE, TILE_SIZE * 7 / 8,
2920 ALIGN_VCENTRE | ALIGN_HCENTRE,
2921 (COL_1 - 1) + v, str);
2922
2923 } else if (v >= 64) {
2924 /*
2925 * Mark a mine.
2926 */
2927 {
2928 int cx = x + TILE_SIZE / 2;
2929 int cy = y + TILE_SIZE / 2;
2930 int r = TILE_SIZE / 2 - 3;
2931
2932 draw_circle(dr, cx, cy, 5*r/6, COL_MINE, COL_MINE);
2933 draw_rect(dr, cx - r/6, cy - r, 2*(r/6)+1, 2*r+1, COL_MINE);
2934 draw_rect(dr, cx - r, cy - r/6, 2*r+1, 2*(r/6)+1, COL_MINE);
2935 draw_rect(dr, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT);
2936 }
2937
2938 if (v == 66) {
2939 /*
2940 * Cross through the mine.
2941 */
2942 int dx;
2943 for (dx = -1; dx <= +1; dx++) {
2944 draw_line(dr, x + 3 + dx, y + 2,
2945 x + TILE_SIZE - 3 + dx,
2946 y + TILE_SIZE - 2, COL_CROSS);
2947 draw_line(dr, x + TILE_SIZE - 3 + dx, y + 2,
2948 x + 3 + dx, y + TILE_SIZE - 2,
2949 COL_CROSS);
2950 }
2951 }
2952 }
2953 }
2954
2955 draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
2956}
2957
2958static void game_redraw(drawing *dr, game_drawstate *ds,
2959 const game_state *oldstate, const game_state *state,
2960 int dir, const game_ui *ui,
2961 float animtime, float flashtime)
2962{
2963 int x, y;
2964 int mines, markers, bg;
2965 int cx = -1, cy = -1, cmoved;
2966
2967 if (flashtime) {
2968 int frame = (int)(flashtime / FLASH_FRAME);
2969 if (frame % 2)
2970 bg = (ui->flash_is_death ? COL_BACKGROUND : COL_LOWLIGHT);
2971 else
2972 bg = (ui->flash_is_death ? COL_BANG : COL_HIGHLIGHT);
2973 } else
2974 bg = COL_BACKGROUND;
2975
2976 if (!ds->started) {
2977 int coords[10];
2978
2979 draw_rect(dr, 0, 0,
2980 TILE_SIZE * state->w + 2 * BORDER,
2981 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
2982 draw_update(dr, 0, 0,
2983 TILE_SIZE * state->w + 2 * BORDER,
2984 TILE_SIZE * state->h + 2 * BORDER);
2985
2986 /*
2987 * Recessed area containing the whole puzzle.
2988 */
2989 coords[0] = COORD(state->w) + OUTER_HIGHLIGHT_WIDTH - 1;
2990 coords[1] = COORD(state->h) + OUTER_HIGHLIGHT_WIDTH - 1;
2991 coords[2] = COORD(state->w) + OUTER_HIGHLIGHT_WIDTH - 1;
2992 coords[3] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
2993 coords[4] = coords[2] - TILE_SIZE;
2994 coords[5] = coords[3] + TILE_SIZE;
2995 coords[8] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
2996 coords[9] = COORD(state->h) + OUTER_HIGHLIGHT_WIDTH - 1;
2997 coords[6] = coords[8] + TILE_SIZE;
2998 coords[7] = coords[9] - TILE_SIZE;
2999 draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
3000
3001 coords[1] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
3002 coords[0] = COORD(0) - OUTER_HIGHLIGHT_WIDTH;
3003 draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
3004
3005 ds->started = TRUE;
3006 }
3007
3008 if (ui->cur_visible) cx = ui->cur_x;
3009 if (ui->cur_visible) cy = ui->cur_y;
3010 cmoved = (cx != ds->cur_x || cy != ds->cur_y);
3011
3012 /*
3013 * Now draw the tiles. Also in this loop, count up the number
3014 * of mines and mine markers.
3015 */
3016 mines = markers = 0;
3017 for (y = 0; y < ds->h; y++)
3018 for (x = 0; x < ds->w; x++) {
3019 int v = state->grid[y*ds->w+x], cc = 0;
3020
3021 if (v == -1)
3022 markers++;
3023 if (state->layout->mines && state->layout->mines[y*ds->w+x])
3024 mines++;
3025
3026 if (v >= 0 && v <= 8) {
3027 /*
3028 * Count up the flags around this tile, and if
3029 * there are too _many_, highlight the tile.
3030 */
3031 int dx, dy, flags = 0;
3032
3033 for (dy = -1; dy <= +1; dy++)
3034 for (dx = -1; dx <= +1; dx++) {
3035 int nx = x+dx, ny = y+dy;
3036 if (nx >= 0 && nx < ds->w &&
3037 ny >= 0 && ny < ds->h &&
3038 state->grid[ny*ds->w+nx] == -1)
3039 flags++;
3040 }
3041
3042 if (flags > v)
3043 v |= 32;
3044 }
3045
3046 if ((v == -2 || v == -3) &&
3047 (abs(x-ui->hx) <= ui->hradius && abs(y-ui->hy) <= ui->hradius))
3048 v -= 20;
3049
3050 if (cmoved && /* if cursor has moved, force redraw of curr and prev pos */
3051 ((x == cx && y == cy) || (x == ds->cur_x && y == ds->cur_y)))
3052 cc = 1;
3053
3054 if (ds->grid[y*ds->w+x] != v || bg != ds->bg || cc) {
3055 draw_tile(dr, ds, COORD(x), COORD(y), v,
3056 (x == cx && y == cy) ? COL_CURSOR : bg);
3057 ds->grid[y*ds->w+x] = v;
3058 }
3059 }
3060 ds->bg = bg;
3061 ds->cur_x = cx; ds->cur_y = cy;
3062
3063 if (!state->layout->mines)
3064 mines = state->layout->n;
3065
3066 /*
3067 * Update the status bar.
3068 */
3069 {
3070 char statusbar[512];
3071 if (state->dead) {
3072 sprintf(statusbar, "DEAD!");
3073 } else if (state->won) {
3074 if (state->used_solve)
3075 sprintf(statusbar, "Auto-solved.");
3076 else
3077 sprintf(statusbar, "COMPLETED!");
3078 } else {
3079 sprintf(statusbar, "Marked: %d / %d", markers, mines);
3080 }
3081 if (ui->deaths)
3082 sprintf(statusbar + strlen(statusbar),
3083 " Deaths: %d", ui->deaths);
3084 status_bar(dr, statusbar);
3085 }
3086}
3087
3088static float game_anim_length(const game_state *oldstate,
3089 const game_state *newstate, int dir, game_ui *ui)
3090{
3091 return 0.0F;
3092}
3093
3094static float game_flash_length(const game_state *oldstate,
3095 const game_state *newstate, int dir, game_ui *ui)
3096{
3097 if (oldstate->used_solve || newstate->used_solve)
3098 return 0.0F;
3099
3100 if (dir > 0 && !oldstate->dead && !oldstate->won) {
3101 if (newstate->dead) {
3102 ui->flash_is_death = TRUE;
3103 return 3 * FLASH_FRAME;
3104 }
3105 if (newstate->won) {
3106 ui->flash_is_death = FALSE;
3107 return 2 * FLASH_FRAME;
3108 }
3109 }
3110 return 0.0F;
3111}
3112
3113static int game_status(const game_state *state)
3114{
3115 /*
3116 * We report the game as lost only if the player has used the
3117 * Solve function to reveal all the mines. Otherwise, we assume
3118 * they'll undo and continue play.
3119 */
3120 return state->won ? (state->used_solve ? -1 : +1) : 0;
3121}
3122
3123static int game_timing_state(const game_state *state, game_ui *ui)
3124{
3125 if (state->dead || state->won || ui->completed || !state->layout->mines)
3126 return FALSE;
3127 return TRUE;
3128}
3129
3130static void game_print_size(const game_params *params, float *x, float *y)
3131{
3132}
3133
3134static void game_print(drawing *dr, const game_state *state, int tilesize)
3135{
3136}
3137
3138#ifdef COMBINED
3139#define thegame mines
3140#endif
3141
3142const struct game thegame = {
3143 "Mines", "games.mines", "mines",
3144 default_params,
3145 game_fetch_preset, NULL,
3146 decode_params,
3147 encode_params,
3148 free_params,
3149 dup_params,
3150 TRUE, game_configure, custom_params,
3151 validate_params,
3152 new_game_desc,
3153 validate_desc,
3154 new_game,
3155 dup_game,
3156 free_game,
3157 TRUE, solve_game,
3158 TRUE, game_can_format_as_text_now, game_text_format,
3159 new_ui,
3160 free_ui,
3161 encode_ui,
3162 decode_ui,
3163 game_changed_state,
3164 interpret_move,
3165 execute_move,
3166 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3167 game_colours,
3168 game_new_drawstate,
3169 game_free_drawstate,
3170 game_redraw,
3171 game_anim_length,
3172 game_flash_length,
3173 game_status,
3174 FALSE, FALSE, game_print_size, game_print,
3175 TRUE, /* wants_statusbar */
3176 TRUE, game_timing_state,
3177 BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON,
3178};
3179
3180#ifdef STANDALONE_OBFUSCATOR
3181
3182/*
3183 * Vaguely useful stand-alone program which translates between
3184 * obfuscated and clear Mines game descriptions. Pass in a game
3185 * description on the command line, and if it's clear it will be
3186 * obfuscated and vice versa. The output text should also be a
3187 * valid game ID describing the same game. Like this:
3188 *
3189 * $ ./mineobfusc 9x9:4,4,mb071b49fbd1cb6a0d5868
3190 * 9x9:4,4,004000007c00010022080
3191 * $ ./mineobfusc 9x9:4,4,004000007c00010022080
3192 * 9x9:4,4,mb071b49fbd1cb6a0d5868
3193 */
3194
3195int main(int argc, char **argv)
3196{
3197 game_params *p;
3198 game_state *s;
3199 char *id = NULL, *desc, *err;
3200 int y, x;
3201
3202 while (--argc > 0) {
3203 char *p = *++argv;
3204 if (*p == '-') {
3205 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
3206 return 1;
3207 } else {
3208 id = p;
3209 }
3210 }
3211
3212 if (!id) {
3213 fprintf(stderr, "usage: %s <game_id>\n", argv[0]);
3214 return 1;
3215 }
3216
3217 desc = strchr(id, ':');
3218 if (!desc) {
3219 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
3220 return 1;
3221 }
3222 *desc++ = '\0';
3223
3224 p = default_params();
3225 decode_params(p, id);
3226 err = validate_desc(p, desc);
3227 if (err) {
3228 fprintf(stderr, "%s: %s\n", argv[0], err);
3229 return 1;
3230 }
3231 s = new_game(NULL, p, desc);
3232
3233 x = atoi(desc);
3234 while (*desc && *desc != ',') desc++;
3235 if (*desc) desc++;
3236 y = atoi(desc);
3237 while (*desc && *desc != ',') desc++;
3238 if (*desc) desc++;
3239
3240 printf("%s:%s\n", id, describe_layout(s->layout->mines,
3241 p->w * p->h,
3242 x, y,
3243 (*desc != 'm')));
3244
3245 return 0;
3246}
3247
3248#endif
3249
3250/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/mines.html b/apps/plugins/puzzles/src/mines.html
new file mode 100644
index 0000000000..15c8b3e9c8
--- /dev/null
+++ b/apps/plugins/puzzles/src/mines.html
@@ -0,0 +1,82 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Mines</title>
7<link rel="previous" href="solo.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="samegame.html">
12</head>
13<body>
14<p><a href="solo.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="samegame.html">Next</a></p>
15<h1><a name="C12"></a>Chapter 12: <a name="i0"></a>Mines</h1>
16<p>
17You have a grid of covered squares, some of which contain mines, but you don't know which. Your job is to uncover every square which does <em>not</em> contain a mine. If you uncover a square containing a mine, you lose. If you uncover a square which does not contain a mine, you are told how many mines are contained within the eight surrounding squares.
18</p>
19<p>
20This game needs no introduction; popularised by Windows, it is perhaps the single best known desktop puzzle game in existence.
21</p>
22<p>
23This version of it has an unusual property. By default, it will generate its mine positions in such a way as to ensure that you never need to <em>guess</em> where a mine is: you will always be able to deduce it somehow. So you will never, as can happen in other versions, get to the last four squares and discover that there are two mines left but you have no way of knowing for sure where they are.
24</p>
25<h2><a name="S12.1"></a>12.1 <a name="i1"></a>Mines controls</h2>
26<p>
27This game is played with the mouse.
28</p>
29<p>
30If you left-click in a covered square, it will be uncovered.
31</p>
32<p>
33If you right-click in a covered square, it will place a flag which indicates that the square is believed to be a mine. Left-clicking in a marked square will not uncover it, for safety. You can right-click again to remove a mark placed in error.
34</p>
35<p>
36If you left-click in an <em>uncovered</em> square, it will &#8216;clear around&#8217; the square. This means: if the square has exactly as many flags surrounding it as it should have mines, then all the covered squares next to it which are <em>not</em> flagged will be uncovered. So once you think you know the location of all the mines around a square, you can use this function as a shortcut to avoid having to click on each of the remaining squares one by one.
37</p>
38<p>
39If you uncover a square which has <em>no</em> mines in the surrounding eight squares, then it is obviously safe to uncover those squares in turn, and so on if any of them also has no surrounding mines. This will be done for you automatically; so sometimes when you uncover a square, a whole new area will open up to be explored.
40</p>
41<p>
42You can also use the cursor keys to move around the minefield. Pressing the return key in a covered square uncovers it, and in an uncovered square will clear around it (so it acts as the left button), pressing the space bar in a covered square will place a flag (similarly, it acts as the right button).
43</p>
44<p>
45All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.
46</p>
47<p>
48Even Undo is available, although you might consider it cheating to use it. If you step on a mine, the program will only reveal the mine in question (unlike most other implementations, which reveal all of them). You can then Undo your fatal move and continue playing if you like. The program will track the number of times you died (and Undo will not reduce that counter), so when you get to the end of the game you know whether or not you did it without making any errors.
49</p>
50<p>
51(If you really want to know the full layout of the grid, which other implementations will show you after you die, you can always use the Solve menu option.)
52</p>
53<h2><a name="S12.2"></a>12.2 <a name="i2"></a>Mines parameters</h2>
54<p>
55The options available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu are:
56</p>
57<dl><dt>
58<em>Width</em>, <em>Height</em>
59</dt>
60<dd>
61Size of grid in squares.
62</dd>
63<dt>
64<em>Mines</em>
65</dt>
66<dd>
67Number of mines in the grid. You can enter this as an absolute mine count, or alternatively you can put a <code>%</code> sign on the end in which case the game will arrange for that proportion of the squares in the grid to be mines.
68<p>
69Beware of setting the mine count too high. At very high densities, the program may spend forever searching for a solvable grid.
70</p>
71
72</dd>
73<dt>
74<em>Ensure solubility</em>
75</dt>
76<dd>
77When this option is enabled (as it is by default), Mines will ensure that the entire grid can be fully deduced starting from the initial open space. If you prefer the riskier grids generated by other implementations, you can switch off this option.
78</dd>
79</dl>
80
81<hr><address></address></body>
82</html>
diff --git a/apps/plugins/puzzles/src/misc.c b/apps/plugins/puzzles/src/misc.c
new file mode 100644
index 0000000000..c721016563
--- /dev/null
+++ b/apps/plugins/puzzles/src/misc.c
@@ -0,0 +1,376 @@
1/*
2 * misc.c: Miscellaneous helpful functions.
3 */
4
5#include <assert.h>
6#include <stdlib.h>
7#include <string.h>
8#include <stdio.h>
9
10#include "puzzles.h"
11
12void free_cfg(config_item *cfg)
13{
14 config_item *i;
15
16 for (i = cfg; i->type != C_END; i++)
17 if (i->type == C_STRING)
18 sfree(i->sval);
19 sfree(cfg);
20}
21
22/*
23 * The Mines (among others) game descriptions contain the location of every
24 * mine, and can therefore be used to cheat.
25 *
26 * It would be pointless to attempt to _prevent_ this form of
27 * cheating by encrypting the description, since Mines is
28 * open-source so anyone can find out the encryption key. However,
29 * I think it is worth doing a bit of gentle obfuscation to prevent
30 * _accidental_ spoilers: if you happened to note that the game ID
31 * starts with an F, for example, you might be unable to put the
32 * knowledge of those mines out of your mind while playing. So,
33 * just as discussions of film endings are rot13ed to avoid
34 * spoiling it for people who don't want to be told, we apply a
35 * keyless, reversible, but visually completely obfuscatory masking
36 * function to the mine bitmap.
37 */
38void obfuscate_bitmap(unsigned char *bmp, int bits, int decode)
39{
40 int bytes, firsthalf, secondhalf;
41 struct step {
42 unsigned char *seedstart;
43 int seedlen;
44 unsigned char *targetstart;
45 int targetlen;
46 } steps[2];
47 int i, j;
48
49 /*
50 * My obfuscation algorithm is similar in concept to the OAEP
51 * encoding used in some forms of RSA. Here's a specification
52 * of it:
53 *
54 * + We have a `masking function' which constructs a stream of
55 * pseudorandom bytes from a seed of some number of input
56 * bytes.
57 *
58 * + We pad out our input bit stream to a whole number of
59 * bytes by adding up to 7 zero bits on the end. (In fact
60 * the bitmap passed as input to this function will already
61 * have had this done in practice.)
62 *
63 * + We divide the _byte_ stream exactly in half, rounding the
64 * half-way position _down_. So an 81-bit input string, for
65 * example, rounds up to 88 bits or 11 bytes, and then
66 * dividing by two gives 5 bytes in the first half and 6 in
67 * the second half.
68 *
69 * + We generate a mask from the second half of the bytes, and
70 * XOR it over the first half.
71 *
72 * + We generate a mask from the (encoded) first half of the
73 * bytes, and XOR it over the second half. Any null bits at
74 * the end which were added as padding are cleared back to
75 * zero even if this operation would have made them nonzero.
76 *
77 * To de-obfuscate, the steps are precisely the same except
78 * that the final two are reversed.
79 *
80 * Finally, our masking function. Given an input seed string of
81 * bytes, the output mask consists of concatenating the SHA-1
82 * hashes of the seed string and successive decimal integers,
83 * starting from 0.
84 */
85
86 bytes = (bits + 7) / 8;
87 firsthalf = bytes / 2;
88 secondhalf = bytes - firsthalf;
89
90 steps[decode ? 1 : 0].seedstart = bmp + firsthalf;
91 steps[decode ? 1 : 0].seedlen = secondhalf;
92 steps[decode ? 1 : 0].targetstart = bmp;
93 steps[decode ? 1 : 0].targetlen = firsthalf;
94
95 steps[decode ? 0 : 1].seedstart = bmp;
96 steps[decode ? 0 : 1].seedlen = firsthalf;
97 steps[decode ? 0 : 1].targetstart = bmp + firsthalf;
98 steps[decode ? 0 : 1].targetlen = secondhalf;
99
100 for (i = 0; i < 2; i++) {
101 SHA_State base, final;
102 unsigned char digest[20];
103 char numberbuf[80];
104 int digestpos = 20, counter = 0;
105
106 SHA_Init(&base);
107 SHA_Bytes(&base, steps[i].seedstart, steps[i].seedlen);
108
109 for (j = 0; j < steps[i].targetlen; j++) {
110 if (digestpos >= 20) {
111 sprintf(numberbuf, "%d", counter++);
112 final = base;
113 SHA_Bytes(&final, numberbuf, strlen(numberbuf));
114 SHA_Final(&final, digest);
115 digestpos = 0;
116 }
117 steps[i].targetstart[j] ^= digest[digestpos++];
118 }
119
120 /*
121 * Mask off the pad bits in the final byte after both steps.
122 */
123 if (bits % 8)
124 bmp[bits / 8] &= 0xFF & (0xFF00 >> (bits % 8));
125 }
126}
127
128/* err, yeah, these two pretty much rely on unsigned char being 8 bits.
129 * Platforms where this is not the case probably have bigger problems
130 * than just making these two work, though... */
131char *bin2hex(const unsigned char *in, int inlen)
132{
133 char *ret = snewn(inlen*2 + 1, char), *p = ret;
134 int i;
135
136 for (i = 0; i < inlen*2; i++) {
137 int v = in[i/2];
138 if (i % 2 == 0) v >>= 4;
139 *p++ = "0123456789abcdef"[v & 0xF];
140 }
141 *p = '\0';
142 return ret;
143}
144
145unsigned char *hex2bin(const char *in, int outlen)
146{
147 unsigned char *ret = snewn(outlen, unsigned char);
148 int i;
149
150 memset(ret, 0, outlen*sizeof(unsigned char));
151 for (i = 0; i < outlen*2; i++) {
152 int c = in[i];
153 int v;
154
155 assert(c != 0);
156 if (c >= '0' && c <= '9')
157 v = c - '0';
158 else if (c >= 'a' && c <= 'f')
159 v = c - 'a' + 10;
160 else if (c >= 'A' && c <= 'F')
161 v = c - 'A' + 10;
162 else
163 v = 0;
164
165 ret[i / 2] |= v << (4 * (1 - (i % 2)));
166 }
167 return ret;
168}
169
170void game_mkhighlight_specific(frontend *fe, float *ret,
171 int background, int highlight, int lowlight)
172{
173 float max;
174 int i;
175
176 /*
177 * Drop the background colour so that the highlight is
178 * noticeably brighter than it while still being under 1.
179 */
180 max = ret[background*3];
181 for (i = 1; i < 3; i++)
182 if (ret[background*3+i] > max)
183 max = ret[background*3+i];
184 if (max * 1.2F > 1.0F) {
185 for (i = 0; i < 3; i++)
186 ret[background*3+i] /= (max * 1.2F);
187 }
188
189 for (i = 0; i < 3; i++) {
190 if (highlight >= 0)
191 ret[highlight * 3 + i] = ret[background * 3 + i] * 1.2F;
192 if (lowlight >= 0)
193 ret[lowlight * 3 + i] = ret[background * 3 + i] * 0.8F;
194 }
195}
196
197void game_mkhighlight(frontend *fe, float *ret,
198 int background, int highlight, int lowlight)
199{
200 frontend_default_colour(fe, &ret[background * 3]);
201 game_mkhighlight_specific(fe, ret, background, highlight, lowlight);
202}
203
204static void memswap(void *av, void *bv, int size)
205{
206 char tmpbuf[512];
207 char *a = av, *b = bv;
208
209 while (size > 0) {
210 int thislen = min(size, sizeof(tmpbuf));
211 memcpy(tmpbuf, a, thislen);
212 memcpy(a, b, thislen);
213 memcpy(b, tmpbuf, thislen);
214 a += thislen;
215 b += thislen;
216 size -= thislen;
217 }
218}
219
220void shuffle(void *array, int nelts, int eltsize, random_state *rs)
221{
222 char *carray = (char *)array;
223 int i;
224
225 for (i = nelts; i-- > 1 ;) {
226 int j = random_upto(rs, i+1);
227 if (j != i)
228 memswap(carray + eltsize * i, carray + eltsize * j, eltsize);
229 }
230}
231
232void draw_rect_outline(drawing *dr, int x, int y, int w, int h, int colour)
233{
234 int x0 = x, x1 = x+w-1, y0 = y, y1 = y+h-1;
235 int coords[8];
236
237 coords[0] = x0;
238 coords[1] = y0;
239 coords[2] = x0;
240 coords[3] = y1;
241 coords[4] = x1;
242 coords[5] = y1;
243 coords[6] = x1;
244 coords[7] = y0;
245
246 draw_polygon(dr, coords, 4, -1, colour);
247}
248
249void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col)
250{
251 draw_line(dr, cx - r, cy - r, cx - r, cy - r/2, col);
252 draw_line(dr, cx - r, cy - r, cx - r/2, cy - r, col);
253 draw_line(dr, cx - r, cy + r, cx - r, cy + r/2, col);
254 draw_line(dr, cx - r, cy + r, cx - r/2, cy + r, col);
255 draw_line(dr, cx + r, cy - r, cx + r, cy - r/2, col);
256 draw_line(dr, cx + r, cy - r, cx + r/2, cy - r, col);
257 draw_line(dr, cx + r, cy + r, cx + r, cy + r/2, col);
258 draw_line(dr, cx + r, cy + r, cx + r/2, cy + r, col);
259}
260
261void move_cursor(int button, int *x, int *y, int maxw, int maxh, int wrap)
262{
263 int dx = 0, dy = 0;
264 switch (button) {
265 case CURSOR_UP: dy = -1; break;
266 case CURSOR_DOWN: dy = 1; break;
267 case CURSOR_RIGHT: dx = 1; break;
268 case CURSOR_LEFT: dx = -1; break;
269 default: return;
270 }
271 if (wrap) {
272 *x = (*x + dx + maxw) % maxw;
273 *y = (*y + dy + maxh) % maxh;
274 } else {
275 *x = min(max(*x+dx, 0), maxw - 1);
276 *y = min(max(*y+dy, 0), maxh - 1);
277 }
278}
279
280/* Used in netslide.c and sixteen.c for cursor movement around edge. */
281
282int c2pos(int w, int h, int cx, int cy)
283{
284 if (cy == -1)
285 return cx; /* top row, 0 .. w-1 (->) */
286 else if (cx == w)
287 return w + cy; /* R col, w .. w+h -1 (v) */
288 else if (cy == h)
289 return w + h + (w-cx-1); /* bottom row, w+h .. w+h+w-1 (<-) */
290 else if (cx == -1)
291 return w + h + w + (h-cy-1); /* L col, w+h+w .. w+h+w+h-1 (^) */
292
293 assert(!"invalid cursor pos!");
294 return -1; /* not reached */
295}
296
297int c2diff(int w, int h, int cx, int cy, int button)
298{
299 int diff = 0;
300
301 assert(IS_CURSOR_MOVE(button));
302
303 /* Obvious moves around edge. */
304 if (cy == -1)
305 diff = (button == CURSOR_RIGHT) ? +1 : (button == CURSOR_LEFT) ? -1 : diff;
306 if (cy == h)
307 diff = (button == CURSOR_RIGHT) ? -1 : (button == CURSOR_LEFT) ? +1 : diff;
308 if (cx == -1)
309 diff = (button == CURSOR_UP) ? +1 : (button == CURSOR_DOWN) ? -1 : diff;
310 if (cx == w)
311 diff = (button == CURSOR_UP) ? -1 : (button == CURSOR_DOWN) ? +1 : diff;
312
313 if (button == CURSOR_LEFT && cx == w && (cy == 0 || cy == h-1))
314 diff = (cy == 0) ? -1 : +1;
315 if (button == CURSOR_RIGHT && cx == -1 && (cy == 0 || cy == h-1))
316 diff = (cy == 0) ? +1 : -1;
317 if (button == CURSOR_DOWN && cy == -1 && (cx == 0 || cx == w-1))
318 diff = (cx == 0) ? -1 : +1;
319 if (button == CURSOR_UP && cy == h && (cx == 0 || cx == w-1))
320 diff = (cx == 0) ? +1 : -1;
321
322 debug(("cx,cy = %d,%d; w%d h%d, diff = %d", cx, cy, w, h, diff));
323 return diff;
324}
325
326void pos2c(int w, int h, int pos, int *cx, int *cy)
327{
328 int max = w+h+w+h;
329
330 pos = (pos + max) % max;
331
332 if (pos < w) {
333 *cx = pos; *cy = -1; return;
334 }
335 pos -= w;
336 if (pos < h) {
337 *cx = w; *cy = pos; return;
338 }
339 pos -= h;
340 if (pos < w) {
341 *cx = w-pos-1; *cy = h; return;
342 }
343 pos -= w;
344 if (pos < h) {
345 *cx = -1; *cy = h-pos-1; return;
346 }
347 assert(!"invalid pos, huh?"); /* limited by % above! */
348}
349
350void draw_text_outline(drawing *dr, int x, int y, int fonttype,
351 int fontsize, int align,
352 int text_colour, int outline_colour, char *text)
353{
354 if (outline_colour > -1) {
355 draw_text(dr, x-1, y, fonttype, fontsize, align, outline_colour, text);
356 draw_text(dr, x+1, y, fonttype, fontsize, align, outline_colour, text);
357 draw_text(dr, x, y-1, fonttype, fontsize, align, outline_colour, text);
358 draw_text(dr, x, y+1, fonttype, fontsize, align, outline_colour, text);
359 }
360 draw_text(dr, x, y, fonttype, fontsize, align, text_colour, text);
361
362}
363
364/* kludge for non-compliant sprintf() */
365void copy_left_justified(char *buf, size_t sz, const char *str)
366{
367 memset(buf, ' ', sz - 1);
368 int len = strlen(str);
369 if(len <= sz - 1)
370 memcpy(buf, str, len);
371 else
372 fatal("overrun");
373 buf[sz - 1] = 0;
374}
375
376/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/missing b/apps/plugins/puzzles/src/missing
new file mode 100755
index 0000000000..f62bbae306
--- /dev/null
+++ b/apps/plugins/puzzles/src/missing
@@ -0,0 +1,215 @@
1#! /bin/sh
2# Common wrapper for a few potentially missing GNU programs.
3
4scriptversion=2013-10-28.13; # UTC
5
6# Copyright (C) 1996-2014 Free Software Foundation, Inc.
7# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
8
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2, or (at your option)
12# any later version.
13
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22# As a special exception to the GNU General Public License, if you
23# distribute this file as part of a program that contains a
24# configuration script generated by Autoconf, you may include it under
25# the same distribution terms that you use for the rest of that program.
26
27if test $# -eq 0; then
28 echo 1>&2 "Try '$0 --help' for more information"
29 exit 1
30fi
31
32case $1 in
33
34 --is-lightweight)
35 # Used by our autoconf macros to check whether the available missing
36 # script is modern enough.
37 exit 0
38 ;;
39
40 --run)
41 # Back-compat with the calling convention used by older automake.
42 shift
43 ;;
44
45 -h|--h|--he|--hel|--help)
46 echo "\
47$0 [OPTION]... PROGRAM [ARGUMENT]...
48
49Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
50to PROGRAM being missing or too old.
51
52Options:
53 -h, --help display this help and exit
54 -v, --version output version information and exit
55
56Supported PROGRAM values:
57 aclocal autoconf autoheader autom4te automake makeinfo
58 bison yacc flex lex help2man
59
60Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
61'g' are ignored when checking the name.
62
63Send bug reports to <bug-automake@gnu.org>."
64 exit $?
65 ;;
66
67 -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
68 echo "missing $scriptversion (GNU Automake)"
69 exit $?
70 ;;
71
72 -*)
73 echo 1>&2 "$0: unknown '$1' option"
74 echo 1>&2 "Try '$0 --help' for more information"
75 exit 1
76 ;;
77
78esac
79
80# Run the given program, remember its exit status.
81"$@"; st=$?
82
83# If it succeeded, we are done.
84test $st -eq 0 && exit 0
85
86# Also exit now if we it failed (or wasn't found), and '--version' was
87# passed; such an option is passed most likely to detect whether the
88# program is present and works.
89case $2 in --version|--help) exit $st;; esac
90
91# Exit code 63 means version mismatch. This often happens when the user
92# tries to use an ancient version of a tool on a file that requires a
93# minimum version.
94if test $st -eq 63; then
95 msg="probably too old"
96elif test $st -eq 127; then
97 # Program was missing.
98 msg="missing on your system"
99else
100 # Program was found and executed, but failed. Give up.
101 exit $st
102fi
103
104perl_URL=http://www.perl.org/
105flex_URL=http://flex.sourceforge.net/
106gnu_software_URL=http://www.gnu.org/software
107
108program_details ()
109{
110 case $1 in
111 aclocal|automake)
112 echo "The '$1' program is part of the GNU Automake package:"
113 echo "<$gnu_software_URL/automake>"
114 echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
115 echo "<$gnu_software_URL/autoconf>"
116 echo "<$gnu_software_URL/m4/>"
117 echo "<$perl_URL>"
118 ;;
119 autoconf|autom4te|autoheader)
120 echo "The '$1' program is part of the GNU Autoconf package:"
121 echo "<$gnu_software_URL/autoconf/>"
122 echo "It also requires GNU m4 and Perl in order to run:"
123 echo "<$gnu_software_URL/m4/>"
124 echo "<$perl_URL>"
125 ;;
126 esac
127}
128
129give_advice ()
130{
131 # Normalize program name to check for.
132 normalized_program=`echo "$1" | sed '
133 s/^gnu-//; t
134 s/^gnu//; t
135 s/^g//; t'`
136
137 printf '%s\n' "'$1' is $msg."
138
139 configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
140 case $normalized_program in
141 autoconf*)
142 echo "You should only need it if you modified 'configure.ac',"
143 echo "or m4 files included by it."
144 program_details 'autoconf'
145 ;;
146 autoheader*)
147 echo "You should only need it if you modified 'acconfig.h' or"
148 echo "$configure_deps."
149 program_details 'autoheader'
150 ;;
151 automake*)
152 echo "You should only need it if you modified 'Makefile.am' or"
153 echo "$configure_deps."
154 program_details 'automake'
155 ;;
156 aclocal*)
157 echo "You should only need it if you modified 'acinclude.m4' or"
158 echo "$configure_deps."
159 program_details 'aclocal'
160 ;;
161 autom4te*)
162 echo "You might have modified some maintainer files that require"
163 echo "the 'autom4te' program to be rebuilt."
164 program_details 'autom4te'
165 ;;
166 bison*|yacc*)
167 echo "You should only need it if you modified a '.y' file."
168 echo "You may want to install the GNU Bison package:"
169 echo "<$gnu_software_URL/bison/>"
170 ;;
171 lex*|flex*)
172 echo "You should only need it if you modified a '.l' file."
173 echo "You may want to install the Fast Lexical Analyzer package:"
174 echo "<$flex_URL>"
175 ;;
176 help2man*)
177 echo "You should only need it if you modified a dependency" \
178 "of a man page."
179 echo "You may want to install the GNU Help2man package:"
180 echo "<$gnu_software_URL/help2man/>"
181 ;;
182 makeinfo*)
183 echo "You should only need it if you modified a '.texi' file, or"
184 echo "any other file indirectly affecting the aspect of the manual."
185 echo "You might want to install the Texinfo package:"
186 echo "<$gnu_software_URL/texinfo/>"
187 echo "The spurious makeinfo call might also be the consequence of"
188 echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
189 echo "want to install GNU make:"
190 echo "<$gnu_software_URL/make/>"
191 ;;
192 *)
193 echo "You might have modified some files without having the proper"
194 echo "tools for further handling them. Check the 'README' file, it"
195 echo "often tells you about the needed prerequisites for installing"
196 echo "this package. You may also peek at any GNU archive site, in"
197 echo "case some other package contains this missing '$1' program."
198 ;;
199 esac
200}
201
202give_advice "$1" | sed -e '1s/^/WARNING: /' \
203 -e '2,$s/^/ /' >&2
204
205# Propagate the correct exit status (expected to be 127 for a program
206# not found, 63 for a program that failed due to version mismatch).
207exit $st
208
209# Local variables:
210# eval: (add-hook 'write-file-hooks 'time-stamp)
211# time-stamp-start: "scriptversion="
212# time-stamp-format: "%:y-%02m-%02d.%02H"
213# time-stamp-time-zone: "UTC"
214# time-stamp-end: "; # UTC"
215# End:
diff --git a/apps/plugins/puzzles/src/mkauto.sh b/apps/plugins/puzzles/src/mkauto.sh
new file mode 100755
index 0000000000..297212ad4d
--- /dev/null
+++ b/apps/plugins/puzzles/src/mkauto.sh
@@ -0,0 +1,2 @@
1#! /bin/sh
2autoreconf -i && rm -rf autom4te.cache
diff --git a/apps/plugins/puzzles/src/mkfiles.pl b/apps/plugins/puzzles/src/mkfiles.pl
new file mode 100755
index 0000000000..c1623dfd12
--- /dev/null
+++ b/apps/plugins/puzzles/src/mkfiles.pl
@@ -0,0 +1,1807 @@
1#!/usr/bin/env perl
2#
3# Cross-platform Makefile generator.
4#
5# Reads the file `Recipe' to determine the list of generated
6# executables and their component objects. Then reads the source
7# files to compute #include dependencies. Finally, writes out the
8# various target Makefiles.
9
10# PuTTY specifics which could still do with removing:
11# - Mac makefile is not portabilised at all. Include directories
12# are hardwired, and also the libraries are fixed. This is
13# mainly because I was too scared to go anywhere near it.
14# - sbcsgen.pl is still run at startup.
15
16# Other things undone:
17# - special-define objects (foo.o[PREPROCSYMBOL]) are not
18# supported in the mac or vcproj makefiles.
19
20use warnings;
21use IO::Handle;
22use Cwd;
23use File::Basename;
24
25while ($#ARGV >= 0) {
26 if ($ARGV[0] eq "-U") {
27 # Convenience for Unix users: -U means that after we finish what
28 # we're doing here, we also run mkauto.sh and then 'configure'. So
29 # it's a one-stop shop for regenerating the actual end-product
30 # Unix makefile.
31 #
32 # Arguments supplied after -U go to configure.
33 $do_unix = 1;
34 shift @ARGV;
35 @confargs = @ARGV;
36 @ARGV = ();
37 } else {
38 die "unrecognised command-line argument '$ARGV[0]'\n";
39 }
40}
41
42@filestack = ();
43$in = new IO::Handle;
44open $in, "Recipe" or do {
45 # We want to deal correctly with being run from one of the
46 # subdirs in the source tree. So if we can't find Recipe here,
47 # try one level up.
48 chdir "..";
49 open $in, "Recipe" or die "unable to open Recipe file\n";
50};
51push @filestack, $in;
52
53# HACK: One of the source files in `charset' is auto-generated by
54# sbcsgen.pl. We need to generate that _now_, before attempting
55# dependency analysis.
56eval 'chdir "charset"; require "sbcsgen.pl"; chdir ".."';
57
58@srcdirs = ("./");
59
60$divert = undef; # ref to array of refs of scalars in which text is
61 # currently being put
62$help = ""; # list of newline-free lines of help text
63$project_name = "project"; # this is a good enough default
64%makefiles = (); # maps makefile types to output makefile pathnames
65%makefile_extra = (); # maps makefile types to extra Makefile text
66%programs = (); # maps prog name + type letter to listref of objects/resources
67%groups = (); # maps group name to listref of objects/resources
68
69@allobjs = (); # all object file names
70
71readinput: while (1) {
72 $in = $filestack[$#filestack];
73 while (not defined ($_ = <$in>)) {
74 close $filestack[$#filestack];
75 pop @filestack;
76 last readinput if 0 == scalar @filestack;
77 $in = $filestack[$#filestack];
78 }
79 chomp;
80 @_ = split;
81
82 # If we're gathering help text, keep doing so.
83 if (defined $divert) {
84 if ((defined $_[0]) && $_[0] eq "!end") {
85 $divert = undef;
86 } else {
87 for my $ref (@$divert) {
88 ${$ref} .= "$_\n";
89 }
90 }
91 next;
92 }
93 # Skip comments and blank lines.
94 next if /^\s*#/ or scalar @_ == 0;
95
96 if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = [\$help]; next; }
97 if ($_[0] eq "!name") { $project_name = $_[1]; next; }
98 if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; }
99 if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;}
100 if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;}
101 if ($_[0] eq "!cflags" and &mfval($_[1])) {
102 ($rest = $_) =~ s/^\s*\S+\s+\S+\s+\S+\s*//; # find rest of input line
103 $rest = 1 if $rest eq "";
104 $cflags{$_[1]}->{$_[2]} = $rest;
105 next;
106 }
107 if ($_[0] eq "!begin") {
108 my @args = @_;
109 shift @args;
110 $divert = [];
111 for my $component (@args) {
112 if ($component =~ /^>(.*)/) {
113 push @$divert, \$auxfiles{$1};
114 } elsif ($component =~ /^([^_]*)(_.*)?$/ and &mfval($1)) {
115 push @$divert, \$makefile_extra{$component};
116 }
117 }
118 next;
119 }
120 if ($_[0] eq "!include") {
121 @newfiles = ();
122 for ($i = 1; $i <= $#_; $i++) {
123 push @newfiles, (sort glob $_[$i]);
124 }
125 for ($i = $#newfiles; $i >= 0; $i--) {
126 $file = $newfiles[$i];
127 $f = new IO::Handle;
128 open $f, "<$file" or die "unable to open include file '$file'\n";
129 push @filestack, $f;
130 }
131 next;
132 }
133
134 # Now we have an ordinary line. See if it's an = line, a : line
135 # or a + line.
136 @objs = @_;
137
138 if ($_[0] eq "+") {
139 $listref = $lastlistref;
140 $prog = undef;
141 die "$.: unexpected + line\n" if !defined $lastlistref;
142 } elsif ($_[1] eq "=") {
143 $groups{$_[0]} = [];
144 $listref = $groups{$_[0]};
145 $prog = undef;
146 shift @objs; # eat the group name
147 } elsif ($_[1] eq "+=") {
148 $groups{$_[0]} = [] if !defined $groups{$_[0]};
149 $listref = $groups{$_[0]};
150 $prog = undef;
151 shift @objs; # eat the group name
152 } elsif ($_[1] eq ":") {
153 $listref = [];
154 $prog = $_[0];
155 shift @objs; # eat the program name
156 } else {
157 die "$.: unrecognised line type: '$_'\n";
158 }
159 shift @objs; # eat the +, the = or the :
160
161 while (scalar @objs > 0) {
162 $i = shift @objs;
163 if ($groups{$i}) {
164 foreach $j (@{$groups{$i}}) { unshift @objs, $j; }
165 } elsif (($i eq "[G]" or $i eq "[C]" or $i eq "[M]" or
166 $i eq "[X]" or $i eq "[U]" or $i eq "[MX]") and defined $prog) {
167 $type = substr($i,1,(length $i)-2);
168 } else {
169 if ($i =~ /\?$/) {
170 # Object files with a trailing question mark are optional:
171 # the build can proceed fine without them, so we only use
172 # them if their primary source files are present.
173 $i =~ s/\?$//;
174 $i = undef unless defined &finddep($i);
175 } elsif ($i =~ /\|/) {
176 # Object file descriptions containing a vertical bar are
177 # lists of choices: we use the _first_ one whose primary
178 # source file is present.
179 @options = split /\|/, $i;
180 $j = undef;
181 foreach $k (@options) {
182 $j=$k, last if defined &finddep($k);
183 }
184 die "no alternative found for $i\n" unless defined $j;
185 $i = $j;
186 }
187 if (defined $i) {
188 push @$listref, $i;
189 push @allobjs, $i;
190 }
191 }
192 }
193 if ($prog and $type) {
194 die "multiple program entries for $prog [$type]\n"
195 if defined $programs{$prog . "," . $type};
196 $programs{$prog . "," . $type} = $listref;
197 }
198 $lastlistref = $listref;
199}
200
201foreach $aux (sort keys %auxfiles) {
202 open AUX, ">$aux";
203 print AUX $auxfiles{$aux};
204 close AUX;
205}
206
207# Find object file names with predefines (in square brackets after
208# the module name), and decide on actual object names for them.
209foreach $i (@allobjs) {
210 if ($i !~ /\[/) {
211 $objname{$i} = $i;
212 $srcname{$i} = $i;
213 $usedobjname{$i} = 1;
214 }
215}
216foreach $i (@allobjs) {
217 if ($i =~ /^(.*)\[([^\]]*)/) {
218 $defs{$i} = [ split ",",$2 ];
219 $srcname{$i} = $s = $1;
220 $index = 1;
221 while (1) {
222 $maxlen = length $s;
223 $maxlen = 8 if $maxlen < 8;
224 $chop = $maxlen - length $index;
225 $chop = length $s if $chop > length $s;
226 $chop = 0 if $chop < 0;
227 $name = substr($s, 0, $chop) . $index;
228 $index++, next if $usedobjname{$name};
229 $objname{$i} = $name;
230 $usedobjname{$name} = 1;
231 last;
232 }
233 }
234}
235
236# Now retrieve the complete list of objects and resource files, and
237# construct dependency data for them. While we're here, expand the
238# object list for each program, and complain if its type isn't set.
239@prognames = sort keys %programs;
240%depends = ();
241@scanlist = ();
242foreach $i (@prognames) {
243 ($prog, $type) = split ",", $i;
244 # Strip duplicate object names.
245 $prev = '';
246 @list = grep { $status = ($prev ne $_); $prev=$_; $status }
247 sort @{$programs{$i}};
248 $programs{$i} = [@list];
249 foreach $jj (@list) {
250 $j = $srcname{$jj};
251 $file = &finddep($j);
252 if (defined $file) {
253 $depends{$jj} = [$file];
254 push @scanlist, $file;
255 }
256 }
257}
258
259# Scan each file on @scanlist and find further inclusions.
260# Inclusions are given by lines of the form `#include "otherfile"'
261# (system headers are automatically ignored by this because they'll
262# be given in angle brackets). Files included by this method are
263# added back on to @scanlist to be scanned in turn (if not already
264# done).
265#
266# Resource scripts (.rc) can also include a file by means of a line
267# ending `ICON "filename"'. Files included by this method are not
268# added to @scanlist because they can never include further files.
269#
270# In this pass we write out a hash %further which maps a source
271# file name into a listref containing further source file names.
272
273%further = ();
274while (scalar @scanlist > 0) {
275 $file = shift @scanlist;
276 next if defined $further{$file}; # skip if we've already done it
277 $further{$file} = [];
278 $dirfile = &findfile($file);
279 open IN, "$dirfile" or die "unable to open source file $file\n";
280 while (<IN>) {
281 chomp;
282 /^\s*#include\s+\"([^\"]+)\"/ and do {
283 push @{$further{$file}}, $1;
284 push @scanlist, $1;
285 next;
286 };
287 /ICON\s+\"([^\"]+)\"\s*$/ and do {
288 push @{$further{$file}}, $1;
289 next;
290 }
291 }
292 close IN;
293}
294
295# Now we're ready to generate the final dependencies section. For
296# each key in %depends, we must expand the dependencies list by
297# iteratively adding entries from %further.
298foreach $i (keys %depends) {
299 %dep = ();
300 @scanlist = @{$depends{$i}};
301 foreach $i (@scanlist) { $dep{$i} = 1; }
302 while (scalar @scanlist > 0) {
303 $file = shift @scanlist;
304 foreach $j (@{$further{$file}}) {
305 if (!$dep{$j}) {
306 $dep{$j} = 1;
307 push @{$depends{$i}}, $j;
308 push @scanlist, $j;
309 }
310 }
311 }
312# printf "%s: %s\n", $i, join ' ',@{$depends{$i}};
313}
314
315# Validation of input.
316
317sub mfval($) {
318 my ($type) = @_;
319 # Returns true if the argument is a known makefile type. Otherwise,
320 # prints a warning and returns false;
321 if (grep { $type eq $_ }
322 ("vc","vcproj","cygwin","borland","lcc","gtk","am","mpw","nestedvm","osx","wce","gnustep","emcc")) {
323 return 1;
324 }
325 warn "$.:unknown makefile type '$type'\n";
326 return 0;
327}
328
329# Utility routines while writing out the Makefiles.
330
331sub dirpfx {
332 my ($path) = shift @_;
333 my ($sep) = shift @_;
334 my $ret = "";
335 my $i;
336 while (($i = index $path, $sep) >= 0) {
337 $path = substr $path, ($i + length $sep);
338 $ret .= "..$sep";
339 }
340 return $ret;
341}
342
343sub findfile {
344 my ($name) = @_;
345 my $dir;
346 my $i;
347 my $outdir = undef;
348 unless (defined $findfilecache{$name}) {
349 $i = 0;
350 foreach $dir (@srcdirs) {
351 $outdir = $dir, $i++ if -f "$dir$name";
352 }
353 die "multiple instances of source file $name\n" if $i > 1;
354 $findfilecache{$name} = (defined $outdir ? $outdir . $name : undef);
355 }
356 return $findfilecache{$name};
357}
358
359sub finddep {
360 my $j = shift @_;
361 my $file;
362 # Find the first dependency of an object.
363
364 # Dependencies for "x" start with "x.c" or "x.m" (depending on
365 # which one exists).
366 # Dependencies for "x.res" start with "x.rc".
367 # Dependencies for "x.rsrc" start with "x.r".
368 # Both types of file are pushed on the list of files to scan.
369 # Libraries (.lib) don't have dependencies at all.
370 if ($j =~ /^(.*)\.res$/) {
371 $file = "$1.rc";
372 } elsif ($j =~ /^(.*)\.rsrc$/) {
373 $file = "$1.r";
374 } elsif ($j !~ /\./) {
375 $file = "$j.c";
376 $file = "$j.m" unless &findfile($file);
377 } else {
378 # For everything else, we assume it's its own dependency.
379 $file = $j;
380 }
381 $file = undef unless &findfile($file);
382 return $file;
383}
384
385sub objects {
386 my ($prog, $otmpl, $rtmpl, $ltmpl, $prefix, $dirsep) = @_;
387 my @ret;
388 my ($i, $x, $y);
389 ($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl);
390 @ret = ();
391 foreach $ii (@{$programs{$prog}}) {
392 $i = $objname{$ii};
393 $x = "";
394 if ($i =~ /^(.*)\.(res|rsrc)/) {
395 $y = $1;
396 ($x = $rtmpl) =~ s/X/$y/;
397 } elsif ($i =~ /^(.*)\.lib/) {
398 $y = $1;
399 ($x = $ltmpl) =~ s/X/$y/;
400 } elsif ($i !~ /\./) {
401 ($x = $otmpl) =~ s/X/$i/;
402 }
403 push @ret, $x if $x ne "";
404 }
405 return join " ", @ret;
406}
407
408sub special {
409 my ($prog, $suffix) = @_;
410 my @ret;
411 my ($i, $x, $y);
412 @ret = ();
413 foreach $ii (@{$programs{$prog}}) {
414 $i = $objname{$ii};
415 if (substr($i, (length $i) - (length $suffix)) eq $suffix) {
416 push @ret, $i;
417 }
418 }
419 return join " ", @ret;
420}
421
422sub splitline {
423 my ($line, $width, $splitchar) = @_;
424 my $result = "";
425 my $len;
426 $len = (defined $width ? $width : 76);
427 $splitchar = (defined $splitchar ? $splitchar : '\\');
428 while (length $line > $len) {
429 $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,}?\s(.*)$/;
430 $result .= $1;
431 $result .= " ${splitchar}\n\t\t" if $2 ne '';
432 $line = $2;
433 $len = 60;
434 }
435 return $result . $line;
436}
437
438sub deps {
439 my ($otmpl, $rtmpl, $prefix, $dirsep, $depchar, $splitchar) = @_;
440 my ($i, $x, $y);
441 my @deps;
442 my @ret;
443 @ret = ();
444 $depchar ||= ':';
445 foreach $ii (sort keys %depends) {
446 $i = $objname{$ii};
447 next if $specialobj{$mftyp}->{$i};
448 if ($i =~ /^(.*)\.(res|rsrc)/) {
449 next if !defined $rtmpl;
450 $y = $1;
451 ($x = $rtmpl) =~ s/X/$y/;
452 } else {
453 ($x = $otmpl) =~ s/X/$i/;
454 }
455 @deps = @{$depends{$ii}};
456 # Skip things which are their own dependency.
457 next if grep { $_ eq $i } @deps;
458 @deps = map {
459 $_ = &findfile($_);
460 s/\//$dirsep/g;
461 $_ = $prefix . $_;
462 } @deps;
463 push @ret, {obj => $x, deps => [@deps], defs => $defs{$ii}};
464 }
465 return @ret;
466}
467
468sub prognames {
469 my ($types) = @_;
470 my ($n, $prog, $type);
471 my @ret;
472 @ret = ();
473 foreach $n (@prognames) {
474 ($prog, $type) = split ",", $n;
475 push @ret, $n if index(":$types:", ":$type:") >= 0;
476 }
477 return @ret;
478}
479
480sub progrealnames {
481 my ($types) = @_;
482 my ($n, $prog, $type);
483 my @ret;
484 @ret = ();
485 foreach $n (@prognames) {
486 ($prog, $type) = split ",", $n;
487 push @ret, $prog if index(":$types:", ":$type:") >= 0;
488 }
489 return @ret;
490}
491
492sub manpages {
493 my ($types,$suffix) = @_;
494
495 # assume that all UNIX programs have a man page
496 if($suffix eq "1" && $types =~ /:X:/) {
497 return map("$_.1", &progrealnames($types));
498 }
499 return ();
500}
501
502$orig_dir = cwd;
503
504# Now we're ready to output the actual Makefiles.
505
506if (defined $makefiles{'cygwin'}) {
507 $mftyp = 'cygwin';
508 $dirpfx = &dirpfx($makefiles{'cygwin'}, "/");
509
510 ##-- CygWin makefile
511 open OUT, ">$makefiles{'cygwin'}"; select OUT;
512 print
513 "# Makefile for $project_name under cygwin.\n".
514 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
515 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
516 # gcc command line option is -D not /D
517 ($_ = $help) =~ s/=\/D/=-D/gs;
518 print $_;
519 print
520 "\n".
521 "# You can define this path to point at your tools if you need to\n".
522 "# TOOLPATH = c:\\cygwin\\bin\\ # or similar, if you're running Windows\n".
523 "# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/\n".
524 "CC = \$(TOOLPATH)gcc\n".
525 "RC = \$(TOOLPATH)windres\n".
526 "# Uncomment the following two lines to compile under Winelib\n".
527 "# CC = winegcc\n".
528 "# RC = wrc\n".
529 "# You may also need to tell windres where to find include files:\n".
530 "# RCINC = --include-dir c:\\cygwin\\include\\\n".
531 "\n".
532 &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT".
533 " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP " .
534 (join " ", map {"-I$dirpfx$_"} @srcdirs)) .
535 "\n".
536 "LDFLAGS = -mno-cygwin -s\n".
537 &splitline("RCFLAGS = \$(RCINC) --define WIN32=1 --define _WIN32=1".
538 " --define WINVER=0x0400 --define MINGW32_FIX=1 " .
539 (join " ", map {"--include $dirpfx$_"} @srcdirs) )."\n".
540 "\n";
541 print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
542 print "\n\n";
543 foreach $p (&prognames("G:C")) {
544 ($prog, $type) = split ",", $p;
545 $objstr = &objects($p, "X.o", "X.res.o", undef);
546 print &splitline($prog . ".exe: " . $objstr), "\n";
547 my $mw = $type eq "G" ? " -mwindows" : "";
548 $libstr = &objects($p, undef, undef, "-lX");
549 print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " .
550 "-Wl,-Map,$prog.map " .
551 $objstr . " $libstr", 69), "\n\n";
552 }
553 foreach $d (&deps("X.o", "X.res.o", $dirpfx, "/")) {
554 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
555 "\n";
556 if ($d->{obj} =~ /\.res\.o$/) {
557 print "\t\$(RC) \$(FWHACK) \$(RCFL) \$(RCFLAGS) \$< \$\@\n";
558 } else {
559 $deflist = join "", map { " -D$_" } @{$d->{defs}};
560 print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS)" .
561 " \$(XFLAGS)$deflist -c \$< -o \$\@\n";
562 }
563 }
564 print "\n";
565 print $makefile_extra{'cygwin'} || "";
566 print "\nclean:\n".
567 "\trm -f *.o *.exe *.res.o *.map\n".
568 "\n";
569 select STDOUT; close OUT;
570
571}
572
573##-- Borland makefile
574if (defined $makefiles{'borland'}) {
575 $mftyp = 'borland';
576 $dirpfx = &dirpfx($makefiles{'borland'}, "\\");
577
578 %stdlibs = ( # Borland provides many Win32 API libraries intrinsically
579 "advapi32" => 1,
580 "comctl32" => 1,
581 "comdlg32" => 1,
582 "gdi32" => 1,
583 "imm32" => 1,
584 "shell32" => 1,
585 "user32" => 1,
586 "winmm" => 1,
587 "winspool" => 1,
588 "wsock32" => 1,
589 );
590 open OUT, ">$makefiles{'borland'}"; select OUT;
591 print
592 "# Makefile for $project_name under Borland C.\n".
593 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
594 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
595 # bcc32 command line option is -D not /D
596 ($_ = $help) =~ s/=\/D/=-D/gs;
597 print $_;
598 print
599 "\n".
600 "# If you rename this file to `Makefile', you should change this line,\n".
601 "# so that the .rsp files still depend on the correct makefile.\n".
602 "MAKEFILE = Makefile.bor\n".
603 "\n".
604 "# C compilation flags\n".
605 "CFLAGS = -D_WINDOWS -DWINVER=0x0401\n".
606 "\n".
607 "# Get include directory for resource compiler\n".
608 "!if !\$d(BCB)\n".
609 "BCB = \$(MAKEDIR)\\..\n".
610 "!endif\n".
611 "\n";
612 print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
613 print "\n\n";
614 foreach $p (&prognames("G:C")) {
615 ($prog, $type) = split ",", $p;
616 $objstr = &objects($p, "X.obj", "X.res", undef);
617 print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";
618 my $ap = ($type eq "G") ? "-aa" : "-ap";
619 print "\tilink32 $ap -Gn -L\$(BCB)\\lib \@$prog.rsp\n\n";
620 }
621 foreach $p (&prognames("G:C")) {
622 ($prog, $type) = split ",", $p;
623 print $prog, ".rsp: \$(MAKEFILE)\n";
624 $objstr = &objects($p, "X.obj", undef, undef);
625 @objlist = split " ", $objstr;
626 @objlines = ("");
627 foreach $i (@objlist) {
628 if (length($objlines[$#objlines] . " $i") > 50) {
629 push @objlines, "";
630 }
631 $objlines[$#objlines] .= " $i";
632 }
633 $c0w = ($type eq "G") ? "c0w32" : "c0x32";
634 print "\techo $c0w + > $prog.rsp\n";
635 for ($i=0; $i<=$#objlines; $i++) {
636 $plus = ($i < $#objlines ? " +" : "");
637 print "\techo$objlines[$i]$plus >> $prog.rsp\n";
638 }
639 print "\techo $prog.exe >> $prog.rsp\n";
640 $objstr = &objects($p, "X.obj", "X.res", undef);
641 @libs = split " ", &objects($p, undef, undef, "X");
642 @libs = grep { !$stdlibs{$_} } @libs;
643 unshift @libs, "cw32", "import32";
644 $libstr = join ' ', @libs;
645 print "\techo nul,$libstr, >> $prog.rsp\n";
646 print "\techo " . &objects($p, undef, "X.res", undef) . " >> $prog.rsp\n";
647 print "\n";
648 }
649 foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) {
650 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
651 "\n";
652 if ($d->{obj} =~ /\.res$/) {
653 print &splitline("\tbrcc32 \$(FWHACK) \$(RCFL) " .
654 "-i \$(BCB)\\include -r -DNO_WINRESRC_H -DWIN32".
655 " -D_WIN32 -DWINVER=0x0401 \$*.rc",69)."\n";
656 } else {
657 $deflist = join "", map { " -D$_" } @{$d->{defs}};
658 print &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT)" .
659 " \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist ".
660 (join " ", map {"-I$dirpfx$_"} @srcdirs) .
661 " /o$d->{obj} /c ".$d->{deps}->[0],69)."\n";
662 }
663 }
664 print "\n";
665 print $makefile_extra{'borland'} || "";
666 print "\nclean:\n".
667 "\t-del *.obj\n".
668 "\t-del *.exe\n".
669 "\t-del *.res\n".
670 "\t-del *.pch\n".
671 "\t-del *.aps\n".
672 "\t-del *.il*\n".
673 "\t-del *.pdb\n".
674 "\t-del *.rsp\n".
675 "\t-del *.tds\n".
676 "\t-del *.\$\$\$\$\$\$\n";
677 select STDOUT; close OUT;
678}
679
680if (defined $makefiles{'vc'}) {
681 $mftyp = 'vc';
682 $dirpfx = &dirpfx($makefiles{'vc'}, "\\");
683
684 ##-- Visual C++ makefile
685 open OUT, ">$makefiles{'vc'}"; select OUT;
686 print
687 "# Makefile for $project_name under Visual C.\n".
688 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
689 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
690 print $help;
691 print
692 "\n".
693 "# If you rename this file to `Makefile', you should change this line,\n".
694 "# so that the .rsp files still depend on the correct makefile.\n".
695 "MAKEFILE = Makefile.vc\n".
696 "\n".
697 "# C compilation flags\n".
698 "CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 /I.\n".
699 "LFLAGS = /incremental:no /fixed\n".
700 "\n";
701 print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
702 print "\n\n";
703 foreach $p (&prognames("G:C")) {
704 ($prog, $type) = split ",", $p;
705 $objstr = &objects($p, "X.obj", "X.res", undef);
706 print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";
707 print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n";
708 }
709 foreach $p (&prognames("G:C")) {
710 ($prog, $type) = split ",", $p;
711 print $prog, ".rsp: \$(MAKEFILE)\n";
712 $objstr = &objects($p, "X.obj", "X.res", "X.lib");
713 @objlist = split " ", $objstr;
714 @objlines = ("");
715 foreach $i (@objlist) {
716 if (length($objlines[$#objlines] . " $i") > 50) {
717 push @objlines, "";
718 }
719 $objlines[$#objlines] .= " $i";
720 }
721 $subsys = ($type eq "G") ? "windows" : "console";
722 print "\techo /nologo /subsystem:$subsys > $prog.rsp\n";
723 for ($i=0; $i<=$#objlines; $i++) {
724 print "\techo$objlines[$i] >> $prog.rsp\n";
725 }
726 print "\n";
727 }
728 foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) {
729 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
730 "\n";
731 if ($d->{obj} =~ /\.res$/) {
732 print "\trc \$(FWHACK) \$(RCFL) -r -DWIN32 -D_WIN32 ".
733 "-DWINVER=0x0400 -fo".$d->{obj}." ".$d->{deps}->[0]."\n";
734 } else {
735 $deflist = join "", map { " /D$_" } @{$d->{defs}};
736 print "\tcl \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist".
737 " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n";
738 }
739 }
740 print "\n";
741 print $makefile_extra{'vc'} || "";
742 print "\nclean: tidy\n".
743 "\t-del *.exe\n\n".
744 "tidy:\n".
745 "\t-del *.obj\n".
746 "\t-del *.res\n".
747 "\t-del *.pch\n".
748 "\t-del *.aps\n".
749 "\t-del *.ilk\n".
750 "\t-del *.pdb\n".
751 "\t-del *.rsp\n".
752 "\t-del *.dsp\n".
753 "\t-del *.dsw\n".
754 "\t-del *.ncb\n".
755 "\t-del *.opt\n".
756 "\t-del *.plg\n".
757 "\t-del *.map\n".
758 "\t-del *.idb\n".
759 "\t-del debug.log\n";
760 select STDOUT; close OUT;
761}
762
763if (defined $makefiles{'wce'}) {
764 $mftyp = 'wce';
765 $dirpfx = &dirpfx($makefiles{'wce'}, "\\");
766
767 ##-- eMbedded Visual C PocketPC makefile
768 open OUT, ">$makefiles{'wce'}"; select OUT;
769 print
770 "# Makefile for $project_name on PocketPC using eMbedded Visual C.\n".
771 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
772 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
773 print $help;
774 print
775 "\n".
776 "# If you rename this file to `Makefile', you should change this line,\n".
777 "# so that the .rsp files still depend on the correct makefile.\n".
778 "MAKEFILE = Makefile.wce\n".
779 "\n".
780 "# This makefile expects the environment to have been set up by one\n".
781 "# of the PocketPC batch files wcearmv4.bat and wceemulator.bat. No\n".
782 "# other build targets are currently supported, because they would\n".
783 "# need a section in this if statement.\n".
784 "!if \"\$(TARGETCPU)\" == \"emulator\"\n".
785 "PLATFORM_DEFS=/D \"_i386_\" /D \"i_386_\" /D \"_X86_\" /D \"x86\"\n".
786 "CC=cl\n".
787 "BASELIBS=commctrl.lib coredll.lib corelibc.lib aygshell.lib\n".
788 "MACHINE=IX86\n".
789 "!else\n".
790 "PLATFORM_DEFS=/D \"ARM\" /D \"_ARM_\" /D \"ARMV4\"\n".
791 "CC=clarm\n".
792 "BASELIBS=commctrl.lib coredll.lib aygshell.lib\n".
793 "MACHINE=ARM\n".
794 "!endif\n".
795 "\n".
796 "# C compilation flags\n".
797 "CFLAGS = /nologo /W3 /O1 /MC /D _WIN32_WCE=420 /D \"WIN32_PLATFORM_PSPC=400\" /D UNDER_CE=420 \\\n".
798 " \$(PLATFORM_DEFS) \\\n".
799 " /D \"UNICODE\" /D \"_UNICODE\" /D \"NDEBUG\" /D \"NO_HTMLHELP\"\n".
800 "\n".
801 "LFLAGS = /nologo /incremental:no \\\n".
802 " /base:0x00010000 /stack:0x10000,0x1000 /entry:WinMainCRTStartup \\\n".
803 " /nodefaultlib:libc.lib /nodefaultlib:libcmt.lib /nodefaultlib:msvcrt.lib /nodefaultlib:OLDNAMES.lib \\\n".
804 " /subsystem:windowsce,4.20 /align:4096 /MACHINE:\$(MACHINE)\n".
805 "\n".
806 "RCFL = /d UNDER_CE=420 /d _WIN32_WCE=420 /d \"WIN32_PLATFORM_PSPC=400\" \\\n".
807 " \$(PLATFORM_DEFS) \\\n".
808 " /d \"NDEBUG\" /d \"UNICODE\" /d \"_UNICODE\"\n".
809 "\n";
810 print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G"));
811 print "\n\n";
812 foreach $p (&prognames("G")) {
813 ($prog, $type) = split ",", $p;
814 $objstr = &objects($p, "X.obj", "X.res", undef);
815 print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n";
816 print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n";
817 }
818 foreach $p (&prognames("G")) {
819 ($prog, $type) = split ",", $p;
820 print $prog, ".rsp: \$(MAKEFILE)\n";
821 $objstr = &objects($p, "X.obj", "X.res", undef);
822 @objlist = split " ", $objstr;
823 @objlines = ("");
824 foreach $i (@objlist) {
825 if (length($objlines[$#objlines] . " $i") > 50) {
826 push @objlines, "";
827 }
828 $objlines[$#objlines] .= " $i";
829 }
830 print "\techo \$(BASELIBS) > $prog.rsp\n";
831 for ($i=0; $i<=$#objlines; $i++) {
832 print "\techo$objlines[$i] >> $prog.rsp\n";
833 }
834 print "\n";
835 }
836 foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) {
837 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
838 "\n";
839 if ($d->{obj} =~ /\.res$/) {
840 print "\trc \$(FWHACK) \$(RCFL) -r -fo".
841 $d->{obj}." ".$d->{deps}->[0]."\n";
842 } else {
843 $deflist = join "", map { " /D$_" } @{$d->{defs}};
844 print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist".
845 " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n";
846 }
847 }
848 print "\n";
849 print $makefile_extra{'wce'} || "";
850 print "\nclean: tidy\n".
851 "\t-del *.exe\n\n".
852 "tidy:\n".
853 "\t-del *.obj\n".
854 "\t-del *.res\n".
855 "\t-del *.pch\n".
856 "\t-del *.aps\n".
857 "\t-del *.ilk\n".
858 "\t-del *.pdb\n".
859 "\t-del *.rsp\n".
860 "\t-del *.dsp\n".
861 "\t-del *.dsw\n".
862 "\t-del *.ncb\n".
863 "\t-del *.opt\n".
864 "\t-del *.plg\n".
865 "\t-del *.map\n".
866 "\t-del *.idb\n".
867 "\t-del debug.log\n";
868 select STDOUT; close OUT;
869}
870
871if (defined $makefiles{'vcproj'}) {
872 $mftyp = 'vcproj';
873
874 ##-- MSVC 6 Workspace and projects
875 #
876 # Note: All files created in this section are written in binary
877 # mode, because although MSVC's command-line make can deal with
878 # LF-only line endings, MSVC project files really _need_ to be
879 # CRLF. Hence, in order for mkfiles.pl to generate usable project
880 # files even when run from Unix, I make sure all files are binary
881 # and explicitly write the CRLFs.
882 #
883 # Create directories if necessary
884 mkdir $makefiles{'vcproj'}
885 if(! -d $makefiles{'vcproj'});
886 chdir $makefiles{'vcproj'};
887 @deps = &deps("X.obj", "X.res", "", "\\");
888 %all_object_deps = map {$_->{obj} => $_->{deps}} @deps;
889 # Create the project files
890 # Get names of all Windows projects (GUI and console)
891 my @prognames = &prognames("G:C");
892 foreach $progname (@prognames) {
893 create_project(\%all_object_deps, $progname);
894 }
895 # Create the workspace file
896 open OUT, ">$project_name.dsw"; binmode OUT; select OUT;
897 print
898 "Microsoft Developer Studio Workspace File, Format Version 6.00\r\n".
899 "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r\n".
900 "\r\n".
901 "###############################################################################\r\n".
902 "\r\n";
903 # List projects
904 foreach $progname (@prognames) {
905 ($windows_project, $type) = split ",", $progname;
906 print "Project: \"$windows_project\"=\".\\$windows_project\\$windows_project.dsp\" - Package Owner=<4>\r\n";
907 }
908 print
909 "\r\n".
910 "Package=<5>\r\n".
911 "{{{\r\n".
912 "}}}\r\n".
913 "\r\n".
914 "Package=<4>\r\n".
915 "{{{\r\n".
916 "}}}\r\n".
917 "\r\n".
918 "###############################################################################\r\n".
919 "\r\n".
920 "Global:\r\n".
921 "\r\n".
922 "Package=<5>\r\n".
923 "{{{\r\n".
924 "}}}\r\n".
925 "\r\n".
926 "Package=<3>\r\n".
927 "{{{\r\n".
928 "}}}\r\n".
929 "\r\n".
930 "###############################################################################\r\n".
931 "\r\n";
932 select STDOUT; close OUT;
933 chdir $orig_dir;
934
935 sub create_project {
936 my ($all_object_deps, $progname) = @_;
937 # Construct program's dependency info
938 %seen_objects = ();
939 %lib_files = ();
940 %source_files = ();
941 %header_files = ();
942 %resource_files = ();
943 @object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib");
944 foreach $object_file (@object_files) {
945 next if defined $seen_objects{$object_file};
946 $seen_objects{$object_file} = 1;
947 if($object_file =~ /\.lib$/io) {
948 $lib_files{$object_file} = 1;
949 next;
950 }
951 $object_deps = $all_object_deps{$object_file};
952 foreach $object_dep (@$object_deps) {
953 if($object_dep =~ /\.c$/io) {
954 $source_files{$object_dep} = 1;
955 next;
956 }
957 if($object_dep =~ /\.h$/io) {
958 $header_files{$object_dep} = 1;
959 next;
960 }
961 if($object_dep =~ /\.(rc|ico)$/io) {
962 $resource_files{$object_dep} = 1;
963 next;
964 }
965 }
966 }
967 $libs = join " ", sort keys %lib_files;
968 @source_files = sort keys %source_files;
969 @header_files = sort keys %header_files;
970 @resources = sort keys %resource_files;
971 ($windows_project, $type) = split ",", $progname;
972 mkdir $windows_project
973 if(! -d $windows_project);
974 chdir $windows_project;
975 $subsys = ($type eq "G") ? "windows" : "console";
976 open OUT, ">$windows_project.dsp"; binmode OUT; select OUT;
977 print
978 "# Microsoft Developer Studio Project File - Name=\"$windows_project\" - Package Owner=<4>\r\n".
979 "# Microsoft Developer Studio Generated Build File, Format Version 6.00\r\n".
980 "# ** DO NOT EDIT **\r\n".
981 "\r\n".
982 "# TARGTYPE \"Win32 (x86) Application\" 0x0101\r\n".
983 "\r\n".
984 "CFG=$windows_project - Win32 Debug\r\n".
985 "!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r\n".
986 "!MESSAGE use the Export Makefile command and run\r\n".
987 "!MESSAGE \r\n".
988 "!MESSAGE NMAKE /f \"$windows_project.mak\".\r\n".
989 "!MESSAGE \r\n".
990 "!MESSAGE You can specify a configuration when running NMAKE\r\n".
991 "!MESSAGE by defining the macro CFG on the command line. For example:\r\n".
992 "!MESSAGE \r\n".
993 "!MESSAGE NMAKE /f \"$windows_project.mak\" CFG=\"$windows_project - Win32 Debug\"\r\n".
994 "!MESSAGE \r\n".
995 "!MESSAGE Possible choices for configuration are:\r\n".
996 "!MESSAGE \r\n".
997 "!MESSAGE \"$windows_project - Win32 Release\" (based on \"Win32 (x86) Application\")\r\n".
998 "!MESSAGE \"$windows_project - Win32 Debug\" (based on \"Win32 (x86) Application\")\r\n".
999 "!MESSAGE \r\n".
1000 "\r\n".
1001 "# Begin Project\r\n".
1002 "# PROP AllowPerConfigDependencies 0\r\n".
1003 "# PROP Scc_ProjName \"\"\r\n".
1004 "# PROP Scc_LocalPath \"\"\r\n".
1005 "CPP=cl.exe\r\n".
1006 "MTL=midl.exe\r\n".
1007 "RSC=rc.exe\r\n".
1008 "\r\n".
1009 "!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".
1010 "\r\n".
1011 "# PROP BASE Use_MFC 0\r\n".
1012 "# PROP BASE Use_Debug_Libraries 0\r\n".
1013 "# PROP BASE Output_Dir \"Release\"\r\n".
1014 "# PROP BASE Intermediate_Dir \"Release\"\r\n".
1015 "# PROP BASE Target_Dir \"\"\r\n".
1016 "# PROP Use_MFC 0\r\n".
1017 "# PROP Use_Debug_Libraries 0\r\n".
1018 "# PROP Output_Dir \"Release\"\r\n".
1019 "# PROP Intermediate_Dir \"Release\"\r\n".
1020 "# PROP Ignore_Export_Lib 0\r\n".
1021 "# PROP Target_Dir \"\"\r\n".
1022 "# ADD BASE CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".
1023 "# ADD CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n".
1024 "# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".
1025 "# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n".
1026 "# ADD BASE RSC /l 0x809 /d \"NDEBUG\"\r\n".
1027 "# ADD RSC /l 0x809 /d \"NDEBUG\"\r\n".
1028 "BSC32=bscmake.exe\r\n".
1029 "# ADD BASE BSC32 /nologo\r\n".
1030 "# ADD BSC32 /nologo\r\n".
1031 "LINK32=link.exe\r\n".
1032 "# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /machine:I386\r\n".
1033 "# ADD LINK32 $libs /nologo /subsystem:$subsys /machine:I386\r\n".
1034 "# SUBTRACT LINK32 /pdb:none\r\n".
1035 "\r\n".
1036 "!ELSEIF \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n".
1037 "\r\n".
1038 "# PROP BASE Use_MFC 0\r\n".
1039 "# PROP BASE Use_Debug_Libraries 1\r\n".
1040 "# PROP BASE Output_Dir \"Debug\"\r\n".
1041 "# PROP BASE Intermediate_Dir \"Debug\"\r\n".
1042 "# PROP BASE Target_Dir \"\"\r\n".
1043 "# PROP Use_MFC 0\r\n".
1044 "# PROP Use_Debug_Libraries 1\r\n".
1045 "# PROP Output_Dir \"Debug\"\r\n".
1046 "# PROP Intermediate_Dir \"Debug\"\r\n".
1047 "# PROP Ignore_Export_Lib 0\r\n".
1048 "# PROP Target_Dir \"\"\r\n".
1049 "# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".
1050 "# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n".
1051 "# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".
1052 "# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n".
1053 "# ADD BASE RSC /l 0x809 /d \"_DEBUG\"\r\n".
1054 "# ADD RSC /l 0x809 /d \"_DEBUG\"\r\n".
1055 "BSC32=bscmake.exe\r\n".
1056 "# ADD BASE BSC32 /nologo\r\n".
1057 "# ADD BSC32 /nologo\r\n".
1058 "LINK32=link.exe\r\n".
1059 "# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".
1060 "# ADD LINK32 $libs /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n".
1061 "# SUBTRACT LINK32 /pdb:none\r\n".
1062 "\r\n".
1063 "!ENDIF \r\n".
1064 "\r\n".
1065 "# Begin Target\r\n".
1066 "\r\n".
1067 "# Name \"$windows_project - Win32 Release\"\r\n".
1068 "# Name \"$windows_project - Win32 Debug\"\r\n".
1069 "# Begin Group \"Source Files\"\r\n".
1070 "\r\n".
1071 "# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\r\n";
1072 foreach $source_file (@source_files) {
1073 print
1074 "# Begin Source File\r\n".
1075 "\r\n".
1076 "SOURCE=..\\..\\$source_file\r\n";
1077 if($source_file =~ /ssh\.c/io) {
1078 # Disable 'Edit and continue' as Visual Studio can't handle the macros
1079 print
1080 "\r\n".
1081 "!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n".
1082 "\r\n".
1083 "!ELSEIF \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n".
1084 "\r\n".
1085 "# ADD CPP /Zi\r\n".
1086 "\r\n".
1087 "!ENDIF \r\n".
1088 "\r\n";
1089 }
1090 print "# End Source File\r\n";
1091 }
1092 print
1093 "# End Group\r\n".
1094 "# Begin Group \"Header Files\"\r\n".
1095 "\r\n".
1096 "# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\r\n";
1097 foreach $header_file (@header_files) {
1098 print
1099 "# Begin Source File\r\n".
1100 "\r\n".
1101 "SOURCE=..\\..\\$header_file\r\n".
1102 "# End Source File\r\n";
1103 }
1104 print
1105 "# End Group\r\n".
1106 "# Begin Group \"Resource Files\"\r\n".
1107 "\r\n".
1108 "# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe\"\r\n";
1109 foreach $resource_file (@resources) {
1110 print
1111 "# Begin Source File\r\n".
1112 "\r\n".
1113 "SOURCE=..\\..\\$resource_file\r\n".
1114 "# End Source File\r\n";
1115 }
1116 print
1117 "# End Group\r\n".
1118 "# End Target\r\n".
1119 "# End Project\r\n";
1120 select STDOUT; close OUT;
1121 chdir "..";
1122 }
1123}
1124
1125if (defined $makefiles{'gtk'}) {
1126 $mftyp = 'gtk';
1127 $dirpfx = &dirpfx($makefiles{'gtk'}, "/");
1128
1129 ##-- X/GTK/Unix makefile
1130 open OUT, ">$makefiles{'gtk'}"; select OUT;
1131 print
1132 "# Makefile for $project_name under X/GTK and Unix.\n".
1133 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1134 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
1135 # gcc command line option is -D not /D
1136 ($_ = $help) =~ s/=\/D/=-D/gs;
1137 print $_;
1138 print
1139 "\n".
1140 "# You can define this path to point at your tools if you need to\n".
1141 "# TOOLPATH = /opt/gcc/bin\n".
1142 "CC := \$(TOOLPATH)\$(CC)\n".
1143 "# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'\n".
1144 "# (depending on what works on your system) if you want to enforce\n".
1145 "# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0'\n".
1146 "# if you want to enforce 2.0. The default is to try 2.0 and fall back\n".
1147 "# to 1.2 if it isn't found.\n".
1148 "GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 \$\$0 2>/dev/null || gtk-config \$\$0'\n".
1149 "\n".
1150 &splitline("CFLAGS := -O2 -Wall -Werror -ansi -pedantic -g " .
1151 (join " ", map {"-I$dirpfx$_"} @srcdirs) .
1152 " `\$(GTK_CONFIG) --cflags` \$(CFLAGS)")."\n".
1153 "XLIBS = `\$(GTK_CONFIG) --libs` -lm\n".
1154 "ULIBS = -lm#\n".
1155 "INSTALL=install\n",
1156 "INSTALL_PROGRAM=\$(INSTALL)\n",
1157 "INSTALL_DATA=\$(INSTALL)\n",
1158 "prefix=/usr/local\n",
1159 "exec_prefix=\$(prefix)\n",
1160 "bindir=\$(exec_prefix)/bin\n",
1161 "gamesdir=\$(exec_prefix)/games\n",
1162 "mandir=\$(prefix)/man\n",
1163 "man1dir=\$(mandir)/man1\n",
1164 "\n";
1165 print &splitline("all:" . join "", map { " \$(BINPREFIX)$_" }
1166 &progrealnames("X:U"));
1167 print "\n\n";
1168 foreach $p (&prognames("X:U")) {
1169 ($prog, $type) = split ",", $p;
1170 $objstr = &objects($p, "X.o", undef, undef);
1171 print &splitline("\$(BINPREFIX)" . $prog . ": " . $objstr), "\n";
1172 $libstr = &objects($p, undef, undef, "-lX");
1173 print &splitline("\t\$(CC) -o \$@ $objstr $libstr \$(XLFLAGS) \$(${type}LIBS)", 69),
1174 "\n\n";
1175 }
1176 foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
1177 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
1178 "\n";
1179 $deflist = join "", map { " -D$_" } @{$d->{defs}};
1180 print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
1181 " -c \$< -o \$\@\n";
1182 }
1183 print "\n";
1184 print $makefile_extra{'gtk'} || "";
1185 print "\nclean:\n".
1186 "\trm -f *.o". (join "", map { " \$(BINPREFIX)$_" } &progrealnames("X:U")) . "\n";
1187 select STDOUT; close OUT;
1188}
1189
1190if (defined $makefiles{'am'}) {
1191 $mftyp = 'am';
1192 die "Makefile.am in a subdirectory is not supported\n"
1193 if &dirpfx($makefiles{'am'}, "/") ne "";
1194
1195 ##-- Unix/autoconf Makefile.am
1196 open OUT, ">$makefiles{'am'}"; select OUT;
1197 print
1198 "# Makefile.am for $project_name under Unix with Autoconf/Automake.\n".
1199 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1200 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n\n";
1201
1202 print $makefile_extra{'am_begin'} || "";
1203
1204 # All programs go in noinstprogs by default. If you want them
1205 # installed anywhere else, you have to also add them to
1206 # bin_PROGRAMS using '!begin am'. (Automake doesn't seem to mind
1207 # having a program name in _both_ of bin_PROGRAMS and
1208 # noinst_PROGRAMS.)
1209 @noinstprogs = ();
1210 foreach $p (&prognames("X:U")) {
1211 ($prog, $type) = split ",", $p;
1212 push @noinstprogs, $prog;
1213 }
1214 print &splitline(join " ", "noinst_PROGRAMS", "=", @noinstprogs), "\n";
1215
1216 %objtosrc = ();
1217 %amspeciallibs = ();
1218 %amlibobjname = ();
1219 %allsources = ();
1220 foreach $d (&deps("X", undef, "", "/", "am")) {
1221 my $obj = $d->{obj};
1222 my $use_archive = 0;
1223
1224 if (defined $d->{defs}) {
1225 # This file needs to go in an archive, so that we can
1226 # change the preprocess flags to include some -Ds
1227 $use_archive = 1;
1228 $archivecppflags{$obj} = [map { " -D$_" } @{$d->{defs}}];
1229 }
1230 if (defined $cflags{'am'} && $cflags{'am'}->{$obj}) {
1231 # This file needs to go in an archive, so that we can
1232 # change the compile flags as specified in Recipe
1233 $use_archive = 1;
1234 $archivecflags{$obj} = [$cflags{'am'}->{$obj}];
1235 }
1236 if ($use_archive) {
1237 $amspeciallibs{$obj} = "lib${obj}.a";
1238 $amlibobjname{$obj} = "lib${obj}_a-" .
1239 basename($d->{deps}->[0], ".c", ".m") .
1240 ".\$(OBJEXT)";
1241 }
1242 $objtosrc{$obj} = $d->{deps};
1243 map { $allsources{$_} = 1 } @{$d->{deps}};
1244 }
1245
1246 # 2014-02-22: as of automake-1.14 we begin to get complained at if
1247 # we don't use this option
1248 print "AUTOMAKE_OPTIONS = subdir-objects\n\n";
1249
1250 # Complete list of source and header files. Not used by the
1251 # auto-generated parts of this makefile, but Recipe might like to
1252 # have it available as a variable so that mandatory-rebuild things
1253 # (version.o) can conveniently be made to depend on it.
1254 print &splitline(join " ", "allsources", "=",
1255 sort {$a cmp $b} keys %allsources), "\n\n";
1256
1257 @amcppflags = map {"-I\$(srcdir)/$_"} @srcdirs;
1258 print &splitline(join " ", "AM_CPPFLAGS", "=", @amcppflags, "\n");
1259
1260 @amcflags = ("\$(GTK_CFLAGS)", "\$(WARNINGOPTS)");
1261 print &splitline(join " ", "AM_CFLAGS", "=", @amcflags), "\n";
1262
1263 %amlibsused = ();
1264 foreach $p (&prognames("X:U")) {
1265 ($prog, $type) = split ",", $p;
1266 @progsources = ("${prog}_SOURCES", "=");
1267 %sourcefiles = ();
1268 @ldadd = ();
1269 $objstr = &objects($p, "X", undef, undef);
1270 foreach $obj (split / /,$objstr) {
1271 if ($amspeciallibs{$obj}) {
1272 $amlibsused{$obj} = 1;
1273 push @ldadd, $amlibobjname{$obj};
1274 } else {
1275 map { $sourcefiles{$_} = 1 } @{$objtosrc{$obj}};
1276 }
1277 }
1278 push @progsources, sort { $a cmp $b } keys %sourcefiles;
1279 print &splitline(join " ", @progsources), "\n";
1280 if ($type eq "X") {
1281 push @ldadd, "\$(GTK_LIBS)";
1282 }
1283 push @ldadd, "-lm";
1284 print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n";
1285 print "\n";
1286 }
1287
1288 foreach $obj (sort { $a cmp $b } keys %amlibsused) {
1289 print &splitline(join " ", "lib${obj}_a_SOURCES", "=",
1290 @{$objtosrc{$obj}}), "\n";
1291 print &splitline(join " ", "lib${obj}_a_CPPFLAGS", "=",
1292 @amcflags, @{$archivecppflags{$obj}}), "\n"
1293 if $archivecppflags{$obj};
1294 print &splitline(join " ", "lib${obj}_a_CFLAGS", "=",
1295 @amcflags, @{$archivecflags{$obj}}), "\n"
1296 if $archivecflags{$obj};
1297 }
1298 print &splitline(join " ", "noinst_LIBRARIES", "=",
1299 sort { $a cmp $b }
1300 map { $amspeciallibs{$_} }
1301 keys %amlibsused),
1302 "\n\n";
1303
1304 print $makefile_extra{'am'} || "";
1305 select STDOUT; close OUT;
1306}
1307
1308if (defined $makefiles{'mpw'}) {
1309 $mftyp = 'mpw';
1310 ##-- MPW Makefile
1311 open OUT, ">$makefiles{'mpw'}"; select OUT;
1312 print
1313 "# Makefile for $project_name under MPW.\n#\n".
1314 "# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1315 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
1316 # MPW command line option is -d not /D
1317 ($_ = $help) =~ s/=\/D/=-d /gs;
1318 print $_;
1319 print "\n\n".
1320 "ROptions = `Echo \"{VER}\" | StreamEdit -e \"1,\$ replace /=(\xc5)\xa81\xb0/ 'STR=\xb6\xb6\xb6\xb6\xb6\"' \xa81 '\xb6\xb6\xb6\xb6\xb6\"'\"`".
1321 "\n".
1322 "C_68K = {C}\n".
1323 "C_CFM68K = {C}\n".
1324 "C_PPC = {PPCC}\n".
1325 "C_Carbon = {PPCC}\n".
1326 "\n".
1327 "# -w 35 disables \"unused parameter\" warnings\n".
1328 "COptions = -i : -i :: -i ::charset -w 35 -w err -proto strict -ansi on \xb6\n".
1329 " -notOnce\n".
1330 "COptions_68K = {COptions} -model far -opt time\n".
1331 "# Enabling \"-opt space\" for CFM-68K gives me undefined references to\n".
1332 "# _\$LDIVT and _\$LMODT.\n".
1333 "COptions_CFM68K = {COptions} -model cfmSeg -opt time\n".
1334 "COptions_PPC = {COptions} -opt size -traceback\n".
1335 "COptions_Carbon = {COptions} -opt size -traceback -d TARGET_API_MAC_CARBON\n".
1336 "\n".
1337 "Link_68K = ILink\n".
1338 "Link_CFM68K = ILink\n".
1339 "Link_PPC = PPCLink\n".
1340 "Link_Carbon = PPCLink\n".
1341 "\n".
1342 "LinkOptions = -c 'pTTY'\n".
1343 "LinkOptions_68K = {LinkOptions} -br 68k -model far -compact\n".
1344 "LinkOptions_CFM68K = {LinkOptions} -br 020 -model cfmseg -compact\n".
1345 "LinkOptions_PPC = {LinkOptions}\n".
1346 "LinkOptions_Carbon = -m __appstart -w {LinkOptions}\n".
1347 "\n".
1348 "Libs_68K = \"{CLibraries}StdCLib.far.o\" \xb6\n".
1349 " \"{Libraries}MacRuntime.o\" \xb6\n".
1350 " \"{Libraries}MathLib.far.o\" \xb6\n".
1351 " \"{Libraries}IntEnv.far.o\" \xb6\n".
1352 " \"{Libraries}Interface.o\" \xb6\n".
1353 " \"{Libraries}Navigation.far.o\" \xb6\n".
1354 " \"{Libraries}OpenTransport.o\" \xb6\n".
1355 " \"{Libraries}OpenTransportApp.o\" \xb6\n".
1356 " \"{Libraries}OpenTptInet.o\" \xb6\n".
1357 " \"{Libraries}UnicodeConverterLib.far.o\"\n".
1358 "\n".
1359 "Libs_CFM = \"{SharedLibraries}InterfaceLib\" \xb6\n".
1360 " \"{SharedLibraries}StdCLib\" \xb6\n".
1361 " \"{SharedLibraries}AppearanceLib\" \xb6\n".
1362 " -weaklib AppearanceLib \xb6\n".
1363 " \"{SharedLibraries}NavigationLib\" \xb6\n".
1364 " -weaklib NavigationLib \xb6\n".
1365 " \"{SharedLibraries}TextCommon\" \xb6\n".
1366 " -weaklib TextCommon \xb6\n".
1367 " \"{SharedLibraries}UnicodeConverter\" \xb6\n".
1368 " -weaklib UnicodeConverter\n".
1369 "\n".
1370 "Libs_CFM68K = {Libs_CFM} \xb6\n".
1371 " \"{CFM68KLibraries}NuMacRuntime.o\"\n".
1372 "\n".
1373 "Libs_PPC = {Libs_CFM} \xb6\n".
1374 " \"{SharedLibraries}ControlsLib\" \xb6\n".
1375 " -weaklib ControlsLib \xb6\n".
1376 " \"{SharedLibraries}WindowsLib\" \xb6\n".
1377 " -weaklib WindowsLib \xb6\n".
1378 " \"{SharedLibraries}OpenTransportLib\" \xb6\n".
1379 " -weaklib OTClientLib \xb6\n".
1380 " -weaklib OTClientUtilLib \xb6\n".
1381 " \"{SharedLibraries}OpenTptInternetLib\" \xb6\n".
1382 " -weaklib OTInetClientLib \xb6\n".
1383 " \"{PPCLibraries}StdCRuntime.o\" \xb6\n".
1384 " \"{PPCLibraries}PPCCRuntime.o\" \xb6\n".
1385 " \"{PPCLibraries}CarbonAccessors.o\" \xb6\n".
1386 " \"{PPCLibraries}OpenTransportAppPPC.o\" \xb6\n".
1387 " \"{PPCLibraries}OpenTptInetPPC.o\"\n".
1388 "\n".
1389 "Libs_Carbon = \"{PPCLibraries}CarbonStdCLib.o\" \xb6\n".
1390 " \"{PPCLibraries}StdCRuntime.o\" \xb6\n".
1391 " \"{PPCLibraries}PPCCRuntime.o\" \xb6\n".
1392 " \"{SharedLibraries}CarbonLib\" \xb6\n".
1393 " \"{SharedLibraries}StdCLib\"\n".
1394 "\n";
1395 print &splitline("all \xc4 " . join(" ", &progrealnames("M")), undef, "\xb6");
1396 print "\n\n";
1397 foreach $p (&prognames("M")) {
1398 ($prog, $type) = split ",", $p;
1399
1400 print &splitline("$prog \xc4 $prog.68k $prog.ppc $prog.carbon",
1401 undef, "\xb6"), "\n\n";
1402
1403 $rsrc = &objects($p, "", "X.rsrc", undef);
1404
1405 foreach $arch (qw(68K CFM68K PPC Carbon)) {
1406 $objstr = &objects($p, "X.\L$arch\E.o", "", undef);
1407 print &splitline("$prog.\L$arch\E \xc4 $objstr $rsrc", undef, "\xb6");
1408 print "\n";
1409 print &splitline("\tDuplicate -y $rsrc {Targ}", 69, "\xb6"), "\n";
1410 print &splitline("\t{Link_$arch} -o {Targ} -fragname $prog " .
1411 "{LinkOptions_$arch} " .
1412 $objstr . " {Libs_$arch}", 69, "\xb6"), "\n";
1413 print &splitline("\tSetFile -a BMi {Targ}", 69, "\xb6"), "\n\n";
1414 }
1415
1416 }
1417 foreach $d (&deps("", "X.rsrc", "::", ":")) {
1418 next unless $d->{obj};
1419 print &splitline(sprintf("%s \xc4 %s", $d->{obj}, join " ", @{$d->{deps}}),
1420 undef, "\xb6"), "\n";
1421 print "\tRez ", $d->{deps}->[0], " -o {Targ} {ROptions}\n\n";
1422 }
1423 foreach $arch (qw(68K CFM68K)) {
1424 foreach $d (&deps("X.\L$arch\E.o", "", "::", ":")) {
1425 next unless $d->{obj};
1426 print &splitline(sprintf("%s \xc4 %s", $d->{obj},
1427 join " ", @{$d->{deps}}),
1428 undef, "\xb6"), "\n";
1429 print "\t{C_$arch} ", $d->{deps}->[0],
1430 " -o {Targ} {COptions_$arch}\n\n";
1431 }
1432 }
1433 foreach $arch (qw(PPC Carbon)) {
1434 foreach $d (&deps("X.\L$arch\E.o", "", "::", ":")) {
1435 next unless $d->{obj};
1436 print &splitline(sprintf("%s \xc4 %s", $d->{obj},
1437 join " ", @{$d->{deps}}),
1438 undef, "\xb6"), "\n";
1439 # The odd stuff here seems to stop afpd getting confused.
1440 print "\techo -n > {Targ}\n";
1441 print "\tsetfile -t XCOF {Targ}\n";
1442 print "\t{C_$arch} ", $d->{deps}->[0],
1443 " -o {Targ} {COptions_$arch}\n\n";
1444 }
1445 }
1446 select STDOUT; close OUT;
1447}
1448
1449if (defined $makefiles{'lcc'}) {
1450 $mftyp = 'lcc';
1451 $dirpfx = &dirpfx($makefiles{'lcc'}, "\\");
1452
1453 ##-- lcc makefile
1454 open OUT, ">$makefiles{'lcc'}"; select OUT;
1455 print
1456 "# Makefile for $project_name under lcc.\n".
1457 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1458 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
1459 # lcc command line option is -D not /D
1460 ($_ = $help) =~ s/=\/D/=-D/gs;
1461 print $_;
1462 print
1463 "\n".
1464 "# If you rename this file to `Makefile', you should change this line,\n".
1465 "# so that the .rsp files still depend on the correct makefile.\n".
1466 "MAKEFILE = Makefile.lcc\n".
1467 "\n".
1468 "# C compilation flags\n".
1469 "CFLAGS = -D_WINDOWS " .
1470 (join " ", map {"-I$dirpfx$_"} @srcdirs) .
1471 "\n".
1472 "\n".
1473 "# Get include directory for resource compiler\n".
1474 "\n";
1475 print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C"));
1476 print "\n\n";
1477 foreach $p (&prognames("G:C")) {
1478 ($prog, $type) = split ",", $p;
1479 $objstr = &objects($p, "X.obj", "X.res", undef);
1480 print &splitline("$prog.exe: " . $objstr ), "\n";
1481 $subsystemtype = undef;
1482 if ($type eq "G") { $subsystemtype = "-subsystem windows"; }
1483 my $libss = "shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib";
1484 print &splitline("\tlcclnk $subsystemtype -o $prog.exe $objstr $libss");
1485 print "\n\n";
1486 }
1487
1488 foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) {
1489 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
1490 "\n";
1491 if ($d->{obj} =~ /\.res$/) {
1492 print &splitline("\tlrc \$(FWHACK) \$(RCFL) -r \$*.rc",69)."\n";
1493 } else {
1494 $deflist = join "", map { " -D$_" } @{$d->{defs}};
1495 print &splitline("\tlcc -O -p6 \$(COMPAT) \$(FWHACK) \$(CFLAGS)".
1496 " \$(XFLAGS)$deflist ".$d->{deps}->[0]." -o \$\@",69)."\n";
1497 }
1498 }
1499 print "\n";
1500 print $makefile_extra{'lcc'} || "";
1501 print "\nclean:\n".
1502 "\t-del *.obj\n".
1503 "\t-del *.exe\n".
1504 "\t-del *.res\n";
1505
1506 select STDOUT; close OUT;
1507}
1508
1509if (defined $makefiles{'nestedvm'}) {
1510 $mftyp = 'nestedvm';
1511 $dirpfx = &dirpfx($makefiles{'nestedvm'}, "/");
1512
1513 ##-- NestedVM makefile
1514 open OUT, ">$makefiles{'nestedvm'}"; select OUT;
1515 print
1516 "# Makefile for $project_name under NestedVM.\n".
1517 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1518 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
1519 # gcc command line option is -D not /D
1520 ($_ = $help) =~ s/=\/D/=-D/gs;
1521 print $_;
1522 print
1523 "\n".
1524 "# This path points at the nestedvm root directory\n".
1525 "NESTEDVM = /opt/nestedvm\n".
1526 "# You can define this path to point at your tools if you need to\n".
1527 "TOOLPATH = \$(NESTEDVM)/upstream/install/bin\n".
1528 "CC = \$(TOOLPATH)/mips-unknown-elf-gcc\n".
1529 "\n".
1530 &splitline("CFLAGS = -O2 -Wall -Werror -DSLOW_SYSTEM -g " .
1531 (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
1532 "\n";
1533 print &splitline("all:" . join "", map { " $_.jar" } &progrealnames("X"));
1534 print "\n\n";
1535 foreach $p (&prognames("X")) {
1536 ($prog, $type) = split ",", $p;
1537 $objstr = &objects($p, "X.o", undef, undef);
1538 $objstr =~ s/gtk\.o/nestedvm\.o/g;
1539 print &splitline($prog . ".mips: " . $objstr), "\n";
1540 $libstr = &objects($p, undef, undef, "-lX");
1541 print &splitline("\t\$(CC) \$(${type}LDFLAGS) -o \$@ " .
1542 $objstr . " $libstr -lm", 69), "\n\n";
1543 }
1544 foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
1545 $oobjs = $d->{obj};
1546 $ddeps= join " ", @{$d->{deps}};
1547 $oobjs =~ s/gtk/nestedvm/g;
1548 $ddeps =~ s/gtk/nestedvm/g;
1549 print &splitline(sprintf("%s: %s", $oobjs, $ddeps)),
1550 "\n";
1551 $deflist = join "", map { " -D$_" } @{$d->{defs}};
1552 print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
1553 " -c \$< -o \$\@\n";
1554 }
1555 print "\n";
1556 print $makefile_extra{'nestedvm'} || "";
1557 print "\nclean:\n".
1558 "\trm -rf *.o *.mips *.class *.html *.jar org applet.manifest\n";
1559 select STDOUT; close OUT;
1560}
1561
1562if (defined $makefiles{'osx'}) {
1563 $mftyp = 'osx';
1564 $dirpfx = &dirpfx($makefiles{'osx'}, "/");
1565 @osxarchs = ('i386');
1566
1567 ##-- Mac OS X makefile
1568 open OUT, ">$makefiles{'osx'}"; select OUT;
1569 print
1570 "# Makefile for $project_name under Mac OS X.\n".
1571 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1572 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
1573 # gcc command line option is -D not /D
1574 ($_ = $help) =~ s/=\/D/=-D/gs;
1575 print $_;
1576 print
1577 "CC = \$(TOOLPATH)gcc\n".
1578 "LIPO = \$(TOOLPATH)lipo\n".
1579 "\n".
1580 &splitline("CFLAGS = -O2 -Wall -Werror -g " .
1581 (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
1582 "LDFLAGS = -framework Cocoa\n".
1583 &splitline("all:" . join "", map { " $_" } &progrealnames("MX:U")) .
1584 "\n";
1585 print $makefile_extra{'osx'} || "";
1586 print "\n".
1587 ".SUFFIXES: .o .c .m\n".
1588 "\n";
1589 print "\n\n";
1590 foreach $p (&prognames("MX")) {
1591 ($prog, $type) = split ",", $p;
1592 $icon = &special($p, ".icns");
1593 $infoplist = &special($p, "info.plist");
1594 print "${prog}.app:\n\tmkdir -p \$\@\n";
1595 print "${prog}.app/Contents: ${prog}.app\n\tmkdir -p \$\@\n";
1596 print "${prog}.app/Contents/MacOS: ${prog}.app/Contents\n\tmkdir -p \$\@\n";
1597 $targets = "${prog}.app/Contents/MacOS/$prog";
1598 if (defined $icon) {
1599 print "${prog}.app/Contents/Resources: ${prog}.app/Contents\n\tmkdir -p \$\@\n";
1600 print "${prog}.app/Contents/Resources/${prog}.icns: ${prog}.app/Contents/Resources $icon\n\tcp $icon \$\@\n";
1601 $targets .= " ${prog}.app/Contents/Resources/${prog}.icns";
1602 }
1603 if (defined $infoplist) {
1604 print "${prog}.app/Contents/Info.plist: ${prog}.app/Contents/Resources $infoplist\n\tcp $infoplist \$\@\n";
1605 $targets .= " ${prog}.app/Contents/Info.plist";
1606 }
1607 $targets .= " \$(${prog}_extra)";
1608 print &splitline("${prog}: $targets", 69) . "\n\n";
1609 $libstr = &objects($p, undef, undef, "-lX");
1610 $archbins = "";
1611 foreach $arch (@osxarchs) {
1612 $objstr = &objects($p, "X.${arch}.o", undef, undef);
1613 print &splitline("${prog}.${arch}.bin: " . $objstr), "\n";
1614 print &splitline("\t\$(CC) -arch ${arch} -mmacosx-version-min=10.4 \$(LDFLAGS) -o \$@ " .
1615 $objstr . " $libstr", 69), "\n\n";
1616 $archbins .= " ${prog}.${arch}.bin";
1617 }
1618 print &splitline("${prog}.app/Contents/MacOS/$prog: ".
1619 "${prog}.app/Contents/MacOS" . $archbins), "\n";
1620 print &splitline("\t\$(LIPO) -create $archbins -output \$@", 69), "\n\n";
1621 }
1622 foreach $p (&prognames("U")) {
1623 ($prog, $type) = split ",", $p;
1624 $libstr = &objects($p, undef, undef, "-lX");
1625 $archbins = "";
1626 foreach $arch (@osxarchs) {
1627 $objstr = &objects($p, "X.${arch}.o", undef, undef);
1628 print &splitline("${prog}.${arch}: " . $objstr), "\n";
1629 print &splitline("\t\$(CC) -arch ${arch} -mmacosx-version-min=10.4 \$(ULDFLAGS) -o \$@ " .
1630 $objstr . " $libstr", 69), "\n\n";
1631 $archbins .= " ${prog}.${arch}";
1632 }
1633 print &splitline("${prog}:" . $archbins), "\n";
1634 print &splitline("\t\$(LIPO) -create $archbins -output \$@", 69), "\n\n";
1635 }
1636 foreach $arch (@osxarchs) {
1637 foreach $d (&deps("X.${arch}.o", undef, $dirpfx, "/")) {
1638 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
1639 "\n";
1640 $deflist = join "", map { " -D$_" } @{$d->{defs}};
1641 if ($d->{deps}->[0] =~ /\.m$/) {
1642 print "\t\$(CC) -arch $arch -mmacosx-version-min=10.4 -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS)".
1643 " \$(XFLAGS)$deflist -c \$< -o \$\@\n";
1644 } else {
1645 print "\t\$(CC) -arch $arch -mmacosx-version-min=10.4 \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
1646 " -c \$< -o \$\@\n";
1647 }
1648 }
1649 }
1650 print "\nclean:\n".
1651 "\trm -f *.o *.dmg". (join "", map { my $a=$_; (" $a", map { " ${a}.$_" } @osxarchs) } &progrealnames("U")) . "\n".
1652 "\trm -rf *.app\n";
1653 select STDOUT; close OUT;
1654}
1655
1656if (defined $makefiles{'gnustep'}) {
1657 $mftyp = 'gnustep';
1658 $dirpfx = &dirpfx($makefiles{'gnustep'}, "/");
1659
1660 ##-- GNUstep makefile (use with 'gs_make -f Makefile.gnustep')
1661
1662 # This is a pretty evil way to do things. In an ideal world, I'd
1663 # use the approved GNUstep makefile mechanism which just defines a
1664 # variable or two saying what source files go into what binary and
1665 # then includes application.make. Unfortunately, that has the
1666 # automake-ish limitation that it doesn't let you choose different
1667 # command lines for each object, so I can't arrange for all those
1668 # files with -DTHIS and -DTHAT to Just Work.
1669 #
1670 # A simple if ugly fix would be to have mkfiles.pl construct a
1671 # directory full of stub C files of the form '#define thing',
1672 # '#include "real_source_file"', and then reference those in this
1673 # makefile. That would also make it easy to build a proper
1674 # automake makefile.
1675 open OUT, ">$makefiles{'gnustep'}"; select OUT;
1676 print
1677 "# Makefile for $project_name under GNUstep.\n".
1678 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1679 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
1680 # gcc command line option is -D not /D
1681 ($_ = $help) =~ s/=\/D/=-D/gs;
1682 print $_;
1683 print
1684 "NEEDS_GUI=yes\n".
1685 "include \$(GNUSTEP_MAKEFILES)/common.make\n".
1686 "include \$(GNUSTEP_MAKEFILES)/rules.make\n".
1687 "include \$(GNUSTEP_MAKEFILES)/Instance/rules.make\n".
1688 "\n".
1689 &splitline("all::" . join "", map { " $_" } &progrealnames("MX:U")) .
1690 "\n";
1691 print $makefile_extra{'gnustep'} || "";
1692 print "\n".
1693 ".SUFFIXES: .o .c .m\n".
1694 "\n";
1695 print "\n\n";
1696 foreach $p (&prognames("MX")) {
1697 ($prog, $type) = split ",", $p;
1698 $icon = &special($p, ".icns");
1699 $infoplist = &special($p, "info.plist");
1700 print "${prog}.app:\n\tmkdir -p \$\@\n";
1701 $targets = "${prog}.app ${prog}.app/$prog";
1702 if (defined $icon) {
1703 print "${prog}.app/Resources: ${prog}.app\n\tmkdir -p \$\@\n";
1704 print "${prog}.app/Resources/${prog}.icns: ${prog}.app/Resources $icon\n\tcp $icon \$\@\n";
1705 $targets .= " ${prog}.app/Resources/${prog}.icns";
1706 }
1707 if (defined $infoplist) {
1708 print "${prog}.app/Info.plist: ${prog}.app $infoplist\n\tcp $infoplist \$\@\n";
1709 $targets .= " ${prog}.app/Info.plist";
1710 }
1711 $targets .= " \$(${prog}_extra)";
1712 print &splitline("${prog}: $targets", 69) . "\n\n";
1713 $libstr = &objects($p, undef, undef, "-lX");
1714 $objstr = &objects($p, "X.o", undef, undef);
1715 print &splitline("${prog}.app/$prog: " . $objstr), "\n";
1716 print &splitline("\t\$(CC) \$(ALL_LDFLAGS) -o \$@ " . $objstr . " \$(ALL_LIB_DIRS) $libstr \$(ALL_LIBS)", 69), "\n\n";
1717 }
1718 foreach $p (&prognames("U")) {
1719 ($prog, $type) = split ",", $p;
1720 $libstr = &objects($p, undef, undef, "-lX");
1721 $objstr = &objects($p, "X.o", undef, undef);
1722 print &splitline("${prog}: " . $objstr), "\n";
1723 print &splitline("\t\$(CC) \$(ULDFLAGS) -o \$@ " . $objstr . " $libstr", 69), "\n\n";
1724 }
1725 foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
1726 print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})),
1727 "\n";
1728 $deflist = join "", map { " -D$_" } @{$d->{defs}};
1729 if ($d->{deps}->[0] =~ /\.m$/) {
1730 print "\t\$(CC) -DGNUSTEP \$(ALL_OBJCFLAGS) \$(COMPAT) \$(FWHACK) \$(OBJCFLAGS)".
1731 " \$(XFLAGS)$deflist -c \$< -o \$\@\n";
1732 } else {
1733 print "\t\$(CC) \$(ALL_CFLAGS) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" .
1734 " -c \$< -o \$\@\n";
1735 }
1736 }
1737 print "\nclean::\n".
1738 "\trm -f *.o ". (join " ", &progrealnames("U")) . "\n".
1739 "\trm -rf *.app\n";
1740 select STDOUT; close OUT;
1741}
1742
1743if (defined $makefiles{'emcc'}) {
1744 $mftyp = 'emcc';
1745 $dirpfx = &dirpfx($makefiles{'emcc'}, "/");
1746
1747 ##-- Makefile for building Javascript puzzles via Emscripten
1748
1749 open OUT, ">$makefiles{'emcc'}"; select OUT;
1750 print
1751 "# Makefile for $project_name using Emscripten. Requires GNU make.\n".
1752 "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
1753 "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
1754 # emcc command line option is -D not /D
1755 ($_ = $help) =~ s/=\/D/=-D/gs;
1756 print $_;
1757 print
1758 "\n".
1759 "# This can be set on the command line to point at the emcc command,\n".
1760 "# if it is not on your PATH.\n".
1761 "EMCC = emcc\n".
1762 "\n".
1763 &splitline("CFLAGS = -DSLOW_SYSTEM " .
1764 (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n".
1765 "\n";
1766 $output_js_files = join "", map { " \$(OUTPREFIX)$_.js" } &progrealnames("X");
1767 print &splitline("all:" . $output_js_files);
1768 print "\n\n";
1769 foreach $p (&prognames("X")) {
1770 ($prog, $type) = split ",", $p;
1771 $objstr = &objects($p, "X.o", undef, undef);
1772 $objstr =~ s/gtk\.o/emcc\.o/g;
1773 print &splitline("\$(OUTPREFIX)" . $prog . ".js: " . $objstr . " emccpre.js emcclib.js emccx.json"), "\n";
1774 print "\t\$(EMCC) -o \$(OUTPREFIX)".$prog.".js ".
1775 "-O2 ".
1776 "-s ASM_JS=1 ".
1777 "--pre-js emccpre.js ".
1778 "--js-library emcclib.js ".
1779 "-s EXPORTED_FUNCTIONS=\"`sed 's://.*::' emccx.json | tr -d ' \\n'`\" " . $objstr . "\n\n";
1780 }
1781 foreach $d (&deps("X.o", undef, $dirpfx, "/")) {
1782 $oobjs = $d->{obj};
1783 $ddeps= join " ", @{$d->{deps}};
1784 $oobjs =~ s/gtk/emcc/g;
1785 $ddeps =~ s/gtk/emcc/g;
1786 print &splitline(sprintf("%s: %s", $oobjs, $ddeps)),
1787 "\n";
1788 $deflist = join "", map { " -D$_" } @{$d->{defs}};
1789 print "\t\$(EMCC) \$(CFLAGS) \$(XFLAGS)$deflist" .
1790 " -c \$< -o \$\@\n";
1791 }
1792 print "\n";
1793 print $makefile_extra{'emcc'} || "";
1794 print "\nclean:\n".
1795 "\trm -rf *.o $output_js_files\n";
1796 select STDOUT; close OUT;
1797}
1798
1799# All done, so do the Unix postprocessing if asked to.
1800
1801if ($do_unix) {
1802 chdir $orig_dir;
1803 system "./mkauto.sh";
1804 die "mkfiles.pl: mkauto.sh returned $?\n" if $? > 0;
1805 system "./configure", @confargs;
1806 die "mkfiles.pl: configure returned $?\n" if $? > 0;
1807}
diff --git a/apps/plugins/puzzles/src/nestedvm.c b/apps/plugins/puzzles/src/nestedvm.c
new file mode 100644
index 0000000000..79b797116f
--- /dev/null
+++ b/apps/plugins/puzzles/src/nestedvm.c
@@ -0,0 +1,447 @@
1/*
2 * nestedvm.c: NestedVM front end for my puzzle collection.
3 */
4
5#include <stdio.h>
6#include <assert.h>
7#include <stdlib.h>
8#include <time.h>
9#include <stdarg.h>
10#include <string.h>
11#include <errno.h>
12
13#include <sys/time.h>
14
15#include "puzzles.h"
16
17extern void _pause();
18extern int _call_java(int cmd, int arg1, int arg2, int arg3);
19
20void fatal(char *fmt, ...)
21{
22 va_list ap;
23 fprintf(stderr, "fatal error: ");
24 va_start(ap, fmt);
25 vfprintf(stderr, fmt, ap);
26 va_end(ap);
27 fprintf(stderr, "\n");
28 exit(1);
29}
30
31struct frontend {
32 // TODO kill unneeded members!
33 midend *me;
34 int timer_active;
35 struct timeval last_time;
36 config_item *cfg;
37 int cfg_which, cfgret;
38 int ox, oy, w, h;
39};
40
41static frontend *_fe;
42
43void get_random_seed(void **randseed, int *randseedsize)
44{
45 struct timeval *tvp = snew(struct timeval);
46 gettimeofday(tvp, NULL);
47 *randseed = (void *)tvp;
48 *randseedsize = sizeof(struct timeval);
49}
50
51void frontend_default_colour(frontend *fe, float *output)
52{
53 output[0] = output[1]= output[2] = 0.8f;
54}
55
56void nestedvm_status_bar(void *handle, char *text)
57{
58 _call_java(4,0,(int)text,0);
59}
60
61void nestedvm_start_draw(void *handle)
62{
63 frontend *fe = (frontend *)handle;
64 _call_java(5, 0, fe->w, fe->h);
65 _call_java(4, 1, fe->ox, fe->oy);
66}
67
68void nestedvm_clip(void *handle, int x, int y, int w, int h)
69{
70 frontend *fe = (frontend *)handle;
71 _call_java(5, w, h, 0);
72 _call_java(4, 3, x + fe->ox, y + fe->oy);
73}
74
75void nestedvm_unclip(void *handle)
76{
77 frontend *fe = (frontend *)handle;
78 _call_java(4, 4, fe->ox, fe->oy);
79}
80
81void nestedvm_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
82 int align, int colour, char *text)
83{
84 frontend *fe = (frontend *)handle;
85 _call_java(5, x + fe->ox, y + fe->oy,
86 (fonttype == FONT_FIXED ? 0x10 : 0x0) | align);
87 _call_java(7, fontsize, colour, (int)text);
88}
89
90void nestedvm_draw_rect(void *handle, int x, int y, int w, int h, int colour)
91{
92 frontend *fe = (frontend *)handle;
93 _call_java(5, w, h, colour);
94 _call_java(4, 5, x + fe->ox, y + fe->oy);
95}
96
97void nestedvm_draw_line(void *handle, int x1, int y1, int x2, int y2,
98 int colour)
99{
100 frontend *fe = (frontend *)handle;
101 _call_java(5, x2 + fe->ox, y2 + fe->oy, colour);
102 _call_java(4, 6, x1 + fe->ox, y1 + fe->oy);
103}
104
105void nestedvm_draw_poly(void *handle, int *coords, int npoints,
106 int fillcolour, int outlinecolour)
107{
108 frontend *fe = (frontend *)handle;
109 int i;
110 _call_java(4, 7, npoints, 0);
111 for (i = 0; i < npoints; i++) {
112 _call_java(6, i, coords[i*2] + fe->ox, coords[i*2+1] + fe->oy);
113 }
114 _call_java(4, 8, outlinecolour, fillcolour);
115}
116
117void nestedvm_draw_circle(void *handle, int cx, int cy, int radius,
118 int fillcolour, int outlinecolour)
119{
120 frontend *fe = (frontend *)handle;
121 _call_java(5, cx+fe->ox, cy+fe->oy, radius);
122 _call_java(4, 9, outlinecolour, fillcolour);
123}
124
125struct blitter {
126 int handle, w, h, x, y;
127};
128
129blitter *nestedvm_blitter_new(void *handle, int w, int h)
130{
131 blitter *bl = snew(blitter);
132 bl->handle = -1;
133 bl->w = w;
134 bl->h = h;
135 return bl;
136}
137
138void nestedvm_blitter_free(void *handle, blitter *bl)
139{
140 if (bl->handle != -1)
141 _call_java(4, 11, bl->handle, 0);
142 sfree(bl);
143}
144
145void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y)
146{
147 frontend *fe = (frontend *)handle;
148 if (bl->handle == -1)
149 bl->handle = _call_java(4,10,bl->w, bl->h);
150 bl->x = x;
151 bl->y = y;
152 _call_java(8, bl->handle, x + fe->ox, y + fe->oy);
153}
154
155void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y)
156{
157 frontend *fe = (frontend *)handle;
158 assert(bl->handle != -1);
159 if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
160 x = bl->x;
161 y = bl->y;
162 }
163 _call_java(9, bl->handle, x + fe->ox, y + fe->oy);
164}
165
166void nestedvm_end_draw(void *handle)
167{
168 _call_java(4,2,0,0);
169}
170
171char *nestedvm_text_fallback(void *handle, const char *const *strings,
172 int nstrings)
173{
174 /*
175 * We assume Java can cope with any UTF-8 likely to be emitted
176 * by a puzzle.
177 */
178 return dupstr(strings[0]);
179}
180
181const struct drawing_api nestedvm_drawing = {
182 nestedvm_draw_text,
183 nestedvm_draw_rect,
184 nestedvm_draw_line,
185 nestedvm_draw_poly,
186 nestedvm_draw_circle,
187 NULL, // draw_update,
188 nestedvm_clip,
189 nestedvm_unclip,
190 nestedvm_start_draw,
191 nestedvm_end_draw,
192 nestedvm_status_bar,
193 nestedvm_blitter_new,
194 nestedvm_blitter_free,
195 nestedvm_blitter_save,
196 nestedvm_blitter_load,
197 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
198 NULL, NULL, /* line_width, line_dotted */
199 nestedvm_text_fallback,
200};
201
202int jcallback_key_event(int x, int y, int keyval)
203{
204 frontend *fe = (frontend *)_fe;
205 if (fe->ox == -1)
206 return 1;
207 if (keyval >= 0 &&
208 !midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval))
209 return 42;
210 return 1;
211}
212
213int jcallback_resize(int width, int height)
214{
215 frontend *fe = (frontend *)_fe;
216 int x, y;
217 x = width;
218 y = height;
219 midend_size(fe->me, &x, &y, TRUE);
220 fe->ox = (width - x) / 2;
221 fe->oy = (height - y) / 2;
222 fe->w = x;
223 fe->h = y;
224 midend_force_redraw(fe->me);
225 return 0;
226}
227
228int jcallback_timer_func()
229{
230 frontend *fe = (frontend *)_fe;
231 if (fe->timer_active) {
232 struct timeval now;
233 float elapsed;
234 gettimeofday(&now, NULL);
235 elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
236 (now.tv_sec - fe->last_time.tv_sec));
237 midend_timer(fe->me, elapsed); /* may clear timer_active */
238 fe->last_time = now;
239 }
240 return fe->timer_active;
241}
242
243void deactivate_timer(frontend *fe)
244{
245 if (fe->timer_active)
246 _call_java(4, 13, 0, 0);
247 fe->timer_active = FALSE;
248}
249
250void activate_timer(frontend *fe)
251{
252 if (!fe->timer_active) {
253 _call_java(4, 12, 0, 0);
254 gettimeofday(&fe->last_time, NULL);
255 }
256 fe->timer_active = TRUE;
257}
258
259void jcallback_config_ok()
260{
261 frontend *fe = (frontend *)_fe;
262 char *err;
263
264 err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
265
266 if (err)
267 _call_java(2, (int) "Error", (int)err, 1);
268 else {
269 fe->cfgret = TRUE;
270 }
271}
272
273void jcallback_config_set_string(int item_ptr, int char_ptr) {
274 config_item *i = (config_item *)item_ptr;
275 char* newval = (char*) char_ptr;
276 sfree(i->sval);
277 i->sval = dupstr(newval);
278 free(newval);
279}
280
281void jcallback_config_set_boolean(int item_ptr, int selected) {
282 config_item *i = (config_item *)item_ptr;
283 i->ival = selected != 0 ? TRUE : FALSE;
284}
285
286void jcallback_config_set_choice(int item_ptr, int selected) {
287 config_item *i = (config_item *)item_ptr;
288 i->ival = selected;
289}
290
291static int get_config(frontend *fe, int which)
292{
293 char *title;
294 config_item *i;
295 fe->cfg = midend_get_config(fe->me, which, &title);
296 fe->cfg_which = which;
297 fe->cfgret = FALSE;
298 _call_java(10, (int)title, 0, 0);
299 for (i = fe->cfg; i->type != C_END; i++) {
300 _call_java(5, (int)i, i->type, (int)i->name);
301 _call_java(11, (int)i->sval, i->ival, 0);
302 }
303 _call_java(12,0,0,0);
304 free_cfg(fe->cfg);
305 return fe->cfgret;
306}
307
308int jcallback_menu_key_event(int key)
309{
310 frontend *fe = (frontend *)_fe;
311 if (!midend_process_key(fe->me, 0, 0, key))
312 return 42;
313 return 0;
314}
315
316static void resize_fe(frontend *fe)
317{
318 int x, y;
319
320 x = INT_MAX;
321 y = INT_MAX;
322 midend_size(fe->me, &x, &y, FALSE);
323 _call_java(3, x, y, 0);
324}
325
326int jcallback_preset_event(int ptr_game_params)
327{
328 frontend *fe = (frontend *)_fe;
329 game_params *params =
330 (game_params *)ptr_game_params;
331
332 midend_set_params(fe->me, params);
333 midend_new_game(fe->me);
334 resize_fe(fe);
335 _call_java(13, midend_which_preset(fe->me), 0, 0);
336 return 0;
337}
338
339int jcallback_solve_event()
340{
341 frontend *fe = (frontend *)_fe;
342 char *msg;
343
344 msg = midend_solve(fe->me);
345
346 if (msg)
347 _call_java(2, (int) "Error", (int)msg, 1);
348 return 0;
349}
350
351int jcallback_restart_event()
352{
353 frontend *fe = (frontend *)_fe;
354
355 midend_restart_game(fe->me);
356 return 0;
357}
358
359int jcallback_config_event(int which)
360{
361 frontend *fe = (frontend *)_fe;
362 _call_java(13, midend_which_preset(fe->me), 0, 0);
363 if (!get_config(fe, which))
364 return 0;
365 midend_new_game(fe->me);
366 resize_fe(fe);
367 _call_java(13, midend_which_preset(fe->me), 0, 0);
368 return 0;
369}
370
371int jcallback_about_event()
372{
373 char titlebuf[256];
374 char textbuf[1024];
375
376 sprintf(titlebuf, "About %.200s", thegame.name);
377 sprintf(textbuf,
378 "%.200s\n\n"
379 "from Simon Tatham's Portable Puzzle Collection\n\n"
380 "%.500s", thegame.name, ver);
381 _call_java(2, (int)&titlebuf, (int)&textbuf, 0);
382 return 0;
383}
384
385void preset_menu_populate(struct preset_menu *menu, int menuid)
386{
387 int i;
388
389 for (i = 0; i < menu->n_entries; i++) {
390 struct preset_menu_entry *entry = &menu->entries[i];
391 if (entry->params) {
392 _call_java(5, (int)entry->params, 0, 0);
393 _call_java(1, (int)entry->title, menuid, entry->id);
394 } else {
395 _call_java(5, 0, 0, 0);
396 _call_java(1, (int)entry->title, menuid, entry->id);
397 preset_menu_populate(entry->submenu, entry->id);
398 }
399 }
400}
401
402int main(int argc, char **argv)
403{
404 int i, n;
405 float* colours;
406
407 _fe = snew(frontend);
408 _fe->timer_active = FALSE;
409 _fe->me = midend_new(_fe, &thegame, &nestedvm_drawing, _fe);
410 if (argc > 1)
411 midend_game_id(_fe->me, argv[1]); /* ignore failure */
412 midend_new_game(_fe->me);
413
414 {
415 struct preset_menu *menu;
416 int nids, topmenu;
417 menu = midend_get_presets(_fe->me, &nids);
418 topmenu = _call_java(1, 0, nids, 0);
419 preset_menu_populate(menu, topmenu);
420 }
421
422 colours = midend_colours(_fe->me, &n);
423 _fe->ox = -1;
424
425 _call_java(0, (int)thegame.name,
426 (thegame.can_configure ? 1 : 0) |
427 (midend_wants_statusbar(_fe->me) ? 2 : 0) |
428 (thegame.can_solve ? 4 : 0), n);
429 for (i = 0; i < n; i++) {
430 _call_java(1024+ i,
431 (int)(colours[i*3] * 0xFF),
432 (int)(colours[i*3+1] * 0xFF),
433 (int)(colours[i*3+2] * 0xFF));
434 }
435 resize_fe(_fe);
436
437 _call_java(13, midend_which_preset(_fe->me), 0, 0);
438
439 // Now pause the vm. The VM will be call()ed when
440 // an input event occurs.
441 _pause();
442
443 // shut down when the VM is resumed.
444 deactivate_timer(_fe);
445 midend_free(_fe->me);
446 return 0;
447}
diff --git a/apps/plugins/puzzles/src/net.R b/apps/plugins/puzzles/src/net.R
new file mode 100644
index 0000000000..8e98216e03
--- /dev/null
+++ b/apps/plugins/puzzles/src/net.R
@@ -0,0 +1,23 @@
1# -*- makefile -*-
2
3NET_EXTRA = tree234 dsf findloop
4
5net : [X] GTK COMMON net NET_EXTRA net-icon|no-icon
6
7# The Windows Net shouldn't be called `net.exe' since Windows
8# already has a reasonably important utility program by that name!
9netgame : [G] WINDOWS COMMON net NET_EXTRA net.res|noicon.res
10
11ALL += net[COMBINED] NET_EXTRA
12
13!begin am gtk
14GAMES += net
15!end
16
17!begin >list.c
18 A(net) \
19!end
20
21!begin >gamedesc.txt
22net:netgame.exe:Net:Network jigsaw puzzle:Rotate each tile to reassemble the network.
23!end
diff --git a/apps/plugins/puzzles/src/net.c b/apps/plugins/puzzles/src/net.c
new file mode 100644
index 0000000000..de51f82fd7
--- /dev/null
+++ b/apps/plugins/puzzles/src/net.c
@@ -0,0 +1,3240 @@
1/*
2 * net.c: Net game.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13#include "tree234.h"
14
15/*
16 * The standard user interface for Net simply has left- and
17 * right-button mouse clicks in a square rotate it one way or the
18 * other. We also provide, by #ifdef, a separate interface based on
19 * rotational dragging motions. I initially developed this for the
20 * Mac on the basis that it might work better than the click
21 * interface with only one mouse button available, but in fact
22 * found it to be quite strange and unintuitive. Apparently it
23 * works better on stylus-driven platforms such as Palm and
24 * PocketPC, though, so we enable it by default there.
25 */
26#ifdef STYLUS_BASED
27#define USE_DRAGGING
28#endif
29
30#define MATMUL(xr,yr,m,x,y) do { \
31 float rx, ry, xx = (x), yy = (y), *mat = (m); \
32 rx = mat[0] * xx + mat[2] * yy; \
33 ry = mat[1] * xx + mat[3] * yy; \
34 (xr) = rx; (yr) = ry; \
35} while (0)
36
37/* Direction and other bitfields */
38#define R 0x01
39#define U 0x02
40#define L 0x04
41#define D 0x08
42#define LOCKED 0x10
43#define ACTIVE 0x20
44#define RLOOP (R << 6)
45#define ULOOP (U << 6)
46#define LLOOP (L << 6)
47#define DLOOP (D << 6)
48#define LOOP(dir) ((dir) << 6)
49
50/* Rotations: Anticlockwise, Clockwise, Flip, general rotate */
51#define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) )
52#define C(x) ( (((x) & 0x0E) >> 1) | (((x) & 0x01) << 3) )
53#define F(x) ( (((x) & 0x0C) >> 2) | (((x) & 0x03) << 2) )
54#define ROT(x, n) ( ((n)&3) == 0 ? (x) : \
55 ((n)&3) == 1 ? A(x) : \
56 ((n)&3) == 2 ? F(x) : C(x) )
57
58/* X and Y displacements */
59#define X(x) ( (x) == R ? +1 : (x) == L ? -1 : 0 )
60#define Y(x) ( (x) == D ? +1 : (x) == U ? -1 : 0 )
61
62/* Bit count */
63#define COUNT(x) ( (((x) & 0x08) >> 3) + (((x) & 0x04) >> 2) + \
64 (((x) & 0x02) >> 1) + ((x) & 0x01) )
65
66#define PREFERRED_TILE_SIZE 32
67#define TILE_SIZE (ds->tilesize)
68#define TILE_BORDER 1
69#ifdef SMALL_SCREEN
70#define WINDOW_OFFSET 4
71#else
72#define WINDOW_OFFSET 16
73#endif
74
75#define ROTATE_TIME 0.13F
76#define FLASH_FRAME 0.07F
77
78/* Transform physical coords to game coords using game_drawstate ds */
79#define GX(x) (((x) + ds->org_x) % ds->width)
80#define GY(y) (((y) + ds->org_y) % ds->height)
81/* ...and game coords to physical coords */
82#define RX(x) (((x) + ds->width - ds->org_x) % ds->width)
83#define RY(y) (((y) + ds->height - ds->org_y) % ds->height)
84
85enum {
86 COL_BACKGROUND,
87 COL_LOCKED,
88 COL_BORDER,
89 COL_WIRE,
90 COL_ENDPOINT,
91 COL_POWERED,
92 COL_BARRIER,
93 COL_LOOP,
94 NCOLOURS
95};
96
97struct game_params {
98 int width;
99 int height;
100 int wrapping;
101 int unique;
102 float barrier_probability;
103};
104
105struct game_state {
106 int width, height, wrapping, completed;
107 int last_rotate_x, last_rotate_y, last_rotate_dir;
108 int used_solve;
109 unsigned char *tiles;
110 unsigned char *barriers;
111};
112
113#define OFFSETWH(x2,y2,x1,y1,dir,width,height) \
114 ( (x2) = ((x1) + width + X((dir))) % width, \
115 (y2) = ((y1) + height + Y((dir))) % height)
116
117#define OFFSET(x2,y2,x1,y1,dir,state) \
118 OFFSETWH(x2,y2,x1,y1,dir,(state)->width,(state)->height)
119
120#define index(state, a, x, y) ( a[(y) * (state)->width + (x)] )
121#define tile(state, x, y) index(state, (state)->tiles, x, y)
122#define barrier(state, x, y) index(state, (state)->barriers, x, y)
123
124struct xyd {
125 int x, y, direction;
126};
127
128static int xyd_cmp(const void *av, const void *bv) {
129 const struct xyd *a = (const struct xyd *)av;
130 const struct xyd *b = (const struct xyd *)bv;
131 if (a->x < b->x)
132 return -1;
133 if (a->x > b->x)
134 return +1;
135 if (a->y < b->y)
136 return -1;
137 if (a->y > b->y)
138 return +1;
139 if (a->direction < b->direction)
140 return -1;
141 if (a->direction > b->direction)
142 return +1;
143 return 0;
144}
145
146static int xyd_cmp_nc(void *av, void *bv) { return xyd_cmp(av, bv); }
147
148static struct xyd *new_xyd(int x, int y, int direction)
149{
150 struct xyd *xyd = snew(struct xyd);
151 xyd->x = x;
152 xyd->y = y;
153 xyd->direction = direction;
154 return xyd;
155}
156
157/* ----------------------------------------------------------------------
158 * Manage game parameters.
159 */
160static game_params *default_params(void)
161{
162 game_params *ret = snew(game_params);
163
164 ret->width = 5;
165 ret->height = 5;
166 ret->wrapping = FALSE;
167 ret->unique = TRUE;
168 ret->barrier_probability = 0.0;
169
170 return ret;
171}
172
173static const struct game_params net_presets[] = {
174 {5, 5, FALSE, TRUE, 0.0},
175 {7, 7, FALSE, TRUE, 0.0},
176 {9, 9, FALSE, TRUE, 0.0},
177 {11, 11, FALSE, TRUE, 0.0},
178#ifndef SMALL_SCREEN
179 {13, 11, FALSE, TRUE, 0.0},
180#endif
181 {5, 5, TRUE, TRUE, 0.0},
182 {7, 7, TRUE, TRUE, 0.0},
183 {9, 9, TRUE, TRUE, 0.0},
184 {11, 11, TRUE, TRUE, 0.0},
185#ifndef SMALL_SCREEN
186 {13, 11, TRUE, TRUE, 0.0},
187#endif
188};
189
190static int game_fetch_preset(int i, char **name, game_params **params)
191{
192 game_params *ret;
193 char str[80];
194
195 if (i < 0 || i >= lenof(net_presets))
196 return FALSE;
197
198 ret = snew(game_params);
199 *ret = net_presets[i];
200
201 sprintf(str, "%dx%d%s", ret->width, ret->height,
202 ret->wrapping ? " wrapping" : "");
203
204 *name = dupstr(str);
205 *params = ret;
206 return TRUE;
207}
208
209static void free_params(game_params *params)
210{
211 sfree(params);
212}
213
214static game_params *dup_params(const game_params *params)
215{
216 game_params *ret = snew(game_params);
217 *ret = *params; /* structure copy */
218 return ret;
219}
220
221static void decode_params(game_params *ret, char const *string)
222{
223 char const *p = string;
224
225 ret->width = atoi(p);
226 while (*p && isdigit((unsigned char)*p)) p++;
227 if (*p == 'x') {
228 p++;
229 ret->height = atoi(p);
230 while (*p && isdigit((unsigned char)*p)) p++;
231 } else {
232 ret->height = ret->width;
233 }
234
235 while (*p) {
236 if (*p == 'w') {
237 p++;
238 ret->wrapping = TRUE;
239 } else if (*p == 'b') {
240 p++;
241 ret->barrier_probability = (float)atof(p);
242 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
243 } else if (*p == 'a') {
244 p++;
245 ret->unique = FALSE;
246 } else
247 p++; /* skip any other gunk */
248 }
249}
250
251static char *encode_params(const game_params *params, int full)
252{
253 char ret[400];
254 int len;
255
256 len = sprintf(ret, "%dx%d", params->width, params->height);
257 if (params->wrapping)
258 ret[len++] = 'w';
259 if (full && params->barrier_probability)
260 len += sprintf(ret+len, "b%g", params->barrier_probability);
261 if (full && !params->unique)
262 ret[len++] = 'a';
263 assert(len < lenof(ret));
264 ret[len] = '\0';
265
266 return dupstr(ret);
267}
268
269static config_item *game_configure(const game_params *params)
270{
271 config_item *ret;
272 char buf[80];
273
274 ret = snewn(6, config_item);
275
276 ret[0].name = "Width";
277 ret[0].type = C_STRING;
278 sprintf(buf, "%d", params->width);
279 ret[0].sval = dupstr(buf);
280 ret[0].ival = 0;
281
282 ret[1].name = "Height";
283 ret[1].type = C_STRING;
284 sprintf(buf, "%d", params->height);
285 ret[1].sval = dupstr(buf);
286 ret[1].ival = 0;
287
288 ret[2].name = "Walls wrap around";
289 ret[2].type = C_BOOLEAN;
290 ret[2].sval = NULL;
291 ret[2].ival = params->wrapping;
292
293 ret[3].name = "Barrier probability";
294 ret[3].type = C_STRING;
295 sprintf(buf, "%g", params->barrier_probability);
296 ret[3].sval = dupstr(buf);
297 ret[3].ival = 0;
298
299 ret[4].name = "Ensure unique solution";
300 ret[4].type = C_BOOLEAN;
301 ret[4].sval = NULL;
302 ret[4].ival = params->unique;
303
304 ret[5].name = NULL;
305 ret[5].type = C_END;
306 ret[5].sval = NULL;
307 ret[5].ival = 0;
308
309 return ret;
310}
311
312static game_params *custom_params(const config_item *cfg)
313{
314 game_params *ret = snew(game_params);
315
316 ret->width = atoi(cfg[0].sval);
317 ret->height = atoi(cfg[1].sval);
318 ret->wrapping = cfg[2].ival;
319 ret->barrier_probability = (float)atof(cfg[3].sval);
320 ret->unique = cfg[4].ival;
321
322 return ret;
323}
324
325static char *validate_params(const game_params *params, int full)
326{
327 if (params->width <= 0 || params->height <= 0)
328 return "Width and height must both be greater than zero";
329 if (params->width <= 1 && params->height <= 1)
330 return "At least one of width and height must be greater than one";
331 if (params->barrier_probability < 0)
332 return "Barrier probability may not be negative";
333 if (params->barrier_probability > 1)
334 return "Barrier probability may not be greater than 1";
335
336 /*
337 * Specifying either grid dimension as 2 in a wrapping puzzle
338 * makes it actually impossible to ensure a unique puzzle
339 * solution.
340 *
341 * Proof:
342 *
343 * Without loss of generality, let us assume the puzzle _width_
344 * is 2, so we can conveniently discuss rows without having to
345 * say `rows/columns' all the time. (The height may be 2 as
346 * well, but that doesn't matter.)
347 *
348 * In each row, there are two edges between tiles: the inner
349 * edge (running down the centre of the grid) and the outer
350 * edge (the identified left and right edges of the grid).
351 *
352 * Lemma: In any valid 2xn puzzle there must be at least one
353 * row in which _exactly one_ of the inner edge and outer edge
354 * is connected.
355 *
356 * Proof: No row can have _both_ inner and outer edges
357 * connected, because this would yield a loop. So the only
358 * other way to falsify the lemma is for every row to have
359 * _neither_ the inner nor outer edge connected. But this
360 * means there is no connection at all between the left and
361 * right columns of the puzzle, so there are two disjoint
362 * subgraphs, which is also disallowed. []
363 *
364 * Given such a row, it is always possible to make the
365 * disconnected edge connected and the connected edge
366 * disconnected without changing the state of any other edge.
367 * (This is easily seen by case analysis on the various tiles:
368 * left-pointing and right-pointing endpoints can be exchanged,
369 * likewise T-pieces, and a corner piece can select its
370 * horizontal connectivity independently of its vertical.) This
371 * yields a distinct valid solution.
372 *
373 * Thus, for _every_ row in which exactly one of the inner and
374 * outer edge is connected, there are two valid states for that
375 * row, and hence the total number of solutions of the puzzle
376 * is at least 2^(number of such rows), and in particular is at
377 * least 2 since there must be at least one such row. []
378 */
379 if (full && params->unique && params->wrapping &&
380 (params->width == 2 || params->height == 2))
381 return "No wrapping puzzle with a width or height of 2 can have"
382 " a unique solution";
383
384 return NULL;
385}
386
387/* ----------------------------------------------------------------------
388 * Solver used to assure solution uniqueness during generation.
389 */
390
391/*
392 * Test cases I used while debugging all this were
393 *
394 * ./net --generate 1 13x11w#12300
395 * which expands under the non-unique grid generation rules to
396 * 13x11w:5eaade1bd222664436d5e2965c12656b1129dd825219e3274d558d5eb2dab5da18898e571d5a2987be79746bd95726c597447d6da96188c513add829da7681da954db113d3cd244
397 * and has two ambiguous areas.
398 *
399 * An even better one is
400 * 13x11w#507896411361192
401 * which expands to
402 * 13x11w:b7125b1aec598eb31bd58d82572bc11494e5dee4e8db2bdd29b88d41a16bdd996d2996ddec8c83741a1e8674e78328ba71737b8894a9271b1cd1399453d1952e43951d9b712822e
403 * and has an ambiguous area _and_ a situation where loop avoidance
404 * is a necessary deductive technique.
405 *
406 * Then there's
407 * 48x25w#820543338195187
408 * becoming
409 * 48x25w:255989d14cdd185deaa753a93821a12edc1ab97943ac127e2685d7b8b3c48861b2192416139212b316eddd35de43714ebc7628d753db32e596284d9ec52c5a7dc1b4c811a655117d16dc28921b2b4161352cab1d89d18bc836b8b891d55ea4622a1251861b5bc9a8aa3e5bcd745c95229ca6c3b5e21d5832d397e917325793d7eb442dc351b2db2a52ba8e1651642275842d8871d5534aabc6d5b741aaa2d48ed2a7dbbb3151ddb49d5b9a7ed1ab98ee75d613d656dbba347bc514c84556b43a9bc65a3256ead792488b862a9d2a8a39b4255a4949ed7dbd79443292521265896b4399c95ede89d7c8c797a6a57791a849adea489359a158aa12e5dacce862b8333b7ebea7d344d1a3c53198864b73a9dedde7b663abb1b539e1e8853b1b7edb14a2a17ebaae4dbe63598a2e7e9a2dbdad415bc1d8cb88cbab5a8c82925732cd282e641ea3bd7d2c6e776de9117a26be86deb7c82c89524b122cb9397cd1acd2284e744ea62b9279bae85479ababe315c3ac29c431333395b24e6a1e3c43a2da42d4dce84aadd5b154aea555eaddcbd6e527d228c19388d9b424d94214555a7edbdeebe569d4a56dc51a86bd9963e377bb74752bd5eaa5761ba545e297b62a1bda46ab4aee423ad6c661311783cc18786d4289236563cb4a75ec67d481c14814994464cd1b87396dee63e5ab6e952cc584baa1d4c47cb557ec84dbb63d487c8728118673a166846dd3a4ebc23d6cb9c5827d96b4556e91899db32b517eda815ae271a8911bd745447121dc8d321557bc2a435ebec1bbac35b1a291669451174e6aa2218a4a9c5a6ca31ebc45d84e3a82c121e9ced7d55e9a
410 * which has a spot (far right) where slightly more complex loop
411 * avoidance is required.
412 */
413
414struct todo {
415 unsigned char *marked;
416 int *buffer;
417 int buflen;
418 int head, tail;
419};
420
421static struct todo *todo_new(int maxsize)
422{
423 struct todo *todo = snew(struct todo);
424 todo->marked = snewn(maxsize, unsigned char);
425 memset(todo->marked, 0, maxsize);
426 todo->buflen = maxsize + 1;
427 todo->buffer = snewn(todo->buflen, int);
428 todo->head = todo->tail = 0;
429 return todo;
430}
431
432static void todo_free(struct todo *todo)
433{
434 sfree(todo->marked);
435 sfree(todo->buffer);
436 sfree(todo);
437}
438
439static void todo_add(struct todo *todo, int index)
440{
441 if (todo->marked[index])
442 return; /* already on the list */
443 todo->marked[index] = TRUE;
444 todo->buffer[todo->tail++] = index;
445 if (todo->tail == todo->buflen)
446 todo->tail = 0;
447}
448
449static int todo_get(struct todo *todo) {
450 int ret;
451
452 if (todo->head == todo->tail)
453 return -1; /* list is empty */
454 ret = todo->buffer[todo->head++];
455 if (todo->head == todo->buflen)
456 todo->head = 0;
457 todo->marked[ret] = FALSE;
458
459 return ret;
460}
461
462static int net_solver(int w, int h, unsigned char *tiles,
463 unsigned char *barriers, int wrapping)
464{
465 unsigned char *tilestate;
466 unsigned char *edgestate;
467 int *deadends;
468 int *equivalence;
469 struct todo *todo;
470 int i, j, x, y;
471 int area;
472 int done_something;
473
474 /*
475 * Set up the solver's data structures.
476 */
477
478 /*
479 * tilestate stores the possible orientations of each tile.
480 * There are up to four of these, so we'll index the array in
481 * fours. tilestate[(y * w + x) * 4] and its three successive
482 * members give the possible orientations, clearing to 255 from
483 * the end as things are ruled out.
484 *
485 * In this loop we also count up the area of the grid (which is
486 * not _necessarily_ equal to w*h, because there might be one
487 * or more blank squares present. This will never happen in a
488 * grid generated _by_ this program, but it's worth keeping the
489 * solver as general as possible.)
490 */
491 tilestate = snewn(w * h * 4, unsigned char);
492 area = 0;
493 for (i = 0; i < w*h; i++) {
494 tilestate[i * 4] = tiles[i] & 0xF;
495 for (j = 1; j < 4; j++) {
496 if (tilestate[i * 4 + j - 1] == 255 ||
497 A(tilestate[i * 4 + j - 1]) == tilestate[i * 4])
498 tilestate[i * 4 + j] = 255;
499 else
500 tilestate[i * 4 + j] = A(tilestate[i * 4 + j - 1]);
501 }
502 if (tiles[i] != 0)
503 area++;
504 }
505
506 /*
507 * edgestate stores the known state of each edge. It is 0 for
508 * unknown, 1 for open (connected) and 2 for closed (not
509 * connected).
510 *
511 * In principle we need only worry about each edge once each,
512 * but in fact it's easier to track each edge twice so that we
513 * can reference it from either side conveniently. Also I'm
514 * going to allocate _five_ bytes per tile, rather than the
515 * obvious four, so that I can index edgestate[(y*w+x) * 5 + d]
516 * where d is 1,2,4,8 and they never overlap.
517 */
518 edgestate = snewn((w * h - 1) * 5 + 9, unsigned char);
519 memset(edgestate, 0, (w * h - 1) * 5 + 9);
520
521 /*
522 * deadends tracks which edges have dead ends on them. It is
523 * indexed by tile and direction: deadends[(y*w+x) * 5 + d]
524 * tells you whether heading out of tile (x,y) in direction d
525 * can reach a limited amount of the grid. Values are area+1
526 * (no dead end known) or less than that (can reach _at most_
527 * this many other tiles by heading this way out of this tile).
528 */
529 deadends = snewn((w * h - 1) * 5 + 9, int);
530 for (i = 0; i < (w * h - 1) * 5 + 9; i++)
531 deadends[i] = area+1;
532
533 /*
534 * equivalence tracks which sets of tiles are known to be
535 * connected to one another, so we can avoid creating loops by
536 * linking together tiles which are already linked through
537 * another route.
538 *
539 * This is a disjoint set forest structure: equivalence[i]
540 * contains the index of another member of the equivalence
541 * class containing i, or contains i itself for precisely one
542 * member in each such class. To find a representative member
543 * of the equivalence class containing i, you keep replacing i
544 * with equivalence[i] until it stops changing; then you go
545 * _back_ along the same path and point everything on it
546 * directly at the representative member so as to speed up
547 * future searches. Then you test equivalence between tiles by
548 * finding the representative of each tile and seeing if
549 * they're the same; and you create new equivalence (merge
550 * classes) by finding the representative of each tile and
551 * setting equivalence[one]=the_other.
552 */
553 equivalence = snew_dsf(w * h);
554
555 /*
556 * On a non-wrapping grid, we instantly know that all the edges
557 * round the edge are closed.
558 */
559 if (!wrapping) {
560 for (i = 0; i < w; i++) {
561 edgestate[i * 5 + 2] = edgestate[((h-1) * w + i) * 5 + 8] = 2;
562 }
563 for (i = 0; i < h; i++) {
564 edgestate[(i * w + w-1) * 5 + 1] = edgestate[(i * w) * 5 + 4] = 2;
565 }
566 }
567
568 /*
569 * If we have barriers available, we can mark those edges as
570 * closed too.
571 */
572 if (barriers) {
573 for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
574 int d;
575 for (d = 1; d <= 8; d += d) {
576 if (barriers[y*w+x] & d) {
577 int x2, y2;
578 /*
579 * In principle the barrier list should already
580 * contain each barrier from each side, but
581 * let's not take chances with our internal
582 * consistency.
583 */
584 OFFSETWH(x2, y2, x, y, d, w, h);
585 edgestate[(y*w+x) * 5 + d] = 2;
586 edgestate[(y2*w+x2) * 5 + F(d)] = 2;
587 }
588 }
589 }
590 }
591
592 /*
593 * Since most deductions made by this solver are local (the
594 * exception is loop avoidance, where joining two tiles
595 * together on one side of the grid can theoretically permit a
596 * fresh deduction on the other), we can address the scaling
597 * problem inherent in iterating repeatedly over the entire
598 * grid by instead working with a to-do list.
599 */
600 todo = todo_new(w * h);
601
602 /*
603 * Main deductive loop.
604 */
605 done_something = TRUE; /* prevent instant termination! */
606 while (1) {
607 int index;
608
609 /*
610 * Take a tile index off the todo list and process it.
611 */
612 index = todo_get(todo);
613 if (index == -1) {
614 /*
615 * If we have run out of immediate things to do, we
616 * have no choice but to scan the whole grid for
617 * longer-range things we've missed. Hence, I now add
618 * every square on the grid back on to the to-do list.
619 * I also set `done_something' to FALSE at this point;
620 * if we later come back here and find it still FALSE,
621 * we will know we've scanned the entire grid without
622 * finding anything new to do, and we can terminate.
623 */
624 if (!done_something)
625 break;
626 for (i = 0; i < w*h; i++)
627 todo_add(todo, i);
628 done_something = FALSE;
629
630 index = todo_get(todo);
631 }
632
633 y = index / w;
634 x = index % w;
635 {
636 int d, ourclass = dsf_canonify(equivalence, y*w+x);
637 int deadendmax[9];
638
639 deadendmax[1] = deadendmax[2] = deadendmax[4] = deadendmax[8] = 0;
640
641 for (i = j = 0; i < 4 && tilestate[(y*w+x) * 4 + i] != 255; i++) {
642 int valid;
643 int nnondeadends, nondeadends[4], deadendtotal;
644 int nequiv, equiv[5];
645 int val = tilestate[(y*w+x) * 4 + i];
646
647 valid = TRUE;
648 nnondeadends = deadendtotal = 0;
649 equiv[0] = ourclass;
650 nequiv = 1;
651 for (d = 1; d <= 8; d += d) {
652 /*
653 * Immediately rule out this orientation if it
654 * conflicts with any known edge.
655 */
656 if ((edgestate[(y*w+x) * 5 + d] == 1 && !(val & d)) ||
657 (edgestate[(y*w+x) * 5 + d] == 2 && (val & d)))
658 valid = FALSE;
659
660 if (val & d) {
661 /*
662 * Count up the dead-end statistics.
663 */
664 if (deadends[(y*w+x) * 5 + d] <= area) {
665 deadendtotal += deadends[(y*w+x) * 5 + d];
666 } else {
667 nondeadends[nnondeadends++] = d;
668 }
669
670 /*
671 * Ensure we aren't linking to any tiles,
672 * through edges not already known to be
673 * open, which create a loop.
674 */
675 if (edgestate[(y*w+x) * 5 + d] == 0) {
676 int c, k, x2, y2;
677
678 OFFSETWH(x2, y2, x, y, d, w, h);
679 c = dsf_canonify(equivalence, y2*w+x2);
680 for (k = 0; k < nequiv; k++)
681 if (c == equiv[k])
682 break;
683 if (k == nequiv)
684 equiv[nequiv++] = c;
685 else
686 valid = FALSE;
687 }
688 }
689 }
690
691 if (nnondeadends == 0) {
692 /*
693 * If this orientation links together dead-ends
694 * with a total area of less than the entire
695 * grid, it is invalid.
696 *
697 * (We add 1 to deadendtotal because of the
698 * tile itself, of course; one tile linking
699 * dead ends of size 2 and 3 forms a subnetwork
700 * with a total area of 6, not 5.)
701 */
702 if (deadendtotal > 0 && deadendtotal+1 < area)
703 valid = FALSE;
704 } else if (nnondeadends == 1) {
705 /*
706 * If this orientation links together one or
707 * more dead-ends with precisely one
708 * non-dead-end, then we may have to mark that
709 * non-dead-end as a dead end going the other
710 * way. However, it depends on whether all
711 * other orientations share the same property.
712 */
713 deadendtotal++;
714 if (deadendmax[nondeadends[0]] < deadendtotal)
715 deadendmax[nondeadends[0]] = deadendtotal;
716 } else {
717 /*
718 * If this orientation links together two or
719 * more non-dead-ends, then we can rule out the
720 * possibility of putting in new dead-end
721 * markings in those directions.
722 */
723 int k;
724 for (k = 0; k < nnondeadends; k++)
725 deadendmax[nondeadends[k]] = area+1;
726 }
727
728 if (valid)
729 tilestate[(y*w+x) * 4 + j++] = val;
730#ifdef SOLVER_DIAGNOSTICS
731 else
732 printf("ruling out orientation %x at %d,%d\n", val, x, y);
733#endif
734 }
735
736 assert(j > 0); /* we can't lose _all_ possibilities! */
737
738 if (j < i) {
739 done_something = TRUE;
740
741 /*
742 * We have ruled out at least one tile orientation.
743 * Make sure the rest are blanked.
744 */
745 while (j < 4)
746 tilestate[(y*w+x) * 4 + j++] = 255;
747 }
748
749 /*
750 * Now go through the tile orientations again and see
751 * if we've deduced anything new about any edges.
752 */
753 {
754 int a, o;
755 a = 0xF; o = 0;
756
757 for (i = 0; i < 4 && tilestate[(y*w+x) * 4 + i] != 255; i++) {
758 a &= tilestate[(y*w+x) * 4 + i];
759 o |= tilestate[(y*w+x) * 4 + i];
760 }
761 for (d = 1; d <= 8; d += d)
762 if (edgestate[(y*w+x) * 5 + d] == 0) {
763 int x2, y2, d2;
764 OFFSETWH(x2, y2, x, y, d, w, h);
765 d2 = F(d);
766 if (a & d) {
767 /* This edge is open in all orientations. */
768#ifdef SOLVER_DIAGNOSTICS
769 printf("marking edge %d,%d:%d open\n", x, y, d);
770#endif
771 edgestate[(y*w+x) * 5 + d] = 1;
772 edgestate[(y2*w+x2) * 5 + d2] = 1;
773 dsf_merge(equivalence, y*w+x, y2*w+x2);
774 done_something = TRUE;
775 todo_add(todo, y2*w+x2);
776 } else if (!(o & d)) {
777 /* This edge is closed in all orientations. */
778#ifdef SOLVER_DIAGNOSTICS
779 printf("marking edge %d,%d:%d closed\n", x, y, d);
780#endif
781 edgestate[(y*w+x) * 5 + d] = 2;
782 edgestate[(y2*w+x2) * 5 + d2] = 2;
783 done_something = TRUE;
784 todo_add(todo, y2*w+x2);
785 }
786 }
787
788 }
789
790 /*
791 * Now check the dead-end markers and see if any of
792 * them has lowered from the real ones.
793 */
794 for (d = 1; d <= 8; d += d) {
795 int x2, y2, d2;
796 OFFSETWH(x2, y2, x, y, d, w, h);
797 d2 = F(d);
798 if (deadendmax[d] > 0 &&
799 deadends[(y2*w+x2) * 5 + d2] > deadendmax[d]) {
800#ifdef SOLVER_DIAGNOSTICS
801 printf("setting dead end value %d,%d:%d to %d\n",
802 x2, y2, d2, deadendmax[d]);
803#endif
804 deadends[(y2*w+x2) * 5 + d2] = deadendmax[d];
805 done_something = TRUE;
806 todo_add(todo, y2*w+x2);
807 }
808 }
809
810 }
811 }
812
813 /*
814 * Mark all completely determined tiles as locked.
815 */
816 j = TRUE;
817 for (i = 0; i < w*h; i++) {
818 if (tilestate[i * 4 + 1] == 255) {
819 assert(tilestate[i * 4 + 0] != 255);
820 tiles[i] = tilestate[i * 4] | LOCKED;
821 } else {
822 tiles[i] &= ~LOCKED;
823 j = FALSE;
824 }
825 }
826
827 /*
828 * Free up working space.
829 */
830 todo_free(todo);
831 sfree(tilestate);
832 sfree(edgestate);
833 sfree(deadends);
834 sfree(equivalence);
835
836 return j;
837}
838
839/* ----------------------------------------------------------------------
840 * Randomly select a new game description.
841 */
842
843/*
844 * Function to randomly perturb an ambiguous section in a grid, to
845 * attempt to ensure unique solvability.
846 */
847static void perturb(int w, int h, unsigned char *tiles, int wrapping,
848 random_state *rs, int startx, int starty, int startd)
849{
850 struct xyd *perimeter, *perim2, *loop[2], looppos[2];
851 int nperim, perimsize, nloop[2], loopsize[2];
852 int x, y, d, i;
853
854 /*
855 * We know that the tile at (startx,starty) is part of an
856 * ambiguous section, and we also know that its neighbour in
857 * direction startd is fully specified. We begin by tracing all
858 * the way round the ambiguous area.
859 */
860 nperim = perimsize = 0;
861 perimeter = NULL;
862 x = startx;
863 y = starty;
864 d = startd;
865#ifdef PERTURB_DIAGNOSTICS
866 printf("perturb %d,%d:%d\n", x, y, d);
867#endif
868 do {
869 int x2, y2, d2;
870
871 if (nperim >= perimsize) {
872 perimsize = perimsize * 3 / 2 + 32;
873 perimeter = sresize(perimeter, perimsize, struct xyd);
874 }
875 perimeter[nperim].x = x;
876 perimeter[nperim].y = y;
877 perimeter[nperim].direction = d;
878 nperim++;
879#ifdef PERTURB_DIAGNOSTICS
880 printf("perimeter: %d,%d:%d\n", x, y, d);
881#endif
882
883 /*
884 * First, see if we can simply turn left from where we are
885 * and find another locked square.
886 */
887 d2 = A(d);
888 OFFSETWH(x2, y2, x, y, d2, w, h);
889 if ((!wrapping && (abs(x2-x) > 1 || abs(y2-y) > 1)) ||
890 (tiles[y2*w+x2] & LOCKED)) {
891 d = d2;
892 } else {
893 /*
894 * Failing that, step left into the new square and look
895 * in front of us.
896 */
897 x = x2;
898 y = y2;
899 OFFSETWH(x2, y2, x, y, d, w, h);
900 if ((wrapping || (abs(x2-x) <= 1 && abs(y2-y) <= 1)) &&
901 !(tiles[y2*w+x2] & LOCKED)) {
902 /*
903 * And failing _that_, we're going to have to step
904 * forward into _that_ square and look right at the
905 * same locked square as we started with.
906 */
907 x = x2;
908 y = y2;
909 d = C(d);
910 }
911 }
912
913 } while (x != startx || y != starty || d != startd);
914
915 /*
916 * Our technique for perturbing this ambiguous area is to
917 * search round its edge for a join we can make: that is, an
918 * edge on the perimeter which is (a) not currently connected,
919 * and (b) connecting it would not yield a full cross on either
920 * side. Then we make that join, search round the network to
921 * find the loop thus constructed, and sever the loop at a
922 * randomly selected other point.
923 */
924 perim2 = snewn(nperim, struct xyd);
925 memcpy(perim2, perimeter, nperim * sizeof(struct xyd));
926 /* Shuffle the perimeter, so as to search it without directional bias. */
927 shuffle(perim2, nperim, sizeof(*perim2), rs);
928 for (i = 0; i < nperim; i++) {
929 int x2, y2;
930
931 x = perim2[i].x;
932 y = perim2[i].y;
933 d = perim2[i].direction;
934
935 OFFSETWH(x2, y2, x, y, d, w, h);
936 if (!wrapping && (abs(x2-x) > 1 || abs(y2-y) > 1))
937 continue; /* can't link across non-wrapping border */
938 if (tiles[y*w+x] & d)
939 continue; /* already linked in this direction! */
940 if (((tiles[y*w+x] | d) & 15) == 15)
941 continue; /* can't turn this tile into a cross */
942 if (((tiles[y2*w+x2] | F(d)) & 15) == 15)
943 continue; /* can't turn other tile into a cross */
944
945 /*
946 * We've found the point at which we're going to make a new
947 * link.
948 */
949#ifdef PERTURB_DIAGNOSTICS
950 printf("linking %d,%d:%d\n", x, y, d);
951#endif
952 tiles[y*w+x] |= d;
953 tiles[y2*w+x2] |= F(d);
954
955 break;
956 }
957 sfree(perim2);
958
959 if (i == nperim) {
960 sfree(perimeter);
961 return; /* nothing we can do! */
962 }
963
964 /*
965 * Now we've constructed a new link, we need to find the entire
966 * loop of which it is a part.
967 *
968 * In principle, this involves doing a complete search round
969 * the network. However, I anticipate that in the vast majority
970 * of cases the loop will be quite small, so what I'm going to
971 * do is make _two_ searches round the network in parallel, one
972 * keeping its metaphorical hand on the left-hand wall while
973 * the other keeps its hand on the right. As soon as one of
974 * them gets back to its starting point, I abandon the other.
975 */
976 for (i = 0; i < 2; i++) {
977 loopsize[i] = nloop[i] = 0;
978 loop[i] = NULL;
979 looppos[i].x = x;
980 looppos[i].y = y;
981 looppos[i].direction = d;
982 }
983 while (1) {
984 for (i = 0; i < 2; i++) {
985 int x2, y2, j;
986
987 x = looppos[i].x;
988 y = looppos[i].y;
989 d = looppos[i].direction;
990
991 OFFSETWH(x2, y2, x, y, d, w, h);
992
993 /*
994 * Add this path segment to the loop, unless it exactly
995 * reverses the previous one on the loop in which case
996 * we take it away again.
997 */
998#ifdef PERTURB_DIAGNOSTICS
999 printf("looppos[%d] = %d,%d:%d\n", i, x, y, d);
1000#endif
1001 if (nloop[i] > 0 &&
1002 loop[i][nloop[i]-1].x == x2 &&
1003 loop[i][nloop[i]-1].y == y2 &&
1004 loop[i][nloop[i]-1].direction == F(d)) {
1005#ifdef PERTURB_DIAGNOSTICS
1006 printf("removing path segment %d,%d:%d from loop[%d]\n",
1007 x2, y2, F(d), i);
1008#endif
1009 nloop[i]--;
1010 } else {
1011 if (nloop[i] >= loopsize[i]) {
1012 loopsize[i] = loopsize[i] * 3 / 2 + 32;
1013 loop[i] = sresize(loop[i], loopsize[i], struct xyd);
1014 }
1015#ifdef PERTURB_DIAGNOSTICS
1016 printf("adding path segment %d,%d:%d to loop[%d]\n",
1017 x, y, d, i);
1018#endif
1019 loop[i][nloop[i]++] = looppos[i];
1020 }
1021
1022#ifdef PERTURB_DIAGNOSTICS
1023 printf("tile at new location is %x\n", tiles[y2*w+x2] & 0xF);
1024#endif
1025 d = F(d);
1026 for (j = 0; j < 4; j++) {
1027 if (i == 0)
1028 d = A(d);
1029 else
1030 d = C(d);
1031#ifdef PERTURB_DIAGNOSTICS
1032 printf("trying dir %d\n", d);
1033#endif
1034 if (tiles[y2*w+x2] & d) {
1035 looppos[i].x = x2;
1036 looppos[i].y = y2;
1037 looppos[i].direction = d;
1038 break;
1039 }
1040 }
1041
1042 assert(j < 4);
1043 assert(nloop[i] > 0);
1044
1045 if (looppos[i].x == loop[i][0].x &&
1046 looppos[i].y == loop[i][0].y &&
1047 looppos[i].direction == loop[i][0].direction) {
1048#ifdef PERTURB_DIAGNOSTICS
1049 printf("loop %d finished tracking\n", i);
1050#endif
1051
1052 /*
1053 * Having found our loop, we now sever it at a
1054 * randomly chosen point - absolutely any will do -
1055 * which is not the one we joined it at to begin
1056 * with. Conveniently, the one we joined it at is
1057 * loop[i][0], so we just avoid that one.
1058 */
1059 j = random_upto(rs, nloop[i]-1) + 1;
1060 x = loop[i][j].x;
1061 y = loop[i][j].y;
1062 d = loop[i][j].direction;
1063 OFFSETWH(x2, y2, x, y, d, w, h);
1064 tiles[y*w+x] &= ~d;
1065 tiles[y2*w+x2] &= ~F(d);
1066
1067 break;
1068 }
1069 }
1070 if (i < 2)
1071 break;
1072 }
1073 sfree(loop[0]);
1074 sfree(loop[1]);
1075
1076 /*
1077 * Finally, we must mark the entire disputed section as locked,
1078 * to prevent the perturb function being called on it multiple
1079 * times.
1080 *
1081 * To do this, we _sort_ the perimeter of the area. The
1082 * existing xyd_cmp function will arrange things into columns
1083 * for us, in such a way that each column has the edges in
1084 * vertical order. Then we can work down each column and fill
1085 * in all the squares between an up edge and a down edge.
1086 */
1087 qsort(perimeter, nperim, sizeof(struct xyd), xyd_cmp);
1088 x = y = -1;
1089 for (i = 0; i <= nperim; i++) {
1090 if (i == nperim || perimeter[i].x > x) {
1091 /*
1092 * Fill in everything from the last Up edge to the
1093 * bottom of the grid, if necessary.
1094 */
1095 if (x != -1) {
1096 while (y < h) {
1097#ifdef PERTURB_DIAGNOSTICS
1098 printf("resolved: locking tile %d,%d\n", x, y);
1099#endif
1100 tiles[y * w + x] |= LOCKED;
1101 y++;
1102 }
1103 x = y = -1;
1104 }
1105
1106 if (i == nperim)
1107 break;
1108
1109 x = perimeter[i].x;
1110 y = 0;
1111 }
1112
1113 if (perimeter[i].direction == U) {
1114 x = perimeter[i].x;
1115 y = perimeter[i].y;
1116 } else if (perimeter[i].direction == D) {
1117 /*
1118 * Fill in everything from the last Up edge to here.
1119 */
1120 assert(x == perimeter[i].x && y <= perimeter[i].y);
1121 while (y <= perimeter[i].y) {
1122#ifdef PERTURB_DIAGNOSTICS
1123 printf("resolved: locking tile %d,%d\n", x, y);
1124#endif
1125 tiles[y * w + x] |= LOCKED;
1126 y++;
1127 }
1128 x = y = -1;
1129 }
1130 }
1131
1132 sfree(perimeter);
1133}
1134
1135static int *compute_loops_inner(int w, int h, int wrapping,
1136 const unsigned char *tiles,
1137 const unsigned char *barriers);
1138
1139static char *new_game_desc(const game_params *params, random_state *rs,
1140 char **aux, int interactive)
1141{
1142 tree234 *possibilities, *barriertree;
1143 int w, h, x, y, cx, cy, nbarriers;
1144 unsigned char *tiles, *barriers;
1145 char *desc, *p;
1146
1147 w = params->width;
1148 h = params->height;
1149
1150 cx = w / 2;
1151 cy = h / 2;
1152
1153 tiles = snewn(w * h, unsigned char);
1154 barriers = snewn(w * h, unsigned char);
1155
1156 begin_generation:
1157
1158 memset(tiles, 0, w * h);
1159 memset(barriers, 0, w * h);
1160
1161 /*
1162 * Construct the unshuffled grid.
1163 *
1164 * To do this, we simply start at the centre point, repeatedly
1165 * choose a random possibility out of the available ways to
1166 * extend a used square into an unused one, and do it. After
1167 * extending the third line out of a square, we remove the
1168 * fourth from the possibilities list to avoid any full-cross
1169 * squares (which would make the game too easy because they
1170 * only have one orientation).
1171 *
1172 * The slightly worrying thing is the avoidance of full-cross
1173 * squares. Can this cause our unsophisticated construction
1174 * algorithm to paint itself into a corner, by getting into a
1175 * situation where there are some unreached squares and the
1176 * only way to reach any of them is to extend a T-piece into a
1177 * full cross?
1178 *
1179 * Answer: no it can't, and here's a proof.
1180 *
1181 * Any contiguous group of such unreachable squares must be
1182 * surrounded on _all_ sides by T-pieces pointing away from the
1183 * group. (If not, then there is a square which can be extended
1184 * into one of the `unreachable' ones, and so it wasn't
1185 * unreachable after all.) In particular, this implies that
1186 * each contiguous group of unreachable squares must be
1187 * rectangular in shape (any deviation from that yields a
1188 * non-T-piece next to an `unreachable' square).
1189 *
1190 * So we have a rectangle of unreachable squares, with T-pieces
1191 * forming a solid border around the rectangle. The corners of
1192 * that border must be connected (since every tile connects all
1193 * the lines arriving in it), and therefore the border must
1194 * form a closed loop around the rectangle.
1195 *
1196 * But this can't have happened in the first place, since we
1197 * _know_ we've avoided creating closed loops! Hence, no such
1198 * situation can ever arise, and the naive grid construction
1199 * algorithm will guaranteeably result in a complete grid
1200 * containing no unreached squares, no full crosses _and_ no
1201 * closed loops. []
1202 */
1203 possibilities = newtree234(xyd_cmp_nc);
1204
1205 if (cx+1 < w)
1206 add234(possibilities, new_xyd(cx, cy, R));
1207 if (cy-1 >= 0)
1208 add234(possibilities, new_xyd(cx, cy, U));
1209 if (cx-1 >= 0)
1210 add234(possibilities, new_xyd(cx, cy, L));
1211 if (cy+1 < h)
1212 add234(possibilities, new_xyd(cx, cy, D));
1213
1214 while (count234(possibilities) > 0) {
1215 int i;
1216 struct xyd *xyd;
1217 int x1, y1, d1, x2, y2, d2, d;
1218
1219 /*
1220 * Extract a randomly chosen possibility from the list.
1221 */
1222 i = random_upto(rs, count234(possibilities));
1223 xyd = delpos234(possibilities, i);
1224 x1 = xyd->x;
1225 y1 = xyd->y;
1226 d1 = xyd->direction;
1227 sfree(xyd);
1228
1229 OFFSET(x2, y2, x1, y1, d1, params);
1230 d2 = F(d1);
1231#ifdef GENERATION_DIAGNOSTICS
1232 printf("picked (%d,%d,%c) <-> (%d,%d,%c)\n",
1233 x1, y1, "0RU3L567D9abcdef"[d1], x2, y2, "0RU3L567D9abcdef"[d2]);
1234#endif
1235
1236 /*
1237 * Make the connection. (We should be moving to an as yet
1238 * unused tile.)
1239 */
1240 index(params, tiles, x1, y1) |= d1;
1241 assert(index(params, tiles, x2, y2) == 0);
1242 index(params, tiles, x2, y2) |= d2;
1243
1244 /*
1245 * If we have created a T-piece, remove its last
1246 * possibility.
1247 */
1248 if (COUNT(index(params, tiles, x1, y1)) == 3) {
1249 struct xyd xyd1, *xydp;
1250
1251 xyd1.x = x1;
1252 xyd1.y = y1;
1253 xyd1.direction = 0x0F ^ index(params, tiles, x1, y1);
1254
1255 xydp = find234(possibilities, &xyd1, NULL);
1256
1257 if (xydp) {
1258#ifdef GENERATION_DIAGNOSTICS
1259 printf("T-piece; removing (%d,%d,%c)\n",
1260 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
1261#endif
1262 del234(possibilities, xydp);
1263 sfree(xydp);
1264 }
1265 }
1266
1267 /*
1268 * Remove all other possibilities that were pointing at the
1269 * tile we've just moved into.
1270 */
1271 for (d = 1; d < 0x10; d <<= 1) {
1272 int x3, y3, d3;
1273 struct xyd xyd1, *xydp;
1274
1275 OFFSET(x3, y3, x2, y2, d, params);
1276 d3 = F(d);
1277
1278 xyd1.x = x3;
1279 xyd1.y = y3;
1280 xyd1.direction = d3;
1281
1282 xydp = find234(possibilities, &xyd1, NULL);
1283
1284 if (xydp) {
1285#ifdef GENERATION_DIAGNOSTICS
1286 printf("Loop avoidance; removing (%d,%d,%c)\n",
1287 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
1288#endif
1289 del234(possibilities, xydp);
1290 sfree(xydp);
1291 }
1292 }
1293
1294 /*
1295 * Add new possibilities to the list for moving _out_ of
1296 * the tile we have just moved into.
1297 */
1298 for (d = 1; d < 0x10; d <<= 1) {
1299 int x3, y3;
1300
1301 if (d == d2)
1302 continue; /* we've got this one already */
1303
1304 if (!params->wrapping) {
1305 if (d == U && y2 == 0)
1306 continue;
1307 if (d == D && y2 == h-1)
1308 continue;
1309 if (d == L && x2 == 0)
1310 continue;
1311 if (d == R && x2 == w-1)
1312 continue;
1313 }
1314
1315 OFFSET(x3, y3, x2, y2, d, params);
1316
1317 if (index(params, tiles, x3, y3))
1318 continue; /* this would create a loop */
1319
1320#ifdef GENERATION_DIAGNOSTICS
1321 printf("New frontier; adding (%d,%d,%c)\n",
1322 x2, y2, "0RU3L567D9abcdef"[d]);
1323#endif
1324 add234(possibilities, new_xyd(x2, y2, d));
1325 }
1326 }
1327 /* Having done that, we should have no possibilities remaining. */
1328 assert(count234(possibilities) == 0);
1329 freetree234(possibilities);
1330
1331 if (params->unique) {
1332 int prevn = -1;
1333
1334 /*
1335 * Run the solver to check unique solubility.
1336 */
1337 while (!net_solver(w, h, tiles, NULL, params->wrapping)) {
1338 int n = 0;
1339
1340 /*
1341 * We expect (in most cases) that most of the grid will
1342 * be uniquely specified already, and the remaining
1343 * ambiguous sections will be small and separate. So
1344 * our strategy is to find each individual such
1345 * section, and perform a perturbation on the network
1346 * in that area.
1347 */
1348 for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
1349 if (x+1 < w && ((tiles[y*w+x] ^ tiles[y*w+x+1]) & LOCKED)) {
1350 n++;
1351 if (tiles[y*w+x] & LOCKED)
1352 perturb(w, h, tiles, params->wrapping, rs, x+1, y, L);
1353 else
1354 perturb(w, h, tiles, params->wrapping, rs, x, y, R);
1355 }
1356 if (y+1 < h && ((tiles[y*w+x] ^ tiles[(y+1)*w+x]) & LOCKED)) {
1357 n++;
1358 if (tiles[y*w+x] & LOCKED)
1359 perturb(w, h, tiles, params->wrapping, rs, x, y+1, U);
1360 else
1361 perturb(w, h, tiles, params->wrapping, rs, x, y, D);
1362 }
1363 }
1364
1365 /*
1366 * Now n counts the number of ambiguous sections we
1367 * have fiddled with. If we haven't managed to decrease
1368 * it from the last time we ran the solver, give up and
1369 * regenerate the entire grid.
1370 */
1371 if (prevn != -1 && prevn <= n)
1372 goto begin_generation; /* (sorry) */
1373
1374 prevn = n;
1375 }
1376
1377 /*
1378 * The solver will have left a lot of LOCKED bits lying
1379 * around in the tiles array. Remove them.
1380 */
1381 for (x = 0; x < w*h; x++)
1382 tiles[x] &= ~LOCKED;
1383 }
1384
1385 /*
1386 * Now compute a list of the possible barrier locations.
1387 */
1388 barriertree = newtree234(xyd_cmp_nc);
1389 for (y = 0; y < h; y++) {
1390 for (x = 0; x < w; x++) {
1391
1392 if (!(index(params, tiles, x, y) & R) &&
1393 (params->wrapping || x < w-1))
1394 add234(barriertree, new_xyd(x, y, R));
1395 if (!(index(params, tiles, x, y) & D) &&
1396 (params->wrapping || y < h-1))
1397 add234(barriertree, new_xyd(x, y, D));
1398 }
1399 }
1400
1401 /*
1402 * Save the unshuffled grid in aux.
1403 */
1404 {
1405 char *solution;
1406 int i;
1407
1408 solution = snewn(w * h + 1, char);
1409 for (i = 0; i < w * h; i++)
1410 solution[i] = "0123456789abcdef"[tiles[i] & 0xF];
1411 solution[w*h] = '\0';
1412
1413 *aux = solution;
1414 }
1415
1416 /*
1417 * Now shuffle the grid.
1418 *
1419 * In order to avoid accidentally generating an already-solved
1420 * grid, we will reshuffle as necessary to ensure that at least
1421 * one edge has a mismatched connection.
1422 *
1423 * This can always be done, since validate_params() enforces a
1424 * grid area of at least 2 and our generator never creates
1425 * either type of rotationally invariant tile (cross and
1426 * blank). Hence there must be at least one edge separating
1427 * distinct tiles, and it must be possible to find orientations
1428 * of those tiles such that one tile is trying to connect
1429 * through that edge and the other is not.
1430 *
1431 * (We could be more subtle, and allow the shuffle to generate
1432 * a grid in which all tiles match up locally and the only
1433 * criterion preventing the grid from being already solved is
1434 * connectedness. However, that would take more effort, and
1435 * it's easier to simply make sure every grid is _obviously_
1436 * not solved.)
1437 *
1438 * We also require that our shuffle produces no loops in the
1439 * initial grid state, because it's a bit rude to light up a 'HEY,
1440 * YOU DID SOMETHING WRONG!' indicator when the user hasn't even
1441 * had a chance to do _anything_ yet. This also is possible just
1442 * by retrying the whole shuffle on failure, because it's clear
1443 * that at least one non-solved shuffle with no loops must exist.
1444 * (Proof: take the _solved_ state of the puzzle, and rotate one
1445 * endpoint.)
1446 */
1447 while (1) {
1448 int mismatches, prev_loopsquares, this_loopsquares, i;
1449 int *loops;
1450
1451 shuffle:
1452 for (y = 0; y < h; y++) {
1453 for (x = 0; x < w; x++) {
1454 int orig = index(params, tiles, x, y);
1455 int rot = random_upto(rs, 4);
1456 index(params, tiles, x, y) = ROT(orig, rot);
1457 }
1458 }
1459
1460 /*
1461 * Check for loops, and try to fix them by reshuffling just
1462 * the squares involved.
1463 */
1464 prev_loopsquares = w*h+1;
1465 while (1) {
1466 loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL);
1467 this_loopsquares = 0;
1468 for (i = 0; i < w*h; i++) {
1469 if (loops[i]) {
1470 int orig = tiles[i];
1471 int rot = random_upto(rs, 4);
1472 tiles[i] = ROT(orig, rot);
1473 this_loopsquares++;
1474 }
1475 }
1476 sfree(loops);
1477 if (this_loopsquares > prev_loopsquares) {
1478 /*
1479 * We're increasing rather than reducing the number of
1480 * loops. Give up and go back to the full shuffle.
1481 */
1482 goto shuffle;
1483 }
1484 if (this_loopsquares == 0)
1485 break;
1486 prev_loopsquares = this_loopsquares;
1487 }
1488
1489 mismatches = 0;
1490 /*
1491 * I can't even be bothered to check for mismatches across
1492 * a wrapping edge, so I'm just going to enforce that there
1493 * must be a mismatch across a non-wrapping edge, which is
1494 * still always possible.
1495 */
1496 for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
1497 if (x+1 < w && ((ROT(index(params, tiles, x, y), 2) ^
1498 index(params, tiles, x+1, y)) & L))
1499 mismatches++;
1500 if (y+1 < h && ((ROT(index(params, tiles, x, y), 2) ^
1501 index(params, tiles, x, y+1)) & U))
1502 mismatches++;
1503 }
1504
1505 if (mismatches == 0)
1506 continue;
1507
1508 /* OK. */
1509 break;
1510 }
1511
1512 /*
1513 * And now choose barrier locations. (We carefully do this
1514 * _after_ shuffling, so that changing the barrier rate in the
1515 * params while keeping the random seed the same will give the
1516 * same shuffled grid and _only_ change the barrier locations.
1517 * Also the way we choose barrier locations, by repeatedly
1518 * choosing one possibility from the list until we have enough,
1519 * is designed to ensure that raising the barrier rate while
1520 * keeping the seed the same will provide a superset of the
1521 * previous barrier set - i.e. if you ask for 10 barriers, and
1522 * then decide that's still too hard and ask for 20, you'll get
1523 * the original 10 plus 10 more, rather than getting 20 new
1524 * ones and the chance of remembering your first 10.)
1525 */
1526 nbarriers = (int)(params->barrier_probability * count234(barriertree));
1527 assert(nbarriers >= 0 && nbarriers <= count234(barriertree));
1528
1529 while (nbarriers > 0) {
1530 int i;
1531 struct xyd *xyd;
1532 int x1, y1, d1, x2, y2, d2;
1533
1534 /*
1535 * Extract a randomly chosen barrier from the list.
1536 */
1537 i = random_upto(rs, count234(barriertree));
1538 xyd = delpos234(barriertree, i);
1539
1540 assert(xyd != NULL);
1541
1542 x1 = xyd->x;
1543 y1 = xyd->y;
1544 d1 = xyd->direction;
1545 sfree(xyd);
1546
1547 OFFSET(x2, y2, x1, y1, d1, params);
1548 d2 = F(d1);
1549
1550 index(params, barriers, x1, y1) |= d1;
1551 index(params, barriers, x2, y2) |= d2;
1552
1553 nbarriers--;
1554 }
1555
1556 /*
1557 * Clean up the rest of the barrier list.
1558 */
1559 {
1560 struct xyd *xyd;
1561
1562 while ( (xyd = delpos234(barriertree, 0)) != NULL)
1563 sfree(xyd);
1564
1565 freetree234(barriertree);
1566 }
1567
1568 /*
1569 * Finally, encode the grid into a string game description.
1570 *
1571 * My syntax is extremely simple: each square is encoded as a
1572 * hex digit in which bit 0 means a connection on the right,
1573 * bit 1 means up, bit 2 left and bit 3 down. (i.e. the same
1574 * encoding as used internally). Each digit is followed by
1575 * optional barrier indicators: `v' means a vertical barrier to
1576 * the right of it, and `h' means a horizontal barrier below
1577 * it.
1578 */
1579 desc = snewn(w * h * 3 + 1, char);
1580 p = desc;
1581 for (y = 0; y < h; y++) {
1582 for (x = 0; x < w; x++) {
1583 *p++ = "0123456789abcdef"[index(params, tiles, x, y)];
1584 if ((params->wrapping || x < w-1) &&
1585 (index(params, barriers, x, y) & R))
1586 *p++ = 'v';
1587 if ((params->wrapping || y < h-1) &&
1588 (index(params, barriers, x, y) & D))
1589 *p++ = 'h';
1590 }
1591 }
1592 assert(p - desc <= w*h*3);
1593 *p = '\0';
1594
1595 sfree(tiles);
1596 sfree(barriers);
1597
1598 return desc;
1599}
1600
1601static char *validate_desc(const game_params *params, const char *desc)
1602{
1603 int w = params->width, h = params->height;
1604 int i;
1605
1606 for (i = 0; i < w*h; i++) {
1607 if (*desc >= '0' && *desc <= '9')
1608 /* OK */;
1609 else if (*desc >= 'a' && *desc <= 'f')
1610 /* OK */;
1611 else if (*desc >= 'A' && *desc <= 'F')
1612 /* OK */;
1613 else if (!*desc)
1614 return "Game description shorter than expected";
1615 else
1616 return "Game description contained unexpected character";
1617 desc++;
1618 while (*desc == 'h' || *desc == 'v')
1619 desc++;
1620 }
1621 if (*desc)
1622 return "Game description longer than expected";
1623
1624 return NULL;
1625}
1626
1627/* ----------------------------------------------------------------------
1628 * Construct an initial game state, given a description and parameters.
1629 */
1630
1631static game_state *new_game(midend *me, const game_params *params,
1632 const char *desc)
1633{
1634 game_state *state;
1635 int w, h, x, y;
1636
1637 assert(params->width > 0 && params->height > 0);
1638 assert(params->width > 1 || params->height > 1);
1639
1640 /*
1641 * Create a blank game state.
1642 */
1643 state = snew(game_state);
1644 w = state->width = params->width;
1645 h = state->height = params->height;
1646 state->wrapping = params->wrapping;
1647 state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0;
1648 state->completed = state->used_solve = FALSE;
1649 state->tiles = snewn(state->width * state->height, unsigned char);
1650 memset(state->tiles, 0, state->width * state->height);
1651 state->barriers = snewn(state->width * state->height, unsigned char);
1652 memset(state->barriers, 0, state->width * state->height);
1653
1654 /*
1655 * Parse the game description into the grid.
1656 */
1657 for (y = 0; y < h; y++) {
1658 for (x = 0; x < w; x++) {
1659 if (*desc >= '0' && *desc <= '9')
1660 tile(state, x, y) = *desc - '0';
1661 else if (*desc >= 'a' && *desc <= 'f')
1662 tile(state, x, y) = *desc - 'a' + 10;
1663 else if (*desc >= 'A' && *desc <= 'F')
1664 tile(state, x, y) = *desc - 'A' + 10;
1665 if (*desc)
1666 desc++;
1667 while (*desc == 'h' || *desc == 'v') {
1668 int x2, y2, d1, d2;
1669 if (*desc == 'v')
1670 d1 = R;
1671 else
1672 d1 = D;
1673
1674 OFFSET(x2, y2, x, y, d1, state);
1675 d2 = F(d1);
1676
1677 barrier(state, x, y) |= d1;
1678 barrier(state, x2, y2) |= d2;
1679
1680 desc++;
1681 }
1682 }
1683 }
1684
1685 /*
1686 * Set up border barriers if this is a non-wrapping game.
1687 */
1688 if (!state->wrapping) {
1689 for (x = 0; x < state->width; x++) {
1690 barrier(state, x, 0) |= U;
1691 barrier(state, x, state->height-1) |= D;
1692 }
1693 for (y = 0; y < state->height; y++) {
1694 barrier(state, 0, y) |= L;
1695 barrier(state, state->width-1, y) |= R;
1696 }
1697 } else {
1698 /*
1699 * We check whether this is de-facto a non-wrapping game
1700 * despite the parameters, in case we were passed the
1701 * description of a non-wrapping game. This is so that we
1702 * can change some aspects of the UI behaviour.
1703 */
1704 state->wrapping = FALSE;
1705 for (x = 0; x < state->width; x++)
1706 if (!(barrier(state, x, 0) & U) ||
1707 !(barrier(state, x, state->height-1) & D))
1708 state->wrapping = TRUE;
1709 for (y = 0; y < state->height; y++)
1710 if (!(barrier(state, 0, y) & L) ||
1711 !(barrier(state, state->width-1, y) & R))
1712 state->wrapping = TRUE;
1713 }
1714
1715 return state;
1716}
1717
1718static game_state *dup_game(const game_state *state)
1719{
1720 game_state *ret;
1721
1722 ret = snew(game_state);
1723 ret->width = state->width;
1724 ret->height = state->height;
1725 ret->wrapping = state->wrapping;
1726 ret->completed = state->completed;
1727 ret->used_solve = state->used_solve;
1728 ret->last_rotate_dir = state->last_rotate_dir;
1729 ret->last_rotate_x = state->last_rotate_x;
1730 ret->last_rotate_y = state->last_rotate_y;
1731 ret->tiles = snewn(state->width * state->height, unsigned char);
1732 memcpy(ret->tiles, state->tiles, state->width * state->height);
1733 ret->barriers = snewn(state->width * state->height, unsigned char);
1734 memcpy(ret->barriers, state->barriers, state->width * state->height);
1735
1736 return ret;
1737}
1738
1739static void free_game(game_state *state)
1740{
1741 sfree(state->tiles);
1742 sfree(state->barriers);
1743 sfree(state);
1744}
1745
1746static char *solve_game(const game_state *state, const game_state *currstate,
1747 const char *aux, char **error)
1748{
1749 unsigned char *tiles;
1750 char *ret;
1751 int retlen, retsize;
1752 int i;
1753
1754 tiles = snewn(state->width * state->height, unsigned char);
1755
1756 if (!aux) {
1757 /*
1758 * Run the internal solver on the provided grid. This might
1759 * not yield a complete solution.
1760 */
1761 memcpy(tiles, state->tiles, state->width * state->height);
1762 net_solver(state->width, state->height, tiles,
1763 state->barriers, state->wrapping);
1764 } else {
1765 for (i = 0; i < state->width * state->height; i++) {
1766 int c = aux[i];
1767
1768 if (c >= '0' && c <= '9')
1769 tiles[i] = c - '0';
1770 else if (c >= 'a' && c <= 'f')
1771 tiles[i] = c - 'a' + 10;
1772 else if (c >= 'A' && c <= 'F')
1773 tiles[i] = c - 'A' + 10;
1774
1775 tiles[i] |= LOCKED;
1776 }
1777 }
1778
1779 /*
1780 * Now construct a string which can be passed to execute_move()
1781 * to transform the current grid into the solved one.
1782 */
1783 retsize = 256;
1784 ret = snewn(retsize, char);
1785 retlen = 0;
1786 ret[retlen++] = 'S';
1787
1788 for (i = 0; i < state->width * state->height; i++) {
1789 int from = currstate->tiles[i], to = tiles[i];
1790 int ft = from & (R|L|U|D), tt = to & (R|L|U|D);
1791 int x = i % state->width, y = i / state->width;
1792 int chr = '\0';
1793 char buf[80], *p = buf;
1794
1795 if (from == to)
1796 continue; /* nothing needs doing at all */
1797
1798 /*
1799 * To transform this tile into the desired tile: first
1800 * unlock the tile if it's locked, then rotate it if
1801 * necessary, then lock it if necessary.
1802 */
1803 if (from & LOCKED)
1804 p += sprintf(p, ";L%d,%d", x, y);
1805
1806 if (tt == A(ft))
1807 chr = 'A';
1808 else if (tt == C(ft))
1809 chr = 'C';
1810 else if (tt == F(ft))
1811 chr = 'F';
1812 else {
1813 assert(tt == ft);
1814 chr = '\0';
1815 }
1816 if (chr)
1817 p += sprintf(p, ";%c%d,%d", chr, x, y);
1818
1819 if (to & LOCKED)
1820 p += sprintf(p, ";L%d,%d", x, y);
1821
1822 if (p > buf) {
1823 if (retlen + (p - buf) >= retsize) {
1824 retsize = retlen + (p - buf) + 512;
1825 ret = sresize(ret, retsize, char);
1826 }
1827 memcpy(ret+retlen, buf, p - buf);
1828 retlen += p - buf;
1829 }
1830 }
1831
1832 assert(retlen < retsize);
1833 ret[retlen] = '\0';
1834 ret = sresize(ret, retlen+1, char);
1835
1836 sfree(tiles);
1837
1838 return ret;
1839}
1840
1841static int game_can_format_as_text_now(const game_params *params)
1842{
1843 return TRUE;
1844}
1845
1846static char *game_text_format(const game_state *state)
1847{
1848 return NULL;
1849}
1850
1851/* ----------------------------------------------------------------------
1852 * Utility routine.
1853 */
1854
1855/*
1856 * Compute which squares are reachable from the centre square, as a
1857 * quick visual aid to determining how close the game is to
1858 * completion. This is also a simple way to tell if the game _is_
1859 * completed - just call this function and see whether every square
1860 * is marked active.
1861 */
1862static unsigned char *compute_active(const game_state *state, int cx, int cy)
1863{
1864 unsigned char *active;
1865 tree234 *todo;
1866 struct xyd *xyd;
1867
1868 active = snewn(state->width * state->height, unsigned char);
1869 memset(active, 0, state->width * state->height);
1870
1871 /*
1872 * We only store (x,y) pairs in todo, but it's easier to reuse
1873 * xyd_cmp and just store direction 0 every time.
1874 */
1875 todo = newtree234(xyd_cmp_nc);
1876 index(state, active, cx, cy) = ACTIVE;
1877 add234(todo, new_xyd(cx, cy, 0));
1878
1879 while ( (xyd = delpos234(todo, 0)) != NULL) {
1880 int x1, y1, d1, x2, y2, d2;
1881
1882 x1 = xyd->x;
1883 y1 = xyd->y;
1884 sfree(xyd);
1885
1886 for (d1 = 1; d1 < 0x10; d1 <<= 1) {
1887 OFFSET(x2, y2, x1, y1, d1, state);
1888 d2 = F(d1);
1889
1890 /*
1891 * If the next tile in this direction is connected to
1892 * us, and there isn't a barrier in the way, and it
1893 * isn't already marked active, then mark it active and
1894 * add it to the to-examine list.
1895 */
1896 if ((tile(state, x1, y1) & d1) &&
1897 (tile(state, x2, y2) & d2) &&
1898 !(barrier(state, x1, y1) & d1) &&
1899 !index(state, active, x2, y2)) {
1900 index(state, active, x2, y2) = ACTIVE;
1901 add234(todo, new_xyd(x2, y2, 0));
1902 }
1903 }
1904 }
1905 /* Now we expect the todo list to have shrunk to zero size. */
1906 assert(count234(todo) == 0);
1907 freetree234(todo);
1908
1909 return active;
1910}
1911
1912struct net_neighbour_ctx {
1913 int w, h;
1914 const unsigned char *tiles, *barriers;
1915 int i, n, neighbours[4];
1916};
1917static int net_neighbour(int vertex, void *vctx)
1918{
1919 struct net_neighbour_ctx *ctx = (struct net_neighbour_ctx *)vctx;
1920
1921 if (vertex >= 0) {
1922 int x = vertex % ctx->w, y = vertex / ctx->w;
1923 int tile, dir, x1, y1, v1;
1924
1925 ctx->i = ctx->n = 0;
1926
1927 tile = ctx->tiles[vertex];
1928 if (ctx->barriers)
1929 tile &= ~ctx->barriers[vertex];
1930
1931 for (dir = 1; dir < 0x10; dir <<= 1) {
1932 if (!(tile & dir))
1933 continue;
1934 OFFSETWH(x1, y1, x, y, dir, ctx->w, ctx->h);
1935 v1 = y1 * ctx->w + x1;
1936 if (ctx->tiles[v1] & F(dir))
1937 ctx->neighbours[ctx->n++] = v1;
1938 }
1939 }
1940
1941 if (ctx->i < ctx->n)
1942 return ctx->neighbours[ctx->i++];
1943 else
1944 return -1;
1945}
1946
1947static int *compute_loops_inner(int w, int h, int wrapping,
1948 const unsigned char *tiles,
1949 const unsigned char *barriers)
1950{
1951 struct net_neighbour_ctx ctx;
1952 struct findloopstate *fls;
1953 int *loops;
1954 int x, y;
1955
1956 fls = findloop_new_state(w*h);
1957 ctx.w = w;
1958 ctx.h = h;
1959 ctx.tiles = tiles;
1960 ctx.barriers = barriers;
1961 findloop_run(fls, w*h, net_neighbour, &ctx);
1962
1963 loops = snewn(w*h, int);
1964
1965 for (y = 0; y < h; y++) {
1966 for (x = 0; x < w; x++) {
1967 int x1, y1, dir;
1968 int flags = 0;
1969
1970 for (dir = 1; dir < 0x10; dir <<= 1) {
1971 if ((tiles[y*w+x] & dir) &&
1972 !(barriers && (barriers[y*w+x] & dir))) {
1973 OFFSETWH(x1, y1, x, y, dir, w, h);
1974 if ((tiles[y1*w+x1] & F(dir)) &&
1975 findloop_is_loop_edge(fls, y*w+x, y1*w+x1))
1976 flags |= LOOP(dir);
1977 }
1978 }
1979 loops[y*w+x] = flags;
1980 }
1981 }
1982
1983 findloop_free_state(fls);
1984 return loops;
1985}
1986
1987static int *compute_loops(const game_state *state)
1988{
1989 return compute_loops_inner(state->width, state->height, state->wrapping,
1990 state->tiles, state->barriers);
1991}
1992
1993struct game_ui {
1994 int org_x, org_y; /* origin */
1995 int cx, cy; /* source tile (game coordinates) */
1996 int cur_x, cur_y;
1997 int cur_visible;
1998 random_state *rs; /* used for jumbling */
1999#ifdef USE_DRAGGING
2000 int dragtilex, dragtiley, dragstartx, dragstarty, dragged;
2001#endif
2002};
2003
2004static game_ui *new_ui(const game_state *state)
2005{
2006 void *seed;
2007 int seedsize;
2008 game_ui *ui = snew(game_ui);
2009 ui->org_x = ui->org_y = 0;
2010 ui->cur_x = ui->cx = state->width / 2;
2011 ui->cur_y = ui->cy = state->height / 2;
2012 ui->cur_visible = FALSE;
2013 get_random_seed(&seed, &seedsize);
2014 ui->rs = random_new(seed, seedsize);
2015 sfree(seed);
2016
2017 return ui;
2018}
2019
2020static void free_ui(game_ui *ui)
2021{
2022 random_free(ui->rs);
2023 sfree(ui);
2024}
2025
2026static char *encode_ui(const game_ui *ui)
2027{
2028 char buf[120];
2029 /*
2030 * We preserve the origin and centre-point coordinates over a
2031 * serialise.
2032 */
2033 sprintf(buf, "O%d,%d;C%d,%d", ui->org_x, ui->org_y, ui->cx, ui->cy);
2034 return dupstr(buf);
2035}
2036
2037static void decode_ui(game_ui *ui, const char *encoding)
2038{
2039 sscanf(encoding, "O%d,%d;C%d,%d",
2040 &ui->org_x, &ui->org_y, &ui->cx, &ui->cy);
2041}
2042
2043static void game_changed_state(game_ui *ui, const game_state *oldstate,
2044 const game_state *newstate)
2045{
2046}
2047
2048struct game_drawstate {
2049 int started;
2050 int width, height;
2051 int org_x, org_y;
2052 int tilesize;
2053 int *visible;
2054};
2055
2056/* ----------------------------------------------------------------------
2057 * Process a move.
2058 */
2059static char *interpret_move(const game_state *state, game_ui *ui,
2060 const game_drawstate *ds,
2061 int x, int y, int button)
2062{
2063 char *nullret;
2064 int tx = -1, ty = -1, dir = 0;
2065 int shift = button & MOD_SHFT, ctrl = button & MOD_CTRL;
2066 enum {
2067 NONE, ROTATE_LEFT, ROTATE_180, ROTATE_RIGHT, TOGGLE_LOCK, JUMBLE,
2068 MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR
2069 } action;
2070
2071 button &= ~MOD_MASK;
2072 nullret = NULL;
2073 action = NONE;
2074
2075 if (button == LEFT_BUTTON ||
2076 button == MIDDLE_BUTTON ||
2077#ifdef USE_DRAGGING
2078 button == LEFT_DRAG ||
2079 button == LEFT_RELEASE ||
2080 button == RIGHT_DRAG ||
2081 button == RIGHT_RELEASE ||
2082#endif
2083 button == RIGHT_BUTTON) {
2084
2085 if (ui->cur_visible) {
2086 ui->cur_visible = FALSE;
2087 nullret = "";
2088 }
2089
2090 /*
2091 * The button must have been clicked on a valid tile.
2092 */
2093 x -= WINDOW_OFFSET + TILE_BORDER;
2094 y -= WINDOW_OFFSET + TILE_BORDER;
2095 if (x < 0 || y < 0)
2096 return nullret;
2097 tx = x / TILE_SIZE;
2098 ty = y / TILE_SIZE;
2099 if (tx >= state->width || ty >= state->height)
2100 return nullret;
2101 /* Transform from physical to game coords */
2102 tx = (tx + ui->org_x) % state->width;
2103 ty = (ty + ui->org_y) % state->height;
2104 if (x % TILE_SIZE >= TILE_SIZE - TILE_BORDER ||
2105 y % TILE_SIZE >= TILE_SIZE - TILE_BORDER)
2106 return nullret;
2107
2108#ifdef USE_DRAGGING
2109
2110 if (button == MIDDLE_BUTTON
2111#ifdef STYLUS_BASED
2112 || button == RIGHT_BUTTON /* with a stylus, `right-click' locks */
2113#endif
2114 ) {
2115 /*
2116 * Middle button never drags: it only toggles the lock.
2117 */
2118 action = TOGGLE_LOCK;
2119 } else if (button == LEFT_BUTTON
2120#ifndef STYLUS_BASED
2121 || button == RIGHT_BUTTON /* (see above) */
2122#endif
2123 ) {
2124 /*
2125 * Otherwise, we note down the start point for a drag.
2126 */
2127 ui->dragtilex = tx;
2128 ui->dragtiley = ty;
2129 ui->dragstartx = x % TILE_SIZE;
2130 ui->dragstarty = y % TILE_SIZE;
2131 ui->dragged = FALSE;
2132 return nullret; /* no actual action */
2133 } else if (button == LEFT_DRAG
2134#ifndef STYLUS_BASED
2135 || button == RIGHT_DRAG
2136#endif
2137 ) {
2138 /*
2139 * Find the new drag point and see if it necessitates a
2140 * rotation.
2141 */
2142 int x0,y0, xA,yA, xC,yC, xF,yF;
2143 int mx, my;
2144 int d0, dA, dC, dF, dmin;
2145
2146 tx = ui->dragtilex;
2147 ty = ui->dragtiley;
2148
2149 mx = x - (ui->dragtilex * TILE_SIZE);
2150 my = y - (ui->dragtiley * TILE_SIZE);
2151
2152 x0 = ui->dragstartx;
2153 y0 = ui->dragstarty;
2154 xA = ui->dragstarty;
2155 yA = TILE_SIZE-1 - ui->dragstartx;
2156 xF = TILE_SIZE-1 - ui->dragstartx;
2157 yF = TILE_SIZE-1 - ui->dragstarty;
2158 xC = TILE_SIZE-1 - ui->dragstarty;
2159 yC = ui->dragstartx;
2160
2161 d0 = (mx-x0)*(mx-x0) + (my-y0)*(my-y0);
2162 dA = (mx-xA)*(mx-xA) + (my-yA)*(my-yA);
2163 dF = (mx-xF)*(mx-xF) + (my-yF)*(my-yF);
2164 dC = (mx-xC)*(mx-xC) + (my-yC)*(my-yC);
2165
2166 dmin = min(min(d0,dA),min(dF,dC));
2167
2168 if (d0 == dmin) {
2169 return nullret;
2170 } else if (dF == dmin) {
2171 action = ROTATE_180;
2172 ui->dragstartx = xF;
2173 ui->dragstarty = yF;
2174 ui->dragged = TRUE;
2175 } else if (dA == dmin) {
2176 action = ROTATE_LEFT;
2177 ui->dragstartx = xA;
2178 ui->dragstarty = yA;
2179 ui->dragged = TRUE;
2180 } else /* dC == dmin */ {
2181 action = ROTATE_RIGHT;
2182 ui->dragstartx = xC;
2183 ui->dragstarty = yC;
2184 ui->dragged = TRUE;
2185 }
2186 } else if (button == LEFT_RELEASE
2187#ifndef STYLUS_BASED
2188 || button == RIGHT_RELEASE
2189#endif
2190 ) {
2191 if (!ui->dragged) {
2192 /*
2193 * There was a click but no perceptible drag:
2194 * revert to single-click behaviour.
2195 */
2196 tx = ui->dragtilex;
2197 ty = ui->dragtiley;
2198
2199 if (button == LEFT_RELEASE)
2200 action = ROTATE_LEFT;
2201 else
2202 action = ROTATE_RIGHT;
2203 } else
2204 return nullret; /* no action */
2205 }
2206
2207#else /* USE_DRAGGING */
2208
2209 action = (button == LEFT_BUTTON ? ROTATE_LEFT :
2210 button == RIGHT_BUTTON ? ROTATE_RIGHT : TOGGLE_LOCK);
2211
2212#endif /* USE_DRAGGING */
2213
2214 } else if (IS_CURSOR_MOVE(button)) {
2215 switch (button) {
2216 case CURSOR_UP: dir = U; break;
2217 case CURSOR_DOWN: dir = D; break;
2218 case CURSOR_LEFT: dir = L; break;
2219 case CURSOR_RIGHT: dir = R; break;
2220 default: return nullret;
2221 }
2222 if (shift && ctrl) action = MOVE_ORIGIN_AND_SOURCE;
2223 else if (shift) action = MOVE_ORIGIN;
2224 else if (ctrl) action = MOVE_SOURCE;
2225 else action = MOVE_CURSOR;
2226 } else if (button == 'a' || button == 's' || button == 'd' ||
2227 button == 'A' || button == 'S' || button == 'D' ||
2228 button == 'f' || button == 'F' ||
2229 IS_CURSOR_SELECT(button)) {
2230 tx = ui->cur_x;
2231 ty = ui->cur_y;
2232 if (button == 'a' || button == 'A' || button == CURSOR_SELECT)
2233 action = ROTATE_LEFT;
2234 else if (button == 's' || button == 'S' || button == CURSOR_SELECT2)
2235 action = TOGGLE_LOCK;
2236 else if (button == 'd' || button == 'D')
2237 action = ROTATE_RIGHT;
2238 else if (button == 'f' || button == 'F')
2239 action = ROTATE_180;
2240 ui->cur_visible = TRUE;
2241 } else if (button == 'j' || button == 'J') {
2242 /* XXX should we have some mouse control for this? */
2243 action = JUMBLE;
2244 } else
2245 return nullret;
2246
2247 /*
2248 * The middle button locks or unlocks a tile. (A locked tile
2249 * cannot be turned, and is visually marked as being locked.
2250 * This is a convenience for the player, so that once they are
2251 * sure which way round a tile goes, they can lock it and thus
2252 * avoid forgetting later on that they'd already done that one;
2253 * and the locking also prevents them turning the tile by
2254 * accident. If they change their mind, another middle click
2255 * unlocks it.)
2256 */
2257 if (action == TOGGLE_LOCK) {
2258 char buf[80];
2259 sprintf(buf, "L%d,%d", tx, ty);
2260 return dupstr(buf);
2261 } else if (action == ROTATE_LEFT || action == ROTATE_RIGHT ||
2262 action == ROTATE_180) {
2263 char buf[80];
2264
2265 /*
2266 * The left and right buttons have no effect if clicked on a
2267 * locked tile.
2268 */
2269 if (tile(state, tx, ty) & LOCKED)
2270 return nullret;
2271
2272 /*
2273 * Otherwise, turn the tile one way or the other. Left button
2274 * turns anticlockwise; right button turns clockwise.
2275 */
2276 sprintf(buf, "%c%d,%d", (int)(action == ROTATE_LEFT ? 'A' :
2277 action == ROTATE_RIGHT ? 'C' : 'F'), tx, ty);
2278 return dupstr(buf);
2279 } else if (action == JUMBLE) {
2280 /*
2281 * Jumble all unlocked tiles to random orientations.
2282 */
2283
2284 int jx, jy, maxlen;
2285 char *ret, *p;
2286
2287 /*
2288 * Maximum string length assumes no int can be converted to
2289 * decimal and take more than 11 digits!
2290 */
2291 maxlen = state->width * state->height * 25 + 3;
2292
2293 ret = snewn(maxlen, char);
2294 p = ret;
2295 *p++ = 'J';
2296
2297 for (jy = 0; jy < state->height; jy++) {
2298 for (jx = 0; jx < state->width; jx++) {
2299 if (!(tile(state, jx, jy) & LOCKED)) {
2300 int rot = random_upto(ui->rs, 4);
2301 if (rot) {
2302 p += sprintf(p, ";%c%d,%d", "AFC"[rot-1], jx, jy);
2303 }
2304 }
2305 }
2306 }
2307 *p++ = '\0';
2308 assert(p - ret < maxlen);
2309 ret = sresize(ret, p - ret, char);
2310
2311 return ret;
2312 } else if (action == MOVE_ORIGIN || action == MOVE_SOURCE ||
2313 action == MOVE_ORIGIN_AND_SOURCE || action == MOVE_CURSOR) {
2314 assert(dir != 0);
2315 if (action == MOVE_ORIGIN || action == MOVE_ORIGIN_AND_SOURCE) {
2316 if (state->wrapping) {
2317 OFFSET(ui->org_x, ui->org_y, ui->org_x, ui->org_y, dir, state);
2318 } else return nullret; /* disallowed for non-wrapping grids */
2319 }
2320 if (action == MOVE_SOURCE || action == MOVE_ORIGIN_AND_SOURCE) {
2321 OFFSET(ui->cx, ui->cy, ui->cx, ui->cy, dir, state);
2322 }
2323 if (action == MOVE_CURSOR) {
2324 OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state);
2325 ui->cur_visible = TRUE;
2326 }
2327 return "";
2328 } else {
2329 return NULL;
2330 }
2331}
2332
2333static game_state *execute_move(const game_state *from, const char *move)
2334{
2335 game_state *ret;
2336 int tx = -1, ty = -1, n, noanim, orig;
2337
2338 ret = dup_game(from);
2339
2340 if (move[0] == 'J' || move[0] == 'S') {
2341 if (move[0] == 'S')
2342 ret->used_solve = TRUE;
2343
2344 move++;
2345 if (*move == ';')
2346 move++;
2347 noanim = TRUE;
2348 } else
2349 noanim = FALSE;
2350
2351 ret->last_rotate_dir = 0; /* suppress animation */
2352 ret->last_rotate_x = ret->last_rotate_y = 0;
2353
2354 while (*move) {
2355 if ((move[0] == 'A' || move[0] == 'C' ||
2356 move[0] == 'F' || move[0] == 'L') &&
2357 sscanf(move+1, "%d,%d%n", &tx, &ty, &n) >= 2 &&
2358 tx >= 0 && tx < from->width && ty >= 0 && ty < from->height) {
2359 orig = tile(ret, tx, ty);
2360 if (move[0] == 'A') {
2361 tile(ret, tx, ty) = A(orig);
2362 if (!noanim)
2363 ret->last_rotate_dir = +1;
2364 } else if (move[0] == 'F') {
2365 tile(ret, tx, ty) = F(orig);
2366 if (!noanim)
2367 ret->last_rotate_dir = +2; /* + for sake of argument */
2368 } else if (move[0] == 'C') {
2369 tile(ret, tx, ty) = C(orig);
2370 if (!noanim)
2371 ret->last_rotate_dir = -1;
2372 } else {
2373 assert(move[0] == 'L');
2374 tile(ret, tx, ty) ^= LOCKED;
2375 }
2376
2377 move += 1 + n;
2378 if (*move == ';') move++;
2379 } else {
2380 free_game(ret);
2381 return NULL;
2382 }
2383 }
2384 if (!noanim) {
2385 if (tx == -1 || ty == -1) { free_game(ret); return NULL; }
2386 ret->last_rotate_x = tx;
2387 ret->last_rotate_y = ty;
2388 }
2389
2390 /*
2391 * Check whether the game has been completed.
2392 *
2393 * For this purpose it doesn't matter where the source square is,
2394 * because we can start from anywhere (or, at least, any square
2395 * that's non-empty!), and correctly determine whether the game is
2396 * completed.
2397 */
2398 {
2399 unsigned char *active;
2400 int pos;
2401 int complete = TRUE;
2402
2403 for (pos = 0; pos < ret->width * ret->height; pos++)
2404 if (ret->tiles[pos] & 0xF)
2405 break;
2406
2407 if (pos < ret->width * ret->height) {
2408 active = compute_active(ret, pos % ret->width, pos / ret->width);
2409
2410 for (pos = 0; pos < ret->width * ret->height; pos++)
2411 if ((ret->tiles[pos] & 0xF) && !active[pos]) {
2412 complete = FALSE;
2413 break;
2414 }
2415
2416 sfree(active);
2417 }
2418
2419 if (complete)
2420 ret->completed = TRUE;
2421 }
2422
2423 return ret;
2424}
2425
2426
2427/* ----------------------------------------------------------------------
2428 * Routines for drawing the game position on the screen.
2429 */
2430
2431static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2432{
2433 game_drawstate *ds = snew(game_drawstate);
2434 int i;
2435
2436 ds->started = FALSE;
2437 ds->width = state->width;
2438 ds->height = state->height;
2439 ds->org_x = ds->org_y = -1;
2440 ds->visible = snewn(state->width * state->height, int);
2441 ds->tilesize = 0; /* undecided yet */
2442 for (i = 0; i < state->width * state->height; i++)
2443 ds->visible[i] = -1;
2444
2445 return ds;
2446}
2447
2448static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2449{
2450 sfree(ds->visible);
2451 sfree(ds);
2452}
2453
2454static void game_compute_size(const game_params *params, int tilesize,
2455 int *x, int *y)
2456{
2457 *x = WINDOW_OFFSET * 2 + tilesize * params->width + TILE_BORDER;
2458 *y = WINDOW_OFFSET * 2 + tilesize * params->height + TILE_BORDER;
2459}
2460
2461static void game_set_size(drawing *dr, game_drawstate *ds,
2462 const game_params *params, int tilesize)
2463{
2464 ds->tilesize = tilesize;
2465}
2466
2467static float *game_colours(frontend *fe, int *ncolours)
2468{
2469 float *ret;
2470
2471 ret = snewn(NCOLOURS * 3, float);
2472 *ncolours = NCOLOURS;
2473
2474 /*
2475 * Basic background colour is whatever the front end thinks is
2476 * a sensible default.
2477 */
2478 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2479
2480 /*
2481 * Wires are black.
2482 */
2483 ret[COL_WIRE * 3 + 0] = 0.0F;
2484 ret[COL_WIRE * 3 + 1] = 0.0F;
2485 ret[COL_WIRE * 3 + 2] = 0.0F;
2486
2487 /*
2488 * Powered wires and powered endpoints are cyan.
2489 */
2490 ret[COL_POWERED * 3 + 0] = 0.0F;
2491 ret[COL_POWERED * 3 + 1] = 1.0F;
2492 ret[COL_POWERED * 3 + 2] = 1.0F;
2493
2494 /*
2495 * Barriers are red.
2496 */
2497 ret[COL_BARRIER * 3 + 0] = 1.0F;
2498 ret[COL_BARRIER * 3 + 1] = 0.0F;
2499 ret[COL_BARRIER * 3 + 2] = 0.0F;
2500
2501 /*
2502 * Highlighted loops are red as well.
2503 */
2504 ret[COL_LOOP * 3 + 0] = 1.0F;
2505 ret[COL_LOOP * 3 + 1] = 0.0F;
2506 ret[COL_LOOP * 3 + 2] = 0.0F;
2507
2508 /*
2509 * Unpowered endpoints are blue.
2510 */
2511 ret[COL_ENDPOINT * 3 + 0] = 0.0F;
2512 ret[COL_ENDPOINT * 3 + 1] = 0.0F;
2513 ret[COL_ENDPOINT * 3 + 2] = 1.0F;
2514
2515 /*
2516 * Tile borders are a darker grey than the background.
2517 */
2518 ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
2519 ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
2520 ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
2521
2522 /*
2523 * Locked tiles are a grey in between those two.
2524 */
2525 ret[COL_LOCKED * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
2526 ret[COL_LOCKED * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
2527 ret[COL_LOCKED * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
2528
2529 return ret;
2530}
2531
2532static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2,
2533 int colour)
2534{
2535 draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE);
2536 draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE);
2537 draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE);
2538 draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE);
2539 draw_line(dr, x1, y1, x2, y2, colour);
2540}
2541
2542static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2,
2543 int colour)
2544{
2545 int mx = (x1 < x2 ? x1 : x2);
2546 int my = (y1 < y2 ? y1 : y2);
2547 int dx = (x2 + x1 - 2*mx + 1);
2548 int dy = (y2 + y1 - 2*my + 1);
2549
2550 draw_rect(dr, mx, my, dx, dy, colour);
2551}
2552
2553/*
2554 * draw_barrier_corner() and draw_barrier() are passed physical coords
2555 */
2556static void draw_barrier_corner(drawing *dr, game_drawstate *ds,
2557 int x, int y, int dx, int dy, int phase)
2558{
2559 int bx = WINDOW_OFFSET + TILE_SIZE * x;
2560 int by = WINDOW_OFFSET + TILE_SIZE * y;
2561 int x1, y1;
2562
2563 x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
2564 y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
2565
2566 if (phase == 0) {
2567 draw_rect_coords(dr, bx+x1+dx, by+y1,
2568 bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy,
2569 COL_WIRE);
2570 draw_rect_coords(dr, bx+x1, by+y1+dy,
2571 bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
2572 COL_WIRE);
2573 } else {
2574 draw_rect_coords(dr, bx+x1, by+y1,
2575 bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy,
2576 COL_BARRIER);
2577 }
2578}
2579
2580static void draw_barrier(drawing *dr, game_drawstate *ds,
2581 int x, int y, int dir, int phase)
2582{
2583 int bx = WINDOW_OFFSET + TILE_SIZE * x;
2584 int by = WINDOW_OFFSET + TILE_SIZE * y;
2585 int x1, y1, w, h;
2586
2587 x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0);
2588 y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0);
2589 w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
2590 h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
2591
2592 if (phase == 0) {
2593 draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
2594 } else {
2595 draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER);
2596 }
2597}
2598
2599/*
2600 * draw_tile() is passed physical coordinates
2601 */
2602static void draw_tile(drawing *dr, const game_state *state, game_drawstate *ds,
2603 int x, int y, int tile, int src, float angle, int cursor)
2604{
2605 int bx = WINDOW_OFFSET + TILE_SIZE * x;
2606 int by = WINDOW_OFFSET + TILE_SIZE * y;
2607 float matrix[4];
2608 float cx, cy, ex, ey, tx, ty;
2609 int dir, col, phase;
2610
2611 /*
2612 * When we draw a single tile, we must draw everything up to
2613 * and including the borders around the tile. This means that
2614 * if the neighbouring tiles have connections to those borders,
2615 * we must draw those connections on the borders themselves.
2616 */
2617
2618 clip(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
2619
2620 /*
2621 * So. First blank the tile out completely: draw a big
2622 * rectangle in border colour, and a smaller rectangle in
2623 * background colour to fill it in.
2624 */
2625 draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER,
2626 COL_BORDER);
2627 draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER,
2628 TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER,
2629 tile & LOCKED ? COL_LOCKED : COL_BACKGROUND);
2630
2631 /*
2632 * Draw an inset outline rectangle as a cursor, in whichever of
2633 * COL_LOCKED and COL_BACKGROUND we aren't currently drawing
2634 * in.
2635 */
2636 if (cursor) {
2637 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8,
2638 bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2639 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2640 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8,
2641 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8,
2642 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2643 draw_line(dr, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8,
2644 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2645 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2646 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2647 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2648 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2649 }
2650
2651 /*
2652 * Set up the rotation matrix.
2653 */
2654 matrix[0] = (float)cos(angle * PI / 180.0);
2655 matrix[1] = (float)-sin(angle * PI / 180.0);
2656 matrix[2] = (float)sin(angle * PI / 180.0);
2657 matrix[3] = (float)cos(angle * PI / 180.0);
2658
2659 /*
2660 * Draw the wires.
2661 */
2662 cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F;
2663 col = (tile & ACTIVE ? COL_POWERED : COL_WIRE);
2664 for (dir = 1; dir < 0x10; dir <<= 1) {
2665 if (tile & dir) {
2666 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
2667 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
2668 MATMUL(tx, ty, matrix, ex, ey);
2669 draw_filled_line(dr, bx+(int)cx, by+(int)cy,
2670 bx+(int)(cx+tx), by+(int)(cy+ty),
2671 COL_WIRE);
2672 }
2673 }
2674 for (dir = 1; dir < 0x10; dir <<= 1) {
2675 if (tile & dir) {
2676 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
2677 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
2678 MATMUL(tx, ty, matrix, ex, ey);
2679 draw_line(dr, bx+(int)cx, by+(int)cy,
2680 bx+(int)(cx+tx), by+(int)(cy+ty),
2681 (tile & LOOP(dir)) ? COL_LOOP : col);
2682 }
2683 }
2684 /* If we've drawn any loop-highlighted arms, make sure the centre
2685 * point is loop-coloured rather than a later arm overwriting it. */
2686 if (tile & (RLOOP | ULOOP | LLOOP | DLOOP))
2687 draw_rect(dr, bx+(int)cx, by+(int)cy, 1, 1, COL_LOOP);
2688
2689 /*
2690 * Draw the box in the middle. We do this in blue if the tile
2691 * is an unpowered endpoint, in cyan if the tile is a powered
2692 * endpoint, in black if the tile is the centrepiece, and
2693 * otherwise not at all.
2694 */
2695 col = -1;
2696 if (src)
2697 col = COL_WIRE;
2698 else if (COUNT(tile) == 1) {
2699 col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT);
2700 }
2701 if (col >= 0) {
2702 int i, points[8];
2703
2704 points[0] = +1; points[1] = +1;
2705 points[2] = +1; points[3] = -1;
2706 points[4] = -1; points[5] = -1;
2707 points[6] = -1; points[7] = +1;
2708
2709 for (i = 0; i < 8; i += 2) {
2710 ex = (TILE_SIZE * 0.24F) * points[i];
2711 ey = (TILE_SIZE * 0.24F) * points[i+1];
2712 MATMUL(tx, ty, matrix, ex, ey);
2713 points[i] = bx+(int)(cx+tx);
2714 points[i+1] = by+(int)(cy+ty);
2715 }
2716
2717 draw_polygon(dr, points, 4, col, COL_WIRE);
2718 }
2719
2720 /*
2721 * Draw the points on the border if other tiles are connected
2722 * to us.
2723 */
2724 for (dir = 1; dir < 0x10; dir <<= 1) {
2725 int dx, dy, px, py, lx, ly, vx, vy, ox, oy;
2726
2727 dx = X(dir);
2728 dy = Y(dir);
2729
2730 ox = x + dx;
2731 oy = y + dy;
2732
2733 if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height)
2734 continue;
2735
2736 if (!(tile(state, GX(ox), GY(oy)) & F(dir)))
2737 continue;
2738
2739 px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx);
2740 py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy);
2741 lx = dx * (TILE_BORDER-1);
2742 ly = dy * (TILE_BORDER-1);
2743 vx = (dy ? 1 : 0);
2744 vy = (dx ? 1 : 0);
2745
2746 if (angle == 0.0 && (tile & dir)) {
2747 /*
2748 * If we are fully connected to the other tile, we must
2749 * draw right across the tile border. (We can use our
2750 * own ACTIVE state to determine what colour to do this
2751 * in: if we are fully connected to the other tile then
2752 * the two ACTIVE states will be the same.)
2753 */
2754 draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
2755 draw_rect_coords(dr, px, py, px+lx, py+ly,
2756 ((tile & LOOP(dir)) ? COL_LOOP :
2757 (tile & ACTIVE) ? COL_POWERED :
2758 COL_WIRE));
2759 } else {
2760 /*
2761 * The other tile extends into our border, but isn't
2762 * actually connected to us. Just draw a single black
2763 * dot.
2764 */
2765 draw_rect_coords(dr, px, py, px, py, COL_WIRE);
2766 }
2767 }
2768
2769 /*
2770 * Draw barrier corners, and then barriers.
2771 */
2772 for (phase = 0; phase < 2; phase++) {
2773 for (dir = 1; dir < 0x10; dir <<= 1) {
2774 int x1, y1, corner = FALSE;
2775 /*
2776 * If at least one barrier terminates at the corner
2777 * between dir and A(dir), draw a barrier corner.
2778 */
2779 if (barrier(state, GX(x), GY(y)) & (dir | A(dir))) {
2780 corner = TRUE;
2781 } else {
2782 /*
2783 * Only count barriers terminating at this corner
2784 * if they're physically next to the corner. (That
2785 * is, if they've wrapped round from the far side
2786 * of the screen, they don't count.)
2787 */
2788 x1 = x + X(dir);
2789 y1 = y + Y(dir);
2790 if (x1 >= 0 && x1 < state->width &&
2791 y1 >= 0 && y1 < state->height &&
2792 (barrier(state, GX(x1), GY(y1)) & A(dir))) {
2793 corner = TRUE;
2794 } else {
2795 x1 = x + X(A(dir));
2796 y1 = y + Y(A(dir));
2797 if (x1 >= 0 && x1 < state->width &&
2798 y1 >= 0 && y1 < state->height &&
2799 (barrier(state, GX(x1), GY(y1)) & dir))
2800 corner = TRUE;
2801 }
2802 }
2803
2804 if (corner) {
2805 /*
2806 * At least one barrier terminates here. Draw a
2807 * corner.
2808 */
2809 draw_barrier_corner(dr, ds, x, y,
2810 X(dir)+X(A(dir)), Y(dir)+Y(A(dir)),
2811 phase);
2812 }
2813 }
2814
2815 for (dir = 1; dir < 0x10; dir <<= 1)
2816 if (barrier(state, GX(x), GY(y)) & dir)
2817 draw_barrier(dr, ds, x, y, dir, phase);
2818 }
2819
2820 unclip(dr);
2821
2822 draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
2823}
2824
2825static void game_redraw(drawing *dr, game_drawstate *ds,
2826 const game_state *oldstate, const game_state *state,
2827 int dir, const game_ui *ui,
2828 float t, float ft)
2829{
2830 int x, y, tx, ty, frame, last_rotate_dir, moved_origin = FALSE;
2831 unsigned char *active;
2832 int *loops;
2833 float angle = 0.0;
2834
2835 /*
2836 * Clear the screen, and draw the exterior barrier lines, if
2837 * this is our first call or if the origin has changed.
2838 */
2839 if (!ds->started || ui->org_x != ds->org_x || ui->org_y != ds->org_y) {
2840 int phase;
2841
2842 ds->started = TRUE;
2843
2844 draw_rect(dr, 0, 0,
2845 WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
2846 WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
2847 COL_BACKGROUND);
2848
2849 ds->org_x = ui->org_x;
2850 ds->org_y = ui->org_y;
2851 moved_origin = TRUE;
2852
2853 draw_update(dr, 0, 0,
2854 WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
2855 WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
2856
2857 for (phase = 0; phase < 2; phase++) {
2858
2859 for (x = 0; x < ds->width; x++) {
2860 if (x+1 < ds->width) {
2861 if (barrier(state, GX(x), GY(0)) & R)
2862 draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
2863 if (barrier(state, GX(x), GY(ds->height-1)) & R)
2864 draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
2865 }
2866 if (barrier(state, GX(x), GY(0)) & U) {
2867 draw_barrier_corner(dr, ds, x, -1, -1, +1, phase);
2868 draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
2869 draw_barrier(dr, ds, x, -1, D, phase);
2870 }
2871 if (barrier(state, GX(x), GY(ds->height-1)) & D) {
2872 draw_barrier_corner(dr, ds, x, ds->height, -1, -1, phase);
2873 draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
2874 draw_barrier(dr, ds, x, ds->height, U, phase);
2875 }
2876 }
2877
2878 for (y = 0; y < ds->height; y++) {
2879 if (y+1 < ds->height) {
2880 if (barrier(state, GX(0), GY(y)) & D)
2881 draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
2882 if (barrier(state, GX(ds->width-1), GY(y)) & D)
2883 draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
2884 }
2885 if (barrier(state, GX(0), GY(y)) & L) {
2886 draw_barrier_corner(dr, ds, -1, y, +1, -1, phase);
2887 draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
2888 draw_barrier(dr, ds, -1, y, R, phase);
2889 }
2890 if (barrier(state, GX(ds->width-1), GY(y)) & R) {
2891 draw_barrier_corner(dr, ds, ds->width, y, -1, -1, phase);
2892 draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
2893 draw_barrier(dr, ds, ds->width, y, L, phase);
2894 }
2895 }
2896 }
2897 }
2898
2899 tx = ty = -1;
2900 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
2901 state->last_rotate_dir;
2902 if (oldstate && (t < ROTATE_TIME) && last_rotate_dir) {
2903 /*
2904 * We're animating a single tile rotation. Find the turning
2905 * tile.
2906 */
2907 tx = (dir==-1 ? oldstate->last_rotate_x : state->last_rotate_x);
2908 ty = (dir==-1 ? oldstate->last_rotate_y : state->last_rotate_y);
2909 angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME);
2910 state = oldstate;
2911 }
2912
2913 frame = -1;
2914 if (ft > 0) {
2915 /*
2916 * We're animating a completion flash. Find which frame
2917 * we're at.
2918 */
2919 frame = (int)(ft / FLASH_FRAME);
2920 }
2921
2922 /*
2923 * Draw any tile which differs from the way it was last drawn.
2924 */
2925 active = compute_active(state, ui->cx, ui->cy);
2926 loops = compute_loops(state);
2927
2928 for (x = 0; x < ds->width; x++)
2929 for (y = 0; y < ds->height; y++) {
2930 int c = tile(state, GX(x), GY(y)) |
2931 index(state, active, GX(x), GY(y)) |
2932 index(state, loops, GX(x), GY(y));
2933 int is_src = GX(x) == ui->cx && GY(y) == ui->cy;
2934 int is_anim = GX(x) == tx && GY(y) == ty;
2935 int is_cursor = ui->cur_visible &&
2936 GX(x) == ui->cur_x && GY(y) == ui->cur_y;
2937
2938 /*
2939 * In a completion flash, we adjust the LOCKED bit
2940 * depending on our distance from the centre point and
2941 * the frame number.
2942 */
2943 if (frame >= 0) {
2944 int rcx = RX(ui->cx), rcy = RY(ui->cy);
2945 int xdist, ydist, dist;
2946 xdist = (x < rcx ? rcx - x : x - rcx);
2947 ydist = (y < rcy ? rcy - y : y - rcy);
2948 dist = (xdist > ydist ? xdist : ydist);
2949
2950 if (frame >= dist && frame < dist+4) {
2951 int lock = (frame - dist) & 1;
2952 lock = lock ? LOCKED : 0;
2953 c = (c &~ LOCKED) | lock;
2954 }
2955 }
2956
2957 if (moved_origin ||
2958 index(state, ds->visible, x, y) != c ||
2959 index(state, ds->visible, x, y) == -1 ||
2960 is_src || is_anim || is_cursor) {
2961 draw_tile(dr, state, ds, x, y, c,
2962 is_src, (is_anim ? angle : 0.0F), is_cursor);
2963 if (is_src || is_anim || is_cursor)
2964 index(state, ds->visible, x, y) = -1;
2965 else
2966 index(state, ds->visible, x, y) = c;
2967 }
2968 }
2969
2970 /*
2971 * Update the status bar.
2972 */
2973 {
2974 char statusbuf[256], *p;
2975 int i, n, n2, a;
2976 int complete = FALSE;
2977
2978 p = statusbuf;
2979 *p = '\0'; /* ensure even an empty status string is terminated */
2980
2981 if (state->used_solve) {
2982 p += sprintf(p, "Auto-solved. ");
2983 complete = TRUE;
2984 } else if (state->completed) {
2985 p += sprintf(p, "COMPLETED! ");
2986 complete = TRUE;
2987 }
2988
2989 /*
2990 * Omit the 'Active: n/N' counter completely if the source
2991 * tile is a completely empty one, because then the active
2992 * count can't help but read '1'.
2993 */
2994 if (tile(state, ui->cx, ui->cy) & 0xF) {
2995 n = state->width * state->height;
2996 for (i = a = n2 = 0; i < n; i++) {
2997 if (active[i])
2998 a++;
2999 if (state->tiles[i] & 0xF)
3000 n2++;
3001 }
3002
3003 /*
3004 * Also, if we're displaying a completion indicator and
3005 * the game is still in its completed state (i.e. every
3006 * tile is active), we might as well omit this too.
3007 */
3008 if (!complete || a < n2)
3009 p += sprintf(p, "Active: %d/%d", a, n2);
3010 }
3011
3012 status_bar(dr, statusbuf);
3013 }
3014
3015 sfree(active);
3016 sfree(loops);
3017}
3018
3019static float game_anim_length(const game_state *oldstate,
3020 const game_state *newstate, int dir, game_ui *ui)
3021{
3022 int last_rotate_dir;
3023
3024 /*
3025 * Don't animate if last_rotate_dir is zero.
3026 */
3027 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
3028 newstate->last_rotate_dir;
3029 if (last_rotate_dir)
3030 return ROTATE_TIME;
3031
3032 return 0.0F;
3033}
3034
3035static float game_flash_length(const game_state *oldstate,
3036 const game_state *newstate, int dir, game_ui *ui)
3037{
3038 /*
3039 * If the game has just been completed, we display a completion
3040 * flash.
3041 */
3042 if (!oldstate->completed && newstate->completed &&
3043 !oldstate->used_solve && !newstate->used_solve) {
3044 int size = 0;
3045 if (size < newstate->width)
3046 size = newstate->width;
3047 if (size < newstate->height)
3048 size = newstate->height;
3049 return FLASH_FRAME * (size+4);
3050 }
3051
3052 return 0.0F;
3053}
3054
3055static int game_status(const game_state *state)
3056{
3057 return state->completed ? +1 : 0;
3058}
3059
3060static int game_timing_state(const game_state *state, game_ui *ui)
3061{
3062 return TRUE;
3063}
3064
3065static void game_print_size(const game_params *params, float *x, float *y)
3066{
3067 int pw, ph;
3068
3069 /*
3070 * I'll use 8mm squares by default.
3071 */
3072 game_compute_size(params, 800, &pw, &ph);
3073 *x = pw / 100.0F;
3074 *y = ph / 100.0F;
3075}
3076
3077static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y,
3078 int topleft, int v, int drawlines, int ink)
3079{
3080 int tx, ty, cx, cy, r, br, k, thick;
3081
3082 tx = WINDOW_OFFSET + TILE_SIZE * x;
3083 ty = WINDOW_OFFSET + TILE_SIZE * y;
3084
3085 /*
3086 * Find our centre point.
3087 */
3088 if (topleft) {
3089 cx = tx + (v & L ? TILE_SIZE / 4 : TILE_SIZE / 6);
3090 cy = ty + (v & U ? TILE_SIZE / 4 : TILE_SIZE / 6);
3091 r = TILE_SIZE / 8;
3092 br = TILE_SIZE / 32;
3093 } else {
3094 cx = tx + TILE_SIZE / 2;
3095 cy = ty + TILE_SIZE / 2;
3096 r = TILE_SIZE / 2;
3097 br = TILE_SIZE / 8;
3098 }
3099 thick = r / 20;
3100
3101 /*
3102 * Draw the square block if we have an endpoint.
3103 */
3104 if (v == 1 || v == 2 || v == 4 || v == 8)
3105 draw_rect(dr, cx - br, cy - br, br*2, br*2, ink);
3106
3107 /*
3108 * Draw each radial line.
3109 */
3110 if (drawlines) {
3111 for (k = 1; k < 16; k *= 2)
3112 if (v & k) {
3113 int x1 = min(cx, cx + (r-thick) * X(k));
3114 int x2 = max(cx, cx + (r-thick) * X(k));
3115 int y1 = min(cy, cy + (r-thick) * Y(k));
3116 int y2 = max(cy, cy + (r-thick) * Y(k));
3117 draw_rect(dr, x1 - thick, y1 - thick,
3118 (x2 - x1) + 2*thick, (y2 - y1) + 2*thick, ink);
3119 }
3120 }
3121}
3122
3123static void game_print(drawing *dr, const game_state *state, int tilesize)
3124{
3125 int w = state->width, h = state->height;
3126 int ink = print_mono_colour(dr, 0);
3127 int x, y;
3128
3129 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3130 game_drawstate ads, *ds = &ads;
3131 game_set_size(dr, ds, NULL, tilesize);
3132
3133 /*
3134 * Border.
3135 */
3136 print_line_width(dr, TILE_SIZE / (state->wrapping ? 128 : 12));
3137 draw_rect_outline(dr, WINDOW_OFFSET, WINDOW_OFFSET,
3138 TILE_SIZE * w, TILE_SIZE * h, ink);
3139
3140 /*
3141 * Grid.
3142 */
3143 print_line_width(dr, TILE_SIZE / 128);
3144 for (x = 1; x < w; x++)
3145 draw_line(dr, WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET,
3146 WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET + TILE_SIZE * h,
3147 ink);
3148 for (y = 1; y < h; y++)
3149 draw_line(dr, WINDOW_OFFSET, WINDOW_OFFSET + TILE_SIZE * y,
3150 WINDOW_OFFSET + TILE_SIZE * w, WINDOW_OFFSET + TILE_SIZE * y,
3151 ink);
3152
3153 /*
3154 * Barriers.
3155 */
3156 for (y = 0; y <= h; y++)
3157 for (x = 0; x <= w; x++) {
3158 int b = barrier(state, x % w, y % h);
3159 if (x < w && (b & U))
3160 draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
3161 WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
3162 TILE_SIZE + TILE_SIZE/24 * 2, TILE_SIZE/24 * 2, ink);
3163 if (y < h && (b & L))
3164 draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
3165 WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
3166 TILE_SIZE/24 * 2, TILE_SIZE + TILE_SIZE/24 * 2, ink);
3167 }
3168
3169 /*
3170 * Grid contents.
3171 */
3172 for (y = 0; y < h; y++)
3173 for (x = 0; x < w; x++) {
3174 int vx, v = tile(state, x, y);
3175 int locked = v & LOCKED;
3176
3177 v &= 0xF;
3178
3179 /*
3180 * Rotate into a standard orientation for the top left
3181 * corner diagram.
3182 */
3183 vx = v;
3184 while (vx != 0 && vx != 15 && vx != 1 && vx != 9 && vx != 13 &&
3185 vx != 5)
3186 vx = A(vx);
3187
3188 /*
3189 * Draw the top left corner diagram.
3190 */
3191 draw_diagram(dr, ds, x, y, TRUE, vx, TRUE, ink);
3192
3193 /*
3194 * Draw the real solution diagram, if we're doing so.
3195 */
3196 draw_diagram(dr, ds, x, y, FALSE, v, locked, ink);
3197 }
3198}
3199
3200#ifdef COMBINED
3201#define thegame net
3202#endif
3203
3204const struct game thegame = {
3205 "Net", "games.net", "net",
3206 default_params,
3207 game_fetch_preset, NULL,
3208 decode_params,
3209 encode_params,
3210 free_params,
3211 dup_params,
3212 TRUE, game_configure, custom_params,
3213 validate_params,
3214 new_game_desc,
3215 validate_desc,
3216 new_game,
3217 dup_game,
3218 free_game,
3219 TRUE, solve_game,
3220 FALSE, game_can_format_as_text_now, game_text_format,
3221 new_ui,
3222 free_ui,
3223 encode_ui,
3224 decode_ui,
3225 game_changed_state,
3226 interpret_move,
3227 execute_move,
3228 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3229 game_colours,
3230 game_new_drawstate,
3231 game_free_drawstate,
3232 game_redraw,
3233 game_anim_length,
3234 game_flash_length,
3235 game_status,
3236 TRUE, FALSE, game_print_size, game_print,
3237 TRUE, /* wants_statusbar */
3238 FALSE, game_timing_state,
3239 0, /* flags */
3240};
diff --git a/apps/plugins/puzzles/src/net.html b/apps/plugins/puzzles/src/net.html
new file mode 100644
index 0000000000..30fe6a1bb5
--- /dev/null
+++ b/apps/plugins/puzzles/src/net.html
@@ -0,0 +1,108 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Net</title>
7<link rel="previous" href="common.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="cube.html">
12</head>
13<body>
14<p><a href="common.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="cube.html">Next</a></p>
15<h1><a name="C3"></a>Chapter 3: <a name="i0"></a>Net</h1>
16<p>
17(<em>Note:</em> the <a name="i1"></a>Windows version of this game is called <a name="i2"></a><code>NETGAME.EXE</code> to avoid clashing with Windows's own <code>NET.EXE</code>.)
18</p>
19<p>
20I originally saw this in the form of a Flash game called <a name="i3"></a>FreeNet <a href="#p0">[1]</a>, written by Pavils Jurjans; there are several other implementations under the name <a name="i4"></a>NetWalk. The computer prepares a network by connecting up the centres of squares in a grid, and then shuffles the network by rotating every tile randomly. Your job is to rotate it all back into place. The successful solution will be an entirely connected network, with no closed loops. As a visual aid, all tiles which are connected to the one in the middle are highlighted.
21</p>
22<p><a name="p0"></a>
23[1] <a href="http://www.jurjans.lv/stuff/net/FreeNet.htm"><code>http://www.jurjans.lv/stuff/net/FreeNet.htm</code></a>
24</p>
25<h2><a name="S3.1"></a>3.1 <a name="i5"></a>Net controls</h2>
26<p>
27This game can be played with either the keyboard or the mouse. The controls are:
28</p>
29<dl><dt>
30<em>Select tile</em>: mouse pointer, arrow keys
31</dt>
32<dt>
33<em>Rotate tile anticlockwise</em>: left mouse button, &#8216;A&#8217; key
34</dt>
35<dt>
36<em>Rotate tile clockwise</em>: right mouse button, &#8216;D&#8217; key
37</dt>
38<dt>
39<em>Rotate tile by 180 degrees</em>: &#8216;F&#8217; key
40</dt>
41<dt>
42<em>Lock (or unlock) tile</em>: middle mouse button, shift-click, &#8216;S&#8217; key
43</dt>
44<dd>
45You can lock a tile once you're sure of its orientation. You can also unlock it again, but while it's locked you can't accidentally turn it.
46</dd>
47</dl>
48<p>
49The following controls are not necessary to complete the game, but may be useful:
50</p>
51<dl><dt>
52<em>Shift grid</em>: Shift + arrow keys
53</dt>
54<dd>
55On grids that wrap, you can move the origin of the grid, so that tiles that were on opposite sides of the grid can be seen together.
56</dd>
57<dt>
58<em>Move centre</em>: Ctrl + arrow keys
59</dt>
60<dd>
61You can change which tile is used as the source of highlighting. (It doesn't ultimately matter which tile this is, as every tile will be connected to every other tile in a correct solution, but it may be helpful in the intermediate stages of solving the puzzle.)
62</dd>
63<dt>
64<em>Jumble tiles</em>: &#8216;J&#8217; key
65</dt>
66<dd>
67This key turns all tiles that are not locked to random orientations.
68</dd>
69</dl>
70<p>
71(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
72</p>
73<h2><a name="S3.2"></a>3.2 <a name="i6"></a>Net parameters</h2>
74<p>
75These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
76</p>
77<dl><dt>
78<em>Width</em>, <em>Height</em>
79</dt>
80<dd>
81Size of grid in tiles.
82</dd>
83<dt>
84<em>Walls wrap around</em>
85</dt>
86<dd>
87If checked, flow can pass from the left edge to the right edge, and from top to bottom, and vice versa.
88</dd>
89<dt>
90<em>Barrier probability</em>
91</dt>
92<dd>
93A number between 0.0 and 1.0 controlling whether an immovable barrier is placed between two tiles to prevent flow between them (a higher number gives more barriers). Since barriers are immovable, they act as constraints on the solution (i.e., hints).
94<p>
95The grid generation in Net has been carefully arranged so that the barriers are independent of the rest of the grid. This means that if you note down the random seed used to generate the current puzzle (see <a href="common.html#S2.2">section 2.2</a>), change the <em>Barrier probability</em> parameter, and then re-enter the same random seed, you should see exactly the same starting grid, with the only change being the number of barriers. So if you're stuck on a particular grid and need a hint, you could start up another instance of Net, set up the same parameters but a higher barrier probability, and enter the game seed from the original Net window.
96</p>
97
98</dd>
99<dt>
100<em>Ensure unique solution</em>
101</dt>
102<dd>
103Normally, Net will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. (Also, finding <em>all</em> the possible solutions can be an additional challenge for an advanced player.)
104</dd>
105</dl>
106
107<hr><address></address></body>
108</html>
diff --git a/apps/plugins/puzzles/src/netslide.R b/apps/plugins/puzzles/src/netslide.R
new file mode 100644
index 0000000000..ecfe7c3df8
--- /dev/null
+++ b/apps/plugins/puzzles/src/netslide.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3NETSLIDE_EXTRA = tree234
4
5netslide : [X] GTK COMMON netslide NETSLIDE_EXTRA netslide-icon|no-icon
6
7netslide : [G] WINDOWS COMMON netslide NETSLIDE_EXTRA netslide.res|noicon.res
8
9ALL += netslide[COMBINED] NETSLIDE_EXTRA
10
11!begin am gtk
12GAMES += netslide
13!end
14
15!begin >list.c
16 A(netslide) \
17!end
18
19!begin >gamedesc.txt
20netslide:netslide.exe:Netslide:Toroidal sliding network puzzle:Slide a row at a time to reassemble the network.
21!end
diff --git a/apps/plugins/puzzles/src/netslide.c b/apps/plugins/puzzles/src/netslide.c
new file mode 100644
index 0000000000..c56e1abd6a
--- /dev/null
+++ b/apps/plugins/puzzles/src/netslide.c
@@ -0,0 +1,1893 @@
1/*
2 * netslide.c: cross between Net and Sixteen, courtesy of Richard
3 * Boulton.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <assert.h>
10#include <ctype.h>
11#include <math.h>
12
13#include "puzzles.h"
14#include "tree234.h"
15
16#define MATMUL(xr,yr,m,x,y) do { \
17 float rx, ry, xx = (x), yy = (y), *mat = (m); \
18 rx = mat[0] * xx + mat[2] * yy; \
19 ry = mat[1] * xx + mat[3] * yy; \
20 (xr) = rx; (yr) = ry; \
21} while (0)
22
23/* Direction and other bitfields */
24#define R 0x01
25#define U 0x02
26#define L 0x04
27#define D 0x08
28#define FLASHING 0x10
29#define ACTIVE 0x20
30/* Corner flags go in the barriers array */
31#define RU 0x10
32#define UL 0x20
33#define LD 0x40
34#define DR 0x80
35
36/* Get tile at given coordinate */
37#define T(state, x, y) ( (y) * (state)->width + (x) )
38
39/* Rotations: Anticlockwise, Clockwise, Flip, general rotate */
40#define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) )
41#define C(x) ( (((x) & 0x0E) >> 1) | (((x) & 0x01) << 3) )
42#define F(x) ( (((x) & 0x0C) >> 2) | (((x) & 0x03) << 2) )
43#define ROT(x, n) ( ((n)&3) == 0 ? (x) : \
44 ((n)&3) == 1 ? A(x) : \
45 ((n)&3) == 2 ? F(x) : C(x) )
46
47/* X and Y displacements */
48#define X(x) ( (x) == R ? +1 : (x) == L ? -1 : 0 )
49#define Y(x) ( (x) == D ? +1 : (x) == U ? -1 : 0 )
50
51/* Bit count */
52#define COUNT(x) ( (((x) & 0x08) >> 3) + (((x) & 0x04) >> 2) + \
53 (((x) & 0x02) >> 1) + ((x) & 0x01) )
54
55#define PREFERRED_TILE_SIZE 48
56#define TILE_SIZE (ds->tilesize)
57#define BORDER TILE_SIZE
58#define TILE_BORDER 1
59#define WINDOW_OFFSET 0
60
61#define ANIM_TIME 0.13F
62#define FLASH_FRAME 0.07F
63
64enum {
65 COL_BACKGROUND,
66 COL_FLASHING,
67 COL_BORDER,
68 COL_WIRE,
69 COL_ENDPOINT,
70 COL_POWERED,
71 COL_BARRIER,
72 COL_LOWLIGHT,
73 COL_TEXT,
74 NCOLOURS
75};
76
77struct game_params {
78 int width;
79 int height;
80 int wrapping;
81 float barrier_probability;
82 int movetarget;
83};
84
85struct game_state {
86 int width, height, cx, cy, wrapping, completed;
87 int used_solve;
88 int move_count, movetarget;
89
90 /* position (row or col number, starting at 0) of last move. */
91 int last_move_row, last_move_col;
92
93 /* direction of last move: +1 or -1 */
94 int last_move_dir;
95
96 unsigned char *tiles;
97 unsigned char *barriers;
98};
99
100#define OFFSET(x2,y2,x1,y1,dir,state) \
101 ( (x2) = ((x1) + (state)->width + X((dir))) % (state)->width, \
102 (y2) = ((y1) + (state)->height + Y((dir))) % (state)->height)
103
104#define index(state, a, x, y) ( a[(y) * (state)->width + (x)] )
105#define tile(state, x, y) index(state, (state)->tiles, x, y)
106#define barrier(state, x, y) index(state, (state)->barriers, x, y)
107
108struct xyd {
109 int x, y, direction;
110};
111
112static int xyd_cmp(void *av, void *bv) {
113 struct xyd *a = (struct xyd *)av;
114 struct xyd *b = (struct xyd *)bv;
115 if (a->x < b->x)
116 return -1;
117 if (a->x > b->x)
118 return +1;
119 if (a->y < b->y)
120 return -1;
121 if (a->y > b->y)
122 return +1;
123 if (a->direction < b->direction)
124 return -1;
125 if (a->direction > b->direction)
126 return +1;
127 return 0;
128}
129
130static struct xyd *new_xyd(int x, int y, int direction)
131{
132 struct xyd *xyd = snew(struct xyd);
133 xyd->x = x;
134 xyd->y = y;
135 xyd->direction = direction;
136 return xyd;
137}
138
139static void slide_col(game_state *state, int dir, int col);
140static void slide_col_int(int w, int h, unsigned char *tiles, int dir, int col);
141static void slide_row(game_state *state, int dir, int row);
142static void slide_row_int(int w, int h, unsigned char *tiles, int dir, int row);
143
144/* ----------------------------------------------------------------------
145 * Manage game parameters.
146 */
147static game_params *default_params(void)
148{
149 game_params *ret = snew(game_params);
150
151 ret->width = 3;
152 ret->height = 3;
153 ret->wrapping = FALSE;
154 ret->barrier_probability = 1.0;
155 ret->movetarget = 0;
156
157 return ret;
158}
159
160static const struct { int x, y, wrap, bprob; const char* desc; }
161netslide_presets[] = {
162 {3, 3, FALSE, 1, " easy"},
163 {3, 3, FALSE, 0, " medium"},
164 {3, 3, TRUE, 0, " hard"},
165 {4, 4, FALSE, 1, " easy"},
166 {4, 4, FALSE, 0, " medium"},
167 {4, 4, TRUE, 0, " hard"},
168 {5, 5, FALSE, 1, " easy"},
169 {5, 5, FALSE, 0, " medium"},
170 {5, 5, TRUE, 0, " hard"},
171};
172
173static int game_fetch_preset(int i, char **name, game_params **params)
174{
175 game_params *ret;
176 char str[80];
177
178 if (i < 0 || i >= lenof(netslide_presets))
179 return FALSE;
180
181 ret = snew(game_params);
182 ret->width = netslide_presets[i].x;
183 ret->height = netslide_presets[i].y;
184 ret->wrapping = netslide_presets[i].wrap;
185 ret->barrier_probability = (float)netslide_presets[i].bprob;
186 ret->movetarget = 0;
187
188 sprintf(str, "%dx%d%s", ret->width, ret->height, netslide_presets[i].desc);
189
190 *name = dupstr(str);
191 *params = ret;
192 return TRUE;
193}
194
195static void free_params(game_params *params)
196{
197 sfree(params);
198}
199
200static game_params *dup_params(const game_params *params)
201{
202 game_params *ret = snew(game_params);
203 *ret = *params; /* structure copy */
204 return ret;
205}
206
207static void decode_params(game_params *ret, char const *string)
208{
209 char const *p = string;
210
211 ret->wrapping = FALSE;
212 ret->barrier_probability = 0.0;
213 ret->movetarget = 0;
214
215 ret->width = atoi(p);
216 while (*p && isdigit((unsigned char)*p)) p++;
217 if (*p == 'x') {
218 p++;
219 ret->height = atoi(p);
220 while (*p && isdigit((unsigned char)*p)) p++;
221 if ( (ret->wrapping = (*p == 'w')) != 0 )
222 p++;
223 if (*p == 'b') {
224 ret->barrier_probability = (float)atof(++p);
225 while (*p && (isdigit((unsigned char)*p) || *p == '.')) p++;
226 }
227 if (*p == 'm') {
228 ret->movetarget = atoi(++p);
229 }
230 } else {
231 ret->height = ret->width;
232 }
233}
234
235static char *encode_params(const game_params *params, int full)
236{
237 char ret[400];
238 int len;
239
240 len = sprintf(ret, "%dx%d", params->width, params->height);
241 if (params->wrapping)
242 ret[len++] = 'w';
243 if (full && params->barrier_probability)
244 len += sprintf(ret+len, "b%g", params->barrier_probability);
245 /* Shuffle limit is part of the limited parameters, because we have to
246 * provide the target move count. */
247 if (params->movetarget)
248 len += sprintf(ret+len, "m%d", params->movetarget);
249 assert(len < lenof(ret));
250 ret[len] = '\0';
251
252 return dupstr(ret);
253}
254
255static config_item *game_configure(const game_params *params)
256{
257 config_item *ret;
258 char buf[80];
259
260 ret = snewn(6, config_item);
261
262 ret[0].name = "Width";
263 ret[0].type = C_STRING;
264 sprintf(buf, "%d", params->width);
265 ret[0].sval = dupstr(buf);
266 ret[0].ival = 0;
267
268 ret[1].name = "Height";
269 ret[1].type = C_STRING;
270 sprintf(buf, "%d", params->height);
271 ret[1].sval = dupstr(buf);
272 ret[1].ival = 0;
273
274 ret[2].name = "Walls wrap around";
275 ret[2].type = C_BOOLEAN;
276 ret[2].sval = NULL;
277 ret[2].ival = params->wrapping;
278
279 ret[3].name = "Barrier probability";
280 ret[3].type = C_STRING;
281 sprintf(buf, "%g", params->barrier_probability);
282 ret[3].sval = dupstr(buf);
283 ret[3].ival = 0;
284
285 ret[4].name = "Number of shuffling moves";
286 ret[4].type = C_STRING;
287 sprintf(buf, "%d", params->movetarget);
288 ret[4].sval = dupstr(buf);
289 ret[4].ival = 0;
290
291 ret[5].name = NULL;
292 ret[5].type = C_END;
293 ret[5].sval = NULL;
294 ret[5].ival = 0;
295
296 return ret;
297}
298
299static game_params *custom_params(const config_item *cfg)
300{
301 game_params *ret = snew(game_params);
302
303 ret->width = atoi(cfg[0].sval);
304 ret->height = atoi(cfg[1].sval);
305 ret->wrapping = cfg[2].ival;
306 ret->barrier_probability = (float)atof(cfg[3].sval);
307 ret->movetarget = atoi(cfg[4].sval);
308
309 return ret;
310}
311
312static char *validate_params(const game_params *params, int full)
313{
314 if (params->width <= 1 || params->height <= 1)
315 return "Width and height must both be greater than one";
316 if (params->barrier_probability < 0)
317 return "Barrier probability may not be negative";
318 if (params->barrier_probability > 1)
319 return "Barrier probability may not be greater than 1";
320 return NULL;
321}
322
323/* ----------------------------------------------------------------------
324 * Randomly select a new game description.
325 */
326
327static char *new_game_desc(const game_params *params, random_state *rs,
328 char **aux, int interactive)
329{
330 tree234 *possibilities, *barriertree;
331 int w, h, x, y, cx, cy, nbarriers;
332 unsigned char *tiles, *barriers;
333 char *desc, *p;
334
335 w = params->width;
336 h = params->height;
337
338 tiles = snewn(w * h, unsigned char);
339 memset(tiles, 0, w * h);
340 barriers = snewn(w * h, unsigned char);
341 memset(barriers, 0, w * h);
342
343 cx = w / 2;
344 cy = h / 2;
345
346 /*
347 * Construct the unshuffled grid.
348 *
349 * To do this, we simply start at the centre point, repeatedly
350 * choose a random possibility out of the available ways to
351 * extend a used square into an unused one, and do it. After
352 * extending the third line out of a square, we remove the
353 * fourth from the possibilities list to avoid any full-cross
354 * squares (which would make the game too easy because they
355 * only have one orientation).
356 *
357 * The slightly worrying thing is the avoidance of full-cross
358 * squares. Can this cause our unsophisticated construction
359 * algorithm to paint itself into a corner, by getting into a
360 * situation where there are some unreached squares and the
361 * only way to reach any of them is to extend a T-piece into a
362 * full cross?
363 *
364 * Answer: no it can't, and here's a proof.
365 *
366 * Any contiguous group of such unreachable squares must be
367 * surrounded on _all_ sides by T-pieces pointing away from the
368 * group. (If not, then there is a square which can be extended
369 * into one of the `unreachable' ones, and so it wasn't
370 * unreachable after all.) In particular, this implies that
371 * each contiguous group of unreachable squares must be
372 * rectangular in shape (any deviation from that yields a
373 * non-T-piece next to an `unreachable' square).
374 *
375 * So we have a rectangle of unreachable squares, with T-pieces
376 * forming a solid border around the rectangle. The corners of
377 * that border must be connected (since every tile connects all
378 * the lines arriving in it), and therefore the border must
379 * form a closed loop around the rectangle.
380 *
381 * But this can't have happened in the first place, since we
382 * _know_ we've avoided creating closed loops! Hence, no such
383 * situation can ever arise, and the naive grid construction
384 * algorithm will guaranteeably result in a complete grid
385 * containing no unreached squares, no full crosses _and_ no
386 * closed loops. []
387 */
388 possibilities = newtree234(xyd_cmp);
389
390 if (cx+1 < w)
391 add234(possibilities, new_xyd(cx, cy, R));
392 if (cy-1 >= 0)
393 add234(possibilities, new_xyd(cx, cy, U));
394 if (cx-1 >= 0)
395 add234(possibilities, new_xyd(cx, cy, L));
396 if (cy+1 < h)
397 add234(possibilities, new_xyd(cx, cy, D));
398
399 while (count234(possibilities) > 0) {
400 int i;
401 struct xyd *xyd;
402 int x1, y1, d1, x2, y2, d2, d;
403
404 /*
405 * Extract a randomly chosen possibility from the list.
406 */
407 i = random_upto(rs, count234(possibilities));
408 xyd = delpos234(possibilities, i);
409 x1 = xyd->x;
410 y1 = xyd->y;
411 d1 = xyd->direction;
412 sfree(xyd);
413
414 OFFSET(x2, y2, x1, y1, d1, params);
415 d2 = F(d1);
416#ifdef GENERATION_DIAGNOSTICS
417 printf("picked (%d,%d,%c) <-> (%d,%d,%c)\n",
418 x1, y1, "0RU3L567D9abcdef"[d1], x2, y2, "0RU3L567D9abcdef"[d2]);
419#endif
420
421 /*
422 * Make the connection. (We should be moving to an as yet
423 * unused tile.)
424 */
425 index(params, tiles, x1, y1) |= d1;
426 assert(index(params, tiles, x2, y2) == 0);
427 index(params, tiles, x2, y2) |= d2;
428
429 /*
430 * If we have created a T-piece, remove its last
431 * possibility.
432 */
433 if (COUNT(index(params, tiles, x1, y1)) == 3) {
434 struct xyd xyd1, *xydp;
435
436 xyd1.x = x1;
437 xyd1.y = y1;
438 xyd1.direction = 0x0F ^ index(params, tiles, x1, y1);
439
440 xydp = find234(possibilities, &xyd1, NULL);
441
442 if (xydp) {
443#ifdef GENERATION_DIAGNOSTICS
444 printf("T-piece; removing (%d,%d,%c)\n",
445 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
446#endif
447 del234(possibilities, xydp);
448 sfree(xydp);
449 }
450 }
451
452 /*
453 * Remove all other possibilities that were pointing at the
454 * tile we've just moved into.
455 */
456 for (d = 1; d < 0x10; d <<= 1) {
457 int x3, y3, d3;
458 struct xyd xyd1, *xydp;
459
460 OFFSET(x3, y3, x2, y2, d, params);
461 d3 = F(d);
462
463 xyd1.x = x3;
464 xyd1.y = y3;
465 xyd1.direction = d3;
466
467 xydp = find234(possibilities, &xyd1, NULL);
468
469 if (xydp) {
470#ifdef GENERATION_DIAGNOSTICS
471 printf("Loop avoidance; removing (%d,%d,%c)\n",
472 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
473#endif
474 del234(possibilities, xydp);
475 sfree(xydp);
476 }
477 }
478
479 /*
480 * Add new possibilities to the list for moving _out_ of
481 * the tile we have just moved into.
482 */
483 for (d = 1; d < 0x10; d <<= 1) {
484 int x3, y3;
485
486 if (d == d2)
487 continue; /* we've got this one already */
488
489 if (!params->wrapping) {
490 if (d == U && y2 == 0)
491 continue;
492 if (d == D && y2 == h-1)
493 continue;
494 if (d == L && x2 == 0)
495 continue;
496 if (d == R && x2 == w-1)
497 continue;
498 }
499
500 OFFSET(x3, y3, x2, y2, d, params);
501
502 if (index(params, tiles, x3, y3))
503 continue; /* this would create a loop */
504
505#ifdef GENERATION_DIAGNOSTICS
506 printf("New frontier; adding (%d,%d,%c)\n",
507 x2, y2, "0RU3L567D9abcdef"[d]);
508#endif
509 add234(possibilities, new_xyd(x2, y2, d));
510 }
511 }
512 /* Having done that, we should have no possibilities remaining. */
513 assert(count234(possibilities) == 0);
514 freetree234(possibilities);
515
516 /*
517 * Now compute a list of the possible barrier locations.
518 */
519 barriertree = newtree234(xyd_cmp);
520 for (y = 0; y < h; y++) {
521 for (x = 0; x < w; x++) {
522
523 if (!(index(params, tiles, x, y) & R) &&
524 (params->wrapping || x < w-1))
525 add234(barriertree, new_xyd(x, y, R));
526 if (!(index(params, tiles, x, y) & D) &&
527 (params->wrapping || y < h-1))
528 add234(barriertree, new_xyd(x, y, D));
529 }
530 }
531
532 /*
533 * Save the unshuffled grid in aux.
534 */
535 {
536 char *solution;
537 int i;
538
539 /*
540 * String format is exactly the same as a solve move, so we
541 * can just dupstr this in solve_game().
542 */
543
544 solution = snewn(w * h + 2, char);
545 solution[0] = 'S';
546 for (i = 0; i < w * h; i++)
547 solution[i+1] = "0123456789abcdef"[tiles[i] & 0xF];
548 solution[w*h+1] = '\0';
549
550 *aux = solution;
551 }
552
553 /*
554 * Now shuffle the grid.
555 * FIXME - this simply does a set of random moves to shuffle the pieces,
556 * although we make a token effort to avoid boring cases by avoiding moves
557 * that directly undo the previous one, or that repeat so often as to
558 * turn into fewer moves.
559 *
560 * A better way would be to number all the pieces, generate a placement
561 * for all the numbers as for "sixteen", observing parity constraints if
562 * neccessary, and then place the pieces according to their numbering.
563 * BUT - I'm not sure if this will work, since we disallow movement of
564 * the middle row and column.
565 */
566 {
567 int i;
568 int cols = w - 1;
569 int rows = h - 1;
570 int moves = params->movetarget;
571 int prevdir = -1, prevrowcol = -1, nrepeats = 0;
572 if (!moves) moves = cols * rows * 2;
573 for (i = 0; i < moves; /* incremented conditionally */) {
574 /* Choose a direction: 0,1,2,3 = up, right, down, left. */
575 int dir = random_upto(rs, 4);
576 int rowcol;
577 if (dir % 2 == 0) {
578 int col = random_upto(rs, cols);
579 if (col >= cx) col += 1; /* avoid centre */
580 if (col == prevrowcol) {
581 if (dir == 2-prevdir)
582 continue; /* undoes last move */
583 else if (dir == prevdir && (nrepeats+1)*2 > h)
584 continue; /* makes fewer moves */
585 }
586 slide_col_int(w, h, tiles, 1 - dir, col);
587 rowcol = col;
588 } else {
589 int row = random_upto(rs, rows);
590 if (row >= cy) row += 1; /* avoid centre */
591 if (row == prevrowcol) {
592 if (dir == 4-prevdir)
593 continue; /* undoes last move */
594 else if (dir == prevdir && (nrepeats+1)*2 > w)
595 continue; /* makes fewer moves */
596 }
597 slide_row_int(w, h, tiles, 2 - dir, row);
598 rowcol = row;
599 }
600 if (dir == prevdir && rowcol == prevrowcol)
601 nrepeats++;
602 else
603 nrepeats = 1;
604 prevdir = dir;
605 prevrowcol = rowcol;
606 i++; /* if we got here, the move was accepted */
607 }
608 }
609
610 /*
611 * And now choose barrier locations. (We carefully do this
612 * _after_ shuffling, so that changing the barrier rate in the
613 * params while keeping the random seed the same will give the
614 * same shuffled grid and _only_ change the barrier locations.
615 * Also the way we choose barrier locations, by repeatedly
616 * choosing one possibility from the list until we have enough,
617 * is designed to ensure that raising the barrier rate while
618 * keeping the seed the same will provide a superset of the
619 * previous barrier set - i.e. if you ask for 10 barriers, and
620 * then decide that's still too hard and ask for 20, you'll get
621 * the original 10 plus 10 more, rather than getting 20 new
622 * ones and the chance of remembering your first 10.)
623 */
624 nbarriers = (int)(params->barrier_probability * count234(barriertree));
625 assert(nbarriers >= 0 && nbarriers <= count234(barriertree));
626
627 while (nbarriers > 0) {
628 int i;
629 struct xyd *xyd;
630 int x1, y1, d1, x2, y2, d2;
631
632 /*
633 * Extract a randomly chosen barrier from the list.
634 */
635 i = random_upto(rs, count234(barriertree));
636 xyd = delpos234(barriertree, i);
637
638 assert(xyd != NULL);
639
640 x1 = xyd->x;
641 y1 = xyd->y;
642 d1 = xyd->direction;
643 sfree(xyd);
644
645 OFFSET(x2, y2, x1, y1, d1, params);
646 d2 = F(d1);
647
648 index(params, barriers, x1, y1) |= d1;
649 index(params, barriers, x2, y2) |= d2;
650
651 nbarriers--;
652 }
653
654 /*
655 * Clean up the rest of the barrier list.
656 */
657 {
658 struct xyd *xyd;
659
660 while ( (xyd = delpos234(barriertree, 0)) != NULL)
661 sfree(xyd);
662
663 freetree234(barriertree);
664 }
665
666 /*
667 * Finally, encode the grid into a string game description.
668 *
669 * My syntax is extremely simple: each square is encoded as a
670 * hex digit in which bit 0 means a connection on the right,
671 * bit 1 means up, bit 2 left and bit 3 down. (i.e. the same
672 * encoding as used internally). Each digit is followed by
673 * optional barrier indicators: `v' means a vertical barrier to
674 * the right of it, and `h' means a horizontal barrier below
675 * it.
676 */
677 desc = snewn(w * h * 3 + 1, char);
678 p = desc;
679 for (y = 0; y < h; y++) {
680 for (x = 0; x < w; x++) {
681 *p++ = "0123456789abcdef"[index(params, tiles, x, y)];
682 if ((params->wrapping || x < w-1) &&
683 (index(params, barriers, x, y) & R))
684 *p++ = 'v';
685 if ((params->wrapping || y < h-1) &&
686 (index(params, barriers, x, y) & D))
687 *p++ = 'h';
688 }
689 }
690 assert(p - desc <= w*h*3);
691 *p = '\0';
692
693 sfree(tiles);
694 sfree(barriers);
695
696 return desc;
697}
698
699static char *validate_desc(const game_params *params, const char *desc)
700{
701 int w = params->width, h = params->height;
702 int i;
703
704 for (i = 0; i < w*h; i++) {
705 if (*desc >= '0' && *desc <= '9')
706 /* OK */;
707 else if (*desc >= 'a' && *desc <= 'f')
708 /* OK */;
709 else if (*desc >= 'A' && *desc <= 'F')
710 /* OK */;
711 else if (!*desc)
712 return "Game description shorter than expected";
713 else
714 return "Game description contained unexpected character";
715 desc++;
716 while (*desc == 'h' || *desc == 'v')
717 desc++;
718 }
719 if (*desc)
720 return "Game description longer than expected";
721
722 return NULL;
723}
724
725/* ----------------------------------------------------------------------
726 * Construct an initial game state, given a description and parameters.
727 */
728
729static game_state *new_game(midend *me, const game_params *params,
730 const char *desc)
731{
732 game_state *state;
733 int w, h, x, y;
734
735 assert(params->width > 0 && params->height > 0);
736 assert(params->width > 1 || params->height > 1);
737
738 /*
739 * Create a blank game state.
740 */
741 state = snew(game_state);
742 w = state->width = params->width;
743 h = state->height = params->height;
744 state->cx = state->width / 2;
745 state->cy = state->height / 2;
746 state->wrapping = params->wrapping;
747 state->movetarget = params->movetarget;
748 state->completed = 0;
749 state->used_solve = FALSE;
750 state->move_count = 0;
751 state->last_move_row = -1;
752 state->last_move_col = -1;
753 state->last_move_dir = 0;
754 state->tiles = snewn(state->width * state->height, unsigned char);
755 memset(state->tiles, 0, state->width * state->height);
756 state->barriers = snewn(state->width * state->height, unsigned char);
757 memset(state->barriers, 0, state->width * state->height);
758
759
760 /*
761 * Parse the game description into the grid.
762 */
763 for (y = 0; y < h; y++) {
764 for (x = 0; x < w; x++) {
765 if (*desc >= '0' && *desc <= '9')
766 tile(state, x, y) = *desc - '0';
767 else if (*desc >= 'a' && *desc <= 'f')
768 tile(state, x, y) = *desc - 'a' + 10;
769 else if (*desc >= 'A' && *desc <= 'F')
770 tile(state, x, y) = *desc - 'A' + 10;
771 if (*desc)
772 desc++;
773 while (*desc == 'h' || *desc == 'v') {
774 int x2, y2, d1, d2;
775 if (*desc == 'v')
776 d1 = R;
777 else
778 d1 = D;
779
780 OFFSET(x2, y2, x, y, d1, state);
781 d2 = F(d1);
782
783 barrier(state, x, y) |= d1;
784 barrier(state, x2, y2) |= d2;
785
786 desc++;
787 }
788 }
789 }
790
791 /*
792 * Set up border barriers if this is a non-wrapping game.
793 */
794 if (!state->wrapping) {
795 for (x = 0; x < state->width; x++) {
796 barrier(state, x, 0) |= U;
797 barrier(state, x, state->height-1) |= D;
798 }
799 for (y = 0; y < state->height; y++) {
800 barrier(state, 0, y) |= L;
801 barrier(state, state->width-1, y) |= R;
802 }
803 }
804
805 /*
806 * Set up the barrier corner flags, for drawing barriers
807 * prettily when they meet.
808 */
809 for (y = 0; y < state->height; y++) {
810 for (x = 0; x < state->width; x++) {
811 int dir;
812
813 for (dir = 1; dir < 0x10; dir <<= 1) {
814 int dir2 = A(dir);
815 int x1, y1, x2, y2, x3, y3;
816 int corner = FALSE;
817
818 if (!(barrier(state, x, y) & dir))
819 continue;
820
821 if (barrier(state, x, y) & dir2)
822 corner = TRUE;
823
824 x1 = x + X(dir), y1 = y + Y(dir);
825 if (x1 >= 0 && x1 < state->width &&
826 y1 >= 0 && y1 < state->height &&
827 (barrier(state, x1, y1) & dir2))
828 corner = TRUE;
829
830 x2 = x + X(dir2), y2 = y + Y(dir2);
831 if (x2 >= 0 && x2 < state->width &&
832 y2 >= 0 && y2 < state->height &&
833 (barrier(state, x2, y2) & dir))
834 corner = TRUE;
835
836 if (corner) {
837 barrier(state, x, y) |= (dir << 4);
838 if (x1 >= 0 && x1 < state->width &&
839 y1 >= 0 && y1 < state->height)
840 barrier(state, x1, y1) |= (A(dir) << 4);
841 if (x2 >= 0 && x2 < state->width &&
842 y2 >= 0 && y2 < state->height)
843 barrier(state, x2, y2) |= (C(dir) << 4);
844 x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2);
845 if (x3 >= 0 && x3 < state->width &&
846 y3 >= 0 && y3 < state->height)
847 barrier(state, x3, y3) |= (F(dir) << 4);
848 }
849 }
850 }
851 }
852
853 return state;
854}
855
856static game_state *dup_game(const game_state *state)
857{
858 game_state *ret;
859
860 ret = snew(game_state);
861 ret->width = state->width;
862 ret->height = state->height;
863 ret->cx = state->cx;
864 ret->cy = state->cy;
865 ret->wrapping = state->wrapping;
866 ret->movetarget = state->movetarget;
867 ret->completed = state->completed;
868 ret->used_solve = state->used_solve;
869 ret->move_count = state->move_count;
870 ret->last_move_row = state->last_move_row;
871 ret->last_move_col = state->last_move_col;
872 ret->last_move_dir = state->last_move_dir;
873 ret->tiles = snewn(state->width * state->height, unsigned char);
874 memcpy(ret->tiles, state->tiles, state->width * state->height);
875 ret->barriers = snewn(state->width * state->height, unsigned char);
876 memcpy(ret->barriers, state->barriers, state->width * state->height);
877
878 return ret;
879}
880
881static void free_game(game_state *state)
882{
883 sfree(state->tiles);
884 sfree(state->barriers);
885 sfree(state);
886}
887
888static char *solve_game(const game_state *state, const game_state *currstate,
889 const char *aux, char **error)
890{
891 if (!aux) {
892 *error = "Solution not known for this puzzle";
893 return NULL;
894 }
895
896 return dupstr(aux);
897}
898
899static int game_can_format_as_text_now(const game_params *params)
900{
901 return TRUE;
902}
903
904static char *game_text_format(const game_state *state)
905{
906 return NULL;
907}
908
909/* ----------------------------------------------------------------------
910 * Utility routine.
911 */
912
913/*
914 * Compute which squares are reachable from the centre square, as a
915 * quick visual aid to determining how close the game is to
916 * completion. This is also a simple way to tell if the game _is_
917 * completed - just call this function and see whether every square
918 * is marked active.
919 *
920 * squares in the moving_row and moving_col are always inactive - this
921 * is so that "current" doesn't appear to jump across moving lines.
922 */
923static unsigned char *compute_active(const game_state *state,
924 int moving_row, int moving_col)
925{
926 unsigned char *active;
927 tree234 *todo;
928 struct xyd *xyd;
929
930 active = snewn(state->width * state->height, unsigned char);
931 memset(active, 0, state->width * state->height);
932
933 /*
934 * We only store (x,y) pairs in todo, but it's easier to reuse
935 * xyd_cmp and just store direction 0 every time.
936 */
937 todo = newtree234(xyd_cmp);
938 index(state, active, state->cx, state->cy) = ACTIVE;
939 add234(todo, new_xyd(state->cx, state->cy, 0));
940
941 while ( (xyd = delpos234(todo, 0)) != NULL) {
942 int x1, y1, d1, x2, y2, d2;
943
944 x1 = xyd->x;
945 y1 = xyd->y;
946 sfree(xyd);
947
948 for (d1 = 1; d1 < 0x10; d1 <<= 1) {
949 OFFSET(x2, y2, x1, y1, d1, state);
950 d2 = F(d1);
951
952 /*
953 * If the next tile in this direction is connected to
954 * us, and there isn't a barrier in the way, and it
955 * isn't already marked active, then mark it active and
956 * add it to the to-examine list.
957 */
958 if ((x2 != moving_col && y2 != moving_row) &&
959 (tile(state, x1, y1) & d1) &&
960 (tile(state, x2, y2) & d2) &&
961 !(barrier(state, x1, y1) & d1) &&
962 !index(state, active, x2, y2)) {
963 index(state, active, x2, y2) = ACTIVE;
964 add234(todo, new_xyd(x2, y2, 0));
965 }
966 }
967 }
968 /* Now we expect the todo list to have shrunk to zero size. */
969 assert(count234(todo) == 0);
970 freetree234(todo);
971
972 return active;
973}
974
975struct game_ui {
976 int cur_x, cur_y;
977 int cur_visible;
978};
979
980static game_ui *new_ui(const game_state *state)
981{
982 game_ui *ui = snew(game_ui);
983 ui->cur_x = 0;
984 ui->cur_y = -1;
985 ui->cur_visible = FALSE;
986
987 return ui;
988}
989
990static void free_ui(game_ui *ui)
991{
992 sfree(ui);
993}
994
995static char *encode_ui(const game_ui *ui)
996{
997 return NULL;
998}
999
1000static void decode_ui(game_ui *ui, const char *encoding)
1001{
1002}
1003
1004/* ----------------------------------------------------------------------
1005 * Process a move.
1006 */
1007
1008static void slide_row_int(int w, int h, unsigned char *tiles, int dir, int row)
1009{
1010 int x = dir > 0 ? -1 : w;
1011 int tx = x + dir;
1012 int n = w - 1;
1013 unsigned char endtile = tiles[row * w + tx];
1014 do {
1015 x = tx;
1016 tx = (x + dir + w) % w;
1017 tiles[row * w + x] = tiles[row * w + tx];
1018 } while (--n > 0);
1019 tiles[row * w + tx] = endtile;
1020}
1021
1022static void slide_col_int(int w, int h, unsigned char *tiles, int dir, int col)
1023{
1024 int y = dir > 0 ? -1 : h;
1025 int ty = y + dir;
1026 int n = h - 1;
1027 unsigned char endtile = tiles[ty * w + col];
1028 do {
1029 y = ty;
1030 ty = (y + dir + h) % h;
1031 tiles[y * w + col] = tiles[ty * w + col];
1032 } while (--n > 0);
1033 tiles[ty * w + col] = endtile;
1034}
1035
1036static void slide_row(game_state *state, int dir, int row)
1037{
1038 slide_row_int(state->width, state->height, state->tiles, dir, row);
1039}
1040
1041static void slide_col(game_state *state, int dir, int col)
1042{
1043 slide_col_int(state->width, state->height, state->tiles, dir, col);
1044}
1045
1046static void game_changed_state(game_ui *ui, const game_state *oldstate,
1047 const game_state *newstate)
1048{
1049}
1050
1051struct game_drawstate {
1052 int started;
1053 int width, height;
1054 int tilesize;
1055 unsigned char *visible;
1056 int cur_x, cur_y;
1057};
1058
1059static char *interpret_move(const game_state *state, game_ui *ui,
1060 const game_drawstate *ds,
1061 int x, int y, int button)
1062{
1063 int cx, cy;
1064 int dx, dy;
1065 char buf[80];
1066
1067 button &= ~MOD_MASK;
1068
1069 if (IS_CURSOR_MOVE(button)) {
1070 int cpos, diff = 0;
1071 cpos = c2pos(state->width, state->height, ui->cur_x, ui->cur_y);
1072 diff = c2diff(state->width, state->height, ui->cur_x, ui->cur_y, button);
1073
1074 if (diff != 0) {
1075 do { /* we might have to do this more than once to skip missing arrows */
1076 cpos += diff;
1077 pos2c(state->width, state->height, cpos, &ui->cur_x, &ui->cur_y);
1078 } while (ui->cur_x == state->cx || ui->cur_y == state->cy);
1079 }
1080
1081 ui->cur_visible = 1;
1082 return "";
1083 }
1084
1085 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1086 cx = (x - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
1087 cy = (y - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
1088 ui->cur_visible = 0;
1089 } else if (IS_CURSOR_SELECT(button)) {
1090 if (ui->cur_visible) {
1091 cx = ui->cur_x;
1092 cy = ui->cur_y;
1093 } else {
1094 /* 'click' when cursor is invisible just makes cursor visible. */
1095 ui->cur_visible = 1;
1096 return "";
1097 }
1098 } else
1099 return NULL;
1100
1101 if (cy >= 0 && cy < state->height && cy != state->cy)
1102 {
1103 if (cx == -1) dx = +1;
1104 else if (cx == state->width) dx = -1;
1105 else return NULL;
1106 dy = 0;
1107 }
1108 else if (cx >= 0 && cx < state->width && cx != state->cx)
1109 {
1110 if (cy == -1) dy = +1;
1111 else if (cy == state->height) dy = -1;
1112 else return NULL;
1113 dx = 0;
1114 }
1115 else
1116 return NULL;
1117
1118 /* reverse direction if right hand button is pressed */
1119 if (button == RIGHT_BUTTON)
1120 {
1121 dx = -dx;
1122 dy = -dy;
1123 }
1124
1125 if (dx == 0)
1126 sprintf(buf, "C%d,%d", cx, dy);
1127 else
1128 sprintf(buf, "R%d,%d", cy, dx);
1129 return dupstr(buf);
1130}
1131
1132static game_state *execute_move(const game_state *from, const char *move)
1133{
1134 game_state *ret;
1135 int c, d, col;
1136
1137 if ((move[0] == 'C' || move[0] == 'R') &&
1138 sscanf(move+1, "%d,%d", &c, &d) == 2 &&
1139 c >= 0 && c < (move[0] == 'C' ? from->width : from->height)) {
1140 col = (move[0] == 'C');
1141 } else if (move[0] == 'S' &&
1142 strlen(move) == from->width * from->height + 1) {
1143 int i;
1144 ret = dup_game(from);
1145 ret->used_solve = TRUE;
1146 ret->completed = ret->move_count = 1;
1147
1148 for (i = 0; i < from->width * from->height; i++) {
1149 c = move[i+1];
1150 if (c >= '0' && c <= '9')
1151 c -= '0';
1152 else if (c >= 'A' && c <= 'F')
1153 c -= 'A' - 10;
1154 else if (c >= 'a' && c <= 'f')
1155 c -= 'a' - 10;
1156 else {
1157 free_game(ret);
1158 return NULL;
1159 }
1160 ret->tiles[i] = c;
1161 }
1162 return ret;
1163 } else
1164 return NULL; /* can't parse move string */
1165
1166 ret = dup_game(from);
1167
1168 if (col)
1169 slide_col(ret, d, c);
1170 else
1171 slide_row(ret, d, c);
1172
1173 ret->move_count++;
1174 ret->last_move_row = col ? -1 : c;
1175 ret->last_move_col = col ? c : -1;
1176 ret->last_move_dir = d;
1177
1178 /*
1179 * See if the game has been completed.
1180 */
1181 if (!ret->completed) {
1182 unsigned char *active = compute_active(ret, -1, -1);
1183 int x1, y1;
1184 int complete = TRUE;
1185
1186 for (x1 = 0; x1 < ret->width; x1++)
1187 for (y1 = 0; y1 < ret->height; y1++)
1188 if (!index(ret, active, x1, y1)) {
1189 complete = FALSE;
1190 goto break_label; /* break out of two loops at once */
1191 }
1192 break_label:
1193
1194 sfree(active);
1195
1196 if (complete)
1197 ret->completed = ret->move_count;
1198 }
1199
1200 return ret;
1201}
1202
1203/* ----------------------------------------------------------------------
1204 * Routines for drawing the game position on the screen.
1205 */
1206
1207static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1208{
1209 game_drawstate *ds = snew(game_drawstate);
1210
1211 ds->started = FALSE;
1212 ds->width = state->width;
1213 ds->height = state->height;
1214 ds->visible = snewn(state->width * state->height, unsigned char);
1215 ds->tilesize = 0; /* not decided yet */
1216 memset(ds->visible, 0xFF, state->width * state->height);
1217 ds->cur_x = ds->cur_y = -1;
1218
1219 return ds;
1220}
1221
1222static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1223{
1224 sfree(ds->visible);
1225 sfree(ds);
1226}
1227
1228static void game_compute_size(const game_params *params, int tilesize,
1229 int *x, int *y)
1230{
1231 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1232 struct { int tilesize; } ads, *ds = &ads;
1233 ads.tilesize = tilesize;
1234
1235 *x = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER;
1236 *y = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER;
1237}
1238
1239static void game_set_size(drawing *dr, game_drawstate *ds,
1240 const game_params *params, int tilesize)
1241{
1242 ds->tilesize = tilesize;
1243}
1244
1245static float *game_colours(frontend *fe, int *ncolours)
1246{
1247 float *ret;
1248
1249 ret = snewn(NCOLOURS * 3, float);
1250 *ncolours = NCOLOURS;
1251
1252 /*
1253 * Basic background colour is whatever the front end thinks is
1254 * a sensible default.
1255 */
1256 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1257
1258 /*
1259 * Wires are black.
1260 */
1261 ret[COL_WIRE * 3 + 0] = 0.0F;
1262 ret[COL_WIRE * 3 + 1] = 0.0F;
1263 ret[COL_WIRE * 3 + 2] = 0.0F;
1264
1265 /*
1266 * Powered wires and powered endpoints are cyan.
1267 */
1268 ret[COL_POWERED * 3 + 0] = 0.0F;
1269 ret[COL_POWERED * 3 + 1] = 1.0F;
1270 ret[COL_POWERED * 3 + 2] = 1.0F;
1271
1272 /*
1273 * Barriers are red.
1274 */
1275 ret[COL_BARRIER * 3 + 0] = 1.0F;
1276 ret[COL_BARRIER * 3 + 1] = 0.0F;
1277 ret[COL_BARRIER * 3 + 2] = 0.0F;
1278
1279 /*
1280 * Unpowered endpoints are blue.
1281 */
1282 ret[COL_ENDPOINT * 3 + 0] = 0.0F;
1283 ret[COL_ENDPOINT * 3 + 1] = 0.0F;
1284 ret[COL_ENDPOINT * 3 + 2] = 1.0F;
1285
1286 /*
1287 * Tile borders are a darker grey than the background.
1288 */
1289 ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1290 ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1291 ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
1292
1293 /*
1294 * Flashing tiles are a grey in between those two.
1295 */
1296 ret[COL_FLASHING * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
1297 ret[COL_FLASHING * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
1298 ret[COL_FLASHING * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
1299
1300 ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
1301 ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.8F;
1302 ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.8F;
1303 ret[COL_TEXT * 3 + 0] = 0.0;
1304 ret[COL_TEXT * 3 + 1] = 0.0;
1305 ret[COL_TEXT * 3 + 2] = 0.0;
1306
1307 return ret;
1308}
1309
1310static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2,
1311 int colour)
1312{
1313 draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE);
1314 draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE);
1315 draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE);
1316 draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE);
1317 draw_line(dr, x1, y1, x2, y2, colour);
1318}
1319
1320static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2,
1321 int colour)
1322{
1323 int mx = (x1 < x2 ? x1 : x2);
1324 int my = (y1 < y2 ? y1 : y2);
1325 int dx = (x2 + x1 - 2*mx + 1);
1326 int dy = (y2 + y1 - 2*my + 1);
1327
1328 draw_rect(dr, mx, my, dx, dy, colour);
1329}
1330
1331static void draw_barrier_corner(drawing *dr, game_drawstate *ds,
1332 int x, int y, int dir, int phase)
1333{
1334 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1335 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1336 int x1, y1, dx, dy, dir2;
1337
1338 dir >>= 4;
1339
1340 dir2 = A(dir);
1341 dx = X(dir) + X(dir2);
1342 dy = Y(dir) + Y(dir2);
1343 x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
1344 y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
1345
1346 if (phase == 0) {
1347 draw_rect_coords(dr, bx+x1, by+y1,
1348 bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy,
1349 COL_WIRE);
1350 draw_rect_coords(dr, bx+x1, by+y1,
1351 bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
1352 COL_WIRE);
1353 } else {
1354 draw_rect_coords(dr, bx+x1, by+y1,
1355 bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy,
1356 COL_BARRIER);
1357 }
1358}
1359
1360static void draw_barrier(drawing *dr, game_drawstate *ds,
1361 int x, int y, int dir, int phase)
1362{
1363 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1364 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1365 int x1, y1, w, h;
1366
1367 x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0);
1368 y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0);
1369 w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
1370 h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
1371
1372 if (phase == 0) {
1373 draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
1374 } else {
1375 draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER);
1376 }
1377}
1378
1379static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
1380 int x, int y, int tile, float xshift, float yshift)
1381{
1382 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x + (int)(xshift * TILE_SIZE);
1383 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y + (int)(yshift * TILE_SIZE);
1384 float cx, cy, ex, ey;
1385 int dir, col;
1386
1387 /*
1388 * When we draw a single tile, we must draw everything up to
1389 * and including the borders around the tile. This means that
1390 * if the neighbouring tiles have connections to those borders,
1391 * we must draw those connections on the borders themselves.
1392 *
1393 * This would be terribly fiddly if we ever had to draw a tile
1394 * while its neighbour was in mid-rotate, because we'd have to
1395 * arrange to _know_ that the neighbour was being rotated and
1396 * hence had an anomalous effect on the redraw of this tile.
1397 * Fortunately, the drawing algorithm avoids ever calling us in
1398 * this circumstance: we're either drawing lots of straight
1399 * tiles at game start or after a move is complete, or we're
1400 * repeatedly drawing only the rotating tile. So no problem.
1401 */
1402
1403 /*
1404 * So. First blank the tile out completely: draw a big
1405 * rectangle in border colour, and a smaller rectangle in
1406 * background colour to fill it in.
1407 */
1408 draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER,
1409 COL_BORDER);
1410 draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER,
1411 TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER,
1412 tile & FLASHING ? COL_FLASHING : COL_BACKGROUND);
1413
1414 /*
1415 * Draw the wires.
1416 */
1417 cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F;
1418 col = (tile & ACTIVE ? COL_POWERED : COL_WIRE);
1419 for (dir = 1; dir < 0x10; dir <<= 1) {
1420 if (tile & dir) {
1421 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
1422 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
1423 draw_filled_line(dr, bx+(int)cx, by+(int)cy,
1424 bx+(int)(cx+ex), by+(int)(cy+ey),
1425 COL_WIRE);
1426 }
1427 }
1428 for (dir = 1; dir < 0x10; dir <<= 1) {
1429 if (tile & dir) {
1430 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
1431 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
1432 draw_line(dr, bx+(int)cx, by+(int)cy,
1433 bx+(int)(cx+ex), by+(int)(cy+ey), col);
1434 }
1435 }
1436
1437 /*
1438 * Draw the box in the middle. We do this in blue if the tile
1439 * is an unpowered endpoint, in cyan if the tile is a powered
1440 * endpoint, in black if the tile is the centrepiece, and
1441 * otherwise not at all.
1442 */
1443 col = -1;
1444 if (x == state->cx && y == state->cy)
1445 col = COL_WIRE;
1446 else if (COUNT(tile) == 1) {
1447 col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT);
1448 }
1449 if (col >= 0) {
1450 int i, points[8];
1451
1452 points[0] = +1; points[1] = +1;
1453 points[2] = +1; points[3] = -1;
1454 points[4] = -1; points[5] = -1;
1455 points[6] = -1; points[7] = +1;
1456
1457 for (i = 0; i < 8; i += 2) {
1458 ex = (TILE_SIZE * 0.24F) * points[i];
1459 ey = (TILE_SIZE * 0.24F) * points[i+1];
1460 points[i] = bx+(int)(cx+ex);
1461 points[i+1] = by+(int)(cy+ey);
1462 }
1463
1464 draw_polygon(dr, points, 4, col, COL_WIRE);
1465 }
1466
1467 /*
1468 * Draw the points on the border if other tiles are connected
1469 * to us.
1470 */
1471 for (dir = 1; dir < 0x10; dir <<= 1) {
1472 int dx, dy, px, py, lx, ly, vx, vy, ox, oy;
1473
1474 dx = X(dir);
1475 dy = Y(dir);
1476
1477 ox = x + dx;
1478 oy = y + dy;
1479
1480 if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height)
1481 continue;
1482
1483 if (!(tile(state, ox, oy) & F(dir)))
1484 continue;
1485
1486 px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx);
1487 py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy);
1488 lx = dx * (TILE_BORDER-1);
1489 ly = dy * (TILE_BORDER-1);
1490 vx = (dy ? 1 : 0);
1491 vy = (dx ? 1 : 0);
1492
1493 if (xshift == 0.0 && yshift == 0.0 && (tile & dir)) {
1494 /*
1495 * If we are fully connected to the other tile, we must
1496 * draw right across the tile border. (We can use our
1497 * own ACTIVE state to determine what colour to do this
1498 * in: if we are fully connected to the other tile then
1499 * the two ACTIVE states will be the same.)
1500 */
1501 draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
1502 draw_rect_coords(dr, px, py, px+lx, py+ly,
1503 (tile & ACTIVE) ? COL_POWERED : COL_WIRE);
1504 } else {
1505 /*
1506 * The other tile extends into our border, but isn't
1507 * actually connected to us. Just draw a single black
1508 * dot.
1509 */
1510 draw_rect_coords(dr, px, py, px, py, COL_WIRE);
1511 }
1512 }
1513
1514 draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
1515}
1516
1517static void draw_tile_barriers(drawing *dr, game_drawstate *ds,
1518 const game_state *state, int x, int y)
1519{
1520 int phase;
1521 int dir;
1522 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1523 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1524 /*
1525 * Draw barrier corners, and then barriers.
1526 */
1527 for (phase = 0; phase < 2; phase++) {
1528 for (dir = 1; dir < 0x10; dir <<= 1)
1529 if (barrier(state, x, y) & (dir << 4))
1530 draw_barrier_corner(dr, ds, x, y, dir << 4, phase);
1531 for (dir = 1; dir < 0x10; dir <<= 1)
1532 if (barrier(state, x, y) & dir)
1533 draw_barrier(dr, ds, x, y, dir, phase);
1534 }
1535
1536 draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
1537}
1538
1539static void draw_arrow(drawing *dr, game_drawstate *ds,
1540 int x, int y, int xdx, int xdy, int cur)
1541{
1542 int coords[14];
1543 int ydy = -xdx, ydx = xdy;
1544
1545 x = x * TILE_SIZE + BORDER + WINDOW_OFFSET;
1546 y = y * TILE_SIZE + BORDER + WINDOW_OFFSET;
1547
1548#define POINT(n, xx, yy) ( \
1549 coords[2*(n)+0] = x + (xx)*xdx + (yy)*ydx, \
1550 coords[2*(n)+1] = y + (xx)*xdy + (yy)*ydy)
1551
1552 POINT(0, TILE_SIZE / 2, 3 * TILE_SIZE / 4); /* top of arrow */
1553 POINT(1, 3 * TILE_SIZE / 4, TILE_SIZE / 2); /* right corner */
1554 POINT(2, 5 * TILE_SIZE / 8, TILE_SIZE / 2); /* right concave */
1555 POINT(3, 5 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom right */
1556 POINT(4, 3 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom left */
1557 POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */
1558 POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */
1559
1560 draw_polygon(dr, coords, 7, cur ? COL_POWERED : COL_LOWLIGHT, COL_TEXT);
1561}
1562
1563static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds,
1564 int cur_x, int cur_y, int cur)
1565{
1566 if (cur_x == -1 && cur_y == -1)
1567 return; /* 'no cursur here */
1568 else if (cur_x == -1) /* LH column. */
1569 draw_arrow(dr, ds, 0, cur_y+1, 0, -1, cur);
1570 else if (cur_x == ds->width) /* RH column */
1571 draw_arrow(dr, ds, ds->width, cur_y, 0, +1, cur);
1572 else if (cur_y == -1) /* Top row */
1573 draw_arrow(dr, ds, cur_x, 0, +1, 0, cur);
1574 else if (cur_y == ds->height) /* Bottom row */
1575 draw_arrow(dr, ds, cur_x+1, ds->height, -1, 0, cur);
1576 else
1577 assert(!"Invalid cursor position");
1578
1579 draw_update(dr,
1580 cur_x * TILE_SIZE + BORDER + WINDOW_OFFSET,
1581 cur_y * TILE_SIZE + BORDER + WINDOW_OFFSET,
1582 TILE_SIZE, TILE_SIZE);
1583}
1584
1585static void game_redraw(drawing *dr, game_drawstate *ds,
1586 const game_state *oldstate, const game_state *state,
1587 int dir, const game_ui *ui,
1588 float t, float ft)
1589{
1590 int x, y, frame;
1591 unsigned char *active;
1592 float xshift = 0.0;
1593 float yshift = 0.0;
1594 int cur_x = -1, cur_y = -1;
1595
1596 /*
1597 * Clear the screen and draw the exterior barrier lines if this
1598 * is our first call.
1599 */
1600 if (!ds->started) {
1601 int phase;
1602
1603 ds->started = TRUE;
1604
1605 draw_rect(dr, 0, 0,
1606 BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
1607 BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
1608 COL_BACKGROUND);
1609 draw_update(dr, 0, 0,
1610 BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
1611 BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
1612
1613 for (phase = 0; phase < 2; phase++) {
1614
1615 for (x = 0; x < ds->width; x++) {
1616 if (barrier(state, x, 0) & UL)
1617 draw_barrier_corner(dr, ds, x, -1, LD, phase);
1618 if (barrier(state, x, 0) & RU)
1619 draw_barrier_corner(dr, ds, x, -1, DR, phase);
1620 if (barrier(state, x, 0) & U)
1621 draw_barrier(dr, ds, x, -1, D, phase);
1622 if (barrier(state, x, ds->height-1) & DR)
1623 draw_barrier_corner(dr, ds, x, ds->height, RU, phase);
1624 if (barrier(state, x, ds->height-1) & LD)
1625 draw_barrier_corner(dr, ds, x, ds->height, UL, phase);
1626 if (barrier(state, x, ds->height-1) & D)
1627 draw_barrier(dr, ds, x, ds->height, U, phase);
1628 }
1629
1630 for (y = 0; y < ds->height; y++) {
1631 if (barrier(state, 0, y) & UL)
1632 draw_barrier_corner(dr, ds, -1, y, RU, phase);
1633 if (barrier(state, 0, y) & LD)
1634 draw_barrier_corner(dr, ds, -1, y, DR, phase);
1635 if (barrier(state, 0, y) & L)
1636 draw_barrier(dr, ds, -1, y, R, phase);
1637 if (barrier(state, ds->width-1, y) & RU)
1638 draw_barrier_corner(dr, ds, ds->width, y, UL, phase);
1639 if (barrier(state, ds->width-1, y) & DR)
1640 draw_barrier_corner(dr, ds, ds->width, y, LD, phase);
1641 if (barrier(state, ds->width-1, y) & R)
1642 draw_barrier(dr, ds, ds->width, y, L, phase);
1643 }
1644 }
1645
1646 /*
1647 * Arrows for making moves.
1648 */
1649 for (x = 0; x < ds->width; x++) {
1650 if (x == state->cx) continue;
1651 draw_arrow(dr, ds, x, 0, +1, 0, 0);
1652 draw_arrow(dr, ds, x+1, ds->height, -1, 0, 0);
1653 }
1654 for (y = 0; y < ds->height; y++) {
1655 if (y == state->cy) continue;
1656 draw_arrow(dr, ds, ds->width, y, 0, +1, 0);
1657 draw_arrow(dr, ds, 0, y+1, 0, -1, 0);
1658 }
1659 }
1660 if (ui->cur_visible) {
1661 cur_x = ui->cur_x; cur_y = ui->cur_y;
1662 }
1663 if (cur_x != ds->cur_x || cur_y != ds->cur_y) {
1664 /* Cursor has changed; redraw two (prev and curr) arrows. */
1665 assert(cur_x != state->cx && cur_y != state->cy);
1666
1667 draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1);
1668 draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0);
1669 ds->cur_x = cur_x; ds->cur_y = cur_y;
1670 }
1671
1672 /* Check if this is an undo. If so, we will need to run any animation
1673 * backwards.
1674 */
1675 if (oldstate && oldstate->move_count > state->move_count) {
1676 const game_state * tmpstate = state;
1677 state = oldstate;
1678 oldstate = tmpstate;
1679 t = ANIM_TIME - t;
1680 }
1681
1682 if (oldstate && (t < ANIM_TIME)) {
1683 /*
1684 * We're animating a slide, of row/column number
1685 * state->last_move_pos, in direction
1686 * state->last_move_dir
1687 */
1688 xshift = state->last_move_row == -1 ? 0.0F :
1689 (1 - t / ANIM_TIME) * state->last_move_dir;
1690 yshift = state->last_move_col == -1 ? 0.0F :
1691 (1 - t / ANIM_TIME) * state->last_move_dir;
1692 }
1693
1694 frame = -1;
1695 if (ft > 0) {
1696 /*
1697 * We're animating a completion flash. Find which frame
1698 * we're at.
1699 */
1700 frame = (int)(ft / FLASH_FRAME);
1701 }
1702
1703 /*
1704 * Draw any tile which differs from the way it was last drawn.
1705 */
1706 if (xshift != 0.0 || yshift != 0.0) {
1707 active = compute_active(state,
1708 state->last_move_row, state->last_move_col);
1709 } else {
1710 active = compute_active(state, -1, -1);
1711 }
1712
1713 clip(dr,
1714 BORDER + WINDOW_OFFSET, BORDER + WINDOW_OFFSET,
1715 TILE_SIZE * state->width + TILE_BORDER,
1716 TILE_SIZE * state->height + TILE_BORDER);
1717
1718 for (x = 0; x < ds->width; x++)
1719 for (y = 0; y < ds->height; y++) {
1720 unsigned char c = tile(state, x, y) | index(state, active, x, y);
1721
1722 /*
1723 * In a completion flash, we adjust the FLASHING bit
1724 * depending on our distance from the centre point and
1725 * the frame number.
1726 */
1727 if (frame >= 0) {
1728 int xdist, ydist, dist;
1729 xdist = (x < state->cx ? state->cx - x : x - state->cx);
1730 ydist = (y < state->cy ? state->cy - y : y - state->cy);
1731 dist = (xdist > ydist ? xdist : ydist);
1732
1733 if (frame >= dist && frame < dist+4) {
1734 int flash = (frame - dist) & 1;
1735 flash = flash ? FLASHING : 0;
1736 c = (c &~ FLASHING) | flash;
1737 }
1738 }
1739
1740 if (index(state, ds->visible, x, y) != c ||
1741 index(state, ds->visible, x, y) == 0xFF ||
1742 (x == state->last_move_col || y == state->last_move_row))
1743 {
1744 float xs = (y == state->last_move_row ? xshift : (float)0.0);
1745 float ys = (x == state->last_move_col ? yshift : (float)0.0);
1746
1747 draw_tile(dr, ds, state, x, y, c, xs, ys);
1748 if (xs < 0 && x == 0)
1749 draw_tile(dr, ds, state, state->width, y, c, xs, ys);
1750 else if (xs > 0 && x == state->width - 1)
1751 draw_tile(dr, ds, state, -1, y, c, xs, ys);
1752 else if (ys < 0 && y == 0)
1753 draw_tile(dr, ds, state, x, state->height, c, xs, ys);
1754 else if (ys > 0 && y == state->height - 1)
1755 draw_tile(dr, ds, state, x, -1, c, xs, ys);
1756
1757 if (x == state->last_move_col || y == state->last_move_row)
1758 index(state, ds->visible, x, y) = 0xFF;
1759 else
1760 index(state, ds->visible, x, y) = c;
1761 }
1762 }
1763
1764 for (x = 0; x < ds->width; x++)
1765 for (y = 0; y < ds->height; y++)
1766 draw_tile_barriers(dr, ds, state, x, y);
1767
1768 unclip(dr);
1769
1770 /*
1771 * Update the status bar.
1772 */
1773 {
1774 char statusbuf[256];
1775 int i, n, a;
1776
1777 n = state->width * state->height;
1778 for (i = a = 0; i < n; i++)
1779 if (active[i])
1780 a++;
1781
1782 if (state->used_solve)
1783 sprintf(statusbuf, "Moves since auto-solve: %d",
1784 state->move_count - state->completed);
1785 else
1786 sprintf(statusbuf, "%sMoves: %d",
1787 (state->completed ? "COMPLETED! " : ""),
1788 (state->completed ? state->completed : state->move_count));
1789
1790 if (state->movetarget)
1791 sprintf(statusbuf + strlen(statusbuf), " (target %d)",
1792 state->movetarget);
1793
1794 sprintf(statusbuf + strlen(statusbuf), " Active: %d/%d", a, n);
1795
1796 status_bar(dr, statusbuf);
1797 }
1798
1799 sfree(active);
1800}
1801
1802static float game_anim_length(const game_state *oldstate,
1803 const game_state *newstate, int dir, game_ui *ui)
1804{
1805 return ANIM_TIME;
1806}
1807
1808static float game_flash_length(const game_state *oldstate,
1809 const game_state *newstate, int dir, game_ui *ui)
1810{
1811 /*
1812 * If the game has just been completed, we display a completion
1813 * flash.
1814 */
1815 if (!oldstate->completed && newstate->completed &&
1816 !oldstate->used_solve && !newstate->used_solve) {
1817 int size;
1818 size = 0;
1819 if (size < newstate->cx+1)
1820 size = newstate->cx+1;
1821 if (size < newstate->cy+1)
1822 size = newstate->cy+1;
1823 if (size < newstate->width - newstate->cx)
1824 size = newstate->width - newstate->cx;
1825 if (size < newstate->height - newstate->cy)
1826 size = newstate->height - newstate->cy;
1827 return FLASH_FRAME * (size+4);
1828 }
1829
1830 return 0.0F;
1831}
1832
1833static int game_status(const game_state *state)
1834{
1835 return state->completed ? +1 : 0;
1836}
1837
1838static int game_timing_state(const game_state *state, game_ui *ui)
1839{
1840 return FALSE;
1841}
1842
1843static void game_print_size(const game_params *params, float *x, float *y)
1844{
1845}
1846
1847static void game_print(drawing *dr, const game_state *state, int tilesize)
1848{
1849}
1850
1851#ifdef COMBINED
1852#define thegame netslide
1853#endif
1854
1855const struct game thegame = {
1856 "Netslide", "games.netslide", "netslide",
1857 default_params,
1858 game_fetch_preset, NULL,
1859 decode_params,
1860 encode_params,
1861 free_params,
1862 dup_params,
1863 TRUE, game_configure, custom_params,
1864 validate_params,
1865 new_game_desc,
1866 validate_desc,
1867 new_game,
1868 dup_game,
1869 free_game,
1870 TRUE, solve_game,
1871 FALSE, game_can_format_as_text_now, game_text_format,
1872 new_ui,
1873 free_ui,
1874 encode_ui,
1875 decode_ui,
1876 game_changed_state,
1877 interpret_move,
1878 execute_move,
1879 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1880 game_colours,
1881 game_new_drawstate,
1882 game_free_drawstate,
1883 game_redraw,
1884 game_anim_length,
1885 game_flash_length,
1886 game_status,
1887 FALSE, FALSE, game_print_size, game_print,
1888 TRUE, /* wants_statusbar */
1889 FALSE, game_timing_state,
1890 0, /* flags */
1891};
1892
1893/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/netslide.html b/apps/plugins/puzzles/src/netslide.html
new file mode 100644
index 0000000000..b63223745f
--- /dev/null
+++ b/apps/plugins/puzzles/src/netslide.html
@@ -0,0 +1,30 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Netslide</title>
7<link rel="previous" href="rect.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="pattern.html">
12</head>
13<body>
14<p><a href="rect.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="pattern.html">Next</a></p>
15<h1><a name="C9"></a>Chapter 9: <a name="i0"></a>Netslide</h1>
16<p>
17This game combines the grid generation of Net (see <a href="net.html#C3">chapter 3</a>) with the movement of Sixteen (see <a href="sixteen.html#C6">chapter 6</a>): you have a Net grid, but instead of rotating tiles back into place you have to slide them into place by moving a whole row at a time.
18</p>
19<p>
20As in Sixteen, <a name="i1"></a>control is with the mouse or cursor keys. See <a href="sixteen.html#S6.1">section 6.1</a>.
21</p>
22<p>
23<a name="i2"></a>The available game parameters have similar meanings to those in Net (see <a href="net.html#S3.2">section 3.2</a>) and Sixteen (see <a href="sixteen.html#S6.2">section 6.2</a>).
24</p>
25<p>
26Netslide was contributed to this collection by Richard Boulton.
27</p>
28
29<hr><address></address></body>
30</html>
diff --git a/apps/plugins/puzzles/src/no-icon.c b/apps/plugins/puzzles/src/no-icon.c
new file mode 100644
index 0000000000..114b2c57c7
--- /dev/null
+++ b/apps/plugins/puzzles/src/no-icon.c
@@ -0,0 +1,8 @@
1
2/*
3 * Dummy source file which replaces the files generated in the
4 * `icons' subdirectory, when they're absent.
5 */
6
7const char *const *const xpm_icons[] = { 0 };
8const int n_xpm_icons = 0;
diff --git a/apps/plugins/puzzles/src/noicon.rc b/apps/plugins/puzzles/src/noicon.rc
new file mode 100644
index 0000000000..1de605d605
--- /dev/null
+++ b/apps/plugins/puzzles/src/noicon.rc
@@ -0,0 +1,11 @@
1/* Puzzle resource file without an icon, used in the absence of icons/foo.rc */
2
3#include "puzzles.rc2"
4
5/* XXX this probably isn't the right test, but it'll do. */
6#ifdef MINGW32_FIX
7/* XXX The MinGW toolchain (specifically, windres) doesn't like a resource
8 * file with no resources. Give it a dummy one.
9 * This can go if/when VERSIONINFO resources are added. */
10200 RCDATA { 0 }
11#endif
diff --git a/apps/plugins/puzzles/src/nullfe.c b/apps/plugins/puzzles/src/nullfe.c
new file mode 100644
index 0000000000..ad381a135b
--- /dev/null
+++ b/apps/plugins/puzzles/src/nullfe.c
@@ -0,0 +1,74 @@
1/*
2 * nullfe.c: Null front-end code containing a bunch of boring stub
3 * functions. Used to ensure successful linking when building the
4 * various stand-alone solver binaries.
5 */
6
7#include <stdarg.h>
8
9#include "puzzles.h"
10
11void frontend_default_colour(frontend *fe, float *output) {}
12void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
13 int align, int colour, char *text) {}
14void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) {}
15void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) {}
16void draw_thick_line(drawing *dr, float thickness,
17 float x1, float y1, float x2, float y2, int colour) {}
18void draw_polygon(drawing *dr, int *coords, int npoints,
19 int fillcolour, int outlinecolour) {}
20void draw_circle(drawing *dr, int cx, int cy, int radius,
21 int fillcolour, int outlinecolour) {}
22char *text_fallback(drawing *dr, const char *const *strings, int nstrings)
23{ return dupstr(strings[0]); }
24void clip(drawing *dr, int x, int y, int w, int h) {}
25void unclip(drawing *dr) {}
26void start_draw(drawing *dr) {}
27void draw_update(drawing *dr, int x, int y, int w, int h) {}
28void end_draw(drawing *dr) {}
29blitter *blitter_new(drawing *dr, int w, int h) {return NULL;}
30void blitter_free(drawing *dr, blitter *bl) {}
31void blitter_save(drawing *dr, blitter *bl, int x, int y) {}
32void blitter_load(drawing *dr, blitter *bl, int x, int y) {}
33int print_mono_colour(drawing *dr, int grey) { return 0; }
34int print_grey_colour(drawing *dr, float grey) { return 0; }
35int print_hatched_colour(drawing *dr, int hatch) { return 0; }
36int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int grey)
37{ return 0; }
38int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey)
39{ return 0; }
40int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch)
41{ return 0; }
42void print_line_width(drawing *dr, int width) {}
43void print_line_dotted(drawing *dr, int dotted) {}
44void midend_supersede_game_desc(midend *me, char *desc, char *privdesc) {}
45void status_bar(drawing *dr, char *text) {}
46struct preset_menu *preset_menu_new(void) {return NULL;}
47struct preset_menu *preset_menu_add_submenu(struct preset_menu *parent,
48 char *title) {return NULL;}
49void preset_menu_add_preset(struct preset_menu *parent,
50 char *title, game_params *params) {}
51
52void fatal(char *fmt, ...)
53{
54 va_list ap;
55
56 fprintf(stderr, "fatal error: ");
57
58 va_start(ap, fmt);
59 vfprintf(stderr, fmt, ap);
60 va_end(ap);
61
62 fprintf(stderr, "\n");
63 exit(1);
64}
65
66#ifdef DEBUGGING
67void debug_printf(char *fmt, ...)
68{
69 va_list ap;
70 va_start(ap, fmt);
71 vfprintf(stdout, fmt, ap);
72 va_end(ap);
73}
74#endif
diff --git a/apps/plugins/puzzles/src/nullgame.R b/apps/plugins/puzzles/src/nullgame.R
new file mode 100644
index 0000000000..41bdb85d57
--- /dev/null
+++ b/apps/plugins/puzzles/src/nullgame.R
@@ -0,0 +1,12 @@
1# -*- makefile -*-
2
3# The `nullgame' source file is a largely blank one, which contains
4# all the correct function definitions to compile and link, but
5# which defines the null game in which nothing is ever drawn and
6# there are no valid moves. Its main purpose is to act as a
7# template for writing new game definition source files. I include
8# it in the Makefile because it will be worse than useless if it
9# ever fails to compile, so it's important that it should actually
10# be built on a regular basis.
11nullgame : [X] GTK COMMON nullgame nullgame-icon|no-icon
12nullgame : [G] WINDOWS COMMON nullgame nullgame.res|noicon.res
diff --git a/apps/plugins/puzzles/src/nullgame.c b/apps/plugins/puzzles/src/nullgame.c
new file mode 100644
index 0000000000..183b1e39c2
--- /dev/null
+++ b/apps/plugins/puzzles/src/nullgame.c
@@ -0,0 +1,303 @@
1/*
2 * nullgame.c [FIXME]: Template defining the null game (in which no
3 * moves are permitted and nothing is ever drawn). This file exists
4 * solely as a basis for constructing new game definitions - it
5 * helps to have something which will compile from the word go and
6 * merely doesn't _do_ very much yet.
7 *
8 * Parts labelled FIXME actually want _removing_ (e.g. the dummy
9 * field in each of the required data structures, and this entire
10 * comment itself) when converting this source file into one
11 * describing a real game.
12 */
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <assert.h>
18#include <ctype.h>
19#include <math.h>
20
21#include "puzzles.h"
22
23enum {
24 COL_BACKGROUND,
25 NCOLOURS
26};
27
28struct game_params {
29 int FIXME;
30};
31
32struct game_state {
33 int FIXME;
34};
35
36static game_params *default_params(void)
37{
38 game_params *ret = snew(game_params);
39
40 ret->FIXME = 0;
41
42 return ret;
43}
44
45static int game_fetch_preset(int i, char **name, game_params **params)
46{
47 return FALSE;
48}
49
50static void free_params(game_params *params)
51{
52 sfree(params);
53}
54
55static game_params *dup_params(const game_params *params)
56{
57 game_params *ret = snew(game_params);
58 *ret = *params; /* structure copy */
59 return ret;
60}
61
62static void decode_params(game_params *params, char const *string)
63{
64}
65
66static char *encode_params(const game_params *params, int full)
67{
68 return dupstr("FIXME");
69}
70
71static config_item *game_configure(const game_params *params)
72{
73 return NULL;
74}
75
76static game_params *custom_params(const config_item *cfg)
77{
78 return NULL;
79}
80
81static char *validate_params(const game_params *params, int full)
82{
83 return NULL;
84}
85
86static char *new_game_desc(const game_params *params, random_state *rs,
87 char **aux, int interactive)
88{
89 return dupstr("FIXME");
90}
91
92static char *validate_desc(const game_params *params, const char *desc)
93{
94 return NULL;
95}
96
97static game_state *new_game(midend *me, const game_params *params,
98 const char *desc)
99{
100 game_state *state = snew(game_state);
101
102 state->FIXME = 0;
103
104 return state;
105}
106
107static game_state *dup_game(const game_state *state)
108{
109 game_state *ret = snew(game_state);
110
111 ret->FIXME = state->FIXME;
112
113 return ret;
114}
115
116static void free_game(game_state *state)
117{
118 sfree(state);
119}
120
121static char *solve_game(const game_state *state, const game_state *currstate,
122 const char *aux, char **error)
123{
124 return NULL;
125}
126
127static int game_can_format_as_text_now(const game_params *params)
128{
129 return TRUE;
130}
131
132static char *game_text_format(const game_state *state)
133{
134 return NULL;
135}
136
137static game_ui *new_ui(const game_state *state)
138{
139 return NULL;
140}
141
142static void free_ui(game_ui *ui)
143{
144}
145
146static char *encode_ui(const game_ui *ui)
147{
148 return NULL;
149}
150
151static void decode_ui(game_ui *ui, const char *encoding)
152{
153}
154
155static void game_changed_state(game_ui *ui, const game_state *oldstate,
156 const game_state *newstate)
157{
158}
159
160struct game_drawstate {
161 int tilesize;
162 int FIXME;
163};
164
165static char *interpret_move(const game_state *state, game_ui *ui,
166 const game_drawstate *ds,
167 int x, int y, int button)
168{
169 return NULL;
170}
171
172static game_state *execute_move(const game_state *state, const char *move)
173{
174 return NULL;
175}
176
177/* ----------------------------------------------------------------------
178 * Drawing routines.
179 */
180
181static void game_compute_size(const game_params *params, int tilesize,
182 int *x, int *y)
183{
184 *x = *y = 10 * tilesize; /* FIXME */
185}
186
187static void game_set_size(drawing *dr, game_drawstate *ds,
188 const game_params *params, int tilesize)
189{
190 ds->tilesize = tilesize;
191}
192
193static float *game_colours(frontend *fe, int *ncolours)
194{
195 float *ret = snewn(3 * NCOLOURS, float);
196
197 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
198
199 *ncolours = NCOLOURS;
200 return ret;
201}
202
203static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
204{
205 struct game_drawstate *ds = snew(struct game_drawstate);
206
207 ds->tilesize = 0;
208 ds->FIXME = 0;
209
210 return ds;
211}
212
213static void game_free_drawstate(drawing *dr, game_drawstate *ds)
214{
215 sfree(ds);
216}
217
218static void game_redraw(drawing *dr, game_drawstate *ds,
219 const game_state *oldstate, const game_state *state,
220 int dir, const game_ui *ui,
221 float animtime, float flashtime)
222{
223 /*
224 * The initial contents of the window are not guaranteed and
225 * can vary with front ends. To be on the safe side, all games
226 * should start by drawing a big background-colour rectangle
227 * covering the whole window.
228 */
229 draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
230 draw_update(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize);
231}
232
233static float game_anim_length(const game_state *oldstate,
234 const game_state *newstate, int dir, game_ui *ui)
235{
236 return 0.0F;
237}
238
239static float game_flash_length(const game_state *oldstate,
240 const game_state *newstate, int dir, game_ui *ui)
241{
242 return 0.0F;
243}
244
245static int game_status(const game_state *state)
246{
247 return 0;
248}
249
250static int game_timing_state(const game_state *state, game_ui *ui)
251{
252 return TRUE;
253}
254
255static void game_print_size(const game_params *params, float *x, float *y)
256{
257}
258
259static void game_print(drawing *dr, const game_state *state, int tilesize)
260{
261}
262
263#ifdef COMBINED
264#define thegame nullgame
265#endif
266
267const struct game thegame = {
268 "Null Game", NULL, NULL,
269 default_params,
270 game_fetch_preset, NULL,
271 decode_params,
272 encode_params,
273 free_params,
274 dup_params,
275 FALSE, game_configure, custom_params,
276 validate_params,
277 new_game_desc,
278 validate_desc,
279 new_game,
280 dup_game,
281 free_game,
282 FALSE, solve_game,
283 FALSE, game_can_format_as_text_now, game_text_format,
284 new_ui,
285 free_ui,
286 encode_ui,
287 decode_ui,
288 game_changed_state,
289 interpret_move,
290 execute_move,
291 20 /* FIXME */, game_compute_size, game_set_size,
292 game_colours,
293 game_new_drawstate,
294 game_free_drawstate,
295 game_redraw,
296 game_anim_length,
297 game_flash_length,
298 game_status,
299 FALSE, FALSE, game_print_size, game_print,
300 FALSE, /* wants_statusbar */
301 FALSE, game_timing_state,
302 0, /* flags */
303};
diff --git a/apps/plugins/puzzles/src/obfusc.c b/apps/plugins/puzzles/src/obfusc.c
new file mode 100644
index 0000000000..dc0656c20e
--- /dev/null
+++ b/apps/plugins/puzzles/src/obfusc.c
@@ -0,0 +1,130 @@
1/*
2 * Stand-alone tool to access the Puzzles obfuscation algorithm.
3 *
4 * To deobfuscate, use "obfusc -d":
5 *
6 * obfusc -d reads binary data from stdin, writes to stdout
7 * obfusc -d <hex string> works on the given hex string instead of stdin
8 * obfusc -d -h writes a hex string instead of binary to stdout
9 *
10 * To obfuscate, "obfusc -e":
11 *
12 * obfusc -e reads binary from stdin, writes hex to stdout
13 * obfusc -e <hex string> works on the given hex string instead of stdin
14 * obfusc -e -b writes binary instead of text to stdout
15 *
16 * The default output format is hex for -e and binary for -d
17 * because that's the way obfuscation is generally used in
18 * Puzzles. Either of -b and -h can always be specified to set it
19 * explicitly.
20 *
21 * Data read from standard input is assumed always to be binary;
22 * data provided on the command line is taken to be hex.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdarg.h>
28#include <string.h>
29#include <errno.h>
30
31#include "puzzles.h"
32
33int main(int argc, char **argv)
34{
35 enum { BINARY, DEFAULT, HEX } outputmode = DEFAULT;
36 char *inhex = NULL;
37 unsigned char *data;
38 int datalen;
39 int decode = -1;
40 int doing_opts = TRUE;
41
42 while (--argc > 0) {
43 char *p = *++argv;
44
45 if (doing_opts && *p == '-') {
46 if (!strcmp(p, "--")) {
47 doing_opts = 0;
48 continue;
49 }
50 p++;
51 while (*p) {
52 switch (*p) {
53 case 'e':
54 decode = 0;
55 break;
56 case 'd':
57 decode = 1;
58 break;
59 case 'b':
60 outputmode = BINARY;
61 break;
62 case 'h':
63 outputmode = HEX;
64 break;
65 default:
66 fprintf(stderr, "obfusc: unrecognised option '-%c'\n",
67 *p);
68 return 1;
69 }
70 p++;
71 }
72 } else {
73 if (!inhex) {
74 inhex = p;
75 } else {
76 fprintf(stderr, "obfusc: expected at most one argument\n");
77 return 1;
78 }
79 }
80 }
81
82 if (decode < 0) {
83 fprintf(stderr, "usage: obfusc < -e | -d > [ -b | -h ] [hex data]\n");
84 return 0;
85 }
86
87 if (outputmode == DEFAULT)
88 outputmode = (decode ? BINARY : HEX);
89
90 if (inhex) {
91 datalen = strlen(inhex) / 2;
92 data = hex2bin(inhex, datalen);
93 } else {
94 int datasize = 4096;
95 datalen = 0;
96 data = snewn(datasize, unsigned char);
97 while (1) {
98 int ret = fread(data + datalen, 1, datasize - datalen, stdin);
99 if (ret < 0) {
100 fprintf(stderr, "obfusc: read: %s\n", strerror(errno));
101 return 1;
102 } else if (ret == 0) {
103 break;
104 } else {
105 datalen += ret;
106 if (datasize - datalen < 4096) {
107 datasize = datalen * 5 / 4 + 4096;
108 data = sresize(data, datasize, unsigned char);
109 }
110 }
111 }
112 }
113
114 obfuscate_bitmap(data, datalen * 8, decode);
115
116 if (outputmode == BINARY) {
117 int ret = fwrite(data, 1, datalen, stdout);
118 if (ret < 0) {
119 fprintf(stderr, "obfusc: write: %s\n", strerror(errno));
120 return 1;
121 }
122 } else {
123 int i;
124 for (i = 0; i < datalen; i++)
125 printf("%02x", data[i]);
126 printf("\n");
127 }
128
129 return 0;
130}
diff --git a/apps/plugins/puzzles/src/osx-help.but b/apps/plugins/puzzles/src/osx-help.but
new file mode 100644
index 0000000000..fa45996aee
--- /dev/null
+++ b/apps/plugins/puzzles/src/osx-help.but
@@ -0,0 +1,14 @@
1\# Additional Halibut fragment to set up the HTML output
2\# appropriately for MacOS online help.
3
4\cfg{html-head-end}{
5<style type="text/css">
6body \{ font-family: "Lucida Grande", Helvetica, Arial; font-size: 9pt \}
7h1 \{ font-size: 12pt \}
8h2 \{ font-size: 10pt \}
9h3 \{ font-size: 9pt \}
10h4 \{ font-size: 9pt \}
11h5 \{ font-size: 9pt \}
12h6 \{ font-size: 9pt \}
13</style>
14}
diff --git a/apps/plugins/puzzles/src/osx-info.plist b/apps/plugins/puzzles/src/osx-info.plist
new file mode 100644
index 0000000000..9f4aef8e53
--- /dev/null
+++ b/apps/plugins/puzzles/src/osx-info.plist
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3<plist version="1.0">
4<dict>
5 <key>CFBundleIconFile</key>
6 <string>Puzzles.icns</string>
7 <key>CFBundleHelpBookFolder</key>
8 <string>Help</string>
9 <key>CFBundleHelpBookName</key>
10 <string>Puzzles Help</string>
11 <key>CFBundleName</key>
12 <string>Puzzles</string>
13 <key>CFBundleDisplayName</key>
14 <string>Puzzles</string>
15 <key>CFBundleExecutable</key>
16 <string>Puzzles</string>
17 <key>CFBundleVersion</key>
18 <string>Unidentified build</string>
19 <key>CFBundleShortVersionString</key>
20 <string>Unidentified build</string>
21 <key>CFBundleDevelopmentRegion</key>
22 <string>en</string>
23 <key>CFBundleIdentifier</key>
24 <string>uk.org.greenend.chiark.sgtatham.puzzles</string>
25 <key>CFBundleInfoDictionaryVersion</key>
26 <string>6.0</string>
27 <key>CFBundlePackageType</key>
28 <string>APPL</string>
29 <key>CFBundleSignature</key>
30 <string>????</string>
31 <key>NSHumanReadableCopyright</key>
32 <string>This software is copyright (c) 2004-2014 Simon Tatham</string>
33</dict>
34</plist>
diff --git a/apps/plugins/puzzles/src/osx.icns b/apps/plugins/puzzles/src/osx.icns
new file mode 100644
index 0000000000..b4346a0da1
--- /dev/null
+++ b/apps/plugins/puzzles/src/osx.icns
Binary files differ
diff --git a/apps/plugins/puzzles/src/osx.m b/apps/plugins/puzzles/src/osx.m
new file mode 100644
index 0000000000..9d74da1574
--- /dev/null
+++ b/apps/plugins/puzzles/src/osx.m
@@ -0,0 +1,1775 @@
1/*
2 * Mac OS X / Cocoa front end to puzzles.
3 *
4 * Still to do:
5 *
6 * - I'd like to be able to call up context help for a specific
7 * game at a time.
8 *
9 * Mac interface issues that possibly could be done better:
10 *
11 * - is there a better approach to frontend_default_colour?
12 *
13 * - do we need any more options in the Window menu?
14 *
15 * - can / should we be doing anything with the titles of the
16 * configuration boxes?
17 *
18 * - not sure what I should be doing about default window
19 * placement. Centring new windows is a bit feeble, but what's
20 * better? Is there a standard way to tell the OS "here's the
21 * _size_ of window I want, now use your best judgment about the
22 * initial position"?
23 * + there's a standard _policy_ on window placement, given in
24 * the HI guidelines. Have to implement it ourselves though,
25 * bah.
26 *
27 * - a brief frob of the Mac numeric keypad suggests that it
28 * generates numbers no matter what you do. I wonder if I should
29 * try to figure out a way of detecting keypad codes so I can
30 * implement UP_LEFT and friends. Alternatively, perhaps I
31 * should simply assign the number keys to UP_LEFT et al?
32 * They're not in use for anything else right now.
33 *
34 * - see if we can do anything to one-button-ise the multi-button
35 * dependent puzzle UIs:
36 * - Pattern is a _little_ unwieldy but not too bad (since
37 * generally you never need the middle button unless you've
38 * made a mistake, so it's just click versus command-click).
39 * - Net is utterly vile; having normal click be one rotate and
40 * command-click be the other introduces a horrid asymmetry,
41 * and yet requiring a shift key for _each_ click would be
42 * even worse because rotation feels as if it ought to be the
43 * default action. I fear this is why the Flash Net had the
44 * UI it did...
45 * + I've tried out an alternative dragging interface for
46 * Net; it might work nicely for stylus-based platforms
47 * where you have better hand/eye feedback for the thing
48 * you're clicking on, but it's rather unwieldy on the
49 * Mac. I fear even shift-clicking is better than that.
50 *
51 * - Should we _return_ to a game configuration sheet once an
52 * error is reported by midend_set_config, to allow the user to
53 * correct the one faulty input and keep the other five OK ones?
54 * The Apple `one sheet at a time' restriction would require me
55 * to do this by closing the config sheet, opening the alert
56 * sheet, and then reopening the config sheet when the alert is
57 * closed; and the human interface types, who presumably
58 * invented the one-sheet-at-a-time rule for good reasons, might
59 * look with disfavour on me trying to get round them to fake a
60 * nested sheet. On the other hand I think there are good
61 * practical reasons for wanting it that way. Uncertain.
62 *
63 * - User feedback dislikes nothing happening when you start the
64 * app; they suggest a finder-like window containing an icon for
65 * each puzzle type, enabling you to start one easily. Needs
66 * thought.
67 *
68 * Grotty implementation details that could probably be improved:
69 *
70 * - I am _utterly_ unconvinced that NSImageView was the right way
71 * to go about having a window with a reliable backing store! It
72 * just doesn't feel right; NSImageView is a _control_. Is there
73 * a simpler way?
74 *
75 * - Resizing is currently very bad; rather than bother to work
76 * out how to resize the NSImageView, I just splatter and
77 * recreate it.
78 */
79
80#define COMBINED /* we put all the puzzles in one binary in this port */
81
82#include <ctype.h>
83#include <time.h>
84#include <sys/time.h>
85#import <Cocoa/Cocoa.h>
86#include "puzzles.h"
87
88/* ----------------------------------------------------------------------
89 * Global variables.
90 */
91
92/*
93 * The `Type' menu. We frob this dynamically to allow the user to
94 * choose a preset set of settings from the current game.
95 */
96NSMenu *typemenu;
97
98/*
99 * Forward reference.
100 */
101extern const struct drawing_api osx_drawing;
102
103/*
104 * The NSApplication shared instance, which I'll want to refer to from
105 * a few places here and there.
106 */
107NSApplication *app;
108
109/* ----------------------------------------------------------------------
110 * Miscellaneous support routines that aren't part of any object or
111 * clearly defined subsystem.
112 */
113
114void fatal(char *fmt, ...)
115{
116 va_list ap;
117 char errorbuf[2048];
118 NSAlert *alert;
119
120 va_start(ap, fmt);
121 vsnprintf(errorbuf, lenof(errorbuf), fmt, ap);
122 va_end(ap);
123
124 alert = [NSAlert alloc];
125 /*
126 * We may have come here because we ran out of memory, in which
127 * case it's entirely likely that that alloc will fail, so we
128 * should have a fallback of some sort.
129 */
130 if (!alert) {
131 fprintf(stderr, "fatal error (and NSAlert failed): %s\n", errorbuf);
132 } else {
133 alert = [[alert init] autorelease];
134 [alert addButtonWithTitle:@"Oh dear"];
135 [alert setInformativeText:[NSString stringWithUTF8String:errorbuf]];
136 [alert runModal];
137 }
138 exit(1);
139}
140
141void frontend_default_colour(frontend *fe, float *output)
142{
143 /* FIXME: Is there a system default we can tap into for this? */
144 output[0] = output[1] = output[2] = 0.8F;
145}
146
147void get_random_seed(void **randseed, int *randseedsize)
148{
149 time_t *tp = snew(time_t);
150 time(tp);
151 *randseed = (void *)tp;
152 *randseedsize = sizeof(time_t);
153}
154
155static void savefile_write(void *wctx, void *buf, int len)
156{
157 FILE *fp = (FILE *)wctx;
158 fwrite(buf, 1, len, fp);
159}
160
161static int savefile_read(void *wctx, void *buf, int len)
162{
163 FILE *fp = (FILE *)wctx;
164 int ret;
165
166 ret = fread(buf, 1, len, fp);
167 return (ret == len);
168}
169
170/*
171 * Since this front end does not support printing (yet), we need
172 * this stub to satisfy the reference in midend_print_puzzle().
173 */
174void document_add_puzzle(document *doc, const game *game, game_params *par,
175 game_state *st, game_state *st2)
176{
177}
178
179/*
180 * setAppleMenu isn't listed in the NSApplication header, but an
181 * NSApp responds to it, so we're adding it here to silence
182 * warnings. (This was removed from the headers in 10.4, so we
183 * only need to include it for 10.4+.)
184 */
185#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
186@interface NSApplication(NSAppleMenu)
187- (void)setAppleMenu:(NSMenu *)menu;
188@end
189#endif
190
191/* ----------------------------------------------------------------------
192 * Tiny extension to NSMenuItem which carries a payload of a `void
193 * *', allowing several menu items to invoke the same message but
194 * pass different data through it.
195 */
196@interface DataMenuItem : NSMenuItem
197{
198 void *payload;
199}
200- (void)setPayload:(void *)d;
201- (void *)getPayload;
202@end
203@implementation DataMenuItem
204- (void)setPayload:(void *)d
205{
206 payload = d;
207}
208- (void *)getPayload
209{
210 return payload;
211}
212@end
213
214/* ----------------------------------------------------------------------
215 * Utility routines for constructing OS X menus.
216 */
217
218NSMenu *newmenu(const char *title)
219{
220 return [[[NSMenu allocWithZone:[NSMenu menuZone]]
221 initWithTitle:[NSString stringWithUTF8String:title]]
222 autorelease];
223}
224
225NSMenu *newsubmenu(NSMenu *parent, const char *title)
226{
227 NSMenuItem *item;
228 NSMenu *child;
229
230 item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]]
231 initWithTitle:[NSString stringWithUTF8String:title]
232 action:NULL
233 keyEquivalent:@""]
234 autorelease];
235 child = newmenu(title);
236 [item setEnabled:YES];
237 [item setSubmenu:child];
238 [parent addItem:item];
239 return child;
240}
241
242id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title,
243 const char *key, id target, SEL action)
244{
245 unsigned mask = NSCommandKeyMask;
246
247 if (key[strcspn(key, "-")]) {
248 while (*key && *key != '-') {
249 int c = tolower((unsigned char)*key);
250 if (c == 's') {
251 mask |= NSShiftKeyMask;
252 } else if (c == 'o' || c == 'a') {
253 mask |= NSAlternateKeyMask;
254 }
255 key++;
256 }
257 if (*key)
258 key++;
259 }
260
261 item = [[item initWithTitle:[NSString stringWithUTF8String:title]
262 action:NULL
263 keyEquivalent:[NSString stringWithUTF8String:key]]
264 autorelease];
265
266 if (*key)
267 [item setKeyEquivalentModifierMask: mask];
268
269 [item setEnabled:YES];
270 [item setTarget:target];
271 [item setAction:action];
272
273 [parent addItem:item];
274
275 return item;
276}
277
278NSMenuItem *newitem(NSMenu *parent, char *title, char *key,
279 id target, SEL action)
280{
281 return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]],
282 parent, title, key, target, action);
283}
284
285/* ----------------------------------------------------------------------
286 * About box.
287 */
288
289@class AboutBox;
290
291@interface AboutBox : NSWindow
292{
293}
294- (id)init;
295@end
296
297@implementation AboutBox
298- (id)init
299{
300 NSRect totalrect;
301 NSView *views[16];
302 int nviews = 0;
303 NSImageView *iv;
304 NSTextField *tf;
305 NSFont *font1 = [NSFont systemFontOfSize:0];
306 NSFont *font2 = [NSFont boldSystemFontOfSize:[font1 pointSize] * 1.1];
307 const int border = 24;
308 int i;
309 double y;
310
311 /*
312 * Construct the controls that go in the About box.
313 */
314
315 iv = [[NSImageView alloc] initWithFrame:NSMakeRect(0,0,64,64)];
316 [iv setImage:[NSImage imageNamed:@"NSApplicationIcon"]];
317 views[nviews++] = iv;
318
319 tf = [[NSTextField alloc]
320 initWithFrame:NSMakeRect(0,0,400,1)];
321 [tf setEditable:NO];
322 [tf setSelectable:NO];
323 [tf setBordered:NO];
324 [tf setDrawsBackground:NO];
325 [tf setFont:font2];
326 [tf setStringValue:@"Simon Tatham's Portable Puzzle Collection"];
327 [tf sizeToFit];
328 views[nviews++] = tf;
329
330 tf = [[NSTextField alloc]
331 initWithFrame:NSMakeRect(0,0,400,1)];
332 [tf setEditable:NO];
333 [tf setSelectable:NO];
334 [tf setBordered:NO];
335 [tf setDrawsBackground:NO];
336 [tf setFont:font1];
337 [tf setStringValue:[NSString stringWithUTF8String:ver]];
338 [tf sizeToFit];
339 views[nviews++] = tf;
340
341 /*
342 * Lay the controls out.
343 */
344 totalrect = NSMakeRect(0,0,0,0);
345 for (i = 0; i < nviews; i++) {
346 NSRect r = [views[i] frame];
347 if (totalrect.size.width < r.size.width)
348 totalrect.size.width = r.size.width;
349 totalrect.size.height += border + r.size.height;
350 }
351 totalrect.size.width += 2 * border;
352 totalrect.size.height += border;
353 y = totalrect.size.height;
354 for (i = 0; i < nviews; i++) {
355 NSRect r = [views[i] frame];
356 r.origin.x = (totalrect.size.width - r.size.width) / 2;
357 y -= border + r.size.height;
358 r.origin.y = y;
359 [views[i] setFrame:r];
360 }
361
362 self = [super initWithContentRect:totalrect
363 styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |
364 NSClosableWindowMask)
365 backing:NSBackingStoreBuffered
366 defer:YES];
367
368 for (i = 0; i < nviews; i++)
369 [[self contentView] addSubview:views[i]];
370
371 [self center]; /* :-) */
372
373 return self;
374}
375@end
376
377/* ----------------------------------------------------------------------
378 * The front end presented to midend.c.
379 *
380 * This is mostly a subclass of NSWindow. The actual `frontend'
381 * structure passed to the midend contains a variety of pointers,
382 * including that window object but also including the image we
383 * draw on, an ImageView to display it in the window, and so on.
384 */
385
386@class GameWindow;
387@class MyImageView;
388
389struct frontend {
390 GameWindow *window;
391 NSImage *image;
392 MyImageView *view;
393 NSColor **colours;
394 int ncolours;
395 int clipped;
396 int w, h;
397};
398
399@interface MyImageView : NSImageView
400{
401 GameWindow *ourwin;
402}
403- (void)setWindow:(GameWindow *)win;
404- (void)mouseEvent:(NSEvent *)ev button:(int)b;
405- (void)mouseDown:(NSEvent *)ev;
406- (void)mouseDragged:(NSEvent *)ev;
407- (void)mouseUp:(NSEvent *)ev;
408- (void)rightMouseDown:(NSEvent *)ev;
409- (void)rightMouseDragged:(NSEvent *)ev;
410- (void)rightMouseUp:(NSEvent *)ev;
411- (void)otherMouseDown:(NSEvent *)ev;
412- (void)otherMouseDragged:(NSEvent *)ev;
413- (void)otherMouseUp:(NSEvent *)ev;
414@end
415
416@interface GameWindow : NSWindow
417{
418 const game *ourgame;
419 midend *me;
420 struct frontend fe;
421 struct timeval last_time;
422 NSTimer *timer;
423 NSWindow *sheet;
424 config_item *cfg;
425 int cfg_which;
426 NSView **cfg_controls;
427 int cfg_ncontrols;
428 NSTextField *status;
429 struct preset_menu *preset_menu;
430 NSMenuItem **preset_menu_items;
431 int n_preset_menu_items;
432}
433- (id)initWithGame:(const game *)g;
434- (void)dealloc;
435- (void)processButton:(int)b x:(int)x y:(int)y;
436- (void)processKey:(int)b;
437- (void)keyDown:(NSEvent *)ev;
438- (void)activateTimer;
439- (void)deactivateTimer;
440- (void)setStatusLine:(char *)text;
441- (void)resizeForNewGameParams;
442- (void)updateTypeMenuTick;
443@end
444
445@implementation MyImageView
446
447- (void)setWindow:(GameWindow *)win
448{
449 ourwin = win;
450}
451
452- (void)mouseEvent:(NSEvent *)ev button:(int)b
453{
454 NSPoint point = [self convertPoint:[ev locationInWindow] fromView:nil];
455 [ourwin processButton:b x:point.x y:point.y];
456}
457
458- (void)mouseDown:(NSEvent *)ev
459{
460 unsigned mod = [ev modifierFlags];
461 [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_BUTTON :
462 (mod & NSShiftKeyMask) ? MIDDLE_BUTTON :
463 LEFT_BUTTON)];
464}
465- (void)mouseDragged:(NSEvent *)ev
466{
467 unsigned mod = [ev modifierFlags];
468 [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_DRAG :
469 (mod & NSShiftKeyMask) ? MIDDLE_DRAG :
470 LEFT_DRAG)];
471}
472- (void)mouseUp:(NSEvent *)ev
473{
474 unsigned mod = [ev modifierFlags];
475 [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_RELEASE :
476 (mod & NSShiftKeyMask) ? MIDDLE_RELEASE :
477 LEFT_RELEASE)];
478}
479- (void)rightMouseDown:(NSEvent *)ev
480{
481 unsigned mod = [ev modifierFlags];
482 [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_BUTTON :
483 RIGHT_BUTTON)];
484}
485- (void)rightMouseDragged:(NSEvent *)ev
486{
487 unsigned mod = [ev modifierFlags];
488 [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_DRAG :
489 RIGHT_DRAG)];
490}
491- (void)rightMouseUp:(NSEvent *)ev
492{
493 unsigned mod = [ev modifierFlags];
494 [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_RELEASE :
495 RIGHT_RELEASE)];
496}
497- (void)otherMouseDown:(NSEvent *)ev
498{
499 [self mouseEvent:ev button:MIDDLE_BUTTON];
500}
501- (void)otherMouseDragged:(NSEvent *)ev
502{
503 [self mouseEvent:ev button:MIDDLE_DRAG];
504}
505- (void)otherMouseUp:(NSEvent *)ev
506{
507 [self mouseEvent:ev button:MIDDLE_RELEASE];
508}
509@end
510
511@implementation GameWindow
512- (void)setupContentView
513{
514 NSRect frame;
515 int w, h;
516
517 if (status) {
518 frame = [status frame];
519 frame.origin.y = frame.size.height;
520 } else
521 frame.origin.y = 0;
522 frame.origin.x = 0;
523
524 w = h = INT_MAX;
525 midend_size(me, &w, &h, FALSE);
526 frame.size.width = w;
527 frame.size.height = h;
528 fe.w = w;
529 fe.h = h;
530
531 fe.image = [[NSImage alloc] initWithSize:frame.size];
532 fe.view = [[MyImageView alloc] initWithFrame:frame];
533 [fe.view setImage:fe.image];
534 [fe.view setWindow:self];
535
536 midend_redraw(me);
537
538 [[self contentView] addSubview:fe.view];
539}
540- (id)initWithGame:(const game *)g
541{
542 NSRect rect = { {0,0}, {0,0} }, rect2;
543 int w, h;
544
545 ourgame = g;
546 preset_menu = NULL;
547 preset_menu_items = NULL;
548
549 fe.window = self;
550
551 me = midend_new(&fe, ourgame, &osx_drawing, &fe);
552 /*
553 * If we ever need to open a fresh window using a provided game
554 * ID, I think the right thing is to move most of this method
555 * into a new initWithGame:gameID: method, and have
556 * initWithGame: simply call that one and pass it NULL.
557 */
558 midend_new_game(me);
559 w = h = INT_MAX;
560 midend_size(me, &w, &h, FALSE);
561 rect.size.width = w;
562 rect.size.height = h;
563 fe.w = w;
564 fe.h = h;
565
566 /*
567 * Create the status bar, which will just be an NSTextField.
568 */
569 if (midend_wants_statusbar(me)) {
570 status = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,100,50)];
571 [status setEditable:NO];
572 [status setSelectable:NO];
573 [status setBordered:YES];
574 [status setBezeled:YES];
575 [status setBezelStyle:NSTextFieldSquareBezel];
576 [status setDrawsBackground:YES];
577 [[status cell] setTitle:@DEFAULT_STATUSBAR_TEXT];
578 [status sizeToFit];
579 rect2 = [status frame];
580 rect.size.height += rect2.size.height;
581 rect2.size.width = rect.size.width;
582 rect2.origin.x = rect2.origin.y = 0;
583 [status setFrame:rect2];
584 } else
585 status = nil;
586
587 self = [super initWithContentRect:rect
588 styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |
589 NSClosableWindowMask)
590 backing:NSBackingStoreBuffered
591 defer:YES];
592 [self setTitle:[NSString stringWithUTF8String:ourgame->name]];
593
594 {
595 float *colours;
596 int i, ncolours;
597
598 colours = midend_colours(me, &ncolours);
599 fe.ncolours = ncolours;
600 fe.colours = snewn(ncolours, NSColor *);
601
602 for (i = 0; i < ncolours; i++) {
603 fe.colours[i] = [[NSColor colorWithDeviceRed:colours[i*3]
604 green:colours[i*3+1] blue:colours[i*3+2]
605 alpha:1.0] retain];
606 }
607 }
608
609 [self setupContentView];
610 if (status)
611 [[self contentView] addSubview:status];
612 [self setIgnoresMouseEvents:NO];
613
614 [self center]; /* :-) */
615
616 return self;
617}
618
619- (void)dealloc
620{
621 int i;
622 for (i = 0; i < fe.ncolours; i++) {
623 [fe.colours[i] release];
624 }
625 sfree(fe.colours);
626 sfree(preset_menu_items);
627 midend_free(me);
628 [super dealloc];
629}
630
631- (void)processButton:(int)b x:(int)x y:(int)y
632{
633 if (!midend_process_key(me, x, fe.h - 1 - y, b))
634 [self close];
635}
636
637- (void)processKey:(int)b
638{
639 if (!midend_process_key(me, -1, -1, b))
640 [self close];
641}
642
643- (void)keyDown:(NSEvent *)ev
644{
645 NSString *s = [ev characters];
646 int i, n = [s length];
647
648 for (i = 0; i < n; i++) {
649 int c = [s characterAtIndex:i];
650
651 /*
652 * ASCII gets passed straight to midend_process_key.
653 * Anything above that has to be translated to our own
654 * function key codes.
655 */
656 if (c >= 0x80) {
657 int mods = FALSE;
658 switch (c) {
659 case NSUpArrowFunctionKey:
660 c = CURSOR_UP;
661 mods = TRUE;
662 break;
663 case NSDownArrowFunctionKey:
664 c = CURSOR_DOWN;
665 mods = TRUE;
666 break;
667 case NSLeftArrowFunctionKey:
668 c = CURSOR_LEFT;
669 mods = TRUE;
670 break;
671 case NSRightArrowFunctionKey:
672 c = CURSOR_RIGHT;
673 mods = TRUE;
674 break;
675 default:
676 continue;
677 }
678
679 if (mods) {
680 if ([ev modifierFlags] & NSShiftKeyMask)
681 c |= MOD_SHFT;
682 if ([ev modifierFlags] & NSControlKeyMask)
683 c |= MOD_CTRL;
684 }
685 }
686
687 if (c >= '0' && c <= '9' && ([ev modifierFlags] & NSNumericPadKeyMask))
688 c |= MOD_NUM_KEYPAD;
689
690 [self processKey:c];
691 }
692}
693
694- (void)activateTimer
695{
696 if (timer != nil)
697 return;
698
699 timer = [NSTimer scheduledTimerWithTimeInterval:0.02
700 target:self selector:@selector(timerTick:)
701 userInfo:nil repeats:YES];
702 gettimeofday(&last_time, NULL);
703}
704
705- (void)deactivateTimer
706{
707 if (timer == nil)
708 return;
709
710 [timer invalidate];
711 timer = nil;
712}
713
714- (void)timerTick:(id)sender
715{
716 struct timeval now;
717 float elapsed;
718 gettimeofday(&now, NULL);
719 elapsed = ((now.tv_usec - last_time.tv_usec) * 0.000001F +
720 (now.tv_sec - last_time.tv_sec));
721 midend_timer(me, elapsed);
722 last_time = now;
723}
724
725- (void)showError:(char *)message
726{
727 NSAlert *alert;
728
729 alert = [[[NSAlert alloc] init] autorelease];
730 [alert addButtonWithTitle:@"Bah"];
731 [alert setInformativeText:[NSString stringWithUTF8String:message]];
732 [alert beginSheetModalForWindow:self modalDelegate:nil
733 didEndSelector:NULL contextInfo:nil];
734}
735
736- (void)newGame:(id)sender
737{
738 [self processKey:'n'];
739}
740- (void)restartGame:(id)sender
741{
742 midend_restart_game(me);
743}
744- (void)saveGame:(id)sender
745{
746 NSSavePanel *sp = [NSSavePanel savePanel];
747
748 if ([sp runModal] == NSFileHandlingPanelOKButton) {
749 const char *name = [[sp filename] UTF8String];
750
751 FILE *fp = fopen(name, "w");
752
753 if (!fp) {
754 [self showError:"Unable to open save file"];
755 return;
756 }
757
758 midend_serialise(me, savefile_write, fp);
759
760 fclose(fp);
761 }
762}
763- (void)loadSavedGame:(id)sender
764{
765 NSOpenPanel *op = [NSOpenPanel openPanel];
766
767 [op setAllowsMultipleSelection:NO];
768
769 if ([op runModalForTypes:nil] == NSOKButton) {
770 /*
771 * This used to be
772 *
773 * [[[op filenames] objectAtIndex:0] cString]
774 *
775 * but the plain cString method became deprecated and Xcode 7
776 * started complaining about it. Since OS X 10.9 we can
777 * apparently use the more modern API
778 *
779 * [[[op URLs] objectAtIndex:0] fileSystemRepresentation]
780 *
781 * but the alternative below still compiles with Xcode 7 and
782 * is a bit more backwards compatible, so I'll try it for the
783 * moment.
784 */
785 const char *name = [[[op filenames] objectAtIndex:0]
786 cStringUsingEncoding:
787 [NSString defaultCStringEncoding]];
788 char *err;
789
790 FILE *fp = fopen(name, "r");
791
792 if (!fp) {
793 [self showError:"Unable to open saved game file"];
794 return;
795 }
796
797 err = midend_deserialise(me, savefile_read, fp);
798
799 fclose(fp);
800
801 if (err) {
802 [self showError:err];
803 return;
804 }
805
806 [self resizeForNewGameParams];
807 [self updateTypeMenuTick];
808 }
809}
810- (void)undoMove:(id)sender
811{
812 [self processKey:'u'];
813}
814- (void)redoMove:(id)sender
815{
816 [self processKey:'r'&0x1F];
817}
818
819- (void)copy:(id)sender
820{
821 char *text;
822
823 if ((text = midend_text_format(me)) != NULL) {
824 NSPasteboard *pb = [NSPasteboard generalPasteboard];
825 NSArray *a = [NSArray arrayWithObject:NSStringPboardType];
826 [pb declareTypes:a owner:nil];
827 [pb setString:[NSString stringWithUTF8String:text]
828 forType:NSStringPboardType];
829 } else
830 NSBeep();
831}
832
833- (void)solveGame:(id)sender
834{
835 char *msg;
836
837 msg = midend_solve(me);
838
839 if (msg)
840 [self showError:msg];
841}
842
843- (BOOL)validateMenuItem:(NSMenuItem *)item
844{
845 if ([item action] == @selector(copy:))
846 return (ourgame->can_format_as_text_ever &&
847 midend_can_format_as_text_now(me) ? YES : NO);
848 else if ([item action] == @selector(solveGame:))
849 return (ourgame->can_solve ? YES : NO);
850 else
851 return [super validateMenuItem:item];
852}
853
854- (void)clearTypeMenu
855{
856 int i;
857
858 while ([typemenu numberOfItems] > 1)
859 [typemenu removeItemAtIndex:0];
860 [[typemenu itemAtIndex:0] setState:NSOffState];
861
862 for (i = 0; i < n_preset_menu_items; i++)
863 preset_menu_items[i] = NULL;
864}
865
866- (void)updateTypeMenuTick
867{
868 int i, n;
869
870 n = midend_which_preset(me);
871
872 for (i = 0; i < n_preset_menu_items; i++)
873 if (preset_menu_items[i])
874 [preset_menu_items[i] setState:(i == n ? NSOnState : NSOffState)];
875
876 /*
877 * The Custom menu item is always right at the bottom of the
878 * Type menu.
879 */
880 [[typemenu itemAtIndex:[typemenu numberOfItems]-1]
881 setState:(n < 0 ? NSOnState : NSOffState)];
882}
883
884- (void)populateTypeMenu:(NSMenu *)nsmenu from:(struct preset_menu *)menu
885{
886 int i;
887
888 /*
889 * We process the entries in reverse order so that (in the
890 * top-level Type menu at least) we don't disturb the 'Custom'
891 * item which remains fixed even when we change back and forth
892 * between puzzle type windows.
893 */
894 for (i = menu->n_entries; i-- > 0 ;) {
895 struct preset_menu_entry *entry = &menu->entries[i];
896 NSMenuItem *item;
897
898 if (entry->params) {
899 DataMenuItem *ditem;
900 ditem = [[[DataMenuItem alloc]
901 initWithTitle:[NSString stringWithUTF8String:
902 entry->title]
903 action:NULL keyEquivalent:@""]
904 autorelease];
905
906 [ditem setTarget:self];
907 [ditem setAction:@selector(presetGame:)];
908 [ditem setPayload:entry->params];
909
910 preset_menu_items[entry->id] = ditem;
911
912 item = ditem;
913 } else {
914 NSMenu *nssubmenu;
915
916 item = [[[NSMenuItem alloc]
917 initWithTitle:[NSString stringWithUTF8String:
918 entry->title]
919 action:NULL keyEquivalent:@""]
920 autorelease];
921 nssubmenu = newmenu(entry->title);
922 [item setSubmenu:nssubmenu];
923
924 [self populateTypeMenu:nssubmenu from:entry->submenu];
925 }
926
927 [item setEnabled:YES];
928 [nsmenu insertItem:item atIndex:0];
929 }
930}
931
932- (void)becomeKeyWindow
933{
934 [self clearTypeMenu];
935
936 [super becomeKeyWindow];
937
938 if (!preset_menu) {
939 int i;
940 preset_menu = midend_get_presets(me, &n_preset_menu_items);
941 preset_menu_items = snewn(n_preset_menu_items, NSMenuItem *);
942 for (i = 0; i < n_preset_menu_items; i++)
943 preset_menu_items[i] = NULL;
944 }
945
946 if (preset_menu->n_entries > 0) {
947 [typemenu insertItem:[NSMenuItem separatorItem] atIndex:0];
948 [self populateTypeMenu:typemenu from:preset_menu];
949 }
950
951 [self updateTypeMenuTick];
952}
953
954- (void)resignKeyWindow
955{
956 [self clearTypeMenu];
957 [super resignKeyWindow];
958}
959
960- (void)close
961{
962 [self clearTypeMenu];
963 [super close];
964}
965
966- (void)resizeForNewGameParams
967{
968 NSSize size = {0,0};
969 int w, h;
970
971 w = h = INT_MAX;
972 midend_size(me, &w, &h, FALSE);
973 size.width = w;
974 size.height = h;
975 fe.w = w;
976 fe.h = h;
977
978 if (status) {
979 NSRect frame = [status frame];
980 size.height += frame.size.height;
981 frame.size.width = size.width;
982 [status setFrame:frame];
983 }
984
985#ifndef GNUSTEP
986 NSDisableScreenUpdates();
987#endif
988 [self setContentSize:size];
989 [self setupContentView];
990#ifndef GNUSTEP
991 NSEnableScreenUpdates();
992#endif
993}
994
995- (void)presetGame:(id)sender
996{
997 game_params *params = [sender getPayload];
998
999 midend_set_params(me, params);
1000 midend_new_game(me);
1001
1002 [self resizeForNewGameParams];
1003 [self updateTypeMenuTick];
1004}
1005
1006- (void)startConfigureSheet:(int)which
1007{
1008 NSButton *ok, *cancel;
1009 int actw, acth, leftw, rightw, totalw, h, thish, y;
1010 int k;
1011 NSRect rect, tmprect;
1012 const int SPACING = 16;
1013 char *title;
1014 config_item *i;
1015 int cfg_controlsize;
1016 NSTextField *tf;
1017 NSButton *b;
1018 NSPopUpButton *pb;
1019
1020 assert(sheet == NULL);
1021
1022 /*
1023 * Every control we create here is going to have this size
1024 * until we tell it to calculate a better one.
1025 */
1026 tmprect = NSMakeRect(0, 0, 100, 50);
1027
1028 /*
1029 * Set up OK and Cancel buttons. (Actually, MacOS doesn't seem
1030 * to be fond of generic OK and Cancel wording, so I'm going to
1031 * rename them to something nicer.)
1032 */
1033 actw = acth = 0;
1034
1035 cancel = [[NSButton alloc] initWithFrame:tmprect];
1036 [cancel setBezelStyle:NSRoundedBezelStyle];
1037 [cancel setTitle:@"Abandon"];
1038 [cancel setTarget:self];
1039 [cancel setKeyEquivalent:@"\033"];
1040 [cancel setAction:@selector(sheetCancelButton:)];
1041 [cancel sizeToFit];
1042 rect = [cancel frame];
1043 if (actw < rect.size.width) actw = rect.size.width;
1044 if (acth < rect.size.height) acth = rect.size.height;
1045
1046 ok = [[NSButton alloc] initWithFrame:tmprect];
1047 [ok setBezelStyle:NSRoundedBezelStyle];
1048 [ok setTitle:@"Accept"];
1049 [ok setTarget:self];
1050 [ok setKeyEquivalent:@"\r"];
1051 [ok setAction:@selector(sheetOKButton:)];
1052 [ok sizeToFit];
1053 rect = [ok frame];
1054 if (actw < rect.size.width) actw = rect.size.width;
1055 if (acth < rect.size.height) acth = rect.size.height;
1056
1057 totalw = SPACING + 2 * actw;
1058 h = 2 * SPACING + acth;
1059
1060 /*
1061 * Now fetch the midend config data and go through it creating
1062 * controls.
1063 */
1064 cfg = midend_get_config(me, which, &title);
1065 sfree(title); /* FIXME: should we use this somehow? */
1066 cfg_which = which;
1067
1068 cfg_ncontrols = cfg_controlsize = 0;
1069 cfg_controls = NULL;
1070 leftw = rightw = 0;
1071 for (i = cfg; i->type != C_END; i++) {
1072 if (cfg_controlsize < cfg_ncontrols + 5) {
1073 cfg_controlsize = cfg_ncontrols + 32;
1074 cfg_controls = sresize(cfg_controls, cfg_controlsize, NSView *);
1075 }
1076
1077 thish = 0;
1078
1079 switch (i->type) {
1080 case C_STRING:
1081 /*
1082 * Two NSTextFields, one being a label and the other
1083 * being an edit box.
1084 */
1085
1086 tf = [[NSTextField alloc] initWithFrame:tmprect];
1087 [tf setEditable:NO];
1088 [tf setSelectable:NO];
1089 [tf setBordered:NO];
1090 [tf setDrawsBackground:NO];
1091 [[tf cell] setTitle:[NSString stringWithUTF8String:i->name]];
1092 [tf sizeToFit];
1093 rect = [tf frame];
1094 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1095 if (leftw < rect.size.width + 1) leftw = rect.size.width + 1;
1096 cfg_controls[cfg_ncontrols++] = tf;
1097
1098 tf = [[NSTextField alloc] initWithFrame:tmprect];
1099 [tf setEditable:YES];
1100 [tf setSelectable:YES];
1101 [tf setBordered:YES];
1102 [[tf cell] setTitle:[NSString stringWithUTF8String:i->sval]];
1103 [tf sizeToFit];
1104 rect = [tf frame];
1105 /*
1106 * We impose a minimum and maximum width on editable
1107 * NSTextFields. If we allow them to size themselves to
1108 * the contents of the text within them, then they will
1109 * look very silly if that text is only one or two
1110 * characters, and equally silly if it's an absolutely
1111 * enormous Rectangles or Pattern game ID!
1112 */
1113 if (rect.size.width < 75) rect.size.width = 75;
1114 if (rect.size.width > 400) rect.size.width = 400;
1115
1116 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1117 if (rightw < rect.size.width + 1) rightw = rect.size.width + 1;
1118 cfg_controls[cfg_ncontrols++] = tf;
1119 break;
1120
1121 case C_BOOLEAN:
1122 /*
1123 * A checkbox is an NSButton with a type of
1124 * NSSwitchButton.
1125 */
1126 b = [[NSButton alloc] initWithFrame:tmprect];
1127 [b setBezelStyle:NSRoundedBezelStyle];
1128 [b setButtonType:NSSwitchButton];
1129 [b setTitle:[NSString stringWithUTF8String:i->name]];
1130 [b sizeToFit];
1131 [b setState:(i->ival ? NSOnState : NSOffState)];
1132 rect = [b frame];
1133 if (totalw < rect.size.width + 1) totalw = rect.size.width + 1;
1134 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1135 cfg_controls[cfg_ncontrols++] = b;
1136 break;
1137
1138 case C_CHOICES:
1139 /*
1140 * A pop-up menu control is an NSPopUpButton, which
1141 * takes an embedded NSMenu. We also need an
1142 * NSTextField to act as a label.
1143 */
1144
1145 tf = [[NSTextField alloc] initWithFrame:tmprect];
1146 [tf setEditable:NO];
1147 [tf setSelectable:NO];
1148 [tf setBordered:NO];
1149 [tf setDrawsBackground:NO];
1150 [[tf cell] setTitle:[NSString stringWithUTF8String:i->name]];
1151 [tf sizeToFit];
1152 rect = [tf frame];
1153 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1154 if (leftw < rect.size.width + 1) leftw = rect.size.width + 1;
1155 cfg_controls[cfg_ncontrols++] = tf;
1156
1157 pb = [[NSPopUpButton alloc] initWithFrame:tmprect pullsDown:NO];
1158 [pb setBezelStyle:NSRoundedBezelStyle];
1159 {
1160 char c, *p;
1161
1162 p = i->sval;
1163 c = *p++;
1164 while (*p) {
1165 char *q, *copy;
1166
1167 q = p;
1168 while (*p && *p != c) p++;
1169
1170 copy = snewn((p-q) + 1, char);
1171 memcpy(copy, q, p-q);
1172 copy[p-q] = '\0';
1173 [pb addItemWithTitle:[NSString stringWithUTF8String:copy]];
1174 sfree(copy);
1175
1176 if (*p) p++;
1177 }
1178 }
1179 [pb selectItemAtIndex:i->ival];
1180 [pb sizeToFit];
1181
1182 rect = [pb frame];
1183 if (rightw < rect.size.width + 1) rightw = rect.size.width + 1;
1184 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1185 cfg_controls[cfg_ncontrols++] = pb;
1186 break;
1187 }
1188
1189 h += SPACING + thish;
1190 }
1191
1192 if (totalw < leftw + SPACING + rightw)
1193 totalw = leftw + SPACING + rightw;
1194 if (totalw > leftw + SPACING + rightw) {
1195 int excess = totalw - (leftw + SPACING + rightw);
1196 int leftexcess = leftw * excess / (leftw + rightw);
1197 int rightexcess = excess - leftexcess;
1198 leftw += leftexcess;
1199 rightw += rightexcess;
1200 }
1201
1202 /*
1203 * Now go through the list again, setting the final position
1204 * for each control.
1205 */
1206 k = 0;
1207 y = h;
1208 for (i = cfg; i->type != C_END; i++) {
1209 y -= SPACING;
1210 thish = 0;
1211 switch (i->type) {
1212 case C_STRING:
1213 case C_CHOICES:
1214 /*
1215 * These two are treated identically, since both expect
1216 * a control on the left and another on the right.
1217 */
1218 rect = [cfg_controls[k] frame];
1219 if (thish < rect.size.height + 1)
1220 thish = rect.size.height + 1;
1221 rect = [cfg_controls[k+1] frame];
1222 if (thish < rect.size.height + 1)
1223 thish = rect.size.height + 1;
1224 rect = [cfg_controls[k] frame];
1225 rect.origin.y = y - thish/2 - rect.size.height/2;
1226 rect.origin.x = SPACING;
1227 rect.size.width = leftw;
1228 [cfg_controls[k] setFrame:rect];
1229 rect = [cfg_controls[k+1] frame];
1230 rect.origin.y = y - thish/2 - rect.size.height/2;
1231 rect.origin.x = 2 * SPACING + leftw;
1232 rect.size.width = rightw;
1233 [cfg_controls[k+1] setFrame:rect];
1234 k += 2;
1235 break;
1236
1237 case C_BOOLEAN:
1238 rect = [cfg_controls[k] frame];
1239 if (thish < rect.size.height + 1)
1240 thish = rect.size.height + 1;
1241 rect.origin.y = y - thish/2 - rect.size.height/2;
1242 rect.origin.x = SPACING;
1243 rect.size.width = totalw;
1244 [cfg_controls[k] setFrame:rect];
1245 k++;
1246 break;
1247 }
1248 y -= thish;
1249 }
1250
1251 assert(k == cfg_ncontrols);
1252
1253 [cancel setFrame:NSMakeRect(SPACING+totalw/4-actw/2, SPACING, actw, acth)];
1254 [ok setFrame:NSMakeRect(SPACING+3*totalw/4-actw/2, SPACING, actw, acth)];
1255
1256 sheet = [[NSWindow alloc]
1257 initWithContentRect:NSMakeRect(0,0,totalw + 2*SPACING,h)
1258 styleMask:NSTitledWindowMask | NSClosableWindowMask
1259 backing:NSBackingStoreBuffered
1260 defer:YES];
1261
1262 [[sheet contentView] addSubview:cancel];
1263 [[sheet contentView] addSubview:ok];
1264
1265 for (k = 0; k < cfg_ncontrols; k++)
1266 [[sheet contentView] addSubview:cfg_controls[k]];
1267
1268 [app beginSheet:sheet modalForWindow:self
1269 modalDelegate:nil didEndSelector:NULL contextInfo:nil];
1270}
1271
1272- (void)specificGame:(id)sender
1273{
1274 [self startConfigureSheet:CFG_DESC];
1275}
1276
1277- (void)specificRandomGame:(id)sender
1278{
1279 [self startConfigureSheet:CFG_SEED];
1280}
1281
1282- (void)customGameType:(id)sender
1283{
1284 [self startConfigureSheet:CFG_SETTINGS];
1285}
1286
1287- (void)sheetEndWithStatus:(BOOL)update
1288{
1289 assert(sheet != NULL);
1290 [app endSheet:sheet];
1291 [sheet orderOut:self];
1292 sheet = NULL;
1293 if (update) {
1294 int k;
1295 config_item *i;
1296 char *error;
1297
1298 k = 0;
1299 for (i = cfg; i->type != C_END; i++) {
1300 switch (i->type) {
1301 case C_STRING:
1302 sfree(i->sval);
1303 i->sval = dupstr([[[(id)cfg_controls[k+1] cell]
1304 title] UTF8String]);
1305 k += 2;
1306 break;
1307 case C_BOOLEAN:
1308 i->ival = [(id)cfg_controls[k] state] == NSOnState;
1309 k++;
1310 break;
1311 case C_CHOICES:
1312 i->ival = [(id)cfg_controls[k+1] indexOfSelectedItem];
1313 k += 2;
1314 break;
1315 }
1316 }
1317
1318 error = midend_set_config(me, cfg_which, cfg);
1319 if (error) {
1320 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1321 [alert addButtonWithTitle:@"Bah"];
1322 [alert setInformativeText:[NSString stringWithUTF8String:error]];
1323 [alert beginSheetModalForWindow:self modalDelegate:nil
1324 didEndSelector:NULL contextInfo:nil];
1325 } else {
1326 midend_new_game(me);
1327 [self resizeForNewGameParams];
1328 [self updateTypeMenuTick];
1329 }
1330 }
1331 sfree(cfg_controls);
1332 cfg_controls = NULL;
1333}
1334- (void)sheetOKButton:(id)sender
1335{
1336 [self sheetEndWithStatus:YES];
1337}
1338- (void)sheetCancelButton:(id)sender
1339{
1340 [self sheetEndWithStatus:NO];
1341}
1342
1343- (void)setStatusLine:(char *)text
1344{
1345 [[status cell] setTitle:[NSString stringWithUTF8String:text]];
1346}
1347
1348@end
1349
1350/*
1351 * Drawing routines called by the midend.
1352 */
1353static void osx_draw_polygon(void *handle, int *coords, int npoints,
1354 int fillcolour, int outlinecolour)
1355{
1356 frontend *fe = (frontend *)handle;
1357 NSBezierPath *path = [NSBezierPath bezierPath];
1358 int i;
1359
1360 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
1361
1362 for (i = 0; i < npoints; i++) {
1363 NSPoint p = { coords[i*2] + 0.5, fe->h - coords[i*2+1] - 0.5 };
1364 if (i == 0)
1365 [path moveToPoint:p];
1366 else
1367 [path lineToPoint:p];
1368 }
1369
1370 [path closePath];
1371
1372 if (fillcolour >= 0) {
1373 assert(fillcolour >= 0 && fillcolour < fe->ncolours);
1374 [fe->colours[fillcolour] set];
1375 [path fill];
1376 }
1377
1378 assert(outlinecolour >= 0 && outlinecolour < fe->ncolours);
1379 [fe->colours[outlinecolour] set];
1380 [path stroke];
1381}
1382static void osx_draw_circle(void *handle, int cx, int cy, int radius,
1383 int fillcolour, int outlinecolour)
1384{
1385 frontend *fe = (frontend *)handle;
1386 NSBezierPath *path = [NSBezierPath bezierPath];
1387
1388 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
1389
1390 [path appendBezierPathWithArcWithCenter:NSMakePoint(cx+0.5, fe->h-cy-0.5)
1391 radius:radius startAngle:0.0 endAngle:360.0];
1392
1393 [path closePath];
1394
1395 if (fillcolour >= 0) {
1396 assert(fillcolour >= 0 && fillcolour < fe->ncolours);
1397 [fe->colours[fillcolour] set];
1398 [path fill];
1399 }
1400
1401 assert(outlinecolour >= 0 && outlinecolour < fe->ncolours);
1402 [fe->colours[outlinecolour] set];
1403 [path stroke];
1404}
1405static void osx_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
1406{
1407 frontend *fe = (frontend *)handle;
1408 NSBezierPath *path = [NSBezierPath bezierPath];
1409 NSPoint p1 = { x1 + 0.5, fe->h - y1 - 0.5 };
1410 NSPoint p2 = { x2 + 0.5, fe->h - y2 - 0.5 };
1411
1412 [[NSGraphicsContext currentContext] setShouldAntialias:NO];
1413
1414 assert(colour >= 0 && colour < fe->ncolours);
1415 [fe->colours[colour] set];
1416
1417 [path moveToPoint:p1];
1418 [path lineToPoint:p2];
1419 [path stroke];
1420 NSRectFill(NSMakeRect(x1, fe->h-y1-1, 1, 1));
1421 NSRectFill(NSMakeRect(x2, fe->h-y2-1, 1, 1));
1422}
1423
1424static void osx_draw_thick_line(
1425 void *handle, float thickness,
1426 float x1, float y1,
1427 float x2, float y2,
1428 int colour)
1429{
1430 frontend *fe = (frontend *)handle;
1431 NSBezierPath *path = [NSBezierPath bezierPath];
1432
1433 assert(colour >= 0 && colour < fe->ncolours);
1434 [fe->colours[colour] set];
1435 [[NSGraphicsContext currentContext] setShouldAntialias: YES];
1436 [path setLineWidth: thickness];
1437 [path setLineCapStyle: NSButtLineCapStyle];
1438 [path moveToPoint: NSMakePoint(x1, fe->h-y1)];
1439 [path lineToPoint: NSMakePoint(x2, fe->h-y2)];
1440 [path stroke];
1441}
1442
1443static void osx_draw_rect(void *handle, int x, int y, int w, int h, int colour)
1444{
1445 frontend *fe = (frontend *)handle;
1446 NSRect r = { {x, fe->h - y - h}, {w,h} };
1447
1448 [[NSGraphicsContext currentContext] setShouldAntialias:NO];
1449
1450 assert(colour >= 0 && colour < fe->ncolours);
1451 [fe->colours[colour] set];
1452
1453 NSRectFill(r);
1454}
1455static void osx_draw_text(void *handle, int x, int y, int fonttype,
1456 int fontsize, int align, int colour, char *text)
1457{
1458 frontend *fe = (frontend *)handle;
1459 NSString *string = [NSString stringWithUTF8String:text];
1460 NSDictionary *attr;
1461 NSFont *font;
1462 NSSize size;
1463 NSPoint point;
1464
1465 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
1466
1467 assert(colour >= 0 && colour < fe->ncolours);
1468
1469 if (fonttype == FONT_FIXED)
1470 font = [NSFont userFixedPitchFontOfSize:fontsize];
1471 else
1472 font = [NSFont userFontOfSize:fontsize];
1473
1474 attr = [NSDictionary dictionaryWithObjectsAndKeys:
1475 fe->colours[colour], NSForegroundColorAttributeName,
1476 font, NSFontAttributeName, nil];
1477
1478 point.x = x;
1479 point.y = fe->h - y;
1480
1481 size = [string sizeWithAttributes:attr];
1482 if (align & ALIGN_HRIGHT)
1483 point.x -= size.width;
1484 else if (align & ALIGN_HCENTRE)
1485 point.x -= size.width / 2;
1486 if (align & ALIGN_VCENTRE)
1487 point.y -= size.height / 2;
1488
1489 [string drawAtPoint:point withAttributes:attr];
1490}
1491static char *osx_text_fallback(void *handle, const char *const *strings,
1492 int nstrings)
1493{
1494 /*
1495 * We assume OS X can cope with any UTF-8 likely to be emitted
1496 * by a puzzle.
1497 */
1498 return dupstr(strings[0]);
1499}
1500struct blitter {
1501 int w, h;
1502 int x, y;
1503 NSImage *img;
1504};
1505static blitter *osx_blitter_new(void *handle, int w, int h)
1506{
1507 blitter *bl = snew(blitter);
1508 bl->x = bl->y = -1;
1509 bl->w = w;
1510 bl->h = h;
1511 bl->img = [[NSImage alloc] initWithSize:NSMakeSize(w, h)];
1512 return bl;
1513}
1514static void osx_blitter_free(void *handle, blitter *bl)
1515{
1516 [bl->img release];
1517 sfree(bl);
1518}
1519static void osx_blitter_save(void *handle, blitter *bl, int x, int y)
1520{
1521 frontend *fe = (frontend *)handle;
1522 int sx, sy, sX, sY, dx, dy, dX, dY;
1523 [fe->image unlockFocus];
1524 [bl->img lockFocus];
1525
1526 /*
1527 * Find the intersection of the source and destination rectangles,
1528 * so as to avoid trying to copy from outside the source image,
1529 * which GNUstep dislikes.
1530 *
1531 * Lower-case x,y coordinates are bottom left box corners;
1532 * upper-case X,Y are the top right.
1533 */
1534 sx = x; sy = fe->h - y - bl->h;
1535 sX = sx + bl->w; sY = sy + bl->h;
1536 dx = dy = 0;
1537 dX = bl->w; dY = bl->h;
1538 if (sx < 0) {
1539 dx += -sx;
1540 sx = 0;
1541 }
1542 if (sy < 0) {
1543 dy += -sy;
1544 sy = 0;
1545 }
1546 if (sX > fe->w) {
1547 dX -= (sX - fe->w);
1548 sX = fe->w;
1549 }
1550 if (sY > fe->h) {
1551 dY -= (sY - fe->h);
1552 sY = fe->h;
1553 }
1554
1555 [fe->image drawInRect:NSMakeRect(dx, dy, dX-dx, dY-dy)
1556 fromRect:NSMakeRect(sx, sy, sX-sx, sY-sy)
1557 operation:NSCompositeCopy fraction:1.0];
1558 [bl->img unlockFocus];
1559 [fe->image lockFocus];
1560 bl->x = x;
1561 bl->y = y;
1562}
1563static void osx_blitter_load(void *handle, blitter *bl, int x, int y)
1564{
1565 frontend *fe = (frontend *)handle;
1566 if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
1567 x = bl->x;
1568 y = bl->y;
1569 }
1570 [bl->img drawInRect:NSMakeRect(x, fe->h - y - bl->h, bl->w, bl->h)
1571 fromRect:NSMakeRect(0, 0, bl->w, bl->h)
1572 operation:NSCompositeCopy fraction:1.0];
1573}
1574static void osx_draw_update(void *handle, int x, int y, int w, int h)
1575{
1576 frontend *fe = (frontend *)handle;
1577 [fe->view setNeedsDisplayInRect:NSMakeRect(x, fe->h - y - h, w, h)];
1578}
1579static void osx_clip(void *handle, int x, int y, int w, int h)
1580{
1581 frontend *fe = (frontend *)handle;
1582 NSRect r = { {x, fe->h - y - h}, {w, h} };
1583
1584 if (!fe->clipped)
1585 [[NSGraphicsContext currentContext] saveGraphicsState];
1586 [NSBezierPath clipRect:r];
1587 fe->clipped = TRUE;
1588}
1589static void osx_unclip(void *handle)
1590{
1591 frontend *fe = (frontend *)handle;
1592 if (fe->clipped)
1593 [[NSGraphicsContext currentContext] restoreGraphicsState];
1594 fe->clipped = FALSE;
1595}
1596static void osx_start_draw(void *handle)
1597{
1598 frontend *fe = (frontend *)handle;
1599 [fe->image lockFocus];
1600 fe->clipped = FALSE;
1601}
1602static void osx_end_draw(void *handle)
1603{
1604 frontend *fe = (frontend *)handle;
1605 [fe->image unlockFocus];
1606}
1607static void osx_status_bar(void *handle, char *text)
1608{
1609 frontend *fe = (frontend *)handle;
1610 [fe->window setStatusLine:text];
1611}
1612
1613const struct drawing_api osx_drawing = {
1614 osx_draw_text,
1615 osx_draw_rect,
1616 osx_draw_line,
1617 osx_draw_polygon,
1618 osx_draw_circle,
1619 osx_draw_update,
1620 osx_clip,
1621 osx_unclip,
1622 osx_start_draw,
1623 osx_end_draw,
1624 osx_status_bar,
1625 osx_blitter_new,
1626 osx_blitter_free,
1627 osx_blitter_save,
1628 osx_blitter_load,
1629 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
1630 NULL, NULL, /* line_width, line_dotted */
1631 osx_text_fallback,
1632 osx_draw_thick_line,
1633};
1634
1635void deactivate_timer(frontend *fe)
1636{
1637 [fe->window deactivateTimer];
1638}
1639void activate_timer(frontend *fe)
1640{
1641 [fe->window activateTimer];
1642}
1643
1644/* ----------------------------------------------------------------------
1645 * AppController: the object which receives the messages from all
1646 * menu selections that aren't standard OS X functions.
1647 */
1648@interface AppController : NSObject <NSApplicationDelegate>
1649{
1650}
1651- (void)newGameWindow:(id)sender;
1652- (void)about:(id)sender;
1653@end
1654
1655@implementation AppController
1656
1657- (void)newGameWindow:(id)sender
1658{
1659 const game *g = [sender getPayload];
1660 id win;
1661
1662 win = [[GameWindow alloc] initWithGame:g];
1663 [win makeKeyAndOrderFront:self];
1664}
1665
1666- (void)about:(id)sender
1667{
1668 id win;
1669
1670 win = [[AboutBox alloc] init];
1671 [win makeKeyAndOrderFront:self];
1672}
1673
1674- (NSMenu *)applicationDockMenu:(NSApplication *)sender
1675{
1676 NSMenu *menu = newmenu("Dock Menu");
1677 {
1678 int i;
1679
1680 for (i = 0; i < gamecount; i++) {
1681 id item =
1682 initnewitem([DataMenuItem allocWithZone:[NSMenu menuZone]],
1683 menu, gamelist[i]->name, "", self,
1684 @selector(newGameWindow:));
1685 [item setPayload:(void *)gamelist[i]];
1686 }
1687 }
1688 return menu;
1689}
1690
1691@end
1692
1693/* ----------------------------------------------------------------------
1694 * Main program. Constructs the menus and runs the application.
1695 */
1696int main(int argc, char **argv)
1697{
1698 NSAutoreleasePool *pool;
1699 NSMenu *menu;
1700 AppController *controller;
1701 NSImage *icon;
1702
1703 pool = [[NSAutoreleasePool alloc] init];
1704
1705 icon = [NSImage imageNamed:@"NSApplicationIcon"];
1706 app = [NSApplication sharedApplication];
1707 [app setApplicationIconImage:icon];
1708
1709 controller = [[[AppController alloc] init] autorelease];
1710 [app setDelegate:controller];
1711
1712 [app setMainMenu: newmenu("Main Menu")];
1713
1714 menu = newsubmenu([app mainMenu], "Apple Menu");
1715 newitem(menu, "About Puzzles", "", NULL, @selector(about:));
1716 [menu addItem:[NSMenuItem separatorItem]];
1717 [app setServicesMenu:newsubmenu(menu, "Services")];
1718 [menu addItem:[NSMenuItem separatorItem]];
1719 newitem(menu, "Hide Puzzles", "h", app, @selector(hide:));
1720 newitem(menu, "Hide Others", "o-h", app, @selector(hideOtherApplications:));
1721 newitem(menu, "Show All", "", app, @selector(unhideAllApplications:));
1722 [menu addItem:[NSMenuItem separatorItem]];
1723 newitem(menu, "Quit", "q", app, @selector(terminate:));
1724 [app setAppleMenu: menu];
1725
1726 menu = newsubmenu([app mainMenu], "File");
1727 newitem(menu, "Open", "o", NULL, @selector(loadSavedGame:));
1728 newitem(menu, "Save As", "s", NULL, @selector(saveGame:));
1729 newitem(menu, "New Game", "n", NULL, @selector(newGame:));
1730 newitem(menu, "Restart Game", "r", NULL, @selector(restartGame:));
1731 newitem(menu, "Specific Game", "", NULL, @selector(specificGame:));
1732 newitem(menu, "Specific Random Seed", "", NULL,
1733 @selector(specificRandomGame:));
1734 [menu addItem:[NSMenuItem separatorItem]];
1735 {
1736 NSMenu *submenu = newsubmenu(menu, "New Window");
1737 int i;
1738
1739 for (i = 0; i < gamecount; i++) {
1740 id item =
1741 initnewitem([DataMenuItem allocWithZone:[NSMenu menuZone]],
1742 submenu, gamelist[i]->name, "", controller,
1743 @selector(newGameWindow:));
1744 [item setPayload:(void *)gamelist[i]];
1745 }
1746 }
1747 [menu addItem:[NSMenuItem separatorItem]];
1748 newitem(menu, "Close", "w", NULL, @selector(performClose:));
1749
1750 menu = newsubmenu([app mainMenu], "Edit");
1751 newitem(menu, "Undo", "z", NULL, @selector(undoMove:));
1752 newitem(menu, "Redo", "S-z", NULL, @selector(redoMove:));
1753 [menu addItem:[NSMenuItem separatorItem]];
1754 newitem(menu, "Cut", "x", NULL, @selector(cut:));
1755 newitem(menu, "Copy", "c", NULL, @selector(copy:));
1756 newitem(menu, "Paste", "v", NULL, @selector(paste:));
1757 [menu addItem:[NSMenuItem separatorItem]];
1758 newitem(menu, "Solve", "S-s", NULL, @selector(solveGame:));
1759
1760 menu = newsubmenu([app mainMenu], "Type");
1761 typemenu = menu;
1762 newitem(menu, "Custom", "", NULL, @selector(customGameType:));
1763
1764 menu = newsubmenu([app mainMenu], "Window");
1765 [app setWindowsMenu: menu];
1766 newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:));
1767
1768 menu = newsubmenu([app mainMenu], "Help");
1769 newitem(menu, "Puzzles Help", "?", app, @selector(showHelp:));
1770
1771 [app run];
1772 [pool release];
1773
1774 return 0;
1775}
diff --git a/apps/plugins/puzzles/src/padtoolbar.bmp b/apps/plugins/puzzles/src/padtoolbar.bmp
new file mode 100644
index 0000000000..46c3e0dcae
--- /dev/null
+++ b/apps/plugins/puzzles/src/padtoolbar.bmp
Binary files differ
diff --git a/apps/plugins/puzzles/src/palisade.R b/apps/plugins/puzzles/src/palisade.R
new file mode 100644
index 0000000000..de4bd83126
--- /dev/null
+++ b/apps/plugins/puzzles/src/palisade.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3PALISADE_EXTRA = divvy dsf
4
5palisade : [X] GTK COMMON palisade PALISADE_EXTRA palisade-icon|no-icon
6
7palisade : [G] WINDOWS COMMON palisade PALISADE_EXTRA palisade.res|noicon.res
8
9ALL += palisade[COMBINED] PALISADE_EXTRA
10
11!begin am gtk
12GAMES += palisade
13!end
14
15!begin >list.c
16 A(palisade) \
17!end
18
19!begin >gamedesc.txt
20palisade:palisade.exe:Palisade:Grid-division puzzle:Divide the grid into equal-sized areas in accordance with the clues.
21!end
diff --git a/apps/plugins/puzzles/src/palisade.c b/apps/plugins/puzzles/src/palisade.c
new file mode 100644
index 0000000000..b9d578d0f7
--- /dev/null
+++ b/apps/plugins/puzzles/src/palisade.c
@@ -0,0 +1,1383 @@
1/* -*- indent-tabs-mode: nil; tab-width: 1000 -*- */
2
3/*
4 * palisade.c: Nikoli's `Five Cells' puzzle.
5 *
6 * See http://nikoli.co.jp/en/puzzles/five_cells.html
7 */
8
9/* TODO:
10 *
11 * - better solver: implement the sketched-out deductions
12 *
13 * - improve the victory flash?
14 * - the LINE_NOs look ugly against COL_FLASH.
15 * - white-blink the edges (instead), a la loopy?
16 */
17
18#include <assert.h>
19#include <ctype.h>
20#include <stdarg.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "puzzles.h"
26
27#define setmem(ptr, byte, len) memset((ptr), (byte), (len) * sizeof (ptr)[0])
28#define scopy(dst, src, len) memcpy((dst), (src), (len) * sizeof (dst)[0])
29#define dupmem(p, n) memcpy(smalloc(n * sizeof (*p)), p, n * sizeof (*p))
30#define snewa(ptr, len) (ptr) = smalloc((len) * sizeof (*ptr))
31#define clone(ptr) (dupmem((ptr), 1))
32
33static char *string(int n, const char *fmt, ...)
34{
35 va_list va;
36 char *ret;
37 int m;
38 va_start(va, fmt);
39 m = vsprintf(snewa(ret, n + 1), fmt, va);
40 va_end(va);
41 if (m > n) fatal("memory corruption");
42 return ret;
43}
44
45struct game_params {
46 int w, h, k;
47};
48
49typedef char clue;
50typedef unsigned char borderflag;
51
52typedef struct shared_state {
53 game_params params;
54 clue *clues;
55 int refcount;
56} shared_state;
57
58struct game_state {
59 shared_state *shared;
60 borderflag *borders; /* length w*h */
61
62 unsigned int completed: 1;
63 unsigned int cheated: 1;
64};
65
66#define DEFAULT_PRESET 0
67static struct game_params presets[] = {
68 {5, 5, 5}, {8, 6, 6}, {10, 8, 8}, {15, 12, 10}
69 /* I definitely want 5x5n5 since that gives "Five Cells" its name.
70 * But how about the others? By which criteria do I choose? */
71};
72
73static game_params *default_params(void)
74{
75 return clone(&presets[DEFAULT_PRESET]);
76}
77
78static int game_fetch_preset(int i, char **name, game_params **params)
79{
80 if (i < 0 || i >= lenof(presets)) return FALSE;
81
82 *params = clone(&presets[i]);
83 *name = string(60, "%d x %d, regions of size %d",
84 presets[i].w, presets[i].h, presets[i].k);
85
86 return TRUE;
87}
88
89static void free_params(game_params *params)
90{
91 sfree(params);
92}
93
94static game_params *dup_params(const game_params *params)
95{
96 return clone(params);
97}
98
99static void decode_params(game_params *params, char const *string)
100{
101 params->w = params->h = params->k = atoi(string);
102 while (*string && isdigit((unsigned char)*string)) ++string;
103 if (*string == 'x') {
104 params->h = atoi(++string);
105 while (*string && isdigit((unsigned char)*string)) ++string;
106 }
107 if (*string == 'n') params->k = atoi(++string);
108}
109
110static char *encode_params(const game_params *params, int full)
111{
112 return string(40, "%dx%dn%d", params->w, params->h, params->k);
113}
114
115#define CONFIG(i, nm, ty, iv, sv) \
116 (ret[i].name = nm, ret[i].type = ty, ret[i].ival = iv, ret[i].sval = sv)
117
118static config_item *game_configure(const game_params *params)
119{
120 config_item *ret = snewn(4, config_item);
121
122 CONFIG(0, "Width", C_STRING, 0, string(20, "%d", params->w));
123 CONFIG(1, "Height", C_STRING, 0, string(20, "%d", params->h));
124 CONFIG(2, "Region size", C_STRING, 0, string(20, "%d", params->k));
125 CONFIG(3, NULL, C_END, 0, NULL);
126
127 return ret;
128}
129
130static game_params *custom_params(const config_item *cfg)
131{
132 game_params *params = snew(game_params);
133
134 params->w = atoi(cfg[0].sval);
135 params->h = atoi(cfg[1].sval);
136 params->k = atoi(cfg[2].sval);
137
138 return params;
139}
140
141/* +---+ << The one possible domino (up to symmetry). +---+---+
142 * | 3 | | 3 | 3 |
143 * | | If two dominos are adjacent as depicted here >> +---+---+
144 * | 3 | then it's ambiguous whether the edge between | 3 | 3 |
145 * +---+ the dominos is horizontal or vertical. +---+---+
146 */
147
148static char *validate_params(const game_params *params, int full)
149{
150 int w = params->w, h = params->h, k = params->k, wh = w * h;
151
152 if (k < 1) return "Region size must be at least one";
153 if (w < 1) return "Width must be at least one";
154 if (h < 1) return "Height must be at least one";
155 if (wh % k) return "Region size must divide grid area";
156
157 if (!full) return NULL; /* succeed partial validation */
158
159 /* MAYBE FIXME: we (just?) don't have the UI for winning these. */
160 if (k == wh) return "Region size must be less than the grid area";
161 assert (k < wh); /* or wh % k != 0 */
162
163 if (k == 2 && w != 1 && h != 1)
164 return "Region size can't be two unless width or height is one";
165
166 return NULL; /* succeed full validation */
167}
168
169/* --- Solver ------------------------------------------------------- */
170
171/* the solver may write at will to these arrays, but shouldn't free them */
172/* it's up to the client to dup/free as needed */
173typedef struct solver_ctx {
174 const game_params *params; /* also in shared_state */
175 clue *clues; /* also in shared_state */
176 borderflag *borders; /* also in game_state */
177 int *dsf; /* particular to the solver */
178} solver_ctx;
179
180/* Deductions:
181 *
182 * - If two adjacent clues do not have a border between them, this
183 * gives a lower limit on the size of their region (which is also an
184 * upper limit if both clues are 3). Rule out any non-border which
185 * would make its region either too large or too small.
186 *
187 * - If a clue, k, is adjacent to k borders or (4 - k) non-borders,
188 * the remaining edges incident to the clue are readily decided.
189 *
190 * - If a region has only one other region (e.g. square) to grow into
191 * and it's not of full size yet, grow it into that one region.
192 *
193 * - If two regions are adjacent and their combined size would be too
194 * large, put an edge between them.
195 *
196 * - If a border is adjacent to two non-borders, its last vertex-mate
197 * must also be a border. If a maybe-border is adjacent to three
198 * nonborders, the maybe-border is a non-border.
199 *
200 * - If a clue square is adjacent to several squares belonging to the
201 * same region, and enabling (disabling) those borders would violate
202 * the clue, those borders must be disabled (enabled).
203 *
204 * - If there's a path crossing only non-borders between two squares,
205 * the maybe-border between them is a non-border.
206 * (This is implicitly computed in the dsf representation)
207 */
208
209/* TODO deductions:
210 *
211 * If a vertex is adjacent to a LINE_YES and (4-3)*LINE_NO, at least
212 * one of the last two edges are LINE_YES. If they're adjacent to a
213 * 1, then the other two edges incident to that 1 are LINE_NO.
214 *
215 * For each square: set all as unknown, then for each k-omino and each
216 * way of placing it on that square, if that way is consistent with
217 * the board, mark its edges and interior as possible LINE_YES and
218 * LINE_NO, respectively. When all k-ominos are through, see what
219 * isn't possible and remove those impossibilities from the board.
220 * (Sounds pretty nasty for k > 4 or so.)
221 *
222 * A black-bordered subregion must have a size divisible by k. So,
223 * draw a graph with one node per dsf component and edges between
224 * those dsf components which have adjacent squares. Identify cut
225 * vertices and edges. If a cut-vertex-delimited component contains a
226 * number of squares not divisible by k, cut vertex not included, then
227 * the cut vertex must belong to the component. If it has exactly one
228 * edge _out_ of the component, the line(s) corresponding to that edge
229 * are all LINE_YES (i.e. a BORDER()).
230 * (This sounds complicated, but visually it is rather easy.)
231 *
232 * [Look at loopy and see how the at-least/-most k out of m edges
233 * thing is done. See how it is propagated across multiple squares.]
234 */
235
236#define EMPTY (~0)
237
238#define BIT(i) (1 << (i))
239#define BORDER(i) BIT(i)
240#define BORDER_U BORDER(0)
241#define BORDER_R BORDER(1)
242#define BORDER_D BORDER(2)
243#define BORDER_L BORDER(3)
244#define FLIP(i) ((i) ^ 2)
245#define BORDER_MASK (BORDER_U|BORDER_R|BORDER_D|BORDER_L)
246#define DISABLED(border) ((border) << 4)
247#define UNDISABLED(border) ((border) >> 4)
248
249static const int dx[4] = { 0, +1, 0, -1};
250static const int dy[4] = {-1, 0, +1, 0};
251static const int bitcount[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
252/* bitcount[x & BORDER_MASK] == number of enabled borders */
253
254#define COMPUTE_J (-1)
255
256static void connect(solver_ctx *ctx, int i, int j)
257{
258 dsf_merge(ctx->dsf, i, j);
259}
260
261static int connected(solver_ctx *ctx, int i, int j, int dir)
262{
263 if (j == COMPUTE_J) j = i + dx[dir] + ctx->params->w*dy[dir];
264 return dsf_canonify(ctx->dsf, i) == dsf_canonify(ctx->dsf, j);
265}
266
267static void disconnect(solver_ctx *ctx, int i, int j, int dir)
268{
269 if (j == COMPUTE_J) j = i + dx[dir] + ctx->params->w*dy[dir];
270 ctx->borders[i] |= BORDER(dir);
271 ctx->borders[j] |= BORDER(FLIP(dir));
272}
273
274static int disconnected(solver_ctx *ctx, int i, int j, int dir)
275{
276 assert (j == COMPUTE_J || j == i + dx[dir] + ctx->params->w*dy[dir]);
277 return ctx->borders[i] & BORDER(dir);
278}
279
280static int maybe(solver_ctx *ctx, int i, int j, int dir)
281{
282 assert (j == COMPUTE_J || j == i + dx[dir] + ctx->params->w*dy[dir]);
283 return !disconnected(ctx, i, j, dir) && !connected(ctx, i, j, dir);
284 /* the ordering is important: disconnected works for invalid
285 * squares (i.e. out of bounds), connected doesn't. */
286}
287
288static void solver_connected_clues_versus_region_size(solver_ctx *ctx)
289{
290 int w = ctx->params->w, h = ctx->params->h, wh = w*h, i, dir;
291
292 /* If i is connected to j and i has borders with p of the
293 * remaining three squares and j with q of the remaining three
294 * squares, then the region has size at least 1+(3-p) + 1+(3-q).
295 * If p = q = 3 then the region has size exactly 2. */
296
297 for (i = 0; i < wh; ++i) {
298 if (ctx->clues[i] == EMPTY) continue;
299 for (dir = 0; dir < 4; ++dir) {
300 int j = i + dx[dir] + w*dy[dir];
301 if (disconnected(ctx, i, j, dir)) continue;
302 if (ctx->clues[j] == EMPTY) continue;
303 if ((8 - ctx->clues[i] - ctx->clues[j] > ctx->params->k) ||
304 (ctx->clues[i] == 3 && ctx->clues[j] == 3 &&
305 ctx->params->k != 2))
306 {
307 disconnect(ctx, i, j, dir);
308 /* changed = TRUE, but this is a one-shot... */
309 }
310 }
311 }
312}
313
314static int solver_number_exhausted(solver_ctx *ctx)
315{
316 int w = ctx->params->w, h = ctx->params->h, wh = w*h, i, dir, off;
317 int changed = FALSE;
318
319 for (i = 0; i < wh; ++i) {
320 if (ctx->clues[i] == EMPTY) continue;
321
322 if (bitcount[(ctx->borders[i] & BORDER_MASK)] == ctx->clues[i]) {
323 for (dir = 0; dir < 4; ++dir) {
324 int j = i + dx[dir] + w*dy[dir];
325 if (!maybe(ctx, i, j, dir)) continue;
326 connect(ctx, i, j);
327 changed = TRUE;
328 }
329 continue;
330 }
331
332 for (off = dir = 0; dir < 4; ++dir) {
333 int j = i + dx[dir] + w*dy[dir];
334 if (!disconnected(ctx, i, j, dir) && connected(ctx, i, j, dir))
335 ++off; /* ^^^ bounds checking before ^^^^^ */
336 }
337
338 if (ctx->clues[i] == 4 - off)
339 for (dir = 0; dir < 4; ++dir) {
340 int j = i + dx[dir] + w*dy[dir];
341 if (!maybe(ctx, i, j, dir)) continue;
342 disconnect(ctx, i, j, dir);
343 changed = TRUE;
344 }
345 }
346
347 return changed;
348}
349
350static int solver_not_too_big(solver_ctx *ctx)
351{
352 int w = ctx->params->w, h = ctx->params->h, wh = w*h, i, dir;
353 int changed = FALSE;
354
355 for (i = 0; i < wh; ++i) {
356 int size = dsf_size(ctx->dsf, i);
357 for (dir = 0; dir < 4; ++dir) {
358 int j = i + dx[dir] + w*dy[dir];
359 if (!maybe(ctx, i, j, dir)) continue;
360 if (size + dsf_size(ctx->dsf, j) <= ctx->params->k) continue;
361 disconnect(ctx, i, j, dir);
362 changed = TRUE;
363 }
364 }
365
366 return changed;
367}
368
369static int solver_not_too_small(solver_ctx *ctx)
370{
371 int w = ctx->params->w, h = ctx->params->h, wh = w*h, i, dir;
372 int *outs, k = ctx->params->k, ci, changed = FALSE;
373
374 snewa(outs, wh);
375 setmem(outs, -1, wh);
376
377 for (i = 0; i < wh; ++i) {
378 ci = dsf_canonify(ctx->dsf, i);
379 if (dsf_size(ctx->dsf, ci) == k) continue;
380 for (dir = 0; dir < 4; ++dir) {
381 int j = i + dx[dir] + w*dy[dir];
382 if (!maybe(ctx, i, j, dir)) continue;
383 if (outs[ci] == -1) outs[ci] = dsf_canonify(ctx->dsf, j);
384 else if (outs[ci] != dsf_canonify(ctx->dsf, j)) outs[ci] = -2;
385 }
386 }
387
388 for (i = 0; i < wh; ++i) {
389 int j = outs[i];
390 if (i != dsf_canonify(ctx->dsf, i)) continue;
391 if (j < 0) continue;
392 connect(ctx, i, j); /* only one place for i to grow */
393 changed = TRUE;
394 }
395
396 sfree(outs);
397 return changed;
398}
399
400static int solver_no_dangling_edges(solver_ctx *ctx)
401{
402 int w = ctx->params->w, h = ctx->params->h, r, c;
403 int changed = FALSE;
404
405 /* for each vertex */
406 for (r = 1; r < h; ++r)
407 for (c = 1; c < w; ++c) {
408 int i = r * w + c, j = i - w - 1, noline = 0, dir;
409 int squares[4], e = -1, f = -1, de = -1, df = -1;
410
411 /* feels hacky: I align these with BORDER_[U0 R1 D2 L3] */
412 squares[1] = squares[2] = j;
413 squares[0] = squares[3] = i;
414
415 /* for each edge adjacent to the vertex */
416 for (dir = 0; dir < 4; ++dir)
417 if (!connected(ctx, squares[dir], COMPUTE_J, dir)) {
418 df = dir;
419 f = squares[df];
420 if (e != -1) continue;
421 e = f;
422 de = df;
423 } else ++noline;
424
425 if (4 - noline == 1) {
426 assert (e != -1);
427 disconnect(ctx, e, COMPUTE_J, de);
428 changed = TRUE;
429 continue;
430 }
431
432 if (4 - noline != 2) continue;
433
434 assert (e != -1);
435 assert (f != -1);
436
437 if (ctx->borders[e] & BORDER(de)) {
438 if (!(ctx->borders[f] & BORDER(df))) {
439 disconnect(ctx, f, COMPUTE_J, df);
440 changed = TRUE;
441 }
442 } else if (ctx->borders[f] & BORDER(df)) {
443 disconnect(ctx, e, COMPUTE_J, de);
444 changed = TRUE;
445 }
446 }
447
448 return changed;
449}
450
451static int solver_equivalent_edges(solver_ctx *ctx)
452{
453 int w = ctx->params->w, h = ctx->params->h, wh = w*h, i, dirj;
454 int changed = FALSE;
455
456 /* if a square is adjacent to two connected squares, the two
457 * borders (i,j) and (i,k) are either both on or both off. */
458
459 for (i = 0; i < wh; ++i) {
460 int n_on = 0, n_off = 0;
461 if (ctx->clues[i] < 1 || ctx->clues[i] > 3) continue;
462
463 if (ctx->clues[i] == 2 /* don't need it otherwise */)
464 for (dirj = 0; dirj < 4; ++dirj) {
465 int j = i + dx[dirj] + w*dy[dirj];
466 if (disconnected(ctx, i, j, dirj)) ++n_on;
467 else if (connected(ctx, i, j, dirj)) ++n_off;
468 }
469
470 for (dirj = 0; dirj < 4; ++dirj) {
471 int j = i + dx[dirj] + w*dy[dirj], dirk;
472 if (!maybe(ctx, i, j, dirj)) continue;
473
474 for (dirk = dirj + 1; dirk < 4; ++dirk) {
475 int k = i + dx[dirk] + w*dy[dirk];
476 if (!maybe(ctx, i, k, dirk)) continue;
477 if (!connected(ctx, j, k, -1)) continue;
478
479 if (n_on + 2 > ctx->clues[i]) {
480 connect(ctx, i, j);
481 connect(ctx, i, k);
482 changed = TRUE;
483 } else if (n_off + 2 > 4 - ctx->clues[i]) {
484 disconnect(ctx, i, j, dirj);
485 disconnect(ctx, i, k, dirk);
486 changed = TRUE;
487 }
488 }
489 }
490 }
491
492 return changed;
493}
494
495#define UNVISITED 6
496
497/* build connected components in `dsf', along the lines of `borders'. */
498static void dfs_dsf(int i, int w, borderflag *border, int *dsf, int black)
499{
500 int dir;
501 for (dir = 0; dir < 4; ++dir) {
502 int ii = i + dx[dir] + w*dy[dir], bdir = BORDER(dir);
503 if (black ? (border[i] & bdir) : !(border[i] & DISABLED(bdir)))
504 continue;
505 if (dsf[ii] != UNVISITED) continue;
506 dsf_merge(dsf, i, ii);
507 dfs_dsf(ii, w, border, dsf, black);
508 }
509}
510
511static int is_solved(const game_params *params, clue *clues,
512 borderflag *border)
513{
514 int w = params->w, h = params->h, wh = w*h, k = params->k;
515 int i, x, y;
516 int *dsf = snew_dsf(wh);
517
518 assert (dsf[0] == UNVISITED); /* check: UNVISITED and dsf.c match up */
519
520 /*
521 * A game is solved if:
522 *
523 * - the borders drawn on the grid divide it into connected
524 * components such that every square is in a component of the
525 * correct size
526 * - the borders also satisfy the clue set
527 */
528 for (i = 0; i < wh; ++i) {
529 if (dsf[i] == UNVISITED) dfs_dsf(i, params->w, border, dsf, TRUE);
530 if (dsf_size(dsf, i) != k) goto error;
531 if (clues[i] == EMPTY) continue;
532 if (clues[i] != bitcount[border[i] & BORDER_MASK]) goto error;
533 }
534
535 /*
536 * ... and thirdly:
537 *
538 * - there are no *stray* borders, in that every border is
539 * actually part of the division between two components.
540 * Otherwise you could cheat by finding a subdivision which did
541 * not *exceed* any clue square's counter, and then adding a
542 * few extra edges.
543 */
544 for (y = 0; y < h; y++) {
545 for (x = 0; x < w; x++) {
546 if (x+1 < w && (border[y*w+x] & BORDER_R) &&
547 dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, y*w+(x+1)))
548 goto error;
549 if (y+1 < h && (border[y*w+x] & BORDER_D) &&
550 dsf_canonify(dsf, y*w+x) == dsf_canonify(dsf, (y+1)*w+x))
551 goto error;
552 }
553 }
554
555 sfree(dsf);
556 return TRUE;
557
558error:
559 sfree(dsf);
560 return FALSE;
561}
562
563static int solver(const game_params *params, clue *clues, borderflag *borders)
564{
565 int w = params->w, h = params->h, wh = w*h, changed;
566 solver_ctx ctx;
567
568 ctx.params = params;
569 ctx.clues = clues;
570 ctx.borders = borders;
571 ctx.dsf = snew_dsf(wh);
572
573 solver_connected_clues_versus_region_size(&ctx); /* idempotent */
574 do {
575 changed = FALSE;
576 changed |= solver_number_exhausted(&ctx);
577 changed |= solver_not_too_big(&ctx);
578 changed |= solver_not_too_small(&ctx);
579 changed |= solver_no_dangling_edges(&ctx);
580 changed |= solver_equivalent_edges(&ctx);
581 } while (changed);
582
583 sfree(ctx.dsf);
584
585 return is_solved(params, clues, borders);
586}
587
588/* --- Generator ---------------------------------------------------- */
589
590static void init_borders(int w, int h, borderflag *borders)
591{
592 int r, c;
593 setmem(borders, 0, w*h);
594 for (c = 0; c < w; ++c) {
595 borders[c] |= BORDER_U;
596 borders[w*h-1 - c] |= BORDER_D;
597 }
598 for (r = 0; r < h; ++r) {
599 borders[r*w] |= BORDER_L;
600 borders[w*h-1 - r*w] |= BORDER_R;
601 }
602}
603
604#define OUT_OF_BOUNDS(x, y, w, h) \
605 ((x) < 0 || (x) >= (w) || (y) < 0 || (y) >= (h))
606
607#define xshuffle(ptr, len, rs) shuffle((ptr), (len), sizeof (ptr)[0], (rs))
608
609static char *new_game_desc(const game_params *params, random_state *rs,
610 char **aux, int interactive)
611{
612 int w = params->w, h = params->h, wh = w*h, k = params->k;
613
614 clue *numbers = snewn(wh + 1, clue), *p;
615 borderflag *rim = snewn(wh, borderflag);
616 borderflag *scratch_borders = snewn(wh, borderflag);
617
618 char *soln = snewa(*aux, wh + 2);
619 int *shuf = snewn(wh, int);
620 int *dsf = NULL, i, r, c;
621
622 int attempts = 0;
623
624 for (i = 0; i < wh; ++i) shuf[i] = i;
625 xshuffle(shuf, wh, rs);
626
627 init_borders(w, h, rim);
628
629 assert (!('@' & BORDER_MASK));
630 *soln++ = 'S';
631 soln[wh] = '\0';
632
633 do {
634 ++attempts;
635 setmem(soln, '@', wh);
636
637 sfree(dsf);
638 dsf = divvy_rectangle(w, h, k, rs);
639
640 for (r = 0; r < h; ++r)
641 for (c = 0; c < w; ++c) {
642 int i = r * w + c, dir;
643 numbers[i] = 0;
644 for (dir = 0; dir < 4; ++dir) {
645 int rr = r + dy[dir], cc = c + dx[dir], ii = rr * w + cc;
646 if (OUT_OF_BOUNDS(cc, rr, w, h) ||
647 dsf_canonify(dsf, i) != dsf_canonify(dsf, ii)) {
648 ++numbers[i];
649 soln[i] |= BORDER(dir);
650 }
651 }
652 }
653
654 scopy(scratch_borders, rim, wh);
655 } while (!solver(params, numbers, scratch_borders));
656
657 for (i = 0; i < wh; ++i) {
658 int j = shuf[i];
659 clue copy = numbers[j];
660
661 scopy(scratch_borders, rim, wh);
662 numbers[j] = EMPTY; /* strip away unnecssary clues */
663 if (!solver(params, numbers, scratch_borders))
664 numbers[j] = copy;
665 }
666
667 numbers[wh] = '\0';
668
669 sfree(scratch_borders);
670 sfree(rim);
671 sfree(shuf);
672 sfree(dsf);
673
674 p = numbers;
675 r = 0;
676 for (i = 0; i < wh; ++i) {
677 if (numbers[i] != EMPTY) {
678 while (r) {
679 while (r > 26) {
680 *p++ = 'z';
681 r -= 26;
682 }
683 *p++ = 'a'-1 + r;
684 r = 0;
685 }
686 *p++ = '0' + numbers[i];
687 } else ++r;
688 }
689 *p++ = '\0';
690
691 return sresize(numbers, p - numbers, clue);
692}
693
694static char *validate_desc(const game_params *params, const char *desc)
695{
696
697 int w = params->w, h = params->h, wh = w*h, squares = 0;
698
699 for (/* nop */; *desc; ++desc) {
700 if (islower((unsigned char)*desc)) {
701 squares += *desc - 'a' + 1;
702 } else if (isdigit((unsigned char)*desc)) {
703 if (*desc > '4') {
704 static char buf[] = "Invalid (too large) number: '5'";
705 assert (isdigit((unsigned char)buf[lenof(buf) - 3]));
706 buf[lenof(buf) - 3] = *desc; /* ... or 6, 7, 8, 9 :-) */
707 return buf;
708 }
709 ++squares;
710 } else if (isprint((unsigned char)*desc)) {
711 static char buf[] = "Invalid character in data: '?'";
712 buf[lenof(buf) - 3] = *desc;
713 return buf;
714 } else return "Invalid (unprintable) character in data";
715 }
716
717 if (squares > wh) return "Data describes too many squares";
718
719 return NULL;
720}
721
722static game_state *new_game(midend *me, const game_params *params,
723 const char *desc)
724{
725 int w = params->w, h = params->h, wh = w*h, i;
726 game_state *state = snew(game_state);
727
728 state->shared = snew(shared_state);
729 state->shared->refcount = 1;
730 state->shared->params = *params; /* struct copy */
731 snewa(state->shared->clues, wh);
732
733 setmem(state->shared->clues, EMPTY, wh);
734 for (i = 0; *desc; ++desc) {
735 if (isdigit((unsigned char)*desc)) state->shared->clues[i++] = *desc - '0';
736 else if (isalpha((unsigned char)*desc)) i += *desc - 'a' + 1;
737 }
738
739 snewa(state->borders, wh);
740 init_borders(w, h, state->borders);
741
742 state->completed = (params->k == wh);
743 state->cheated = FALSE;
744
745 return state;
746}
747
748static game_state *dup_game(const game_state *state)
749{
750 int wh = state->shared->params.w * state->shared->params.h;
751 game_state *ret = snew(game_state);
752
753 ret->borders = dupmem(state->borders, wh);
754
755 ret->shared = state->shared;
756 ++ret->shared->refcount;
757
758 ret->completed = state->completed;
759 ret->cheated = state->cheated;
760
761 return ret;
762}
763
764static void free_game(game_state *state)
765{
766 if (--state->shared->refcount == 0) {
767 sfree(state->shared->clues);
768 sfree(state->shared);
769 }
770 sfree(state->borders);
771 sfree(state);
772}
773
774static char *solve_game(const game_state *state, const game_state *currstate,
775 const char *aux, char **error)
776{
777 int w = state->shared->params.w, h = state->shared->params.h, wh = w*h;
778 borderflag *move;
779
780 if (aux) return dupstr(aux);
781
782 snewa(move, wh + 2);
783
784 move[0] = 'S';
785 init_borders(w, h, move + 1);
786 move[wh + 1] = '\0';
787
788 if (solver(&state->shared->params, state->shared->clues, move + 1)) {
789 int i;
790 for (i = 0; i < wh; i++)
791 move[i+1] |= '@'; /* turn into sensible ASCII */
792 return (char *) move;
793 }
794
795 *error = "Sorry, I can't solve this puzzle";
796 sfree(move);
797 return NULL;
798
799 {
800 /* compile-time-assert (borderflag is-a-kind-of char).
801 *
802 * depends on zero-size arrays being disallowed. GCC says
803 * ISO C forbids this, pointing to [-Werror=edantic]. Also,
804 * it depends on type-checking of (obviously) dead code. */
805 borderflag b[sizeof (borderflag) == sizeof (char)];
806 char c = b[0]; b[0] = c;
807 /* we could at least in principle put this anywhere, but it
808 * seems silly to not put it where the assumption is used. */
809 }
810}
811
812static int game_can_format_as_text_now(const game_params *params)
813{
814 return TRUE;
815}
816
817static char *game_text_format(const game_state *state)
818{
819 int w = state->shared->params.w, h = state->shared->params.h, r, c;
820 int cw = 4, ch = 2, gw = cw*w + 2, gh = ch * h + 1, len = gw * gh;
821 char *board;
822
823 setmem(snewa(board, len + 1), ' ', len);
824 for (r = 0; r < h; ++r) {
825 for (c = 0; c < w; ++c) {
826 int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2;
827 int i = r * w + c, clue = state->shared->clues[i];
828
829 if (clue != EMPTY) board[center] = '0' + clue;
830
831 board[cell] = '+';
832
833 if (state->borders[i] & BORDER_U)
834 setmem(board + cell + 1, '-', cw - 1);
835 else if (state->borders[i] & DISABLED(BORDER_U))
836 board[cell + cw / 2] = 'x';
837
838 if (state->borders[i] & BORDER_L)
839 board[cell + gw] = '|';
840 else if (state->borders[i] & DISABLED(BORDER_L))
841 board[cell + gw] = 'x';
842 }
843
844 for (c = 0; c < ch; ++c) {
845 board[(r*ch + c)*gw + gw - 2] = c ? '|' : '+';
846 board[(r*ch + c)*gw + gw - 1] = '\n';
847 }
848 }
849
850 scopy(board + len - gw, board, gw);
851 board[len] = '\0';
852
853 return board;
854}
855
856struct game_ui {
857 int x, y;
858 unsigned int show: 1;
859};
860
861static game_ui *new_ui(const game_state *state)
862{
863 game_ui *ui = snew(game_ui);
864 ui->x = ui->y = 0;
865 ui->show = FALSE;
866 return ui;
867}
868
869static void free_ui(game_ui *ui)
870{
871 sfree(ui);
872}
873
874static char *encode_ui(const game_ui *ui)
875{
876 return NULL;
877}
878
879static void decode_ui(game_ui *ui, const char *encoding)
880{
881 assert (encoding == NULL);
882}
883
884static void game_changed_state(game_ui *ui, const game_state *oldstate,
885 const game_state *newstate)
886{
887}
888
889typedef unsigned short dsflags;
890
891struct game_drawstate {
892 int tilesize;
893 dsflags *grid;
894};
895
896#define TILESIZE (ds->tilesize)
897#define MARGIN (ds->tilesize / 2)
898#define WIDTH (1 + (TILESIZE >= 16) + (TILESIZE >= 32) + (TILESIZE >= 64))
899#define CENTER ((ds->tilesize / 2) + WIDTH/2)
900
901#define FROMCOORD(x) (((x) - MARGIN) / TILESIZE)
902
903enum {MAYBE_LEFT, MAYBE_RIGHT, ON_LEFT, ON_RIGHT, OFF_LEFT, OFF_RIGHT};
904
905static char *interpret_move(const game_state *state, game_ui *ui,
906 const game_drawstate *ds, int x, int y, int button)
907{
908 int w = state->shared->params.w, h = state->shared->params.h;
909 int control = button & MOD_CTRL, shift = button & MOD_SHFT;
910
911 button &= ~MOD_MASK;
912
913 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
914 int gx = FROMCOORD(x), gy = FROMCOORD(y), possible = BORDER_MASK;
915 int px = (x - MARGIN) % TILESIZE, py = (y - MARGIN) % TILESIZE;
916 int hx, hy, dir, i;
917
918 if (OUT_OF_BOUNDS(gx, gy, w, h)) return NULL;
919
920 ui->x = gx;
921 ui->y = gy;
922
923 /* find edge closest to click point */
924 possible &=~ (2*px < TILESIZE ? BORDER_R : BORDER_L);
925 possible &=~ (2*py < TILESIZE ? BORDER_D : BORDER_U);
926 px = min(px, TILESIZE - px);
927 py = min(py, TILESIZE - py);
928 possible &=~ (px < py ? (BORDER_U|BORDER_D) : (BORDER_L|BORDER_R));
929
930 for (dir = 0; dir < 4 && BORDER(dir) != possible; ++dir);
931 if (dir == 4) return NULL; /* there's not exactly one such edge */
932
933 hx = gx + dx[dir];
934 hy = gy + dy[dir];
935
936 if (OUT_OF_BOUNDS(hx, hy, w, h)) return NULL;
937
938 ui->show = FALSE;
939
940 i = gy * w + gx;
941 switch ((button == RIGHT_BUTTON) |
942 ((state->borders[i] & BORDER(dir)) >> dir << 1) |
943 ((state->borders[i] & DISABLED(BORDER(dir))) >> dir >> 2)) {
944
945 case MAYBE_LEFT:
946 case ON_LEFT:
947 case ON_RIGHT:
948 return string(80, "F%d,%d,%dF%d,%d,%d",
949 gx, gy, BORDER(dir),
950 hx, hy, BORDER(FLIP(dir)));
951
952 case MAYBE_RIGHT:
953 case OFF_LEFT:
954 case OFF_RIGHT:
955 return string(80, "F%d,%d,%dF%d,%d,%d",
956 gx, gy, DISABLED(BORDER(dir)),
957 hx, hy, DISABLED(BORDER(FLIP(dir))));
958 }
959 }
960
961 if (IS_CURSOR_MOVE(button)) {
962 ui->show = TRUE;
963 if (control || shift) {
964 borderflag flag = 0, newflag;
965 int dir, i = ui->y * w + ui->x;
966 x = ui->x;
967 y = ui->y;
968 move_cursor(button, &x, &y, w, h, FALSE);
969 if (OUT_OF_BOUNDS(x, y, w, h)) return NULL;
970
971 for (dir = 0; dir < 4; ++dir)
972 if (dx[dir] == x - ui->x && dy[dir] == y - ui->y) break;
973 if (dir == 4) return NULL; /* how the ... ?! */
974
975 if (control) flag |= BORDER(dir);
976 if (shift) flag |= DISABLED(BORDER(dir));
977
978 newflag = state->borders[i] ^ flag;
979 if (newflag & BORDER(dir) && newflag & DISABLED(BORDER(dir)))
980 return NULL;
981
982 newflag = 0;
983 if (control) newflag |= BORDER(FLIP(dir));
984 if (shift) newflag |= DISABLED(BORDER(FLIP(dir)));
985 return string(80, "F%d,%d,%dF%d,%d,%d",
986 ui->x, ui->y, flag, x, y, newflag);
987 } else {
988 move_cursor(button, &ui->x, &ui->y, w, h, FALSE);
989 return "";
990 }
991 }
992
993 return NULL;
994}
995
996static game_state *execute_move(const game_state *state, const char *move)
997{
998 int w = state->shared->params.w, h = state->shared->params.h, wh = w * h;
999 game_state *ret = dup_game(state);
1000 int nchars, x, y, flag;
1001
1002 if (*move == 'S') {
1003 int i;
1004 ++move;
1005 for (i = 0; i < wh && move[i]; ++i)
1006 ret->borders[i] =
1007 (move[i] & BORDER_MASK) | DISABLED(~move[i] & BORDER_MASK);
1008 if (i < wh || move[i]) return NULL; /* leaks `ret', then we die */
1009 ret->cheated = ret->completed = TRUE;
1010 return ret;
1011 }
1012
1013 while (sscanf(move, "F%d,%d,%d%n", &x, &y, &flag, &nchars) == 3 &&
1014 !OUT_OF_BOUNDS(x, y, w, h)) {
1015 move += nchars;
1016 ret->borders[y*w + x] ^= flag;
1017 }
1018
1019 if (*move) return NULL; /* leaks `ret', then we die */
1020
1021 if (!ret->completed)
1022 ret->completed = is_solved(&ret->shared->params, ret->shared->clues,
1023 ret->borders);
1024
1025 return ret;
1026}
1027
1028/* --- Drawing routines --------------------------------------------- */
1029
1030static void game_compute_size(const game_params *params, int tilesize,
1031 int *x, int *y)
1032{
1033 *x = (params->w + 1) * tilesize;
1034 *y = (params->h + 1) * tilesize;
1035}
1036
1037static void game_set_size(drawing *dr, game_drawstate *ds,
1038 const game_params *params, int tilesize)
1039{
1040 ds->tilesize = tilesize;
1041}
1042
1043enum {
1044 COL_BACKGROUND,
1045 COL_FLASH,
1046 COL_GRID,
1047 COL_CLUE = COL_GRID,
1048 COL_LINE_YES = COL_GRID,
1049 COL_LINE_MAYBE,
1050 COL_LINE_NO,
1051 COL_ERROR,
1052
1053 NCOLOURS
1054};
1055
1056#define COLOUR(i, r, g, b) \
1057 ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b)))
1058#define DARKER 0.9F
1059
1060static float *game_colours(frontend *fe, int *ncolours)
1061{
1062 float *ret = snewn(3 * NCOLOURS, float);
1063
1064 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_FLASH);
1065
1066 COLOUR(COL_GRID, 0.0F, 0.0F, 0.0F); /* black */
1067 COLOUR(COL_ERROR, 1.0F, 0.0F, 0.0F); /* red */
1068
1069 COLOUR(COL_LINE_MAYBE, /* yellow */
1070 ret[COL_BACKGROUND*3 + 0] * DARKER,
1071 ret[COL_BACKGROUND*3 + 1] * DARKER,
1072 0.0F);
1073
1074 COLOUR(COL_LINE_NO,
1075 ret[COL_BACKGROUND*3 + 0] * DARKER,
1076 ret[COL_BACKGROUND*3 + 1] * DARKER,
1077 ret[COL_BACKGROUND*3 + 2] * DARKER);
1078
1079 *ncolours = NCOLOURS;
1080 return ret;
1081}
1082#undef COLOUR
1083
1084#define BORDER_ERROR(x) ((x) << 8)
1085#define F_ERROR_U BORDER_ERROR(BORDER_U) /* BIT( 8) */
1086#define F_ERROR_R BORDER_ERROR(BORDER_R) /* BIT( 9) */
1087#define F_ERROR_D BORDER_ERROR(BORDER_D) /* BIT(10) */
1088#define F_ERROR_L BORDER_ERROR(BORDER_L) /* BIT(11) */
1089#define F_ERROR_CLUE BIT(12)
1090#define F_FLASH BIT(13)
1091#define F_CURSOR BIT(14)
1092
1093static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1094{
1095 struct game_drawstate *ds = snew(struct game_drawstate);
1096
1097 ds->tilesize = 0;
1098 ds->grid = NULL;
1099
1100 return ds;
1101}
1102
1103static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1104{
1105 sfree(ds->grid);
1106 sfree(ds);
1107}
1108
1109#define COLOUR(border) \
1110 (flags & BORDER_ERROR((border)) ? COL_ERROR : \
1111 flags & (border) ? COL_LINE_YES : \
1112 flags & DISABLED((border)) ? COL_LINE_NO : \
1113 COL_LINE_MAYBE)
1114
1115static void draw_tile(drawing *dr, game_drawstate *ds, int r, int c,
1116 dsflags flags, int clue)
1117{
1118 int x = MARGIN + TILESIZE * c, y = MARGIN + TILESIZE * r;
1119
1120 clip(dr, x, y, TILESIZE + WIDTH, TILESIZE + WIDTH); /* { */
1121
1122 draw_rect(dr, x + WIDTH, y + WIDTH, TILESIZE - WIDTH, TILESIZE - WIDTH,
1123 (flags & F_FLASH ? COL_FLASH : COL_BACKGROUND));
1124
1125 if (flags & F_CURSOR)
1126 draw_rect_corners(dr, x + CENTER, y + CENTER, TILESIZE / 3, COL_GRID);
1127
1128 if (clue != EMPTY) {
1129 char buf[2];
1130 buf[0] = '0' + clue;
1131 buf[1] = '\0';
1132 draw_text(dr, x + CENTER, y + CENTER, FONT_VARIABLE,
1133 TILESIZE / 2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1134 (flags & F_ERROR_CLUE ? COL_ERROR : COL_CLUE), buf);
1135 }
1136
1137
1138#define ts TILESIZE
1139#define w WIDTH
1140 draw_rect(dr, x + w, y, ts - w, w, COLOUR(BORDER_U));
1141 draw_rect(dr, x + ts, y + w, w, ts - w, COLOUR(BORDER_R));
1142 draw_rect(dr, x + w, y + ts, ts - w, w, COLOUR(BORDER_D));
1143 draw_rect(dr, x, y + w, w, ts - w, COLOUR(BORDER_L));
1144#undef ts
1145#undef w
1146
1147 unclip(dr); /* } */
1148 draw_update(dr, x, y, TILESIZE + WIDTH, TILESIZE + WIDTH);
1149}
1150
1151#define FLASH_TIME 0.7F
1152
1153static void game_redraw(drawing *dr, game_drawstate *ds,
1154 const game_state *oldstate, const game_state *state,
1155 int dir, const game_ui *ui,
1156 float animtime, float flashtime)
1157{
1158 int w = state->shared->params.w, h = state->shared->params.h, wh = w*h;
1159 int r, c, i, flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
1160 int *black_border_dsf = snew_dsf(wh), *yellow_border_dsf = snew_dsf(wh);
1161 int k = state->shared->params.k;
1162
1163 if (!ds->grid) {
1164 char buf[40];
1165 int bgw = (w+1) * ds->tilesize, bgh = (h+1) * ds->tilesize;
1166 draw_rect(dr, 0, 0, bgw, bgh, COL_BACKGROUND);
1167
1168 for (r = 0; r <= h; ++r)
1169 for (c = 0; c <= w; ++c)
1170 draw_rect(dr, MARGIN + TILESIZE * c, MARGIN + TILESIZE * r,
1171 WIDTH, WIDTH, COL_GRID);
1172 draw_update(dr, 0, 0, bgw, bgh);
1173
1174 snewa(ds->grid, wh);
1175 setmem(ds->grid, ~0, wh);
1176
1177 sprintf(buf, "Region size: %d", state->shared->params.k);
1178 status_bar(dr, buf);
1179 }
1180
1181 for (i = 0; i < wh; ++i) {
1182 if (black_border_dsf[i] == UNVISITED)
1183 dfs_dsf(i, w, state->borders, black_border_dsf, TRUE);
1184 if (yellow_border_dsf[i] == UNVISITED)
1185 dfs_dsf(i, w, state->borders, yellow_border_dsf, FALSE);
1186 }
1187
1188 for (r = 0; r < h; ++r)
1189 for (c = 0; c < w; ++c) {
1190 int i = r * w + c, clue = state->shared->clues[i], flags, dir;
1191 int on = bitcount[state->borders[i] & BORDER_MASK];
1192 int off = bitcount[(state->borders[i] >> 4) & BORDER_MASK];
1193
1194 flags = state->borders[i];
1195
1196 if (flash) flags |= F_FLASH;
1197
1198 if (clue != EMPTY && (on > clue || clue > 4 - off))
1199 flags |= F_ERROR_CLUE;
1200
1201 if (ui->show && ui->x == c && ui->y == r)
1202 flags |= F_CURSOR;
1203
1204 /* border errors */
1205 for (dir = 0; dir < 4; ++dir) {
1206 int rr = r + dy[dir], cc = c + dx[dir], ii = rr * w + cc;
1207
1208 if (OUT_OF_BOUNDS(cc, rr, w, h)) continue;
1209
1210 /* we draw each border twice, except the outermost
1211 * big border, so we have to check for errors on
1212 * both sides of each border.*/
1213 if (/* region too large */
1214 ((dsf_size(yellow_border_dsf, i) > k ||
1215 dsf_size(yellow_border_dsf, ii) > k) &&
1216 (dsf_canonify(yellow_border_dsf, i) !=
1217 dsf_canonify(yellow_border_dsf, ii)))
1218
1219 ||
1220 /* region too small */
1221 ((dsf_size(black_border_dsf, i) < k ||
1222 dsf_size(black_border_dsf, ii) < k) &&
1223 dsf_canonify(black_border_dsf, i) !=
1224 dsf_canonify(black_border_dsf, ii))
1225
1226 ||
1227 /* dangling borders within a single region */
1228 ((state->borders[i] & BORDER(dir)) &&
1229 /* we know it's a single region because there's a
1230 * path crossing no border from i to ii... */
1231 (dsf_canonify(yellow_border_dsf, i) ==
1232 dsf_canonify(yellow_border_dsf, ii) ||
1233 /* or because any such border would be an error */
1234 (dsf_size(black_border_dsf, i) <= k &&
1235 dsf_canonify(black_border_dsf, i) ==
1236 dsf_canonify(black_border_dsf, ii)))))
1237
1238 flags |= BORDER_ERROR(BORDER(dir));
1239 }
1240
1241 if (flags == ds->grid[i]) continue;
1242 ds->grid[i] = flags;
1243 draw_tile(dr, ds, r, c, ds->grid[i], clue);
1244 }
1245
1246 sfree(black_border_dsf);
1247 sfree(yellow_border_dsf);
1248}
1249
1250static float game_anim_length(const game_state *oldstate,
1251 const game_state *newstate,
1252 int dir, game_ui *ui)
1253{
1254 return 0.0F;
1255}
1256
1257static float game_flash_length(const game_state *oldstate,
1258 const game_state *newstate,
1259 int dir, game_ui *ui)
1260{
1261 if (newstate->completed && !newstate->cheated && !oldstate->completed)
1262 return FLASH_TIME;
1263 return 0.0F;
1264}
1265
1266static int game_status(const game_state *state)
1267{
1268 return state->completed ? +1 : 0;
1269}
1270
1271static int game_timing_state(const game_state *state, game_ui *ui)
1272{
1273 assert (!"this shouldn't get called");
1274 return 0; /* placate optimiser */
1275}
1276
1277static void game_print_size(const game_params *params, float *x, float *y)
1278{
1279 int pw, ph;
1280
1281 game_compute_size(params, 700, &pw, &ph); /* 7mm, like loopy */
1282
1283 *x = pw / 100.0F;
1284 *y = ph / 100.0F;
1285}
1286
1287static void print_line(drawing *dr, int x1, int y1, int x2, int y2,
1288 int colour, int full)
1289{
1290 if (!full) {
1291 int i, subdivisions = 8;
1292 for (i = 1; i < subdivisions; ++i) {
1293 int x = (x1 * (subdivisions - i) + x2 * i) / subdivisions;
1294 int y = (y1 * (subdivisions - i) + y2 * i) / subdivisions;
1295 draw_circle(dr, x, y, 3, colour, colour);
1296 }
1297 } else draw_line(dr, x1, y1, x2, y2, colour);
1298}
1299
1300static void game_print(drawing *dr, const game_state *state, int tilesize)
1301{
1302 int w = state->shared->params.w, h = state->shared->params.h;
1303 int ink = print_mono_colour(dr, 0);
1304 game_drawstate for_tilesize_macros, *ds = &for_tilesize_macros;
1305 int r, c;
1306
1307 ds->tilesize = tilesize;
1308
1309 for (r = 0; r < h; ++r)
1310 for (c = 0; c < w; ++c) {
1311 int x = MARGIN + TILESIZE * c, y = MARGIN + TILESIZE * r;
1312 int i = r * w + c, clue = state->shared->clues[i];
1313
1314 if (clue != EMPTY) {
1315 char buf[2];
1316 buf[0] = '0' + clue;
1317 buf[1] = '\0';
1318 draw_text(dr, x + CENTER, y + CENTER, FONT_VARIABLE,
1319 TILESIZE / 2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1320 ink, buf);
1321 }
1322
1323#define ts TILESIZE
1324#define FULL(DIR) (state->borders[i] & (BORDER_ ## DIR))
1325 print_line(dr, x, y, x + ts, y, ink, FULL(U));
1326 print_line(dr, x + ts, y, x + ts, y + ts, ink, FULL(R));
1327 print_line(dr, x, y + ts, x + ts, y + ts, ink, FULL(D));
1328 print_line(dr, x, y, x, y + ts, ink, FULL(L));
1329#undef ts
1330#undef FULL
1331 }
1332
1333 for (r = 1; r < h; ++r)
1334 for (c = 1; c < w; ++c) {
1335 int j = r * w + c, i = j - 1 - w;
1336 int x = MARGIN + TILESIZE * c, y = MARGIN + TILESIZE * r;
1337 if (state->borders[i] & (BORDER_D|BORDER_R)) continue;
1338 if (state->borders[j] & (BORDER_U|BORDER_L)) continue;
1339 draw_circle(dr, x, y, 3, ink, ink);
1340 }
1341}
1342
1343#ifdef COMBINED
1344#define thegame palisade
1345#endif
1346
1347const struct game thegame = {
1348 "Palisade", "games.palisade", "palisade",
1349 default_params,
1350 game_fetch_preset, NULL,
1351 decode_params,
1352 encode_params,
1353 free_params,
1354 dup_params,
1355 TRUE, game_configure, custom_params,
1356 validate_params,
1357 new_game_desc,
1358 validate_desc,
1359 new_game,
1360 dup_game,
1361 free_game,
1362 TRUE, solve_game,
1363 TRUE, game_can_format_as_text_now, game_text_format,
1364 new_ui,
1365 free_ui,
1366 encode_ui,
1367 decode_ui,
1368 game_changed_state,
1369 interpret_move,
1370 execute_move,
1371 48, game_compute_size, game_set_size,
1372 game_colours,
1373 game_new_drawstate,
1374 game_free_drawstate,
1375 game_redraw,
1376 game_anim_length,
1377 game_flash_length,
1378 game_status,
1379 TRUE, FALSE, game_print_size, game_print,
1380 TRUE, /* wants_statusbar */
1381 FALSE, game_timing_state,
1382 0, /* flags */
1383};
diff --git a/apps/plugins/puzzles/src/palisade.html b/apps/plugins/puzzles/src/palisade.html
new file mode 100644
index 0000000000..c0f19c5f5c
--- /dev/null
+++ b/apps/plugins/puzzles/src/palisade.html
@@ -0,0 +1,54 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Palisade</title>
7<link rel="previous" href="tracks.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="licence.html">
12</head>
13<body>
14<p><a href="tracks.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="licence.html">Next</a></p>
15<h1><a name="C41"></a>Chapter 41: <a name="i0"></a>Palisade</h1>
16<p>
17You're given a grid of squares, some of which contain numbers. Your goal is to subdivide the grid into contiguous regions, all of the same (given) size, such that each square containing a number is adjacent to exactly that many edges (including those between the inside and the outside of the grid).
18</p>
19<p>
20Credit for this puzzle goes to <a name="i1"></a>Nikoli, who call it &#8216;Five Cells&#8217;. <a href="#p0">[22]</a>.
21</p>
22<p>
23Palisade was contributed to this collection by Jonas K&#246;lker.
24</p>
25<p><a name="p0"></a>
26[22] <a href="http://nikoli.co.jp/en/puzzles/five_cells.html"><code>http://nikoli.co.jp/en/puzzles/five_cells.html</code></a>
27</p>
28<h2><a name="S41.1"></a>41.1 <a name="i2"></a>Palisade controls</h2>
29<p>
30Left-click to place an edge. Right-click to indicate &#8216;no edge&#8217;. Alternatively, the arrow keys will move a keyboard cursor. Holding Control while pressing an arrow key will place an edge. Press Shift-arrowkey to switch off an edge. Repeat an action to perform its inverse.
31</p>
32<p>
33(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
34</p>
35<h2><a name="S41.2"></a>41.2 <a name="i3"></a>Palisade parameters</h2>
36<p>
37These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
38</p>
39<dl><dt>
40<em>Width</em>, <em>Height</em>
41</dt>
42<dd>
43Size of grid in squares.
44</dd>
45<dt>
46<em>Region size</em>
47</dt>
48<dd>
49The size of the regions into which the grid must be subdivided.
50</dd>
51</dl>
52
53<hr><address></address></body>
54</html>
diff --git a/apps/plugins/puzzles/src/pattern.R b/apps/plugins/puzzles/src/pattern.R
new file mode 100644
index 0000000000..d6695715f9
--- /dev/null
+++ b/apps/plugins/puzzles/src/pattern.R
@@ -0,0 +1,25 @@
1# -*- makefile -*-
2
3pattern : [X] GTK COMMON pattern pattern-icon|no-icon
4
5pattern : [G] WINDOWS COMMON pattern pattern.res|noicon.res
6
7patternsolver : [U] pattern[STANDALONE_SOLVER] STANDALONE
8patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE
9
10patternpicture : [U] pattern[STANDALONE_PICTURE_GENERATOR] STANDALONE
11patternpicture : [C] pattern[STANDALONE_PICTURE_GENERATOR] STANDALONE
12
13ALL += pattern[COMBINED]
14
15!begin am gtk
16GAMES += pattern
17!end
18
19!begin >list.c
20 A(pattern) \
21!end
22
23!begin >gamedesc.txt
24pattern:pattern.exe:Pattern:Pattern puzzle:Fill in the pattern in the grid, given only the lengths of runs of black squares.
25!end
diff --git a/apps/plugins/puzzles/src/pattern.c b/apps/plugins/puzzles/src/pattern.c
new file mode 100644
index 0000000000..9a74e55318
--- /dev/null
+++ b/apps/plugins/puzzles/src/pattern.c
@@ -0,0 +1,2255 @@
1/*
2 * pattern.c: the pattern-reconstruction game known as `nonograms'.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13
14enum {
15 COL_BACKGROUND,
16 COL_EMPTY,
17 COL_FULL,
18 COL_TEXT,
19 COL_UNKNOWN,
20 COL_GRID,
21 COL_CURSOR,
22 COL_ERROR,
23 NCOLOURS
24};
25
26#define PREFERRED_TILE_SIZE 24
27#define TILE_SIZE (ds->tilesize)
28#define BORDER (3 * TILE_SIZE / 4)
29#define TLBORDER(d) ( (d) / 5 + 2 )
30#define GUTTER (TILE_SIZE / 2)
31
32#define FROMCOORD(d, x) \
33 ( ((x) - (BORDER + GUTTER + TILE_SIZE * TLBORDER(d))) / TILE_SIZE )
34
35#define SIZE(d) (2*BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (d)))
36#define GETTILESIZE(d, w) ((double)w / (2.0 + (double)TLBORDER(d) + (double)(d)))
37
38#define TOCOORD(d, x) (BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (x)))
39
40struct game_params {
41 int w, h;
42};
43
44#define GRID_UNKNOWN 2
45#define GRID_FULL 1
46#define GRID_EMPTY 0
47
48typedef struct game_state_common {
49 /* Parts of the game state that don't change during play. */
50 int w, h;
51 int rowsize;
52 int *rowdata, *rowlen;
53 unsigned char *immutable;
54 int refcount;
55} game_state_common;
56
57struct game_state {
58 game_state_common *common;
59 unsigned char *grid;
60 int completed, cheated;
61};
62
63#define FLASH_TIME 0.13F
64
65static game_params *default_params(void)
66{
67 game_params *ret = snew(game_params);
68
69 ret->w = ret->h = 15;
70
71 return ret;
72}
73
74static const struct game_params pattern_presets[] = {
75 {10, 10},
76 {15, 15},
77 {20, 20},
78#ifndef SLOW_SYSTEM
79 {25, 25},
80 {30, 30},
81#endif
82};
83
84static int game_fetch_preset(int i, char **name, game_params **params)
85{
86 game_params *ret;
87 char str[80];
88
89 if (i < 0 || i >= lenof(pattern_presets))
90 return FALSE;
91
92 ret = snew(game_params);
93 *ret = pattern_presets[i];
94
95 sprintf(str, "%dx%d", ret->w, ret->h);
96
97 *name = dupstr(str);
98 *params = ret;
99 return TRUE;
100}
101
102static void free_params(game_params *params)
103{
104 sfree(params);
105}
106
107static game_params *dup_params(const game_params *params)
108{
109 game_params *ret = snew(game_params);
110 *ret = *params; /* structure copy */
111 return ret;
112}
113
114static void decode_params(game_params *ret, char const *string)
115{
116 char const *p = string;
117
118 ret->w = atoi(p);
119 while (*p && isdigit((unsigned char)*p)) p++;
120 if (*p == 'x') {
121 p++;
122 ret->h = atoi(p);
123 while (*p && isdigit((unsigned char)*p)) p++;
124 } else {
125 ret->h = ret->w;
126 }
127}
128
129static char *encode_params(const game_params *params, int full)
130{
131 char ret[400];
132 int len;
133
134 len = sprintf(ret, "%dx%d", params->w, params->h);
135 assert(len < lenof(ret));
136 ret[len] = '\0';
137
138 return dupstr(ret);
139}
140
141static config_item *game_configure(const game_params *params)
142{
143 config_item *ret;
144 char buf[80];
145
146 ret = snewn(3, config_item);
147
148 ret[0].name = "Width";
149 ret[0].type = C_STRING;
150 sprintf(buf, "%d", params->w);
151 ret[0].sval = dupstr(buf);
152 ret[0].ival = 0;
153
154 ret[1].name = "Height";
155 ret[1].type = C_STRING;
156 sprintf(buf, "%d", params->h);
157 ret[1].sval = dupstr(buf);
158 ret[1].ival = 0;
159
160 ret[2].name = NULL;
161 ret[2].type = C_END;
162 ret[2].sval = NULL;
163 ret[2].ival = 0;
164
165 return ret;
166}
167
168static game_params *custom_params(const config_item *cfg)
169{
170 game_params *ret = snew(game_params);
171
172 ret->w = atoi(cfg[0].sval);
173 ret->h = atoi(cfg[1].sval);
174
175 return ret;
176}
177
178static char *validate_params(const game_params *params, int full)
179{
180 if (params->w <= 0 || params->h <= 0)
181 return "Width and height must both be greater than zero";
182 return NULL;
183}
184
185/* ----------------------------------------------------------------------
186 * Puzzle generation code.
187 *
188 * For this particular puzzle, it seemed important to me to ensure
189 * a unique solution. I do this the brute-force way, by having a
190 * solver algorithm alongside the generator, and repeatedly
191 * generating a random grid until I find one whose solution is
192 * unique. It turns out that this isn't too onerous on a modern PC
193 * provided you keep grid size below around 30. Any offers of
194 * better algorithms, however, will be very gratefully received.
195 *
196 * Another annoyance of this approach is that it limits the
197 * available puzzles to those solvable by the algorithm I've used.
198 * My algorithm only ever considers a single row or column at any
199 * one time, which means it's incapable of solving the following
200 * difficult example (found by Bella Image around 1995/6, when she
201 * and I were both doing maths degrees):
202 *
203 * 2 1 2 1
204 *
205 * +--+--+--+--+
206 * 1 1 | | | | |
207 * +--+--+--+--+
208 * 2 | | | | |
209 * +--+--+--+--+
210 * 1 | | | | |
211 * +--+--+--+--+
212 * 1 | | | | |
213 * +--+--+--+--+
214 *
215 * Obviously this cannot be solved by a one-row-or-column-at-a-time
216 * algorithm (it would require at least one row or column reading
217 * `2 1', `1 2', `3' or `4' to get started). However, it can be
218 * proved to have a unique solution: if the top left square were
219 * empty, then the only option for the top row would be to fill the
220 * two squares in the 1 columns, which would imply the squares
221 * below those were empty, leaving no place for the 2 in the second
222 * row. Contradiction. Hence the top left square is full, and the
223 * unique solution follows easily from that starting point.
224 *
225 * (The game ID for this puzzle is 4x4:2/1/2/1/1.1/2/1/1 , in case
226 * it's useful to anyone.)
227 */
228
229#ifndef STANDALONE_PICTURE_GENERATOR
230static int float_compare(const void *av, const void *bv)
231{
232 const float *a = (const float *)av;
233 const float *b = (const float *)bv;
234 if (*a < *b)
235 return -1;
236 else if (*a > *b)
237 return +1;
238 else
239 return 0;
240}
241
242static void generate(random_state *rs, int w, int h, unsigned char *retgrid)
243{
244 float *fgrid;
245 float *fgrid2;
246 int step, i, j;
247 float threshold;
248
249 fgrid = snewn(w*h, float);
250
251 for (i = 0; i < h; i++) {
252 for (j = 0; j < w; j++) {
253 fgrid[i*w+j] = random_upto(rs, 100000000UL) / 100000000.F;
254 }
255 }
256
257 /*
258 * The above gives a completely random splattering of black and
259 * white cells. We want to gently bias this in favour of _some_
260 * reasonably thick areas of white and black, while retaining
261 * some randomness and fine detail.
262 *
263 * So we evolve the starting grid using a cellular automaton.
264 * Currently, I'm doing something very simple indeed, which is
265 * to set each square to the average of the surrounding nine
266 * cells (or the average of fewer, if we're on a corner).
267 */
268 for (step = 0; step < 1; step++) {
269 fgrid2 = snewn(w*h, float);
270
271 for (i = 0; i < h; i++) {
272 for (j = 0; j < w; j++) {
273 float sx, xbar;
274 int n, p, q;
275
276 /*
277 * Compute the average of the surrounding cells.
278 */
279 n = 0;
280 sx = 0.F;
281 for (p = -1; p <= +1; p++) {
282 for (q = -1; q <= +1; q++) {
283 if (i+p < 0 || i+p >= h || j+q < 0 || j+q >= w)
284 continue;
285 /*
286 * An additional special case not mentioned
287 * above: if a grid dimension is 2xn then
288 * we do not average across that dimension
289 * at all. Otherwise a 2x2 grid would
290 * contain four identical squares.
291 */
292 if ((h==2 && p!=0) || (w==2 && q!=0))
293 continue;
294 n++;
295 sx += fgrid[(i+p)*w+(j+q)];
296 }
297 }
298 xbar = sx / n;
299
300 fgrid2[i*w+j] = xbar;
301 }
302 }
303
304 sfree(fgrid);
305 fgrid = fgrid2;
306 }
307
308 fgrid2 = snewn(w*h, float);
309 memcpy(fgrid2, fgrid, w*h*sizeof(float));
310 qsort(fgrid2, w*h, sizeof(float), float_compare);
311 threshold = fgrid2[w*h/2];
312 sfree(fgrid2);
313
314 for (i = 0; i < h; i++) {
315 for (j = 0; j < w; j++) {
316 retgrid[i*w+j] = (fgrid[i*w+j] >= threshold ? GRID_FULL :
317 GRID_EMPTY);
318 }
319 }
320
321 sfree(fgrid);
322}
323#endif
324
325static int compute_rowdata(int *ret, unsigned char *start, int len, int step)
326{
327 int i, n;
328
329 n = 0;
330
331 for (i = 0; i < len; i++) {
332 if (start[i*step] == GRID_FULL) {
333 int runlen = 1;
334 while (i+runlen < len && start[(i+runlen)*step] == GRID_FULL)
335 runlen++;
336 ret[n++] = runlen;
337 i += runlen;
338 }
339
340 if (i < len && start[i*step] == GRID_UNKNOWN)
341 return -1;
342 }
343
344 return n;
345}
346
347#define UNKNOWN 0
348#define BLOCK 1
349#define DOT 2
350#define STILL_UNKNOWN 3
351
352#ifdef STANDALONE_SOLVER
353int verbose = FALSE;
354#endif
355
356static int do_recurse(unsigned char *known, unsigned char *deduced,
357 unsigned char *row,
358 unsigned char *minpos_done, unsigned char *maxpos_done,
359 unsigned char *minpos_ok, unsigned char *maxpos_ok,
360 int *data, int len,
361 int freespace, int ndone, int lowest)
362{
363 int i, j, k;
364
365
366 /* This algorithm basically tries all possible ways the given rows of
367 * black blocks can be laid out in the row/column being examined.
368 * Special care is taken to avoid checking the tail of a row/column
369 * if the same conditions have already been checked during this recursion
370 * The algorithm also takes care to cut its losses as soon as an
371 * invalid (partial) solution is detected.
372 */
373 if (data[ndone]) {
374 if (lowest >= minpos_done[ndone] && lowest <= maxpos_done[ndone]) {
375 if (lowest >= minpos_ok[ndone] && lowest <= maxpos_ok[ndone]) {
376 for (i=0; i<lowest; i++)
377 deduced[i] |= row[i];
378 }
379 return lowest >= minpos_ok[ndone] && lowest <= maxpos_ok[ndone];
380 } else {
381 if (lowest < minpos_done[ndone]) minpos_done[ndone] = lowest;
382 if (lowest > maxpos_done[ndone]) maxpos_done[ndone] = lowest;
383 }
384 for (i=0; i<=freespace; i++) {
385 j = lowest;
386 for (k=0; k<i; k++) {
387 if (known[j] == BLOCK) goto next_iter;
388 row[j++] = DOT;
389 }
390 for (k=0; k<data[ndone]; k++) {
391 if (known[j] == DOT) goto next_iter;
392 row[j++] = BLOCK;
393 }
394 if (j < len) {
395 if (known[j] == BLOCK) goto next_iter;
396 row[j++] = DOT;
397 }
398 if (do_recurse(known, deduced, row, minpos_done, maxpos_done,
399 minpos_ok, maxpos_ok, data, len, freespace-i, ndone+1, j)) {
400 if (lowest < minpos_ok[ndone]) minpos_ok[ndone] = lowest;
401 if (lowest + i > maxpos_ok[ndone]) maxpos_ok[ndone] = lowest + i;
402 if (lowest + i > maxpos_done[ndone]) maxpos_done[ndone] = lowest + i;
403 }
404 next_iter:
405 j++;
406 }
407 return lowest >= minpos_ok[ndone] && lowest <= maxpos_ok[ndone];
408 } else {
409 for (i=lowest; i<len; i++) {
410 if (known[i] == BLOCK) return FALSE;
411 row[i] = DOT;
412 }
413 for (i=0; i<len; i++)
414 deduced[i] |= row[i];
415 return TRUE;
416 }
417}
418
419
420static int do_row(unsigned char *known, unsigned char *deduced,
421 unsigned char *row,
422 unsigned char *minpos_done, unsigned char *maxpos_done,
423 unsigned char *minpos_ok, unsigned char *maxpos_ok,
424 unsigned char *start, int len, int step, int *data,
425 unsigned int *changed
426#ifdef STANDALONE_SOLVER
427 , const char *rowcol, int index, int cluewid
428#endif
429 )
430{
431 int rowlen, i, freespace, done_any;
432
433 freespace = len+1;
434 for (rowlen = 0; data[rowlen]; rowlen++) {
435 minpos_done[rowlen] = minpos_ok[rowlen] = len - 1;
436 maxpos_done[rowlen] = maxpos_ok[rowlen] = 0;
437 freespace -= data[rowlen]+1;
438 }
439
440 for (i = 0; i < len; i++) {
441 known[i] = start[i*step];
442 deduced[i] = 0;
443 }
444 for (i = len - 1; i >= 0 && known[i] == DOT; i--)
445 freespace--;
446
447 if (rowlen == 0) {
448 memset(deduced, DOT, len);
449 } else {
450 do_recurse(known, deduced, row, minpos_done, maxpos_done, minpos_ok,
451 maxpos_ok, data, len, freespace, 0, 0);
452 }
453
454 done_any = FALSE;
455 for (i=0; i<len; i++)
456 if (deduced[i] && deduced[i] != STILL_UNKNOWN && !known[i]) {
457 start[i*step] = deduced[i];
458 if (changed) changed[i]++;
459 done_any = TRUE;
460 }
461#ifdef STANDALONE_SOLVER
462 if (verbose && done_any) {
463 char buf[80];
464 int thiscluewid;
465 printf("%s %2d: [", rowcol, index);
466 for (thiscluewid = -1, i = 0; data[i]; i++)
467 thiscluewid += sprintf(buf, " %d", data[i]);
468 printf("%*s", cluewid - thiscluewid, "");
469 for (i = 0; data[i]; i++)
470 printf(" %d", data[i]);
471 printf(" ] ");
472 for (i = 0; i < len; i++)
473 putchar(known[i] == BLOCK ? '#' :
474 known[i] == DOT ? '.' : '?');
475 printf(" -> ");
476 for (i = 0; i < len; i++)
477 putchar(start[i*step] == BLOCK ? '#' :
478 start[i*step] == DOT ? '.' : '?');
479 putchar('\n');
480 }
481#endif
482 return done_any;
483}
484
485static int solve_puzzle(const game_state *state, unsigned char *grid,
486 int w, int h,
487 unsigned char *matrix, unsigned char *workspace,
488 unsigned int *changed_h, unsigned int *changed_w,
489 int *rowdata
490#ifdef STANDALONE_SOLVER
491 , int cluewid
492#else
493 , int dummy
494#endif
495 )
496{
497 int i, j, ok, max;
498 int max_h, max_w;
499
500 assert((state!=NULL && state->common->rowdata!=NULL) ^ (grid!=NULL));
501
502 max = max(w, h);
503
504 memset(matrix, 0, w*h);
505 if (state) {
506 for (i=0; i<w*h; i++) {
507 if (state->common->immutable[i])
508 matrix[i] = state->grid[i];
509 }
510 }
511
512 /* For each column, compute how many squares can be deduced
513 * from just the row-data and initial clues.
514 * Later, changed_* will hold how many squares were changed
515 * in every row/column in the previous iteration
516 * Changed_* is used to choose the next rows / cols to re-examine
517 */
518 for (i=0; i<h; i++) {
519 int freespace, rowlen;
520 if (state && state->common->rowdata) {
521 memcpy(rowdata, state->common->rowdata + state->common->rowsize*(w+i), max*sizeof(int));
522 rowlen = state->common->rowlen[w+i];
523 } else {
524 rowlen = compute_rowdata(rowdata, grid+i*w, w, 1);
525 }
526 rowdata[rowlen] = 0;
527 if (rowlen == 0) {
528 changed_h[i] = w;
529 } else {
530 for (j=0, freespace=w+1; rowdata[j]; j++)
531 freespace -= rowdata[j] + 1;
532 for (j=0, changed_h[i]=0; rowdata[j]; j++)
533 if (rowdata[j] > freespace)
534 changed_h[i] += rowdata[j] - freespace;
535 }
536 for (j = 0; j < w; j++)
537 if (matrix[i*w+j])
538 changed_h[i]++;
539 }
540 for (i=0,max_h=0; i<h; i++)
541 if (changed_h[i] > max_h)
542 max_h = changed_h[i];
543 for (i=0; i<w; i++) {
544 int freespace, rowlen;
545 if (state && state->common->rowdata) {
546 memcpy(rowdata, state->common->rowdata + state->common->rowsize*i, max*sizeof(int));
547 rowlen = state->common->rowlen[i];
548 } else {
549 rowlen = compute_rowdata(rowdata, grid+i, h, w);
550 }
551 rowdata[rowlen] = 0;
552 if (rowlen == 0) {
553 changed_w[i] = h;
554 } else {
555 for (j=0, freespace=h+1; rowdata[j]; j++)
556 freespace -= rowdata[j] + 1;
557 for (j=0, changed_w[i]=0; rowdata[j]; j++)
558 if (rowdata[j] > freespace)
559 changed_w[i] += rowdata[j] - freespace;
560 }
561 for (j = 0; j < h; j++)
562 if (matrix[j*w+i])
563 changed_w[i]++;
564 }
565 for (i=0,max_w=0; i<w; i++)
566 if (changed_w[i] > max_w)
567 max_w = changed_w[i];
568
569 /* Solve the puzzle.
570 * Process rows/columns individually. Deductions involving more than one
571 * row and/or column at a time are not supported.
572 * Take care to only process rows/columns which have been changed since they
573 * were previously processed.
574 * Also, prioritize rows/columns which have had the most changes since their
575 * previous processing, as they promise the greatest benefit.
576 * Extremely rectangular grids (e.g. 10x20, 15x40, etc.) are not treated specially.
577 */
578 do {
579 for (; max_h && max_h >= max_w; max_h--) {
580 for (i=0; i<h; i++) {
581 if (changed_h[i] >= max_h) {
582 if (state && state->common->rowdata) {
583 memcpy(rowdata, state->common->rowdata + state->common->rowsize*(w+i), max*sizeof(int));
584 rowdata[state->common->rowlen[w+i]] = 0;
585 } else {
586 rowdata[compute_rowdata(rowdata, grid+i*w, w, 1)] = 0;
587 }
588 do_row(workspace, workspace+max, workspace+2*max,
589 workspace+3*max, workspace+4*max,
590 workspace+5*max, workspace+6*max,
591 matrix+i*w, w, 1, rowdata, changed_w
592#ifdef STANDALONE_SOLVER
593 , "row", i+1, cluewid
594#endif
595 );
596 changed_h[i] = 0;
597 }
598 }
599 for (i=0,max_w=0; i<w; i++)
600 if (changed_w[i] > max_w)
601 max_w = changed_w[i];
602 }
603 for (; max_w && max_w >= max_h; max_w--) {
604 for (i=0; i<w; i++) {
605 if (changed_w[i] >= max_w) {
606 if (state && state->common->rowdata) {
607 memcpy(rowdata, state->common->rowdata + state->common->rowsize*i, max*sizeof(int));
608 rowdata[state->common->rowlen[i]] = 0;
609 } else {
610 rowdata[compute_rowdata(rowdata, grid+i, h, w)] = 0;
611 }
612 do_row(workspace, workspace+max, workspace+2*max,
613 workspace+3*max, workspace+4*max,
614 workspace+5*max, workspace+6*max,
615 matrix+i, h, w, rowdata, changed_h
616#ifdef STANDALONE_SOLVER
617 , "col", i+1, cluewid
618#endif
619 );
620 changed_w[i] = 0;
621 }
622 }
623 for (i=0,max_h=0; i<h; i++)
624 if (changed_h[i] > max_h)
625 max_h = changed_h[i];
626 }
627 } while (max_h>0 || max_w>0);
628
629 ok = TRUE;
630 for (i=0; i<h; i++) {
631 for (j=0; j<w; j++) {
632 if (matrix[i*w+j] == UNKNOWN)
633 ok = FALSE;
634 }
635 }
636
637 return ok;
638}
639
640#ifndef STANDALONE_PICTURE_GENERATOR
641static unsigned char *generate_soluble(random_state *rs, int w, int h)
642{
643 int i, j, ok, ntries, max;
644 unsigned char *grid, *matrix, *workspace;
645 unsigned int *changed_h, *changed_w;
646 int *rowdata;
647
648 max = max(w, h);
649
650 grid = snewn(w*h, unsigned char);
651 /* Allocate this here, to avoid having to reallocate it again for every geneerated grid */
652 matrix = snewn(w*h, unsigned char);
653 workspace = snewn(max*7, unsigned char);
654 changed_h = snewn(max+1, unsigned int);
655 changed_w = snewn(max+1, unsigned int);
656 rowdata = snewn(max+1, int);
657
658 ntries = 0;
659
660 do {
661 ntries++;
662
663 generate(rs, w, h, grid);
664
665 /*
666 * The game is a bit too easy if any row or column is
667 * completely black or completely white. An exception is
668 * made for rows/columns that are under 3 squares,
669 * otherwise nothing will ever be successfully generated.
670 */
671 ok = TRUE;
672 if (w > 2) {
673 for (i = 0; i < h; i++) {
674 int colours = 0;
675 for (j = 0; j < w; j++)
676 colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1);
677 if (colours != 3)
678 ok = FALSE;
679 }
680 }
681 if (h > 2) {
682 for (j = 0; j < w; j++) {
683 int colours = 0;
684 for (i = 0; i < h; i++)
685 colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1);
686 if (colours != 3)
687 ok = FALSE;
688 }
689 }
690 if (!ok)
691 continue;
692
693 ok = solve_puzzle(NULL, grid, w, h, matrix, workspace,
694 changed_h, changed_w, rowdata, 0);
695 } while (!ok);
696
697 sfree(matrix);
698 sfree(workspace);
699 sfree(changed_h);
700 sfree(changed_w);
701 sfree(rowdata);
702 return grid;
703}
704#endif
705
706#ifdef STANDALONE_PICTURE_GENERATOR
707unsigned char *picture;
708#endif
709
710static char *new_game_desc(const game_params *params, random_state *rs,
711 char **aux, int interactive)
712{
713 unsigned char *grid;
714 int i, j, max, rowlen, *rowdata;
715 char intbuf[80], *desc;
716 int desclen, descpos;
717#ifdef STANDALONE_PICTURE_GENERATOR
718 game_state *state;
719 int *index;
720#endif
721
722 max = max(params->w, params->h);
723
724#ifdef STANDALONE_PICTURE_GENERATOR
725 /*
726 * Fixed input picture.
727 */
728 grid = snewn(params->w * params->h, unsigned char);
729 memcpy(grid, picture, params->w * params->h);
730
731 /*
732 * Now winnow the immutable square set as far as possible.
733 */
734 state = snew(game_state);
735 state->grid = grid;
736 state->common = snew(game_state_common);
737 state->common->rowdata = NULL;
738 state->common->immutable = snewn(params->w * params->h, unsigned char);
739 memset(state->common->immutable, 1, params->w * params->h);
740
741 index = snewn(params->w * params->h, int);
742 for (i = 0; i < params->w * params->h; i++)
743 index[i] = i;
744 shuffle(index, params->w * params->h, sizeof(*index), rs);
745
746 {
747 unsigned char *matrix = snewn(params->w*params->h, unsigned char);
748 unsigned char *workspace = snewn(max*7, unsigned char);
749 unsigned int *changed_h = snewn(max+1, unsigned int);
750 unsigned int *changed_w = snewn(max+1, unsigned int);
751 int *rowdata = snewn(max+1, int);
752 for (i = 0; i < params->w * params->h; i++) {
753 state->common->immutable[index[i]] = 0;
754 if (!solve_puzzle(state, grid, params->w, params->h,
755 matrix, workspace, changed_h, changed_w,
756 rowdata, 0))
757 state->common->immutable[index[i]] = 1;
758 }
759 sfree(workspace);
760 sfree(changed_h);
761 sfree(changed_w);
762 sfree(rowdata);
763 sfree(matrix);
764 }
765#else
766 grid = generate_soluble(rs, params->w, params->h);
767#endif
768 rowdata = snewn(max, int);
769
770 /*
771 * Save the solved game in aux.
772 */
773 if (aux) {
774 char *ai = snewn(params->w * params->h + 2, char);
775
776 /*
777 * String format is exactly the same as a solve move, so we
778 * can just dupstr this in solve_game().
779 */
780
781 ai[0] = 'S';
782
783 for (i = 0; i < params->w * params->h; i++)
784 ai[i+1] = grid[i] ? '1' : '0';
785
786 ai[params->w * params->h + 1] = '\0';
787
788 *aux = ai;
789 }
790
791 /*
792 * Seed is a slash-separated list of row contents; each row
793 * contents section is a dot-separated list of integers. Row
794 * contents are listed in the order (columns left to right,
795 * then rows top to bottom).
796 *
797 * Simplest way to handle memory allocation is to make two
798 * passes, first computing the seed size and then writing it
799 * out.
800 */
801 desclen = 0;
802 for (i = 0; i < params->w + params->h; i++) {
803 if (i < params->w)
804 rowlen = compute_rowdata(rowdata, grid+i, params->h, params->w);
805 else
806 rowlen = compute_rowdata(rowdata, grid+(i-params->w)*params->w,
807 params->w, 1);
808 if (rowlen > 0) {
809 for (j = 0; j < rowlen; j++) {
810 desclen += 1 + sprintf(intbuf, "%d", rowdata[j]);
811 }
812 } else {
813 desclen++;
814 }
815 }
816 desc = snewn(desclen, char);
817 descpos = 0;
818 for (i = 0; i < params->w + params->h; i++) {
819 if (i < params->w)
820 rowlen = compute_rowdata(rowdata, grid+i, params->h, params->w);
821 else
822 rowlen = compute_rowdata(rowdata, grid+(i-params->w)*params->w,
823 params->w, 1);
824 if (rowlen > 0) {
825 for (j = 0; j < rowlen; j++) {
826 int len = sprintf(desc+descpos, "%d", rowdata[j]);
827 if (j+1 < rowlen)
828 desc[descpos + len] = '.';
829 else
830 desc[descpos + len] = '/';
831 descpos += len+1;
832 }
833 } else {
834 desc[descpos++] = '/';
835 }
836 }
837 assert(descpos == desclen);
838 assert(desc[desclen-1] == '/');
839 desc[desclen-1] = '\0';
840#ifdef STANDALONE_PICTURE_GENERATOR
841 for (i = 0; i < params->w * params->h; i++)
842 if (state->common->immutable[i])
843 break;
844 if (i < params->w * params->h) {
845 /*
846 * At least one immutable square, so we need a suffix.
847 */
848 int run;
849
850 desc = sresize(desc, desclen + params->w * params->h + 3, char);
851 desc[descpos-1] = ',';
852
853 run = 0;
854 for (i = 0; i < params->w * params->h; i++) {
855 if (!state->common->immutable[i]) {
856 run++;
857 if (run == 25) {
858 desc[descpos++] = 'z';
859 run = 0;
860 }
861 } else {
862 desc[descpos++] = run + (grid[i] == GRID_FULL ? 'A' : 'a');
863 run = 0;
864 }
865 }
866 if (run > 0)
867 desc[descpos++] = run + 'a';
868 desc[descpos] = '\0';
869 }
870 sfree(state->common->immutable);
871 sfree(state->common);
872 sfree(state);
873#endif
874 sfree(rowdata);
875 sfree(grid);
876 return desc;
877}
878
879static char *validate_desc(const game_params *params, const char *desc)
880{
881 int i, n, rowspace;
882 const char *p;
883
884 for (i = 0; i < params->w + params->h; i++) {
885 if (i < params->w)
886 rowspace = params->h + 1;
887 else
888 rowspace = params->w + 1;
889
890 if (*desc && isdigit((unsigned char)*desc)) {
891 do {
892 p = desc;
893 while (*desc && isdigit((unsigned char)*desc)) desc++;
894 n = atoi(p);
895 rowspace -= n+1;
896
897 if (rowspace < 0) {
898 if (i < params->w)
899 return "at least one column contains more numbers than will fit";
900 else
901 return "at least one row contains more numbers than will fit";
902 }
903 } while (*desc++ == '.');
904 } else {
905 desc++; /* expect a slash immediately */
906 }
907
908 if (desc[-1] == '/') {
909 if (i+1 == params->w + params->h)
910 return "too many row/column specifications";
911 } else if (desc[-1] == '\0' || desc[-1] == ',') {
912 if (i+1 < params->w + params->h)
913 return "too few row/column specifications";
914 } else
915 return "unrecognised character in game specification";
916 }
917
918 if (desc[-1] == ',') {
919 /*
920 * Optional extra piece of game description which fills in
921 * some grid squares as extra clues.
922 */
923 i = 0;
924 while (i < params->w * params->h) {
925 int c = (unsigned char)*desc++;
926 if ((c >= 'a' && c <= 'z') ||
927 (c >= 'A' && c <= 'Z')) {
928 int len = tolower(c) - 'a';
929 i += len;
930 if (len < 25 && i < params->w*params->h)
931 i++;
932 if (i > params->w * params->h) {
933 return "too much data in clue-squares section";
934 }
935 } else if (!c) {
936 return "too little data in clue-squares section";
937 } else {
938 return "unrecognised character in clue-squares section";
939 }
940 }
941 if (*desc) {
942 return "too much data in clue-squares section";
943 }
944 }
945
946 return NULL;
947}
948
949static game_state *new_game(midend *me, const game_params *params,
950 const char *desc)
951{
952 int i;
953 const char *p;
954 game_state *state = snew(game_state);
955
956 state->common = snew(game_state_common);
957 state->common->refcount = 1;
958
959 state->common->w = params->w;
960 state->common->h = params->h;
961
962 state->grid = snewn(state->common->w * state->common->h, unsigned char);
963 memset(state->grid, GRID_UNKNOWN, state->common->w * state->common->h);
964
965 state->common->immutable = snewn(state->common->w * state->common->h,
966 unsigned char);
967 memset(state->common->immutable, 0, state->common->w * state->common->h);
968
969 state->common->rowsize = max(state->common->w, state->common->h);
970 state->common->rowdata = snewn(state->common->rowsize * (state->common->w + state->common->h), int);
971 state->common->rowlen = snewn(state->common->w + state->common->h, int);
972
973 state->completed = state->cheated = FALSE;
974
975 for (i = 0; i < params->w + params->h; i++) {
976 state->common->rowlen[i] = 0;
977 if (*desc && isdigit((unsigned char)*desc)) {
978 do {
979 p = desc;
980 while (*desc && isdigit((unsigned char)*desc)) desc++;
981 state->common->rowdata[state->common->rowsize * i + state->common->rowlen[i]++] =
982 atoi(p);
983 } while (*desc++ == '.');
984 } else {
985 desc++; /* expect a slash immediately */
986 }
987 }
988
989 if (desc[-1] == ',') {
990 /*
991 * Optional extra piece of game description which fills in
992 * some grid squares as extra clues.
993 */
994 i = 0;
995 while (i < params->w * params->h) {
996 int c = (unsigned char)*desc++;
997 int full = isupper(c), len = tolower(c) - 'a';
998 i += len;
999 if (len < 25 && i < params->w*params->h) {
1000 state->grid[i] = full ? GRID_FULL : GRID_EMPTY;
1001 state->common->immutable[i] = TRUE;
1002 i++;
1003 }
1004 }
1005 }
1006
1007 return state;
1008}
1009
1010static game_state *dup_game(const game_state *state)
1011{
1012 game_state *ret = snew(game_state);
1013
1014 ret->common = state->common;
1015 ret->common->refcount++;
1016
1017 ret->grid = snewn(ret->common->w * ret->common->h, unsigned char);
1018 memcpy(ret->grid, state->grid, ret->common->w * ret->common->h);
1019
1020 ret->completed = state->completed;
1021 ret->cheated = state->cheated;
1022
1023 return ret;
1024}
1025
1026static void free_game(game_state *state)
1027{
1028 if (--state->common->refcount == 0) {
1029 sfree(state->common->rowdata);
1030 sfree(state->common->rowlen);
1031 sfree(state->common->immutable);
1032 sfree(state->common);
1033 }
1034 sfree(state->grid);
1035 sfree(state);
1036}
1037
1038static char *solve_game(const game_state *state, const game_state *currstate,
1039 const char *ai, char **error)
1040{
1041 unsigned char *matrix;
1042 int w = state->common->w, h = state->common->h;
1043 int i;
1044 char *ret;
1045 int max, ok;
1046 unsigned char *workspace;
1047 unsigned int *changed_h, *changed_w;
1048 int *rowdata;
1049
1050 /*
1051 * If we already have the solved state in ai, copy it out.
1052 */
1053 if (ai)
1054 return dupstr(ai);
1055
1056 max = max(w, h);
1057 matrix = snewn(w*h, unsigned char);
1058 workspace = snewn(max*7, unsigned char);
1059 changed_h = snewn(max+1, unsigned int);
1060 changed_w = snewn(max+1, unsigned int);
1061 rowdata = snewn(max+1, int);
1062
1063 ok = solve_puzzle(state, NULL, w, h, matrix, workspace,
1064 changed_h, changed_w, rowdata, 0);
1065
1066 sfree(workspace);
1067 sfree(changed_h);
1068 sfree(changed_w);
1069 sfree(rowdata);
1070
1071 if (!ok) {
1072 sfree(matrix);
1073 *error = "Solving algorithm cannot complete this puzzle";
1074 return NULL;
1075 }
1076
1077 ret = snewn(w*h+2, char);
1078 ret[0] = 'S';
1079 for (i = 0; i < w*h; i++) {
1080 assert(matrix[i] == BLOCK || matrix[i] == DOT);
1081 ret[i+1] = (matrix[i] == BLOCK ? '1' : '0');
1082 }
1083 ret[w*h+1] = '\0';
1084
1085 sfree(matrix);
1086
1087 return ret;
1088}
1089
1090static int game_can_format_as_text_now(const game_params *params)
1091{
1092 return TRUE;
1093}
1094
1095static char *game_text_format(const game_state *state)
1096{
1097 int w = state->common->w, h = state->common->h, i, j;
1098 int left_gap = 0, top_gap = 0, ch = 2, cw = 1, limit = 1;
1099
1100 int len, topleft, lw, lh, gw, gh; /* {line,grid}_{width,height} */
1101 char *board, *buf;
1102
1103 for (i = 0; i < w; ++i) {
1104 top_gap = max(top_gap, state->common->rowlen[i]);
1105 for (j = 0; j < state->common->rowlen[i]; ++j)
1106 while (state->common->rowdata[i*state->common->rowsize + j] >= limit) {
1107 ++cw;
1108 limit *= 10;
1109 }
1110 }
1111 for (i = 0; i < h; ++i) {
1112 int rowlen = 0, predecessors = FALSE;
1113 for (j = 0; j < state->common->rowlen[i+w]; ++j) {
1114 int copy = state->common->rowdata[(i+w)*state->common->rowsize + j];
1115 rowlen += predecessors;
1116 predecessors = TRUE;
1117 do ++rowlen; while (copy /= 10);
1118 }
1119 left_gap = max(left_gap, rowlen);
1120 }
1121
1122 cw = max(cw, 3);
1123
1124 gw = w*cw + 2;
1125 gh = h*ch + 1;
1126 lw = gw + left_gap;
1127 lh = gh + top_gap;
1128 len = lw * lh;
1129 topleft = lw * top_gap + left_gap;
1130
1131 board = snewn(len + 1, char);
1132 sprintf(board, "%*s\n", len - 2, "");
1133
1134 for (i = 0; i < lh; ++i) {
1135 board[lw - 1 + i*lw] = '\n';
1136 if (i < top_gap) continue;
1137 board[lw - 2 + i*lw] = ((i - top_gap) % ch ? '|' : '+');
1138 }
1139
1140 for (i = 0; i < w; ++i) {
1141 for (j = 0; j < state->common->rowlen[i]; ++j) {
1142 int cell = topleft + i*cw + 1 + lw*(j - state->common->rowlen[i]);
1143 int nch = sprintf(board + cell, "%*d", cw - 1,
1144 state->common->rowdata[i*state->common->rowsize + j]);
1145 board[cell + nch] = ' '; /* de-NUL-ify */
1146 }
1147 }
1148
1149 buf = snewn(left_gap, char);
1150 for (i = 0; i < h; ++i) {
1151 char *p = buf, *start = board + top_gap*lw + left_gap + (i*ch+1)*lw;
1152 for (j = 0; j < state->common->rowlen[i+w]; ++j) {
1153 if (p > buf) *p++ = ' ';
1154 p += sprintf(p, "%d", state->common->rowdata[(i+w)*state->common->rowsize + j]);
1155 }
1156 memcpy(start - (p - buf), buf, p - buf);
1157 }
1158
1159 for (i = 0; i < w; ++i) {
1160 for (j = 0; j < h; ++j) {
1161 int cell = topleft + i*cw + j*ch*lw;
1162 int center = cell + cw/2 + (ch/2)*lw;
1163 int dx, dy;
1164 board[cell] = 0 ? center : '+';
1165 for (dx = 1; dx < cw; ++dx) board[cell + dx] = '-';
1166 for (dy = 1; dy < ch; ++dy) board[cell + dy*lw] = '|';
1167 if (state->grid[i*w+j] == GRID_UNKNOWN) continue;
1168 for (dx = 1; dx < cw; ++dx)
1169 for (dy = 1; dy < ch; ++dy)
1170 board[cell + dx + dy*lw] =
1171 state->grid[i*w+j] == GRID_FULL ? '#' : '.';
1172 }
1173 }
1174
1175 memcpy(board + topleft + h*ch*lw, board + topleft, gw - 1);
1176
1177 sfree(buf);
1178
1179 return board;
1180}
1181
1182struct game_ui {
1183 int dragging;
1184 int drag_start_x;
1185 int drag_start_y;
1186 int drag_end_x;
1187 int drag_end_y;
1188 int drag, release, state;
1189 int cur_x, cur_y, cur_visible;
1190};
1191
1192static game_ui *new_ui(const game_state *state)
1193{
1194 game_ui *ret;
1195
1196 ret = snew(game_ui);
1197 ret->dragging = FALSE;
1198 ret->cur_x = ret->cur_y = ret->cur_visible = 0;
1199
1200 return ret;
1201}
1202
1203static void free_ui(game_ui *ui)
1204{
1205 sfree(ui);
1206}
1207
1208static char *encode_ui(const game_ui *ui)
1209{
1210 return NULL;
1211}
1212
1213static void decode_ui(game_ui *ui, const char *encoding)
1214{
1215}
1216
1217static void game_changed_state(game_ui *ui, const game_state *oldstate,
1218 const game_state *newstate)
1219{
1220}
1221
1222struct game_drawstate {
1223 int started;
1224 int w, h;
1225 int tilesize;
1226 unsigned char *visible, *numcolours;
1227 int cur_x, cur_y;
1228};
1229
1230static char *interpret_move(const game_state *state, game_ui *ui,
1231 const game_drawstate *ds,
1232 int x, int y, int button)
1233{
1234 int control = button & MOD_CTRL, shift = button & MOD_SHFT;
1235 button &= ~MOD_MASK;
1236
1237 x = FROMCOORD(state->common->w, x);
1238 y = FROMCOORD(state->common->h, y);
1239
1240 if (x >= 0 && x < state->common->w && y >= 0 && y < state->common->h &&
1241 (button == LEFT_BUTTON || button == RIGHT_BUTTON ||
1242 button == MIDDLE_BUTTON)) {
1243#ifdef STYLUS_BASED
1244 int currstate = state->grid[y * state->common->w + x];
1245#endif
1246
1247 ui->dragging = TRUE;
1248
1249 if (button == LEFT_BUTTON) {
1250 ui->drag = LEFT_DRAG;
1251 ui->release = LEFT_RELEASE;
1252#ifdef STYLUS_BASED
1253 ui->state = (currstate + 2) % 3; /* FULL -> EMPTY -> UNKNOWN */
1254#else
1255 ui->state = GRID_FULL;
1256#endif
1257 } else if (button == RIGHT_BUTTON) {
1258 ui->drag = RIGHT_DRAG;
1259 ui->release = RIGHT_RELEASE;
1260#ifdef STYLUS_BASED
1261 ui->state = (currstate + 1) % 3; /* EMPTY -> FULL -> UNKNOWN */
1262#else
1263 ui->state = GRID_EMPTY;
1264#endif
1265 } else /* if (button == MIDDLE_BUTTON) */ {
1266 ui->drag = MIDDLE_DRAG;
1267 ui->release = MIDDLE_RELEASE;
1268 ui->state = GRID_UNKNOWN;
1269 }
1270
1271 ui->drag_start_x = ui->drag_end_x = x;
1272 ui->drag_start_y = ui->drag_end_y = y;
1273 ui->cur_visible = 0;
1274
1275 return ""; /* UI activity occurred */
1276 }
1277
1278 if (ui->dragging && button == ui->drag) {
1279 /*
1280 * There doesn't seem much point in allowing a rectangle
1281 * drag; people will generally only want to drag a single
1282 * horizontal or vertical line, so we make that easy by
1283 * snapping to it.
1284 *
1285 * Exception: if we're _middle_-button dragging to tag
1286 * things as UNKNOWN, we may well want to trash an entire
1287 * area and start over!
1288 */
1289 if (ui->state != GRID_UNKNOWN) {
1290 if (abs(x - ui->drag_start_x) > abs(y - ui->drag_start_y))
1291 y = ui->drag_start_y;
1292 else
1293 x = ui->drag_start_x;
1294 }
1295
1296 if (x < 0) x = 0;
1297 if (y < 0) y = 0;
1298 if (x >= state->common->w) x = state->common->w - 1;
1299 if (y >= state->common->h) y = state->common->h - 1;
1300
1301 ui->drag_end_x = x;
1302 ui->drag_end_y = y;
1303
1304 return ""; /* UI activity occurred */
1305 }
1306
1307 if (ui->dragging && button == ui->release) {
1308 int x1, x2, y1, y2, xx, yy;
1309 int move_needed = FALSE;
1310
1311 x1 = min(ui->drag_start_x, ui->drag_end_x);
1312 x2 = max(ui->drag_start_x, ui->drag_end_x);
1313 y1 = min(ui->drag_start_y, ui->drag_end_y);
1314 y2 = max(ui->drag_start_y, ui->drag_end_y);
1315
1316 for (yy = y1; yy <= y2; yy++)
1317 for (xx = x1; xx <= x2; xx++)
1318 if (!state->common->immutable[yy * state->common->w + xx] &&
1319 state->grid[yy * state->common->w + xx] != ui->state)
1320 move_needed = TRUE;
1321
1322 ui->dragging = FALSE;
1323
1324 if (move_needed) {
1325 char buf[80];
1326 sprintf(buf, "%c%d,%d,%d,%d",
1327 (char)(ui->state == GRID_FULL ? 'F' :
1328 ui->state == GRID_EMPTY ? 'E' : 'U'),
1329 x1, y1, x2-x1+1, y2-y1+1);
1330 return dupstr(buf);
1331 } else
1332 return ""; /* UI activity occurred */
1333 }
1334
1335 if (IS_CURSOR_MOVE(button)) {
1336 int x = ui->cur_x, y = ui->cur_y, newstate;
1337 char buf[80];
1338 move_cursor(button, &ui->cur_x, &ui->cur_y, state->common->w, state->common->h, 0);
1339 ui->cur_visible = 1;
1340 if (!control && !shift) return "";
1341
1342 newstate = control ? shift ? GRID_UNKNOWN : GRID_FULL : GRID_EMPTY;
1343 if (state->grid[y * state->common->w + x] == newstate &&
1344 state->grid[ui->cur_y * state->common->w + ui->cur_x] == newstate)
1345 return "";
1346
1347 sprintf(buf, "%c%d,%d,%d,%d", control ? shift ? 'U' : 'F' : 'E',
1348 min(x, ui->cur_x), min(y, ui->cur_y),
1349 abs(x - ui->cur_x) + 1, abs(y - ui->cur_y) + 1);
1350 return dupstr(buf);
1351 }
1352
1353 if (IS_CURSOR_SELECT(button)) {
1354 int currstate = state->grid[ui->cur_y * state->common->w + ui->cur_x];
1355 int newstate;
1356 char buf[80];
1357
1358 if (!ui->cur_visible) {
1359 ui->cur_visible = 1;
1360 return "";
1361 }
1362
1363 if (button == CURSOR_SELECT2)
1364 newstate = currstate == GRID_UNKNOWN ? GRID_EMPTY :
1365 currstate == GRID_EMPTY ? GRID_FULL : GRID_UNKNOWN;
1366 else
1367 newstate = currstate == GRID_UNKNOWN ? GRID_FULL :
1368 currstate == GRID_FULL ? GRID_EMPTY : GRID_UNKNOWN;
1369
1370 sprintf(buf, "%c%d,%d,%d,%d",
1371 (char)(newstate == GRID_FULL ? 'F' :
1372 newstate == GRID_EMPTY ? 'E' : 'U'),
1373 ui->cur_x, ui->cur_y, 1, 1);
1374 return dupstr(buf);
1375 }
1376
1377 return NULL;
1378}
1379
1380static game_state *execute_move(const game_state *from, const char *move)
1381{
1382 game_state *ret;
1383 int x1, x2, y1, y2, xx, yy;
1384 int val;
1385
1386 if (move[0] == 'S' &&
1387 strlen(move) == from->common->w * from->common->h + 1) {
1388 int i;
1389
1390 ret = dup_game(from);
1391
1392 for (i = 0; i < ret->common->w * ret->common->h; i++)
1393 ret->grid[i] = (move[i+1] == '1' ? GRID_FULL : GRID_EMPTY);
1394
1395 ret->completed = ret->cheated = TRUE;
1396
1397 return ret;
1398 } else if ((move[0] == 'F' || move[0] == 'E' || move[0] == 'U') &&
1399 sscanf(move+1, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4 &&
1400 x1 >= 0 && x2 >= 0 && x1+x2 <= from->common->w &&
1401 y1 >= 0 && y2 >= 0 && y1+y2 <= from->common->h) {
1402
1403 x2 += x1;
1404 y2 += y1;
1405 val = (move[0] == 'F' ? GRID_FULL :
1406 move[0] == 'E' ? GRID_EMPTY : GRID_UNKNOWN);
1407
1408 ret = dup_game(from);
1409 for (yy = y1; yy < y2; yy++)
1410 for (xx = x1; xx < x2; xx++)
1411 if (!ret->common->immutable[yy * ret->common->w + xx])
1412 ret->grid[yy * ret->common->w + xx] = val;
1413
1414 /*
1415 * An actual change, so check to see if we've completed the
1416 * game.
1417 */
1418 if (!ret->completed) {
1419 int *rowdata = snewn(ret->common->rowsize, int);
1420 int i, len;
1421
1422 ret->completed = TRUE;
1423
1424 for (i=0; i<ret->common->w; i++) {
1425 len = compute_rowdata(rowdata, ret->grid+i,
1426 ret->common->h, ret->common->w);
1427 if (len != ret->common->rowlen[i] ||
1428 memcmp(ret->common->rowdata+i*ret->common->rowsize,
1429 rowdata, len * sizeof(int))) {
1430 ret->completed = FALSE;
1431 break;
1432 }
1433 }
1434 for (i=0; i<ret->common->h; i++) {
1435 len = compute_rowdata(rowdata, ret->grid+i*ret->common->w,
1436 ret->common->w, 1);
1437 if (len != ret->common->rowlen[i+ret->common->w] ||
1438 memcmp(ret->common->rowdata +
1439 (i+ret->common->w)*ret->common->rowsize,
1440 rowdata, len * sizeof(int))) {
1441 ret->completed = FALSE;
1442 break;
1443 }
1444 }
1445
1446 sfree(rowdata);
1447 }
1448
1449 return ret;
1450 } else
1451 return NULL;
1452}
1453
1454/* ----------------------------------------------------------------------
1455 * Error-checking during gameplay.
1456 */
1457
1458/*
1459 * The difficulty in error-checking Pattern is to make the error check
1460 * _weak_ enough. The most obvious way would be to check each row and
1461 * column by calling (a modified form of) do_row() to recursively
1462 * analyse the row contents against the clue set and see if the
1463 * GRID_UNKNOWNs could be filled in in any way that would end up
1464 * correct. However, this turns out to be such a strong error check as
1465 * to constitute a spoiler in many situations: you make a typo while
1466 * trying to fill in one row, and not only does the row light up to
1467 * indicate an error, but several columns crossed by the move also
1468 * light up and draw your attention to deductions you hadn't even
1469 * noticed you could make.
1470 *
1471 * So instead I restrict error-checking to 'complete runs' within a
1472 * row, by which I mean contiguous sequences of GRID_FULL bounded at
1473 * both ends by either GRID_EMPTY or the ends of the row. We identify
1474 * all the complete runs in a row, and verify that _those_ are
1475 * consistent with the row's clue list. Sequences of complete runs
1476 * separated by solid GRID_EMPTY are required to match contiguous
1477 * sequences in the clue list, whereas if there's at least one
1478 * GRID_UNKNOWN between any two complete runs then those two need not
1479 * be contiguous in the clue list.
1480 *
1481 * To simplify the edge cases, I pretend that the clue list for the
1482 * row is extended with a 0 at each end, and I also pretend that the
1483 * grid data for the row is extended with a GRID_EMPTY and a
1484 * zero-length run at each end. This permits the contiguity checker to
1485 * handle the fiddly end effects (e.g. if the first contiguous
1486 * sequence of complete runs in the grid matches _something_ in the
1487 * clue list but not at the beginning, this is allowable iff there's a
1488 * GRID_UNKNOWN before the first one) with minimal faff, since the end
1489 * effects just drop out as special cases of the normal inter-run
1490 * handling (in this code the above case is not 'at the end of the
1491 * clue list' at all, but between the implicit initial zero run and
1492 * the first nonzero one).
1493 *
1494 * We must also be a little careful about how we search for a
1495 * contiguous sequence of runs. In the clue list (1 1 2 1 2 3),
1496 * suppose we see a GRID_UNKNOWN and then a length-1 run. We search
1497 * for 1 in the clue list and find it at the very beginning. But now
1498 * suppose we find a length-2 run with no GRID_UNKNOWN before it. We
1499 * can't naively look at the next clue from the 1 we found, because
1500 * that'll be the second 1 and won't match. Instead, we must backtrack
1501 * by observing that the 2 we've just found must be contiguous with
1502 * the 1 we've already seen, so we search for the sequence (1 2) and
1503 * find it starting at the second 1. Now if we see a 3, we must
1504 * rethink again and search for (1 2 3).
1505 */
1506
1507struct errcheck_state {
1508 /*
1509 * rowdata and rowlen point at the clue data for this row in the
1510 * game state.
1511 */
1512 int *rowdata;
1513 int rowlen;
1514 /*
1515 * rowpos indicates the lowest position where it would be valid to
1516 * see our next run length. It might be equal to rowlen,
1517 * indicating that the next run would have to be the terminating 0.
1518 */
1519 int rowpos;
1520 /*
1521 * ncontig indicates how many runs we've seen in a contiguous
1522 * block. This is taken into account when searching for the next
1523 * run we find, unless ncontig is zeroed out first by encountering
1524 * a GRID_UNKNOWN.
1525 */
1526 int ncontig;
1527};
1528
1529static int errcheck_found_run(struct errcheck_state *es, int r)
1530{
1531/* Macro to handle the pretence that rowdata has a 0 at each end */
1532#define ROWDATA(k) ((k)<0 || (k)>=es->rowlen ? 0 : es->rowdata[(k)])
1533
1534 /*
1535 * See if we can find this new run length at a position where it
1536 * also matches the last 'ncontig' runs we've seen.
1537 */
1538 int i, newpos;
1539 for (newpos = es->rowpos; newpos <= es->rowlen; newpos++) {
1540
1541 if (ROWDATA(newpos) != r)
1542 goto notfound;
1543
1544 for (i = 1; i <= es->ncontig; i++)
1545 if (ROWDATA(newpos - i) != ROWDATA(es->rowpos - i))
1546 goto notfound;
1547
1548 es->rowpos = newpos+1;
1549 es->ncontig++;
1550 return TRUE;
1551
1552 notfound:;
1553 }
1554
1555 return FALSE;
1556
1557#undef ROWDATA
1558}
1559
1560static int check_errors(const game_state *state, int i)
1561{
1562 int start, step, end, j;
1563 int val, runlen;
1564 struct errcheck_state aes, *es = &aes;
1565
1566 es->rowlen = state->common->rowlen[i];
1567 es->rowdata = state->common->rowdata + state->common->rowsize * i;
1568 /* Pretend that we've already encountered the initial zero run */
1569 es->ncontig = 1;
1570 es->rowpos = 0;
1571
1572 if (i < state->common->w) {
1573 start = i;
1574 step = state->common->w;
1575 end = start + step * state->common->h;
1576 } else {
1577 start = (i - state->common->w) * state->common->w;
1578 step = 1;
1579 end = start + step * state->common->w;
1580 }
1581
1582 runlen = -1;
1583 for (j = start - step; j <= end; j += step) {
1584 if (j < start || j == end)
1585 val = GRID_EMPTY;
1586 else
1587 val = state->grid[j];
1588
1589 if (val == GRID_UNKNOWN) {
1590 runlen = -1;
1591 es->ncontig = 0;
1592 } else if (val == GRID_FULL) {
1593 if (runlen >= 0)
1594 runlen++;
1595 } else if (val == GRID_EMPTY) {
1596 if (runlen > 0) {
1597 if (!errcheck_found_run(es, runlen))
1598 return TRUE; /* error! */
1599 }
1600 runlen = 0;
1601 }
1602 }
1603
1604 /* Signal end-of-row by sending errcheck_found_run the terminating
1605 * zero run, which will be marked as contiguous with the previous
1606 * run if and only if there hasn't been a GRID_UNKNOWN before. */
1607 if (!errcheck_found_run(es, 0))
1608 return TRUE; /* error at the last minute! */
1609
1610 return FALSE; /* no error */
1611}
1612
1613/* ----------------------------------------------------------------------
1614 * Drawing routines.
1615 */
1616
1617static void game_compute_size(const game_params *params, int tilesize,
1618 int *x, int *y)
1619{
1620 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1621 struct { int tilesize; } ads, *ds = &ads;
1622 ads.tilesize = tilesize;
1623
1624 *x = SIZE(params->w);
1625 *y = SIZE(params->h);
1626}
1627
1628static void game_set_size(drawing *dr, game_drawstate *ds,
1629 const game_params *params, int tilesize)
1630{
1631 ds->tilesize = tilesize;
1632}
1633
1634static float *game_colours(frontend *fe, int *ncolours)
1635{
1636 float *ret = snewn(3 * NCOLOURS, float);
1637 int i;
1638
1639 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1640
1641 for (i = 0; i < 3; i++) {
1642 ret[COL_GRID * 3 + i] = 0.3F;
1643 ret[COL_UNKNOWN * 3 + i] = 0.5F;
1644 ret[COL_TEXT * 3 + i] = 0.0F;
1645 ret[COL_FULL * 3 + i] = 0.0F;
1646 ret[COL_EMPTY * 3 + i] = 1.0F;
1647 }
1648 ret[COL_CURSOR * 3 + 0] = 1.0F;
1649 ret[COL_CURSOR * 3 + 1] = 0.25F;
1650 ret[COL_CURSOR * 3 + 2] = 0.25F;
1651 ret[COL_ERROR * 3 + 0] = 1.0F;
1652 ret[COL_ERROR * 3 + 1] = 0.0F;
1653 ret[COL_ERROR * 3 + 2] = 0.0F;
1654
1655 *ncolours = NCOLOURS;
1656 return ret;
1657}
1658
1659static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1660{
1661 struct game_drawstate *ds = snew(struct game_drawstate);
1662
1663 ds->started = FALSE;
1664 ds->w = state->common->w;
1665 ds->h = state->common->h;
1666 ds->visible = snewn(ds->w * ds->h, unsigned char);
1667 ds->tilesize = 0; /* not decided yet */
1668 memset(ds->visible, 255, ds->w * ds->h);
1669 ds->numcolours = snewn(ds->w + ds->h, unsigned char);
1670 memset(ds->numcolours, 255, ds->w + ds->h);
1671 ds->cur_x = ds->cur_y = 0;
1672
1673 return ds;
1674}
1675
1676static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1677{
1678 sfree(ds->visible);
1679 sfree(ds);
1680}
1681
1682static void grid_square(drawing *dr, game_drawstate *ds,
1683 int y, int x, int state, int cur)
1684{
1685 int xl, xr, yt, yb, dx, dy, dw, dh;
1686
1687 draw_rect(dr, TOCOORD(ds->w, x), TOCOORD(ds->h, y),
1688 TILE_SIZE, TILE_SIZE, COL_GRID);
1689
1690 xl = (x % 5 == 0 ? 1 : 0);
1691 yt = (y % 5 == 0 ? 1 : 0);
1692 xr = (x % 5 == 4 || x == ds->w-1 ? 1 : 0);
1693 yb = (y % 5 == 4 || y == ds->h-1 ? 1 : 0);
1694
1695 dx = TOCOORD(ds->w, x) + 1 + xl;
1696 dy = TOCOORD(ds->h, y) + 1 + yt;
1697 dw = TILE_SIZE - xl - xr - 1;
1698 dh = TILE_SIZE - yt - yb - 1;
1699
1700 draw_rect(dr, dx, dy, dw, dh,
1701 (state == GRID_FULL ? COL_FULL :
1702 state == GRID_EMPTY ? COL_EMPTY : COL_UNKNOWN));
1703 if (cur) {
1704 draw_rect_outline(dr, dx, dy, dw, dh, COL_CURSOR);
1705 draw_rect_outline(dr, dx+1, dy+1, dw-2, dh-2, COL_CURSOR);
1706 }
1707
1708 draw_update(dr, TOCOORD(ds->w, x), TOCOORD(ds->h, y),
1709 TILE_SIZE, TILE_SIZE);
1710}
1711
1712/*
1713 * Draw the numbers for a single row or column.
1714 */
1715static void draw_numbers(drawing *dr, game_drawstate *ds,
1716 const game_state *state, int i, int erase, int colour)
1717{
1718 int rowlen = state->common->rowlen[i];
1719 int *rowdata = state->common->rowdata + state->common->rowsize * i;
1720 int nfit;
1721 int j;
1722
1723 if (erase) {
1724 if (i < state->common->w) {
1725 draw_rect(dr, TOCOORD(state->common->w, i), 0,
1726 TILE_SIZE, BORDER + TLBORDER(state->common->h) * TILE_SIZE,
1727 COL_BACKGROUND);
1728 } else {
1729 draw_rect(dr, 0, TOCOORD(state->common->h, i - state->common->w),
1730 BORDER + TLBORDER(state->common->w) * TILE_SIZE, TILE_SIZE,
1731 COL_BACKGROUND);
1732 }
1733 }
1734
1735 /*
1736 * Normally I space the numbers out by the same distance as the
1737 * tile size. However, if there are more numbers than available
1738 * spaces, I have to squash them up a bit.
1739 */
1740 if (i < state->common->w)
1741 nfit = TLBORDER(state->common->h);
1742 else
1743 nfit = TLBORDER(state->common->w);
1744 nfit = max(rowlen, nfit) - 1;
1745 assert(nfit > 0);
1746
1747 for (j = 0; j < rowlen; j++) {
1748 int x, y;
1749 char str[80];
1750
1751 if (i < state->common->w) {
1752 x = TOCOORD(state->common->w, i);
1753 y = BORDER + TILE_SIZE * (TLBORDER(state->common->h)-1);
1754 y -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->h)-1) / nfit;
1755 } else {
1756 y = TOCOORD(state->common->h, i - state->common->w);
1757 x = BORDER + TILE_SIZE * (TLBORDER(state->common->w)-1);
1758 x -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->common->w)-1) / nfit;
1759 }
1760
1761 sprintf(str, "%d", rowdata[j]);
1762 draw_text(dr, x+TILE_SIZE/2, y+TILE_SIZE/2, FONT_VARIABLE,
1763 TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, colour, str);
1764 }
1765
1766 if (i < state->common->w) {
1767 draw_update(dr, TOCOORD(state->common->w, i), 0,
1768 TILE_SIZE, BORDER + TLBORDER(state->common->h) * TILE_SIZE);
1769 } else {
1770 draw_update(dr, 0, TOCOORD(state->common->h, i - state->common->w),
1771 BORDER + TLBORDER(state->common->w) * TILE_SIZE, TILE_SIZE);
1772 }
1773}
1774
1775static void game_redraw(drawing *dr, game_drawstate *ds,
1776 const game_state *oldstate, const game_state *state,
1777 int dir, const game_ui *ui,
1778 float animtime, float flashtime)
1779{
1780 int i, j;
1781 int x1, x2, y1, y2;
1782 int cx, cy, cmoved;
1783
1784 if (!ds->started) {
1785 /*
1786 * The initial contents of the window are not guaranteed
1787 * and can vary with front ends. To be on the safe side,
1788 * all games should start by drawing a big background-
1789 * colour rectangle covering the whole window.
1790 */
1791 draw_rect(dr, 0, 0, SIZE(ds->w), SIZE(ds->h), COL_BACKGROUND);
1792
1793 /*
1794 * Draw the grid outline.
1795 */
1796 draw_rect(dr, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1,
1797 ds->w * TILE_SIZE + 3, ds->h * TILE_SIZE + 3,
1798 COL_GRID);
1799
1800 ds->started = TRUE;
1801
1802 draw_update(dr, 0, 0, SIZE(ds->w), SIZE(ds->h));
1803 }
1804
1805 if (ui->dragging) {
1806 x1 = min(ui->drag_start_x, ui->drag_end_x);
1807 x2 = max(ui->drag_start_x, ui->drag_end_x);
1808 y1 = min(ui->drag_start_y, ui->drag_end_y);
1809 y2 = max(ui->drag_start_y, ui->drag_end_y);
1810 } else {
1811 x1 = x2 = y1 = y2 = -1; /* placate gcc warnings */
1812 }
1813
1814 if (ui->cur_visible) {
1815 cx = ui->cur_x; cy = ui->cur_y;
1816 } else {
1817 cx = cy = -1;
1818 }
1819 cmoved = (cx != ds->cur_x || cy != ds->cur_y);
1820
1821 /*
1822 * Now draw any grid squares which have changed since last
1823 * redraw.
1824 */
1825 for (i = 0; i < ds->h; i++) {
1826 for (j = 0; j < ds->w; j++) {
1827 int val, cc = 0;
1828
1829 /*
1830 * Work out what state this square should be drawn in,
1831 * taking any current drag operation into account.
1832 */
1833 if (ui->dragging && x1 <= j && j <= x2 && y1 <= i && i <= y2 &&
1834 !state->common->immutable[i * state->common->w + j])
1835 val = ui->state;
1836 else
1837 val = state->grid[i * state->common->w + j];
1838
1839 if (cmoved) {
1840 /* the cursor has moved; if we were the old or
1841 * the new cursor position we need to redraw. */
1842 if (j == cx && i == cy) cc = 1;
1843 if (j == ds->cur_x && i == ds->cur_y) cc = 1;
1844 }
1845
1846 /*
1847 * Briefly invert everything twice during a completion
1848 * flash.
1849 */
1850 if (flashtime > 0 &&
1851 (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3) &&
1852 val != GRID_UNKNOWN)
1853 val = (GRID_FULL ^ GRID_EMPTY) ^ val;
1854
1855 if (ds->visible[i * ds->w + j] != val || cc) {
1856 grid_square(dr, ds, i, j, val,
1857 (j == cx && i == cy));
1858 ds->visible[i * ds->w + j] = val;
1859 }
1860 }
1861 }
1862 ds->cur_x = cx; ds->cur_y = cy;
1863
1864 /*
1865 * Redraw any numbers which have changed their colour due to error
1866 * indication.
1867 */
1868 for (i = 0; i < state->common->w + state->common->h; i++) {
1869 int colour = check_errors(state, i) ? COL_ERROR : COL_TEXT;
1870 if (ds->numcolours[i] != colour) {
1871 draw_numbers(dr, ds, state, i, TRUE, colour);
1872 ds->numcolours[i] = colour;
1873 }
1874 }
1875}
1876
1877static float game_anim_length(const game_state *oldstate,
1878 const game_state *newstate, int dir, game_ui *ui)
1879{
1880 return 0.0F;
1881}
1882
1883static float game_flash_length(const game_state *oldstate,
1884 const game_state *newstate, int dir, game_ui *ui)
1885{
1886 if (!oldstate->completed && newstate->completed &&
1887 !oldstate->cheated && !newstate->cheated)
1888 return FLASH_TIME;
1889 return 0.0F;
1890}
1891
1892static int game_status(const game_state *state)
1893{
1894 return state->completed ? +1 : 0;
1895}
1896
1897static int game_timing_state(const game_state *state, game_ui *ui)
1898{
1899 return TRUE;
1900}
1901
1902static void game_print_size(const game_params *params, float *x, float *y)
1903{
1904 int pw, ph;
1905
1906 /*
1907 * I'll use 5mm squares by default.
1908 */
1909 game_compute_size(params, 500, &pw, &ph);
1910 *x = pw / 100.0F;
1911 *y = ph / 100.0F;
1912}
1913
1914static void game_print(drawing *dr, const game_state *state, int tilesize)
1915{
1916 int w = state->common->w, h = state->common->h;
1917 int ink = print_mono_colour(dr, 0);
1918 int x, y, i;
1919
1920 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1921 game_drawstate ads, *ds = &ads;
1922 game_set_size(dr, ds, NULL, tilesize);
1923
1924 /*
1925 * Border.
1926 */
1927 print_line_width(dr, TILE_SIZE / 16);
1928 draw_rect_outline(dr, TOCOORD(w, 0), TOCOORD(h, 0),
1929 w*TILE_SIZE, h*TILE_SIZE, ink);
1930
1931 /*
1932 * Grid.
1933 */
1934 for (x = 1; x < w; x++) {
1935 print_line_width(dr, TILE_SIZE / (x % 5 ? 128 : 24));
1936 draw_line(dr, TOCOORD(w, x), TOCOORD(h, 0),
1937 TOCOORD(w, x), TOCOORD(h, h), ink);
1938 }
1939 for (y = 1; y < h; y++) {
1940 print_line_width(dr, TILE_SIZE / (y % 5 ? 128 : 24));
1941 draw_line(dr, TOCOORD(w, 0), TOCOORD(h, y),
1942 TOCOORD(w, w), TOCOORD(h, y), ink);
1943 }
1944
1945 /*
1946 * Clues.
1947 */
1948 for (i = 0; i < state->common->w + state->common->h; i++)
1949 draw_numbers(dr, ds, state, i, FALSE, ink);
1950
1951 /*
1952 * Solution.
1953 */
1954 print_line_width(dr, TILE_SIZE / 128);
1955 for (y = 0; y < h; y++)
1956 for (x = 0; x < w; x++) {
1957 if (state->grid[y*w+x] == GRID_FULL)
1958 draw_rect(dr, TOCOORD(w, x), TOCOORD(h, y),
1959 TILE_SIZE, TILE_SIZE, ink);
1960 else if (state->grid[y*w+x] == GRID_EMPTY)
1961 draw_circle(dr, TOCOORD(w, x) + TILE_SIZE/2,
1962 TOCOORD(h, y) + TILE_SIZE/2,
1963 TILE_SIZE/12, ink, ink);
1964 }
1965}
1966
1967#ifdef COMBINED
1968#define thegame pattern
1969#endif
1970
1971const struct game thegame = {
1972 "Pattern", "games.pattern", "pattern",
1973 default_params,
1974 game_fetch_preset, NULL,
1975 decode_params,
1976 encode_params,
1977 free_params,
1978 dup_params,
1979 TRUE, game_configure, custom_params,
1980 validate_params,
1981 new_game_desc,
1982 validate_desc,
1983 new_game,
1984 dup_game,
1985 free_game,
1986 TRUE, solve_game,
1987 TRUE, game_can_format_as_text_now, game_text_format,
1988 new_ui,
1989 free_ui,
1990 encode_ui,
1991 decode_ui,
1992 game_changed_state,
1993 interpret_move,
1994 execute_move,
1995 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1996 game_colours,
1997 game_new_drawstate,
1998 game_free_drawstate,
1999 game_redraw,
2000 game_anim_length,
2001 game_flash_length,
2002 game_status,
2003 TRUE, FALSE, game_print_size, game_print,
2004 FALSE, /* wants_statusbar */
2005 FALSE, game_timing_state,
2006 REQUIRE_RBUTTON, /* flags */
2007};
2008
2009#ifdef STANDALONE_SOLVER
2010
2011int main(int argc, char **argv)
2012{
2013 game_params *p;
2014 game_state *s;
2015 char *id = NULL, *desc, *err;
2016
2017 while (--argc > 0) {
2018 char *p = *++argv;
2019 if (*p == '-') {
2020 if (!strcmp(p, "-v")) {
2021 verbose = TRUE;
2022 } else {
2023 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2024 return 1;
2025 }
2026 } else {
2027 id = p;
2028 }
2029 }
2030
2031 if (!id) {
2032 fprintf(stderr, "usage: %s <game_id>\n", argv[0]);
2033 return 1;
2034 }
2035
2036 desc = strchr(id, ':');
2037 if (!desc) {
2038 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2039 return 1;
2040 }
2041 *desc++ = '\0';
2042
2043 p = default_params();
2044 decode_params(p, id);
2045 err = validate_desc(p, desc);
2046 if (err) {
2047 fprintf(stderr, "%s: %s\n", argv[0], err);
2048 return 1;
2049 }
2050 s = new_game(NULL, p, desc);
2051
2052 {
2053 int w = p->w, h = p->h, i, j, max, cluewid = 0;
2054 unsigned char *matrix, *workspace;
2055 unsigned int *changed_h, *changed_w;
2056 int *rowdata;
2057
2058 matrix = snewn(w*h, unsigned char);
2059 max = max(w, h);
2060 workspace = snewn(max*7, unsigned char);
2061 changed_h = snewn(max+1, unsigned int);
2062 changed_w = snewn(max+1, unsigned int);
2063 rowdata = snewn(max+1, int);
2064
2065 if (verbose) {
2066 int thiswid;
2067 /*
2068 * Work out the maximum text width of the clue numbers
2069 * in a row or column, so we can print the solver's
2070 * working in a nicely lined up way.
2071 */
2072 for (i = 0; i < (w+h); i++) {
2073 char buf[80];
2074 for (thiswid = -1, j = 0; j < s->common->rowlen[i]; j++)
2075 thiswid += sprintf
2076 (buf, " %d",
2077 s->common->rowdata[s->common->rowsize*i+j]);
2078 if (cluewid < thiswid)
2079 cluewid = thiswid;
2080 }
2081 }
2082
2083 solve_puzzle(s, NULL, w, h, matrix, workspace,
2084 changed_h, changed_w, rowdata, cluewid);
2085
2086 for (i = 0; i < h; i++) {
2087 for (j = 0; j < w; j++) {
2088 int c = (matrix[i*w+j] == UNKNOWN ? '?' :
2089 matrix[i*w+j] == BLOCK ? '#' :
2090 matrix[i*w+j] == DOT ? '.' :
2091 '!');
2092 putchar(c);
2093 }
2094 printf("\n");
2095 }
2096 }
2097
2098 return 0;
2099}
2100
2101#endif
2102
2103#ifdef STANDALONE_PICTURE_GENERATOR
2104
2105/*
2106 * Main program for the standalone picture generator. To use it,
2107 * simply provide it with an XBM-format bitmap file (note XBM, not
2108 * XPM) on standard input, and it will output a game ID in return.
2109 * For example:
2110 *
2111 * $ ./patternpicture < calligraphic-A.xbm
2112 * 15x15:2/4/2/2/2/3/3/3.1/3.1/3.1/11/14/12/6/1/2/2/3/4/5/1.3/2.3/1.3/2.3/1.4/9/1.1.3/2.2.3/5.4/3.2
2113 *
2114 * That looks easy, of course - all the program has done is to count
2115 * up the clue numbers! But in fact, it's done more than that: it's
2116 * also checked that the result is uniquely soluble from just the
2117 * numbers. If it hadn't been, then it would have also left some
2118 * filled squares in the playing area as extra clues.
2119 *
2120 * $ ./patternpicture < cube.xbm
2121 * 15x15:10/2.1/1.1.1/1.1.1/1.1.1/1.1.1/1.1.1/1.1.1/1.1.1/1.10/1.1.1/1.1.1/1.1.1/2.1/10/10/1.2/1.1.1/1.1.1/1.1.1/10.1/1.1.1/1.1.1/1.1.1/1.1.1/1.1.1/1.1.1/1.1.1/1.2/10,TNINzzzzGNzw
2122 *
2123 * This enables a reasonably convenient design workflow for coming up
2124 * with pictorial Pattern puzzles which _are_ uniquely soluble without
2125 * those inelegant pre-filled squares. Fire up a bitmap editor (X11
2126 * bitmap(1) is good enough), save a trial .xbm, and then test it by
2127 * running a command along the lines of
2128 *
2129 * $ ./pattern $(./patternpicture < test.xbm)
2130 *
2131 * If the resulting window pops up with some pre-filled squares, then
2132 * that tells you which parts of the image are giving rise to
2133 * ambiguities, so try making tweaks in those areas, try the test
2134 * command again, and see if it helps. Once you have a design for
2135 * which the Pattern starting grid comes out empty, there's your game
2136 * ID.
2137 */
2138
2139#include <time.h>
2140
2141int main(int argc, char **argv)
2142{
2143 game_params *par;
2144 char *params, *desc;
2145 random_state *rs;
2146 time_t seed = time(NULL);
2147 char buf[4096];
2148 int i;
2149 int x, y;
2150
2151 par = default_params();
2152 if (argc > 1)
2153 decode_params(par, argv[1]); /* get difficulty */
2154 par->w = par->h = -1;
2155
2156 /*
2157 * Now read an XBM file from standard input. This is simple and
2158 * hacky and will do very little error detection, so don't feed
2159 * it bogus data.
2160 */
2161 picture = NULL;
2162 x = y = 0;
2163 while (fgets(buf, sizeof(buf), stdin)) {
2164 buf[strcspn(buf, "\r\n")] = '\0';
2165 if (!strncmp(buf, "#define", 7)) {
2166 /*
2167 * Lines starting `#define' give the width and height.
2168 */
2169 char *num = buf + strlen(buf);
2170 char *symend;
2171
2172 while (num > buf && isdigit((unsigned char)num[-1]))
2173 num--;
2174 symend = num;
2175 while (symend > buf && isspace((unsigned char)symend[-1]))
2176 symend--;
2177
2178 if (symend-5 >= buf && !strncmp(symend-5, "width", 5))
2179 par->w = atoi(num);
2180 else if (symend-6 >= buf && !strncmp(symend-6, "height", 6))
2181 par->h = atoi(num);
2182 } else {
2183 /*
2184 * Otherwise, break the string up into words and take
2185 * any word of the form `0x' plus hex digits to be a
2186 * byte.
2187 */
2188 char *p, *wordstart;
2189
2190 if (!picture) {
2191 if (par->w < 0 || par->h < 0) {
2192 printf("failed to read width and height\n");
2193 return 1;
2194 }
2195 picture = snewn(par->w * par->h, unsigned char);
2196 for (i = 0; i < par->w * par->h; i++)
2197 picture[i] = GRID_UNKNOWN;
2198 }
2199
2200 p = buf;
2201 while (*p) {
2202 while (*p && (*p == ',' || isspace((unsigned char)*p)))
2203 p++;
2204 wordstart = p;
2205 while (*p && !(*p == ',' || *p == '}' ||
2206 isspace((unsigned char)*p)))
2207 p++;
2208 if (*p)
2209 *p++ = '\0';
2210
2211 if (wordstart[0] == '0' &&
2212 (wordstart[1] == 'x' || wordstart[1] == 'X') &&
2213 !wordstart[2 + strspn(wordstart+2,
2214 "0123456789abcdefABCDEF")]) {
2215 unsigned long byte = strtoul(wordstart+2, NULL, 16);
2216 for (i = 0; i < 8; i++) {
2217 int bit = (byte >> i) & 1;
2218 if (y < par->h && x < par->w)
2219 picture[y * par->w + x] =
2220 bit ? GRID_FULL : GRID_EMPTY;
2221 x++;
2222 }
2223
2224 if (x >= par->w) {
2225 x = 0;
2226 y++;
2227 }
2228 }
2229 }
2230 }
2231 }
2232
2233 for (i = 0; i < par->w * par->h; i++)
2234 if (picture[i] == GRID_UNKNOWN) {
2235 fprintf(stderr, "failed to read enough bitmap data\n");
2236 return 1;
2237 }
2238
2239 rs = random_new((void*)&seed, sizeof(time_t));
2240
2241 desc = new_game_desc(par, rs, NULL, FALSE);
2242 params = encode_params(par, FALSE);
2243 printf("%s:%s\n", params, desc);
2244
2245 sfree(desc);
2246 sfree(params);
2247 free_params(par);
2248 random_free(rs);
2249
2250 return 0;
2251}
2252
2253#endif
2254
2255/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/pattern.html b/apps/plugins/puzzles/src/pattern.html
new file mode 100644
index 0000000000..35f570ec85
--- /dev/null
+++ b/apps/plugins/puzzles/src/pattern.html
@@ -0,0 +1,50 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Pattern</title>
7<link rel="previous" href="netslide.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="solo.html">
12</head>
13<body>
14<p><a href="netslide.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="solo.html">Next</a></p>
15<h1><a name="C10"></a>Chapter 10: <a name="i0"></a>Pattern</h1>
16<p>
17You have a grid of squares, which must all be filled in either black or white. Beside each row of the grid are listed the lengths of the runs of black squares on that row; above each column are listed the lengths of the runs of black squares in that column. Your aim is to fill in the entire grid black or white.
18</p>
19<p>
20I first saw this puzzle form around 1995, under the name &#8216;<a name="i1"></a>nonograms&#8217;. I've seen it in various places since then, under different names.
21</p>
22<p>
23Normally, puzzles of this type turn out to be a meaningful picture of something once you've solved them. However, since this version generates the puzzles automatically, they will just look like random groupings of squares. (One user has suggested that this is actually a <em>good</em> thing, since it prevents you from guessing the colour of squares based on the picture, and forces you to use logic instead.) The advantage, though, is that you never run out of them.
24</p>
25<h2><a name="S10.1"></a>10.1 <a name="i2"></a>Pattern controls</h2>
26<p>
27This game is played with the mouse.
28</p>
29<p>
30Left-click in a square to colour it black. Right-click to colour it white. If you make a mistake, you can middle-click, or hold down Shift while clicking with any button, to colour the square in the default grey (meaning &#8216;undecided&#8217;) again.
31</p>
32<p>
33You can click and drag with the left or right mouse button to colour a vertical or horizontal line of squares black or white at a time (respectively). If you click and drag with the middle button, or with Shift held down, you can colour a whole rectangle of squares grey.
34</p>
35<p>
36You can also move around the grid with the cursor keys. Pressing the return key will cycle the current cell through empty, then black, then white, then empty, and the space bar does the same cycle in reverse.
37</p>
38<p>
39Moving the cursor while holding Control will colour the moved-over squares black. Holding Shift will colour the moved-over squares white, and holding both will colour them grey.
40</p>
41<p>
42(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
43</p>
44<h2><a name="S10.2"></a>10.2 <a name="i3"></a>Pattern parameters</h2>
45<p>
46The only options available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu are <em>Width</em> and <em>Height</em>, which are self-explanatory.
47</p>
48
49<hr><address></address></body>
50</html>
diff --git a/apps/plugins/puzzles/src/pearl.R b/apps/plugins/puzzles/src/pearl.R
new file mode 100644
index 0000000000..79bc7325c7
--- /dev/null
+++ b/apps/plugins/puzzles/src/pearl.R
@@ -0,0 +1,23 @@
1# -*- makefile -*-
2
3PEARL_EXTRA = dsf tree234 grid penrose loopgen tdq
4
5pearl : [X] GTK COMMON pearl PEARL_EXTRA pearl-icon|no-icon
6pearl : [G] WINDOWS COMMON pearl PEARL_EXTRA pearl.res?
7
8pearlbench : [U] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE m.lib
9pearlbench : [C] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE
10
11ALL += pearl[COMBINED] PEARL_EXTRA
12
13!begin am gtk
14GAMES += pearl
15!end
16
17!begin >list.c
18 A(pearl) \
19!end
20
21!begin >gamedesc.txt
22pearl:pearl.exe:Pearl:Loop-drawing puzzle:Draw a single closed loop, given clues about corner and straight squares.
23!end
diff --git a/apps/plugins/puzzles/src/pearl.c b/apps/plugins/puzzles/src/pearl.c
new file mode 100644
index 0000000000..c6c305f3f2
--- /dev/null
+++ b/apps/plugins/puzzles/src/pearl.c
@@ -0,0 +1,2770 @@
1/*
2 * pearl.c: Nikoli's `Masyu' puzzle.
3 */
4
5/*
6 * TODO:
7 *
8 * - The current keyboard cursor mechanism works well on ordinary PC
9 * keyboards, but for platforms with only arrow keys and a select
10 * button or two, we may at some point need a simpler one which can
11 * handle 'x' markings without needing shift keys. For instance, a
12 * cursor with twice the grid resolution, so that it can range
13 * across face centres, edge centres and vertices; 'clicks' on face
14 * centres begin a drag as currently, clicks on edges toggle
15 * markings, and clicks on vertices are ignored (but it would be
16 * too confusing not to let the cursor rest on them). But I'm
17 * pretty sure that would be less pleasant to play on a full
18 * keyboard, so probably a #ifdef would be the thing.
19 *
20 * - Generation is still pretty slow, due to difficulty coming up in
21 * the first place with a loop that makes a soluble puzzle even
22 * with all possible clues filled in.
23 * + A possible alternative strategy to further tuning of the
24 * existing loop generator would be to throw the entire
25 * mechanism out and instead write a different generator from
26 * scratch which evolves the solution along with the puzzle:
27 * place a few clues, nail down a bit of the loop, place another
28 * clue, nail down some more, etc. However, I don't have a
29 * detailed plan for any such mechanism, so it may be a pipe
30 * dream.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <assert.h>
37#include <ctype.h>
38#include <math.h>
39
40#include "puzzles.h"
41#include "grid.h"
42#include "loopgen.h"
43
44#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
45
46#define NOCLUE 0
47#define CORNER 1
48#define STRAIGHT 2
49
50#define R 1
51#define U 2
52#define L 4
53#define D 8
54
55#define DX(d) ( ((d)==R) - ((d)==L) )
56#define DY(d) ( ((d)==D) - ((d)==U) )
57
58#define F(d) (((d << 2) | (d >> 2)) & 0xF)
59#define C(d) (((d << 3) | (d >> 1)) & 0xF)
60#define A(d) (((d << 1) | (d >> 3)) & 0xF)
61
62#define LR (L | R)
63#define RL (R | L)
64#define UD (U | D)
65#define DU (D | U)
66#define LU (L | U)
67#define UL (U | L)
68#define LD (L | D)
69#define DL (D | L)
70#define RU (R | U)
71#define UR (U | R)
72#define RD (R | D)
73#define DR (D | R)
74#define BLANK 0
75#define UNKNOWN 15
76
77#define bLR (1 << LR)
78#define bRL (1 << RL)
79#define bUD (1 << UD)
80#define bDU (1 << DU)
81#define bLU (1 << LU)
82#define bUL (1 << UL)
83#define bLD (1 << LD)
84#define bDL (1 << DL)
85#define bRU (1 << RU)
86#define bUR (1 << UR)
87#define bRD (1 << RD)
88#define bDR (1 << DR)
89#define bBLANK (1 << BLANK)
90
91enum {
92 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
93 COL_CURSOR_BACKGROUND = COL_LOWLIGHT,
94 COL_BLACK, COL_WHITE,
95 COL_ERROR, COL_GRID, COL_FLASH,
96 COL_DRAGON, COL_DRAGOFF,
97 NCOLOURS
98};
99
100/* Macro ickery copied from slant.c */
101#define DIFFLIST(A) \
102 A(EASY,Easy,e) \
103 A(TRICKY,Tricky,t)
104#define ENUM(upper,title,lower) DIFF_ ## upper,
105#define TITLE(upper,title,lower) #title,
106#define ENCODE(upper,title,lower) #lower
107#define CONFIG(upper,title,lower) ":" #title
108enum { DIFFLIST(ENUM) DIFFCOUNT };
109static char const *const pearl_diffnames[] = { DIFFLIST(TITLE) "(count)" };
110static char const pearl_diffchars[] = DIFFLIST(ENCODE);
111#define DIFFCONFIG DIFFLIST(CONFIG)
112
113struct game_params {
114 int w, h;
115 int difficulty;
116 int nosolve; /* XXX remove me! */
117};
118
119struct shared_state {
120 int w, h, sz;
121 char *clues; /* size w*h */
122 int refcnt;
123};
124
125#define INGRID(state, gx, gy) ((gx) >= 0 && (gx) < (state)->shared->w && \
126 (gy) >= 0 && (gy) < (state)->shared->h)
127struct game_state {
128 struct shared_state *shared;
129 char *lines; /* size w*h: lines placed */
130 char *errors; /* size w*h: errors detected */
131 char *marks; /* size w*h: 'no line here' marks placed. */
132 int completed, used_solve;
133};
134
135#define DEFAULT_PRESET 3
136
137static const struct game_params pearl_presets[] = {
138 {6, 6, DIFF_EASY},
139 {6, 6, DIFF_TRICKY},
140 {8, 8, DIFF_EASY},
141 {8, 8, DIFF_TRICKY},
142 {10, 10, DIFF_EASY},
143 {10, 10, DIFF_TRICKY},
144 {12, 8, DIFF_EASY},
145 {12, 8, DIFF_TRICKY},
146};
147
148static game_params *default_params(void)
149{
150 game_params *ret = snew(game_params);
151
152 *ret = pearl_presets[DEFAULT_PRESET];
153 ret->nosolve = FALSE;
154
155 return ret;
156}
157
158static int game_fetch_preset(int i, char **name, game_params **params)
159{
160 game_params *ret;
161 char buf[64];
162
163 if (i < 0 || i >= lenof(pearl_presets)) return FALSE;
164
165 ret = default_params();
166 *ret = pearl_presets[i]; /* struct copy */
167 *params = ret;
168
169 sprintf(buf, "%dx%d %s",
170 pearl_presets[i].w, pearl_presets[i].h,
171 pearl_diffnames[pearl_presets[i].difficulty]);
172 *name = dupstr(buf);
173
174 return TRUE;
175}
176
177static void free_params(game_params *params)
178{
179 sfree(params);
180}
181
182static game_params *dup_params(const game_params *params)
183{
184 game_params *ret = snew(game_params);
185 *ret = *params; /* structure copy */
186 return ret;
187}
188
189static void decode_params(game_params *ret, char const *string)
190{
191 ret->w = ret->h = atoi(string);
192 while (*string && isdigit((unsigned char) *string)) ++string;
193 if (*string == 'x') {
194 string++;
195 ret->h = atoi(string);
196 while (*string && isdigit((unsigned char)*string)) string++;
197 }
198
199 ret->difficulty = DIFF_EASY;
200 if (*string == 'd') {
201 int i;
202 string++;
203 for (i = 0; i < DIFFCOUNT; i++)
204 if (*string == pearl_diffchars[i])
205 ret->difficulty = i;
206 if (*string) string++;
207 }
208
209 ret->nosolve = FALSE;
210 if (*string == 'n') {
211 ret->nosolve = TRUE;
212 string++;
213 }
214}
215
216static char *encode_params(const game_params *params, int full)
217{
218 char buf[256];
219 sprintf(buf, "%dx%d", params->w, params->h);
220 if (full)
221 sprintf(buf + strlen(buf), "d%c%s",
222 pearl_diffchars[params->difficulty],
223 params->nosolve ? "n" : "");
224 return dupstr(buf);
225}
226
227static config_item *game_configure(const game_params *params)
228{
229 config_item *ret;
230 char buf[64];
231
232 ret = snewn(5, config_item);
233
234 ret[0].name = "Width";
235 ret[0].type = C_STRING;
236 sprintf(buf, "%d", params->w);
237 ret[0].sval = dupstr(buf);
238 ret[0].ival = 0;
239
240 ret[1].name = "Height";
241 ret[1].type = C_STRING;
242 sprintf(buf, "%d", params->h);
243 ret[1].sval = dupstr(buf);
244 ret[1].ival = 0;
245
246 ret[2].name = "Difficulty";
247 ret[2].type = C_CHOICES;
248 ret[2].sval = DIFFCONFIG;
249 ret[2].ival = params->difficulty;
250
251 ret[3].name = "Allow unsoluble";
252 ret[3].type = C_BOOLEAN;
253 ret[3].sval = NULL;
254 ret[3].ival = params->nosolve;
255
256 ret[4].name = NULL;
257 ret[4].type = C_END;
258 ret[4].sval = NULL;
259 ret[4].ival = 0;
260
261 return ret;
262}
263
264static game_params *custom_params(const config_item *cfg)
265{
266 game_params *ret = snew(game_params);
267
268 ret->w = atoi(cfg[0].sval);
269 ret->h = atoi(cfg[1].sval);
270 ret->difficulty = cfg[2].ival;
271 ret->nosolve = cfg[3].ival;
272
273 return ret;
274}
275
276static char *validate_params(const game_params *params, int full)
277{
278 if (params->w < 5) return "Width must be at least five";
279 if (params->h < 5) return "Height must be at least five";
280 if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT)
281 return "Unknown difficulty level";
282
283 return NULL;
284}
285
286/* ----------------------------------------------------------------------
287 * Solver.
288 */
289
290int pearl_solve(int w, int h, char *clues, char *result,
291 int difficulty, int partial)
292{
293 int W = 2*w+1, H = 2*h+1;
294 short *workspace;
295 int *dsf, *dsfsize;
296 int x, y, b, d;
297 int ret = -1;
298
299 /*
300 * workspace[(2*y+1)*W+(2*x+1)] indicates the possible nature
301 * of the square (x,y), as a logical OR of bitfields.
302 *
303 * workspace[(2*y)*W+(2*x+1)], for x odd and y even, indicates
304 * whether the horizontal edge between (x,y) and (x+1,y) is
305 * connected (1), disconnected (2) or unknown (3).
306 *
307 * workspace[(2*y+1)*W+(2*x)], indicates the same about the
308 * vertical edge between (x,y) and (x,y+1).
309 *
310 * Initially, every square is considered capable of being in
311 * any of the seven possible states (two straights, four
312 * corners and empty), except those corresponding to clue
313 * squares which are more restricted.
314 *
315 * Initially, all edges are unknown, except the ones around the
316 * grid border which are known to be disconnected.
317 */
318 workspace = snewn(W*H, short);
319 for (x = 0; x < W*H; x++)
320 workspace[x] = 0;
321 /* Square states */
322 for (y = 0; y < h; y++)
323 for (x = 0; x < w; x++)
324 switch (clues[y*w+x]) {
325 case CORNER:
326 workspace[(2*y+1)*W+(2*x+1)] = bLU|bLD|bRU|bRD;
327 break;
328 case STRAIGHT:
329 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD;
330 break;
331 default:
332 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD|bLU|bLD|bRU|bRD|bBLANK;
333 break;
334 }
335 /* Horizontal edges */
336 for (y = 0; y <= h; y++)
337 for (x = 0; x < w; x++)
338 workspace[(2*y)*W+(2*x+1)] = (y==0 || y==h ? 2 : 3);
339 /* Vertical edges */
340 for (y = 0; y < h; y++)
341 for (x = 0; x <= w; x++)
342 workspace[(2*y+1)*W+(2*x)] = (x==0 || x==w ? 2 : 3);
343
344 /*
345 * We maintain a dsf of connected squares, together with a
346 * count of the size of each equivalence class.
347 */
348 dsf = snewn(w*h, int);
349 dsfsize = snewn(w*h, int);
350
351 /*
352 * Now repeatedly try to find something we can do.
353 */
354 while (1) {
355 int done_something = FALSE;
356
357#ifdef SOLVER_DIAGNOSTICS
358 for (y = 0; y < H; y++) {
359 for (x = 0; x < W; x++)
360 printf("%*x", (x&1) ? 5 : 2, workspace[y*W+x]);
361 printf("\n");
362 }
363#endif
364
365 /*
366 * Go through the square state words, and discard any
367 * square state which is inconsistent with known facts
368 * about the edges around the square.
369 */
370 for (y = 0; y < h; y++)
371 for (x = 0; x < w; x++) {
372 for (b = 0; b < 0xD; b++)
373 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) {
374 /*
375 * If any edge of this square is known to
376 * be connected when state b would require
377 * it disconnected, or vice versa, discard
378 * the state.
379 */
380 for (d = 1; d <= 8; d += d) {
381 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
382 if (workspace[ey*W+ex] ==
383 ((b & d) ? 2 : 1)) {
384 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<b);
385#ifdef SOLVER_DIAGNOSTICS
386 printf("edge (%d,%d)-(%d,%d) rules out state"
387 " %d for square (%d,%d)\n",
388 ex/2, ey/2, (ex+1)/2, (ey+1)/2,
389 b, x, y);
390#endif
391 done_something = TRUE;
392 break;
393 }
394 }
395 }
396
397 /*
398 * Consistency check: each square must have at
399 * least one state left!
400 */
401 if (!workspace[(2*y+1)*W+(2*x+1)]) {
402#ifdef SOLVER_DIAGNOSTICS
403 printf("edge check at (%d,%d): inconsistency\n", x, y);
404#endif
405 ret = 0;
406 goto cleanup;
407 }
408 }
409
410 /*
411 * Now go through the states array again, and nail down any
412 * unknown edge if one of its neighbouring squares makes it
413 * known.
414 */
415 for (y = 0; y < h; y++)
416 for (x = 0; x < w; x++) {
417 int edgeor = 0, edgeand = 15;
418
419 for (b = 0; b < 0xD; b++)
420 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) {
421 edgeor |= b;
422 edgeand &= b;
423 }
424
425 /*
426 * Now any bit clear in edgeor marks a disconnected
427 * edge, and any bit set in edgeand marks a
428 * connected edge.
429 */
430
431 /* First check consistency: neither bit is both! */
432 if (edgeand & ~edgeor) {
433#ifdef SOLVER_DIAGNOSTICS
434 printf("square check at (%d,%d): inconsistency\n", x, y);
435#endif
436 ret = 0;
437 goto cleanup;
438 }
439
440 for (d = 1; d <= 8; d += d) {
441 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
442
443 if (!(edgeor & d) && workspace[ey*W+ex] == 3) {
444 workspace[ey*W+ex] = 2;
445 done_something = TRUE;
446#ifdef SOLVER_DIAGNOSTICS
447 printf("possible states of square (%d,%d) force edge"
448 " (%d,%d)-(%d,%d) to be disconnected\n",
449 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2);
450#endif
451 } else if ((edgeand & d) && workspace[ey*W+ex] == 3) {
452 workspace[ey*W+ex] = 1;
453 done_something = TRUE;
454#ifdef SOLVER_DIAGNOSTICS
455 printf("possible states of square (%d,%d) force edge"
456 " (%d,%d)-(%d,%d) to be connected\n",
457 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2);
458#endif
459 }
460 }
461 }
462
463 if (done_something)
464 continue;
465
466 /*
467 * Now for longer-range clue-based deductions (using the
468 * rules that a corner clue must connect to two straight
469 * squares, and a straight clue must connect to at least
470 * one corner square).
471 */
472 for (y = 0; y < h; y++)
473 for (x = 0; x < w; x++)
474 switch (clues[y*w+x]) {
475 case CORNER:
476 for (d = 1; d <= 8; d += d) {
477 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
478 int fx = ex + DX(d), fy = ey + DY(d);
479 int type = d | F(d);
480
481 if (workspace[ey*W+ex] == 1) {
482 /*
483 * If a corner clue is connected on any
484 * edge, then we can immediately nail
485 * down the square beyond that edge as
486 * being a straight in the appropriate
487 * direction.
488 */
489 if (workspace[fy*W+fx] != (1<<type)) {
490 workspace[fy*W+fx] = (1<<type);
491 done_something = TRUE;
492#ifdef SOLVER_DIAGNOSTICS
493 printf("corner clue at (%d,%d) forces square "
494 "(%d,%d) into state %d\n", x, y,
495 fx/2, fy/2, type);
496#endif
497
498 }
499 } else if (workspace[ey*W+ex] == 3) {
500 /*
501 * Conversely, if a corner clue is
502 * separated by an unknown edge from a
503 * square which _cannot_ be a straight
504 * in the appropriate direction, we can
505 * mark that edge as disconnected.
506 */
507 if (!(workspace[fy*W+fx] & (1<<type))) {
508 workspace[ey*W+ex] = 2;
509 done_something = TRUE;
510#ifdef SOLVER_DIAGNOSTICS
511 printf("corner clue at (%d,%d), plus square "
512 "(%d,%d) not being state %d, "
513 "disconnects edge (%d,%d)-(%d,%d)\n",
514 x, y, fx/2, fy/2, type,
515 ex/2, ey/2, (ex+1)/2, (ey+1)/2);
516#endif
517
518 }
519 }
520 }
521
522 break;
523 case STRAIGHT:
524 /*
525 * If a straight clue is between two squares
526 * neither of which is capable of being a
527 * corner connected to it, then the straight
528 * clue cannot point in that direction.
529 */
530 for (d = 1; d <= 2; d += d) {
531 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d);
532 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d);
533 int type = d | F(d);
534
535 if (!(workspace[(2*y+1)*W+(2*x+1)] & (1<<type)))
536 continue;
537
538 if (!(workspace[fy*W+fx] & ((1<<(F(d)|A(d))) |
539 (1<<(F(d)|C(d))))) &&
540 !(workspace[gy*W+gx] & ((1<<( d |A(d))) |
541 (1<<( d |C(d)))))) {
542 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<type);
543 done_something = TRUE;
544#ifdef SOLVER_DIAGNOSTICS
545 printf("straight clue at (%d,%d) cannot corner at "
546 "(%d,%d) or (%d,%d) so is not state %d\n",
547 x, y, fx/2, fy/2, gx/2, gy/2, type);
548#endif
549 }
550
551 }
552
553 /*
554 * If a straight clue with known direction is
555 * connected on one side to a known straight,
556 * then on the other side it must be a corner.
557 */
558 for (d = 1; d <= 8; d += d) {
559 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d);
560 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d);
561 int type = d | F(d);
562
563 if (workspace[(2*y+1)*W+(2*x+1)] != (1<<type))
564 continue;
565
566 if (!(workspace[fy*W+fx] &~ (bLR|bUD)) &&
567 (workspace[gy*W+gx] &~ (bLU|bLD|bRU|bRD))) {
568 workspace[gy*W+gx] &= (bLU|bLD|bRU|bRD);
569 done_something = TRUE;
570#ifdef SOLVER_DIAGNOSTICS
571 printf("straight clue at (%d,%d) connecting to "
572 "straight at (%d,%d) makes (%d,%d) a "
573 "corner\n", x, y, fx/2, fy/2, gx/2, gy/2);
574#endif
575 }
576
577 }
578 break;
579 }
580
581 if (done_something)
582 continue;
583
584 /*
585 * Now detect shortcut loops.
586 */
587
588 {
589 int nonblanks, loopclass;
590
591 dsf_init(dsf, w*h);
592 for (x = 0; x < w*h; x++)
593 dsfsize[x] = 1;
594
595 /*
596 * First go through the edge entries and update the dsf
597 * of which squares are connected to which others. We
598 * also track the number of squares in each equivalence
599 * class, and count the overall number of
600 * known-non-blank squares.
601 *
602 * In the process of doing this, we must notice if a
603 * loop has already been formed. If it has, we blank
604 * out any square which isn't part of that loop
605 * (failing a consistency check if any such square does
606 * not have BLANK as one of its remaining options) and
607 * exit the deduction loop with success.
608 */
609 nonblanks = 0;
610 loopclass = -1;
611 for (y = 1; y < H-1; y++)
612 for (x = 1; x < W-1; x++)
613 if ((y ^ x) & 1) {
614 /*
615 * (x,y) are the workspace coordinates of
616 * an edge field. Compute the normal-space
617 * coordinates of the squares it connects.
618 */
619 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax;
620 int bx = x/2, by = y/2, bc = by*w+bx;
621
622 /*
623 * If the edge is connected, do the dsf
624 * thing.
625 */
626 if (workspace[y*W+x] == 1) {
627 int ae, be;
628
629 ae = dsf_canonify(dsf, ac);
630 be = dsf_canonify(dsf, bc);
631
632 if (ae == be) {
633 /*
634 * We have a loop!
635 */
636 if (loopclass != -1) {
637 /*
638 * In fact, we have two
639 * separate loops, which is
640 * doom.
641 */
642#ifdef SOLVER_DIAGNOSTICS
643 printf("two loops found in grid!\n");
644#endif
645 ret = 0;
646 goto cleanup;
647 }
648 loopclass = ae;
649 } else {
650 /*
651 * Merge the two equivalence
652 * classes.
653 */
654 int size = dsfsize[ae] + dsfsize[be];
655 dsf_merge(dsf, ac, bc);
656 ae = dsf_canonify(dsf, ac);
657 dsfsize[ae] = size;
658 }
659 }
660 } else if ((y & x) & 1) {
661 /*
662 * (x,y) are the workspace coordinates of a
663 * square field. If the square is
664 * definitely not blank, count it.
665 */
666 if (!(workspace[y*W+x] & bBLANK))
667 nonblanks++;
668 }
669
670 /*
671 * If we discovered an existing loop above, we must now
672 * blank every square not part of it, and exit the main
673 * deduction loop.
674 */
675 if (loopclass != -1) {
676#ifdef SOLVER_DIAGNOSTICS
677 printf("loop found in grid!\n");
678#endif
679 for (y = 0; y < h; y++)
680 for (x = 0; x < w; x++)
681 if (dsf_canonify(dsf, y*w+x) != loopclass) {
682 if (workspace[(y*2+1)*W+(x*2+1)] & bBLANK) {
683 workspace[(y*2+1)*W+(x*2+1)] = bBLANK;
684 } else {
685 /*
686 * This square is not part of the
687 * loop, but is known non-blank. We
688 * have goofed.
689 */
690#ifdef SOLVER_DIAGNOSTICS
691 printf("non-blank square (%d,%d) found outside"
692 " loop!\n", x, y);
693#endif
694 ret = 0;
695 goto cleanup;
696 }
697 }
698 /*
699 * And we're done.
700 */
701 ret = 1;
702 break;
703 }
704
705 /* Further deductions are considered 'tricky'. */
706 if (difficulty == DIFF_EASY) goto done_deductions;
707
708 /*
709 * Now go through the workspace again and mark any edge
710 * which would cause a shortcut loop (i.e. would
711 * connect together two squares in the same equivalence
712 * class, and that equivalence class does not contain
713 * _all_ the known-non-blank squares currently in the
714 * grid) as disconnected. Also, mark any _square state_
715 * which would cause a shortcut loop as disconnected.
716 */
717 for (y = 1; y < H-1; y++)
718 for (x = 1; x < W-1; x++)
719 if ((y ^ x) & 1) {
720 /*
721 * (x,y) are the workspace coordinates of
722 * an edge field. Compute the normal-space
723 * coordinates of the squares it connects.
724 */
725 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax;
726 int bx = x/2, by = y/2, bc = by*w+bx;
727
728 /*
729 * If the edge is currently unknown, and
730 * sits between two squares in the same
731 * equivalence class, and the size of that
732 * class is less than nonblanks, then
733 * connecting this edge would be a shortcut
734 * loop and so we must not do so.
735 */
736 if (workspace[y*W+x] == 3) {
737 int ae, be;
738
739 ae = dsf_canonify(dsf, ac);
740 be = dsf_canonify(dsf, bc);
741
742 if (ae == be) {
743 /*
744 * We have a loop. Is it a shortcut?
745 */
746 if (dsfsize[ae] < nonblanks) {
747 /*
748 * Yes! Mark this edge disconnected.
749 */
750 workspace[y*W+x] = 2;
751 done_something = TRUE;
752#ifdef SOLVER_DIAGNOSTICS
753 printf("edge (%d,%d)-(%d,%d) would create"
754 " a shortcut loop, hence must be"
755 " disconnected\n", x/2, y/2,
756 (x+1)/2, (y+1)/2);
757#endif
758 }
759 }
760 }
761 } else if ((y & x) & 1) {
762 /*
763 * (x,y) are the workspace coordinates of a
764 * square field. Go through its possible
765 * (non-blank) states and see if any gives
766 * rise to a shortcut loop.
767 *
768 * This is slightly fiddly, because we have
769 * to check whether this square is already
770 * part of the same equivalence class as
771 * the things it's joining.
772 */
773 int ae = dsf_canonify(dsf, (y/2)*w+(x/2));
774
775 for (b = 2; b < 0xD; b++)
776 if (workspace[y*W+x] & (1<<b)) {
777 /*
778 * Find the equivalence classes of
779 * the two squares this one would
780 * connect if it were in this
781 * state.
782 */
783 int e = -1;
784
785 for (d = 1; d <= 8; d += d) if (b & d) {
786 int xx = x/2 + DX(d), yy = y/2 + DY(d);
787 int ee = dsf_canonify(dsf, yy*w+xx);
788
789 if (e == -1)
790 ee = e;
791 else if (e != ee)
792 e = -2;
793 }
794
795 if (e >= 0) {
796 /*
797 * This square state would form
798 * a loop on equivalence class
799 * e. Measure the size of that
800 * loop, and see if it's a
801 * shortcut.
802 */
803 int loopsize = dsfsize[e];
804 if (e != ae)
805 loopsize++;/* add the square itself */
806 if (loopsize < nonblanks) {
807 /*
808 * It is! Mark this square
809 * state invalid.
810 */
811 workspace[y*W+x] &= ~(1<<b);
812 done_something = TRUE;
813#ifdef SOLVER_DIAGNOSTICS
814 printf("square (%d,%d) would create a "
815 "shortcut loop in state %d, "
816 "hence cannot be\n",
817 x/2, y/2, b);
818#endif
819 }
820 }
821 }
822 }
823 }
824
825done_deductions:
826
827 if (done_something)
828 continue;
829
830 /*
831 * If we reach here, there is nothing left we can do.
832 * Return 2 for ambiguous puzzle.
833 */
834 ret = 2;
835 break;
836 }
837
838cleanup:
839
840 /*
841 * If ret = 1 then we've successfully achieved a solution. This
842 * means that we expect every square to be nailed down to
843 * exactly one possibility. If this is the case, or if the caller
844 * asked for a partial solution anyway, transcribe those
845 * possibilities into the result array.
846 */
847 if (ret == 1 || partial) {
848 for (y = 0; y < h; y++) {
849 for (x = 0; x < w; x++) {
850 for (b = 0; b < 0xD; b++)
851 if (workspace[(2*y+1)*W+(2*x+1)] == (1<<b)) {
852 result[y*w+x] = b;
853 break;
854 }
855 if (ret == 1) assert(b < 0xD); /* we should have had a break by now */
856 }
857 }
858 }
859
860 sfree(dsfsize);
861 sfree(dsf);
862 sfree(workspace);
863 assert(ret >= 0);
864 return ret;
865}
866
867/* ----------------------------------------------------------------------
868 * Loop generator.
869 */
870
871/*
872 * We use the loop generator code from loopy, hard-coding to a square
873 * grid of the appropriate size. Knowing the grid layout and the tile
874 * size we can shrink that to our small grid and then make our line
875 * layout from the face colour info.
876 *
877 * We provide a bias function to the loop generator which tries to
878 * bias in favour of loops with more scope for Pearl black clues. This
879 * seems to improve the success rate of the puzzle generator, in that
880 * such loops have a better chance of being soluble with all valid
881 * clues put in.
882 */
883
884struct pearl_loopgen_bias_ctx {
885 /*
886 * Our bias function counts the number of 'black clue' corners
887 * (i.e. corners adjacent to two straights) in both the
888 * BLACK/nonBLACK and WHITE/nonWHITE boundaries. In order to do
889 * this, we must:
890 *
891 * - track the edges that are part of each of those loops
892 * - track the types of vertex in each loop (corner, straight,
893 * none)
894 * - track the current black-clue status of each vertex in each
895 * loop.
896 *
897 * Each of these chunks of data is updated incrementally from the
898 * previous one, to avoid slowdown due to the bias function
899 * rescanning the whole grid every time it's called.
900 *
901 * So we need a lot of separate arrays, plus a tdq for each one,
902 * and we must repeat it all twice for the BLACK and WHITE
903 * boundaries.
904 */
905 struct pearl_loopgen_bias_ctx_boundary {
906 int colour; /* FACE_WHITE or FACE_BLACK */
907
908 char *edges; /* is each edge part of the loop? */
909 tdq *edges_todo;
910
911 char *vertextypes; /* bits 0-3 == outgoing edge bitmap;
912 * bit 4 set iff corner clue.
913 * Hence, 0 means non-vertex;
914 * nonzero but bit 4 zero = straight. */
915 int *neighbour[2]; /* indices of neighbour vertices in loop */
916 tdq *vertextypes_todo;
917
918 char *blackclues; /* is each vertex a black clue site? */
919 tdq *blackclues_todo;
920 } boundaries[2]; /* boundaries[0]=WHITE, [1]=BLACK */
921
922 char *faces; /* remember last-seen colour of each face */
923 tdq *faces_todo;
924
925 int score;
926
927 grid *g;
928};
929int pearl_loopgen_bias(void *vctx, char *board, int face)
930{
931 struct pearl_loopgen_bias_ctx *ctx = (struct pearl_loopgen_bias_ctx *)vctx;
932 grid *g = ctx->g;
933 int oldface, newface;
934 int i, j, k;
935
936 tdq_add(ctx->faces_todo, face);
937 while ((j = tdq_remove(ctx->faces_todo)) >= 0) {
938 oldface = ctx->faces[j];
939 ctx->faces[j] = newface = board[j];
940 for (i = 0; i < 2; i++) {
941 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i];
942 int c = b->colour;
943
944 /*
945 * If the face has changed either from or to colour c, we need
946 * to reprocess the edges for this boundary.
947 */
948 if (oldface == c || newface == c) {
949 grid_face *f = &g->faces[face];
950 for (k = 0; k < f->order; k++)
951 tdq_add(b->edges_todo, f->edges[k] - g->edges);
952 }
953 }
954 }
955
956 for (i = 0; i < 2; i++) {
957 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i];
958 int c = b->colour;
959
960 /*
961 * Go through the to-do list of edges. For each edge, decide
962 * anew whether it's part of this boundary or not. Any edge
963 * that changes state has to have both its endpoints put on
964 * the vertextypes_todo list.
965 */
966 while ((j = tdq_remove(b->edges_todo)) >= 0) {
967 grid_edge *e = &g->edges[j];
968 int fc1 = e->face1 ? board[e->face1 - g->faces] : FACE_BLACK;
969 int fc2 = e->face2 ? board[e->face2 - g->faces] : FACE_BLACK;
970 int oldedge = b->edges[j];
971 int newedge = (fc1==c) ^ (fc2==c);
972 if (oldedge != newedge) {
973 b->edges[j] = newedge;
974 tdq_add(b->vertextypes_todo, e->dot1 - g->dots);
975 tdq_add(b->vertextypes_todo, e->dot2 - g->dots);
976 }
977 }
978
979 /*
980 * Go through the to-do list of vertices whose types need
981 * refreshing. For each one, decide whether it's a corner, a
982 * straight, or a vertex not in the loop, and in the former
983 * two cases also work out the indices of its neighbour
984 * vertices along the loop. Any vertex that changes state must
985 * be put back on the to-do list for deciding if it's a black
986 * clue site, and so must its two new neighbours _and_ its two
987 * old neighbours.
988 */
989 while ((j = tdq_remove(b->vertextypes_todo)) >= 0) {
990 grid_dot *d = &g->dots[j];
991 int neighbours[2], type = 0, n = 0;
992
993 for (k = 0; k < d->order; k++) {
994 grid_edge *e = d->edges[k];
995 grid_dot *d2 = (e->dot1 == d ? e->dot2 : e->dot1);
996 /* dir == 0,1,2,3 for an edge going L,U,R,D */
997 int dir = (d->y == d2->y) + 2*(d->x+d->y > d2->x+d2->y);
998 int ei = e - g->edges;
999 if (b->edges[ei]) {
1000 type |= 1 << dir;
1001 neighbours[n] = d2 - g->dots;
1002 n++;
1003 }
1004 }
1005
1006 /*
1007 * Decide if it's a corner, and set the corner flag if so.
1008 */
1009 if (type != 0 && type != 0x5 && type != 0xA)
1010 type |= 0x10;
1011
1012 if (type != b->vertextypes[j]) {
1013 /*
1014 * Recompute old neighbours, if any.
1015 */
1016 if (b->vertextypes[j]) {
1017 tdq_add(b->blackclues_todo, b->neighbour[0][j]);
1018 tdq_add(b->blackclues_todo, b->neighbour[1][j]);
1019 }
1020 /*
1021 * Recompute this vertex.
1022 */
1023 tdq_add(b->blackclues_todo, j);
1024 b->vertextypes[j] = type;
1025 /*
1026 * Recompute new neighbours, if any.
1027 */
1028 if (b->vertextypes[j]) {
1029 b->neighbour[0][j] = neighbours[0];
1030 b->neighbour[1][j] = neighbours[1];
1031 tdq_add(b->blackclues_todo, b->neighbour[0][j]);
1032 tdq_add(b->blackclues_todo, b->neighbour[1][j]);
1033 }
1034 }
1035 }
1036
1037 /*
1038 * Go through the list of vertices which we must check to see
1039 * if they're black clue sites. Each one is a black clue site
1040 * iff it is a corner and its loop neighbours are non-corners.
1041 * Adjust the running total of black clues we've counted.
1042 */
1043 while ((j = tdq_remove(b->blackclues_todo)) >= 0) {
1044 ctx->score -= b->blackclues[j];
1045 b->blackclues[j] = ((b->vertextypes[j] & 0x10) &&
1046 !((b->vertextypes[b->neighbour[0][j]] |
1047 b->vertextypes[b->neighbour[1][j]])
1048 & 0x10));
1049 ctx->score += b->blackclues[j];
1050 }
1051 }
1052
1053 return ctx->score;
1054}
1055
1056void pearl_loopgen(int w, int h, char *lines, random_state *rs)
1057{
1058 grid *g = grid_new(GRID_SQUARE, w-1, h-1, NULL);
1059 char *board = snewn(g->num_faces, char);
1060 int i, s = g->tilesize;
1061 struct pearl_loopgen_bias_ctx biasctx;
1062
1063 memset(lines, 0, w*h);
1064
1065 /*
1066 * Initialise the context for the bias function. Initially we fill
1067 * all the to-do lists, so that the first call will scan
1068 * everything; thereafter the lists stay empty so we make
1069 * incremental changes.
1070 */
1071 biasctx.g = g;
1072 biasctx.faces = snewn(g->num_faces, char);
1073 biasctx.faces_todo = tdq_new(g->num_faces);
1074 tdq_fill(biasctx.faces_todo);
1075 biasctx.score = 0;
1076 memset(biasctx.faces, FACE_GREY, g->num_faces);
1077 for (i = 0; i < 2; i++) {
1078 biasctx.boundaries[i].edges = snewn(g->num_edges, char);
1079 memset(biasctx.boundaries[i].edges, 0, g->num_edges);
1080 biasctx.boundaries[i].edges_todo = tdq_new(g->num_edges);
1081 tdq_fill(biasctx.boundaries[i].edges_todo);
1082 biasctx.boundaries[i].vertextypes = snewn(g->num_dots, char);
1083 memset(biasctx.boundaries[i].vertextypes, 0, g->num_dots);
1084 biasctx.boundaries[i].neighbour[0] = snewn(g->num_dots, int);
1085 biasctx.boundaries[i].neighbour[1] = snewn(g->num_dots, int);
1086 biasctx.boundaries[i].vertextypes_todo = tdq_new(g->num_dots);
1087 tdq_fill(biasctx.boundaries[i].vertextypes_todo);
1088 biasctx.boundaries[i].blackclues = snewn(g->num_dots, char);
1089 memset(biasctx.boundaries[i].blackclues, 0, g->num_dots);
1090 biasctx.boundaries[i].blackclues_todo = tdq_new(g->num_dots);
1091 tdq_fill(biasctx.boundaries[i].blackclues_todo);
1092 }
1093 biasctx.boundaries[0].colour = FACE_WHITE;
1094 biasctx.boundaries[1].colour = FACE_BLACK;
1095 generate_loop(g, board, rs, pearl_loopgen_bias, &biasctx);
1096 sfree(biasctx.faces);
1097 tdq_free(biasctx.faces_todo);
1098 for (i = 0; i < 2; i++) {
1099 sfree(biasctx.boundaries[i].edges);
1100 tdq_free(biasctx.boundaries[i].edges_todo);
1101 sfree(biasctx.boundaries[i].vertextypes);
1102 sfree(biasctx.boundaries[i].neighbour[0]);
1103 sfree(biasctx.boundaries[i].neighbour[1]);
1104 tdq_free(biasctx.boundaries[i].vertextypes_todo);
1105 sfree(biasctx.boundaries[i].blackclues);
1106 tdq_free(biasctx.boundaries[i].blackclues_todo);
1107 }
1108
1109 for (i = 0; i < g->num_edges; i++) {
1110 grid_edge *e = g->edges + i;
1111 enum face_colour c1 = FACE_COLOUR(e->face1);
1112 enum face_colour c2 = FACE_COLOUR(e->face2);
1113 assert(c1 != FACE_GREY);
1114 assert(c2 != FACE_GREY);
1115 if (c1 != c2) {
1116 /* This grid edge is on the loop: lay line along it */
1117 int x1 = e->dot1->x/s, y1 = e->dot1->y/s;
1118 int x2 = e->dot2->x/s, y2 = e->dot2->y/s;
1119
1120 /* (x1,y1) and (x2,y2) are now in our grid coords (0-w,0-h). */
1121 if (x1 == x2) {
1122 if (y1 > y2) SWAP(y1,y2);
1123
1124 assert(y1+1 == y2);
1125 lines[y1*w+x1] |= D;
1126 lines[y2*w+x1] |= U;
1127 } else if (y1 == y2) {
1128 if (x1 > x2) SWAP(x1,x2);
1129
1130 assert(x1+1 == x2);
1131 lines[y1*w+x1] |= R;
1132 lines[y1*w+x2] |= L;
1133 } else
1134 assert(!"grid with diagonal coords?!");
1135 }
1136 }
1137
1138 grid_free(g);
1139 sfree(board);
1140
1141#if defined LOOPGEN_DIAGNOSTICS && !defined GENERATION_DIAGNOSTICS
1142 printf("as returned:\n");
1143 for (y = 0; y < h; y++) {
1144 for (x = 0; x < w; x++) {
1145 int type = lines[y*w+x];
1146 char s[5], *p = s;
1147 if (type & L) *p++ = 'L';
1148 if (type & R) *p++ = 'R';
1149 if (type & U) *p++ = 'U';
1150 if (type & D) *p++ = 'D';
1151 *p = '\0';
1152 printf("%3s", s);
1153 }
1154 printf("\n");
1155 }
1156 printf("\n");
1157#endif
1158}
1159
1160static int new_clues(const game_params *params, random_state *rs,
1161 char *clues, char *grid)
1162{
1163 int w = params->w, h = params->h, diff = params->difficulty;
1164 int ngen = 0, x, y, d, ret, i;
1165
1166
1167 /*
1168 * Difficulty exception: 5x5 Tricky is not generable (the
1169 * generator will spin forever trying) and so we fudge it to Easy.
1170 */
1171 if (w == 5 && h == 5 && diff > DIFF_EASY)
1172 diff = DIFF_EASY;
1173
1174 while (1) {
1175 ngen++;
1176 pearl_loopgen(w, h, grid, rs);
1177
1178#ifdef GENERATION_DIAGNOSTICS
1179 printf("grid array:\n");
1180 for (y = 0; y < h; y++) {
1181 for (x = 0; x < w; x++) {
1182 int type = grid[y*w+x];
1183 char s[5], *p = s;
1184 if (type & L) *p++ = 'L';
1185 if (type & R) *p++ = 'R';
1186 if (type & U) *p++ = 'U';
1187 if (type & D) *p++ = 'D';
1188 *p = '\0';
1189 printf("%2s ", s);
1190 }
1191 printf("\n");
1192 }
1193 printf("\n");
1194#endif
1195
1196 /*
1197 * Set up the maximal clue array.
1198 */
1199 for (y = 0; y < h; y++)
1200 for (x = 0; x < w; x++) {
1201 int type = grid[y*w+x];
1202
1203 clues[y*w+x] = NOCLUE;
1204
1205 if ((bLR|bUD) & (1 << type)) {
1206 /*
1207 * This is a straight; see if it's a viable
1208 * candidate for a straight clue. It qualifies if
1209 * at least one of the squares it connects to is a
1210 * corner.
1211 */
1212 for (d = 1; d <= 8; d += d) if (type & d) {
1213 int xx = x + DX(d), yy = y + DY(d);
1214 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1215 if ((bLU|bLD|bRU|bRD) & (1 << grid[yy*w+xx]))
1216 break;
1217 }
1218 if (d <= 8) /* we found one */
1219 clues[y*w+x] = STRAIGHT;
1220 } else if ((bLU|bLD|bRU|bRD) & (1 << type)) {
1221 /*
1222 * This is a corner; see if it's a viable candidate
1223 * for a corner clue. It qualifies if all the
1224 * squares it connects to are straights.
1225 */
1226 for (d = 1; d <= 8; d += d) if (type & d) {
1227 int xx = x + DX(d), yy = y + DY(d);
1228 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1229 if (!((bLR|bUD) & (1 << grid[yy*w+xx])))
1230 break;
1231 }
1232 if (d > 8) /* we didn't find a counterexample */
1233 clues[y*w+x] = CORNER;
1234 }
1235 }
1236
1237#ifdef GENERATION_DIAGNOSTICS
1238 printf("clue array:\n");
1239 for (y = 0; y < h; y++) {
1240 for (x = 0; x < w; x++) {
1241 printf("%c", " *O"[(unsigned char)clues[y*w+x]]);
1242 }
1243 printf("\n");
1244 }
1245 printf("\n");
1246#endif
1247
1248 if (!params->nosolve) {
1249 int *cluespace, *straights, *corners;
1250 int nstraights, ncorners, nstraightpos, ncornerpos;
1251
1252 /*
1253 * See if we can solve the puzzle just like this.
1254 */
1255 ret = pearl_solve(w, h, clues, grid, diff, FALSE);
1256 assert(ret > 0); /* shouldn't be inconsistent! */
1257 if (ret != 1)
1258 continue; /* go round and try again */
1259
1260 /*
1261 * Check this puzzle isn't too easy.
1262 */
1263 if (diff > DIFF_EASY) {
1264 ret = pearl_solve(w, h, clues, grid, diff-1, FALSE);
1265 assert(ret > 0);
1266 if (ret == 1)
1267 continue; /* too easy: try again */
1268 }
1269
1270 /*
1271 * Now shuffle the grid points and gradually remove the
1272 * clues to find a minimal set which still leaves the
1273 * puzzle soluble.
1274 *
1275 * We preferentially attempt to remove whichever type of
1276 * clue is currently most numerous, to combat a general
1277 * tendency of plain random generation to bias in favour
1278 * of many white clues and few black.
1279 *
1280 * 'nstraights' and 'ncorners' count the number of clues
1281 * of each type currently remaining in the grid;
1282 * 'nstraightpos' and 'ncornerpos' count the clues of each
1283 * type we have left to try to remove. (Clues which we
1284 * have tried and failed to remove are counted by the
1285 * former but not the latter.)
1286 */
1287 cluespace = snewn(w*h, int);
1288 straights = cluespace;
1289 nstraightpos = 0;
1290 for (i = 0; i < w*h; i++)
1291 if (clues[i] == STRAIGHT)
1292 straights[nstraightpos++] = i;
1293 corners = straights + nstraightpos;
1294 ncornerpos = 0;
1295 for (i = 0; i < w*h; i++)
1296 if (clues[i] == STRAIGHT)
1297 corners[ncornerpos++] = i;
1298 nstraights = nstraightpos;
1299 ncorners = ncornerpos;
1300
1301 shuffle(straights, nstraightpos, sizeof(*straights), rs);
1302 shuffle(corners, ncornerpos, sizeof(*corners), rs);
1303 while (nstraightpos > 0 || ncornerpos > 0) {
1304 int cluepos;
1305 int clue;
1306
1307 /*
1308 * Decide which clue to try to remove next. If both
1309 * types are available, we choose whichever kind is
1310 * currently overrepresented; otherwise we take
1311 * whatever we can get.
1312 */
1313 if (nstraightpos > 0 && ncornerpos > 0) {
1314 if (nstraights >= ncorners)
1315 cluepos = straights[--nstraightpos];
1316 else
1317 cluepos = straights[--ncornerpos];
1318 } else {
1319 if (nstraightpos > 0)
1320 cluepos = straights[--nstraightpos];
1321 else
1322 cluepos = straights[--ncornerpos];
1323 }
1324
1325 y = cluepos / w;
1326 x = cluepos % w;
1327
1328 clue = clues[y*w+x];
1329 clues[y*w+x] = 0; /* try removing this clue */
1330
1331 ret = pearl_solve(w, h, clues, grid, diff, FALSE);
1332 assert(ret > 0);
1333 if (ret != 1)
1334 clues[y*w+x] = clue; /* oops, put it back again */
1335 }
1336 sfree(cluespace);
1337 }
1338
1339#ifdef FINISHED_PUZZLE
1340 printf("clue array:\n");
1341 for (y = 0; y < h; y++) {
1342 for (x = 0; x < w; x++) {
1343 printf("%c", " *O"[(unsigned char)clues[y*w+x]]);
1344 }
1345 printf("\n");
1346 }
1347 printf("\n");
1348#endif
1349
1350 break; /* got it */
1351 }
1352
1353 debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h));
1354
1355 return ngen;
1356}
1357
1358static char *new_game_desc(const game_params *params, random_state *rs,
1359 char **aux, int interactive)
1360{
1361 char *grid, *clues;
1362 char *desc;
1363 int w = params->w, h = params->h, i, j;
1364
1365 grid = snewn(w*h, char);
1366 clues = snewn(w*h, char);
1367
1368 new_clues(params, rs, clues, grid);
1369
1370 desc = snewn(w * h + 1, char);
1371 for (i = j = 0; i < w*h; i++) {
1372 if (clues[i] == NOCLUE && j > 0 &&
1373 desc[j-1] >= 'a' && desc[j-1] < 'z')
1374 desc[j-1]++;
1375 else if (clues[i] == NOCLUE)
1376 desc[j++] = 'a';
1377 else if (clues[i] == CORNER)
1378 desc[j++] = 'B';
1379 else if (clues[i] == STRAIGHT)
1380 desc[j++] = 'W';
1381 }
1382 desc[j] = '\0';
1383
1384 *aux = snewn(w*h+1, char);
1385 for (i = 0; i < w*h; i++)
1386 (*aux)[i] = (grid[i] < 10) ? (grid[i] + '0') : (grid[i] + 'A' - 10);
1387 (*aux)[w*h] = '\0';
1388
1389 sfree(grid);
1390 sfree(clues);
1391
1392 return desc;
1393}
1394
1395static char *validate_desc(const game_params *params, const char *desc)
1396{
1397 int i, sizesofar;
1398 const int totalsize = params->w * params->h;
1399
1400 sizesofar = 0;
1401 for (i = 0; desc[i]; i++) {
1402 if (desc[i] >= 'a' && desc[i] <= 'z')
1403 sizesofar += desc[i] - 'a' + 1;
1404 else if (desc[i] == 'B' || desc[i] == 'W')
1405 sizesofar++;
1406 else
1407 return "unrecognised character in string";
1408 }
1409
1410 if (sizesofar > totalsize)
1411 return "string too long";
1412 else if (sizesofar < totalsize)
1413 return "string too short";
1414
1415 return NULL;
1416}
1417
1418static game_state *new_game(midend *me, const game_params *params,
1419 const char *desc)
1420{
1421 game_state *state = snew(game_state);
1422 int i, j, sz = params->w*params->h;
1423
1424 state->completed = state->used_solve = FALSE;
1425 state->shared = snew(struct shared_state);
1426
1427 state->shared->w = params->w;
1428 state->shared->h = params->h;
1429 state->shared->sz = sz;
1430 state->shared->refcnt = 1;
1431 state->shared->clues = snewn(sz, char);
1432 for (i = j = 0; desc[i]; i++) {
1433 assert(j < sz);
1434 if (desc[i] >= 'a' && desc[i] <= 'z') {
1435 int n = desc[i] - 'a' + 1;
1436 assert(j + n <= sz);
1437 while (n-- > 0)
1438 state->shared->clues[j++] = NOCLUE;
1439 } else if (desc[i] == 'B') {
1440 state->shared->clues[j++] = CORNER;
1441 } else if (desc[i] == 'W') {
1442 state->shared->clues[j++] = STRAIGHT;
1443 }
1444 }
1445
1446 state->lines = snewn(sz, char);
1447 state->errors = snewn(sz, char);
1448 state->marks = snewn(sz, char);
1449 for (i = 0; i < sz; i++)
1450 state->lines[i] = state->errors[i] = state->marks[i] = BLANK;
1451
1452 return state;
1453}
1454
1455static game_state *dup_game(const game_state *state)
1456{
1457 game_state *ret = snew(game_state);
1458 int sz = state->shared->sz, i;
1459
1460 ret->shared = state->shared;
1461 ret->completed = state->completed;
1462 ret->used_solve = state->used_solve;
1463 ++ret->shared->refcnt;
1464
1465 ret->lines = snewn(sz, char);
1466 ret->errors = snewn(sz, char);
1467 ret->marks = snewn(sz, char);
1468 for (i = 0; i < sz; i++) {
1469 ret->lines[i] = state->lines[i];
1470 ret->errors[i] = state->errors[i];
1471 ret->marks[i] = state->marks[i];
1472 }
1473
1474 return ret;
1475}
1476
1477static void free_game(game_state *state)
1478{
1479 assert(state);
1480 if (--state->shared->refcnt == 0) {
1481 sfree(state->shared->clues);
1482 sfree(state->shared);
1483 }
1484 sfree(state->lines);
1485 sfree(state->errors);
1486 sfree(state->marks);
1487 sfree(state);
1488}
1489
1490static char nbits[16] = { 0, 1, 1, 2,
1491 1, 2, 2, 3,
1492 1, 2, 2, 3,
1493 2, 3, 3, 4 };
1494#define NBITS(l) ( ((l) < 0 || (l) > 15) ? 4 : nbits[l] )
1495
1496#define ERROR_CLUE 16
1497
1498static void dsf_update_completion(game_state *state, int ax, int ay, char dir,
1499 int *dsf)
1500{
1501 int w = state->shared->w /*, h = state->shared->h */;
1502 int ac = ay*w+ax, bx, by, bc;
1503
1504 if (!(state->lines[ac] & dir)) return; /* no link */
1505 bx = ax + DX(dir); by = ay + DY(dir);
1506
1507 assert(INGRID(state, bx, by)); /* should not have a link off grid */
1508
1509 bc = by*w+bx;
1510 assert(state->lines[bc] & F(dir)); /* should have reciprocal link */
1511 if (!(state->lines[bc] & F(dir))) return;
1512
1513 dsf_merge(dsf, ac, bc);
1514}
1515
1516static void check_completion(game_state *state, int mark)
1517{
1518 int w = state->shared->w, h = state->shared->h, x, y, i, d;
1519 int had_error = FALSE;
1520 int *dsf, *component_state;
1521 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
1522 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
1523
1524 if (mark) {
1525 for (i = 0; i < w*h; i++) {
1526 state->errors[i] = 0;
1527 }
1528 }
1529
1530#define ERROR(x,y,e) do { had_error = TRUE; if (mark) state->errors[(y)*w+(x)] |= (e); } while(0)
1531
1532 /*
1533 * Analyse the solution into loops, paths and stranger things.
1534 * Basic strategy here is all the same as in Loopy - see the big
1535 * comment in loopy.c's check_completion() - and for exactly the
1536 * same reasons, since Loopy and Pearl have basically the same
1537 * form of expected solution.
1538 */
1539 dsf = snew_dsf(w*h);
1540
1541 /* Build the dsf. */
1542 for (x = 0; x < w; x++) {
1543 for (y = 0; y < h; y++) {
1544 dsf_update_completion(state, x, y, R, dsf);
1545 dsf_update_completion(state, x, y, D, dsf);
1546 }
1547 }
1548
1549 /* Initialise a state variable for each connected component. */
1550 component_state = snewn(w*h, int);
1551 for (i = 0; i < w*h; i++) {
1552 if (dsf_canonify(dsf, i) == i)
1553 component_state[i] = COMP_LOOP;
1554 else
1555 component_state[i] = COMP_NONE;
1556 }
1557
1558 /*
1559 * Classify components, and mark errors where a square has more
1560 * than two line segments.
1561 */
1562 for (x = 0; x < w; x++) {
1563 for (y = 0; y < h; y++) {
1564 int type = state->lines[y*w+x];
1565 int degree = NBITS(type);
1566 int comp = dsf_canonify(dsf, y*w+x);
1567 if (degree > 2) {
1568 ERROR(x,y,type);
1569 component_state[comp] = COMP_SILLY;
1570 } else if (degree == 0) {
1571 component_state[comp] = COMP_EMPTY;
1572 } else if (degree == 1) {
1573 if (component_state[comp] != COMP_SILLY)
1574 component_state[comp] = COMP_PATH;
1575 }
1576 }
1577 }
1578
1579 /* Count the components, and find the largest sensible one. */
1580 nsilly = nloop = npath = 0;
1581 total_pathsize = 0;
1582 largest_comp = largest_size = -1;
1583 for (i = 0; i < w*h; i++) {
1584 if (component_state[i] == COMP_SILLY) {
1585 nsilly++;
1586 } else if (component_state[i] == COMP_PATH) {
1587 total_pathsize += dsf_size(dsf, i);
1588 npath = 1;
1589 } else if (component_state[i] == COMP_LOOP) {
1590 int this_size;
1591
1592 nloop++;
1593
1594 if ((this_size = dsf_size(dsf, i)) > largest_size) {
1595 largest_comp = i;
1596 largest_size = this_size;
1597 }
1598 }
1599 }
1600 if (largest_size < total_pathsize) {
1601 largest_comp = -1; /* means the paths */
1602 largest_size = total_pathsize;
1603 }
1604
1605 if (nloop > 0 && nloop + npath > 1) {
1606 /*
1607 * If there are at least two sensible components including at
1608 * least one loop, highlight every sensible component that is
1609 * not the largest one.
1610 */
1611 for (i = 0; i < w*h; i++) {
1612 int comp = dsf_canonify(dsf, i);
1613 if ((component_state[comp] == COMP_PATH &&
1614 -1 != largest_comp) ||
1615 (component_state[comp] == COMP_LOOP &&
1616 comp != largest_comp))
1617 ERROR(i%w, i/w, state->lines[i]);
1618 }
1619 }
1620
1621 /* Now we've finished with the dsf and component states. The only
1622 * thing we'll need to remember later on is whether all edges were
1623 * part of a single loop, for which our counter variables
1624 * nsilly,nloop,npath are enough. */
1625 sfree(component_state);
1626 sfree(dsf);
1627
1628 /*
1629 * Check that no clues are contradicted. This code is similar to
1630 * the code that sets up the maximal clue array for any given
1631 * loop.
1632 */
1633 for (x = 0; x < w; x++) {
1634 for (y = 0; y < h; y++) {
1635 int type = state->lines[y*w+x];
1636 if (state->shared->clues[y*w+x] == CORNER) {
1637 /* Supposed to be a corner: will find a contradiction if
1638 * it actually contains a straight line, or if it touches any
1639 * corners. */
1640 if ((bLR|bUD) & (1 << type)) {
1641 ERROR(x,y,ERROR_CLUE); /* actually straight */
1642 }
1643 for (d = 1; d <= 8; d += d) if (type & d) {
1644 int xx = x + DX(d), yy = y + DY(d);
1645 if (!INGRID(state, xx, yy)) {
1646 ERROR(x,y,d); /* leads off grid */
1647 } else {
1648 if ((bLU|bLD|bRU|bRD) & (1 << state->lines[yy*w+xx])) {
1649 ERROR(x,y,ERROR_CLUE); /* touches corner */
1650 }
1651 }
1652 }
1653 } else if (state->shared->clues[y*w+x] == STRAIGHT) {
1654 /* Supposed to be straight: will find a contradiction if
1655 * it actually contains a corner, or if it only touches
1656 * straight lines. */
1657 if ((bLU|bLD|bRU|bRD) & (1 << type)) {
1658 ERROR(x,y,ERROR_CLUE); /* actually a corner */
1659 }
1660 i = 0;
1661 for (d = 1; d <= 8; d += d) if (type & d) {
1662 int xx = x + DX(d), yy = y + DY(d);
1663 if (!INGRID(state, xx, yy)) {
1664 ERROR(x,y,d); /* leads off grid */
1665 } else {
1666 if ((bLR|bUD) & (1 << state->lines[yy*w+xx]))
1667 i++; /* a straight */
1668 }
1669 }
1670 if (i >= 2 && NBITS(type) >= 2) {
1671 ERROR(x,y,ERROR_CLUE); /* everything touched is straight */
1672 }
1673 }
1674 }
1675 }
1676
1677 if (nloop == 1 && nsilly == 0 && npath == 0) {
1678 /*
1679 * If there's exactly one loop (so that the puzzle is at least
1680 * potentially complete), we need to ensure it hasn't left any
1681 * clue out completely.
1682 */
1683 for (x = 0; x < w; x++) {
1684 for (y = 0; y < h; y++) {
1685 if (state->lines[y*w+x] == BLANK) {
1686 if (state->shared->clues[y*w+x] != NOCLUE) {
1687 /* the loop doesn't include this clue square! */
1688 ERROR(x, y, ERROR_CLUE);
1689 }
1690 }
1691 }
1692 }
1693
1694 /*
1695 * But if not, then we're done!
1696 */
1697 if (!had_error)
1698 state->completed = TRUE;
1699 }
1700}
1701
1702/* completion check:
1703 *
1704 * - no clues must be contradicted (highlight clue itself in error if so)
1705 * - if there is a closed loop it must include every line segment laid
1706 * - if there's a smaller closed loop then highlight whole loop as error
1707 * - no square must have more than 2 lines radiating from centre point
1708 * (highlight all lines in that square as error if so)
1709 */
1710
1711static char *solve_for_diff(game_state *state, char *old_lines, char *new_lines)
1712{
1713 int w = state->shared->w, h = state->shared->h, i;
1714 char *move = snewn(w*h*40, char), *p = move;
1715
1716 *p++ = 'S';
1717 for (i = 0; i < w*h; i++) {
1718 if (old_lines[i] != new_lines[i]) {
1719 p += sprintf(p, ";R%d,%d,%d", new_lines[i], i%w, i/w);
1720 }
1721 }
1722 *p++ = '\0';
1723 move = sresize(move, p - move, char);
1724
1725 return move;
1726}
1727
1728static char *solve_game(const game_state *state, const game_state *currstate,
1729 const char *aux, char **error)
1730{
1731 game_state *solved = dup_game(state);
1732 int i, ret, sz = state->shared->sz;
1733 char *move;
1734
1735 if (aux) {
1736 for (i = 0; i < sz; i++) {
1737 if (aux[i] >= '0' && aux[i] <= '9')
1738 solved->lines[i] = aux[i] - '0';
1739 else if (aux[i] >= 'A' && aux[i] <= 'F')
1740 solved->lines[i] = aux[i] - 'A' + 10;
1741 else {
1742 *error = "invalid char in aux";
1743 move = NULL;
1744 goto done;
1745 }
1746 }
1747 ret = 1;
1748 } else {
1749 /* Try to solve with present (half-solved) state first: if there's no
1750 * solution from there go back to original state. */
1751 ret = pearl_solve(currstate->shared->w, currstate->shared->h,
1752 currstate->shared->clues, solved->lines,
1753 DIFFCOUNT, FALSE);
1754 if (ret < 1)
1755 ret = pearl_solve(state->shared->w, state->shared->h,
1756 state->shared->clues, solved->lines,
1757 DIFFCOUNT, FALSE);
1758
1759 }
1760
1761 if (ret < 1) {
1762 *error = "Unable to find solution";
1763 move = NULL;
1764 } else {
1765 move = solve_for_diff(solved, currstate->lines, solved->lines);
1766 }
1767
1768done:
1769 free_game(solved);
1770 return move;
1771}
1772
1773static int game_can_format_as_text_now(const game_params *params)
1774{
1775 return TRUE;
1776}
1777
1778static char *game_text_format(const game_state *state)
1779{
1780 int w = state->shared->w, h = state->shared->h, cw = 4, ch = 2;
1781 int gw = cw*(w-1) + 2, gh = ch*(h-1) + 1, len = gw * gh, r, c, j;
1782 char *board = snewn(len + 1, char);
1783
1784 assert(board);
1785 memset(board, ' ', len);
1786
1787 for (r = 0; r < h; ++r) {
1788 for (c = 0; c < w; ++c) {
1789 int i = r*w + c, cell = r*ch*gw + c*cw;
1790 board[cell] = "+BW"[(unsigned char)state->shared->clues[i]];
1791 if (c < w - 1 && (state->lines[i] & R || state->lines[i+1] & L))
1792 memset(board + cell + 1, '-', cw - 1);
1793 if (r < h - 1 && (state->lines[i] & D || state->lines[i+w] & U))
1794 for (j = 1; j < ch; ++j) board[cell + j*gw] = '|';
1795 if (c < w - 1 && (state->marks[i] & R || state->marks[i+1] & L))
1796 board[cell + cw/2] = 'x';
1797 if (r < h - 1 && (state->marks[i] & D || state->marks[i+w] & U))
1798 board[cell + (ch/2 * gw)] = 'x';
1799 }
1800
1801 for (j = 0; j < (r == h - 1 ? 1 : ch); ++j)
1802 board[r*ch*gw + (gw - 1) + j*gw] = '\n';
1803 }
1804
1805 board[len] = '\0';
1806 return board;
1807}
1808
1809struct game_ui {
1810 int *dragcoords; /* list of (y*w+x) coords in drag so far */
1811 int ndragcoords; /* number of entries in dragcoords.
1812 * 0 = click but no drag yet. -1 = no drag at all */
1813 int clickx, clicky; /* pixel position of initial click */
1814
1815 int curx, cury; /* grid position of keyboard cursor */
1816 int cursor_active; /* TRUE iff cursor is shown */
1817};
1818
1819static game_ui *new_ui(const game_state *state)
1820{
1821 game_ui *ui = snew(game_ui);
1822 int sz = state->shared->sz;
1823
1824 ui->ndragcoords = -1;
1825 ui->dragcoords = snewn(sz, int);
1826 ui->cursor_active = FALSE;
1827 ui->curx = ui->cury = 0;
1828
1829 return ui;
1830}
1831
1832static void free_ui(game_ui *ui)
1833{
1834 sfree(ui->dragcoords);
1835 sfree(ui);
1836}
1837
1838static char *encode_ui(const game_ui *ui)
1839{
1840 return NULL;
1841}
1842
1843static void decode_ui(game_ui *ui, const char *encoding)
1844{
1845}
1846
1847static void game_changed_state(game_ui *ui, const game_state *oldstate,
1848 const game_state *newstate)
1849{
1850}
1851
1852#define PREFERRED_TILE_SIZE 31
1853#define HALFSZ (ds->halfsz)
1854#define TILE_SIZE (ds->halfsz*2 + 1)
1855
1856#define BORDER ((get_gui_style() == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2))
1857
1858#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
1859
1860#define COORD(x) ( (x) * TILE_SIZE + BORDER )
1861#define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 )
1862#define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) )
1863
1864#define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */
1865#define DS_DSHIFT 8 /* R/U/L/D shift, for drag-in-progress flags */
1866#define DS_MSHIFT 12 /* shift for no-line mark */
1867
1868#define DS_ERROR_CLUE (1 << 20)
1869#define DS_FLASH (1 << 21)
1870#define DS_CURSOR (1 << 22)
1871
1872enum { GUI_MASYU, GUI_LOOPY };
1873
1874static int get_gui_style(void)
1875{
1876 static int gui_style = -1;
1877
1878 if (gui_style == -1) {
1879 char *env = getenv("PEARL_GUI_LOOPY");
1880 if (env && (env[0] == 'y' || env[0] == 'Y'))
1881 gui_style = GUI_LOOPY;
1882 else
1883 gui_style = GUI_MASYU;
1884 }
1885 return gui_style;
1886}
1887
1888struct game_drawstate {
1889 int halfsz;
1890 int started;
1891
1892 int w, h, sz;
1893 unsigned int *lflags; /* size w*h */
1894
1895 char *draglines; /* size w*h; lines flipped by current drag */
1896};
1897
1898static void update_ui_drag(const game_state *state, game_ui *ui,
1899 int gx, int gy)
1900{
1901 int /* sz = state->shared->sz, */ w = state->shared->w;
1902 int i, ox, oy, pos;
1903 int lastpos;
1904
1905 if (!INGRID(state, gx, gy))
1906 return; /* square is outside grid */
1907
1908 if (ui->ndragcoords < 0)
1909 return; /* drag not in progress anyway */
1910
1911 pos = gy * w + gx;
1912
1913 lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0];
1914 if (pos == lastpos)
1915 return; /* same square as last visited one */
1916
1917 /* Drag confirmed, if it wasn't already. */
1918 if (ui->ndragcoords == 0)
1919 ui->ndragcoords = 1;
1920
1921 /*
1922 * Dragging the mouse into a square that's already been visited by
1923 * the drag path so far has the effect of truncating the path back
1924 * to that square, so a player can back out part of an uncommitted
1925 * drag without having to let go of the mouse.
1926 */
1927 for (i = 0; i < ui->ndragcoords; i++)
1928 if (pos == ui->dragcoords[i]) {
1929 ui->ndragcoords = i+1;
1930 return;
1931 }
1932
1933 /*
1934 * Otherwise, dragging the mouse into a square that's a rook-move
1935 * away from the last one on the path extends the path.
1936 */
1937 oy = ui->dragcoords[ui->ndragcoords-1] / w;
1938 ox = ui->dragcoords[ui->ndragcoords-1] % w;
1939 if (ox == gx || oy == gy) {
1940 int dx = (gx < ox ? -1 : gx > ox ? +1 : 0);
1941 int dy = (gy < oy ? -1 : gy > oy ? +1 : 0);
1942 int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L);
1943 while (ox != gx || oy != gy) {
1944 /*
1945 * If the drag attempts to cross a 'no line here' mark,
1946 * stop there. We physically don't allow the user to drag
1947 * over those marks.
1948 */
1949 if (state->marks[oy*w+ox] & dir)
1950 break;
1951 ox += dx;
1952 oy += dy;
1953 ui->dragcoords[ui->ndragcoords++] = oy * w + ox;
1954 }
1955 }
1956
1957 /*
1958 * Failing that, we do nothing at all: if the user has dragged
1959 * diagonally across the board, they'll just have to return the
1960 * mouse to the last known position and do whatever they meant to
1961 * do again, more slowly and clearly.
1962 */
1963}
1964
1965/*
1966 * Routine shared between interpret_move and game_redraw to work out
1967 * the intended effect of a drag path on the grid.
1968 *
1969 * Call it in a loop, like this:
1970 *
1971 * int clearing = TRUE;
1972 * for (i = 0; i < ui->ndragcoords - 1; i++) {
1973 * int sx, sy, dx, dy, dir, oldstate, newstate;
1974 * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
1975 * &dir, &oldstate, &newstate);
1976 *
1977 * [do whatever is needed to handle the fact that the drag
1978 * wants the edge from sx,sy to dx,dy (heading in direction
1979 * 'dir' at the sx,sy end) to be changed from state oldstate
1980 * to state newstate, each of which equals either 0 or dir]
1981 * }
1982 */
1983static void interpret_ui_drag(const game_state *state, const game_ui *ui,
1984 int *clearing, int i, int *sx, int *sy,
1985 int *dx, int *dy, int *dir,
1986 int *oldstate, int *newstate)
1987{
1988 int w = state->shared->w;
1989 int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1];
1990 *sy = sp/w;
1991 *sx = sp%w;
1992 *dy = dp/w;
1993 *dx = dp%w;
1994 *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L);
1995 *oldstate = state->lines[sp] & *dir;
1996 if (*oldstate) {
1997 /*
1998 * The edge we've dragged over was previously
1999 * present. Set it to absent, unless we've already
2000 * stopped doing that.
2001 */
2002 *newstate = *clearing ? 0 : *dir;
2003 } else {
2004 /*
2005 * The edge we've dragged over was previously
2006 * absent. Set it to present, and cancel the
2007 * 'clearing' flag so that all subsequent edges in
2008 * the drag are set rather than cleared.
2009 */
2010 *newstate = *dir;
2011 *clearing = FALSE;
2012 }
2013}
2014
2015static char *mark_in_direction(const game_state *state, int x, int y, int dir,
2016 int primary, char *buf)
2017{
2018 int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */;
2019 int x2 = x + DX(dir);
2020 int y2 = y + DY(dir);
2021 int dir2 = F(dir);
2022
2023 char ch = primary ? 'F' : 'M', *other;
2024
2025 if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return "";
2026
2027 /* disallow laying a mark over a line, or vice versa. */
2028 other = primary ? state->marks : state->lines;
2029 if (other[y*w+x] & dir || other[y2*w+x2] & dir2) return "";
2030
2031 sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2);
2032 return dupstr(buf);
2033}
2034
2035#define KEY_DIRECTION(btn) (\
2036 (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\
2037 (btn) == CURSOR_LEFT ? L : R)
2038
2039static char *interpret_move(const game_state *state, game_ui *ui,
2040 const game_drawstate *ds,
2041 int x, int y, int button)
2042{
2043 int w = state->shared->w, h = state->shared->h /*, sz = state->shared->sz */;
2044 int gx = FROMCOORD(x), gy = FROMCOORD(y), i;
2045 int release = FALSE;
2046 char tmpbuf[80];
2047
2048 int shift = button & MOD_SHFT, control = button & MOD_CTRL;
2049 button &= ~MOD_MASK;
2050
2051 if (IS_MOUSE_DOWN(button)) {
2052 ui->cursor_active = FALSE;
2053
2054 if (!INGRID(state, gx, gy)) {
2055 ui->ndragcoords = -1;
2056 return NULL;
2057 }
2058
2059 ui->clickx = x; ui->clicky = y;
2060 ui->dragcoords[0] = gy * w + gx;
2061 ui->ndragcoords = 0; /* will be 1 once drag is confirmed */
2062
2063 return "";
2064 }
2065
2066 if (button == LEFT_DRAG && ui->ndragcoords >= 0) {
2067 update_ui_drag(state, ui, gx, gy);
2068 return "";
2069 }
2070
2071 if (IS_MOUSE_RELEASE(button)) release = TRUE;
2072
2073 if (IS_CURSOR_MOVE(button)) {
2074 if (!ui->cursor_active) {
2075 ui->cursor_active = TRUE;
2076 } else if (control | shift) {
2077 char *move;
2078 if (ui->ndragcoords > 0) return NULL;
2079 ui->ndragcoords = -1;
2080 move = mark_in_direction(state, ui->curx, ui->cury,
2081 KEY_DIRECTION(button), control, tmpbuf);
2082 if (control && !shift && *move)
2083 move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE);
2084 return move;
2085 } else {
2086 move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE);
2087 if (ui->ndragcoords >= 0)
2088 update_ui_drag(state, ui, ui->curx, ui->cury);
2089 }
2090 return "";
2091 }
2092
2093 if (IS_CURSOR_SELECT(button)) {
2094 if (!ui->cursor_active) {
2095 ui->cursor_active = TRUE;
2096 return "";
2097 } else if (button == CURSOR_SELECT) {
2098 if (ui->ndragcoords == -1) {
2099 ui->ndragcoords = 0;
2100 ui->dragcoords[0] = ui->cury * w + ui->curx;
2101 ui->clickx = CENTERED_COORD(ui->curx);
2102 ui->clicky = CENTERED_COORD(ui->cury);
2103 return "";
2104 } else release = TRUE;
2105 } else if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) {
2106 ui->ndragcoords = -1;
2107 return "";
2108 }
2109 }
2110
2111 if (button == 27 || button == '\b') {
2112 ui->ndragcoords = -1;
2113 return "";
2114 }
2115
2116 if (release) {
2117 if (ui->ndragcoords > 0) {
2118 /* End of a drag: process the cached line data. */
2119 int buflen = 0, bufsize = 256, tmplen;
2120 char *buf = NULL;
2121 const char *sep = "";
2122 int clearing = TRUE;
2123
2124 for (i = 0; i < ui->ndragcoords - 1; i++) {
2125 int sx, sy, dx, dy, dir, oldstate, newstate;
2126 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2127 &dir, &oldstate, &newstate);
2128
2129 if (oldstate != newstate) {
2130 if (!buf) buf = snewn(bufsize, char);
2131 tmplen = sprintf(tmpbuf, "%sF%d,%d,%d;F%d,%d,%d", sep,
2132 dir, sx, sy, F(dir), dx, dy);
2133 if (buflen + tmplen >= bufsize) {
2134 bufsize = (buflen + tmplen) * 5 / 4 + 256;
2135 buf = sresize(buf, bufsize, char);
2136 }
2137 strcpy(buf + buflen, tmpbuf);
2138 buflen += tmplen;
2139 sep = ";";
2140 }
2141 }
2142
2143 ui->ndragcoords = -1;
2144
2145 return buf ? buf : "";
2146 } else if (ui->ndragcoords == 0) {
2147 /* Click (or tiny drag). Work out which edge we were
2148 * closest to. */
2149 int cx, cy;
2150
2151 ui->ndragcoords = -1;
2152
2153 /*
2154 * We process clicks based on the mouse-down location,
2155 * because that's more natural for a user to carefully
2156 * control than the mouse-up.
2157 */
2158 x = ui->clickx;
2159 y = ui->clicky;
2160
2161 gx = FROMCOORD(x);
2162 gy = FROMCOORD(y);
2163 cx = CENTERED_COORD(gx);
2164 cy = CENTERED_COORD(gy);
2165
2166 if (!INGRID(state, gx, gy)) return "";
2167
2168 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
2169 /* TODO closer to centre of grid: process as a cell click not an edge click. */
2170
2171 return "";
2172 } else {
2173 int direction;
2174 if (abs(x-cx) < abs(y-cy)) {
2175 /* Closest to top/bottom edge. */
2176 direction = (y < cy) ? U : D;
2177 } else {
2178 /* Closest to left/right edge. */
2179 direction = (x < cx) ? L : R;
2180 }
2181 return mark_in_direction(state, gx, gy, direction,
2182 (button == LEFT_RELEASE), tmpbuf);
2183 }
2184 }
2185 }
2186
2187 if (button == 'H' || button == 'h')
2188 return dupstr("H");
2189
2190 return NULL;
2191}
2192
2193static game_state *execute_move(const game_state *state, const char *move)
2194{
2195 int w = state->shared->w, h = state->shared->h;
2196 char c;
2197 int x, y, l, n;
2198 game_state *ret = dup_game(state);
2199
2200 debug(("move: %s\n", move));
2201
2202 while (*move) {
2203 c = *move;
2204 if (c == 'S') {
2205 ret->used_solve = TRUE;
2206 move++;
2207 } else if (c == 'L' || c == 'N' || c == 'R' || c == 'F' || c == 'M') {
2208 /* 'line' or 'noline' or 'replace' or 'flip' or 'mark' */
2209 move++;
2210 if (sscanf(move, "%d,%d,%d%n", &l, &x, &y, &n) != 3)
2211 goto badmove;
2212 if (!INGRID(state, x, y)) goto badmove;
2213 if (l < 0 || l > 15) goto badmove;
2214
2215 if (c == 'L')
2216 ret->lines[y*w + x] |= (char)l;
2217 else if (c == 'N')
2218 ret->lines[y*w + x] &= ~((char)l);
2219 else if (c == 'R') {
2220 ret->lines[y*w + x] = (char)l;
2221 ret->marks[y*w + x] &= ~((char)l); /* erase marks too */
2222 } else if (c == 'F')
2223 ret->lines[y*w + x] ^= (char)l;
2224 else if (c == 'M')
2225 ret->marks[y*w + x] ^= (char)l;
2226
2227 /*
2228 * If we ended up trying to lay a line _over_ a mark,
2229 * that's a failed move: interpret_move() should have
2230 * ensured we never received a move string like that in
2231 * the first place.
2232 */
2233 if ((ret->lines[y*w + x] & (char)l) &&
2234 (ret->marks[y*w + x] & (char)l))
2235 goto badmove;
2236
2237 move += n;
2238 } else if (strcmp(move, "H") == 0) {
2239 pearl_solve(ret->shared->w, ret->shared->h,
2240 ret->shared->clues, ret->lines, DIFFCOUNT, TRUE);
2241 for (n = 0; n < w*h; n++)
2242 ret->marks[n] &= ~ret->lines[n]; /* erase marks too */
2243 move++;
2244 } else {
2245 goto badmove;
2246 }
2247 if (*move == ';')
2248 move++;
2249 else if (*move)
2250 goto badmove;
2251 }
2252
2253 check_completion(ret, TRUE);
2254
2255 return ret;
2256
2257badmove:
2258 free_game(ret);
2259 return NULL;
2260}
2261
2262/* ----------------------------------------------------------------------
2263 * Drawing routines.
2264 */
2265
2266#define FLASH_TIME 0.5F
2267
2268static void game_compute_size(const game_params *params, int tilesize,
2269 int *x, int *y)
2270{
2271 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2272 struct { int halfsz; } ads, *ds = &ads;
2273 ads.halfsz = (tilesize-1)/2;
2274
2275 *x = (params->w) * TILE_SIZE + 2 * BORDER;
2276 *y = (params->h) * TILE_SIZE + 2 * BORDER;
2277}
2278
2279static void game_set_size(drawing *dr, game_drawstate *ds,
2280 const game_params *params, int tilesize)
2281{
2282 ds->halfsz = (tilesize-1)/2;
2283}
2284
2285static float *game_colours(frontend *fe, int *ncolours)
2286{
2287 float *ret = snewn(3 * NCOLOURS, float);
2288 int i;
2289
2290 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
2291
2292 for (i = 0; i < 3; i++) {
2293 ret[COL_BLACK * 3 + i] = 0.0F;
2294 ret[COL_WHITE * 3 + i] = 1.0F;
2295 ret[COL_GRID * 3 + i] = 0.4F;
2296 }
2297
2298 ret[COL_ERROR * 3 + 0] = 1.0F;
2299 ret[COL_ERROR * 3 + 1] = 0.0F;
2300 ret[COL_ERROR * 3 + 2] = 0.0F;
2301
2302 ret[COL_DRAGON * 3 + 0] = 0.0F;
2303 ret[COL_DRAGON * 3 + 1] = 0.0F;
2304 ret[COL_DRAGON * 3 + 2] = 1.0F;
2305
2306 ret[COL_DRAGOFF * 3 + 0] = 0.8F;
2307 ret[COL_DRAGOFF * 3 + 1] = 0.8F;
2308 ret[COL_DRAGOFF * 3 + 2] = 1.0F;
2309
2310 ret[COL_FLASH * 3 + 0] = 1.0F;
2311 ret[COL_FLASH * 3 + 1] = 1.0F;
2312 ret[COL_FLASH * 3 + 2] = 1.0F;
2313
2314 *ncolours = NCOLOURS;
2315
2316 return ret;
2317}
2318
2319static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2320{
2321 struct game_drawstate *ds = snew(struct game_drawstate);
2322 int i;
2323
2324 ds->halfsz = 0;
2325 ds->started = FALSE;
2326
2327 ds->w = state->shared->w;
2328 ds->h = state->shared->h;
2329 ds->sz = state->shared->sz;
2330 ds->lflags = snewn(ds->sz, unsigned int);
2331 for (i = 0; i < ds->sz; i++)
2332 ds->lflags[i] = 0;
2333
2334 ds->draglines = snewn(ds->sz, char);
2335
2336 return ds;
2337}
2338
2339static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2340{
2341 sfree(ds->draglines);
2342 sfree(ds->lflags);
2343 sfree(ds);
2344}
2345
2346static void draw_lines_specific(drawing *dr, game_drawstate *ds,
2347 int x, int y, unsigned int lflags,
2348 unsigned int shift, int c)
2349{
2350 int ox = COORD(x), oy = COORD(y);
2351 int t2 = HALFSZ, t16 = HALFSZ/4;
2352 int cx = ox + t2, cy = oy + t2;
2353 int d;
2354
2355 /* Draw each of the four directions, where laid (or error, or drag, etc.) */
2356 for (d = 1; d < 16; d *= 2) {
2357 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2358 int xnudge = abs(t16 * DX(C(d))), ynudge = abs(t16 * DY(C(d)));
2359
2360 if ((lflags >> shift) & d) {
2361 int lx = cx + ((xoff < 0) ? xoff : 0) - xnudge;
2362 int ly = cy + ((yoff < 0) ? yoff : 0) - ynudge;
2363
2364 if (c == COL_DRAGOFF && !(lflags & d))
2365 continue;
2366 if (c == COL_DRAGON && (lflags & d))
2367 continue;
2368
2369 draw_rect(dr, lx, ly,
2370 abs(xoff)+2*xnudge+1,
2371 abs(yoff)+2*ynudge+1, c);
2372 /* end cap */
2373 draw_rect(dr, cx - t16, cy - t16, 2*t16+1, 2*t16+1, c);
2374 }
2375 }
2376}
2377
2378static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
2379 int x, int y, unsigned int lflags, char clue)
2380{
2381 int ox = COORD(x), oy = COORD(y);
2382 int t2 = HALFSZ, t16 = HALFSZ/4;
2383 int cx = ox + t2, cy = oy + t2;
2384 int d;
2385
2386 assert(dr);
2387
2388 /* Clip to the grid square. */
2389 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2390
2391 /* Clear the square. */
2392 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE,
2393 (lflags & DS_CURSOR) ?
2394 COL_CURSOR_BACKGROUND : COL_BACKGROUND);
2395
2396
2397 if (get_gui_style() == GUI_LOOPY) {
2398 /* Draw small dot, underneath any lines. */
2399 draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID);
2400 } else {
2401 /* Draw outline of grid square */
2402 draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID);
2403 draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID);
2404 }
2405
2406 /* Draw grid: either thin gridlines, or no-line marks.
2407 * We draw these first because the thick laid lines should be on top. */
2408 for (d = 1; d < 16; d *= 2) {
2409 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2410
2411 if ((x == 0 && d == L) ||
2412 (y == 0 && d == U) ||
2413 (x == ds->w-1 && d == R) ||
2414 (y == ds->h-1 && d == D))
2415 continue; /* no gridlines out to the border. */
2416
2417 if ((lflags >> DS_MSHIFT) & d) {
2418 /* either a no-line mark ... */
2419 int mx = cx + xoff, my = cy + yoff, msz = t16;
2420
2421 draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK);
2422 draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK);
2423 } else {
2424 if (get_gui_style() == GUI_LOOPY) {
2425 /* draw grid lines connecting centre of cells */
2426 draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID);
2427 }
2428 }
2429 }
2430
2431 /* Draw each of the four directions, where laid (or error, or drag, etc.)
2432 * Order is important here, specifically for the eventual colours of the
2433 * exposed end caps. */
2434 draw_lines_specific(dr, ds, x, y, lflags, 0,
2435 (lflags & DS_FLASH ? COL_FLASH : COL_BLACK));
2436 draw_lines_specific(dr, ds, x, y, lflags, DS_ESHIFT, COL_ERROR);
2437 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGOFF);
2438 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGON);
2439
2440 /* Draw a clue, if present */
2441 if (clue != NOCLUE) {
2442 int c = (lflags & DS_FLASH) ? COL_FLASH :
2443 (clue == STRAIGHT) ? COL_WHITE : COL_BLACK;
2444
2445 if (lflags & DS_ERROR_CLUE) /* draw a bigger 'error' clue circle. */
2446 draw_circle(dr, cx, cy, TILE_SIZE*3/8, COL_ERROR, COL_ERROR);
2447
2448 draw_circle(dr, cx, cy, TILE_SIZE/4, c, COL_BLACK);
2449 }
2450
2451 unclip(dr);
2452 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2453}
2454
2455static void game_redraw(drawing *dr, game_drawstate *ds,
2456 const game_state *oldstate, const game_state *state,
2457 int dir, const game_ui *ui,
2458 float animtime, float flashtime)
2459{
2460 int w = state->shared->w, h = state->shared->h, sz = state->shared->sz;
2461 int x, y, force = 0, flashing = 0;
2462
2463 if (!ds->started) {
2464 /*
2465 * The initial contents of the window are not guaranteed and
2466 * can vary with front ends. To be on the safe side, all games
2467 * should start by drawing a big background-colour rectangle
2468 * covering the whole window.
2469 */
2470 draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER,
2471 COL_BACKGROUND);
2472
2473 if (get_gui_style() == GUI_MASYU) {
2474 /*
2475 * Smaller black rectangle which is the main grid.
2476 */
2477 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2478 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
2479 h*TILE_SIZE + 2*BORDER_WIDTH + 1,
2480 COL_GRID);
2481 }
2482
2483 draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER);
2484
2485 ds->started = TRUE;
2486 force = 1;
2487 }
2488
2489 if (flashtime > 0 &&
2490 (flashtime <= FLASH_TIME/3 ||
2491 flashtime >= FLASH_TIME*2/3))
2492 flashing = DS_FLASH;
2493
2494 memset(ds->draglines, 0, sz);
2495 if (ui->ndragcoords > 0) {
2496 int i, clearing = TRUE;
2497 for (i = 0; i < ui->ndragcoords - 1; i++) {
2498 int sx, sy, dx, dy, dir, oldstate, newstate;
2499 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2500 &dir, &oldstate, &newstate);
2501 ds->draglines[sy*w+sx] ^= (oldstate ^ newstate);
2502 ds->draglines[dy*w+dx] ^= (F(oldstate) ^ F(newstate));
2503 }
2504 }
2505
2506 for (x = 0; x < w; x++) {
2507 for (y = 0; y < h; y++) {
2508 unsigned int f = (unsigned int)state->lines[y*w+x];
2509 unsigned int eline = (unsigned int)(state->errors[y*w+x] & (R|U|L|D));
2510
2511 f |= eline << DS_ESHIFT;
2512 f |= ((unsigned int)ds->draglines[y*w+x]) << DS_DSHIFT;
2513 f |= ((unsigned int)state->marks[y*w+x]) << DS_MSHIFT;
2514
2515 if (state->errors[y*w+x] & ERROR_CLUE)
2516 f |= DS_ERROR_CLUE;
2517
2518 f |= flashing;
2519
2520 if (ui->cursor_active && x == ui->curx && y == ui->cury)
2521 f |= DS_CURSOR;
2522
2523 if (f != ds->lflags[y*w+x] || force) {
2524 ds->lflags[y*w+x] = f;
2525 draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]);
2526 }
2527 }
2528 }
2529}
2530
2531static float game_anim_length(const game_state *oldstate,
2532 const game_state *newstate, int dir, game_ui *ui)
2533{
2534 return 0.0F;
2535}
2536
2537static float game_flash_length(const game_state *oldstate,
2538 const game_state *newstate, int dir, game_ui *ui)
2539{
2540 if (!oldstate->completed && newstate->completed &&
2541 !oldstate->used_solve && !newstate->used_solve)
2542 return FLASH_TIME;
2543 else
2544 return 0.0F;
2545}
2546
2547static int game_status(const game_state *state)
2548{
2549 return state->completed ? +1 : 0;
2550}
2551
2552static int game_timing_state(const game_state *state, game_ui *ui)
2553{
2554 return TRUE;
2555}
2556
2557static void game_print_size(const game_params *params, float *x, float *y)
2558{
2559 int pw, ph;
2560
2561 /*
2562 * I'll use 6mm squares by default.
2563 */
2564 game_compute_size(params, 600, &pw, &ph);
2565 *x = pw / 100.0F;
2566 *y = ph / 100.0F;
2567}
2568
2569static void game_print(drawing *dr, const game_state *state, int tilesize)
2570{
2571 int w = state->shared->w, h = state->shared->h, x, y;
2572 int black = print_mono_colour(dr, 0);
2573 int white = print_mono_colour(dr, 1);
2574
2575 /* No GUI_LOOPY here: only use the familiar masyu style. */
2576
2577 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2578 game_drawstate *ds = game_new_drawstate(dr, state);
2579 game_set_size(dr, ds, NULL, tilesize);
2580
2581 /* Draw grid outlines (black). */
2582 for (x = 0; x <= w; x++)
2583 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
2584 for (y = 0; y <= h; y++)
2585 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
2586
2587 for (x = 0; x < w; x++) {
2588 for (y = 0; y < h; y++) {
2589 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ;
2590 int clue = state->shared->clues[y*w+x];
2591
2592 draw_lines_specific(dr, ds, x, y, state->lines[y*w+x], 0, black);
2593
2594 if (clue != NOCLUE) {
2595 int c = (clue == CORNER) ? black : white;
2596 draw_circle(dr, cx, cy, TILE_SIZE/4, c, black);
2597 }
2598 }
2599 }
2600
2601 game_free_drawstate(dr, ds);
2602}
2603
2604#ifdef COMBINED
2605#define thegame pearl
2606#endif
2607
2608const struct game thegame = {
2609 "Pearl", "games.pearl", "pearl",
2610 default_params,
2611 game_fetch_preset, NULL,
2612 decode_params,
2613 encode_params,
2614 free_params,
2615 dup_params,
2616 TRUE, game_configure, custom_params,
2617 validate_params,
2618 new_game_desc,
2619 validate_desc,
2620 new_game,
2621 dup_game,
2622 free_game,
2623 TRUE, solve_game,
2624 TRUE, game_can_format_as_text_now, game_text_format,
2625 new_ui,
2626 free_ui,
2627 encode_ui,
2628 decode_ui,
2629 game_changed_state,
2630 interpret_move,
2631 execute_move,
2632 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2633 game_colours,
2634 game_new_drawstate,
2635 game_free_drawstate,
2636 game_redraw,
2637 game_anim_length,
2638 game_flash_length,
2639 game_status,
2640 TRUE, FALSE, game_print_size, game_print,
2641 FALSE, /* wants_statusbar */
2642 FALSE, game_timing_state,
2643 0, /* flags */
2644};
2645
2646#ifdef STANDALONE_SOLVER
2647
2648#include <time.h>
2649#include <stdarg.h>
2650
2651const char *quis = NULL;
2652
2653static void usage(FILE *out) {
2654 fprintf(out, "usage: %s <params>\n", quis);
2655}
2656
2657static void pnum(int n, int ntot, const char *desc)
2658{
2659 printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc);
2660}
2661
2662static void start_soak(game_params *p, random_state *rs, int nsecs)
2663{
2664 time_t tt_start, tt_now, tt_last;
2665 int n = 0, nsolved = 0, nimpossible = 0, ret;
2666 char *grid, *clues;
2667
2668 tt_start = tt_last = time(NULL);
2669
2670 /* Currently this generates puzzles of any difficulty (trying to solve it
2671 * on the maximum difficulty level and not checking it's not too easy). */
2672 printf("Soak-testing a %dx%d grid (any difficulty)", p->w, p->h);
2673 if (nsecs > 0) printf(" for %d seconds", nsecs);
2674 printf(".\n");
2675
2676 p->nosolve = TRUE;
2677
2678 grid = snewn(p->w*p->h, char);
2679 clues = snewn(p->w*p->h, char);
2680
2681 while (1) {
2682 n += new_clues(p, rs, clues, grid); /* should be 1, with nosolve */
2683
2684 ret = pearl_solve(p->w, p->h, clues, grid, DIFF_TRICKY, FALSE);
2685 if (ret <= 0) nimpossible++;
2686 if (ret == 1) nsolved++;
2687
2688 tt_now = time(NULL);
2689 if (tt_now > tt_last) {
2690 tt_last = tt_now;
2691
2692 printf("%d total, %3.1f/s, ",
2693 n, (double)n / ((double)tt_now - tt_start));
2694 pnum(nsolved, n, "solved"); printf(", ");
2695 printf("%3.1f/s", (double)nsolved / ((double)tt_now - tt_start));
2696 if (nimpossible > 0)
2697 pnum(nimpossible, n, "impossible");
2698 printf("\n");
2699 }
2700 if (nsecs > 0 && (tt_now - tt_start) > nsecs) {
2701 printf("\n");
2702 break;
2703 }
2704 }
2705
2706 sfree(grid);
2707 sfree(clues);
2708}
2709
2710int main(int argc, const char *argv[])
2711{
2712 game_params *p = NULL;
2713 random_state *rs = NULL;
2714 time_t seed = time(NULL);
2715 char *id = NULL, *err;
2716
2717 setvbuf(stdout, NULL, _IONBF, 0);
2718
2719 quis = argv[0];
2720
2721 while (--argc > 0) {
2722 char *p = (char*)(*++argv);
2723 if (!strcmp(p, "-e") || !strcmp(p, "--seed")) {
2724 seed = atoi(*++argv);
2725 argc--;
2726 } else if (*p == '-') {
2727 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2728 usage(stderr);
2729 exit(1);
2730 } else {
2731 id = p;
2732 }
2733 }
2734
2735 rs = random_new((void*)&seed, sizeof(time_t));
2736 p = default_params();
2737
2738 if (id) {
2739 if (strchr(id, ':')) {
2740 fprintf(stderr, "soak takes params only.\n");
2741 goto done;
2742 }
2743
2744 decode_params(p, id);
2745 err = validate_params(p, 1);
2746 if (err) {
2747 fprintf(stderr, "%s: %s", argv[0], err);
2748 goto done;
2749 }
2750
2751 start_soak(p, rs, 0); /* run forever */
2752 } else {
2753 int i;
2754
2755 for (i = 5; i <= 12; i++) {
2756 p->w = p->h = i;
2757 start_soak(p, rs, 5);
2758 }
2759 }
2760
2761done:
2762 free_params(p);
2763 random_free(rs);
2764
2765 return 0;
2766}
2767
2768#endif
2769
2770/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/pearl.html b/apps/plugins/puzzles/src/pearl.html
new file mode 100644
index 0000000000..c366ed3a4c
--- /dev/null
+++ b/apps/plugins/puzzles/src/pearl.html
@@ -0,0 +1,65 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Pearl</title>
7<link rel="previous" href="range.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="undead.html">
12</head>
13<body>
14<p><a href="range.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="undead.html">Next</a></p>
15<h1><a name="C36"></a>Chapter 36: <a name="i0"></a>Pearl</h1>
16<p>
17You have a grid of squares. Your job is to draw lines between the centres of horizontally or vertically adjacent squares, so that the lines form a single closed loop. In the resulting grid, some of the squares that the loop passes through will contain corners, and some will be straight horizontal or vertical lines. (And some squares can be completely empty &#8211; the loop doesn't have to pass through every square.)
18</p>
19<p>
20Some of the squares contain black and white circles, which are clues that the loop must satisfy.
21</p>
22<p>
23A black circle in a square indicates that that square is a corner, but neither of the squares adjacent to it in the loop is also a corner.
24</p>
25<p>
26A white circle indicates that the square is a straight edge, but <em>at least one</em> of the squares adjacent to it in the loop is a corner.
27</p>
28<p>
29(In both cases, the clue only constrains the two squares adjacent <em>in the loop</em>, that is, the squares that the loop passes into after leaving the clue square. The squares that are only adjacent <em>in the grid</em> are not constrained.)
30</p>
31<p>
32Credit for this puzzle goes to <a name="i1"></a>Nikoli, who call it &#8216;Masyu&#8217;. <a href="#p0">[19]</a>
33</p>
34<p>
35Thanks to James Harvey for assistance with the implementation.
36</p>
37<p><a name="p0"></a>
38[19] <a href="http://www.nikoli.co.jp/en/puzzles/masyu.html"><code>http://www.nikoli.co.jp/en/puzzles/masyu.html</code></a> (beware of Flash)
39</p>
40<h2><a name="S36.1"></a>36.1 <a name="i2"></a>Pearl controls</h2>
41<p>
42Click with the left button on a grid edge to draw a segment of the loop through that edge, or to remove a segment once it is drawn.
43</p>
44<p>
45Drag with the left button through a series of squares to draw more than one segment of the loop in one go. Alternatively, drag over an existing part of the loop to undraw it, or to undraw part of it and then go in a different direction.
46</p>
47<p>
48Click with the right button on a grid edge to mark it with a cross, indicating that you are sure the loop does not go through that edge. (For instance, if you have decided which of the squares adjacent to a white clue has to be a corner, but don't yet know which way the corner turns, you might mark the one way it <em>can't</em> go with a cross.)
49</p>
50<p>
51Alternatively, use the cursor keys to move the cursor. Use the Enter key to begin and end keyboard &#8216;drag&#8217; operations. Use the Space, Escape or Backspace keys to cancel the drag. Or, hold Control while dragging with the cursor keys to toggle segments as you move between squares.
52</p>
53<p>
54Pressing Control-Shift-arrowkey or Shift-arrowkey simulates a left or right click, respectively, on the edge in the direction of the key.
55</p>
56<p>
57(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
58</p>
59<h2><a name="S36.2"></a>36.2 <a name="i3"></a>Pearl parameters</h2>
60<p>
61These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
62</p>
63
64<hr><address></address></body>
65</html>
diff --git a/apps/plugins/puzzles/src/pegs.R b/apps/plugins/puzzles/src/pegs.R
new file mode 100644
index 0000000000..1e79e99ca0
--- /dev/null
+++ b/apps/plugins/puzzles/src/pegs.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3PEGS_EXTRA = tree234
4
5pegs : [X] GTK COMMON pegs PEGS_EXTRA pegs-icon|no-icon
6
7pegs : [G] WINDOWS COMMON pegs PEGS_EXTRA pegs.res|noicon.res
8
9ALL += pegs[COMBINED] PEGS_EXTRA
10
11!begin am gtk
12GAMES += pegs
13!end
14
15!begin >list.c
16 A(pegs) \
17!end
18
19!begin >gamedesc.txt
20pegs:pegs.exe:Pegs:Peg solitaire puzzle:Jump pegs over each other to remove all but one.
21!end
diff --git a/apps/plugins/puzzles/src/pegs.c b/apps/plugins/puzzles/src/pegs.c
new file mode 100644
index 0000000000..8286851954
--- /dev/null
+++ b/apps/plugins/puzzles/src/pegs.c
@@ -0,0 +1,1340 @@
1/*
2 * pegs.c: the classic Peg Solitaire game.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13#include "tree234.h"
14
15#define GRID_HOLE 0
16#define GRID_PEG 1
17#define GRID_OBST 2
18
19#define GRID_CURSOR 10
20#define GRID_JUMPING 20
21
22enum {
23 COL_BACKGROUND,
24 COL_HIGHLIGHT,
25 COL_LOWLIGHT,
26 COL_PEG,
27 COL_CURSOR,
28 NCOLOURS
29};
30
31/*
32 * Grid shapes. I do some macro ickery here to ensure that my enum
33 * and the various forms of my name list always match up.
34 */
35#define TYPELIST(A) \
36 A(CROSS,Cross,cross) \
37 A(OCTAGON,Octagon,octagon) \
38 A(RANDOM,Random,random)
39#define ENUM(upper,title,lower) TYPE_ ## upper,
40#define TITLE(upper,title,lower) #title,
41#define LOWER(upper,title,lower) #lower,
42#define CONFIG(upper,title,lower) ":" #title
43
44enum { TYPELIST(ENUM) TYPECOUNT };
45static char const *const pegs_titletypes[] = { TYPELIST(TITLE) };
46static char const *const pegs_lowertypes[] = { TYPELIST(LOWER) };
47#define TYPECONFIG TYPELIST(CONFIG)
48
49#define FLASH_FRAME 0.13F
50
51struct game_params {
52 int w, h;
53 int type;
54};
55
56struct game_state {
57 int w, h;
58 int completed;
59 unsigned char *grid;
60};
61
62static game_params *default_params(void)
63{
64 game_params *ret = snew(game_params);
65
66 ret->w = ret->h = 7;
67 ret->type = TYPE_CROSS;
68
69 return ret;
70}
71
72static const struct game_params pegs_presets[] = {
73 {7, 7, TYPE_CROSS},
74 {7, 7, TYPE_OCTAGON},
75 {5, 5, TYPE_RANDOM},
76 {7, 7, TYPE_RANDOM},
77 {9, 9, TYPE_RANDOM},
78};
79
80static int game_fetch_preset(int i, char **name, game_params **params)
81{
82 game_params *ret;
83 char str[80];
84
85 if (i < 0 || i >= lenof(pegs_presets))
86 return FALSE;
87
88 ret = snew(game_params);
89 *ret = pegs_presets[i];
90
91 strcpy(str, pegs_titletypes[ret->type]);
92 if (ret->type == TYPE_RANDOM)
93 sprintf(str + strlen(str), " %dx%d", ret->w, ret->h);
94
95 *name = dupstr(str);
96 *params = ret;
97 return TRUE;
98}
99
100static void free_params(game_params *params)
101{
102 sfree(params);
103}
104
105static game_params *dup_params(const game_params *params)
106{
107 game_params *ret = snew(game_params);
108 *ret = *params; /* structure copy */
109 return ret;
110}
111
112static void decode_params(game_params *params, char const *string)
113{
114 char const *p = string;
115 int i;
116
117 params->w = atoi(p);
118 while (*p && isdigit((unsigned char)*p)) p++;
119 if (*p == 'x') {
120 p++;
121 params->h = atoi(p);
122 while (*p && isdigit((unsigned char)*p)) p++;
123 } else {
124 params->h = params->w;
125 }
126
127 for (i = 0; i < lenof(pegs_lowertypes); i++)
128 if (!strcmp(p, pegs_lowertypes[i]))
129 params->type = i;
130}
131
132static char *encode_params(const game_params *params, int full)
133{
134 char str[80];
135
136 sprintf(str, "%dx%d", params->w, params->h);
137 if (full) {
138 assert(params->type >= 0 && params->type < lenof(pegs_lowertypes));
139 strcat(str, pegs_lowertypes[params->type]);
140 }
141 return dupstr(str);
142}
143
144static config_item *game_configure(const game_params *params)
145{
146 config_item *ret = snewn(4, config_item);
147 char buf[80];
148
149 ret[0].name = "Width";
150 ret[0].type = C_STRING;
151 sprintf(buf, "%d", params->w);
152 ret[0].sval = dupstr(buf);
153 ret[0].ival = 0;
154
155 ret[1].name = "Height";
156 ret[1].type = C_STRING;
157 sprintf(buf, "%d", params->h);
158 ret[1].sval = dupstr(buf);
159 ret[1].ival = 0;
160
161 ret[2].name = "Board type";
162 ret[2].type = C_CHOICES;
163 ret[2].sval = TYPECONFIG;
164 ret[2].ival = params->type;
165
166 ret[3].name = NULL;
167 ret[3].type = C_END;
168 ret[3].sval = NULL;
169 ret[3].ival = 0;
170
171 return ret;
172}
173
174static game_params *custom_params(const config_item *cfg)
175{
176 game_params *ret = snew(game_params);
177
178 ret->w = atoi(cfg[0].sval);
179 ret->h = atoi(cfg[1].sval);
180 ret->type = cfg[2].ival;
181
182 return ret;
183}
184
185static char *validate_params(const game_params *params, int full)
186{
187 if (full && (params->w <= 3 || params->h <= 3))
188 return "Width and height must both be greater than three";
189
190 /*
191 * It might be possible to implement generalisations of Cross
192 * and Octagon, but only if I can find a proof that they're all
193 * soluble. For the moment, therefore, I'm going to disallow
194 * them at any size other than the standard one.
195 */
196 if (full && (params->type == TYPE_CROSS || params->type == TYPE_OCTAGON)) {
197 if (params->w != 7 || params->h != 7)
198 return "This board type is only supported at 7x7";
199 }
200 return NULL;
201}
202
203/* ----------------------------------------------------------------------
204 * Beginning of code to generate random Peg Solitaire boards.
205 *
206 * This procedure is done with no aesthetic judgment, no effort at
207 * symmetry, no difficulty grading and generally no finesse
208 * whatsoever. We simply begin with an empty board containing a
209 * single peg, and repeatedly make random reverse moves until it's
210 * plausibly full. This typically yields a scrappy haphazard mess
211 * with several holes, an uneven shape, and no redeeming features
212 * except guaranteed solubility.
213 *
214 * My only concessions to sophistication are (a) to repeat the
215 * generation process until I at least get a grid that touches
216 * every edge of the specified board size, and (b) to try when
217 * selecting moves to reuse existing space rather than expanding
218 * into new space (so that non-rectangular board shape becomes a
219 * factor during play).
220 */
221
222struct move {
223 /*
224 * x,y are the start point of the move during generation (hence
225 * its endpoint during normal play).
226 *
227 * dx,dy are the direction of the move during generation.
228 * Absolute value 1. Hence, for example, x=3,y=5,dx=1,dy=0
229 * means that the move during generation starts at (3,5) and
230 * ends at (5,5), and vice versa during normal play.
231 */
232 int x, y, dx, dy;
233 /*
234 * cost is 0, 1 or 2, depending on how many GRID_OBSTs we must
235 * turn into GRID_HOLEs to play this move.
236 */
237 int cost;
238};
239
240static int movecmp(void *av, void *bv)
241{
242 struct move *a = (struct move *)av;
243 struct move *b = (struct move *)bv;
244
245 if (a->y < b->y)
246 return -1;
247 else if (a->y > b->y)
248 return +1;
249
250 if (a->x < b->x)
251 return -1;
252 else if (a->x > b->x)
253 return +1;
254
255 if (a->dy < b->dy)
256 return -1;
257 else if (a->dy > b->dy)
258 return +1;
259
260 if (a->dx < b->dx)
261 return -1;
262 else if (a->dx > b->dx)
263 return +1;
264
265 return 0;
266}
267
268static int movecmpcost(void *av, void *bv)
269{
270 struct move *a = (struct move *)av;
271 struct move *b = (struct move *)bv;
272
273 if (a->cost < b->cost)
274 return -1;
275 else if (a->cost > b->cost)
276 return +1;
277
278 return movecmp(av, bv);
279}
280
281struct movetrees {
282 tree234 *bymove, *bycost;
283};
284
285static void update_moves(unsigned char *grid, int w, int h, int x, int y,
286 struct movetrees *trees)
287{
288 struct move move;
289 int dir, pos;
290
291 /*
292 * There are twelve moves that can include (x,y): three in each
293 * of four directions. Check each one to see if it's possible.
294 */
295 for (dir = 0; dir < 4; dir++) {
296 int dx, dy;
297
298 if (dir & 1)
299 dx = 0, dy = dir - 2;
300 else
301 dy = 0, dx = dir - 1;
302
303 assert(abs(dx) + abs(dy) == 1);
304
305 for (pos = 0; pos < 3; pos++) {
306 int v1, v2, v3;
307
308 move.dx = dx;
309 move.dy = dy;
310 move.x = x - pos*dx;
311 move.y = y - pos*dy;
312
313 if (move.x < 0 || move.x >= w || move.y < 0 || move.y >= h)
314 continue; /* completely invalid move */
315 if (move.x+2*move.dx < 0 || move.x+2*move.dx >= w ||
316 move.y+2*move.dy < 0 || move.y+2*move.dy >= h)
317 continue; /* completely invalid move */
318
319 v1 = grid[move.y * w + move.x];
320 v2 = grid[(move.y+move.dy) * w + (move.x+move.dx)];
321 v3 = grid[(move.y+2*move.dy)*w + (move.x+2*move.dx)];
322 if (v1 == GRID_PEG && v2 != GRID_PEG && v3 != GRID_PEG) {
323 struct move *m;
324
325 move.cost = (v2 == GRID_OBST) + (v3 == GRID_OBST);
326
327 /*
328 * This move is possible. See if it's already in
329 * the tree.
330 */
331 m = find234(trees->bymove, &move, NULL);
332 if (m && m->cost != move.cost) {
333 /*
334 * It's in the tree but listed with the wrong
335 * cost. Remove the old version.
336 */
337#ifdef GENERATION_DIAGNOSTICS
338 printf("correcting %d%+d,%d%+d at cost %d\n",
339 m->x, m->dx, m->y, m->dy, m->cost);
340#endif
341 del234(trees->bymove, m);
342 del234(trees->bycost, m);
343 sfree(m);
344 m = NULL;
345 }
346 if (!m) {
347 struct move *m, *m2;
348 m = snew(struct move);
349 *m = move;
350 m2 = add234(trees->bymove, m);
351 m2 = add234(trees->bycost, m);
352 assert(m2 == m);
353#ifdef GENERATION_DIAGNOSTICS
354 printf("adding %d%+d,%d%+d at cost %d\n",
355 move.x, move.dx, move.y, move.dy, move.cost);
356#endif
357 } else {
358#ifdef GENERATION_DIAGNOSTICS
359 printf("not adding %d%+d,%d%+d at cost %d\n",
360 move.x, move.dx, move.y, move.dy, move.cost);
361#endif
362 }
363 } else {
364 /*
365 * This move is impossible. If it is already in the
366 * tree, delete it.
367 *
368 * (We make use here of the fact that del234
369 * doesn't have to be passed a pointer to the
370 * _actual_ element it's deleting: it merely needs
371 * one that compares equal to it, and it will
372 * return the one it deletes.)
373 */
374 struct move *m = del234(trees->bymove, &move);
375#ifdef GENERATION_DIAGNOSTICS
376 printf("%sdeleting %d%+d,%d%+d\n", m ? "" : "not ",
377 move.x, move.dx, move.y, move.dy);
378#endif
379 if (m) {
380 del234(trees->bycost, m);
381 sfree(m);
382 }
383 }
384 }
385 }
386}
387
388static void pegs_genmoves(unsigned char *grid, int w, int h, random_state *rs)
389{
390 struct movetrees atrees, *trees = &atrees;
391 struct move *m;
392 int x, y, i, nmoves;
393
394 trees->bymove = newtree234(movecmp);
395 trees->bycost = newtree234(movecmpcost);
396
397 for (y = 0; y < h; y++)
398 for (x = 0; x < w; x++)
399 if (grid[y*w+x] == GRID_PEG)
400 update_moves(grid, w, h, x, y, trees);
401
402 nmoves = 0;
403
404 while (1) {
405 int limit, maxcost, index;
406 struct move mtmp, move, *m;
407
408 /*
409 * See how many moves we can make at zero cost. Make one,
410 * if possible. Failing that, make a one-cost move, and
411 * then a two-cost one.
412 *
413 * After filling at least half the input grid, we no longer
414 * accept cost-2 moves: if that's our only option, we give
415 * up and finish.
416 */
417 mtmp.y = h+1;
418 maxcost = (nmoves < w*h/2 ? 2 : 1);
419 m = NULL; /* placate optimiser */
420 for (mtmp.cost = 0; mtmp.cost <= maxcost; mtmp.cost++) {
421 limit = -1;
422 m = findrelpos234(trees->bycost, &mtmp, NULL, REL234_LT, &limit);
423#ifdef GENERATION_DIAGNOSTICS
424 printf("%d moves available with cost %d\n", limit+1, mtmp.cost);
425#endif
426 if (m)
427 break;
428 }
429 if (!m)
430 break;
431
432 index = random_upto(rs, limit+1);
433 move = *(struct move *)index234(trees->bycost, index);
434
435#ifdef GENERATION_DIAGNOSTICS
436 printf("selecting move %d%+d,%d%+d at cost %d\n",
437 move.x, move.dx, move.y, move.dy, move.cost);
438#endif
439
440 grid[move.y * w + move.x] = GRID_HOLE;
441 grid[(move.y+move.dy) * w + (move.x+move.dx)] = GRID_PEG;
442 grid[(move.y+2*move.dy)*w + (move.x+2*move.dx)] = GRID_PEG;
443
444 for (i = 0; i <= 2; i++) {
445 int tx = move.x + i*move.dx;
446 int ty = move.y + i*move.dy;
447 update_moves(grid, w, h, tx, ty, trees);
448 }
449
450 nmoves++;
451 }
452
453 while ((m = delpos234(trees->bymove, 0)) != NULL) {
454 del234(trees->bycost, m);
455 sfree(m);
456 }
457 freetree234(trees->bymove);
458 freetree234(trees->bycost);
459}
460
461static void pegs_generate(unsigned char *grid, int w, int h, random_state *rs)
462{
463 while (1) {
464 int x, y, extremes;
465
466 memset(grid, GRID_OBST, w*h);
467 grid[(h/2) * w + (w/2)] = GRID_PEG;
468#ifdef GENERATION_DIAGNOSTICS
469 printf("beginning move selection\n");
470#endif
471 pegs_genmoves(grid, w, h, rs);
472#ifdef GENERATION_DIAGNOSTICS
473 printf("finished move selection\n");
474#endif
475
476 extremes = 0;
477 for (y = 0; y < h; y++) {
478 if (grid[y*w+0] != GRID_OBST)
479 extremes |= 1;
480 if (grid[y*w+w-1] != GRID_OBST)
481 extremes |= 2;
482 }
483 for (x = 0; x < w; x++) {
484 if (grid[0*w+x] != GRID_OBST)
485 extremes |= 4;
486 if (grid[(h-1)*w+x] != GRID_OBST)
487 extremes |= 8;
488 }
489
490 if (extremes == 15)
491 break;
492#ifdef GENERATION_DIAGNOSTICS
493 printf("insufficient extent; trying again\n");
494#endif
495 }
496#ifdef GENERATION_DIAGNOSTICS
497 fflush(stdout);
498#endif
499}
500
501/* ----------------------------------------------------------------------
502 * End of board generation code. Now for the client code which uses
503 * it as part of the puzzle.
504 */
505
506static char *new_game_desc(const game_params *params, random_state *rs,
507 char **aux, int interactive)
508{
509 int w = params->w, h = params->h;
510 unsigned char *grid;
511 char *ret;
512 int i;
513
514 grid = snewn(w*h, unsigned char);
515 if (params->type == TYPE_RANDOM) {
516 pegs_generate(grid, w, h, rs);
517 } else {
518 int x, y, cx, cy, v;
519
520 for (y = 0; y < h; y++)
521 for (x = 0; x < w; x++) {
522 v = GRID_OBST; /* placate optimiser */
523 switch (params->type) {
524 case TYPE_CROSS:
525 cx = abs(x - w/2);
526 cy = abs(y - h/2);
527 if (cx == 0 && cy == 0)
528 v = GRID_HOLE;
529 else if (cx > 1 && cy > 1)
530 v = GRID_OBST;
531 else
532 v = GRID_PEG;
533 break;
534 case TYPE_OCTAGON:
535 cx = abs(x - w/2);
536 cy = abs(y - h/2);
537 if (cx + cy > 1 + max(w,h)/2)
538 v = GRID_OBST;
539 else
540 v = GRID_PEG;
541 break;
542 }
543 grid[y*w+x] = v;
544 }
545
546 if (params->type == TYPE_OCTAGON) {
547 /*
548 * The octagonal (European) solitaire layout is
549 * actually _insoluble_ with the starting hole at the
550 * centre. Here's a proof:
551 *
552 * Colour the squares of the board diagonally in
553 * stripes of three different colours, which I'll call
554 * A, B and C. So the board looks like this:
555 *
556 * A B C
557 * A B C A B
558 * A B C A B C A
559 * B C A B C A B
560 * C A B C A B C
561 * B C A B C
562 * A B C
563 *
564 * Suppose we keep running track of the number of pegs
565 * occuping each colour of square. This colouring has
566 * the property that any valid move whatsoever changes
567 * all three of those counts by one (two of them go
568 * down and one goes up), which means that the _parity_
569 * of every count flips on every move.
570 *
571 * If the centre square starts off unoccupied, then
572 * there are twelve pegs on each colour and all three
573 * counts start off even; therefore, after 35 moves all
574 * three counts would have to be odd, which isn't
575 * possible if there's only one peg left. []
576 *
577 * This proof works just as well if the starting hole
578 * is _any_ of the thirteen positions labelled B. Also,
579 * we can stripe the board in the opposite direction
580 * and rule out any square labelled B in that colouring
581 * as well. This leaves:
582 *
583 * Y n Y
584 * n n Y n n
585 * Y n n Y n n Y
586 * n Y Y n Y Y n
587 * Y n n Y n n Y
588 * n n Y n n
589 * Y n Y
590 *
591 * where the ns are squares we've proved insoluble, and
592 * the Ys are the ones remaining.
593 *
594 * That doesn't prove all those starting positions to
595 * be soluble, of course; they're merely the ones we
596 * _haven't_ proved to be impossible. Nevertheless, it
597 * turns out that they are all soluble, so when the
598 * user requests an Octagon board the simplest thing is
599 * to pick one of these at random.
600 *
601 * Rather than picking equiprobably from those twelve
602 * positions, we'll pick equiprobably from the three
603 * equivalence classes
604 */
605 switch (random_upto(rs, 3)) {
606 case 0:
607 /* Remove a random corner piece. */
608 {
609 int dx, dy;
610
611 dx = random_upto(rs, 2) * 2 - 1; /* +1 or -1 */
612 dy = random_upto(rs, 2) * 2 - 1; /* +1 or -1 */
613 if (random_upto(rs, 2))
614 dy *= 3;
615 else
616 dx *= 3;
617 grid[(3+dy)*w+(3+dx)] = GRID_HOLE;
618 }
619 break;
620 case 1:
621 /* Remove a random piece two from the centre. */
622 {
623 int dx, dy;
624 dx = 2 * (random_upto(rs, 2) * 2 - 1);
625 if (random_upto(rs, 2))
626 dy = 0;
627 else
628 dy = dx, dx = 0;
629 grid[(3+dy)*w+(3+dx)] = GRID_HOLE;
630 }
631 break;
632 default /* case 2 */:
633 /* Remove a random piece one from the centre. */
634 {
635 int dx, dy;
636 dx = random_upto(rs, 2) * 2 - 1;
637 if (random_upto(rs, 2))
638 dy = 0;
639 else
640 dy = dx, dx = 0;
641 grid[(3+dy)*w+(3+dx)] = GRID_HOLE;
642 }
643 break;
644 }
645 }
646 }
647
648 /*
649 * Encode a game description which is simply a long list of P
650 * for peg, H for hole or O for obstacle.
651 */
652 ret = snewn(w*h+1, char);
653 for (i = 0; i < w*h; i++)
654 ret[i] = (grid[i] == GRID_PEG ? 'P' :
655 grid[i] == GRID_HOLE ? 'H' : 'O');
656 ret[w*h] = '\0';
657
658 sfree(grid);
659
660 return ret;
661}
662
663static char *validate_desc(const game_params *params, const char *desc)
664{
665 int len = params->w * params->h;
666
667 if (len != strlen(desc))
668 return "Game description is wrong length";
669 if (len != strspn(desc, "PHO"))
670 return "Invalid character in game description";
671
672 return NULL;
673}
674
675static game_state *new_game(midend *me, const game_params *params,
676 const char *desc)
677{
678 int w = params->w, h = params->h;
679 game_state *state = snew(game_state);
680 int i;
681
682 state->w = w;
683 state->h = h;
684 state->completed = 0;
685 state->grid = snewn(w*h, unsigned char);
686 for (i = 0; i < w*h; i++)
687 state->grid[i] = (desc[i] == 'P' ? GRID_PEG :
688 desc[i] == 'H' ? GRID_HOLE : GRID_OBST);
689
690 return state;
691}
692
693static game_state *dup_game(const game_state *state)
694{
695 int w = state->w, h = state->h;
696 game_state *ret = snew(game_state);
697
698 ret->w = state->w;
699 ret->h = state->h;
700 ret->completed = state->completed;
701 ret->grid = snewn(w*h, unsigned char);
702 memcpy(ret->grid, state->grid, w*h);
703
704 return ret;
705}
706
707static void free_game(game_state *state)
708{
709 sfree(state->grid);
710 sfree(state);
711}
712
713static char *solve_game(const game_state *state, const game_state *currstate,
714 const char *aux, char **error)
715{
716 return NULL;
717}
718
719static int game_can_format_as_text_now(const game_params *params)
720{
721 return TRUE;
722}
723
724static char *game_text_format(const game_state *state)
725{
726 int w = state->w, h = state->h;
727 int x, y;
728 char *ret;
729
730 ret = snewn((w+1)*h + 1, char);
731
732 for (y = 0; y < h; y++) {
733 for (x = 0; x < w; x++)
734 ret[y*(w+1)+x] = (state->grid[y*w+x] == GRID_HOLE ? '-' :
735 state->grid[y*w+x] == GRID_PEG ? '*' : ' ');
736 ret[y*(w+1)+w] = '\n';
737 }
738 ret[h*(w+1)] = '\0';
739
740 return ret;
741}
742
743struct game_ui {
744 int dragging; /* boolean: is a drag in progress? */
745 int sx, sy; /* grid coords of drag start cell */
746 int dx, dy; /* pixel coords of current drag posn */
747 int cur_x, cur_y, cur_visible, cur_jumping;
748};
749
750static game_ui *new_ui(const game_state *state)
751{
752 game_ui *ui = snew(game_ui);
753 int x, y, v;
754
755 ui->sx = ui->sy = ui->dx = ui->dy = 0;
756 ui->dragging = FALSE;
757 ui->cur_visible = ui->cur_jumping = 0;
758
759 /* make sure we start the cursor somewhere on the grid. */
760 for (x = 0; x < state->w; x++) {
761 for (y = 0; y < state->h; y++) {
762 v = state->grid[y*state->w+x];
763 if (v == GRID_PEG || v == GRID_HOLE) {
764 ui->cur_x = x; ui->cur_y = y;
765 goto found;
766 }
767 }
768 }
769 assert(!"new_ui found nowhere for cursor");
770found:
771
772 return ui;
773}
774
775static void free_ui(game_ui *ui)
776{
777 sfree(ui);
778}
779
780static char *encode_ui(const game_ui *ui)
781{
782 return NULL;
783}
784
785static void decode_ui(game_ui *ui, const char *encoding)
786{
787}
788
789static void game_changed_state(game_ui *ui, const game_state *oldstate,
790 const game_state *newstate)
791{
792 /*
793 * Cancel a drag, in case the source square has become
794 * unoccupied.
795 */
796 ui->dragging = FALSE;
797}
798
799#define PREFERRED_TILE_SIZE 33
800#define TILESIZE (ds->tilesize)
801#define BORDER (TILESIZE / 2)
802
803#define HIGHLIGHT_WIDTH (TILESIZE / 16)
804
805#define COORD(x) ( BORDER + (x) * TILESIZE )
806#define FROMCOORD(x) ( ((x) + TILESIZE - BORDER) / TILESIZE - 1 )
807
808struct game_drawstate {
809 int tilesize;
810 blitter *drag_background;
811 int dragging, dragx, dragy;
812 int w, h;
813 unsigned char *grid;
814 int started;
815 int bgcolour;
816};
817
818static char *interpret_move(const game_state *state, game_ui *ui,
819 const game_drawstate *ds,
820 int x, int y, int button)
821{
822 int w = state->w, h = state->h;
823 char buf[80];
824
825 if (button == LEFT_BUTTON) {
826 int tx, ty;
827
828 /*
829 * Left button down: we attempt to start a drag.
830 */
831
832 /*
833 * There certainly shouldn't be a current drag in progress,
834 * unless the midend failed to send us button events in
835 * order; it has a responsibility to always get that right,
836 * so we can legitimately punish it by failing an
837 * assertion.
838 */
839 assert(!ui->dragging);
840
841 tx = FROMCOORD(x);
842 ty = FROMCOORD(y);
843 if (tx >= 0 && tx < w && ty >= 0 && ty < h &&
844 state->grid[ty*w+tx] == GRID_PEG) {
845 ui->dragging = TRUE;
846 ui->sx = tx;
847 ui->sy = ty;
848 ui->dx = x;
849 ui->dy = y;
850 ui->cur_visible = ui->cur_jumping = 0;
851 return ""; /* ui modified */
852 }
853 } else if (button == LEFT_DRAG && ui->dragging) {
854 /*
855 * Mouse moved; just move the peg being dragged.
856 */
857 ui->dx = x;
858 ui->dy = y;
859 return ""; /* ui modified */
860 } else if (button == LEFT_RELEASE && ui->dragging) {
861 int tx, ty, dx, dy;
862
863 /*
864 * Button released. Identify the target square of the drag,
865 * see if it represents a valid move, and if so make it.
866 */
867 ui->dragging = FALSE; /* cancel the drag no matter what */
868 tx = FROMCOORD(x);
869 ty = FROMCOORD(y);
870 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
871 return ""; /* target out of range */
872 dx = tx - ui->sx;
873 dy = ty - ui->sy;
874 if (max(abs(dx),abs(dy)) != 2 || min(abs(dx),abs(dy)) != 0)
875 return ""; /* move length was wrong */
876 dx /= 2;
877 dy /= 2;
878
879 if (state->grid[ty*w+tx] != GRID_HOLE ||
880 state->grid[(ty-dy)*w+(tx-dx)] != GRID_PEG ||
881 state->grid[ui->sy*w+ui->sx] != GRID_PEG)
882 return ""; /* grid contents were invalid */
883
884 /*
885 * We have a valid move. Encode it simply as source and
886 * destination coordinate pairs.
887 */
888 sprintf(buf, "%d,%d-%d,%d", ui->sx, ui->sy, tx, ty);
889 return dupstr(buf);
890 } else if (IS_CURSOR_MOVE(button)) {
891 if (!ui->cur_jumping) {
892 /* Not jumping; move cursor as usual, making sure we don't
893 * leave the gameboard (which may be an irregular shape) */
894 int cx = ui->cur_x, cy = ui->cur_y;
895 move_cursor(button, &cx, &cy, w, h, 0);
896 ui->cur_visible = 1;
897 if (state->grid[cy*w+cx] == GRID_HOLE ||
898 state->grid[cy*w+cx] == GRID_PEG) {
899 ui->cur_x = cx;
900 ui->cur_y = cy;
901 }
902 return "";
903 } else {
904 int dx, dy, mx, my, jx, jy;
905
906 /* We're jumping; if the requested direction has a hole, and
907 * there's a peg in the way, */
908 assert(state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG);
909 dx = (button == CURSOR_RIGHT) ? 1 : (button == CURSOR_LEFT) ? -1 : 0;
910 dy = (button == CURSOR_DOWN) ? 1 : (button == CURSOR_UP) ? -1 : 0;
911
912 mx = ui->cur_x+dx; my = ui->cur_y+dy;
913 jx = mx+dx; jy = my+dy;
914
915 ui->cur_jumping = 0; /* reset, whatever. */
916 if (jx >= 0 && jy >= 0 && jx < w && jy < h &&
917 state->grid[my*w+mx] == GRID_PEG &&
918 state->grid[jy*w+jx] == GRID_HOLE) {
919 /* Move cursor to the jumped-to location (this felt more
920 * natural while playtesting) */
921 sprintf(buf, "%d,%d-%d,%d", ui->cur_x, ui->cur_y, jx, jy);
922 ui->cur_x = jx; ui->cur_y = jy;
923 return dupstr(buf);
924 }
925 return "";
926 }
927 } else if (IS_CURSOR_SELECT(button)) {
928 if (!ui->cur_visible) {
929 ui->cur_visible = 1;
930 return "";
931 }
932 if (ui->cur_jumping) {
933 ui->cur_jumping = 0;
934 return "";
935 }
936 if (state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG) {
937 /* cursor is on peg: next arrow-move wil jump. */
938 ui->cur_jumping = 1;
939 return "";
940 }
941 return NULL;
942 }
943
944 return NULL;
945}
946
947static game_state *execute_move(const game_state *state, const char *move)
948{
949 int w = state->w, h = state->h;
950 int sx, sy, tx, ty;
951 game_state *ret;
952
953 if (sscanf(move, "%d,%d-%d,%d", &sx, &sy, &tx, &ty) == 4) {
954 int mx, my, dx, dy;
955
956 if (sx < 0 || sx >= w || sy < 0 || sy >= h)
957 return NULL; /* source out of range */
958 if (tx < 0 || tx >= w || ty < 0 || ty >= h)
959 return NULL; /* target out of range */
960
961 dx = tx - sx;
962 dy = ty - sy;
963 if (max(abs(dx),abs(dy)) != 2 || min(abs(dx),abs(dy)) != 0)
964 return NULL; /* move length was wrong */
965 mx = sx + dx/2;
966 my = sy + dy/2;
967
968 if (state->grid[sy*w+sx] != GRID_PEG ||
969 state->grid[my*w+mx] != GRID_PEG ||
970 state->grid[ty*w+tx] != GRID_HOLE)
971 return NULL; /* grid contents were invalid */
972
973 ret = dup_game(state);
974 ret->grid[sy*w+sx] = GRID_HOLE;
975 ret->grid[my*w+mx] = GRID_HOLE;
976 ret->grid[ty*w+tx] = GRID_PEG;
977
978 /*
979 * Opinion varies on whether getting to a single peg counts as
980 * completing the game, or whether that peg has to be at a
981 * specific location (central in the classic cross game, for
982 * instance). For now we take the former, rather lax position.
983 */
984 if (!ret->completed) {
985 int count = 0, i;
986 for (i = 0; i < w*h; i++)
987 if (ret->grid[i] == GRID_PEG)
988 count++;
989 if (count == 1)
990 ret->completed = 1;
991 }
992
993 return ret;
994 }
995 return NULL;
996}
997
998/* ----------------------------------------------------------------------
999 * Drawing routines.
1000 */
1001
1002static void game_compute_size(const game_params *params, int tilesize,
1003 int *x, int *y)
1004{
1005 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1006 struct { int tilesize; } ads, *ds = &ads;
1007 ads.tilesize = tilesize;
1008
1009 *x = TILESIZE * params->w + 2 * BORDER;
1010 *y = TILESIZE * params->h + 2 * BORDER;
1011}
1012
1013static void game_set_size(drawing *dr, game_drawstate *ds,
1014 const game_params *params, int tilesize)
1015{
1016 ds->tilesize = tilesize;
1017
1018 assert(TILESIZE > 0);
1019
1020 assert(!ds->drag_background); /* set_size is never called twice */
1021 ds->drag_background = blitter_new(dr, TILESIZE, TILESIZE);
1022}
1023
1024static float *game_colours(frontend *fe, int *ncolours)
1025{
1026 float *ret = snewn(3 * NCOLOURS, float);
1027
1028 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1029
1030 ret[COL_PEG * 3 + 0] = 0.0F;
1031 ret[COL_PEG * 3 + 1] = 0.0F;
1032 ret[COL_PEG * 3 + 2] = 1.0F;
1033
1034 ret[COL_CURSOR * 3 + 0] = 0.5F;
1035 ret[COL_CURSOR * 3 + 1] = 0.5F;
1036 ret[COL_CURSOR * 3 + 2] = 1.0F;
1037
1038 *ncolours = NCOLOURS;
1039 return ret;
1040}
1041
1042static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1043{
1044 int w = state->w, h = state->h;
1045 struct game_drawstate *ds = snew(struct game_drawstate);
1046
1047 ds->tilesize = 0; /* not decided yet */
1048
1049 /* We can't allocate the blitter rectangle for the drag background
1050 * until we know what size to make it. */
1051 ds->drag_background = NULL;
1052 ds->dragging = FALSE;
1053
1054 ds->w = w;
1055 ds->h = h;
1056 ds->grid = snewn(w*h, unsigned char);
1057 memset(ds->grid, 255, w*h);
1058
1059 ds->started = FALSE;
1060 ds->bgcolour = -1;
1061
1062 return ds;
1063}
1064
1065static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1066{
1067 if (ds->drag_background)
1068 blitter_free(dr, ds->drag_background);
1069 sfree(ds->grid);
1070 sfree(ds);
1071}
1072
1073static void draw_tile(drawing *dr, game_drawstate *ds,
1074 int x, int y, int v, int bgcolour)
1075{
1076 int cursor = 0, jumping = 0, bg;
1077
1078 if (bgcolour >= 0) {
1079 draw_rect(dr, x, y, TILESIZE, TILESIZE, bgcolour);
1080 }
1081 if (v >= GRID_JUMPING) {
1082 jumping = 1; v -= GRID_JUMPING;
1083 }
1084 if (v >= GRID_CURSOR) {
1085 cursor = 1; v -= GRID_CURSOR;
1086 }
1087
1088 if (v == GRID_HOLE) {
1089 bg = cursor ? COL_HIGHLIGHT : COL_LOWLIGHT;
1090 assert(!jumping); /* can't jump from a hole! */
1091 draw_circle(dr, x+TILESIZE/2, y+TILESIZE/2, TILESIZE/4,
1092 bg, bg);
1093 } else if (v == GRID_PEG) {
1094 bg = (cursor || jumping) ? COL_CURSOR : COL_PEG;
1095 draw_circle(dr, x+TILESIZE/2, y+TILESIZE/2, TILESIZE/3,
1096 bg, bg);
1097 bg = (!cursor || jumping) ? COL_PEG : COL_CURSOR;
1098 draw_circle(dr, x+TILESIZE/2, y+TILESIZE/2, TILESIZE/4,
1099 bg, bg);
1100 }
1101
1102 draw_update(dr, x, y, TILESIZE, TILESIZE);
1103}
1104
1105static void game_redraw(drawing *dr, game_drawstate *ds,
1106 const game_state *oldstate, const game_state *state,
1107 int dir, const game_ui *ui,
1108 float animtime, float flashtime)
1109{
1110 int w = state->w, h = state->h;
1111 int x, y;
1112 int bgcolour;
1113
1114 if (flashtime > 0) {
1115 int frame = (int)(flashtime / FLASH_FRAME);
1116 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
1117 } else
1118 bgcolour = COL_BACKGROUND;
1119
1120 /*
1121 * Erase the sprite currently being dragged, if any.
1122 */
1123 if (ds->dragging) {
1124 assert(ds->drag_background);
1125 blitter_load(dr, ds->drag_background, ds->dragx, ds->dragy);
1126 draw_update(dr, ds->dragx, ds->dragy, TILESIZE, TILESIZE);
1127 ds->dragging = FALSE;
1128 }
1129
1130 if (!ds->started) {
1131 draw_rect(dr, 0, 0,
1132 TILESIZE * state->w + 2 * BORDER,
1133 TILESIZE * state->h + 2 * BORDER, COL_BACKGROUND);
1134
1135 /*
1136 * Draw relief marks around all the squares that aren't
1137 * GRID_OBST.
1138 */
1139 for (y = 0; y < h; y++)
1140 for (x = 0; x < w; x++)
1141 if (state->grid[y*w+x] != GRID_OBST) {
1142 /*
1143 * First pass: draw the full relief square.
1144 */
1145 int coords[6];
1146 coords[0] = COORD(x+1) + HIGHLIGHT_WIDTH - 1;
1147 coords[1] = COORD(y) - HIGHLIGHT_WIDTH;
1148 coords[2] = COORD(x) - HIGHLIGHT_WIDTH;
1149 coords[3] = COORD(y+1) + HIGHLIGHT_WIDTH - 1;
1150 coords[4] = COORD(x) - HIGHLIGHT_WIDTH;
1151 coords[5] = COORD(y) - HIGHLIGHT_WIDTH;
1152 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1153 coords[4] = COORD(x+1) + HIGHLIGHT_WIDTH - 1;
1154 coords[5] = COORD(y+1) + HIGHLIGHT_WIDTH - 1;
1155 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
1156 }
1157 for (y = 0; y < h; y++)
1158 for (x = 0; x < w; x++)
1159 if (state->grid[y*w+x] != GRID_OBST) {
1160 /*
1161 * Second pass: draw everything but the two
1162 * diagonal corners.
1163 */
1164 draw_rect(dr, COORD(x) - HIGHLIGHT_WIDTH,
1165 COORD(y) - HIGHLIGHT_WIDTH,
1166 TILESIZE + HIGHLIGHT_WIDTH,
1167 TILESIZE + HIGHLIGHT_WIDTH, COL_HIGHLIGHT);
1168 draw_rect(dr, COORD(x),
1169 COORD(y),
1170 TILESIZE + HIGHLIGHT_WIDTH,
1171 TILESIZE + HIGHLIGHT_WIDTH, COL_LOWLIGHT);
1172 }
1173 for (y = 0; y < h; y++)
1174 for (x = 0; x < w; x++)
1175 if (state->grid[y*w+x] != GRID_OBST) {
1176 /*
1177 * Third pass: draw a trapezium on each edge.
1178 */
1179 int coords[8];
1180 int dx, dy, s, sn, c;
1181
1182 for (dx = 0; dx < 2; dx++) {
1183 dy = 1 - dx;
1184 for (s = 0; s < 2; s++) {
1185 sn = 2*s - 1;
1186 c = s ? COL_LOWLIGHT : COL_HIGHLIGHT;
1187
1188 coords[0] = COORD(x) + (s*dx)*(TILESIZE-1);
1189 coords[1] = COORD(y) + (s*dy)*(TILESIZE-1);
1190 coords[2] = COORD(x) + (s*dx+dy)*(TILESIZE-1);
1191 coords[3] = COORD(y) + (s*dy+dx)*(TILESIZE-1);
1192 coords[4] = coords[2] - HIGHLIGHT_WIDTH * (dy-sn*dx);
1193 coords[5] = coords[3] - HIGHLIGHT_WIDTH * (dx-sn*dy);
1194 coords[6] = coords[0] + HIGHLIGHT_WIDTH * (dy+sn*dx);
1195 coords[7] = coords[1] + HIGHLIGHT_WIDTH * (dx+sn*dy);
1196 draw_polygon(dr, coords, 4, c, c);
1197 }
1198 }
1199 }
1200 for (y = 0; y < h; y++)
1201 for (x = 0; x < w; x++)
1202 if (state->grid[y*w+x] != GRID_OBST) {
1203 /*
1204 * Second pass: draw everything but the two
1205 * diagonal corners.
1206 */
1207 draw_rect(dr, COORD(x),
1208 COORD(y),
1209 TILESIZE,
1210 TILESIZE, COL_BACKGROUND);
1211 }
1212
1213 ds->started = TRUE;
1214
1215 draw_update(dr, 0, 0,
1216 TILESIZE * state->w + 2 * BORDER,
1217 TILESIZE * state->h + 2 * BORDER);
1218 }
1219
1220 /*
1221 * Loop over the grid redrawing anything that looks as if it
1222 * needs it.
1223 */
1224 for (y = 0; y < h; y++)
1225 for (x = 0; x < w; x++) {
1226 int v;
1227
1228 v = state->grid[y*w+x];
1229 /*
1230 * Blank the source of a drag so it looks as if the
1231 * user picked the peg up physically.
1232 */
1233 if (ui->dragging && ui->sx == x && ui->sy == y && v == GRID_PEG)
1234 v = GRID_HOLE;
1235
1236 if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y)
1237 v += ui->cur_jumping ? GRID_JUMPING : GRID_CURSOR;
1238
1239 if (v != GRID_OBST &&
1240 (bgcolour != ds->bgcolour || /* always redraw when flashing */
1241 v != ds->grid[y*w+x])) {
1242 draw_tile(dr, ds, COORD(x), COORD(y), v, bgcolour);
1243 ds->grid[y*w+x] = v;
1244 }
1245 }
1246
1247 /*
1248 * Draw the dragging sprite if any.
1249 */
1250 if (ui->dragging) {
1251 ds->dragging = TRUE;
1252 ds->dragx = ui->dx - TILESIZE/2;
1253 ds->dragy = ui->dy - TILESIZE/2;
1254 blitter_save(dr, ds->drag_background, ds->dragx, ds->dragy);
1255 draw_tile(dr, ds, ds->dragx, ds->dragy, GRID_PEG, -1);
1256 }
1257
1258 ds->bgcolour = bgcolour;
1259}
1260
1261static float game_anim_length(const game_state *oldstate,
1262 const game_state *newstate, int dir, game_ui *ui)
1263{
1264 return 0.0F;
1265}
1266
1267static float game_flash_length(const game_state *oldstate,
1268 const game_state *newstate, int dir, game_ui *ui)
1269{
1270 if (!oldstate->completed && newstate->completed)
1271 return 2 * FLASH_FRAME;
1272 else
1273 return 0.0F;
1274}
1275
1276static int game_status(const game_state *state)
1277{
1278 /*
1279 * Dead-end situations are assumed to be rescuable by Undo, so we
1280 * don't bother to identify them and return -1.
1281 */
1282 return state->completed ? +1 : 0;
1283}
1284
1285static int game_timing_state(const game_state *state, game_ui *ui)
1286{
1287 return TRUE;
1288}
1289
1290static void game_print_size(const game_params *params, float *x, float *y)
1291{
1292}
1293
1294static void game_print(drawing *dr, const game_state *state, int tilesize)
1295{
1296}
1297
1298#ifdef COMBINED
1299#define thegame pegs
1300#endif
1301
1302const struct game thegame = {
1303 "Pegs", "games.pegs", "pegs",
1304 default_params,
1305 game_fetch_preset, NULL,
1306 decode_params,
1307 encode_params,
1308 free_params,
1309 dup_params,
1310 TRUE, game_configure, custom_params,
1311 validate_params,
1312 new_game_desc,
1313 validate_desc,
1314 new_game,
1315 dup_game,
1316 free_game,
1317 FALSE, solve_game,
1318 TRUE, game_can_format_as_text_now, game_text_format,
1319 new_ui,
1320 free_ui,
1321 encode_ui,
1322 decode_ui,
1323 game_changed_state,
1324 interpret_move,
1325 execute_move,
1326 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1327 game_colours,
1328 game_new_drawstate,
1329 game_free_drawstate,
1330 game_redraw,
1331 game_anim_length,
1332 game_flash_length,
1333 game_status,
1334 FALSE, FALSE, game_print_size, game_print,
1335 FALSE, /* wants_statusbar */
1336 FALSE, game_timing_state,
1337 0, /* flags */
1338};
1339
1340/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/pegs.html b/apps/plugins/puzzles/src/pegs.html
new file mode 100644
index 0000000000..cfe2e76eeb
--- /dev/null
+++ b/apps/plugins/puzzles/src/pegs.html
@@ -0,0 +1,54 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Pegs</title>
7<link rel="previous" href="guess.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="dominosa.html">
12</head>
13<body>
14<p><a href="guess.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="dominosa.html">Next</a></p>
15<h1><a name="C16"></a>Chapter 16: <a name="i0"></a>Pegs</h1>
16<p>
17A number of pegs are placed in holes on a board. You can remove a peg by jumping an adjacent peg over it (horizontally or vertically) to a vacant hole on the other side. Your aim is to remove all but one of the pegs initially present.
18</p>
19<p>
20This game, best known as <a name="i1"></a>&#8216;Peg Solitaire&#8217;, is possibly one of the oldest puzzle games still commonly known.
21</p>
22<h2><a name="S16.1"></a>16.1 <a name="i2"></a>Pegs controls</h2>
23<p>
24To move a peg, drag it with the mouse from its current position to its final position. If the final position is exactly two holes away from the initial position, is currently unoccupied by a peg, and there is a peg in the intervening square, the move will be permitted and the intervening peg will be removed.
25</p>
26<p>
27Vacant spaces which you can move a peg into are marked with holes. A space with no peg and no hole is not available for moving at all: it is an obstacle which you must work around.
28</p>
29<p>
30You can also use the cursor keys to move a position indicator around the board. Pressing the return key while over a peg, followed by a cursor key, will jump the peg in that direction (if that is a legal move).
31</p>
32<p>
33(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
34</p>
35<h2><a name="S16.2"></a>16.2 <a name="i3"></a>Pegs parameters</h2>
36<p>
37These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
38</p>
39<dl><dt>
40<em>Width</em>, <em>Height</em>
41</dt>
42<dd>
43Size of grid in holes.
44</dd>
45<dt>
46<em>Board type</em>
47</dt>
48<dd>
49Controls whether you are given a board of a standard shape or a randomly generated shape. The two standard shapes currently supported are &#8216;Cross&#8217; and &#8216;Octagon&#8217; (also commonly known as the English and European traditional board layouts respectively). Selecting &#8216;Random&#8217; will give you a different board shape every time (but always one that is known to have a solution).
50</dd>
51</dl>
52
53<hr><address></address></body>
54</html>
diff --git a/apps/plugins/puzzles/src/penrose.c b/apps/plugins/puzzles/src/penrose.c
new file mode 100644
index 0000000000..ccde30d8b4
--- /dev/null
+++ b/apps/plugins/puzzles/src/penrose.c
@@ -0,0 +1,629 @@
1/* penrose.c
2 *
3 * Penrose tile generator.
4 *
5 * Uses half-tile technique outlined on:
6 *
7 * http://tartarus.org/simon/20110412-penrose/penrose.xhtml
8 */
9
10#include <assert.h>
11#include <string.h>
12#include <math.h>
13#include <stdio.h>
14
15#include "puzzles.h" /* for malloc routines, and PI */
16#include "penrose.h"
17
18/* -------------------------------------------------------
19 * 36-degree basis vector arithmetic routines.
20 */
21
22/* Imagine drawing a
23 * ten-point 'clock face' like this:
24 *
25 * -E
26 * -D | A
27 * \ | /
28 * -C. \ | / ,B
29 * `-._\|/_,-'
30 * ,-' /|\ `-.
31 * -B' / | \ `C
32 * / | \
33 * -A | D
34 * E
35 *
36 * In case the ASCII art isn't clear, those are supposed to be ten
37 * vectors of length 1, all sticking out from the origin at equal
38 * angular spacing (hence 36 degrees). Our basis vectors are A,B,C,D (I
39 * choose them to be symmetric about the x-axis so that the final
40 * translation into 2d coordinates will also be symmetric, which I
41 * think will avoid minor rounding uglinesses), so our vector
42 * representation sets
43 *
44 * A = (1,0,0,0)
45 * B = (0,1,0,0)
46 * C = (0,0,1,0)
47 * D = (0,0,0,1)
48 *
49 * The fifth vector E looks at first glance as if it needs to be
50 * another basis vector, but in fact it doesn't, because it can be
51 * represented in terms of the other four. Imagine starting from the
52 * origin and following the path -A, +B, -C, +D: you'll find you've
53 * traced four sides of a pentagram, and ended up one E-vector away
54 * from the origin. So we have
55 *
56 * E = (-1,1,-1,1)
57 *
58 * This tells us that we can rotate any vector in this system by 36
59 * degrees: if we start with a*A + b*B + c*C + d*D, we want to end up
60 * with a*B + b*C + c*D + d*E, and we substitute our identity for E to
61 * turn that into a*B + b*C + c*D + d*(-A+B-C+D). In other words,
62 *
63 * rotate_one_notch_clockwise(a,b,c,d) = (-d, d+a, -d+b, d+c)
64 *
65 * and you can verify for yourself that applying that operation
66 * repeatedly starting with (1,0,0,0) cycles round ten vectors and
67 * comes back to where it started.
68 *
69 * The other operation that may be required is to construct vectors
70 * with lengths that are multiples of phi. That can be done by
71 * observing that the vector C-B is parallel to E and has length 1/phi,
72 * and the vector D-A is parallel to E and has length phi. So this
73 * tells us that given any vector, we can construct one which points in
74 * the same direction and is 1/phi or phi times its length, like this:
75 *
76 * divide_by_phi(vector) = rotate(vector, 2) - rotate(vector, 3)
77 * multiply_by_phi(vector) = rotate(vector, 1) - rotate(vector, 4)
78 *
79 * where rotate(vector, n) means applying the above
80 * rotate_one_notch_clockwise primitive n times. Expanding out the
81 * applications of rotate gives the following direct representation in
82 * terms of the vector coordinates:
83 *
84 * divide_by_phi(a,b,c,d) = (b-d, c+d-b, a+b-c, c-a)
85 * multiply_by_phi(a,b,c,d) = (a+b-d, c+d, a+b, c+d-a)
86 *
87 * and you can verify for yourself that those two operations are
88 * inverses of each other (as you'd hope!).
89 *
90 * Having done all of this, testing for equality between two vectors is
91 * a trivial matter of comparing the four integer coordinates. (Which
92 * it _wouldn't_ have been if we'd kept E as a fifth basis vector,
93 * because then (-1,1,-1,1,0) and (0,0,0,0,1) would have had to be
94 * considered identical. So leaving E out is vital.)
95 */
96
97struct vector { int a, b, c, d; };
98
99static vector v_origin(void)
100{
101 vector v;
102 v.a = v.b = v.c = v.d = 0;
103 return v;
104}
105
106/* We start with a unit vector of B: this means we can easily
107 * draw an isoceles triangle centred on the X axis. */
108#ifdef TEST_VECTORS
109
110static vector v_unit(void)
111{
112 vector v;
113
114 v.b = 1;
115 v.a = v.c = v.d = 0;
116 return v;
117}
118
119#endif
120
121#define COS54 0.5877852
122#define SIN54 0.8090169
123#define COS18 0.9510565
124#define SIN18 0.3090169
125
126/* These two are a bit rough-and-ready for now. Note that B/C are
127 * 18 degrees from the x-axis, and A/D are 54 degrees. */
128double v_x(vector *vs, int i)
129{
130 return (vs[i].a + vs[i].d) * COS54 +
131 (vs[i].b + vs[i].c) * COS18;
132}
133
134double v_y(vector *vs, int i)
135{
136 return (vs[i].a - vs[i].d) * SIN54 +
137 (vs[i].b - vs[i].c) * SIN18;
138
139}
140
141static vector v_trans(vector v, vector trans)
142{
143 v.a += trans.a;
144 v.b += trans.b;
145 v.c += trans.c;
146 v.d += trans.d;
147 return v;
148}
149
150static vector v_rotate_36(vector v)
151{
152 vector vv;
153 vv.a = -v.d;
154 vv.b = v.d + v.a;
155 vv.c = -v.d + v.b;
156 vv.d = v.d + v.c;
157 return vv;
158}
159
160static vector v_rotate(vector v, int ang)
161{
162 int i;
163
164 assert((ang % 36) == 0);
165 while (ang < 0) ang += 360;
166 ang = 360-ang;
167 for (i = 0; i < (ang/36); i++)
168 v = v_rotate_36(v);
169 return v;
170}
171
172#ifdef TEST_VECTORS
173
174static vector v_scale(vector v, int sc)
175{
176 v.a *= sc;
177 v.b *= sc;
178 v.c *= sc;
179 v.d *= sc;
180 return v;
181}
182
183#endif
184
185static vector v_growphi(vector v)
186{
187 vector vv;
188 vv.a = v.a + v.b - v.d;
189 vv.b = v.c + v.d;
190 vv.c = v.a + v.b;
191 vv.d = v.c + v.d - v.a;
192 return vv;
193}
194
195static vector v_shrinkphi(vector v)
196{
197 vector vv;
198 vv.a = v.b - v.d;
199 vv.b = v.c + v.d - v.b;
200 vv.c = v.a + v.b - v.c;
201 vv.d = v.c - v.a;
202 return vv;
203}
204
205#ifdef TEST_VECTORS
206
207static const char *v_debug(vector v)
208{
209 static char buf[255];
210 sprintf(buf,
211 "(%d,%d,%d,%d)[%2.2f,%2.2f]",
212 v.a, v.b, v.c, v.d, v_x(&v,0), v_y(&v,0));
213 return buf;
214}
215
216#endif
217
218/* -------------------------------------------------------
219 * Tiling routines.
220 */
221
222static vector xform_coord(vector vo, int shrink, vector vtrans, int ang)
223{
224 if (shrink < 0)
225 vo = v_shrinkphi(vo);
226 else if (shrink > 0)
227 vo = v_growphi(vo);
228
229 vo = v_rotate(vo, ang);
230 vo = v_trans(vo, vtrans);
231
232 return vo;
233}
234
235
236#define XFORM(n,o,s,a) vs[(n)] = xform_coord(v_edge, (s), vs[(o)], (a))
237
238static int penrose_p2_small(penrose_state *state, int depth, int flip,
239 vector v_orig, vector v_edge);
240
241static int penrose_p2_large(penrose_state *state, int depth, int flip,
242 vector v_orig, vector v_edge)
243{
244 vector vv_orig, vv_edge;
245
246#ifdef DEBUG_PENROSE
247 {
248 vector vs[3];
249 vs[0] = v_orig;
250 XFORM(1, 0, 0, 0);
251 XFORM(2, 0, 0, -36*flip);
252
253 state->new_tile(state, vs, 3, depth);
254 }
255#endif
256
257 if (flip > 0) {
258 vector vs[4];
259
260 vs[0] = v_orig;
261 XFORM(1, 0, 0, -36);
262 XFORM(2, 0, 0, 0);
263 XFORM(3, 0, 0, 36);
264
265 state->new_tile(state, vs, 4, depth);
266 }
267 if (depth >= state->max_depth) return 0;
268
269 vv_orig = v_trans(v_orig, v_rotate(v_edge, -36*flip));
270 vv_edge = v_rotate(v_edge, 108*flip);
271
272 penrose_p2_small(state, depth+1, flip,
273 v_orig, v_shrinkphi(v_edge));
274
275 penrose_p2_large(state, depth+1, flip,
276 vv_orig, v_shrinkphi(vv_edge));
277 penrose_p2_large(state, depth+1, -flip,
278 vv_orig, v_shrinkphi(vv_edge));
279
280 return 0;
281}
282
283static int penrose_p2_small(penrose_state *state, int depth, int flip,
284 vector v_orig, vector v_edge)
285{
286 vector vv_orig;
287
288#ifdef DEBUG_PENROSE
289 {
290 vector vs[3];
291 vs[0] = v_orig;
292 XFORM(1, 0, 0, 0);
293 XFORM(2, 0, -1, -36*flip);
294
295 state->new_tile(state, vs, 3, depth);
296 }
297#endif
298
299 if (flip > 0) {
300 vector vs[4];
301
302 vs[0] = v_orig;
303 XFORM(1, 0, 0, -72);
304 XFORM(2, 0, -1, -36);
305 XFORM(3, 0, 0, 0);
306
307 state->new_tile(state, vs, 4, depth);
308 }
309
310 if (depth >= state->max_depth) return 0;
311
312 vv_orig = v_trans(v_orig, v_edge);
313
314 penrose_p2_large(state, depth+1, -flip,
315 v_orig, v_shrinkphi(v_rotate(v_edge, -36*flip)));
316
317 penrose_p2_small(state, depth+1, flip,
318 vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip)));
319
320 return 0;
321}
322
323static int penrose_p3_small(penrose_state *state, int depth, int flip,
324 vector v_orig, vector v_edge);
325
326static int penrose_p3_large(penrose_state *state, int depth, int flip,
327 vector v_orig, vector v_edge)
328{
329 vector vv_orig;
330
331#ifdef DEBUG_PENROSE
332 {
333 vector vs[3];
334 vs[0] = v_orig;
335 XFORM(1, 0, 1, 0);
336 XFORM(2, 0, 0, -36*flip);
337
338 state->new_tile(state, vs, 3, depth);
339 }
340#endif
341
342 if (flip > 0) {
343 vector vs[4];
344
345 vs[0] = v_orig;
346 XFORM(1, 0, 0, -36);
347 XFORM(2, 0, 1, 0);
348 XFORM(3, 0, 0, 36);
349
350 state->new_tile(state, vs, 4, depth);
351 }
352 if (depth >= state->max_depth) return 0;
353
354 vv_orig = v_trans(v_orig, v_edge);
355
356 penrose_p3_large(state, depth+1, -flip,
357 vv_orig, v_shrinkphi(v_rotate(v_edge, 180)));
358
359 penrose_p3_small(state, depth+1, flip,
360 vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip)));
361
362 vv_orig = v_trans(v_orig, v_growphi(v_edge));
363
364 penrose_p3_large(state, depth+1, flip,
365 vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip)));
366
367
368 return 0;
369}
370
371static int penrose_p3_small(penrose_state *state, int depth, int flip,
372 vector v_orig, vector v_edge)
373{
374 vector vv_orig;
375
376#ifdef DEBUG_PENROSE
377 {
378 vector vs[3];
379 vs[0] = v_orig;
380 XFORM(1, 0, 0, 0);
381 XFORM(2, 0, 0, -36*flip);
382
383 state->new_tile(state, vs, 3, depth);
384 }
385#endif
386
387 if (flip > 0) {
388 vector vs[4];
389
390 vs[0] = v_orig;
391 XFORM(1, 0, 0, -36);
392 XFORM(3, 0, 0, 0);
393 XFORM(2, 3, 0, -36);
394
395 state->new_tile(state, vs, 4, depth);
396 }
397 if (depth >= state->max_depth) return 0;
398
399 /* NB these two are identical to the first two of p3_large. */
400 vv_orig = v_trans(v_orig, v_edge);
401
402 penrose_p3_large(state, depth+1, -flip,
403 vv_orig, v_shrinkphi(v_rotate(v_edge, 180)));
404
405 penrose_p3_small(state, depth+1, flip,
406 vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip)));
407
408 return 0;
409}
410
411/* -------------------------------------------------------
412 * Utility routines.
413 */
414
415double penrose_side_length(double start_size, int depth)
416{
417 return start_size / pow(PHI, depth);
418}
419
420void penrose_count_tiles(int depth, int *nlarge, int *nsmall)
421{
422 /* Steal sgt's fibonacci thingummy. */
423}
424
425/*
426 * It turns out that an acute isosceles triangle with sides in ratio 1:phi:phi
427 * has an incentre which is conveniently 2*phi^-2 of the way from the apex to
428 * the base. Why's that convenient? Because: if we situate the incentre of the
429 * triangle at the origin, then we can place the apex at phi^-2 * (B+C), and
430 * the other two vertices at apex-B and apex-C respectively. So that's an acute
431 * triangle with its long sides of unit length, covering a circle about the
432 * origin of radius 1-(2*phi^-2), which is conveniently enough phi^-3.
433 *
434 * (later mail: this is an overestimate by about 5%)
435 */
436
437int penrose(penrose_state *state, int which, int angle)
438{
439 vector vo = v_origin();
440 vector vb = v_origin();
441
442 vo.b = vo.c = -state->start_size;
443 vo = v_shrinkphi(v_shrinkphi(vo));
444
445 vb.b = state->start_size;
446
447 vo = v_rotate(vo, angle);
448 vb = v_rotate(vb, angle);
449
450 if (which == PENROSE_P2)
451 return penrose_p2_large(state, 0, 1, vo, vb);
452 else
453 return penrose_p3_small(state, 0, 1, vo, vb);
454}
455
456/*
457 * We're asked for a MxN grid, which just means a tiling fitting into roughly
458 * an MxN space in some kind of reasonable unit - say, the side length of the
459 * two-arrow edges of the tiles. By some reasoning in a previous email, that
460 * means we want to pick some subarea of a circle of radius 3.11*sqrt(M^2+N^2).
461 * To cover that circle, we need to subdivide a triangle large enough that it
462 * contains a circle of that radius.
463 *
464 * Hence: start with those three vectors marking triangle vertices, scale them
465 * all up by phi repeatedly until the radius of the inscribed circle gets
466 * bigger than the target, and then recurse into that triangle with the same
467 * recursion depth as the number of times you scaled up. That will give you
468 * tiles of unit side length, covering a circle big enough that if you randomly
469 * choose an orientation and coordinates within the circle, you'll be able to
470 * get any valid piece of Penrose tiling of size MxN.
471 */
472#define INCIRCLE_RADIUS 0.22426 /* phi^-3 less 5%: see above */
473
474void penrose_calculate_size(int which, int tilesize, int w, int h,
475 double *required_radius, int *start_size, int *depth)
476{
477 double rradius, size;
478 int n = 0;
479
480 /*
481 * Fudge factor to scale P2 and P3 tilings differently. This
482 * doesn't seem to have much relevance to questions like the
483 * average number of tiles per unit area; it's just aesthetic.
484 */
485 if (which == PENROSE_P2)
486 tilesize = tilesize * 3 / 2;
487 else
488 tilesize = tilesize * 5 / 4;
489
490 rradius = tilesize * 3.11 * sqrt((double)(w*w + h*h));
491 size = tilesize;
492
493 while ((size * INCIRCLE_RADIUS) < rradius) {
494 n++;
495 size = size * PHI;
496 }
497
498 *start_size = (int)size;
499 *depth = n;
500 *required_radius = rradius;
501}
502
503/* -------------------------------------------------------
504 * Test code.
505 */
506
507#ifdef TEST_PENROSE
508
509#include <stdio.h>
510#include <string.h>
511
512int show_recursion = 0;
513int ntiles, nfinal;
514
515int test_cb(penrose_state *state, vector *vs, int n, int depth)
516{
517 int i, xoff = 0, yoff = 0;
518 double l = penrose_side_length(state->start_size, depth);
519 double rball = l / 10.0;
520 const char *col;
521
522 ntiles++;
523 if (state->max_depth == depth) {
524 col = n == 4 ? "black" : "green";
525 nfinal++;
526 } else {
527 if (!show_recursion)
528 return 0;
529 col = n == 4 ? "red" : "blue";
530 }
531 if (n != 4) yoff = state->start_size;
532
533 printf("<polygon points=\"");
534 for (i = 0; i < n; i++) {
535 printf("%s%f,%f", (i == 0) ? "" : " ",
536 v_x(vs, i) + xoff, v_y(vs, i) + yoff);
537 }
538 printf("\" style=\"fill: %s; fill-opacity: 0.2; stroke: %s\" />\n", col, col);
539 printf("<ellipse cx=\"%f\" cy=\"%f\" rx=\"%f\" ry=\"%f\" fill=\"%s\" />",
540 v_x(vs, 0) + xoff, v_y(vs, 0) + yoff, rball, rball, col);
541
542 return 0;
543}
544
545void usage_exit(void)
546{
547 fprintf(stderr, "Usage: penrose-test [--recursion] P2|P3 SIZE DEPTH\n");
548 exit(1);
549}
550
551int main(int argc, char *argv[])
552{
553 penrose_state ps;
554 int which = 0;
555
556 while (--argc > 0) {
557 char *p = *++argv;
558 if (!strcmp(p, "-h") || !strcmp(p, "--help")) {
559 usage_exit();
560 } else if (!strcmp(p, "--recursion")) {
561 show_recursion = 1;
562 } else if (*p == '-') {
563 fprintf(stderr, "Unrecognised option '%s'\n", p);
564 exit(1);
565 } else {
566 break;
567 }
568 }
569
570 if (argc < 3) usage_exit();
571
572 if (strcmp(argv[0], "P2") == 0) which = PENROSE_P2;
573 else if (strcmp(argv[0], "P3") == 0) which = PENROSE_P3;
574 else usage_exit();
575
576 ps.start_size = atoi(argv[1]);
577 ps.max_depth = atoi(argv[2]);
578 ps.new_tile = test_cb;
579
580 ntiles = nfinal = 0;
581
582 printf("\
583<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
584<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n\
585\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\
586\n\
587<svg xmlns=\"http://www.w3.org/2000/svg\"\n\
588xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
589
590 printf("<g>\n");
591 penrose(&ps, which);
592 printf("</g>\n");
593
594 printf("<!-- %d tiles and %d leaf tiles total -->\n",
595 ntiles, nfinal);
596
597 printf("</svg>");
598
599 return 0;
600}
601
602#endif
603
604#ifdef TEST_VECTORS
605
606static void dbgv(const char *msg, vector v)
607{
608 printf("%s: %s\n", msg, v_debug(v));
609}
610
611int main(int argc, const char *argv[])
612{
613 vector v = v_unit();
614
615 dbgv("unit vector", v);
616 v = v_rotate(v, 36);
617 dbgv("rotated 36", v);
618 v = v_scale(v, 2);
619 dbgv("scaled x2", v);
620 v = v_shrinkphi(v);
621 dbgv("shrunk phi", v);
622 v = v_rotate(v, -36);
623 dbgv("rotated -36", v);
624
625 return 0;
626}
627
628#endif
629/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/penrose.h b/apps/plugins/puzzles/src/penrose.h
new file mode 100644
index 0000000000..ba5ae16f2c
--- /dev/null
+++ b/apps/plugins/puzzles/src/penrose.h
@@ -0,0 +1,59 @@
1/* penrose.h
2 *
3 * Penrose tiling functions.
4 *
5 * Provides an interface with which to generate Penrose tilings
6 * by recursive subdivision of an initial tile of choice (one of the
7 * four sets of two pairs kite/dart, or thin/thick rhombus).
8 *
9 * You supply a callback function and a context pointer, which is
10 * called with each tile in turn: you choose how many times to recurse.
11 */
12
13#ifndef _PENROSE_H
14#define _PENROSE_H
15
16#ifndef PHI
17#define PHI 1.6180339887
18#endif
19
20typedef struct vector vector;
21
22double v_x(vector *vs, int i);
23double v_y(vector *vs, int i);
24
25typedef struct penrose_state penrose_state;
26
27/* Return non-zero to clip the tree here (i.e. not recurse
28 * below this tile).
29 *
30 * Parameters are state, vector array, npoints, depth.
31 * ctx is inside state.
32 */
33typedef int (*tile_callback)(penrose_state *, vector *, int, int);
34
35struct penrose_state {
36 int start_size; /* initial side length */
37 int max_depth; /* Recursion depth */
38
39 tile_callback new_tile;
40 void *ctx; /* for callback */
41};
42
43enum { PENROSE_P2, PENROSE_P3 };
44
45extern int penrose(penrose_state *state, int which, int angle);
46
47/* Returns the side-length of a penrose tile at recursion level
48 * gen, given a starting side length. */
49extern double penrose_side_length(double start_size, int depth);
50
51/* Returns the count of each type of tile at a given recursion depth. */
52extern void penrose_count_tiles(int gen, int *nlarge, int *nsmall);
53
54/* Calculate start size and recursion depth required to produce a
55 * width-by-height sized patch of penrose tiles with the given tilesize */
56extern void penrose_calculate_size(int which, int tilesize, int w, int h,
57 double *required_radius, int *start_size, int *depth);
58
59#endif
diff --git a/apps/plugins/puzzles/src/printing.c b/apps/plugins/puzzles/src/printing.c
new file mode 100644
index 0000000000..e921a4d545
--- /dev/null
+++ b/apps/plugins/puzzles/src/printing.c
@@ -0,0 +1,247 @@
1/*
2 * printing.c: Cross-platform printing manager. Handles document
3 * setup and layout.
4 */
5
6#include "puzzles.h"
7
8struct puzzle {
9 const game *game;
10 game_params *par;
11 game_state *st;
12 game_state *st2;
13};
14
15struct document {
16 int pw, ph;
17 int npuzzles;
18 struct puzzle *puzzles;
19 int puzzlesize;
20 int got_solns;
21 float *colwid, *rowht;
22 float userscale;
23};
24
25/*
26 * Create a new print document. pw and ph are the layout
27 * parameters: they state how many puzzles will be printed across
28 * the page, and down the page.
29 */
30document *document_new(int pw, int ph, float userscale)
31{
32 document *doc = snew(document);
33
34 doc->pw = pw;
35 doc->ph = ph;
36 doc->puzzles = NULL;
37 doc->puzzlesize = doc->npuzzles = 0;
38 doc->got_solns = FALSE;
39
40 doc->colwid = snewn(pw, float);
41 doc->rowht = snewn(ph, float);
42
43 doc->userscale = userscale;
44
45 return doc;
46}
47
48/*
49 * Free a document structure, whether it's been printed or not.
50 */
51void document_free(document *doc)
52{
53 int i;
54
55 for (i = 0; i < doc->npuzzles; i++) {
56 doc->puzzles[i].game->free_params(doc->puzzles[i].par);
57 doc->puzzles[i].game->free_game(doc->puzzles[i].st);
58 if (doc->puzzles[i].st2)
59 doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
60 }
61
62 sfree(doc->colwid);
63 sfree(doc->rowht);
64
65 sfree(doc->puzzles);
66 sfree(doc);
67}
68
69/*
70 * Called from midend.c to add a puzzle to be printed. Provides a
71 * game_params (for initial layout computation), a game_state, and
72 * optionally a second game_state to be printed in parallel on
73 * another sheet (typically the solution to the first game_state).
74 */
75void document_add_puzzle(document *doc, const game *game, game_params *par,
76 game_state *st, game_state *st2)
77{
78 if (doc->npuzzles >= doc->puzzlesize) {
79 doc->puzzlesize += 32;
80 doc->puzzles = sresize(doc->puzzles, doc->puzzlesize, struct puzzle);
81 }
82 doc->puzzles[doc->npuzzles].game = game;
83 doc->puzzles[doc->npuzzles].par = par;
84 doc->puzzles[doc->npuzzles].st = st;
85 doc->puzzles[doc->npuzzles].st2 = st2;
86 doc->npuzzles++;
87 if (st2)
88 doc->got_solns = TRUE;
89}
90
91static void get_puzzle_size(document *doc, struct puzzle *pz,
92 float *w, float *h, float *scale)
93{
94 float ww, hh, ourscale;
95
96 /* Get the preferred size of the game, in mm. */
97 pz->game->print_size(pz->par, &ww, &hh);
98
99 /* Adjust for user-supplied scale factor. */
100 ourscale = doc->userscale;
101
102 /*
103 * FIXME: scale it down here if it's too big for the page size.
104 * Rather than do complicated things involving scaling all
105 * columns down in proportion, the simplest approach seems to
106 * me to be to scale down until the game fits within one evenly
107 * divided cell of the page (i.e. width/pw by height/ph).
108 *
109 * In order to do this step we need the page size available.
110 */
111
112 *scale = ourscale;
113 *w = ww * ourscale;
114 *h = hh * ourscale;
115}
116
117/*
118 * Having accumulated a load of puzzles, actually do the printing.
119 */
120void document_print(document *doc, drawing *dr)
121{
122 int ppp; /* puzzles per page */
123 int pages, passes;
124 int page, pass;
125 int pageno;
126
127 ppp = doc->pw * doc->ph;
128 pages = (doc->npuzzles + ppp - 1) / ppp;
129 passes = (doc->got_solns ? 2 : 1);
130
131 print_begin_doc(dr, pages * passes);
132
133 pageno = 1;
134 for (pass = 0; pass < passes; pass++) {
135 for (page = 0; page < pages; page++) {
136 int i, n, offset;
137 float colsum, rowsum;
138
139 print_begin_page(dr, pageno);
140
141 offset = page * ppp;
142 n = min(ppp, doc->npuzzles - offset);
143
144 for (i = 0; i < doc->pw; i++)
145 doc->colwid[i] = 0;
146 for (i = 0; i < doc->ph; i++)
147 doc->rowht[i] = 0;
148
149 /*
150 * Lay the page out by computing all the puzzle sizes.
151 */
152 for (i = 0; i < n; i++) {
153 struct puzzle *pz = doc->puzzles + offset + i;
154 int x = i % doc->pw, y = i / doc->pw;
155 float w, h, scale;
156
157 get_puzzle_size(doc, pz, &w, &h, &scale);
158
159 /* Update the maximum width/height of this column. */
160 doc->colwid[x] = max(doc->colwid[x], w);
161 doc->rowht[y] = max(doc->rowht[y], h);
162 }
163
164 /*
165 * Add up the maximum column/row widths to get the
166 * total amount of space used up by puzzles on the
167 * page. We will use this to compute gutter widths.
168 */
169 colsum = 0.0;
170 for (i = 0; i < doc->pw; i++)
171 colsum += doc->colwid[i];
172 rowsum = 0.0;
173 for (i = 0; i < doc->ph; i++)
174 rowsum += doc->rowht[i];
175
176 /*
177 * Now do the printing.
178 */
179 for (i = 0; i < n; i++) {
180 struct puzzle *pz = doc->puzzles + offset + i;
181 int x = i % doc->pw, y = i / doc->pw, j;
182 float w, h, scale, xm, xc, ym, yc;
183 int pixw, pixh, tilesize;
184
185 if (pass == 1 && !pz->st2)
186 continue; /* nothing to do */
187
188 /*
189 * The total amount of gutter space is the page
190 * width minus colsum. This is divided into pw+1
191 * gutters, so the amount of horizontal gutter
192 * space appearing to the left of this puzzle
193 * column is
194 *
195 * (width-colsum) * (x+1)/(pw+1)
196 * = width * (x+1)/(pw+1) - (colsum * (x+1)/(pw+1))
197 */
198 xm = (float)(x+1) / (doc->pw + 1);
199 xc = -xm * colsum;
200 /* And similarly for y. */
201 ym = (float)(y+1) / (doc->ph + 1);
202 yc = -ym * rowsum;
203
204 /*
205 * However, the amount of space to the left of this
206 * puzzle isn't just gutter space: we must also
207 * count the widths of all the previous columns.
208 */
209 for (j = 0; j < x; j++)
210 xc += doc->colwid[j];
211 /* And similarly for rows. */
212 for (j = 0; j < y; j++)
213 yc += doc->rowht[j];
214
215 /*
216 * Now we adjust for this _specific_ puzzle, which
217 * means centring it within the cell we've just
218 * computed.
219 */
220 get_puzzle_size(doc, pz, &w, &h, &scale);
221 xc += (doc->colwid[x] - w) / 2;
222 yc += (doc->rowht[y] - h) / 2;
223
224 /*
225 * And now we know where and how big we want to
226 * print the puzzle, just go ahead and do so. For
227 * the moment I'll pick a standard pixel tile size
228 * of 512.
229 *
230 * (FIXME: would it be better to pick this value
231 * with reference to the printer resolution? Or
232 * permit each game to choose its own?)
233 */
234 tilesize = 512;
235 pz->game->compute_size(pz->par, tilesize, &pixw, &pixh);
236 print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
237 pz->game->print(dr, pass == 0 ? pz->st : pz->st2, tilesize);
238 print_end_puzzle(dr);
239 }
240
241 print_end_page(dr, pageno);
242 pageno++;
243 }
244 }
245
246 print_end_doc(dr);
247}
diff --git a/apps/plugins/puzzles/src/ps.c b/apps/plugins/puzzles/src/ps.c
new file mode 100644
index 0000000000..2394cc5e8e
--- /dev/null
+++ b/apps/plugins/puzzles/src/ps.c
@@ -0,0 +1,433 @@
1/*
2 * ps.c: PostScript printing functions.
3 */
4
5#include <stdio.h>
6#include <stdarg.h>
7#include <string.h>
8#include <assert.h>
9
10#include "puzzles.h"
11
12#define ROOT2 1.414213562
13
14struct psdata {
15 FILE *fp;
16 int colour;
17 int ytop;
18 int clipped;
19 float hatchthick, hatchspace;
20 int gamewidth, gameheight;
21 drawing *drawing;
22};
23
24static void ps_printf(psdata *ps, char *fmt, ...)
25{
26 va_list ap;
27
28 va_start(ap, fmt);
29 vfprintf(ps->fp, fmt, ap);
30 va_end(ap);
31}
32
33static void ps_fill(psdata *ps, int colour)
34{
35 int hatch;
36 float r, g, b;
37
38 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
39
40 if (hatch < 0) {
41 if (ps->colour)
42 ps_printf(ps, "%g %g %g setrgbcolor fill\n", r, g, b);
43 else
44 ps_printf(ps, "%g setgray fill\n", r);
45 } else {
46 /* Clip to the region. */
47 ps_printf(ps, "gsave clip\n");
48 /* Hatch the entire game printing area. */
49 ps_printf(ps, "newpath\n");
50 if (hatch == HATCH_VERT || hatch == HATCH_PLUS)
51 ps_printf(ps, "0 %g %d {\n"
52 " 0 moveto 0 %d rlineto\n"
53 "} for\n", ps->hatchspace, ps->gamewidth,
54 ps->gameheight);
55 if (hatch == HATCH_HORIZ || hatch == HATCH_PLUS)
56 ps_printf(ps, "0 %g %d {\n"
57 " 0 exch moveto %d 0 rlineto\n"
58 "} for\n", ps->hatchspace, ps->gameheight,
59 ps->gamewidth);
60 if (hatch == HATCH_SLASH || hatch == HATCH_X)
61 ps_printf(ps, "%d %g %d {\n"
62 " 0 moveto %d dup rlineto\n"
63 "} for\n", -ps->gameheight, ps->hatchspace * ROOT2,
64 ps->gamewidth, max(ps->gamewidth, ps->gameheight));
65 if (hatch == HATCH_BACKSLASH || hatch == HATCH_X)
66 ps_printf(ps, "0 %g %d {\n"
67 " 0 moveto %d neg dup neg rlineto\n"
68 "} for\n", ps->hatchspace * ROOT2,
69 ps->gamewidth+ps->gameheight,
70 max(ps->gamewidth, ps->gameheight));
71 ps_printf(ps, "0 setgray %g setlinewidth stroke grestore\n",
72 ps->hatchthick);
73 }
74}
75
76static void ps_setcolour_internal(psdata *ps, int colour, char *suffix)
77{
78 int hatch;
79 float r, g, b;
80
81 print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b);
82
83 /*
84 * Stroking in hatched colours is not permitted.
85 */
86 assert(hatch < 0);
87
88 if (ps->colour)
89 ps_printf(ps, "%g %g %g setrgbcolor%s\n", r, g, b, suffix);
90 else
91 ps_printf(ps, "%g setgray%s\n", r, suffix);
92}
93
94static void ps_setcolour(psdata *ps, int colour)
95{
96 ps_setcolour_internal(ps, colour, "");
97}
98
99static void ps_stroke(psdata *ps, int colour)
100{
101 ps_setcolour_internal(ps, colour, " stroke");
102}
103
104static void ps_draw_text(void *handle, int x, int y, int fonttype,
105 int fontsize, int align, int colour, char *text)
106{
107 psdata *ps = (psdata *)handle;
108
109 y = ps->ytop - y;
110 ps_setcolour(ps, colour);
111 ps_printf(ps, "/%s findfont %d scalefont setfont\n",
112 fonttype == FONT_FIXED ? "Courier-L1" : "Helvetica-L1",
113 fontsize);
114 if (align & ALIGN_VCENTRE) {
115 ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath"
116 " pathbbox\n"
117 "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n",
118 y, x);
119 } else {
120 ps_printf(ps, "%d %d moveto\n", x, y);
121 }
122 ps_printf(ps, "(");
123 while (*text) {
124 if (*text == '\\' || *text == '(' || *text == ')')
125 ps_printf(ps, "\\");
126 ps_printf(ps, "%c", *text);
127 text++;
128 }
129 ps_printf(ps, ") ");
130 if (align & (ALIGN_HCENTRE | ALIGN_HRIGHT))
131 ps_printf(ps, "dup stringwidth pop %sneg 0 rmoveto show\n",
132 (align & ALIGN_HCENTRE) ? "2 div " : "");
133 else
134 ps_printf(ps, "show\n");
135}
136
137static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour)
138{
139 psdata *ps = (psdata *)handle;
140
141 y = ps->ytop - y;
142 /*
143 * Offset by half a pixel for the exactness requirement.
144 */
145 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
146 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
147 ps_fill(ps, colour);
148}
149
150static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2,
151 int colour)
152{
153 psdata *ps = (psdata *)handle;
154
155 y1 = ps->ytop - y1;
156 y2 = ps->ytop - y2;
157 ps_printf(ps, "newpath %d %d moveto %d %d lineto\n", x1, y1, x2, y2);
158 ps_stroke(ps, colour);
159}
160
161static void ps_draw_polygon(void *handle, int *coords, int npoints,
162 int fillcolour, int outlinecolour)
163{
164 psdata *ps = (psdata *)handle;
165
166 int i;
167
168 ps_printf(ps, "newpath %d %d moveto\n", coords[0], ps->ytop - coords[1]);
169
170 for (i = 1; i < npoints; i++)
171 ps_printf(ps, "%d %d lineto\n", coords[i*2], ps->ytop - coords[i*2+1]);
172
173 ps_printf(ps, "closepath\n");
174
175 if (fillcolour >= 0) {
176 ps_printf(ps, "gsave\n");
177 ps_fill(ps, fillcolour);
178 ps_printf(ps, "grestore\n");
179 }
180 ps_stroke(ps, outlinecolour);
181}
182
183static void ps_draw_circle(void *handle, int cx, int cy, int radius,
184 int fillcolour, int outlinecolour)
185{
186 psdata *ps = (psdata *)handle;
187
188 cy = ps->ytop - cy;
189
190 ps_printf(ps, "newpath %d %d %d 0 360 arc closepath\n", cx, cy, radius);
191
192 if (fillcolour >= 0) {
193 ps_printf(ps, "gsave\n");
194 ps_fill(ps, fillcolour);
195 ps_printf(ps, "grestore\n");
196 }
197 ps_stroke(ps, outlinecolour);
198}
199
200static void ps_unclip(void *handle)
201{
202 psdata *ps = (psdata *)handle;
203
204 assert(ps->clipped);
205 ps_printf(ps, "grestore\n");
206 ps->clipped = FALSE;
207}
208
209static void ps_clip(void *handle, int x, int y, int w, int h)
210{
211 psdata *ps = (psdata *)handle;
212
213 if (ps->clipped)
214 ps_unclip(ps);
215
216 y = ps->ytop - y;
217 /*
218 * Offset by half a pixel for the exactness requirement.
219 */
220 ps_printf(ps, "gsave\n");
221 ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto"
222 " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w);
223 ps_printf(ps, "clip\n");
224 ps->clipped = TRUE;
225}
226
227static void ps_line_width(void *handle, float width)
228{
229 psdata *ps = (psdata *)handle;
230
231 ps_printf(ps, "%g setlinewidth\n", width);
232}
233
234static void ps_line_dotted(void *handle, int dotted)
235{
236 psdata *ps = (psdata *)handle;
237
238 if (dotted) {
239 ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n");
240 } else {
241 ps_printf(ps, "[ ] 0 setdash\n");
242 }
243}
244
245static char *ps_text_fallback(void *handle, const char *const *strings,
246 int nstrings)
247{
248 /*
249 * We can handle anything in ISO 8859-1, and we'll manually
250 * translate it out of UTF-8 for the purpose.
251 */
252 int i, maxlen;
253 char *ret;
254
255 maxlen = 0;
256 for (i = 0; i < nstrings; i++) {
257 int len = strlen(strings[i]);
258 if (maxlen < len) maxlen = len;
259 }
260
261 ret = snewn(maxlen + 1, char);
262
263 for (i = 0; i < nstrings; i++) {
264 const char *p = strings[i];
265 char *q = ret;
266
267 while (*p) {
268 int c = (unsigned char)*p++;
269 if (c < 0x80) {
270 *q++ = c; /* ASCII */
271 } else if ((c == 0xC2 || c == 0xC3) && (*p & 0xC0) == 0x80) {
272 *q++ = (c << 6) | (*p++ & 0x3F); /* top half of 8859-1 */
273 } else {
274 break;
275 }
276 }
277
278 if (!*p) {
279 *q = '\0';
280 return ret;
281 }
282 }
283
284 assert(!"Should never reach here");
285 return NULL;
286}
287
288static void ps_begin_doc(void *handle, int pages)
289{
290 psdata *ps = (psdata *)handle;
291
292 fputs("%!PS-Adobe-3.0\n", ps->fp);
293 fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp);
294 fputs("%%DocumentData: Clean7Bit\n", ps->fp);
295 fputs("%%LanguageLevel: 1\n", ps->fp);
296 fprintf(ps->fp, "%%%%Pages: %d\n", pages);
297 fputs("%%DocumentNeededResources:\n", ps->fp);
298 fputs("%%+ font Helvetica\n", ps->fp);
299 fputs("%%+ font Courier\n", ps->fp);
300 fputs("%%EndComments\n", ps->fp);
301 fputs("%%BeginSetup\n", ps->fp);
302 fputs("%%IncludeResource: font Helvetica\n", ps->fp);
303 fputs("%%IncludeResource: font Courier\n", ps->fp);
304 fputs("%%EndSetup\n", ps->fp);
305 fputs("%%BeginProlog\n", ps->fp);
306 /*
307 * Re-encode Helvetica and Courier into ISO-8859-1, which gives
308 * us times and divide signs - and also (according to the
309 * Language Reference Manual) a bonus in that the ASCII '-' code
310 * point now points to a minus sign instead of a hyphen.
311 */
312 fputs("/Helvetica findfont " /* get the font dictionary */
313 "dup maxlength dict dup begin " /* create and open a new dict */
314 "exch " /* move the original font to top of stack */
315 "{1 index /FID ne {def} {pop pop} ifelse} forall "
316 /* copy everything except FID */
317 "/Encoding ISOLatin1Encoding def "
318 /* set the thing we actually wanted to change */
319 "/FontName /Helvetica-L1 def " /* set a new font name */
320 "FontName end exch definefont" /* and define the font */
321 "\n", ps->fp);
322 fputs("/Courier findfont " /* get the font dictionary */
323 "dup maxlength dict dup begin " /* create and open a new dict */
324 "exch " /* move the original font to top of stack */
325 "{1 index /FID ne {def} {pop pop} ifelse} forall "
326 /* copy everything except FID */
327 "/Encoding ISOLatin1Encoding def "
328 /* set the thing we actually wanted to change */
329 "/FontName /Courier-L1 def " /* set a new font name */
330 "FontName end exch definefont" /* and define the font */
331 "\n", ps->fp);
332 fputs("%%EndProlog\n", ps->fp);
333}
334
335static void ps_begin_page(void *handle, int number)
336{
337 psdata *ps = (psdata *)handle;
338
339 fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n",
340 number, number, 72.0 / 25.4);
341}
342
343static void ps_begin_puzzle(void *handle, float xm, float xc,
344 float ym, float yc, int pw, int ph, float wmm)
345{
346 psdata *ps = (psdata *)handle;
347
348 fprintf(ps->fp, "gsave\n"
349 "clippath flattenpath pathbbox pop pop translate\n"
350 "clippath flattenpath pathbbox 4 2 roll pop pop\n"
351 "exch %g mul %g add exch dup %g mul %g add sub translate\n"
352 "%g dup scale\n"
353 "0 -%d translate\n", xm, xc, ym, yc, wmm/pw, ph);
354 ps->ytop = ph;
355 ps->clipped = FALSE;
356 ps->gamewidth = pw;
357 ps->gameheight = ph;
358 ps->hatchthick = 0.2 * pw / wmm;
359 ps->hatchspace = 1.0 * pw / wmm;
360}
361
362static void ps_end_puzzle(void *handle)
363{
364 psdata *ps = (psdata *)handle;
365
366 fputs("grestore\n", ps->fp);
367}
368
369static void ps_end_page(void *handle, int number)
370{
371 psdata *ps = (psdata *)handle;
372
373 fputs("restore grestore showpage\n", ps->fp);
374}
375
376static void ps_end_doc(void *handle)
377{
378 psdata *ps = (psdata *)handle;
379
380 fputs("%%EOF\n", ps->fp);
381}
382
383static const struct drawing_api ps_drawing = {
384 ps_draw_text,
385 ps_draw_rect,
386 ps_draw_line,
387 ps_draw_polygon,
388 ps_draw_circle,
389 NULL /* draw_update */,
390 ps_clip,
391 ps_unclip,
392 NULL /* start_draw */,
393 NULL /* end_draw */,
394 NULL /* status_bar */,
395 NULL /* blitter_new */,
396 NULL /* blitter_free */,
397 NULL /* blitter_save */,
398 NULL /* blitter_load */,
399 ps_begin_doc,
400 ps_begin_page,
401 ps_begin_puzzle,
402 ps_end_puzzle,
403 ps_end_page,
404 ps_end_doc,
405 ps_line_width,
406 ps_line_dotted,
407 ps_text_fallback,
408};
409
410psdata *ps_init(FILE *outfile, int colour)
411{
412 psdata *ps = snew(psdata);
413
414 ps->fp = outfile;
415 ps->colour = colour;
416 ps->ytop = 0;
417 ps->clipped = FALSE;
418 ps->hatchthick = ps->hatchspace = ps->gamewidth = ps->gameheight = 0;
419 ps->drawing = drawing_new(&ps_drawing, NULL, ps);
420
421 return ps;
422}
423
424void ps_free(psdata *ps)
425{
426 drawing_free(ps->drawing);
427 sfree(ps);
428}
429
430drawing *ps_drawing_api(psdata *ps)
431{
432 return ps->drawing;
433}
diff --git a/apps/plugins/puzzles/src/puzzles.but b/apps/plugins/puzzles/src/puzzles.but
new file mode 100644
index 0000000000..2508fe3337
--- /dev/null
+++ b/apps/plugins/puzzles/src/puzzles.but
@@ -0,0 +1,3401 @@
1\title Simon Tatham's Portable Puzzle Collection
2
3\cfg{winhelp-filename}{puzzles.hlp}
4\cfg{winhelp-contents-titlepage}{Contents}
5
6\cfg{text-filename}{puzzles.txt}
7
8\cfg{html-contents-filename}{index.html}
9\cfg{html-template-filename}{%k.html}
10\cfg{html-index-filename}{docindex.html}
11\cfg{html-leaf-level}{1}
12\cfg{html-contents-depth-0}{1}
13\cfg{html-contents-depth-1}{2}
14\cfg{html-leaf-contains-contents}{true}
15
16\cfg{info-filename}{puzzles.info}
17
18\cfg{ps-filename}{puzzles.ps}
19\cfg{pdf-filename}{puzzles.pdf}
20
21\define{by} \u00D7{x}
22
23\define{dash} \u2013{-}
24
25\define{times} \u00D7{*}
26
27\define{divide} \u00F7{/}
28
29\define{minus} \u2212{-}
30
31This is a collection of small one-player puzzle games.
32
33\copyright This manual is copyright 2004-2014 Simon Tatham. All rights
34reserved. You may distribute this documentation under the MIT licence.
35See \k{licence} for the licence text in full.
36
37\cfg{html-local-head}{<meta name="AppleTitle" content="Puzzles Help">}
38
39\C{intro} Introduction
40
41I wrote this collection because I thought there should be more small
42desktop toys available: little games you can pop up in a window and
43play for two or three minutes while you take a break from whatever
44else you were doing. And I was also annoyed that every time I found
45a good game on (say) \i{Unix}, it wasn't available the next time I
46was sitting at a \i{Windows} machine, or vice versa; so I arranged
47that everything in my personal puzzle collection will happily run on
48both, and have more recently done a port to \i{Mac OS X} as well. When I
49find (or perhaps invent) further puzzle games that I like, they'll
50be added to this collection and will immediately be available on
51both platforms. And if anyone feels like writing any other front
52ends \dash PocketPC, Mac OS pre-10, or whatever it might be \dash
53then all the games in this framework will immediately become
54available on another platform as well.
55
56The actual games in this collection were mostly not my invention; they
57are re-implementations of existing game concepts within my portable
58puzzle framework. I do not claim credit, in general, for inventing the
59rules of any of these puzzles. (I don't even claim authorship of all
60the code; some of the puzzles have been submitted by other authors.)
61
62This collection is distributed under the \i{MIT licence} (see
63\k{licence}). This means that you can do pretty much anything you like
64with the game binaries or the code, except pretending you wrote them
65yourself, or suing me if anything goes wrong.
66
67The most recent versions, and \i{source code}, can be found at
68\I{website}\W{http://www.chiark.greenend.org.uk/~sgtatham/puzzles/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/puzzles/}.
69
70Please report \I{feedback}\i{bugs} to
71\W{mailto:anakin@pobox.com}\cw{anakin@pobox.com}.
72You might find it helpful to read this article before reporting a bug:
73
74\W{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\cw{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}
75
76\ii{Patches} are welcome. Especially if they provide a new front end
77(to make all these games run on another platform), or a new game.
78
79
80\C{common} \ii{Common features}
81
82This chapter describes features that are common to all the games.
83
84\H{common-actions} \I{controls}Common actions
85
86These actions are all available from the \I{Game menu}\q{Game} menu
87and via \I{keys}keyboard shortcuts, in addition to any game-specific
88actions.
89
90(On \i{Mac OS X}, to conform with local user interface standards, these
91actions are situated on the \I{File menu}\q{File} and \I{Edit
92menu}\q{Edit} menus instead.)
93
94\dt \ii\e{New game} (\q{N}, Ctrl+\q{N})
95
96\dd Starts a new game, with a random initial state.
97
98\dt \ii\e{Restart game}
99
100\dd Resets the current game to its initial state. (This can be undone.)
101
102\dt \ii\e{Load}
103
104\dd Loads a saved game from a file on disk.
105
106\dt \ii\e{Save}
107
108\dd Saves the current state of your game to a file on disk.
109
110\lcont{
111
112The Load and Save operations preserve your entire game
113history (so you can save, reload, and still Undo and Redo things you
114had done before saving).
115
116}
117
118\dt \I{printing, on Windows}\e{Print}
119
120\dd Where supported (currently only on Windows), brings up a dialog
121allowing you to print an arbitrary number of puzzles randomly
122generated from the current parameters, optionally including the
123current puzzle. (Only for puzzles which make sense to print, of
124course \dash it's hard to think of a sensible printable representation
125of Fifteen!)
126
127\dt \ii\e{Undo} (\q{U}, Ctrl+\q{Z}, Ctrl+\q{_})
128
129\dd Undoes a single move. (You can undo moves back to the start of the
130session.)
131
132\dt \ii\e{Redo} (\q{R}, Ctrl+\q{R})
133
134\dd Redoes a previously undone move.
135
136\dt \ii\e{Copy}
137
138\dd Copies the current state of your game to the clipboard in text
139format, so that you can paste it into (say) an e-mail client or a
140web message board if you're discussing the game with someone else.
141(Not all games support this feature.)
142
143\dt \ii\e{Solve}
144
145\dd Transforms the puzzle instantly into its solved state. For some
146games (Cube) this feature is not supported at all because it is of
147no particular use. For other games (such as Pattern), the solved
148state can be used to give you information, if you can't see how a
149solution can exist at all or you want to know where you made a
150mistake. For still other games (such as Sixteen), automatic solution
151tells you nothing about how to \e{get} to the solution, but it does
152provide a useful way to get there quickly so that you can experiment
153with set-piece moves and transformations.
154
155\lcont{
156
157Some games (such as Solo) are capable of solving a game ID you have
158typed in from elsewhere. Other games (such as Rectangles) cannot
159solve a game ID they didn't invent themself, but when they did
160invent the game ID they know what the solution is already. Still
161other games (Pattern) can solve \e{some} external game IDs, but only
162if they aren't too difficult.
163
164The \q{Solve} command adds the solved state to the end of the undo
165chain for the puzzle. In other words, if you want to go back to
166solving it yourself after seeing the answer, you can just press Undo.
167
168}
169
170\dt \I{exit}\ii\e{Quit} (\q{Q}, Ctrl+\q{Q})
171
172\dd Closes the application entirely.
173
174\H{common-id} Specifying games with the \ii{game ID}
175
176There are two ways to save a game specification out of a puzzle and
177recreate it later, or recreate it in somebody else's copy of the
178same puzzle.
179
180The \q{\i{Specific}} and \q{\i{Random Seed}} options from the
181\I{Game menu}\q{Game} menu (or the \q{File} menu, on \i{Mac OS X}) each
182show a piece of text (a \q{game ID}) which is sufficient to
183reconstruct precisely the same game at a later date.
184
185You can enter either of these pieces of text back into the program
186(via the same \q{Specific} or \q{Random Seed} menu options) at a
187later point, and it will recreate the same game. You can also use
188either one as a \i{command line} argument (on Windows or Unix); see
189\k{common-cmdline} for more detail.
190
191The difference between the two forms is that a descriptive game ID
192is a literal \e{description} of the \i{initial state} of the game,
193whereas a random seed is just a piece of arbitrary text which was
194provided as input to the random number generator used to create the
195puzzle. This means that:
196
197\b Descriptive game IDs tend to be longer in many puzzles (although
198some, such as Cube (\k{cube}), only need very short descriptions).
199So a random seed is often a \e{quicker} way to note down the puzzle
200you're currently playing, or to tell it to somebody else so they can
201play the same one as you.
202
203\b Any text at all is a valid random seed. The automatically
204generated ones are fifteen-digit numbers, but anything will do; you
205can type in your full name, or a word you just made up, and a valid
206puzzle will be generated from it. This provides a way for two or
207more people to race to complete the same puzzle: you think of a
208random seed, then everybody types it in at the same time, and nobody
209has an advantage due to having seen the generated puzzle before
210anybody else.
211
212\b It is often possible to convert puzzles from other sources (such
213as \q{nonograms} or \q{sudoku} from newspapers) into descriptive
214game IDs suitable for use with these programs.
215
216\b Random seeds are not guaranteed to produce the same result if you
217use them with a different \i\e{version} of the puzzle program. This
218is because the generation algorithm might have been improved or
219modified in later versions of the code, and will therefore produce a
220different result when given the same sequence of random numbers. Use
221a descriptive game ID if you aren't sure that it will be used on the
222same version of the program as yours.
223
224\lcont{(Use the \q{About} menu option to find out the version number
225of the program. Programs with the same version number running on
226different platforms should still be random-seed compatible.)}
227
228\I{ID format}A descriptive game ID starts with a piece of text which
229encodes the \i\e{parameters} of the current game (such as grid
230size). Then there is a colon, and after that is the description of
231the game's initial state. A random seed starts with a similar string
232of parameters, but then it contains a hash sign followed by
233arbitrary data.
234
235If you enter a descriptive game ID, the program will not be able to
236show you the random seed which generated it, since it wasn't
237generated \e{from} a random seed. If you \e{enter} a random seed,
238however, the program will be able to show you the descriptive game
239ID derived from that random seed.
240
241Note that the game parameter strings are not always identical
242between the two forms. For some games, there will be parameter data
243provided with the random seed which is not included in the
244descriptive game ID. This is because that parameter information is
245only relevant when \e{generating} puzzle grids, and is not important
246when playing them. Thus, for example, the difficulty level in Solo
247(\k{solo}) is not mentioned in the descriptive game ID.
248
249These additional parameters are also not set permanently if you type
250in a game ID. For example, suppose you have Solo set to \q{Advanced}
251difficulty level, and then a friend wants your help with a
252\q{Trivial} puzzle; so the friend reads out a random seed specifying
253\q{Trivial} difficulty, and you type it in. The program will
254generate you the same \q{Trivial} grid which your friend was having
255trouble with, but once you have finished playing it, when you ask
256for a new game it will automatically go back to the \q{Advanced}
257difficulty which it was previously set on.
258
259\H{common-type} The \q{Type} menu
260
261The \I{Type menu}\q{Type} menu, if present, may contain a list of
262\i{preset} game settings. Selecting one of these will start a new
263random game with the parameters specified.
264
265The \q{Type} menu may also contain a \q{\i{Custom}} option which
266allows you to fine-tune game \i{parameters}. The parameters
267available are specific to each game and are described in the
268following sections.
269
270\H{common-cmdline} Specifying game parameters on the \i{command line}
271
272(This section does not apply to the \i{Mac OS X} version.)
273
274The games in this collection deliberately do not ever save
275information on to the computer they run on: they have no high score
276tables and no saved preferences. (This is because I expect at least
277some people to play them at work, and those people will probably
278appreciate leaving as little evidence as possible!)
279
280However, if you do want to arrange for one of these games to
281\I{default parameters, specifying}default to a particular set of
282parameters, you can specify them on the command line.
283
284The easiest way to do this is to set up the parameters you want
285using the \q{Type} menu (see \k{common-type}), and then to select
286\q{Random Seed} from the \q{Game} or \q{File} menu (see
287\k{common-id}). The text in the \q{Game ID} box will be composed of
288two parts, separated by a hash. The first of these parts represents
289the game parameters (the size of the playing area, for example, and
290anything else you set using the \q{Type} menu).
291
292If you run the game with just that parameter text on the command
293line, it will start up with the settings you specified.
294
295For example: if you run Cube (see \k{cube}), select \q{Octahedron}
296from the \q{Type} menu, and then go to the game ID selection, you
297will see a string of the form \cq{o2x2#338686542711620}. Take only
298the part before the hash (\cq{o2x2}), and start Cube with that text
299on the command line: \cq{PREFIX-cube o2x2}.
300
301If you copy the \e{entire} game ID on to the command line, the game
302will start up in the specific game that was described. This is
303occasionally a more convenient way to start a particular game ID
304than by pasting it into the game ID selection box.
305
306(You could also retrieve the encoded game parameters using the
307\q{Specific} menu option instead of \q{Random Seed}, but if you do
308then some options, such as the difficulty level in Solo, will be
309missing. See \k{common-id} for more details on this.)
310
311\H{common-unix-cmdline} \i{Unix} \i{command-line} options
312
313(This section only applies to the Unix port.)
314
315In addition to being able to specify game parameters on the command
316line (see \k{common-cmdline}), there are various other options:
317
318\dt \cw{--game}
319
320\dt \cw{--load}
321
322\dd These options respectively determine whether the command-line
323argument is treated as specifying game parameters or a \i{save} file
324to \i{load}. Only one should be specified. If neither of these options
325is specified, a guess is made based on the format of the argument.
326
327\dt \cw{--generate }\e{n}
328
329\dd If this option is specified, instead of a puzzle being displayed,
330a number of descriptive game IDs will be \I{generating game IDs}invented
331and printed on standard output. This is useful for gaining access to
332the game generation algorithms without necessarily using the frontend.
333
334\lcont{
335
336If game parameters are specified on the command-line, they will be
337used to generate the game IDs; otherwise a default set of parameters
338will be used.
339
340The most common use of this option is in conjunction with \c{--print},
341in which case its behaviour is slightly different; see below.
342
343}
344
345\dt \I{printing, on Unix}\cw{--print }\e{w}\cw{x}\e{h}
346
347\dd If this option is specified, instead of a puzzle being displayed,
348a printed representation of one or more unsolved puzzles is sent to
349standard output, in \i{PostScript} format.
350
351\lcont{
352
353On each page of puzzles, there will be \e{w} across and \e{h} down. If
354there are more puzzles than \e{w}\by\e{h}, more than one page will be
355printed.
356
357If \c{--generate} has also been specified, the invented game IDs will
358be used to generate the printed output. Otherwise, a list of game IDs
359is expected on standard input (which can be descriptive or random
360seeds; see \k{common-id}), in the same format produced by
361\c{--generate}.
362
363For example:
364
365\c PREFIX-net --generate 12 --print 2x3 7x7w | lpr
366
367will generate two pages of printed Net puzzles (each of which will
368have a 7\by\.7 wrapping grid), and pipe the output to the \c{lpr}
369command, which on many systems will send them to an actual printer.
370
371There are various other options which affect printing; see below.
372
373}
374
375\dt \cw{--save }\e{file-prefix} [ \cw{--save-suffix }\e{file-suffix} ]
376
377\dd If this option is specified, instead of a puzzle being
378displayed, saved-game files for one or more unsolved puzzles are
379written to files constructed from the supplied prefix and/or suffix.
380
381\lcont{
382
383If \c{--generate} has also been specified, the invented game IDs will
384be used to generate the printed output. Otherwise, a list of game IDs
385is expected on standard input (which can be descriptive or random
386seeds; see \k{common-id}), in the same format produced by
387\c{--generate}.
388
389For example:
390
391\c PREFIX-net --generate 12 --save game --save-suffix .sav
392
393will generate twelve Net saved-game files with the names
394\cw{game0.sav} to \cw{game11.sav}.
395
396}
397
398\dt \cw{--version}
399
400\dd Prints version information about the game, and then quits.
401
402The following options are only meaningful if \c{--print} is also
403specified:
404
405\dt \cw{--with-solutions}
406
407\dd The set of pages filled with unsolved puzzles will be followed by
408the solutions to those puzzles.
409
410\dt \cw{--scale }\e{n}
411
412\dd Adjusts how big each puzzle is when printed. Larger numbers make
413puzzles bigger; the default is 1.0.
414
415\dt \cw{--colour}
416
417\dd Puzzles will be printed in colour, rather than in black and white
418(if supported by the puzzle).
419
420
421\C{net} \i{Net}
422
423\cfg{winhelp-topic}{games.net}
424
425(\e{Note:} the \i{Windows} version of this game is called
426\i\cw{NETGAME.EXE} to avoid clashing with Windows's own \cw{NET.EXE}.)
427
428I originally saw this in the form of a Flash game called \i{FreeNet}
429\k{FreeNet}, written by Pavils Jurjans; there are several other
430implementations under the name \i{NetWalk}. The computer prepares a
431network by connecting up the centres of squares in a grid, and then
432shuffles the network by rotating every tile randomly. Your job is to
433rotate it all back into place. The successful solution will be an
434entirely connected network, with no closed loops. \#{The latter
435clause means that there are no closed paths within the network.
436Could this be clearer? "No closed paths"?} As a visual aid,
437all tiles which are connected to the one in the middle are
438highlighted.
439
440\B{FreeNet} \W{http://www.jurjans.lv/stuff/net/FreeNet.htm}\cw{http://www.jurjans.lv/stuff/net/FreeNet.htm}
441
442\H{net-controls} \i{Net controls}
443
444\IM{Net controls} controls, for Net
445\IM{Net controls} keys, for Net
446\IM{Net controls} shortcuts (keyboard), for Net
447
448This game can be played with either the keyboard or the mouse. The
449controls are:
450
451\dt \e{Select tile}: mouse pointer, arrow keys
452
453\dt \e{Rotate tile anticlockwise}: left mouse button, \q{A} key
454
455\dt \e{Rotate tile clockwise}: right mouse button, \q{D} key
456
457\dt \e{Rotate tile by 180 degrees}: \q{F} key
458
459\dt \e{Lock (or unlock) tile}: middle mouse button, shift-click, \q{S} key
460
461\dd You can lock a tile once you're sure of its orientation. You can
462also unlock it again, but while it's locked you can't accidentally
463turn it.
464
465The following controls are not necessary to complete the game, but may
466be useful:
467
468\dt \e{Shift grid}: Shift + arrow keys
469
470\dd On grids that wrap, you can move the origin of the grid, so that
471tiles that were on opposite sides of the grid can be seen together.
472
473\dt \e{Move centre}: Ctrl + arrow keys
474
475\dd You can change which tile is used as the source of highlighting.
476(It doesn't ultimately matter which tile this is, as every tile will
477be connected to every other tile in a correct solution, but it may be
478helpful in the intermediate stages of solving the puzzle.)
479
480\dt \e{Jumble tiles}: \q{J} key
481
482\dd This key turns all tiles that are not locked to random
483orientations.
484
485(All the actions described in \k{common-actions} are also available.)
486
487\H{net-params} \I{parameters, for Net}Net parameters
488
489These parameters are available from the \q{Custom...} option on the
490\q{Type} menu.
491
492\dt \e{Width}, \e{Height}
493
494\dd Size of grid in tiles.
495
496\dt \e{Walls wrap around}
497
498\dd If checked, flow can pass from the left edge to the right edge,
499and from top to bottom, and vice versa.
500
501\dt \e{Barrier probability}
502
503\dd A number between 0.0 and 1.0 controlling whether an immovable
504barrier is placed between two tiles to prevent flow between them (a
505higher number gives more barriers). Since barriers are immovable, they
506act as constraints on the solution (i.e., hints).
507
508\lcont{
509
510The grid generation in Net has been carefully arranged so that the
511barriers are independent of the rest of the grid. This means that if
512you note down the random seed used to generate the current puzzle
513(see \k{common-id}), change the \e{Barrier probability} parameter,
514and then re-enter the same random seed, you should see exactly the
515same starting grid, with the only change being the number of
516barriers. So if you're stuck on a particular grid and need a hint,
517you could start up another instance of Net, set up the same
518parameters but a higher barrier probability, and enter the game seed
519from the original Net window.
520
521}
522
523\dt \e{Ensure unique solution}
524
525\dd Normally, Net will make sure that the puzzles it presents have
526only one solution. Puzzles with ambiguous sections can be more
527difficult and more subtle, so if you like you can turn off this
528feature and risk having ambiguous puzzles. (Also, finding \e{all}
529the possible solutions can be an additional challenge for an
530advanced player.)
531
532
533\C{cube} \i{Cube}
534
535\cfg{winhelp-topic}{games.cube}
536
537This is another one I originally saw as a web game. This one was a
538Java game \k{cube-java-game}, by Paul Scott. You have a grid of 16
539squares, six of which are blue; on one square rests a cube. Your move
540is to use the arrow keys to roll the cube through 90 degrees so that
541it moves to an adjacent square. If you roll the cube on to a blue
542square, the blue square is picked up on one face of the cube; if you
543roll a blue face of the cube on to a non-blue square, the blueness is
544put down again. (In general, whenever you roll the cube, the two faces
545that come into contact swap colours.) Your job is to get all six blue
546squares on to the six faces of the cube at the same time. Count your
547moves and try to do it in as few as possible.
548
549Unlike the original Java game, my version has an additional feature:
550once you've mastered the game with a cube rolling on a square grid,
551you can change to a triangular grid and roll any of a tetrahedron, an
552octahedron or an icosahedron.
553
554\B{cube-java-game} \W{http://www3.sympatico.ca/paulscott/cube/cube.htm}\cw{http://www3.sympatico.ca/paulscott/cube/cube.htm}
555
556\H{cube-controls} \i{Cube controls}
557
558\IM{Cube controls} controls, for Cube
559\IM{Cube controls} keys, for Cube
560\IM{Cube controls} shortcuts (keyboard), for Cube
561
562This game can be played with either the keyboard or the mouse.
563
564Left-clicking anywhere on the window will move the cube (or other
565solid) towards the mouse pointer.
566
567The arrow keys can also used to roll the cube on its square grid in
568the four cardinal directions.
569On the triangular grids, the mapping of arrow keys to directions is
570more approximate. Vertical movement is disallowed where it doesn't
571make sense. The four keys surrounding the arrow keys on the numeric
572keypad (\q{7}, \q{9}, \q{1}, \q{3}) can be used for diagonal movement.
573
574(All the actions described in \k{common-actions} are also available.)
575
576\H{cube-params} \I{parameters, for Cube}Cube parameters
577
578These parameters are available from the \q{Custom...} option on the
579\q{Type} menu.
580
581\dt \e{Type of solid}
582
583\dd Selects the solid to roll (and hence the shape of the grid):
584tetrahedron, cube, octahedron, or icosahedron.
585
586\dt \e{Width / top}, \e{Height / bottom}
587
588\dd On a square grid, horizontal and vertical dimensions. On a
589triangular grid, the number of triangles on the top and bottom rows
590respectively.
591
592
593\C{fifteen} \i{Fifteen}
594
595\cfg{winhelp-topic}{games.fifteen}
596
597The old ones are the best: this is the good old \q{\i{15-puzzle}}
598with sliding tiles. You have a 4\by\.4 square grid; 15 squares
599contain numbered tiles, and the sixteenth is empty. Your move is to
600choose a tile next to the empty space, and slide it into the space.
601The aim is to end up with the tiles in numerical order, with the
602space in the bottom right (so that the top row reads 1,2,3,4 and the
603bottom row reads 13,14,15,\e{space}).
604
605\H{fifteen-controls} \i{Fifteen controls}
606
607\IM{Fifteen controls} controls, for Fifteen
608\IM{Fifteen controls} keys, for Fifteen
609\IM{Fifteen controls} shortcuts (keyboard), for Fifteen
610
611This game can be controlled with the mouse or the keyboard.
612
613A left-click with the mouse in the row or column containing the empty
614space will move as many tiles as necessary to move the space to the
615mouse pointer.
616
617The arrow keys will move a tile adjacent to the space in the direction
618indicated (moving the space in the \e{opposite} direction).
619
620Pressing \q{h} will make a suggested move. Pressing \q{h} enough
621times will solve the game, but it may scramble your progress while
622doing so.
623
624(All the actions described in \k{common-actions} are also available.)
625
626\H{fifteen-params} \I{parameters, for Fifteen}Fifteen parameters
627
628The only options available from the \q{Custom...} option on the \q{Type}
629menu are \e{Width} and \e{Height}, which are self-explanatory. (Once
630you've changed these, it's not a \q{15-puzzle} any more, of course!)
631
632
633\C{sixteen} \i{Sixteen}
634
635\cfg{winhelp-topic}{games.sixteen}
636
637Another sliding tile puzzle, visually similar to Fifteen (see
638\k{fifteen}) but with a different type of move. This time, there is no
639hole: all 16 squares on the grid contain numbered squares. Your move
640is to shift an entire row left or right, or shift an entire column up
641or down; every time you do that, the tile you shift off the grid
642re-appears at the other end of the same row, in the space you just
643vacated. To win, arrange the tiles into numerical order (1,2,3,4 on
644the top row, 13,14,15,16 on the bottom). When you've done that, try
645playing on different sizes of grid.
646
647I \e{might} have invented this game myself, though only by accident if
648so (and I'm sure other people have independently invented it). I
649thought I was imitating a screensaver I'd seen, but I have a feeling
650that the screensaver might actually have been a Fifteen-type puzzle
651rather than this slightly different kind. So this might be the one
652thing in my puzzle collection which represents creativity on my part
653rather than just engineering.
654
655\H{sixteen-controls} \I{controls, for Sixteen}Sixteen controls
656
657Left-clicking on an arrow will move the appropriate row or column in
658the direction indicated. Right-clicking will move it in the opposite
659direction.
660
661Alternatively, use the cursor keys to move the position indicator
662around the edge of the grid, and use the return key to move the
663row/column in the direction indicated.
664
665You can also move the tiles directly. Move the cursor onto a tile,
666hold Control and press an arrow key to move the tile under the
667cursor and move the cursor along with the tile. Or, hold Shift to
668move only the tile. Pressing Enter simulates holding down Control
669(press Enter again to release), while pressing Space simulates
670holding down shift.
671
672(All the actions described in \k{common-actions} are also available.)
673
674\H{sixteen-params} \I{parameters, for Sixteen}Sixteen parameters
675
676The parameters available from the \q{Custom...} option on the
677\q{Type} menu are:
678
679\b \e{Width} and \e{Height}, which are self-explanatory.
680
681\b You can ask for a limited shuffling operation to be performed on
682the grid. By default, Sixteen will shuffle the grid in such a way
683that any arrangement is about as probable as any other. You can
684override this by requesting a precise number of shuffling moves to
685be performed. Typically your aim is then to determine the precise
686set of shuffling moves and invert them exactly, so that you answer
687(say) a four-move shuffle with a four-move solution. Note that the
688more moves you ask for, the more likely it is that solutions shorter
689than the target length will turn out to be possible.
690
691
692\C{twiddle} \i{Twiddle}
693
694\cfg{winhelp-topic}{games.twiddle}
695
696Twiddle is a tile-rearrangement puzzle, visually similar to Sixteen
697(see \k{sixteen}): you are given a grid of square tiles, each
698containing a number, and your aim is to arrange the numbers into
699ascending order.
700
701In basic Twiddle, your move is to rotate a square group of four
702tiles about their common centre. (Orientation is not significant in
703the basic puzzle, although you can select it.) On more advanced
704settings, you can rotate a larger square group of tiles.
705
706I first saw this type of puzzle in the GameCube game \q{Metroid
707Prime 2}. In the Main Gyro Chamber in that game, there is a puzzle
708you solve to unlock a door, which is a special case of Twiddle. I
709developed this game as a generalisation of that puzzle.
710
711\H{twiddle-controls} \I{controls, for Twiddle}Twiddle controls
712
713To play Twiddle, click the mouse in the centre of the square group
714you wish to rotate. In the basic mode, you rotate a 2\by\.2 square,
715which means you have to click at a corner point where four tiles
716meet.
717
718In more advanced modes you might be rotating 3\by\.3 or even more at
719a time; if the size of the square is odd then you simply click in
720the centre tile of the square you want to rotate.
721
722Clicking with the left mouse button rotates the group anticlockwise.
723Clicking with the right button rotates it clockwise.
724
725You can also move an outline square around the grid with the cursor
726keys; the square is the size above (2\by\.2 by default, or larger).
727Pressing the return key or space bar will rotate the current square
728anticlockwise or clockwise respectively.
729
730(All the actions described in \k{common-actions} are also available.)
731
732\H{twiddle-parameters} \I{parameters, for Twiddle}Twiddle parameters
733
734Twiddle provides several configuration options via the \q{Custom}
735option on the \q{Type} menu:
736
737\b You can configure the width and height of the puzzle grid.
738
739\b You can configure the size of square block that rotates at a time.
740
741\b You can ask for every square in the grid to be distinguishable
742(the default), or you can ask for a simplified puzzle in which there
743are groups of identical numbers. In the simplified puzzle your aim
744is just to arrange all the 1s into the first row, all the 2s into
745the second row, and so on.
746
747\b You can configure whether the orientation of tiles matters. If
748you ask for an orientable puzzle, each tile will have a triangle
749drawn in it. All the triangles must be pointing upwards to complete
750the puzzle.
751
752\b You can ask for a limited shuffling operation to be performed on
753the grid. By default, Twiddle will shuffle the grid so much that any
754arrangement is about as probable as any other. You can override this
755by requesting a precise number of shuffling moves to be performed.
756Typically your aim is then to determine the precise set of shuffling
757moves and invert them exactly, so that you answer (say) a four-move
758shuffle with a four-move solution. Note that the more moves you ask
759for, the more likely it is that solutions shorter than the target
760length will turn out to be possible.
761
762
763\C{rect} \i{Rectangles}
764
765\cfg{winhelp-topic}{games.rectangles}
766
767You have a grid of squares, with numbers written in some (but not all)
768of the squares. Your task is to subdivide the grid into rectangles of
769various sizes, such that (a) every rectangle contains exactly one
770numbered square, and (b) the area of each rectangle is equal to the
771number written in its numbered square.
772
773Credit for this game goes to the Japanese puzzle magazine \i{Nikoli}
774\k{nikoli-rect}; I've also seen a Palm implementation at \i{Puzzle
775Palace} \k{puzzle-palace-rect}. Unlike Puzzle Palace's
776implementation, my version automatically generates random grids of
777any size you like. The quality of puzzle design is therefore not
778quite as good as hand-crafted puzzles would be, but on the plus side
779you get an inexhaustible supply of puzzles tailored to your own
780specification.
781
782\B{nikoli-rect} \W{http://www.nikoli.co.jp/en/puzzles/shikaku.html}\cw{http://www.nikoli.co.jp/en/puzzles/shikaku.html}
783(beware of Flash)
784
785\B{puzzle-palace-rect} \W{https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en}\cw{https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en}
786
787\H{rectangles-controls} \I{controls, for Rectangles}Rectangles controls
788
789This game is played with the mouse or cursor keys.
790
791Left-click any edge to toggle it on or off, or left-click and drag to draw
792an entire rectangle (or line) on the grid in one go (removing any existing
793edges within that rectangle). Right-clicking and dragging will allow you
794to erase the contents of a rectangle without affecting its edges.
795
796Alternatively, use the cursor keys to move the position indicator
797around the board. Pressing the return key then allows you to use the
798cursor keys to drag a rectangle out from that position, and pressing
799the return key again completes the rectangle. Using the space bar
800instead of the return key allows you to erase the contents of a
801rectangle without affecting its edges, as above. Pressing escape
802cancels a drag.
803
804When a rectangle of the correct size is completed, it will be shaded.
805
806(All the actions described in \k{common-actions} are also available.)
807
808\H{rectangles-params} \I{parameters, for Rectangles}Rectangles parameters
809
810These parameters are available from the \q{Custom...} option on the
811\q{Type} menu.
812
813\dt \e{Width}, \e{Height}
814
815\dd Size of grid, in squares.
816
817\dt \e{Expansion factor}
818
819\dd This is a mechanism for changing the type of grids generated by
820the program. Some people prefer a grid containing a few large
821rectangles to one containing many small ones. So you can ask
822Rectangles to essentially generate a \e{smaller} grid than the size
823you specified, and then to expand it by adding rows and columns.
824
825\lcont{
826
827The default expansion factor of zero means that Rectangles will
828simply generate a grid of the size you ask for, and do nothing
829further. If you set an expansion factor of (say) 0.5, it means that
830each dimension of the grid will be expanded to half again as big
831after generation. In other words, the initial grid will be 2/3 the
832size in each dimension, and will be expanded to its full size
833without adding any more rectangles.
834
835Setting an expansion factor of around 0.5 tends to make the game
836more difficult, and also (in my experience) rewards a less deductive
837and more intuitive playing style. If you set it \e{too} high,
838though, the game simply cannot generate more than a few rectangles
839to cover the entire grid, and the game becomes trivial.
840
841}
842
843\dt \e{Ensure unique solution}
844
845\dd Normally, Rectangles will make sure that the puzzles it presents
846have only one solution. Puzzles with ambiguous sections can be more
847difficult and more subtle, so if you like you can turn off this
848feature and risk having ambiguous puzzles. Also, finding \e{all} the
849possible solutions can be an additional challenge for an advanced
850player. Turning off this option can also speed up puzzle generation.
851
852
853\C{netslide} \i{Netslide}
854
855\cfg{winhelp-topic}{games.netslide}
856
857This game combines the grid generation of Net (see \k{net}) with the
858movement of Sixteen (see \k{sixteen}): you have a Net grid, but
859instead of rotating tiles back into place you have to slide them
860into place by moving a whole row at a time.
861
862As in Sixteen, \I{controls, for Netslide}control is with the mouse or
863cursor keys. See \k{sixteen-controls}.
864
865\I{parameters, for Netslide}The available game parameters have similar
866meanings to those in Net (see \k{net-params}) and Sixteen (see
867\k{sixteen-params}).
868
869Netslide was contributed to this collection by Richard Boulton.
870
871
872\C{pattern} \i{Pattern}
873
874\cfg{winhelp-topic}{games.pattern}
875
876You have a grid of squares, which must all be filled in either black
877or white. Beside each row of the grid are listed the lengths of the
878runs of black squares on that row; above each column are listed the
879lengths of the runs of black squares in that column. Your aim is to
880fill in the entire grid black or white.
881
882I first saw this puzzle form around 1995, under the name
883\q{\i{nonograms}}. I've seen it in various places since then, under
884different names.
885
886Normally, puzzles of this type turn out to be a meaningful picture
887of something once you've solved them. However, since this version
888generates the puzzles automatically, they will just look like random
889groupings of squares. (One user has suggested that this is actually
890a \e{good} thing, since it prevents you from guessing the colour of
891squares based on the picture, and forces you to use logic instead.)
892The advantage, though, is that you never run out of them.
893
894\H{pattern-controls} \I{controls, for Pattern}Pattern controls
895
896This game is played with the mouse.
897
898Left-click in a square to colour it black. Right-click to colour it
899white. If you make a mistake, you can middle-click, or hold down
900Shift while clicking with any button, to colour the square in the
901default grey (meaning \q{undecided}) again.
902
903You can click and drag with the left or right mouse button to colour
904a vertical or horizontal line of squares black or white at a time
905(respectively). If you click and drag with the middle button, or
906with Shift held down, you can colour a whole rectangle of squares
907grey.
908
909You can also move around the grid with the cursor keys. Pressing the
910return key will cycle the current cell through empty, then black, then
911white, then empty, and the space bar does the same cycle in reverse.
912
913Moving the cursor while holding Control will colour the moved-over
914squares black. Holding Shift will colour the moved-over squares
915white, and holding both will colour them grey.
916
917(All the actions described in \k{common-actions} are also available.)
918
919\H{pattern-parameters} \I{parameters, for Pattern}Pattern parameters
920
921The only options available from the \q{Custom...} option on the \q{Type}
922menu are \e{Width} and \e{Height}, which are self-explanatory.
923
924
925\C{solo} \i{Solo}
926
927\cfg{winhelp-topic}{games.solo}
928
929You have a square grid, which is divided into as many equally sized
930sub-blocks as the grid has rows. Each square must be filled in with
931a digit from 1 to the size of the grid, in such a way that
932
933\b every row contains only one occurrence of each digit
934
935\b every column contains only one occurrence of each digit
936
937\b every block contains only one occurrence of each digit.
938
939\b (optionally, by default off) each of the square's two main
940diagonals contains only one occurrence of each digit.
941
942You are given some of the numbers as clues; your aim is to place the
943rest of the numbers correctly.
944
945Under the default settings, the sub-blocks are square or
946rectangular. The default puzzle size is 3\by\.3 (a 9\by\.9 actual
947grid, divided into nine 3\by\.3 blocks). You can also select sizes
948with rectangular blocks instead of square ones, such as 2\by\.3 (a
9496\by\.6 grid divided into six 3\by\.2 blocks). Alternatively, you
950can select \q{jigsaw} mode, in which the sub-blocks are arbitrary
951shapes which differ between individual puzzles.
952
953Another available mode is \q{killer}. In this mode, clues are not
954given in the form of filled-in squares; instead, the grid is divided
955into \q{cages} by coloured lines, and for each cage the game tells
956you what the sum of all the digits in that cage should be. Also, no
957digit may appear more than once within a cage, even if the cage
958crosses the boundaries of existing regions.
959
960If you select a puzzle size which requires more than 9 digits, the
961additional digits will be letters of the alphabet. For example, if
962you select 3\by\.4 then the digits which go in your grid will be 1
963to 9, plus \cq{a}, \cq{b} and \cq{c}. This cannot be selected for
964killer puzzles.
965
966I first saw this puzzle in \i{Nikoli} \k{nikoli-solo}, although it's
967also been popularised by various newspapers under the name
968\q{Sudoku} or \q{Su Doku}. Howard Garns is considered the inventor
969of the modern form of the puzzle, and it was first published in
970\e{Dell Pencil Puzzles and Word Games}. A more elaborate treatment
971of the history of the puzzle can be found on Wikipedia
972\k{wikipedia-solo}.
973
974\B{nikoli-solo} \W{http://www.nikoli.co.jp/en/puzzles/sudoku.html}\cw{http://www.nikoli.co.jp/en/puzzles/sudoku.html}
975(beware of Flash)
976
977\B{wikipedia-solo} \W{http://en.wikipedia.org/wiki/Sudoku}\cw{http://en.wikipedia.org/wiki/Sudoku}
978
979\H{solo-controls} \I{controls, for Solo}Solo controls
980
981To play Solo, simply click the mouse in any empty square and then
982type a digit or letter on the keyboard to fill that square. If you
983make a mistake, click the mouse in the incorrect square and press
984Space to clear it again (or use the Undo feature).
985
986If you \e{right}-click in a square and then type a number, that
987number will be entered in the square as a \q{pencil mark}. You can
988have pencil marks for multiple numbers in the same square. Squares
989containing filled-in numbers cannot also contain pencil marks.
990
991The game pays no attention to pencil marks, so exactly what you use
992them for is up to you: you can use them as reminders that a
993particular square needs to be re-examined once you know more about a
994particular number, or you can use them as lists of the possible
995numbers in a given square, or anything else you feel like.
996
997To erase a single pencil mark, right-click in the square and type
998the same number again.
999
1000All pencil marks in a square are erased when you left-click and type
1001a number, or when you left-click and press space. Right-clicking and
1002pressing space will also erase pencil marks.
1003
1004Alternatively, use the cursor keys to move the mark around the grid.
1005Pressing the return key toggles the mark (from a normal mark to a
1006pencil mark), and typing a number in is entered in the square in the
1007appropriate way; typing in a 0 or using the space bar will clear a
1008filled square.
1009
1010(All the actions described in \k{common-actions} are also available.)
1011
1012\H{solo-parameters} \I{parameters, for Solo}Solo parameters
1013
1014Solo allows you to configure two separate dimensions of the puzzle
1015grid on the \q{Type} menu: the number of columns, and the number of
1016rows, into which the main grid is divided. (The size of a block is
1017the inverse of this: for example, if you select 2 columns and 3 rows,
1018each actual block will have 3 columns and 2 rows.)
1019
1020If you tick the \q{X} checkbox, Solo will apply the optional extra
1021constraint that the two main diagonals of the grid also contain one
1022of every digit. (This is sometimes known as \q{Sudoku-X} in
1023newspapers.) In this mode, the squares on the two main diagonals
1024will be shaded slightly so that you know it's enabled.
1025
1026If you tick the \q{Jigsaw} checkbox, Solo will generate randomly
1027shaped sub-blocks. In this mode, the actual grid size will be taken
1028to be the product of the numbers entered in the \q{Columns} and
1029\q{Rows} boxes. There is no reason why you have to enter a number
1030greater than 1 in both boxes; Jigsaw mode has no constraint on the
1031grid size, and it can even be a prime number if you feel like it.
1032
1033If you tick the \q{Killer} checkbox, Solo will generate a set of
1034of cages, which are randomly shaped and drawn in an outline of a
1035different colour. Each of these regions contains a smaller clue
1036which shows the digit sum of all the squares in this region.
1037
1038You can also configure the type of symmetry shown in the generated
1039puzzles. More symmetry makes the puzzles look prettier but may also
1040make them easier, since the symmetry constraints can force more
1041clues than necessary to be present. Completely asymmetric puzzles
1042have the freedom to contain as few clues as possible.
1043
1044Finally, you can configure the difficulty of the generated puzzles.
1045Difficulty levels are judged by the complexity of the techniques of
1046deduction required to solve the puzzle: each level requires a mode
1047of reasoning which was not necessary in the previous one. In
1048particular, on difficulty levels \q{Trivial} and \q{Basic} there
1049will be a square you can fill in with a single number at all times,
1050whereas at \q{Intermediate} level and beyond you will have to make
1051partial deductions about the \e{set} of squares a number could be in
1052(or the set of numbers that could be in a square).
1053\#{Advanced, Extreme?}
1054At \q{Unreasonable} level, even this is not enough, and you will
1055eventually have to make a guess, and then backtrack if it turns out
1056to be wrong.
1057
1058Generating difficult puzzles is itself difficult: if you select one
1059of the higher difficulty levels, Solo may have to make many attempts
1060at generating a puzzle before it finds one hard enough for you. Be
1061prepared to wait, especially if you have also configured a large
1062puzzle size.
1063
1064
1065\C{mines} \i{Mines}
1066
1067\cfg{winhelp-topic}{games.mines}
1068
1069You have a grid of covered squares, some of which contain mines, but
1070you don't know which. Your job is to uncover every square which does
1071\e{not} contain a mine. If you uncover a square containing a mine,
1072you lose. If you uncover a square which does not contain a mine, you
1073are told how many mines are contained within the eight surrounding
1074squares.
1075
1076This game needs no introduction; popularised by Windows, it is
1077perhaps the single best known desktop puzzle game in existence.
1078
1079This version of it has an unusual property. By default, it will
1080generate its mine positions in such a way as to ensure that you
1081never need to \e{guess} where a mine is: you will always be able to
1082deduce it somehow. So you will never, as can happen in other
1083versions, get to the last four squares and discover that there are
1084two mines left but you have no way of knowing for sure where they
1085are.
1086
1087\H{mines-controls} \I{controls, for Mines}Mines controls
1088
1089This game is played with the mouse.
1090
1091If you left-click in a covered square, it will be uncovered.
1092
1093If you right-click in a covered square, it will place a flag which
1094indicates that the square is believed to be a mine. Left-clicking in
1095a marked square will not uncover it, for safety. You can right-click
1096again to remove a mark placed in error.
1097
1098If you left-click in an \e{uncovered} square, it will \q{clear
1099around} the square. This means: if the square has exactly as many
1100flags surrounding it as it should have mines, then all the covered
1101squares next to it which are \e{not} flagged will be uncovered. So
1102once you think you know the location of all the mines around a
1103square, you can use this function as a shortcut to avoid having to
1104click on each of the remaining squares one by one.
1105
1106If you uncover a square which has \e{no} mines in the surrounding
1107eight squares, then it is obviously safe to uncover those squares in
1108turn, and so on if any of them also has no surrounding mines. This
1109will be done for you automatically; so sometimes when you uncover a
1110square, a whole new area will open up to be explored.
1111
1112You can also use the cursor keys to move around the minefield.
1113Pressing the return key in a covered square uncovers it, and in an
1114uncovered square will clear around it (so it acts as the left button),
1115pressing the space bar in a covered square will place a flag
1116(similarly, it acts as the right button).
1117
1118All the actions described in \k{common-actions} are also available.
1119
1120Even Undo is available, although you might consider it cheating to
1121use it. If you step on a mine, the program will only reveal the mine
1122in question (unlike most other implementations, which reveal all of
1123them). You can then Undo your fatal move and continue playing if you
1124like. The program will track the number of times you died (and Undo
1125will not reduce that counter), so when you get to the end of the
1126game you know whether or not you did it without making any errors.
1127
1128(If you really want to know the full layout of the grid, which other
1129implementations will show you after you die, you can always use the
1130Solve menu option.)
1131
1132\H{mines-parameters} \I{parameters, for Mines}Mines parameters
1133
1134The options available from the \q{Custom...} option on the \q{Type}
1135menu are:
1136
1137\dt \e{Width}, \e{Height}
1138
1139\dd Size of grid in squares.
1140
1141\dt \e{Mines}
1142
1143\dd Number of mines in the grid. You can enter this as an absolute
1144mine count, or alternatively you can put a \cw{%} sign on the end in
1145which case the game will arrange for that proportion of the squares
1146in the grid to be mines.
1147
1148\lcont{
1149
1150Beware of setting the mine count too high. At very high densities,
1151the program may spend forever searching for a solvable grid.
1152
1153}
1154
1155\dt \e{Ensure solubility}
1156
1157\dd When this option is enabled (as it is by default), Mines will
1158ensure that the entire grid can be fully deduced starting from the
1159initial open space. If you prefer the riskier grids generated by
1160other implementations, you can switch off this option.
1161
1162
1163\C{samegame} \i{Same Game}
1164
1165\cfg{winhelp-topic}{games.samegame}
1166
1167You have a grid of coloured squares, which you have to clear by
1168highlighting contiguous regions of more than one coloured square;
1169the larger the region you highlight, the more points you get (and
1170the faster you clear the arena).
1171
1172If you clear the grid you win. If you end up with nothing but
1173single squares (i.e., there are no more clickable regions left) you
1174lose.
1175
1176Removing a region causes the rest of the grid to shuffle up:
1177blocks that are suspended will fall down (first), and then empty
1178columns are filled from the right.
1179
1180Same Game was contributed to this collection by James Harvey.
1181
1182\H{samegame-controls} \i{Same Game controls}
1183
1184\IM{Same Game controls} controls, for Same Game
1185\IM{Same Game controls} keys, for Same Game
1186\IM{Same Game controls} shortcuts (keyboard), for Same Game
1187
1188This game can be played with either the keyboard or the mouse.
1189
1190If you left-click an unselected region, it becomes selected (possibly
1191clearing the current selection).
1192
1193If you left-click the selected region, it will be removed (and the
1194rest of the grid shuffled immediately).
1195
1196If you right-click the selected region, it will be unselected.
1197
1198The cursor keys move a cursor around the grid. Pressing the Space or
1199Enter keys while the cursor is in an unselected region selects it;
1200pressing Space or Enter again removes it as above.
1201
1202(All the actions described in \k{common-actions} are also available.)
1203
1204\H{samegame-parameters} \I{parameters, for Same Game}Same Game parameters
1205
1206These parameters are available from the \q{Custom...} option on the
1207\q{Type} menu.
1208
1209\dt \e{Width}, \e{Height}
1210
1211\dd Size of grid in squares.
1212
1213\dt \e{No. of colours}
1214
1215\dd Number of different colours used to fill the grid; the more colours,
1216the fewer large regions of colour and thus the more difficult it is to
1217successfully clear the grid.
1218
1219\dt \e{Scoring system}
1220
1221\dd Controls the precise mechanism used for scoring. With the default
1222system, \q{(n-2)^2}, only regions of three squares or more will score
1223any points at all. With the alternative \q{(n-1)^2} system, regions of
1224two squares score a point each, and larger regions score relatively
1225more points.
1226
1227\dt \e{Ensure solubility}
1228
1229\dd If this option is ticked (the default state), generated grids
1230will be guaranteed to have at least one solution.
1231
1232\lcont{
1233
1234If you turn it off, the game generator will not try to guarantee
1235soluble grids; it will, however, still ensure that there are at
1236least 2 squares of each colour on the grid at the start (since a
1237grid with exactly one square of a given colour is \e{definitely}
1238insoluble). Grids generated with this option disabled may contain
1239more large areas of contiguous colour, leading to opportunities for
1240higher scores; they can also take less time to generate.
1241
1242}
1243
1244
1245\C{flip} \i{Flip}
1246
1247\cfg{winhelp-topic}{games.flip}
1248
1249You have a grid of squares, some light and some dark. Your aim is to
1250light all the squares up at the same time. You can choose any square
1251and flip its state from light to dark or dark to light, but when you
1252do so, other squares around it change state as well.
1253
1254Each square contains a small diagram showing which other squares
1255change when you flip it.
1256
1257\H{flip-controls} \i{Flip controls}
1258
1259\IM{Flip controls} controls, for Flip
1260\IM{Flip controls} keys, for Flip
1261\IM{Flip controls} shortcuts (keyboard), for Flip
1262
1263This game can be played with either the keyboard or the mouse.
1264
1265Left-click in a square to flip it and its associated squares, or
1266use the cursor keys to choose a square and the space bar or Enter
1267key to flip.
1268
1269If you use the \q{Solve} function on this game, it will mark some of
1270the squares in red. If you click once in every square with a red
1271mark, the game should be solved. (If you click in a square
1272\e{without} a red mark, a red mark will appear in it to indicate
1273that you will need to reverse that operation to reach the solution.)
1274
1275(All the actions described in \k{common-actions} are also available.)
1276
1277\H{flip-parameters} \I{parameters, for flip}Flip parameters
1278
1279These parameters are available from the \q{Custom...} option on the
1280\q{Type} menu.
1281
1282\dt \e{Width}, \e{Height}
1283
1284\dd Size of grid in squares.
1285
1286\dt \e{Shape type}
1287
1288\dd This control determines the shape of the region which is flipped
1289by clicking in any given square. The default setting, \q{Crosses},
1290causes every square to flip itself and its four immediate neighbours
1291(or three or two if it's at an edge or corner). The other setting,
1292\q{Random}, causes a random shape to be chosen for every square, so
1293the game is different every time.
1294
1295
1296\C{guess} \i{Guess}
1297
1298\cfg{winhelp-topic}{games.guess}
1299
1300You have a set of coloured pegs, and have to reproduce a
1301predetermined sequence of them (chosen by the computer) within a
1302certain number of guesses.
1303
1304Each guess gets marked with the number of correctly-coloured pegs
1305in the correct places (in black), and also the number of
1306correctly-coloured pegs in the wrong places (in white).
1307
1308This game is also known (and marketed, by Hasbro, mainly) as
1309a board game \q{\i{Mastermind}}, with 6 colours, 4 pegs per row,
1310and 10 guesses. However, this version allows custom settings of number
1311of colours (up to 10), number of pegs per row, and number of guesses.
1312
1313Guess was contributed to this collection by James Harvey.
1314
1315\H{guess-controls} \i{Guess controls}
1316
1317\IM{Guess controls} controls, for Guess
1318\IM{Guess controls} keys, for Guess
1319\IM{Guess controls} shortcuts (keyboard), for Guess
1320
1321This game can be played with either the keyboard or the mouse.
1322
1323With the mouse, drag a coloured peg from the tray on the left-hand
1324side to its required position in the current guess; pegs may also be
1325dragged from current and past guesses to copy them elsewhere. To
1326remove a peg, drag it off its current position to somewhere invalid.
1327
1328Right-clicking in the current guess adds a \q{hold} marker; pegs
1329that have hold markers will be automatically added to the next guess
1330after marking.
1331
1332Alternatively, with the keyboard, the up and down cursor keys can be
1333used to select a peg colour, the left and right keys to select a
1334peg position, and the space bar or Enter key to place a peg of the
1335selected colour in the chosen position. \q{D} or Backspace removes a
1336peg, and Space adds a hold marker.
1337
1338Pressing \q{h} or \q{?} will fill the current guess with a suggested
1339guess. Using this is not recommended for 10 or more pegs as it is
1340slow.
1341
1342When the guess is complete, the smaller feedback pegs will be highlighted;
1343clicking on these (or moving the peg cursor to them with the arrow keys
1344and pressing the space bar or Enter key) will mark the current guess,
1345copy any held pegs to the next guess, and move the \q{current guess}
1346marker.
1347
1348If you correctly position all the pegs the solution will be displayed
1349below; if you run out of guesses (or select \q{Solve...}) the solution
1350will also be revealed.
1351
1352(All the actions described in \k{common-actions} are also available.)
1353
1354\H{guess-parameters} \I{parameters, for Guess}Guess parameters
1355
1356These parameters are available from the \q{Custom...} option on the
1357\q{Type} menu. The default game matches the parameters for the
1358board game \q{Mastermind}.
1359
1360\dt \e{Colours}
1361
1362\dd Number of colours the solution is chosen from; from 2 to 10
1363(more is harder).
1364
1365\dt \e{Pegs per guess}
1366
1367\dd Number of pegs per guess (more is harder).
1368
1369\dt \e{Guesses}
1370
1371\dd Number of guesses you have to find the solution in (fewer is harder).
1372
1373\dt \e{Allow blanks}
1374
1375\dd Allows blank pegs to be given as part of a guess (makes it easier, because
1376you know that those will never be counted as part of the solution). This
1377is turned off by default.
1378
1379\lcont{
1380
1381Note that this doesn't allow blank pegs in the solution; if you really wanted
1382that, use one extra colour.
1383
1384}
1385
1386\dt \e{Allow duplicates}
1387
1388\dd Allows the solution (and the guesses) to contain colours more than once;
1389this increases the search space (making things harder), and is turned on by
1390default.
1391
1392
1393\C{pegs} \i{Pegs}
1394
1395\cfg{winhelp-topic}{games.pegs}
1396
1397A number of pegs are placed in holes on a board. You can remove a
1398peg by jumping an adjacent peg over it (horizontally or vertically)
1399to a vacant hole on the other side. Your aim is to remove all but one
1400of the pegs initially present.
1401
1402This game, best known as \I{Solitaire, Peg}\q{Peg Solitaire}, is
1403possibly one of the oldest puzzle games still commonly known.
1404
1405\H{pegs-controls} \i{Pegs controls}
1406
1407\IM{Pegs controls} controls, for Pegs
1408
1409To move a peg, drag it with the mouse from its current position to
1410its final position. If the final position is exactly two holes away
1411from the initial position, is currently unoccupied by a peg, and
1412there is a peg in the intervening square, the move will be permitted
1413and the intervening peg will be removed.
1414
1415Vacant spaces which you can move a peg into are marked with holes. A
1416space with no peg and no hole is not available for moving at all: it
1417is an obstacle which you must work around.
1418
1419You can also use the cursor keys to move a position indicator around
1420the board. Pressing the return key while over a peg, followed by a
1421cursor key, will jump the peg in that direction (if that is a legal
1422move).
1423
1424(All the actions described in \k{common-actions} are also available.)
1425
1426\H{pegs-parameters} \I{parameters, for Pegs}Pegs parameters
1427
1428These parameters are available from the \q{Custom...} option on the
1429\q{Type} menu.
1430
1431\dt \e{Width}, \e{Height}
1432
1433\dd Size of grid in holes.
1434
1435\dt \e{Board type}
1436
1437\dd Controls whether you are given a board of a standard shape or a
1438randomly generated shape. The two standard shapes currently
1439supported are \q{Cross} and \q{Octagon} (also commonly known as the
1440English and European traditional board layouts respectively).
1441Selecting \q{Random} will give you a different board shape every
1442time (but always one that is known to have a solution).
1443
1444
1445\C{dominosa} \i{Dominosa}
1446
1447\cfg{winhelp-topic}{games.dominosa}
1448
1449A normal set of dominoes \dash that is, one instance of every
1450(unordered) pair of numbers from 0 to 6 \dash has been arranged
1451irregularly into a rectangle; then the number in each square has
1452been written down and the dominoes themselves removed. Your task is
1453to reconstruct the pattern by arranging the set of dominoes to match
1454the provided array of numbers.
1455
1456This puzzle is widely credited to O. S. Adler, and takes part of its
1457name from those initials.
1458
1459\H{dominosa-controls} \i{Dominosa controls}
1460
1461\IM{Dominosa controls} controls, for Dominosa
1462
1463Left-clicking between any two adjacent numbers places a domino
1464covering them, or removes one if it is already present. Trying to
1465place a domino which overlaps existing dominoes will remove the ones
1466it overlaps.
1467
1468Right-clicking between two adjacent numbers draws a line between
1469them, which you can use to remind yourself that you know those two
1470numbers are \e{not} covered by a single domino. Right-clicking again
1471removes the line.
1472
1473You can also use the cursor keys to move a cursor around the grid.
1474When the cursor is half way between two adjacent numbers, pressing
1475the return key will place a domino covering those numbers, or
1476pressing the space bar will lay a line between the two squares.
1477Repeating either action removes the domino or line.
1478
1479Pressing a number key will highlight all occurrences of that
1480number. Pressing that number again will clear the highlighting. Up to two
1481different numbers can be highlighted at any given time.
1482
1483(All the actions described in \k{common-actions} are also available.)
1484
1485\H{dominosa-parameters} \I{parameters, for Dominosa}Dominosa parameters
1486
1487These parameters are available from the \q{Custom...} option on the
1488\q{Type} menu.
1489
1490\dt \e{Maximum number on dominoes}
1491
1492\dd Controls the size of the puzzle, by controlling the size of the
1493set of dominoes used to make it. Dominoes with numbers going up to N
1494will give rise to an (N+2) \by (N+1) rectangle; so, in particular,
1495the default value of 6 gives an 8\by\.7 grid.
1496
1497\dt \e{Ensure unique solution}
1498
1499\dd Normally, Dominosa will make sure that the puzzles it presents
1500have only one solution. Puzzles with ambiguous sections can be more
1501difficult and sometimes more subtle, so if you like you can turn off
1502this feature. Also, finding \e{all} the possible solutions can be an
1503additional challenge for an advanced player. Turning off this option
1504can also speed up puzzle generation.
1505
1506
1507\C{untangle} \i{Untangle}
1508
1509\cfg{winhelp-topic}{games.untangle}
1510
1511You are given a number of points, some of which have lines drawn
1512between them. You can move the points about arbitrarily; your aim is
1513to position the points so that no line crosses another.
1514
1515I originally saw this in the form of a Flash game called \i{Planarity}
1516\k{Planarity}, written by John Tantalo.
1517
1518\B{Planarity} \W{http://planarity.net}\cw{http://planarity.net}
1519
1520\H{untangle-controls} \i{Untangle controls}
1521
1522\IM{Untangle controls} controls, for Untangle
1523
1524To move a point, click on it with the left mouse button and drag it
1525into a new position.
1526
1527(All the actions described in \k{common-actions} are also available.)
1528
1529\H{untangle-parameters} \I{parameters, for Untangle}Untangle parameters
1530
1531There is only one parameter available from the \q{Custom...} option
1532on the \q{Type} menu:
1533
1534\dt \e{Number of points}
1535
1536\dd Controls the size of the puzzle, by specifying the number of
1537points in the generated graph.
1538
1539
1540\C{blackbox} \i{Black Box}
1541
1542\cfg{winhelp-topic}{games.blackbox}
1543
1544A number of balls are hidden in a rectangular arena. You have to
1545deduce the positions of the balls by firing lasers positioned at
1546the edges of the arena and observing how their beams are deflected.
1547
1548Beams will travel straight from their origin until they hit the
1549opposite side of the arena (at which point they emerge), unless
1550affected by balls in one of the following ways:
1551
1552\b A beam that hits a ball head-on is absorbed and will never
1553 re-emerge. This includes beams that meet a ball on the first rank
1554 of the arena.
1555
1556\b A beam with a ball in its front-left square and no ball ahead of it
1557 gets deflected 90 degrees to the right.
1558
1559\b A beam with a ball in its front-right square and no ball ahead of
1560 it gets similarly deflected to the left.
1561
1562\b A beam that would re-emerge from its entry location is considered to be
1563 \q{reflected}.
1564
1565\b A beam which would get deflected before entering the arena by a
1566 ball to the front-left or front-right of its entry point is also
1567 considered to be \q{reflected}.
1568
1569Beams that are reflected appear as a \q{R}; beams that hit balls
1570head-on appear as \q{H}. Otherwise, a number appears at the firing
1571point and the location where the beam emerges (this number is unique
1572to that shot).
1573
1574You can place guesses as to the location of the balls, based on the
1575entry and exit patterns of the beams; once you have placed enough
1576balls a button appears enabling you to have your guesses checked.
1577
1578Here is a diagram showing how the positions of balls can create each
1579of the beam behaviours shown above:
1580
1581\c 1RHR----
1582\c |..O.O...|
1583\c 2........3
1584\c |........|
1585\c |........|
1586\c 3........|
1587\c |......O.|
1588\c H........|
1589\c |.....O..|
1590\c 12-RR---
1591
1592As shown, it is possible for a beam to receive multiple reflections
1593before re-emerging (see turn 3). Similarly, a beam may be reflected
1594(possibly more than once) before receiving a hit (the \q{H} on the
1595left side of the example).
1596
1597Note that any layout with more than 4 balls may have a non-unique
1598solution. The following diagram illustrates this; if you know the
1599board contains 5 balls, it is impossible to determine where the fifth
1600ball is (possible positions marked with an \cw{x}):
1601
1602\c --------
1603\c |........|
1604\c |........|
1605\c |..O..O..|
1606\c |...xx...|
1607\c |...xx...|
1608\c |..O..O..|
1609\c |........|
1610\c |........|
1611\c --------
1612
1613For this reason, when you have your guesses checked, the game will
1614check that your solution \e{produces the same results} as the
1615computer's, rather than that your solution is identical to the
1616computer's. So in the above example, you could put the fifth ball at
1617\e{any} of the locations marked with an \cw{x}, and you would still
1618win.
1619
1620Black Box was contributed to this collection by James Harvey.
1621
1622\H{blackbox-controls} \i{Black Box controls}
1623
1624\IM{Black Box controls} controls, for Black Box
1625\IM{Black Box controls} keys, for Black Box
1626\IM{Black Box controls} shortcuts (keyboard), for Black Box
1627
1628To fire a laser beam, left-click in a square around the edge of the
1629arena. The results will be displayed immediately. Clicking or holding
1630the left button on one of these squares will highlight the current go
1631(or a previous go) to confirm the exit point for that laser, if
1632applicable.
1633
1634To guess the location of a ball, left-click within the arena and a
1635black circle will appear marking the guess; click again to remove the
1636guessed ball.
1637
1638Locations in the arena may be locked against modification by
1639right-clicking; whole rows and columns may be similarly locked by
1640right-clicking in the laser square above/below that column, or to the
1641left/right of that row.
1642
1643The cursor keys may also be used to move around the grid. Pressing the
1644Enter key will fire a laser or add a new ball-location guess, and
1645pressing Space will lock a cell, row, or column.
1646
1647When an appropriate number of balls have been guessed, a button will
1648appear at the top-left corner of the grid; clicking that (with mouse
1649or cursor) will check your guesses.
1650
1651If you click the \q{check} button and your guesses are not correct,
1652the game will show you the minimum information necessary to
1653demonstrate this to you, so you can try again. If your ball
1654positions are not consistent with the beam paths you already know
1655about, one beam path will be circled to indicate that it proves you
1656wrong. If your positions match all the existing beam paths but are
1657still wrong, one new beam path will be revealed (written in red)
1658which is not consistent with your current guesses.
1659
1660If you decide to give up completely, you can select Solve to reveal
1661the actual ball positions. At this point, correctly-placed balls
1662will be displayed as filled black circles, incorrectly-placed balls
1663as filled black circles with red crosses, and missing balls as filled
1664red circles. In addition, a red circle marks any laser you had already
1665fired which is not consistent with your ball layout (just as when you
1666press the \q{check} button), and red text marks any laser you
1667\e{could} have fired in order to distinguish your ball layout from the
1668correct one.
1669
1670(All the actions described in \k{common-actions} are also available.)
1671
1672\H{blackbox-parameters} \I{parameters, for Black Box}Black Box parameters
1673
1674These parameters are available from the \q{Custom...} option on the
1675\q{Type} menu.
1676
1677\dt \e{Width}, \e{Height}
1678
1679\dd Size of grid in squares. There are 2 \by \e{Width} \by \e{Height} lasers
1680per grid, two per row and two per column.
1681
1682\dt \e{No. of balls}
1683
1684\dd Number of balls to place in the grid. This can be a single number,
1685or a range (separated with a hyphen, like \q{2-6}), and determines the
1686number of balls to place on the grid. The \q{reveal} button is only
1687enabled if you have guessed an appropriate number of balls; a guess
1688using a different number to the original solution is still acceptable,
1689if all the beam inputs and outputs match.
1690
1691
1692\C{slant} \i{Slant}
1693
1694\cfg{winhelp-topic}{games.slant}
1695
1696You have a grid of squares. Your aim is to draw a diagonal line
1697through each square, and choose which way each line slants so that
1698the following conditions are met:
1699
1700\b The diagonal lines never form a loop.
1701
1702\b Any point with a circled number has precisely that many lines
1703meeting at it. (Thus, a 4 is the centre of a cross shape, whereas a
1704zero is the centre of a diamond shape \dash or rather, a partial
1705diamond shape, because a zero can never appear in the middle of the
1706grid because that would immediately cause a loop.)
1707
1708Credit for this puzzle goes to \i{Nikoli} \k{nikoli-slant}.
1709
1710\B{nikoli-slant}
1711\W{http://www.nikoli.co.jp/ja/puzzles/gokigen_naname}\cw{http://www.nikoli.co.jp/ja/puzzles/gokigen_naname}
1712(in Japanese)
1713
1714\H{slant-controls} \i{Slant controls}
1715
1716\IM{Slant controls} controls, for Slant
1717
1718Left-clicking in a blank square will place a \cw{\\} in it (a line
1719leaning to the left, i.e. running from the top left of the square to
1720the bottom right). Right-clicking in a blank square will place a
1721\cw{/} in it (leaning to the right, running from top right to bottom
1722left).
1723
1724Continuing to click either button will cycle between the three
1725possible square contents. Thus, if you left-click repeatedly in a
1726blank square it will change from blank to \cw{\\} to \cw{/} back to
1727blank, and if you right-click repeatedly the square will change from
1728blank to \cw{/} to \cw{\\} back to blank. (Therefore, you can play
1729the game entirely with one button if you need to.)
1730
1731You can also use the cursor keys to move around the grid. Pressing the
1732return or space keys will place a \cw{\\} or a \cw{/}, respectively,
1733and will then cycle them as above. You can also press \cw{/} or
1734\cw{\\} to place a \cw{/} or \cw{\\}, respectively, independent of
1735what is already in the cursor square. Backspace removes any line from
1736the cursor square.
1737
1738(All the actions described in \k{common-actions} are also available.)
1739
1740\H{slant-parameters} \I{parameters, for Slant}Slant parameters
1741
1742These parameters are available from the \q{Custom...} option on the
1743\q{Type} menu.
1744
1745\dt \e{Width}, \e{Height}
1746
1747\dd Size of grid in squares.
1748
1749\dt \e{Difficulty}
1750
1751\dd Controls the difficulty of the generated puzzle. At Hard level,
1752you are required to do deductions based on knowledge of
1753\e{relationships} between squares rather than always being able to
1754deduce the exact contents of one square at a time. (For example, you
1755might know that two squares slant in the same direction, even if you
1756don't yet know what that direction is, and this might enable you to
1757deduce something about still other squares.) Even at Hard level,
1758guesswork and backtracking should never be necessary.
1759
1760
1761\C{lightup} \i{Light Up}
1762
1763\cfg{winhelp-topic}{games.lightup}
1764
1765You have a grid of squares. Some are filled in black; some of the
1766black squares are numbered. Your aim is to \q{light up} all the
1767empty squares by placing light bulbs in some of them.
1768
1769Each light bulb illuminates the square it is on, plus all squares in
1770line with it horizontally or vertically unless a black square is
1771blocking the way.
1772
1773To win the game, you must satisfy the following conditions:
1774
1775\b All non-black squares are lit.
1776
1777\b No light is lit by another light.
1778
1779\b All numbered black squares have exactly that number of lights adjacent to
1780 them (in the four squares above, below, and to the side).
1781
1782Non-numbered black squares may have any number of lights adjacent to them.
1783
1784Credit for this puzzle goes to \i{Nikoli} \k{nikoli-lightup}.
1785
1786Light Up was contributed to this collection by James Harvey.
1787
1788\B{nikoli-lightup}
1789\W{http://www.nikoli.co.jp/en/puzzles/akari.html}\cw{http://www.nikoli.co.jp/en/puzzles/akari.html}
1790(beware of Flash)
1791
1792\H{lightup-controls} \i{Light Up controls}
1793
1794\IM{Light Up controls} controls, for Light Up
1795
1796Left-clicking in a non-black square will toggle the presence of a light
1797in that square. Right-clicking in a non-black square toggles a mark there to aid
1798solving; it can be used to highlight squares that cannot be lit, for example.
1799
1800You may not place a light in a marked square, nor place a mark in a lit square.
1801
1802The game will highlight obvious errors in red. Lights lit by other
1803lights are highlighted in this way, as are numbered squares which
1804do not (or cannot) have the right number of lights next to them.
1805
1806Thus, the grid is solved when all non-black squares have yellow
1807highlights and there are no red lights.
1808
1809(All the actions described in \k{common-actions} are also available.)
1810
1811\H{lightup-parameters} \I{parameters, for Light Up}Light Up parameters
1812
1813These parameters are available from the \q{Custom...} option on the
1814\q{Type} menu.
1815
1816\dt \e{Width}, \e{Height}
1817
1818\dd Size of grid in squares.
1819
1820\dt \e{%age of black squares}
1821
1822\dd Rough percentage of black squares in the grid.
1823
1824\lcont{
1825
1826This is a hint rather than an instruction. If the grid generator is
1827unable to generate a puzzle to this precise specification, it will
1828increase the proportion of black squares until it can.
1829
1830}
1831
1832\dt \e{Symmetry}
1833
1834\dd Allows you to specify the required symmetry of the black squares
1835in the grid. (This does not affect the difficulty of the puzzles
1836noticeably.)
1837
1838\dt \e{Difficulty}
1839
1840\dd \q{Easy} means that the puzzles should be soluble without
1841backtracking or guessing, \q{Hard} means that some guesses will
1842probably be necessary.
1843
1844
1845\C{map} \i{Map}
1846
1847\cfg{winhelp-topic}{games.map}
1848
1849You are given a map consisting of a number of regions. Your task is
1850to colour each region with one of four colours, in such a way that
1851no two regions sharing a boundary have the same colour. You are
1852provided with some regions already coloured, sufficient to make the
1853remainder of the solution unique.
1854
1855Only regions which share a length of border are required to be
1856different colours. Two regions which meet at only one \e{point}
1857(i.e. are diagonally separated) may be the same colour.
1858
1859I believe this puzzle is original; I've never seen an implementation
1860of it anywhere else. The concept of a \i{four-colouring} puzzle was
1861suggested by Owen Dunn; credit must also go to Nikoli and to Verity
1862Allan for inspiring the train of thought that led to me realising
1863Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor
1864for many detailed suggestions.
1865
1866\H{map-controls} \i{Map controls}
1867
1868\IM{Map controls} controls, for Map
1869
1870To colour a region, click the left mouse button on an existing
1871region of the desired colour and drag that colour into the new
1872region.
1873
1874(The program will always ensure the starting puzzle has at least one
1875region of each colour, so that this is always possible!)
1876
1877If you need to clear a region, you can drag from an empty region, or
1878from the puzzle boundary if there are no empty regions left.
1879
1880Dragging a colour using the \e{right} mouse button will stipple the
1881region in that colour, which you can use as a note to yourself that
1882you think the region \e{might} be that colour. A region can contain
1883stipples in multiple colours at once. (This is often useful at the
1884harder difficulty levels.)
1885
1886You can also use the cursor keys to move around the map: the colour of
1887the cursor indicates the position of the colour you would drag (which
1888is not obvious if you're on a region's boundary, since it depends on the
1889direction from which you approached the boundary). Pressing the return
1890key starts a drag of that colour, as above, which you control with the
1891cursor keys; pressing the return key again finishes the drag. The
1892space bar can be used similarly to create a stippled region.
1893Double-pressing the return key (without moving the cursor) will clear
1894the region, as a drag from an empty region does: this is useful with
1895the cursor mode if you have filled the entire map in but need to
1896correct the layout.
1897
1898If you press L during play, the game will toggle display of a number
1899in each region of the map. This is useful if you want to discuss a
1900particular puzzle instance with a friend \dash having an unambiguous
1901name for each region is much easier than trying to refer to them all
1902by names such as \q{the one down and right of the brown one on the
1903top border}.
1904
1905(All the actions described in \k{common-actions} are also available.)
1906
1907\H{map-parameters} \I{parameters, for Map}Map parameters
1908
1909These parameters are available from the \q{Custom...} option on the
1910\q{Type} menu.
1911
1912\dt \e{Width}, \e{Height}
1913
1914\dd Size of grid in squares.
1915
1916\dt \e{Regions}
1917
1918\dd Number of regions in the generated map.
1919
1920\dt \e{Difficulty}
1921
1922\dd In \q{Easy} mode, there should always be at least one region
1923whose colour can be determined trivially. In \q{Normal} and \q{Hard}
1924modes, you will have to use increasingly complex logic to deduce the
1925colour of some regions. However, it will always be possible without
1926having to guess or backtrack.
1927
1928\lcont{
1929
1930In \q{Unreasonable} mode, the program will feel free to generate
1931puzzles which are as hard as it can possibly make them: the only
1932constraint is that they should still have a unique solution. Solving
1933Unreasonable puzzles may require guessing and backtracking.
1934
1935}
1936
1937
1938\C{loopy} \i{Loopy}
1939
1940\cfg{winhelp-topic}{games.loopy}
1941
1942You are given a grid of dots, marked with yellow lines to indicate
1943which dots you are allowed to connect directly together. Your aim is
1944to use some subset of those yellow lines to draw a single unbroken
1945loop from dot to dot within the grid.
1946
1947Some of the spaces between the lines contain numbers. These numbers
1948indicate how many of the lines around that space form part of the
1949loop. The loop you draw must correctly satisfy all of these clues to
1950be considered a correct solution.
1951
1952In the default mode, the dots are arranged in a grid of squares;
1953however, you can also play on triangular or hexagonal grids, or even
1954more exotic ones.
1955
1956Credit for the basic puzzle idea goes to \i{Nikoli}
1957\k{nikoli-loopy}.
1958
1959Loopy was originally contributed to this collection by Mike Pinna,
1960and subsequently enhanced to handle various types of non-square grid
1961by Lambros Lambrou.
1962
1963\B{nikoli-loopy}
1964\W{http://www.nikoli.co.jp/en/puzzles/slitherlink.html}\cw{http://www.nikoli.co.jp/en/puzzles/slitherlink.html}
1965(beware of Flash)
1966
1967\H{loopy-controls} \i{Loopy controls}
1968
1969\IM{Loopy controls} controls, for Loopy
1970
1971Click the left mouse button on a yellow line to turn it black,
1972indicating that you think it is part of the loop. Click again to
1973turn the line yellow again (meaning you aren't sure yet).
1974
1975If you are sure that a particular line segment is \e{not} part of
1976the loop, you can click the right mouse button to remove it
1977completely. Again, clicking a second time will turn the line back to
1978yellow.
1979
1980(All the actions described in \k{common-actions} are also available.)
1981
1982\H{loopy-parameters} \I{parameters, for Loopy}Loopy parameters
1983
1984These parameters are available from the \q{Custom...} option on the
1985\q{Type} menu.
1986
1987\dt \e{Width}, \e{Height}
1988
1989\dd Size of grid, measured in number of regions across and down. For
1990square grids, it's clear how this is counted; for other types of
1991grid you may have to think a bit to see how the dimensions are
1992measured.
1993
1994\dt \e{Grid type}
1995
1996\dd Allows you to choose between a selection of types of tiling.
1997Some have all the faces the same but may have multiple different
1998types of vertex (e.g. the \e{Cairo} or \e{Kites} mode); others have
1999all the vertices the same but may have different types of face (e.g.
2000the \e{Great Hexagonal}). The square, triangular and honeycomb grids
2001are fully regular, and have all their vertices \e{and} faces the
2002same; this makes them the least confusing to play.
2003
2004\dt \e{Difficulty}
2005
2006\dd Controls the difficulty of the generated puzzle.
2007\#{FIXME: what distinguishes Easy, Medium, and Hard? In particular,
2008when are backtracking/guesswork required, if ever?}
2009
2010
2011\C{inertia} \i{Inertia}
2012
2013\cfg{winhelp-topic}{games.inertia}
2014
2015You are a small green ball sitting in a grid full of obstacles. Your
2016aim is to collect all the gems without running into any mines.
2017
2018You can move the ball in any orthogonal \e{or diagonal} direction.
2019Once the ball starts moving, it will continue until something stops
2020it. A wall directly in its path will stop it (but if it is moving
2021diagonally, it will move through a diagonal gap between two other
2022walls without stopping). Also, some of the squares are \q{stops};
2023when the ball moves on to a stop, it will stop moving no matter what
2024direction it was going in. Gems do \e{not} stop the ball; it picks
2025them up and keeps on going.
2026
2027Running into a mine is fatal. Even if you picked up the last gem in
2028the same move which then hit a mine, the game will count you as dead
2029rather than victorious.
2030
2031This game was originally implemented for Windows by Ben Olmstead
2032\k{bem}, who was kind enough to release his source code on request
2033so that it could be re-implemented for this collection.
2034
2035\B{bem} \W{http://xn13.com/}\cw{http://xn13.com/}
2036
2037\H{inertia-controls} \i{Inertia controls}
2038
2039\IM{Inertia controls} controls, for Inertia
2040\IM{Inertia controls} keys, for Inertia
2041\IM{Inertia controls} shortcuts (keyboard), for Inertia
2042
2043You can move the ball in any of the eight directions using the
2044numeric keypad. Alternatively, if you click the left mouse button on
2045the grid, the ball will begin a move in the general direction of
2046where you clicked.
2047
2048If you use the \q{Solve} function on this game, the program will
2049compute a path through the grid which collects all the remaining
2050gems and returns to the current position. A hint arrow will appear
2051on the ball indicating the direction in which you should move to
2052begin on this path. If you then move in that direction, the arrow
2053will update to indicate the next direction on the path. You can also
2054press Space to automatically move in the direction of the hint
2055arrow. If you move in a different direction from the one shown by
2056the arrow, arrows will be shown only if the puzzle is still solvable.
2057
2058All the actions described in \k{common-actions} are also available.
2059In particular, if you do run into a mine and die, you can use the
2060Undo function and resume playing from before the fatal move. The
2061game will keep track of the number of times you have done this.
2062
2063\H{inertia-parameters} \I{parameters, for Inertia}Inertia parameters
2064
2065These parameters are available from the \q{Custom...} option on the
2066\q{Type} menu.
2067
2068\dt \e{Width}, \e{Height}
2069
2070\dd Size of grid in squares.
2071
2072
2073\C{tents} \i{Tents}
2074
2075\cfg{winhelp-topic}{games.tents}
2076
2077You have a grid of squares, some of which contain trees. Your aim is
2078to place tents in some of the remaining squares, in such a way that
2079the following conditions are met:
2080
2081\b There are exactly as many tents as trees.
2082
2083\b The tents and trees can be matched up in such a way that each
2084tent is directly adjacent (horizontally or vertically, but not
2085diagonally) to its own tree. However, a tent may be adjacent to
2086other trees as well as its own.
2087
2088\b No two tents are adjacent horizontally, vertically \e{or
2089diagonally}.
2090
2091\b The number of tents in each row, and in each column, matches the
2092numbers given round the sides of the grid.
2093
2094This puzzle can be found in several places on the Internet, and was
2095brought to my attention by e-mail. I don't know who I should credit
2096for inventing it.
2097
2098\H{tents-controls} \i{Tents controls}
2099
2100\IM{Tents controls} controls, for Tents
2101
2102Left-clicking in a blank square will place a tent in it.
2103Right-clicking in a blank square will colour it green, indicating
2104that you are sure it \e{isn't} a tent. Clicking either button in an
2105occupied square will clear it.
2106
2107If you \e{drag} with the right button along a row or column, every
2108blank square in the region you cover will be turned green, and no
2109other squares will be affected. (This is useful for clearing the
2110remainder of a row once you have placed all its tents.)
2111
2112You can also use the cursor keys to move around the grid. Pressing the
2113return key over an empty square will place a tent, and pressing the
2114space bar over an empty square will colour it green; either key will
2115clear an occupied square. Holding Shift and pressing the cursor keys
2116will colour empty squares green. Holding Control and pressing the
2117cursor keys will colour green both empty squares and squares with tents.
2118
2119(All the actions described in \k{common-actions} are also available.)
2120
2121\H{tents-parameters} \I{parameters, for Tents}Tents parameters
2122
2123These parameters are available from the \q{Custom...} option on the
2124\q{Type} menu.
2125
2126\dt \e{Width}, \e{Height}
2127
2128\dd Size of grid in squares.
2129
2130\dt \e{Difficulty}
2131
2132\dd Controls the difficulty of the generated puzzle. More difficult
2133puzzles require more complex deductions, but at present none of the
2134available difficulty levels requires guesswork or backtracking.
2135
2136
2137\C{bridges} \i{Bridges}
2138
2139\cfg{winhelp-topic}{games.bridges}
2140
2141You have a set of islands distributed across the playing area. Each
2142island contains a number. Your aim is to connect the islands
2143together with bridges, in such a way that:
2144
2145\b Bridges run horizontally or vertically.
2146
2147\b The number of bridges terminating at any island is equal to the
2148number written in that island.
2149
2150\b Two bridges may run in parallel between the same two islands, but
2151no more than two may do so.
2152
2153\b No bridge crosses another bridge.
2154
2155\b All the islands are connected together.
2156
2157There are some configurable alternative modes, which involve
2158changing the parallel-bridge limit to something other than 2, and
2159introducing the additional constraint that no sequence of bridges
2160may form a loop from one island back to the same island. The rules
2161stated above are the default ones.
2162
2163Credit for this puzzle goes to \i{Nikoli} \k{nikoli-bridges}.
2164
2165Bridges was contributed to this collection by James Harvey.
2166
2167\B{nikoli-bridges}
2168\W{http://www.nikoli.co.jp/en/puzzles/hashiwokakero.html}\cw{http://www.nikoli.co.jp/en/puzzles/hashiwokakero.html}
2169(beware of Flash)
2170
2171\H{bridges-controls} \i{Bridges controls}
2172
2173\IM{Bridges controls} controls, for Bridges
2174
2175To place a bridge between two islands, click the mouse down on one
2176island and drag it towards the other. You do not need to drag all
2177the way to the other island; you only need to move the mouse far
2178enough for the intended bridge direction to be unambiguous. (So you
2179can keep the mouse near the starting island and conveniently throw
2180bridges out from it in many directions.)
2181
2182Doing this again when a bridge is already present will add another
2183parallel bridge. If there are already as many bridges between the
2184two islands as permitted by the current game rules (i.e. two by
2185default), the same dragging action will remove all of them.
2186
2187If you want to remind yourself that two islands definitely \e{do
2188not} have a bridge between them, you can right-drag between them in
2189the same way to draw a \q{non-bridge} marker.
2190
2191If you think you have finished with an island (i.e. you have placed
2192all its bridges and are confident that they are in the right
2193places), you can mark the island as finished by left-clicking on it.
2194This will highlight it and all the bridges connected to it, and you
2195will be prevented from accidentally modifying any of those bridges
2196in future. Left-clicking again on a highlighted island will unmark
2197it and restore your ability to modify it.
2198
2199You can also use the cursor keys to move around the grid: if possible
2200the cursor will always move orthogonally, otherwise it will move
2201towards the nearest island to the indicated direction. Holding Control
2202and pressing a cursor key will lay a bridge in that direction (if
2203available); Shift and a cursor key will lay a \q{non-bridge} marker.
2204Pressing the return key followed by a cursor key will also lay a
2205bridge in that direction.
2206
2207You can mark an island as finished by pressing the space bar or by
2208pressing the return key twice.
2209
2210By pressing a number key, you can jump to the nearest island with that
2211number. Letters \q{a}, ..., \q{f} count as 10, ..., 15 and \q{0} as
221216.
2213
2214Violations of the puzzle rules will be marked in red:
2215
2216\b An island with too many bridges will be highlighted in red.
2217
2218\b An island with too few bridges will be highlighted in red if it
2219is definitely an error (as opposed to merely not being finished
2220yet): if adding enough bridges would involve having to cross another
2221bridge or remove a non-bridge marker, or if the island has been
2222highlighted as complete.
2223
2224\b A group of islands and bridges may be highlighted in red if it is
2225a closed subset of the puzzle with no way to connect it to the rest
2226of the islands. For example, if you directly connect two 1s together
2227with a bridge and they are not the only two islands on the grid,
2228they will light up red to indicate that such a group cannot be
2229contained in any valid solution.
2230
2231\b If you have selected the (non-default) option to disallow loops
2232in the solution, a group of bridges which forms a loop will be
2233highlighted.
2234
2235(All the actions described in \k{common-actions} are also available.)
2236
2237\H{bridges-parameters} \I{parameters, for Bridges}Bridges parameters
2238
2239These parameters are available from the \q{Custom...} option on the
2240\q{Type} menu.
2241
2242\dt \e{Width}, \e{Height}
2243
2244\dd Size of grid in squares.
2245
2246\dt \e{Difficulty}
2247
2248\dd Difficulty level of puzzle.
2249
2250\dt \e{Allow loops}
2251
2252\dd This is set by default. If cleared, puzzles will be generated in
2253such a way that they are always soluble without creating a loop, and
2254solutions which do involve a loop will be disallowed.
2255
2256\dt \e{Max. bridges per direction}
2257
2258\dd Maximum number of bridges in any particular direction. The
2259default is 2, but you can change it to 1, 3 or 4. In general, fewer
2260is easier.
2261
2262\dt \e{%age of island squares}
2263
2264\dd Gives a rough percentage of islands the generator will try and
2265lay before finishing the puzzle. Certain layouts will not manage to
2266lay enough islands; this is an upper bound.
2267
2268\dt \e{Expansion factor (%age)}
2269
2270\dd The grid generator works by picking an existing island at random
2271(after first creating an initial island somewhere). It then decides
2272on a direction (at random), and then works out how far it could
2273extend before creating another island. This parameter determines how
2274likely it is to extend as far as it can, rather than choosing
2275somewhere closer.
2276
2277\lcont{
2278
2279High expansion factors usually mean easier puzzles with fewer
2280possible islands; low expansion factors can create lots of
2281tightly-packed islands.
2282
2283}
2284
2285
2286\C{unequal} \i{Unequal}
2287
2288\cfg{winhelp-topic}{games.unequal}
2289
2290You have a square grid; each square may contain a digit from 1 to
2291the size of the grid, and some squares have clue signs between
2292them. Your aim is to fully populate the grid with numbers such that:
2293
2294\b Each row contains only one occurrence of each digit
2295
2296\b Each column contains only one occurrence of each digit
2297
2298\b All the clue signs are satisfied.
2299
2300There are two modes for this game, \q{Unequal} and \q{Adjacent}.
2301
2302In \q{Unequal} mode, the clue signs are greater-than symbols indicating one
2303square's value is greater than its neighbour's. In this mode not all clues
2304may be visible, particularly at higher difficulty levels.
2305
2306In \q{Adjacent} mode, the clue signs are bars indicating
2307one square's value is numerically adjacent (i.e. one higher or one lower)
2308than its neighbour. In this mode all clues are always visible: absence of
2309a bar thus means that a square's value is definitely not numerically adjacent
2310to that neighbour's.
2311
2312In \q{Trivial} difficulty level (available via the \q{Custom} game type
2313selector), there are no greater-than signs in \q{Unequal} mode; the puzzle is
2314to solve the \i{Latin square} only.
2315
2316At the time of writing, the \q{Unequal} mode of this puzzle is appearing in the
2317Guardian weekly under the name \q{\i{Futoshiki}}.
2318
2319Unequal was contributed to this collection by James Harvey.
2320
2321\H{unequal-controls} \i{Unequal controls}
2322
2323\IM{Unequal controls} controls, for Unequal
2324
2325Unequal shares much of its control system with Solo.
2326
2327To play Unequal, simply click the mouse in any empty square and then
2328type a digit or letter on the keyboard to fill that square. If you
2329make a mistake, click the mouse in the incorrect square and press
2330Space to clear it again (or use the Undo feature).
2331
2332If you \e{right}-click in a square and then type a number, that
2333number will be entered in the square as a \q{pencil mark}. You can
2334have pencil marks for multiple numbers in the same square. Squares
2335containing filled-in numbers cannot also contain pencil marks.
2336
2337The game pays no attention to pencil marks, so exactly what you use
2338them for is up to you: you can use them as reminders that a
2339particular square needs to be re-examined once you know more about a
2340particular number, or you can use them as lists of the possible
2341numbers in a given square, or anything else you feel like.
2342
2343To erase a single pencil mark, right-click in the square and type
2344the same number again.
2345
2346All pencil marks in a square are erased when you left-click and type
2347a number, or when you left-click and press space. Right-clicking and
2348pressing space will also erase pencil marks.
2349
2350As for Solo, the cursor keys can be used in conjunction with the digit
2351keys to set numbers or pencil marks. You can also use the \q{M} key to
2352auto-fill every numeric hint, ready for removal as required, or the \q{H}
2353key to do the same but also to remove all obvious hints.
2354
2355Alternatively, use the cursor keys to move the mark around the grid.
2356Pressing the return key toggles the mark (from a normal mark to a
2357pencil mark), and typing a number in is entered in the square in the
2358appropriate way; typing in a 0 or using the space bar will clear a
2359filled square.
2360
2361Left-clicking a clue will mark it as done (grey it out), or unmark it
2362if it is already marked. Holding Control or Shift and pressing an
2363arrow key likewise marks any clue adjacent to the cursor in the given
2364direction.
2365
2366(All the actions described in \k{common-actions} are also available.)
2367
2368\H{unequal-parameters} \I{parameters, for Unequal}Unequal parameters
2369
2370These parameters are available from the \q{Custom...} option on the
2371\q{Type} menu.
2372
2373\dt \e{Mode}
2374
2375\dd Mode of the puzzle (\q{Unequal} or \q{Adjacent})
2376
2377\dt \e{Size (s*s)}
2378
2379\dd Size of grid.
2380
2381\dt \e{Difficulty}
2382
2383\dd Controls the difficulty of the generated puzzle. At Trivial
2384level, there are no greater-than signs; the puzzle is to solve the
2385Latin square only. At Recursive level (only available via the
2386\q{Custom} game type selector) backtracking will be required, but
2387the solution should still be unique. The levels in between require
2388increasingly complex reasoning to avoid having to backtrack.
2389
2390
2391
2392\C{galaxies} \i{Galaxies}
2393
2394\cfg{winhelp-topic}{games.galaxies}
2395
2396You have a rectangular grid containing a number of dots. Your aim is
2397to draw edges along the grid lines which divide the rectangle into
2398regions in such a way that every region is 180\u00b0{-degree}
2399rotationally symmetric, and contains exactly one dot which is
2400located at its centre of symmetry.
2401
2402This puzzle was invented by \i{Nikoli} \k{nikoli-galaxies}, under
2403the name \q{Tentai Show}; its name is commonly translated into
2404English as \q{Spiral Galaxies}.
2405
2406Galaxies was contributed to this collection by James Harvey.
2407
2408\B{nikoli-galaxies} \W{http://www.nikoli.co.jp/en/puzzles/astronomical_show.html}\cw{http://www.nikoli.co.jp/en/puzzles/astronomical_show.html}
2409
2410\H{galaxies-controls} \i{Galaxies controls}
2411
2412\IM{Galaxies controls} controls, for Galaxies
2413
2414Left-click on any grid line to draw an edge if there isn't one
2415already, or to remove one if there is. When you create a valid
2416region (one which is closed, contains exactly one dot, is
2417180\u00b0{-degree} symmetric about that dot, and contains no
2418extraneous edges inside it) it will be highlighted automatically; so
2419your aim is to have the whole grid highlighted in that way.
2420
2421During solving, you might know that a particular grid square belongs
2422to a specific dot, but not be sure of where the edges go and which
2423other squares are connected to the dot. In order to mark this so you
2424don't forget, you can right-click on the dot and drag, which will
2425create an arrow marker pointing at the dot. Drop that in a square of
2426your choice and it will remind you which dot it's associated with.
2427You can also right-click on existing arrows to pick them up and move
2428them, or destroy them by dropping them off the edge of the grid.
2429(Also, if you're not sure which dot an arrow is pointing at, you can
2430pick it up and move it around to make it clearer. It will swivel
2431constantly as you drag it, to stay pointed at its parent dot.)
2432
2433You can also use the cursor keys to move around the grid squares and
2434lines. Pressing the return key when over a grid line will draw or
2435clear its edge, as above. Pressing the return key when over a dot will
2436pick up an arrow, to be dropped the next time the return key is
2437pressed; this can also be used to move existing arrows around, removing
2438them by dropping them on a dot or another arrow.
2439
2440(All the actions described in \k{common-actions} are also available.)
2441
2442\H{galaxies-parameters} \I{parameters, for Galaxies}Galaxies parameters
2443
2444These parameters are available from the \q{Custom...} option on the
2445\q{Type} menu.
2446
2447\dt \e{Width}, \e{Height}
2448
2449\dd Size of grid in squares.
2450
2451\dt \e{Difficulty}
2452
2453\dd Controls the difficulty of the generated puzzle. More difficult
2454puzzles require more complex deductions, and the \q{Unreasonable}
2455difficulty level may require backtracking.
2456
2457
2458
2459\C{filling} \i{Filling}
2460
2461\cfg{winhelp-topic}{games.filling}
2462
2463You have a grid of squares, some of which contain digits, and the
2464rest of which are empty. Your job is to fill in digits in the empty
2465squares, in such a way that each connected region of squares all
2466containing the same digit has an area equal to that digit.
2467
2468(\q{Connected region}, for the purposes of this game, does not count
2469diagonally separated squares as adjacent.)
2470
2471For example, it follows that no square can contain a zero, and that
2472two adjacent squares can not both contain a one. No region has an
2473area greater than 9 (because then its area would not be a single
2474digit).
2475
2476Credit for this puzzle goes to \i{Nikoli} \k{nikoli-fillomino}.
2477
2478Filling was contributed to this collection by Jonas K\u00F6{oe}lker.
2479
2480\B{nikoli-fillomino}
2481\W{http://www.nikoli.co.jp/en/puzzles/fillomino.html}\cw{http://www.nikoli.co.jp/en/puzzles/fillomino.html}
2482
2483\H{filling-controls} \I{controls, for Filling}Filling controls
2484
2485To play Filling, simply click the mouse in any empty square and then
2486type a digit on the keyboard to fill that square. By dragging the
2487mouse, you can select multiple squares to fill with a single keypress.
2488If you make a mistake, click the mouse in the incorrect square and
2489press 0, Space, Backspace or Enter to clear it again (or use the Undo
2490feature).
2491
2492You can also move around the grid with the cursor keys; typing a digit will
2493fill the square containing the cursor with that number; typing 0 will clear
2494it. You can also select multiple squares for numbering or clearing with the
2495return and arrow keys, before typing a digit to fill or clear the highlighted
2496squares (as above). The space bar adds and removes single squares to and from
2497the selection. Backspace and escape remove all squares from the selection.
2498
2499(All the actions described in \k{common-actions} are also available.)
2500
2501\H{filling-parameters} \I{parameters, for Filling}Filling parameters
2502
2503Filling allows you to configure the number of rows and columns of the
2504grid, through the \q{Type} menu.
2505
2506
2507\C{keen} \i{Keen}
2508
2509\cfg{winhelp-topic}{games.keen}
2510
2511You have a square grid; each square may contain a digit from 1 to
2512the size of the grid. The grid is divided into blocks of varying
2513shape and size, with arithmetic clues written in them. Your aim is
2514to fully populate the grid with digits such that:
2515
2516\b Each row contains only one occurrence of each digit
2517
2518\b Each column contains only one occurrence of each digit
2519
2520\b The digits in each block can be combined to form the number
2521stated in the clue, using the arithmetic operation given in the
2522clue. That is:
2523
2524\lcont{
2525
2526\b An addition clue means that the sum of the digits in the block
2527must be the given number. For example, \q{15+} means the contents of
2528the block adds up to fifteen.
2529
2530\b A multiplication clue (e.g. \q{60\times}), similarly, means that
2531the product of the digits in the block must be the given number.
2532
2533\b A subtraction clue will always be written in a block of size two,
2534and it means that one of the digits in the block is greater than the
2535other by the given amount. For example, \q{2\minus} means that one
2536of the digits in the block is 2 more than the other, or equivalently
2537that one digit minus the other one is 2. The two digits could be
2538either way round, though.
2539
2540\b A division clue (e.g. \q{3\divide}), similarly, is always in a
2541block of size two and means that one digit divided by the other is
2542equal to the given amount.
2543
2544Note that a block may contain the same digit more than once
2545(provided the identical ones are not in the same row and column).
2546This rule is precisely the opposite of the rule in Solo's \q{Killer}
2547mode (see \k{solo}).
2548
2549}
2550
2551This puzzle appears in the Times under the name \q{\i{KenKen}}.
2552
2553
2554\H{keen-controls} \i{Keen controls}
2555
2556\IM{Keen controls} controls, for Keen
2557
2558Keen shares much of its control system with Solo (and Unequal).
2559
2560To play Keen, simply click the mouse in any empty square and then
2561type a digit on the keyboard to fill that square. If you make a
2562mistake, click the mouse in the incorrect square and press Space to
2563clear it again (or use the Undo feature).
2564
2565If you \e{right}-click in a square and then type a number, that
2566number will be entered in the square as a \q{pencil mark}. You can
2567have pencil marks for multiple numbers in the same square. Squares
2568containing filled-in numbers cannot also contain pencil marks.
2569
2570The game pays no attention to pencil marks, so exactly what you use
2571them for is up to you: you can use them as reminders that a
2572particular square needs to be re-examined once you know more about a
2573particular number, or you can use them as lists of the possible
2574numbers in a given square, or anything else you feel like.
2575
2576To erase a single pencil mark, right-click in the square and type
2577the same number again.
2578
2579All pencil marks in a square are erased when you left-click and type
2580a number, or when you left-click and press space. Right-clicking and
2581pressing space will also erase pencil marks.
2582
2583As for Solo, the cursor keys can be used in conjunction with the
2584digit keys to set numbers or pencil marks. Use the cursor keys to
2585move a highlight around the grid, and type a digit to enter it in
2586the highlighted square. Pressing return toggles the highlight into a
2587mode in which you can enter or remove pencil marks.
2588
2589Pressing M will fill in a full set of pencil marks in every square
2590that does not have a main digit in it.
2591
2592(All the actions described in \k{common-actions} are also available.)
2593
2594\H{keen-parameters} \I{parameters, for Keen}Keen parameters
2595
2596These parameters are available from the \q{Custom...} option on the
2597\q{Type} menu.
2598
2599\dt \e{Grid size}
2600
2601\dd Specifies the size of the grid. Lower limit is 3; upper limit is
26029 (because the user interface would become more difficult with
2603\q{digits} bigger than 9!).
2604
2605\dt \e{Difficulty}
2606
2607\dd Controls the difficulty of the generated puzzle. At Unreasonable
2608level, some backtracking will be required, but the solution should
2609still be unique. The remaining levels require increasingly complex
2610reasoning to avoid having to backtrack.
2611
2612\dt \e{Multiplication only}
2613
2614\dd If this is enabled, all boxes will be multiplication boxes.
2615With this rule, the puzzle is known as \q{Inshi No Heya}.
2616
2617\C{towers} \i{Towers}
2618
2619\cfg{winhelp-topic}{games.towers}
2620
2621You have a square grid. On each square of the grid you can build a
2622tower, with its height ranging from 1 to the size of the grid.
2623Around the edge of the grid are some numeric clues.
2624
2625Your task is to build a tower on every square, in such a way that:
2626
2627\b Each row contains every possible height of tower once
2628
2629\b Each column contains every possible height of tower once
2630
2631\b Each numeric clue describes the number of towers that can be seen
2632if you look into the square from that direction, assuming that
2633shorter towers are hidden behind taller ones. For example, in a
26345\by\.5 grid, a clue marked \q{5} indicates that the five tower
2635heights must appear in increasing order (otherwise you would not be
2636able to see all five towers), whereas a clue marked \q{1} indicates
2637that the tallest tower (the one marked 5) must come first.
2638
2639In harder or larger puzzles, some towers will be specified for you
2640as well as the clues round the edge, and some edge clues may be
2641missing.
2642
2643This puzzle appears on the web under various names, particularly
2644\q{\i{Skyscrapers}}, but I don't know who first invented it.
2645
2646
2647\H{towers-controls} \i{Towers controls}
2648
2649\IM{Towers controls} controls, for Towers
2650
2651Towers shares much of its control system with Solo, Unequal and Keen.
2652
2653To play Towers, simply click the mouse in any empty square and then
2654type a digit on the keyboard to fill that square with a tower of the
2655given height. If you make a mistake, click the mouse in the
2656incorrect square and press Space to clear it again (or use the Undo
2657feature).
2658
2659If you \e{right}-click in a square and then type a number, that
2660number will be entered in the square as a \q{pencil mark}. You can
2661have pencil marks for multiple numbers in the same square. A square
2662containing a tower cannot also contain pencil marks.
2663
2664The game pays no attention to pencil marks, so exactly what you use
2665them for is up to you: you can use them as reminders that a
2666particular square needs to be re-examined once you know more about a
2667particular number, or you can use them as lists of the possible
2668numbers in a given square, or anything else you feel like.
2669
2670To erase a single pencil mark, right-click in the square and type
2671the same number again.
2672
2673All pencil marks in a square are erased when you left-click and type
2674a number, or when you left-click and press space. Right-clicking and
2675pressing space will also erase pencil marks.
2676
2677As for Solo, the cursor keys can be used in conjunction with the
2678digit keys to set numbers or pencil marks. Use the cursor keys to
2679move a highlight around the grid, and type a digit to enter it in
2680the highlighted square. Pressing return toggles the highlight into a
2681mode in which you can enter or remove pencil marks.
2682
2683Pressing M will fill in a full set of pencil marks in every square
2684that does not have a main digit in it.
2685
2686Left-clicking a clue will mark it as done (grey it out), or unmark it
2687if it is already marked. Holding Control or Shift and pressing an
2688arrow key likewise marks any clue in the given direction.
2689
2690(All the actions described in \k{common-actions} are also available.)
2691
2692\H{towers-parameters} \I{parameters, for Towers}Towers parameters
2693
2694These parameters are available from the \q{Custom...} option on the
2695\q{Type} menu.
2696
2697\dt \e{Grid size}
2698
2699\dd Specifies the size of the grid. Lower limit is 3; upper limit is
27009 (because the user interface would become more difficult with
2701\q{digits} bigger than 9!).
2702
2703\dt \e{Difficulty}
2704
2705\dd Controls the difficulty of the generated puzzle. At Unreasonable
2706level, some backtracking will be required, but the solution should
2707still be unique. The remaining levels require increasingly complex
2708reasoning to avoid having to backtrack.
2709
2710
2711\C{singles} \i{Singles}
2712
2713\cfg{winhelp-topic}{games.singles}
2714
2715You have a grid of white squares, all of which contain numbers. Your task
2716is to colour some of the squares black (removing the number) so as to satisfy
2717all of the following conditions:
2718
2719\b No number occurs more than once in any row or column.
2720
2721\b No black square is horizontally or vertically adjacent to any other black
2722square.
2723
2724\b The remaining white squares must all form one contiguous region
2725(connected by edges, not just touching at corners).
2726
2727Credit for this puzzle goes to \i{Nikoli} \k{nikoli-hitori} who call it
2728\i{Hitori}.
2729
2730Singles was contributed to this collection by James Harvey.
2731
2732\B{nikoli-hitori}
2733\W{http://www.nikoli.com/en/puzzles/hitori.html}\cw{http://www.nikoli.com/en/puzzles/hitori.html}
2734(beware of Flash)
2735
2736\H{singles-controls} \i{Singles controls}
2737
2738\IM{Singles controls} controls, for Singles
2739
2740Left-clicking on an empty square will colour it black; left-clicking again
2741will restore the number. Right-clicking will add a circle (useful for
2742indicating that a cell is definitely not black).
2743
2744You can also use the cursor keys to move around the grid. Pressing the
2745return or space keys will turn a square black or add a circle respectively,
2746and pressing the key again will restore the number or remove the circle.
2747
2748(All the actions described in \k{common-actions} are also available.)
2749
2750\H{singles-parameters} \I{parameters, for Singles}Singles parameters
2751
2752These parameters are available from the \q{Custom...} option on the
2753\q{Type} menu.
2754
2755\dt \e{Width}, \e{Height}
2756
2757\dd Size of grid in squares.
2758
2759\dt \e{Difficulty}
2760
2761\dd Controls the difficulty of the generated puzzle.
2762
2763
2764\C{magnets} \i{Magnets}
2765
2766\cfg{winhelp-topic}{games.magnets}
2767
2768A rectangular grid has been filled with a mixture of magnets (that is,
2769dominoes with one positive end and one negative end) and blank dominoes
2770(that is, dominoes with two neutral poles).
2771These dominoes are initially only seen in silhouette. Around the grid
2772are placed a number of clues indicating the number of positive and
2773negative poles contained in certain columns and rows.
2774
2775Your aim is to correctly place the magnets and blank dominoes such that
2776all the clues are satisfied, with the additional constraint that no two
2777similar magnetic poles may be orthogonally adjacent (since they repel).
2778Neutral poles do not repel, and can be adjacent to any other pole.
2779
2780Credit for this puzzle goes to \i{Janko} \k{janko-magnets}.
2781
2782Magnets was contributed to this collection by James Harvey.
2783
2784\B{janko-magnets}
2785\W{http://www.janko.at/Raetsel/Magnete/index.htm}\cw{http://www.janko.at/Raetsel/Magnete/index.htm}
2786
2787\H{magnets-controls} \i{Magnets controls}
2788
2789\IM{Magnets controls} controls, for Magnets
2790
2791Left-clicking on an empty square places a magnet at that position with
2792the positive pole on the square and the negative pole on the other half
2793of the magnet; left-clicking again reverses the polarity, and a third
2794click removes the magnet.
2795
2796Right-clicking on an empty square places a blank domino there.
2797Right-clicking again places two question marks on the domino, signifying
2798\q{this cannot be blank} (which can be useful to note deductions while
2799solving), and right-clicking again empties the domino.
2800
2801Left-clicking a clue will mark it as done (grey it out), or unmark it if
2802it is already marked.
2803
2804You can also use the cursor keys to move a cursor around the grid.
2805Pressing the return key will lay a domino with a positive pole at that
2806position; pressing again reverses the polarity and then removes the
2807domino, as with left-clicking. Using the space bar allows placement
2808of blank dominoes and cannot-be-blank hints, as for right-clicking.
2809
2810(All the actions described in \k{common-actions} are also available.)
2811
2812\H{magnets-parameters} \I{parameters, for Magnets}Magnets parameters
2813
2814These parameters are available from the \q{Custom...} option on the
2815\q{Type} menu.
2816
2817\dt \e{Width}, \e{Height}
2818
2819\dd Size of grid in squares. There will be half \e{Width} \by \e{Height}
2820dominoes in the grid: if this number is odd then one square will be blank.
2821
2822\lcont{
2823
2824(Grids with at least one odd dimension tend to be easier to solve.)
2825
2826}
2827
2828\dt \e{Difficulty}
2829
2830\dd Controls the difficulty of the generated puzzle. At Tricky level,
2831you are required to make more deductions about empty dominoes and
2832row/column counts.
2833
2834\dt \e{Strip clues}
2835
2836\dd If true, some of the clues around the grid are removed at generation
2837time, making the puzzle more difficult.
2838
2839
2840\C{signpost} \i{Signpost}
2841
2842\cfg{winhelp-topic}{games.signpost}
2843
2844You have a grid of squares; each square (except the last one)
2845contains an arrow, and some squares also contain numbers. Your job
2846is to connect the squares to form a continuous list of numbers
2847starting at 1 and linked in the direction of the arrows \dash so the
2848arrow inside the square with the number 1 will point to the square
2849containing the number 2, which will point to the square containing
2850the number 3, etc. Each square can be any distance away from the
2851previous one, as long as it is somewhere in the direction of the
2852arrow.
2853
2854By convention the first and last numbers are shown; one or more
2855interim numbers may also appear at the beginning.
2856
2857Credit for this puzzle goes to \i{Janko} \k{janko-arrowpath}, who call it
2858\q{Pfeilpfad} (\q{arrow path}).
2859
2860Signpost was contributed to this collection by James Harvey.
2861
2862\B{janko-arrowpath}
2863\W{http://janko.at/Raetsel/Pfeilpfad/index.htm}\cw{http://janko.at/Raetsel/Pfeilpfad/index.htm}
2864
2865\H{signpost-controls} \I{controls, for Signpost}Signpost controls
2866
2867To play Signpost, you connect squares together by dragging from one
2868square to another, indicating that they are adjacent in the
2869sequence. Drag with the left button from a square to its successor,
2870or with the right button from a square to its predecessor.
2871
2872If you connect together two squares in this way and one of them has
2873a number in it, the appropriate number will appear in the other
2874square. If you connect two non-numbered squares, they will be
2875assigned temporary algebraic labels: on the first occasion, they
2876will be labelled \cq{a} and \cq{a+1}, and then \cq{b} and \cq{b+1},
2877and so on. Connecting more squares on to the ends of such a chain
2878will cause them all to be labelled with the same letter.
2879
2880When you left-click or right-click in a square, the legal squares to
2881connect it to will be shown.
2882
2883The arrow in each square starts off black, and goes grey once you
2884connect the square to its successor. Also, each square which needs a
2885predecessor has a small dot in the bottom left corner, which
2886vanishes once you link a square to it. So your aim is always to
2887connect a square with a black arrow to a square with a dot.
2888
2889To remove any links for a particular square (both incoming and
2890outgoing), left-drag it off the grid. To remove a whole chain,
2891right-drag any square in the chain off the grid.
2892
2893You can also use the cursor keys to move around the grid squares and
2894lines. Pressing the return key when over a square starts a link
2895operation, and pressing the return key again over a square will
2896finish the link, if allowable. Pressing the space bar over a square
2897will show the other squares pointing to it, and allow you to form a
2898backward link, and pressing the space bar again cancels this.
2899
2900(All the actions described in \k{common-actions} are also available.)
2901
2902\H{signpost-parameters} \I{parameters, for Signpost}Signpost parameters
2903
2904These parameters are available from the \q{Custom...} option on the
2905\q{Type} menu.
2906
2907\dt \e{Width}, \e{Height}
2908
2909\dd Size of grid in squares.
2910
2911\dt \e{Force start/end to corners}
2912
2913\dd If true, the start and end squares are always placed in opposite corners
2914(the start at the top left, and the end at the bottom right). If false the start
2915and end squares are placed randomly (although always both shown).
2916
2917\C{range} \i{Range}
2918
2919\cfg{winhelp-topic}{games.range}
2920
2921You have a grid of squares; some squares contain numbers. Your job is
2922to colour some of the squares black, such that several criteria are
2923satisfied:
2924
2925\b no square with a number is coloured black.
2926
2927\b no two black squares are adjacent (horizontally or vertically).
2928
2929\b for any two white squares, there is a path between them using only
2930white squares.
2931
2932\b for each square with a number, that number denotes the total number
2933of white squares reachable from that square going in a straight line
2934in any horizontal or vertical direction until hitting a wall or a
2935black square; the square with the number is included in the total
2936(once).
2937
2938For instance, a square containing the number one must have four black
2939squares as its neighbours by the last criterion; but then it's
2940impossible for it to be connected to any outside white square, which
2941violates the second to last criterion. So no square will contain the
2942number one.
2943
2944Credit for this puzzle goes to \i{Nikoli}, who have variously called
2945it \q{Kurodoko}, \q{Kuromasu} or \q{Where is Black Cells}.
2946\k{nikoli-range}.
2947
2948Range was contributed to this collection by Jonas K\u00F6{oe}lker.
2949
2950\B{nikoli-range}
2951\W{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells.html}\cw{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells.html}
2952
2953\H{range-controls} \I{controls, for Range}Range controls
2954
2955Click with the left button to paint a square black, or with the right
2956button to mark a square with a dot to indicate that you are sure it
2957should \e{not} be painted black. Repeated clicking with either button
2958will cycle the square through the three possible states (filled,
2959dotted or empty) in opposite directions.
2960
2961You can also use the cursor keys to move around the grid squares.
2962Pressing Return does the same as clicking with the left button, while
2963pressing Space does the same as a right button click. Moving with the
2964cursor keys while holding Shift will place dots in all squares that
2965are moved through.
2966
2967
2968(All the actions described in \k{common-actions} are also available.)
2969
2970\H{range-parameters} \I{parameters, for Range}Range parameters
2971
2972These parameters are available from the \q{Custom...} option on the
2973\q{Type} menu.
2974
2975\dt \e{Width}, \e{Height}
2976
2977\dd Size of grid in squares.
2978
2979\C{pearl} \i{Pearl}
2980
2981\cfg{winhelp-topic}{games.pearl}
2982
2983You have a grid of squares. Your job is to draw lines between the
2984centres of horizontally or vertically adjacent squares, so that the
2985lines form a single closed loop. In the resulting grid, some of the
2986squares that the loop passes through will contain corners, and some
2987will be straight horizontal or vertical lines. (And some squares can
2988be completely empty \dash the loop doesn't have to pass through every
2989square.)
2990
2991Some of the squares contain black and white circles, which are clues
2992that the loop must satisfy.
2993
2994A black circle in a square indicates that that square is a corner, but
2995neither of the squares adjacent to it in the loop is also a corner.
2996
2997A white circle indicates that the square is a straight edge, but \e{at
2998least one} of the squares adjacent to it in the loop is a corner.
2999
3000(In both cases, the clue only constrains the two squares adjacent
3001\e{in the loop}, that is, the squares that the loop passes into after
3002leaving the clue square. The squares that are only adjacent \e{in the
3003grid} are not constrained.)
3004
3005Credit for this puzzle goes to \i{Nikoli}, who call it \q{Masyu}.
3006\k{nikoli-pearl}
3007
3008Thanks to James Harvey for assistance with the implementation.
3009
3010\B{nikoli-pearl}
3011\W{http://www.nikoli.co.jp/en/puzzles/masyu.html}\cw{http://www.nikoli.co.jp/en/puzzles/masyu.html}
3012(beware of Flash)
3013
3014\H{pearl-controls} \I{controls, for Pearl}Pearl controls
3015
3016Click with the left button on a grid edge to draw a segment of the
3017loop through that edge, or to remove a segment once it is drawn.
3018
3019Drag with the left button through a series of squares to draw more
3020than one segment of the loop in one go. Alternatively, drag over an
3021existing part of the loop to undraw it, or to undraw part of it and
3022then go in a different direction.
3023
3024Click with the right button on a grid edge to mark it with a cross,
3025indicating that you are sure the loop does not go through that edge.
3026(For instance, if you have decided which of the squares adjacent to a
3027white clue has to be a corner, but don't yet know which way the corner
3028turns, you might mark the one way it \e{can't} go with a cross.)
3029
3030Alternatively, use the cursor keys to move the cursor. Use the Enter
3031key to begin and end keyboard \q{drag} operations. Use the Space,
3032Escape or Backspace keys to cancel the drag. Or, hold Control while
3033dragging with the cursor keys to toggle segments as you move between
3034squares.
3035
3036Pressing Control-Shift-arrowkey or Shift-arrowkey simulates a left or
3037right click, respectively, on the edge in the direction of the key.
3038
3039(All the actions described in \k{common-actions} are also available.)
3040
3041\H{pearl-parameters} \I{parameters, for Pearl}Pearl parameters
3042
3043These parameters are available from the \q{Custom...} option on the
3044\q{Type} menu.
3045
3046\C{undead} \i{Undead}
3047
3048\cfg{winhelp-topic}{games.undead}
3049
3050You are given a grid of squares, some of which contain diagonal
3051mirrors. Every square which is not a mirror must be filled with one of
3052three types of undead monster: a ghost, a vampire, or a zombie.
3053
3054Vampires can be seen directly, but are invisible when reflected in
3055mirrors. Ghosts are the opposite way round: they can be seen in
3056mirrors, but are invisible when looked at directly. Zombies are
3057visible by any means.
3058
3059You are also told the total number of each type of monster in the
3060grid. Also around the edge of the grid are written numbers, which
3061indicate how many monsters can be seen if you look into the grid along
3062a row or column starting from that position. (The diagonal mirrors are
3063reflective on both sides. If your reflected line of sight crosses the
3064same monster more than once, the number will count it each time it is
3065visible, not just once.)
3066
3067This puzzle type was invented by David Millar, under the name
3068\q{Haunted Mirror Maze}. See \k{janko-undead} for more details.
3069
3070Undead was contributed to this collection by Steffen Bauer.
3071
3072\B{janko-undead}
3073\W{http://www.janko.at/Raetsel/Spukschloss/index.htm}\cw{http://www.janko.at/Raetsel/Spukschloss/index.htm}
3074
3075\H{undead-controls} \I{controls, for Undead}Undead controls
3076
3077Undead has a similar control system to Solo, Unequal and Keen.
3078
3079To play Undead, click the mouse in any empty square and then type a
3080letter on the keyboard indicating the type of monster: \q{G} for a
3081ghost, \q{V} for a vampire, or \q{Z} for a zombie. If you make a
3082mistake, click the mouse in the incorrect square and press Space to
3083clear it again (or use the Undo feature).
3084
3085If you \e{right}-click in a square and then type a letter, the
3086corresponding monster will be shown in reduced size in that square, as
3087a \q{pencil mark}. You can have pencil marks for multiple monsters in
3088the same square. A square containing a full-size monster cannot also
3089contain pencil marks.
3090
3091The game pays no attention to pencil marks, so exactly what you use
3092them for is up to you: you can use them as reminders that a particular
3093square needs to be re-examined once you know more about a particular
3094monster, or you can use them as lists of the possible monster in a
3095given square, or anything else you feel like.
3096
3097To erase a single pencil mark, right-click in the square and type
3098the same letter again.
3099
3100All pencil marks in a square are erased when you left-click and type a
3101monster letter, or when you left-click and press Space. Right-clicking
3102and pressing space will also erase pencil marks.
3103
3104As for Solo, the cursor keys can be used in conjunction with the letter
3105keys to place monsters or pencil marks. Use the cursor keys to move a
3106highlight around the grid, and type a monster letter to enter it in
3107the highlighted square. Pressing return toggles the highlight into a
3108mode in which you can enter or remove pencil marks.
3109
3110If you prefer plain letters of the alphabet to cute monster pictures,
3111you can press \q{A} to toggle between showing the monsters as monsters or
3112showing them as letters.
3113
3114Left-clicking a clue will mark it as done (grey it out), or unmark it
3115if it is already marked.
3116
3117(All the actions described in \k{common-actions} are also available.)
3118
3119\H{undead-parameters} \I{parameters, for Undead}Undead parameters
3120
3121These parameters are available from the \q{Custom...} option on the
3122\q{Type} menu.
3123
3124\dt \e{Width}, \e{Height}
3125
3126\dd Size of grid in squares.
3127
3128\dt \e{Difficulty}
3129
3130\dd Controls the difficulty of the generated puzzle.
3131
3132\C{unruly} \i{Unruly}
3133
3134\cfg{winhelp-topic}{games.unruly}
3135
3136You are given a grid of squares, which you must colour either black or
3137white. Some squares are provided as clues; the rest are left for you
3138to fill in. Each row and column must contain the same number of black
3139and white squares, and no row or column may contain three consecutive
3140squares of the same colour.
3141
3142This puzzle type was invented by Adolfo Zanellati, under the name
3143\q{Tohu wa Vohu}. See \k{janko-unruly} for more details.
3144
3145Unruly was contributed to this collection by Lennard Sprong.
3146
3147\B{janko-unruly}
3148\W{http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm}\cw{http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm}
3149
3150\H{unruly-controls} \I{controls, for Unruly}Unruly controls
3151
3152To play Unruly, click the mouse in a square to change its colour.
3153Left-clicking an empty square will turn it black, and right-clicking
3154will turn it white. Keep clicking the same button to cycle through the
3155three possible states for the square. If you middle-click in a square
3156it will be reset to empty.
3157
3158You can also use the cursor keys to move around the grid. Pressing the
3159return or space keys will turn an empty square black or white
3160respectively (and then cycle the colours in the same way as the mouse
3161buttons), and pressing Backspace will reset a square to empty.
3162
3163(All the actions described in \k{common-actions} are also available.)
3164
3165\H{unruly-parameters} \I{parameters, for Unruly}Unruly parameters
3166
3167These parameters are available from the \q{Custom...} option on the
3168\q{Type} menu.
3169
3170\dt \e{Width}, \e{Height}
3171
3172\dd Size of grid in squares. (Note that the rules of the game require
3173both the width and height to be even numbers.)
3174
3175\dt \e{Difficulty}
3176
3177\dd Controls the difficulty of the generated puzzle.
3178
3179\dt \e{Unique rows and columns}
3180
3181\dd If enabled, no two rows are permitted to have exactly the same
3182pattern, and likewise columns. (A row and a column can match, though.)
3183
3184\C{flood} \i{Flood}
3185
3186\cfg{winhelp-topic}{games.flood}
3187
3188You are given a grid of squares, coloured at random in multiple
3189colours. In each move, you can flood-fill the top left square in a
3190colour of your choice (i.e. every square reachable from the starting
3191square by an orthogonally connected path of squares all the same
3192colour will be filled in the new colour). As you do this, more and
3193more of the grid becomes connected to the starting square.
3194
3195Your aim is to make the whole grid the same colour, in as few moves as
3196possible. The game will set a limit on the number of moves, based on
3197running its own internal solver. You win if you can make the whole
3198grid the same colour in that many moves or fewer.
3199
3200I saw this game (with a fixed grid size, fixed number of colours, and
3201fixed move limit) at http://floodit.appspot.com (no longer accessible).
3202
3203\H{flood-controls} \I{controls, for Flood}Flood controls
3204
3205To play Flood, click the mouse in a square. The top left corner and
3206everything connected to it will be flood-filled with the colour of the
3207square you clicked. Clicking a square the same colour as the top left
3208corner has no effect, and therefore does not count as a move.
3209
3210You can also use the cursor keys to move a cursor (outline black
3211square) around the grid. Pressing the return key will fill the top
3212left corner in the colour of the square under the cursor.
3213
3214(All the actions described in \k{common-actions} are also available.)
3215
3216\H{flood-parameters} \I{parameters, for Flood}Flood parameters
3217
3218These parameters are available from the \q{Custom...} option on the
3219\q{Type} menu.
3220
3221\dt \e{Width}, \e{Height}
3222
3223\dd Size of the grid, in squares.
3224
3225\dt \e{Colours}
3226
3227\dd Number of colours used to fill the grid. Must be at least 3 (with
3228two colours there would only be one legal move at any stage, hence no
3229choice to make at all), and at most 10.
3230
3231\dt \e{Extra moves permitted}
3232
3233\dd Controls the difficulty of the puzzle, by increasing the move
3234limit. In each new grid, Flood will run an internal solver to generate
3235its own solution, and then the value in this field will be added to
3236the length of Flood's solution to generate the game's move limit. So a
3237value of 0 requires you to be just as efficient as Flood's automated
3238solver, and a larger value makes it easier.
3239
3240\lcont{
3241
3242(Note that Flood's internal solver will not necessarily find the
3243shortest possible solution, though I believe it's pretty close. For a
3244real challenge, set this value to 0 and then try to solve a grid in
3245\e{strictly fewer} moves than the limit you're given!)
3246
3247}
3248
3249\C{tracks} \i{Tracks}
3250
3251\cfg{winhelp-topic}{games.tracks}
3252
3253You are given a grid of squares, some of which are filled with train
3254tracks. You need to complete the track from A to B so that the rows and
3255columns contain the same number of track segments as are indicated in the
3256clues to the top and right of the grid.
3257
3258There are only straight and 90 degree curved rails, and the track may not
3259cross itself.
3260
3261Tracks was contributed to this collection by James Harvey.
3262
3263\H{tracks-controls} \I{controls, for Tracks}Tracks controls
3264
3265Left-clicking on an edge between two squares adds a track segment between
3266the two squares. Right-clicking on an edge adds a cross on the edge,
3267indicating no track is possible there.
3268
3269Left-clicking in a square adds a colour indicator showing that you know the
3270square must contain a track, even if you don't know which edges it crosses
3271yet. Right-clicking in a square adds a cross indicating it contains no
3272track segment.
3273
3274Left- or right-dragging between squares allows you to lay a straight line
3275of is-track or is-not-track indicators, useful for filling in rows or
3276columns to match the clue.
3277
3278(All the actions described in \k{common-actions} are also available.)
3279
3280\H{tracks-parameters} \I{parameters, for Tracks}Tracks parameters
3281
3282These parameters are available from the \q{Custom...} option on the
3283\q{Type} menu.
3284
3285\dt \e{Width}, \e{Height}
3286
3287\dd Size of the grid, in squares.
3288
3289\dt \e{Difficulty}
3290
3291\dd Controls the difficulty of the generated puzzle: at Tricky level,
3292you are required to make more deductions regarding disregarding moves
3293that would lead to impossible crossings later.
3294
3295\dt \e{Disallow consecutive 1 clues}
3296
3297\dd Controls whether the Tracks game generation permits two adjacent
3298rows or columns to have a 1 clue, or permits the row or column of the
3299track's endpoint to have a 1 clue. By default this is not permitted,
3300to avoid long straight boring segments of track and make the games
3301more twiddly and interesting. If you want to restore the possibility,
3302turn this option off.
3303
3304
3305\C{palisade} \i{Palisade}
3306
3307\cfg{winhelp-topic}{games.palisade}
3308
3309You're given a grid of squares, some of which contain numbers. Your
3310goal is to subdivide the grid into contiguous regions, all of the same
3311(given) size, such that each square containing a number is adjacent to
3312exactly that many edges (including those between the inside and the
3313outside of the grid).
3314
3315Credit for this puzzle goes to \i{Nikoli}, who call it \q{Five Cells}.
3316\k{nikoli-palisade}.
3317
3318Palisade was contributed to this collection by Jonas K\u00F6{oe}lker.
3319
3320\B{nikoli-palisade}
3321\W{http://nikoli.co.jp/en/puzzles/five_cells.html}\cw{http://nikoli.co.jp/en/puzzles/five_cells.html}
3322
3323\H{palisade-controls} \I{controls, for Palisade}Palisade controls
3324
3325Left-click to place an edge. Right-click to indicate \q{no edge}.
3326Alternatively, the arrow keys will move a keyboard cursor. Holding
3327Control while pressing an arrow key will place an edge. Press
3328Shift-arrowkey to switch off an edge. Repeat an action to perform
3329its inverse.
3330
3331(All the actions described in \k{common-actions} are also available.)
3332
3333\H{Palisade-parameters} \I{parameters, for Palisade}Palisade parameters
3334
3335These parameters are available from the \q{Custom...} option on the
3336\q{Type} menu.
3337
3338\dt \e{Width}, \e{Height}
3339
3340\dd Size of grid in squares.
3341
3342\dt \e{Region size}
3343
3344\dd The size of the regions into which the grid must be subdivided.
3345
3346\A{licence} \I{MIT licence}\ii{Licence}
3347
3348This software is \i{copyright} 2004-2014 Simon Tatham.
3349
3350Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
3351K\u00F6{oe}lker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou,
3352Bernd Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens.
3353
3354Permission is hereby granted, free of charge, to any person
3355obtaining a copy of this software and associated documentation files
3356(the \q{Software}), to deal in the Software without restriction,
3357including without limitation the rights to use, copy, modify, merge,
3358publish, distribute, sublicense, and/or sell copies of the Software,
3359and to permit persons to whom the Software is furnished to do so,
3360subject to the following conditions:
3361
3362The above copyright notice and this permission notice shall be
3363included in all copies or substantial portions of the Software.
3364
3365THE SOFTWARE IS PROVIDED \q{AS IS}, WITHOUT WARRANTY OF ANY KIND,
3366EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
3367MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
3368NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
3369BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3370ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3371CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3372SOFTWARE.
3373
3374\IM{command-line}{command line} command line
3375
3376\IM{default parameters, specifying} default parameters, specifying
3377\IM{default parameters, specifying} preferences, specifying default
3378
3379\IM{Unix} Unix
3380\IM{Unix} Linux
3381
3382\IM{generating game IDs} generating game IDs
3383\IM{generating game IDs} game ID, generating
3384
3385\IM{specific} \q{Specific}, menu option
3386\IM{custom} \q{Custom}, menu option
3387
3388\IM{game ID} game ID
3389\IM{game ID} ID, game
3390\IM{ID format} ID format
3391\IM{ID format} format, ID
3392\IM{ID format} game ID, format
3393
3394\IM{keys} keys
3395\IM{keys} shortcuts (keyboard)
3396
3397\IM{initial state} initial state
3398\IM{initial state} state, initial
3399
3400\IM{MIT licence} MIT licence
3401\IM{MIT licence} licence, MIT
diff --git a/apps/plugins/puzzles/src/puzzles.cnt b/apps/plugins/puzzles/src/puzzles.cnt
new file mode 100644
index 0000000000..fef32228ea
--- /dev/null
+++ b/apps/plugins/puzzles/src/puzzles.cnt
@@ -0,0 +1,167 @@
1:Title Simon Tatham's Portable Puzzle Collection
21 Contents=Top
31 Chapter 1: Introduction
42 Chapter 1: Introduction=t00000000
51 Chapter 2: Common features
62 Chapter 2: Common features=t00000001
72 Section 2.1: Common actions=t00000002
82 Section 2.2: Specifying games with the game ID=t00000003
92 Section 2.3: The ‘Type’ menu=t00000004
102 Section 2.4: Specifying game parameters on the command line=t00000005
112 Section 2.5: Unix command-line options=t00000006
121 Chapter 3: Net
132 Chapter 3: Net=games.net
142 Section 3.1: Net controls=t00000007
152 Section 3.2: Net parameters=t00000008
161 Chapter 4: Cube
172 Chapter 4: Cube=games.cube
182 Section 4.1: Cube controls=t00000009
192 Section 4.2: Cube parameters=t00000010
201 Chapter 5: Fifteen
212 Chapter 5: Fifteen=games.fifteen
222 Section 5.1: Fifteen controls=t00000011
232 Section 5.2: Fifteen parameters=t00000012
241 Chapter 6: Sixteen
252 Chapter 6: Sixteen=games.sixteen
262 Section 6.1: Sixteen controls=t00000013
272 Section 6.2: Sixteen parameters=t00000014
281 Chapter 7: Twiddle
292 Chapter 7: Twiddle=games.twiddle
302 Section 7.1: Twiddle controls=t00000015
312 Section 7.2: Twiddle parameters=t00000016
321 Chapter 8: Rectangles
332 Chapter 8: Rectangles=games.rectangles
342 Section 8.1: Rectangles controls=t00000017
352 Section 8.2: Rectangles parameters=t00000018
361 Chapter 9: Netslide
372 Chapter 9: Netslide=games.netslide
381 Chapter 10: Pattern
392 Chapter 10: Pattern=games.pattern
402 Section 10.1: Pattern controls=t00000019
412 Section 10.2: Pattern parameters=t00000020
421 Chapter 11: Solo
432 Chapter 11: Solo=games.solo
442 Section 11.1: Solo controls=t00000021
452 Section 11.2: Solo parameters=t00000022
461 Chapter 12: Mines
472 Chapter 12: Mines=games.mines
482 Section 12.1: Mines controls=t00000023
492 Section 12.2: Mines parameters=t00000024
501 Chapter 13: Same Game
512 Chapter 13: Same Game=games.samegame
522 Section 13.1: Same Game controls=t00000025
532 Section 13.2: Same Game parameters=t00000026
541 Chapter 14: Flip
552 Chapter 14: Flip=games.flip
562 Section 14.1: Flip controls=t00000027
572 Section 14.2: Flip parameters=t00000028
581 Chapter 15: Guess
592 Chapter 15: Guess=games.guess
602 Section 15.1: Guess controls=t00000029
612 Section 15.2: Guess parameters=t00000030
621 Chapter 16: Pegs
632 Chapter 16: Pegs=games.pegs
642 Section 16.1: Pegs controls=t00000031
652 Section 16.2: Pegs parameters=t00000032
661 Chapter 17: Dominosa
672 Chapter 17: Dominosa=games.dominosa
682 Section 17.1: Dominosa controls=t00000033
692 Section 17.2: Dominosa parameters=t00000034
701 Chapter 18: Untangle
712 Chapter 18: Untangle=games.untangle
722 Section 18.1: Untangle controls=t00000035
732 Section 18.2: Untangle parameters=t00000036
741 Chapter 19: Black Box
752 Chapter 19: Black Box=games.blackbox
762 Section 19.1: Black Box controls=t00000037
772 Section 19.2: Black Box parameters=t00000038
781 Chapter 20: Slant
792 Chapter 20: Slant=games.slant
802 Section 20.1: Slant controls=t00000039
812 Section 20.2: Slant parameters=t00000040
821 Chapter 21: Light Up
832 Chapter 21: Light Up=games.lightup
842 Section 21.1: Light Up controls=t00000041
852 Section 21.2: Light Up parameters=t00000042
861 Chapter 22: Map
872 Chapter 22: Map=games.map
882 Section 22.1: Map controls=t00000043
892 Section 22.2: Map parameters=t00000044
901 Chapter 23: Loopy
912 Chapter 23: Loopy=games.loopy
922 Section 23.1: Loopy controls=t00000045
932 Section 23.2: Loopy parameters=t00000046
941 Chapter 24: Inertia
952 Chapter 24: Inertia=games.inertia
962 Section 24.1: Inertia controls=t00000047
972 Section 24.2: Inertia parameters=t00000048
981 Chapter 25: Tents
992 Chapter 25: Tents=games.tents
1002 Section 25.1: Tents controls=t00000049
1012 Section 25.2: Tents parameters=t00000050
1021 Chapter 26: Bridges
1032 Chapter 26: Bridges=games.bridges
1042 Section 26.1: Bridges controls=t00000051
1052 Section 26.2: Bridges parameters=t00000052
1061 Chapter 27: Unequal
1072 Chapter 27: Unequal=games.unequal
1082 Section 27.1: Unequal controls=t00000053
1092 Section 27.2: Unequal parameters=t00000054
1101 Chapter 28: Galaxies
1112 Chapter 28: Galaxies=games.galaxies
1122 Section 28.1: Galaxies controls=t00000055
1132 Section 28.2: Galaxies parameters=t00000056
1141 Chapter 29: Filling
1152 Chapter 29: Filling=games.filling
1162 Section 29.1: Filling controls=t00000057
1172 Section 29.2: Filling parameters=t00000058
1181 Chapter 30: Keen
1192 Chapter 30: Keen=games.keen
1202 Section 30.1: Keen controls=t00000059
1212 Section 30.2: Keen parameters=t00000060
1221 Chapter 31: Towers
1232 Chapter 31: Towers=games.towers
1242 Section 31.1: Towers controls=t00000061
1252 Section 31.2: Towers parameters=t00000062
1261 Chapter 32: Singles
1272 Chapter 32: Singles=games.singles
1282 Section 32.1: Singles controls=t00000063
1292 Section 32.2: Singles parameters=t00000064
1301 Chapter 33: Magnets
1312 Chapter 33: Magnets=games.magnets
1322 Section 33.1: Magnets controls=t00000065
1332 Section 33.2: Magnets parameters=t00000066
1341 Chapter 34: Signpost
1352 Chapter 34: Signpost=games.signpost
1362 Section 34.1: Signpost controls=t00000067
1372 Section 34.2: Signpost parameters=t00000068
1381 Chapter 35: Range
1392 Chapter 35: Range=games.range
1402 Section 35.1: Range controls=t00000069
1412 Section 35.2: Range parameters=t00000070
1421 Chapter 36: Pearl
1432 Chapter 36: Pearl=games.pearl
1442 Section 36.1: Pearl controls=t00000071
1452 Section 36.2: Pearl parameters=t00000072
1461 Chapter 37: Undead
1472 Chapter 37: Undead=games.undead
1482 Section 37.1: Undead controls=t00000073
1492 Section 37.2: Undead parameters=t00000074
1501 Chapter 38: Unruly
1512 Chapter 38: Unruly=games.unruly
1522 Section 38.1: Unruly controls=t00000075
1532 Section 38.2: Unruly parameters=t00000076
1541 Chapter 39: Flood
1552 Chapter 39: Flood=games.flood
1562 Section 39.1: Flood controls=t00000077
1572 Section 39.2: Flood parameters=t00000078
1581 Chapter 40: Tracks
1592 Chapter 40: Tracks=games.tracks
1602 Section 40.1: Tracks controls=t00000079
1612 Section 40.2: Tracks parameters=t00000080
1621 Chapter 41: Palisade
1632 Chapter 41: Palisade=games.palisade
1642 Section 41.1: Palisade controls=t00000081
1652 Section 41.2: Palisade parameters=t00000082
1661 Appendix A: Licence
1672 Appendix A: Licence=t00000083
diff --git a/apps/plugins/puzzles/src/puzzles.h b/apps/plugins/puzzles/src/puzzles.h
new file mode 100644
index 0000000000..03af2ca186
--- /dev/null
+++ b/apps/plugins/puzzles/src/puzzles.h
@@ -0,0 +1,680 @@
1/*
2 * puzzles.h: header file for my puzzle collection
3 */
4
5#ifndef PUZZLES_PUZZLES_H
6#define PUZZLES_PUZZLES_H
7
8#include <stdio.h> /* for FILE */
9#include <stdlib.h> /* for size_t */
10#include <limits.h> /* for UINT_MAX */
11
12#ifndef TRUE
13#define TRUE 1
14#endif
15#ifndef FALSE
16#define FALSE 0
17#endif
18
19#define PI 3.141592653589793238462643383279502884197169399
20
21#define lenof(array) ( sizeof(array) / sizeof(*(array)) )
22
23#define STR_INT(x) #x
24#define STR(x) STR_INT(x)
25
26/* NB not perfect because they evaluate arguments multiple times. */
27#ifndef max
28#define max(x,y) ( (x)>(y) ? (x) : (y) )
29#endif /* max */
30#ifndef min
31#define min(x,y) ( (x)<(y) ? (x) : (y) )
32#endif /* min */
33
34enum {
35 LEFT_BUTTON = 0x0200,
36 MIDDLE_BUTTON,
37 RIGHT_BUTTON,
38 LEFT_DRAG,
39 MIDDLE_DRAG,
40 RIGHT_DRAG,
41 LEFT_RELEASE,
42 MIDDLE_RELEASE,
43 RIGHT_RELEASE,
44 CURSOR_UP,
45 CURSOR_DOWN,
46 CURSOR_LEFT,
47 CURSOR_RIGHT,
48 CURSOR_SELECT,
49 CURSOR_SELECT2,
50
51 /* made smaller because of 'limited range of datatype' errors. */
52 MOD_CTRL = 0x1000,
53 MOD_SHFT = 0x2000,
54 MOD_NUM_KEYPAD = 0x4000,
55 MOD_MASK = 0x7000 /* mask for all modifiers */
56};
57
58#define IS_MOUSE_DOWN(m) ( (unsigned)((m) - LEFT_BUTTON) <= \
59 (unsigned)(RIGHT_BUTTON - LEFT_BUTTON))
60#define IS_MOUSE_DRAG(m) ( (unsigned)((m) - LEFT_DRAG) <= \
61 (unsigned)(RIGHT_DRAG - LEFT_DRAG))
62#define IS_MOUSE_RELEASE(m) ( (unsigned)((m) - LEFT_RELEASE) <= \
63 (unsigned)(RIGHT_RELEASE - LEFT_RELEASE))
64#define IS_CURSOR_MOVE(m) ( (m) == CURSOR_UP || (m) == CURSOR_DOWN || \
65 (m) == CURSOR_RIGHT || (m) == CURSOR_LEFT )
66#define IS_CURSOR_SELECT(m) ( (m) == CURSOR_SELECT || (m) == CURSOR_SELECT2)
67
68/*
69 * Flags in the back end's `flags' word.
70 */
71/* Bit flags indicating mouse button priorities */
72#define BUTTON_BEATS(x,y) ( 1 << (((x)-LEFT_BUTTON)*3+(y)-LEFT_BUTTON) )
73/* Flag indicating that Solve operations should be animated */
74#define SOLVE_ANIMATES ( 1 << 9 )
75/* Pocket PC: Game requires right mouse button emulation */
76#define REQUIRE_RBUTTON ( 1 << 10 )
77/* Pocket PC: Game requires numeric input */
78#define REQUIRE_NUMPAD ( 1 << 11 )
79/* end of `flags' word definitions */
80
81#ifdef _WIN32_WCE
82 /* Pocket PC devices have small, portrait screen that requires more vivid colours */
83 #define SMALL_SCREEN
84 #define PORTRAIT_SCREEN
85 #define VIVID_COLOURS
86 #define STYLUS_BASED
87#endif
88
89#define IGNOREARG(x) ( (x) = (x) )
90
91typedef struct frontend frontend;
92typedef struct config_item config_item;
93typedef struct midend midend;
94typedef struct random_state random_state;
95typedef struct game_params game_params;
96typedef struct game_state game_state;
97typedef struct game_ui game_ui;
98typedef struct game_drawstate game_drawstate;
99typedef struct game game;
100typedef struct blitter blitter;
101typedef struct document document;
102typedef struct drawing_api drawing_api;
103typedef struct drawing drawing;
104typedef struct psdata psdata;
105
106#define ALIGN_VNORMAL 0x000
107#define ALIGN_VCENTRE 0x100
108
109#define ALIGN_HLEFT 0x000
110#define ALIGN_HCENTRE 0x001
111#define ALIGN_HRIGHT 0x002
112
113#define FONT_FIXED 0
114#define FONT_VARIABLE 1
115
116/* For printing colours */
117#define HATCH_SLASH 1
118#define HATCH_BACKSLASH 2
119#define HATCH_HORIZ 3
120#define HATCH_VERT 4
121#define HATCH_PLUS 5
122#define HATCH_X 6
123
124/*
125 * Structure used to pass configuration data between frontend and
126 * game
127 */
128enum { C_STRING, C_CHOICES, C_BOOLEAN, C_END };
129struct config_item {
130 /*
131 * `name' is never dynamically allocated.
132 */
133 char *name;
134 /*
135 * `type' contains one of the above values.
136 */
137 int type;
138 /*
139 * For C_STRING, `sval' is always dynamically allocated and
140 * non-NULL. For C_BOOLEAN and C_END, `sval' is always NULL.
141 * For C_CHOICES, `sval' is non-NULL, _not_ dynamically
142 * allocated, and contains a set of option strings separated by
143 * a delimiter. The delimeter is also the first character in
144 * the string, so for example ":Foo:Bar:Baz" gives three
145 * options `Foo', `Bar' and `Baz'.
146 */
147 char *sval;
148 /*
149 * For C_BOOLEAN, this is TRUE or FALSE. For C_CHOICES, it
150 * indicates the chosen index from the `sval' list. In the
151 * above example, 0==Foo, 1==Bar and 2==Baz.
152 */
153 int ival;
154};
155
156/*
157 * Structure used to communicate the presets menu from midend to
158 * frontend. In principle, it's also used to pass the same information
159 * from game to midend, though games that don't specify a menu
160 * hierarchy (i.e. most of them) will use the simpler fetch_preset()
161 * function to return an unstructured list.
162 *
163 * A tree of these structures always belongs to the midend, and only
164 * the midend should ever need to free it. The front end should treat
165 * them as read-only.
166 */
167struct preset_menu_entry {
168 char *title;
169 /* Exactly one of the next two fields is NULL, depending on
170 * whether this entry is a submenu title or an actual preset */
171 game_params *params;
172 struct preset_menu *submenu;
173 /* Every preset menu entry has a number allocated by the mid-end,
174 * so that midend_which_preset() can return a value that
175 * identifies an entry anywhere in the menu hierarchy. The values
176 * will be allocated reasonably densely from 1 upwards (so it's
177 * reasonable for the front end to use them as array indices if it
178 * needs to store GUI state per menu entry), but no other
179 * guarantee is given about their ordering.
180 *
181 * Entries containing submenus have ids too - not only the actual
182 * presets are numbered. */
183 int id;
184};
185struct preset_menu {
186 int n_entries; /* number of entries actually in use */
187 int entries_size; /* space currently allocated in this array */
188 struct preset_menu_entry *entries;
189};
190/* For games which do want to directly return a tree of these, here
191 * are convenience routines (in midend.c) for constructing one. These
192 * assume that 'title' and 'encoded_params' are already dynamically
193 * allocated by the caller; the resulting preset_menu tree takes
194 * ownership of them. */
195struct preset_menu *preset_menu_new(void);
196struct preset_menu *preset_menu_add_submenu(struct preset_menu *parent,
197 char *title);
198void preset_menu_add_preset(struct preset_menu *menu,
199 char *title, game_params *params);
200/* Helper routine front ends can use for one of the ways they might
201 * want to organise their preset menu usage */
202game_params *preset_menu_lookup_by_id(struct preset_menu *menu, int id);
203
204/*
205 * Platform routines
206 */
207
208/* We can't use #ifdef DEBUG, because Cygwin defines it by default. */
209#ifdef DEBUGGING
210#define debug(x) (debug_printf x)
211void debug_printf(char *fmt, ...);
212#else
213#define debug(x)
214#endif
215
216void fatal(char *fmt, ...);
217void frontend_default_colour(frontend *fe, float *output);
218void deactivate_timer(frontend *fe);
219void activate_timer(frontend *fe);
220void get_random_seed(void **randseed, int *randseedsize);
221
222/*
223 * drawing.c
224 */
225drawing *drawing_new(const drawing_api *api, midend *me, void *handle);
226void drawing_free(drawing *dr);
227void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
228 int align, int colour, char *text);
229void draw_rect(drawing *dr, int x, int y, int w, int h, int colour);
230void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour);
231void draw_polygon(drawing *dr, int *coords, int npoints,
232 int fillcolour, int outlinecolour);
233void draw_circle(drawing *dr, int cx, int cy, int radius,
234 int fillcolour, int outlinecolour);
235void draw_thick_line(drawing *dr, float thickness,
236 float x1, float y1, float x2, float y2, int colour);
237void clip(drawing *dr, int x, int y, int w, int h);
238void unclip(drawing *dr);
239void start_draw(drawing *dr);
240void draw_update(drawing *dr, int x, int y, int w, int h);
241void end_draw(drawing *dr);
242char *text_fallback(drawing *dr, const char *const *strings, int nstrings);
243void status_bar(drawing *dr, char *text);
244blitter *blitter_new(drawing *dr, int w, int h);
245void blitter_free(drawing *dr, blitter *bl);
246/* save puts the portion of the current display with top-left corner
247 * (x,y) to the blitter. load puts it back again to the specified
248 * coords, or else wherever it was saved from
249 * (if x = y = BLITTER_FROMSAVED). */
250void blitter_save(drawing *dr, blitter *bl, int x, int y);
251#define BLITTER_FROMSAVED (-1)
252void blitter_load(drawing *dr, blitter *bl, int x, int y);
253void print_begin_doc(drawing *dr, int pages);
254void print_begin_page(drawing *dr, int number);
255void print_begin_puzzle(drawing *dr, float xm, float xc,
256 float ym, float yc, int pw, int ph, float wmm,
257 float scale);
258void print_end_puzzle(drawing *dr);
259void print_end_page(drawing *dr, int number);
260void print_end_doc(drawing *dr);
261void print_get_colour(drawing *dr, int colour, int printing_in_colour,
262 int *hatch, float *r, float *g, float *b);
263int print_mono_colour(drawing *dr, int grey); /* 0==black, 1==white */
264int print_grey_colour(drawing *dr, float grey);
265int print_hatched_colour(drawing *dr, int hatch);
266int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int mono);
267int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey);
268int print_rgb_hatched_colour(drawing *dr, float r, float g, float b,
269 int hatch);
270void print_line_width(drawing *dr, int width);
271void print_line_dotted(drawing *dr, int dotted);
272
273/*
274 * midend.c
275 */
276midend *midend_new(frontend *fe, const game *ourgame,
277 const drawing_api *drapi, void *drhandle);
278void midend_free(midend *me);
279const game *midend_which_game(midend *me);
280void midend_set_params(midend *me, game_params *params);
281game_params *midend_get_params(midend *me);
282void midend_size(midend *me, int *x, int *y, int user_size);
283void midend_reset_tilesize(midend *me);
284void midend_new_game(midend *me);
285void midend_restart_game(midend *me);
286void midend_stop_anim(midend *me);
287int midend_process_key(midend *me, int x, int y, int button);
288void midend_force_redraw(midend *me);
289void midend_redraw(midend *me);
290float *midend_colours(midend *me, int *ncolours);
291void midend_freeze_timer(midend *me, float tprop);
292void midend_timer(midend *me, float tplus);
293struct preset_menu *midend_get_presets(midend *me, int *id_limit);
294int midend_which_preset(midend *me);
295int midend_wants_statusbar(midend *me);
296enum { CFG_SETTINGS, CFG_SEED, CFG_DESC, CFG_FRONTEND_SPECIFIC };
297config_item *midend_get_config(midend *me, int which, char **wintitle);
298char *midend_set_config(midend *me, int which, config_item *cfg);
299char *midend_game_id(midend *me, char *id);
300char *midend_get_game_id(midend *me);
301char *midend_get_random_seed(midend *me);
302int midend_can_format_as_text_now(midend *me);
303char *midend_text_format(midend *me);
304char *midend_solve(midend *me);
305int midend_status(midend *me);
306int midend_can_undo(midend *me);
307int midend_can_redo(midend *me);
308void midend_supersede_game_desc(midend *me, char *desc, char *privdesc);
309char *midend_rewrite_statusbar(midend *me, char *text);
310void midend_serialise(midend *me,
311 void (*write)(void *ctx, void *buf, int len),
312 void *wctx);
313char *midend_deserialise(midend *me,
314 int (*read)(void *ctx, void *buf, int len),
315 void *rctx);
316char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len),
317 void *rctx);
318void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx);
319/* Printing functions supplied by the mid-end */
320char *midend_print_puzzle(midend *me, document *doc, int with_soln);
321int midend_tilesize(midend *me);
322
323/*
324 * malloc.c
325 */
326void *smalloc(size_t size);
327void *srealloc(void *p, size_t size);
328void sfree(void *p);
329char *dupstr(const char *s);
330#define snew(type) \
331 ( (type *) smalloc (sizeof (type)) )
332#define snewn(number, type) \
333 ( (type *) smalloc ((number) * sizeof (type)) )
334#define sresize(array, number, type) \
335 ( (type *) srealloc ((array), (number) * sizeof (type)) )
336
337/*
338 * misc.c
339 */
340void free_cfg(config_item *cfg);
341void obfuscate_bitmap(unsigned char *bmp, int bits, int decode);
342
343/* allocates output each time. len is always in bytes of binary data.
344 * May assert (or just go wrong) if lengths are unchecked. */
345char *bin2hex(const unsigned char *in, int inlen);
346unsigned char *hex2bin(const char *in, int outlen);
347
348/* Sets (and possibly dims) background from frontend default colour,
349 * and auto-generates highlight and lowlight colours too. */
350void game_mkhighlight(frontend *fe, float *ret,
351 int background, int highlight, int lowlight);
352/* As above, but starts from a provided background colour rather
353 * than the frontend default. */
354void game_mkhighlight_specific(frontend *fe, float *ret,
355 int background, int highlight, int lowlight);
356
357/* Randomly shuffles an array of items. */
358void shuffle(void *array, int nelts, int eltsize, random_state *rs);
359
360/* Draw a rectangle outline, using the drawing API's draw_line. */
361void draw_rect_outline(drawing *dr, int x, int y, int w, int h,
362 int colour);
363
364/* Draw a set of rectangle corners (e.g. for a cursor display). */
365void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col);
366
367void move_cursor(int button, int *x, int *y, int maxw, int maxh, int wrap);
368
369/* Used in netslide.c and sixteen.c for cursor movement around edge. */
370int c2pos(int w, int h, int cx, int cy);
371int c2diff(int w, int h, int cx, int cy, int button);
372void pos2c(int w, int h, int pos, int *cx, int *cy);
373
374/* Draws text with an 'outline' formed by offsetting the text
375 * by one pixel; useful for highlighting. Outline is omitted if -1. */
376void draw_text_outline(drawing *dr, int x, int y, int fonttype,
377 int fontsize, int align,
378 int text_colour, int outline_colour, char *text);
379
380/* Copies text left-justified with spaces. Length of string must be
381 * less than buffer size. */
382void copy_left_justified(char *buf, size_t sz, const char *str);
383
384/*
385 * dsf.c
386 */
387int *snew_dsf(int size);
388
389void print_dsf(int *dsf, int size);
390
391/* Return the canonical element of the equivalence class containing element
392 * val. If 'inverse' is non-NULL, this function will put into it a flag
393 * indicating whether the canonical element is inverse to val. */
394int edsf_canonify(int *dsf, int val, int *inverse);
395int dsf_canonify(int *dsf, int val);
396int dsf_size(int *dsf, int val);
397
398/* Allow the caller to specify that two elements should be in the same
399 * equivalence class. If 'inverse' is TRUE, the elements are actually opposite
400 * to one another in some sense. This function will fail an assertion if the
401 * caller gives it self-contradictory data, ie if two elements are claimed to
402 * be both opposite and non-opposite. */
403void edsf_merge(int *dsf, int v1, int v2, int inverse);
404void dsf_merge(int *dsf, int v1, int v2);
405void dsf_init(int *dsf, int len);
406
407/*
408 * tdq.c
409 */
410
411/*
412 * Data structure implementing a 'to-do queue', a simple
413 * de-duplicating to-do list mechanism.
414 *
415 * Specification: a tdq is a queue which can hold integers from 0 to
416 * n-1, where n was some constant specified at tdq creation time. No
417 * integer may appear in the queue's current contents more than once;
418 * an attempt to add an already-present integer again will do nothing,
419 * so that that integer is removed from the queue at the position
420 * where it was _first_ inserted. The add and remove operations take
421 * constant time.
422 *
423 * The idea is that you might use this in applications like solvers:
424 * keep a tdq listing the indices of grid squares that you currently
425 * need to process in some way. Whenever you modify a square in a way
426 * that will require you to re-scan its neighbours, add them to the
427 * list with tdq_add; meanwhile you're constantly taking elements off
428 * the list when you need another square to process. In solvers where
429 * deductions are mostly localised, this should prevent repeated
430 * O(N^2) loops over the whole grid looking for something to do. (But
431 * if only _most_ of the deductions are localised, then you should
432 * respond to an empty to-do list by re-adding everything using
433 * tdq_fill, so _then_ you rescan the whole grid looking for newly
434 * enabled non-local deductions. Only if you've done that and emptied
435 * the list again finding nothing new to do are you actually done.)
436 */
437typedef struct tdq tdq;
438tdq *tdq_new(int n);
439void tdq_free(tdq *tdq);
440void tdq_add(tdq *tdq, int k);
441int tdq_remove(tdq *tdq); /* returns -1 if nothing available */
442void tdq_fill(tdq *tdq); /* add everything to the tdq at once */
443
444/*
445 * laydomino.c
446 */
447int *domino_layout(int w, int h, random_state *rs);
448void domino_layout_prealloc(int w, int h, random_state *rs,
449 int *grid, int *grid2, int *list);
450/*
451 * version.c
452 */
453extern char ver[];
454
455/*
456 * random.c
457 */
458random_state *random_new(const char *seed, int len);
459random_state *random_copy(random_state *tocopy);
460unsigned long random_bits(random_state *state, int bits);
461unsigned long random_upto(random_state *state, unsigned long limit);
462void random_free(random_state *state);
463char *random_state_encode(random_state *state);
464random_state *random_state_decode(const char *input);
465/* random.c also exports SHA, which occasionally comes in useful. */
466#if __STDC_VERSION__ >= 199901L
467#include <stdint.h>
468typedef uint32_t uint32;
469#elif UINT_MAX >= 4294967295L
470typedef unsigned int uint32;
471#else
472typedef unsigned long uint32;
473#endif
474typedef struct {
475 uint32 h[5];
476 unsigned char block[64];
477 int blkused;
478 uint32 lenhi, lenlo;
479} SHA_State;
480void SHA_Init(SHA_State *s);
481void SHA_Bytes(SHA_State *s, const void *p, int len);
482void SHA_Final(SHA_State *s, unsigned char *output);
483void SHA_Simple(const void *p, int len, unsigned char *output);
484
485/*
486 * printing.c
487 */
488document *document_new(int pw, int ph, float userscale);
489void document_free(document *doc);
490void document_add_puzzle(document *doc, const game *game, game_params *par,
491 game_state *st, game_state *st2);
492void document_print(document *doc, drawing *dr);
493
494/*
495 * ps.c
496 */
497psdata *ps_init(FILE *outfile, int colour);
498void ps_free(psdata *ps);
499drawing *ps_drawing_api(psdata *ps);
500
501/*
502 * combi.c: provides a structure and functions for iterating over
503 * combinations (i.e. choosing r things out of n).
504 */
505typedef struct _combi_ctx {
506 int r, n, nleft, total;
507 int *a;
508} combi_ctx;
509
510combi_ctx *new_combi(int r, int n);
511void reset_combi(combi_ctx *combi);
512combi_ctx *next_combi(combi_ctx *combi); /* returns NULL for end */
513void free_combi(combi_ctx *combi);
514
515/*
516 * divvy.c
517 */
518/* divides w*h rectangle into pieces of size k. Returns w*h dsf. */
519int *divvy_rectangle(int w, int h, int k, random_state *rs);
520
521/*
522 * findloop.c
523 */
524struct findloopstate;
525struct findloopstate *findloop_new_state(int nvertices);
526void findloop_free_state(struct findloopstate *);
527/*
528 * Callback provided by the client code to enumerate the graph
529 * vertices joined directly to a given vertex.
530 *
531 * Semantics: if vertex >= 0, return one of its neighbours; if vertex
532 * < 0, return a previously unmentioned neighbour of whatever vertex
533 * was last passed as input. Write to 'ctx' as necessary to store
534 * state. In either case, return < 0 if no such vertex can be found.
535 */
536typedef int (*neighbour_fn_t)(int vertex, void *ctx);
537/*
538 * Actual function to find loops. 'ctx' will be passed unchanged to
539 * the 'neighbour' function to query graph edges. Returns FALSE if no
540 * loop was found, or TRUE if one was.
541 */
542int findloop_run(struct findloopstate *state, int nvertices,
543 neighbour_fn_t neighbour, void *ctx);
544/*
545 * Query whether an edge is part of a loop, in the output of
546 * find_loops.
547 *
548 * Due to the internal storage format, if you pass u,v which are not
549 * connected at all, the output will be TRUE. (The algorithm actually
550 * stores an exhaustive list of *non*-loop edges, because there are
551 * fewer of those, so really it's querying whether the edge is on that
552 * list.)
553 */
554int findloop_is_loop_edge(struct findloopstate *state, int u, int v);
555
556/*
557 * Data structure containing the function calls and data specific
558 * to a particular game. This is enclosed in a data structure so
559 * that a particular platform can choose, if it wishes, to compile
560 * all the games into a single combined executable rather than
561 * having lots of little ones.
562 */
563struct game {
564 const char *name;
565 const char *winhelp_topic, *htmlhelp_topic;
566 game_params *(*default_params)(void);
567 int (*fetch_preset)(int i, char **name, game_params **params);
568 struct preset_menu *(*preset_menu)(void);
569 void (*decode_params)(game_params *, char const *string);
570 char *(*encode_params)(const game_params *, int full);
571 void (*free_params)(game_params *params);
572 game_params *(*dup_params)(const game_params *params);
573 int can_configure;
574 config_item *(*configure)(const game_params *params);
575 game_params *(*custom_params)(const config_item *cfg);
576 char *(*validate_params)(const game_params *params, int full);
577 char *(*new_desc)(const game_params *params, random_state *rs,
578 char **aux, int interactive);
579 char *(*validate_desc)(const game_params *params, const char *desc);
580 game_state *(*new_game)(midend *me, const game_params *params,
581 const char *desc);
582 game_state *(*dup_game)(const game_state *state);
583 void (*free_game)(game_state *state);
584 int can_solve;
585 char *(*solve)(const game_state *orig, const game_state *curr,
586 const char *aux, char **error);
587 int can_format_as_text_ever;
588 int (*can_format_as_text_now)(const game_params *params);
589 char *(*text_format)(const game_state *state);
590 game_ui *(*new_ui)(const game_state *state);
591 void (*free_ui)(game_ui *ui);
592 char *(*encode_ui)(const game_ui *ui);
593 void (*decode_ui)(game_ui *ui, const char *encoding);
594 void (*changed_state)(game_ui *ui, const game_state *oldstate,
595 const game_state *newstate);
596 char *(*interpret_move)(const game_state *state, game_ui *ui,
597 const game_drawstate *ds, int x, int y, int button);
598 game_state *(*execute_move)(const game_state *state, const char *move);
599 int preferred_tilesize;
600 void (*compute_size)(const game_params *params, int tilesize,
601 int *x, int *y);
602 void (*set_size)(drawing *dr, game_drawstate *ds,
603 const game_params *params, int tilesize);
604 float *(*colours)(frontend *fe, int *ncolours);
605 game_drawstate *(*new_drawstate)(drawing *dr, const game_state *state);
606 void (*free_drawstate)(drawing *dr, game_drawstate *ds);
607 void (*redraw)(drawing *dr, game_drawstate *ds, const game_state *oldstate,
608 const game_state *newstate, int dir, const game_ui *ui,
609 float anim_time, float flash_time);
610 float (*anim_length)(const game_state *oldstate,
611 const game_state *newstate, int dir, game_ui *ui);
612 float (*flash_length)(const game_state *oldstate,
613 const game_state *newstate, int dir, game_ui *ui);
614 int (*status)(const game_state *state);
615 int can_print, can_print_in_colour;
616 void (*print_size)(const game_params *params, float *x, float *y);
617 void (*print)(drawing *dr, const game_state *state, int tilesize);
618 int wants_statusbar;
619 int is_timed;
620 int (*timing_state)(const game_state *state, game_ui *ui);
621 int flags;
622};
623
624/*
625 * Data structure containing the drawing API implemented by the
626 * front end and also by cross-platform printing modules such as
627 * PostScript.
628 */
629struct drawing_api {
630 void (*draw_text)(void *handle, int x, int y, int fonttype, int fontsize,
631 int align, int colour, char *text);
632 void (*draw_rect)(void *handle, int x, int y, int w, int h, int colour);
633 void (*draw_line)(void *handle, int x1, int y1, int x2, int y2,
634 int colour);
635 void (*draw_polygon)(void *handle, int *coords, int npoints,
636 int fillcolour, int outlinecolour);
637 void (*draw_circle)(void *handle, int cx, int cy, int radius,
638 int fillcolour, int outlinecolour);
639 void (*draw_update)(void *handle, int x, int y, int w, int h);
640 void (*clip)(void *handle, int x, int y, int w, int h);
641 void (*unclip)(void *handle);
642 void (*start_draw)(void *handle);
643 void (*end_draw)(void *handle);
644 void (*status_bar)(void *handle, char *text);
645 blitter *(*blitter_new)(void *handle, int w, int h);
646 void (*blitter_free)(void *handle, blitter *bl);
647 void (*blitter_save)(void *handle, blitter *bl, int x, int y);
648 void (*blitter_load)(void *handle, blitter *bl, int x, int y);
649 void (*begin_doc)(void *handle, int pages);
650 void (*begin_page)(void *handle, int number);
651 void (*begin_puzzle)(void *handle, float xm, float xc,
652 float ym, float yc, int pw, int ph, float wmm);
653 void (*end_puzzle)(void *handle);
654 void (*end_page)(void *handle, int number);
655 void (*end_doc)(void *handle);
656 void (*line_width)(void *handle, float width);
657 void (*line_dotted)(void *handle, int dotted);
658 char *(*text_fallback)(void *handle, const char *const *strings,
659 int nstrings);
660 void (*draw_thick_line)(void *handle, float thickness,
661 float x1, float y1, float x2, float y2,
662 int colour);
663};
664
665/*
666 * For one-game-at-a-time platforms, there's a single structure
667 * like the above, under a fixed name. For all-at-once platforms,
668 * there's a list of all available puzzles in array form.
669 */
670#ifdef COMBINED
671extern const game *gamelist[];
672extern const int gamecount;
673#else
674extern const game thegame;
675#endif
676
677/* A little bit of help to lazy developers */
678#define DEFAULT_STATUSBAR_TEXT "Use status_bar() to fill this in."
679
680#endif /* PUZZLES_PUZZLES_H */
diff --git a/apps/plugins/puzzles/src/puzzles.hlp b/apps/plugins/puzzles/src/puzzles.hlp
new file mode 100644
index 0000000000..f59292eb20
--- /dev/null
+++ b/apps/plugins/puzzles/src/puzzles.hlp
Binary files differ
diff --git a/apps/plugins/puzzles/src/puzzles.rc2 b/apps/plugins/puzzles/src/puzzles.rc2
new file mode 100644
index 0000000000..4442a9b138
--- /dev/null
+++ b/apps/plugins/puzzles/src/puzzles.rc2
@@ -0,0 +1,65 @@
1/* Standard stuff that goes into the Windows resources for all puzzles. */
2
3#ifdef _WIN32_WCE
4
5#include <winuser.h>
6#include <commctrl.h>
7
8#define SHMENUBAR RCDATA
9#define I_IMAGENONE (-2)
10#define IDC_STATIC (-1)
11
12#include "resource.h"
13
14IDR_MENUBAR1 MENU DISCARDABLE
15BEGIN
16 POPUP "Game"
17 BEGIN
18 MENUITEM "Dummy", 0
19 END
20 POPUP "Type"
21 BEGIN
22 MENUITEM "Dummy", 0
23 END
24END
25
26IDR_MENUBAR1 SHMENUBAR DISCARDABLE
27BEGIN
28 IDR_MENUBAR1, 2,
29 I_IMAGENONE, ID_GAME, TBSTATE_ENABLED,
30 TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_GAME, 0, 0,
31 I_IMAGENONE, ID_TYPE, TBSTATE_ENABLED,
32 TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_TYPE, 0, 1,
33END
34
35STRINGTABLE DISCARDABLE
36BEGIN
37 IDS_CAP_GAME "Game"
38 IDS_CAP_TYPE "Type"
39END
40
41IDD_ABOUT DIALOG DISCARDABLE 0, 0, 0, 0
42STYLE WS_POPUP
43FONT 8, "Tahoma"
44BEGIN
45 LTEXT "", IDC_ABOUT_CAPTION, 4, 4, 150, 8
46 LTEXT "", IDC_ABOUT_LINE, 4, 16, 150, 1, WS_BORDER
47 LTEXT "", IDC_ABOUT_GAME, 4, 22, 150, 8
48 LTEXT "from Simon Tatham's Portable Puzzle Collection",
49 IDC_STATIC, 4, 36, 150, 8, SS_LEFTNOWORDWRAP
50 LTEXT "Pocket PC port by Darek Olszewski",
51 IDC_STATIC, 4, 46, 150, 8
52 LTEXT "", IDC_ABOUT_VERSION, 4, 60, 150, 8
53END
54
55IDD_CONFIG DIALOG DISCARDABLE 0, 0, 0, 0
56STYLE WS_POPUP
57FONT 8, "Tahoma"
58BEGIN
59 LTEXT "", IDC_CONFIG_CAPTION, 4, 4, 150, 8
60 LTEXT "", IDC_CONFIG_LINE, 4, 16, 150, 1, WS_BORDER
61END
62
63IDR_PADTOOLBAR BITMAP DISCARDABLE "padtoolbar.bmp"
64
65#endif /* _WIN32_WCE */
diff --git a/apps/plugins/puzzles/src/puzzles.txt b/apps/plugins/puzzles/src/puzzles.txt
new file mode 100644
index 0000000000..88d83f791d
--- /dev/null
+++ b/apps/plugins/puzzles/src/puzzles.txt
@@ -0,0 +1,3163 @@
1 Simon Tatham's Portable Puzzle Collection
2 =========================================
3
4This is a collection of small one-player puzzle games.
5
6This manual is copyright 2004-2014 Simon Tatham. All rights reserved. You
7may distribute this documentation under the MIT licence. See appendix A for
8the licence text in full.
9
10Chapter 1: Introduction
11-----------------------
12
13 I wrote this collection because I thought there should be more small
14 desktop toys available: little games you can pop up in a window and
15 play for two or three minutes while you take a break from whatever
16 else you were doing. And I was also annoyed that every time I found
17 a good game on (say) Unix, it wasn't available the next time I was
18 sitting at a Windows machine, or vice versa; so I arranged that
19 everything in my personal puzzle collection will happily run on
20 both, and have more recently done a port to Mac OS X as well. When I
21 find (or perhaps invent) further puzzle games that I like, they'll
22 be added to this collection and will immediately be available on
23 both platforms. And if anyone feels like writing any other front
24 ends - PocketPC, Mac OS pre-10, or whatever it might be - then all
25 the games in this framework will immediately become available on
26 another platform as well.
27
28 The actual games in this collection were mostly not my invention;
29 they are re-implementations of existing game concepts within my
30 portable puzzle framework. I do not claim credit, in general, for
31 inventing the rules of any of these puzzles. (I don't even claim
32 authorship of all the code; some of the puzzles have been submitted
33 by other authors.)
34
35 This collection is distributed under the MIT licence (see appendix
36 A). This means that you can do pretty much anything you like with
37 the game binaries or the code, except pretending you wrote them
38 yourself, or suing me if anything goes wrong.
39
40 The most recent versions, and source code, can be found at
41 http://www.chiark.greenend.org.uk/~sgtatham/puzzles/.
42
43 Please report bugs to anakin@pobox.com. You might find it helpful to
44 read this article before reporting a bug:
45
46 http://www.chiark.greenend.org.uk/~sgtatham/bugs.html
47
48 Patches are welcome. Especially if they provide a new front end (to
49 make all these games run on another platform), or a new game.
50
51Chapter 2: Common features
52--------------------------
53
54 This chapter describes features that are common to all the games.
55
56 2.1 Common actions
57
58 These actions are all available from the `Game' menu and via
59 keyboard shortcuts, in addition to any game-specific actions.
60
61 (On Mac OS X, to conform with local user interface standards, these
62 actions are situated on the `File' and `Edit' menus instead.)
63
64 _New game_ (`N', Ctrl+`N')
65
66 Starts a new game, with a random initial state.
67
68 _Restart game_
69
70 Resets the current game to its initial state. (This can be
71 undone.)
72
73 _Load_
74
75 Loads a saved game from a file on disk.
76
77 _Save_
78
79 Saves the current state of your game to a file on disk.
80
81 The Load and Save operations preserve your entire game history
82 (so you can save, reload, and still Undo and Redo things you had
83 done before saving).
84
85 _Print_
86
87 Where supported (currently only on Windows), brings up a dialog
88 allowing you to print an arbitrary number of puzzles randomly
89 generated from the current parameters, optionally including
90 the current puzzle. (Only for puzzles which make sense to
91 print, of course - it's hard to think of a sensible printable
92 representation of Fifteen!)
93
94 _Undo_ (`U', Ctrl+`Z', Ctrl+`_')
95
96 Undoes a single move. (You can undo moves back to the start of
97 the session.)
98
99 _Redo_ (`R', Ctrl+`R')
100
101 Redoes a previously undone move.
102
103 _Copy_
104
105 Copies the current state of your game to the clipboard in text
106 format, so that you can paste it into (say) an e-mail client or
107 a web message board if you're discussing the game with someone
108 else. (Not all games support this feature.)
109
110 _Solve_
111
112 Transforms the puzzle instantly into its solved state. For some
113 games (Cube) this feature is not supported at all because it is
114 of no particular use. For other games (such as Pattern), the
115 solved state can be used to give you information, if you can't
116 see how a solution can exist at all or you want to know where
117 you made a mistake. For still other games (such as Sixteen),
118 automatic solution tells you nothing about how to _get_ to
119 the solution, but it does provide a useful way to get there
120 quickly so that you can experiment with set-piece moves and
121 transformations.
122
123 Some games (such as Solo) are capable of solving a game ID you
124 have typed in from elsewhere. Other games (such as Rectangles)
125 cannot solve a game ID they didn't invent themself, but when
126 they did invent the game ID they know what the solution is
127 already. Still other games (Pattern) can solve _some_ external
128 game IDs, but only if they aren't too difficult.
129
130 The `Solve' command adds the solved state to the end of the undo
131 chain for the puzzle. In other words, if you want to go back to
132 solving it yourself after seeing the answer, you can just press
133 Undo.
134
135 _Quit_ (`Q', Ctrl+`Q')
136
137 Closes the application entirely.
138
139 2.2 Specifying games with the game ID
140
141 There are two ways to save a game specification out of a puzzle and
142 recreate it later, or recreate it in somebody else's copy of the
143 same puzzle.
144
145 The `Specific' and `Random Seed' options from the `Game' menu (or
146 the `File' menu, on Mac OS X) each show a piece of text (a `game
147 ID') which is sufficient to reconstruct precisely the same game at a
148 later date.
149
150 You can enter either of these pieces of text back into the program
151 (via the same `Specific' or `Random Seed' menu options) at a later
152 point, and it will recreate the same game. You can also use either
153 one as a command line argument (on Windows or Unix); see section 2.4
154 for more detail.
155
156 The difference between the two forms is that a descriptive game ID
157 is a literal _description_ of the initial state of the game, whereas
158 a random seed is just a piece of arbitrary text which was provided
159 as input to the random number generator used to create the puzzle.
160 This means that:
161
162 - Descriptive game IDs tend to be longer in many puzzles
163 (although some, such as Cube (chapter 4), only need very short
164 descriptions). So a random seed is often a _quicker_ way to
165 note down the puzzle you're currently playing, or to tell it to
166 somebody else so they can play the same one as you.
167
168 - Any text at all is a valid random seed. The automatically
169 generated ones are fifteen-digit numbers, but anything will do;
170 you can type in your full name, or a word you just made up, and
171 a valid puzzle will be generated from it. This provides a way
172 for two or more people to race to complete the same puzzle:
173 you think of a random seed, then everybody types it in at the
174 same time, and nobody has an advantage due to having seen the
175 generated puzzle before anybody else.
176
177 - It is often possible to convert puzzles from other sources (such
178 as `nonograms' or `sudoku' from newspapers) into descriptive
179 game IDs suitable for use with these programs.
180
181 - Random seeds are not guaranteed to produce the same result
182 if you use them with a different _version_ of the puzzle
183 program. This is because the generation algorithm might have
184 been improved or modified in later versions of the code, and
185 will therefore produce a different result when given the same
186 sequence of random numbers. Use a descriptive game ID if you
187 aren't sure that it will be used on the same version of the
188 program as yours.
189
190 (Use the `About' menu option to find out the version number of
191 the program. Programs with the same version number running on
192 different platforms should still be random-seed compatible.)
193
194 A descriptive game ID starts with a piece of text which encodes the
195 _parameters_ of the current game (such as grid size). Then there is
196 a colon, and after that is the description of the game's initial
197 state. A random seed starts with a similar string of parameters, but
198 then it contains a hash sign followed by arbitrary data.
199
200 If you enter a descriptive game ID, the program will not be able
201 to show you the random seed which generated it, since it wasn't
202 generated _from_ a random seed. If you _enter_ a random seed,
203 however, the program will be able to show you the descriptive game
204 ID derived from that random seed.
205
206 Note that the game parameter strings are not always identical
207 between the two forms. For some games, there will be parameter
208 data provided with the random seed which is not included in the
209 descriptive game ID. This is because that parameter information is
210 only relevant when _generating_ puzzle grids, and is not important
211 when playing them. Thus, for example, the difficulty level in Solo
212 (chapter 11) is not mentioned in the descriptive game ID.
213
214 These additional parameters are also not set permanently if you type
215 in a game ID. For example, suppose you have Solo set to `Advanced'
216 difficulty level, and then a friend wants your help with a `Trivial'
217 puzzle; so the friend reads out a random seed specifying `Trivial'
218 difficulty, and you type it in. The program will generate you the
219 same `Trivial' grid which your friend was having trouble with, but
220 once you have finished playing it, when you ask for a new game it
221 will automatically go back to the `Advanced' difficulty which it was
222 previously set on.
223
224 2.3 The `Type' menu
225
226 The `Type' menu, if present, may contain a list of preset game
227 settings. Selecting one of these will start a new random game with
228 the parameters specified.
229
230 The `Type' menu may also contain a `Custom' option which allows you
231 to fine-tune game parameters. The parameters available are specific
232 to each game and are described in the following sections.
233
234 2.4 Specifying game parameters on the command line
235
236 (This section does not apply to the Mac OS X version.)
237
238 The games in this collection deliberately do not ever save
239 information on to the computer they run on: they have no high score
240 tables and no saved preferences. (This is because I expect at least
241 some people to play them at work, and those people will probably
242 appreciate leaving as little evidence as possible!)
243
244 However, if you do want to arrange for one of these games to default
245 to a particular set of parameters, you can specify them on the
246 command line.
247
248 The easiest way to do this is to set up the parameters you want
249 using the `Type' menu (see section 2.3), and then to select `Random
250 Seed' from the `Game' or `File' menu (see section 2.2). The text
251 in the `Game ID' box will be composed of two parts, separated by a
252 hash. The first of these parts represents the game parameters (the
253 size of the playing area, for example, and anything else you set
254 using the `Type' menu).
255
256 If you run the game with just that parameter text on the command
257 line, it will start up with the settings you specified.
258
259 For example: if you run Cube (see chapter 4), select `Octahedron'
260 from the `Type' menu, and then go to the game ID selection, you
261 will see a string of the form `o2x2#338686542711620'. Take only the
262 part before the hash (`o2x2'), and start Cube with that text on the
263 command line: `PREFIX-cube o2x2'.
264
265 If you copy the _entire_ game ID on to the command line, the game
266 will start up in the specific game that was described. This is
267 occasionally a more convenient way to start a particular game ID
268 than by pasting it into the game ID selection box.
269
270 (You could also retrieve the encoded game parameters using the
271 `Specific' menu option instead of `Random Seed', but if you do then
272 some options, such as the difficulty level in Solo, will be missing.
273 See section 2.2 for more details on this.)
274
275 2.5 Unix command-line options
276
277 (This section only applies to the Unix port.)
278
279 In addition to being able to specify game parameters on the command
280 line (see section 2.4), there are various other options:
281
282 --game
283
284 --load
285
286 These options respectively determine whether the command-line
287 argument is treated as specifying game parameters or a save
288 file to load. Only one should be specified. If neither of these
289 options is specified, a guess is made based on the format of the
290 argument.
291
292 --generate _n_
293
294 If this option is specified, instead of a puzzle being
295 displayed, a number of descriptive game IDs will be invented and
296 printed on standard output. This is useful for gaining access
297 to the game generation algorithms without necessarily using the
298 frontend.
299
300 If game parameters are specified on the command-line, they will
301 be used to generate the game IDs; otherwise a default set of
302 parameters will be used.
303
304 The most common use of this option is in conjunction with `--
305 print', in which case its behaviour is slightly different; see
306 below.
307
308 --print _w_x_h_
309
310 If this option is specified, instead of a puzzle being
311 displayed, a printed representation of one or more unsolved
312 puzzles is sent to standard output, in PostScript format.
313
314 On each page of puzzles, there will be _w_ across and _h_ down.
315 If there are more puzzles than _w_x_h_, more than one page will
316 be printed.
317
318 If `--generate' has also been specified, the invented game
319 IDs will be used to generate the printed output. Otherwise,
320 a list of game IDs is expected on standard input (which can
321 be descriptive or random seeds; see section 2.2), in the same
322 format produced by `--generate'.
323
324 For example:
325
326 PREFIX-net --generate 12 --print 2x3 7x7w | lpr
327
328 will generate two pages of printed Net puzzles (each of which
329 will have a 7x7 wrapping grid), and pipe the output to the `lpr'
330 command, which on many systems will send them to an actual
331 printer.
332
333 There are various other options which affect printing; see
334 below.
335
336 --save _file-prefix_ [ --save-suffix _file-suffix_ ]
337
338 If this option is specified, instead of a puzzle being
339 displayed, saved-game files for one or more unsolved puzzles are
340 written to files constructed from the supplied prefix and/or
341 suffix.
342
343 If `--generate' has also been specified, the invented game
344 IDs will be used to generate the printed output. Otherwise,
345 a list of game IDs is expected on standard input (which can
346 be descriptive or random seeds; see section 2.2), in the same
347 format produced by `--generate'.
348
349 For example:
350
351 PREFIX-net --generate 12 --save game --save-suffix .sav
352
353 will generate twelve Net saved-game files with the names
354 game0.sav to game11.sav.
355
356 --version
357
358 Prints version information about the game, and then quits.
359
360 The following options are only meaningful if `--print' is also
361 specified:
362
363 --with-solutions
364
365 The set of pages filled with unsolved puzzles will be followed
366 by the solutions to those puzzles.
367
368 --scale _n_
369
370 Adjusts how big each puzzle is when printed. Larger numbers make
371 puzzles bigger; the default is 1.0.
372
373 --colour
374
375 Puzzles will be printed in colour, rather than in black and
376 white (if supported by the puzzle).
377
378Chapter 3: Net
379--------------
380
381 (_Note:_ the Windows version of this game is called NETGAME.EXE to
382 avoid clashing with Windows's own NET.EXE.)
383
384 I originally saw this in the form of a Flash game called
385 FreeNet [1], written by Pavils Jurjans; there are several other
386 implementations under the name NetWalk. The computer prepares a
387 network by connecting up the centres of squares in a grid, and then
388 shuffles the network by rotating every tile randomly. Your job is
389 to rotate it all back into place. The successful solution will be
390 an entirely connected network, with no closed loops. As a visual
391 aid, all tiles which are connected to the one in the middle are
392 highlighted.
393
394 [1] http://www.jurjans.lv/stuff/net/FreeNet.htm
395
396 3.1 Net controls
397
398 This game can be played with either the keyboard or the mouse. The
399 controls are:
400
401 _Select tile_: mouse pointer, arrow keys
402
403 _Rotate tile anticlockwise_: left mouse button, `A' key
404
405 _Rotate tile clockwise_: right mouse button, `D' key
406
407 _Rotate tile by 180 degrees_: `F' key
408
409 _Lock (or unlock) tile_: middle mouse button, shift-click, `S' key
410
411 You can lock a tile once you're sure of its orientation. You
412 can also unlock it again, but while it's locked you can't
413 accidentally turn it.
414
415 The following controls are not necessary to complete the game, but
416 may be useful:
417
418 _Shift grid_: Shift + arrow keys
419
420 On grids that wrap, you can move the origin of the grid, so
421 that tiles that were on opposite sides of the grid can be seen
422 together.
423
424 _Move centre_: Ctrl + arrow keys
425
426 You can change which tile is used as the source of highlighting.
427 (It doesn't ultimately matter which tile this is, as every tile
428 will be connected to every other tile in a correct solution,
429 but it may be helpful in the intermediate stages of solving the
430 puzzle.)
431
432 _Jumble tiles_: `J' key
433
434 This key turns all tiles that are not locked to random
435 orientations.
436
437 (All the actions described in section 2.1 are also available.)
438
439 3.2 Net parameters
440
441 These parameters are available from the `Custom...' option on the
442 `Type' menu.
443
444 _Width_, _Height_
445
446 Size of grid in tiles.
447
448 _Walls wrap around_
449
450 If checked, flow can pass from the left edge to the right edge,
451 and from top to bottom, and vice versa.
452
453 _Barrier probability_
454
455 A number between 0.0 and 1.0 controlling whether an immovable
456 barrier is placed between two tiles to prevent flow between
457 them (a higher number gives more barriers). Since barriers
458 are immovable, they act as constraints on the solution (i.e.,
459 hints).
460
461 The grid generation in Net has been carefully arranged so that
462 the barriers are independent of the rest of the grid. This
463 means that if you note down the random seed used to generate
464 the current puzzle (see section 2.2), change the _Barrier
465 probability_ parameter, and then re-enter the same random seed,
466 you should see exactly the same starting grid, with the only
467 change being the number of barriers. So if you're stuck on a
468 particular grid and need a hint, you could start up another
469 instance of Net, set up the same parameters but a higher barrier
470 probability, and enter the game seed from the original Net
471 window.
472
473 _Ensure unique solution_
474
475 Normally, Net will make sure that the puzzles it presents have
476 only one solution. Puzzles with ambiguous sections can be more
477 difficult and more subtle, so if you like you can turn off this
478 feature and risk having ambiguous puzzles. (Also, finding _all_
479 the possible solutions can be an additional challenge for an
480 advanced player.)
481
482Chapter 4: Cube
483---------------
484
485 This is another one I originally saw as a web game. This one was a
486 Java game [2], by Paul Scott. You have a grid of 16 squares, six of
487 which are blue; on one square rests a cube. Your move is to use the
488 arrow keys to roll the cube through 90 degrees so that it moves to
489 an adjacent square. If you roll the cube on to a blue square, the
490 blue square is picked up on one face of the cube; if you roll a blue
491 face of the cube on to a non-blue square, the blueness is put down
492 again. (In general, whenever you roll the cube, the two faces that
493 come into contact swap colours.) Your job is to get all six blue
494 squares on to the six faces of the cube at the same time. Count your
495 moves and try to do it in as few as possible.
496
497 Unlike the original Java game, my version has an additional feature:
498 once you've mastered the game with a cube rolling on a square grid,
499 you can change to a triangular grid and roll any of a tetrahedron,
500 an octahedron or an icosahedron.
501
502 [2] http://www3.sympatico.ca/paulscott/cube/cube.htm
503
504 4.1 Cube controls
505
506 This game can be played with either the keyboard or the mouse.
507
508 Left-clicking anywhere on the window will move the cube (or other
509 solid) towards the mouse pointer.
510
511 The arrow keys can also used to roll the cube on its square grid in
512 the four cardinal directions. On the triangular grids, the mapping
513 of arrow keys to directions is more approximate. Vertical movement
514 is disallowed where it doesn't make sense. The four keys surrounding
515 the arrow keys on the numeric keypad (`7', `9', `1', `3') can be
516 used for diagonal movement.
517
518 (All the actions described in section 2.1 are also available.)
519
520 4.2 Cube parameters
521
522 These parameters are available from the `Custom...' option on the
523 `Type' menu.
524
525 _Type of solid_
526
527 Selects the solid to roll (and hence the shape of the grid):
528 tetrahedron, cube, octahedron, or icosahedron.
529
530 _Width / top_, _Height / bottom_
531
532 On a square grid, horizontal and vertical dimensions. On a
533 triangular grid, the number of triangles on the top and bottom
534 rows respectively.
535
536Chapter 5: Fifteen
537------------------
538
539 The old ones are the best: this is the good old `15-puzzle' with
540 sliding tiles. You have a 4x4 square grid; 15 squares contain
541 numbered tiles, and the sixteenth is empty. Your move is to choose a
542 tile next to the empty space, and slide it into the space. The aim
543 is to end up with the tiles in numerical order, with the space in
544 the bottom right (so that the top row reads 1,2,3,4 and the bottom
545 row reads 13,14,15,_space_).
546
547 5.1 Fifteen controls
548
549 This game can be controlled with the mouse or the keyboard.
550
551 A left-click with the mouse in the row or column containing the
552 empty space will move as many tiles as necessary to move the space
553 to the mouse pointer.
554
555 The arrow keys will move a tile adjacent to the space in the
556 direction indicated (moving the space in the _opposite_ direction).
557
558 Pressing `h' will make a suggested move. Pressing `h' enough times
559 will solve the game, but it may scramble your progress while doing
560 so.
561
562 (All the actions described in section 2.1 are also available.)
563
564 5.2 Fifteen parameters
565
566 The only options available from the `Custom...' option on the `Type'
567 menu are _Width_ and _Height_, which are self-explanatory. (Once
568 you've changed these, it's not a `15-puzzle' any more, of course!)
569
570Chapter 6: Sixteen
571------------------
572
573 Another sliding tile puzzle, visually similar to Fifteen (see
574 chapter 5) but with a different type of move. This time, there is no
575 hole: all 16 squares on the grid contain numbered squares. Your move
576 is to shift an entire row left or right, or shift an entire column
577 up or down; every time you do that, the tile you shift off the grid
578 re-appears at the other end of the same row, in the space you just
579 vacated. To win, arrange the tiles into numerical order (1,2,3,4 on
580 the top row, 13,14,15,16 on the bottom). When you've done that, try
581 playing on different sizes of grid.
582
583 I _might_ have invented this game myself, though only by accident
584 if so (and I'm sure other people have independently invented it). I
585 thought I was imitating a screensaver I'd seen, but I have a feeling
586 that the screensaver might actually have been a Fifteen-type puzzle
587 rather than this slightly different kind. So this might be the one
588 thing in my puzzle collection which represents creativity on my part
589 rather than just engineering.
590
591 6.1 Sixteen controls
592
593 Left-clicking on an arrow will move the appropriate row or column in
594 the direction indicated. Right-clicking will move it in the opposite
595 direction.
596
597 Alternatively, use the cursor keys to move the position indicator
598 around the edge of the grid, and use the return key to move the
599 row/column in the direction indicated.
600
601 You can also move the tiles directly. Move the cursor onto a tile,
602 hold Control and press an arrow key to move the tile under the
603 cursor and move the cursor along with the tile. Or, hold Shift to
604 move only the tile. Pressing Enter simulates holding down Control
605 (press Enter again to release), while pressing Space simulates
606 holding down shift.
607
608 (All the actions described in section 2.1 are also available.)
609
610 6.2 Sixteen parameters
611
612 The parameters available from the `Custom...' option on the `Type'
613 menu are:
614
615 - _Width_ and _Height_, which are self-explanatory.
616
617 - You can ask for a limited shuffling operation to be performed on
618 the grid. By default, Sixteen will shuffle the grid in such a
619 way that any arrangement is about as probable as any other. You
620 can override this by requesting a precise number of shuffling
621 moves to be performed. Typically your aim is then to determine
622 the precise set of shuffling moves and invert them exactly,
623 so that you answer (say) a four-move shuffle with a four-move
624 solution. Note that the more moves you ask for, the more likely
625 it is that solutions shorter than the target length will turn
626 out to be possible.
627
628Chapter 7: Twiddle
629------------------
630
631 Twiddle is a tile-rearrangement puzzle, visually similar to Sixteen
632 (see chapter 6): you are given a grid of square tiles, each
633 containing a number, and your aim is to arrange the numbers into
634 ascending order.
635
636 In basic Twiddle, your move is to rotate a square group of four
637 tiles about their common centre. (Orientation is not significant
638 in the basic puzzle, although you can select it.) On more advanced
639 settings, you can rotate a larger square group of tiles.
640
641 I first saw this type of puzzle in the GameCube game `Metroid
642 Prime 2'. In the Main Gyro Chamber in that game, there is a puzzle
643 you solve to unlock a door, which is a special case of Twiddle. I
644 developed this game as a generalisation of that puzzle.
645
646 7.1 Twiddle controls
647
648 To play Twiddle, click the mouse in the centre of the square group
649 you wish to rotate. In the basic mode, you rotate a 2x2 square,
650 which means you have to click at a corner point where four tiles
651 meet.
652
653 In more advanced modes you might be rotating 3x3 or even more at a
654 time; if the size of the square is odd then you simply click in the
655 centre tile of the square you want to rotate.
656
657 Clicking with the left mouse button rotates the group anticlockwise.
658 Clicking with the right button rotates it clockwise.
659
660 You can also move an outline square around the grid with the cursor
661 keys; the square is the size above (2x2 by default, or larger).
662 Pressing the return key or space bar will rotate the current square
663 anticlockwise or clockwise respectively.
664
665 (All the actions described in section 2.1 are also available.)
666
667 7.2 Twiddle parameters
668
669 Twiddle provides several configuration options via the `Custom'
670 option on the `Type' menu:
671
672 - You can configure the width and height of the puzzle grid.
673
674 - You can configure the size of square block that rotates at a
675 time.
676
677 - You can ask for every square in the grid to be distinguishable
678 (the default), or you can ask for a simplified puzzle in which
679 there are groups of identical numbers. In the simplified puzzle
680 your aim is just to arrange all the 1s into the first row, all
681 the 2s into the second row, and so on.
682
683 - You can configure whether the orientation of tiles matters. If
684 you ask for an orientable puzzle, each tile will have a triangle
685 drawn in it. All the triangles must be pointing upwards to
686 complete the puzzle.
687
688 - You can ask for a limited shuffling operation to be performed
689 on the grid. By default, Twiddle will shuffle the grid so much
690 that any arrangement is about as probable as any other. You can
691 override this by requesting a precise number of shuffling moves
692 to be performed. Typically your aim is then to determine the
693 precise set of shuffling moves and invert them exactly, so that
694 you answer (say) a four-move shuffle with a four-move solution.
695 Note that the more moves you ask for, the more likely it is that
696 solutions shorter than the target length will turn out to be
697 possible.
698
699Chapter 8: Rectangles
700---------------------
701
702 You have a grid of squares, with numbers written in some (but
703 not all) of the squares. Your task is to subdivide the grid into
704 rectangles of various sizes, such that (a) every rectangle contains
705 exactly one numbered square, and (b) the area of each rectangle is
706 equal to the number written in its numbered square.
707
708 Credit for this game goes to the Japanese puzzle magazine Nikoli [3]
709 ; I've also seen a Palm implementation at Puzzle Palace [4]. Unlike
710 Puzzle Palace's implementation, my version automatically generates
711 random grids of any size you like. The quality of puzzle design is
712 therefore not quite as good as hand-crafted puzzles would be, but on
713 the plus side you get an inexhaustible supply of puzzles tailored to
714 your own specification.
715
716 [3] http://www.nikoli.co.jp/en/puzzles/shikaku.html (beware of
717 Flash)
718
719 [4]
720 https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en
721
722 8.1 Rectangles controls
723
724 This game is played with the mouse or cursor keys.
725
726 Left-click any edge to toggle it on or off, or left-click and
727 drag to draw an entire rectangle (or line) on the grid in one go
728 (removing any existing edges within that rectangle). Right-clicking
729 and dragging will allow you to erase the contents of a rectangle
730 without affecting its edges.
731
732 Alternatively, use the cursor keys to move the position indicator
733 around the board. Pressing the return key then allows you to use the
734 cursor keys to drag a rectangle out from that position, and pressing
735 the return key again completes the rectangle. Using the space bar
736 instead of the return key allows you to erase the contents of a
737 rectangle without affecting its edges, as above. Pressing escape
738 cancels a drag.
739
740 When a rectangle of the correct size is completed, it will be
741 shaded.
742
743 (All the actions described in section 2.1 are also available.)
744
745 8.2 Rectangles parameters
746
747 These parameters are available from the `Custom...' option on the
748 `Type' menu.
749
750 _Width_, _Height_
751
752 Size of grid, in squares.
753
754 _Expansion factor_
755
756 This is a mechanism for changing the type of grids generated by
757 the program. Some people prefer a grid containing a few large
758 rectangles to one containing many small ones. So you can ask
759 Rectangles to essentially generate a _smaller_ grid than the
760 size you specified, and then to expand it by adding rows and
761 columns.
762
763 The default expansion factor of zero means that Rectangles will
764 simply generate a grid of the size you ask for, and do nothing
765 further. If you set an expansion factor of (say) 0.5, it means
766 that each dimension of the grid will be expanded to half again
767 as big after generation. In other words, the initial grid will
768 be 2/3 the size in each dimension, and will be expanded to its
769 full size without adding any more rectangles.
770
771 Setting an expansion factor of around 0.5 tends to make the
772 game more difficult, and also (in my experience) rewards a
773 less deductive and more intuitive playing style. If you set it
774 _too_ high, though, the game simply cannot generate more than a
775 few rectangles to cover the entire grid, and the game becomes
776 trivial.
777
778 _Ensure unique solution_
779
780 Normally, Rectangles will make sure that the puzzles it presents
781 have only one solution. Puzzles with ambiguous sections can be
782 more difficult and more subtle, so if you like you can turn off
783 this feature and risk having ambiguous puzzles. Also, finding
784 _all_ the possible solutions can be an additional challenge for
785 an advanced player. Turning off this option can also speed up
786 puzzle generation.
787
788Chapter 9: Netslide
789-------------------
790
791 This game combines the grid generation of Net (see chapter 3) with
792 the movement of Sixteen (see chapter 6): you have a Net grid, but
793 instead of rotating tiles back into place you have to slide them
794 into place by moving a whole row at a time.
795
796 As in Sixteen, control is with the mouse or cursor keys. See section
797 6.1.
798
799 The available game parameters have similar meanings to those in Net
800 (see section 3.2) and Sixteen (see section 6.2).
801
802 Netslide was contributed to this collection by Richard Boulton.
803
804Chapter 10: Pattern
805-------------------
806
807 You have a grid of squares, which must all be filled in either black
808 or white. Beside each row of the grid are listed the lengths of the
809 runs of black squares on that row; above each column are listed the
810 lengths of the runs of black squares in that column. Your aim is to
811 fill in the entire grid black or white.
812
813 I first saw this puzzle form around 1995, under the name
814 `nonograms'. I've seen it in various places since then, under
815 different names.
816
817 Normally, puzzles of this type turn out to be a meaningful picture
818 of something once you've solved them. However, since this version
819 generates the puzzles automatically, they will just look like random
820 groupings of squares. (One user has suggested that this is actually
821 a _good_ thing, since it prevents you from guessing the colour of
822 squares based on the picture, and forces you to use logic instead.)
823 The advantage, though, is that you never run out of them.
824
825 10.1 Pattern controls
826
827 This game is played with the mouse.
828
829 Left-click in a square to colour it black. Right-click to colour it
830 white. If you make a mistake, you can middle-click, or hold down
831 Shift while clicking with any button, to colour the square in the
832 default grey (meaning `undecided') again.
833
834 You can click and drag with the left or right mouse button to colour
835 a vertical or horizontal line of squares black or white at a time
836 (respectively). If you click and drag with the middle button, or
837 with Shift held down, you can colour a whole rectangle of squares
838 grey.
839
840 You can also move around the grid with the cursor keys. Pressing the
841 return key will cycle the current cell through empty, then black,
842 then white, then empty, and the space bar does the same cycle in
843 reverse.
844
845 Moving the cursor while holding Control will colour the moved-over
846 squares black. Holding Shift will colour the moved-over squares
847 white, and holding both will colour them grey.
848
849 (All the actions described in section 2.1 are also available.)
850
851 10.2 Pattern parameters
852
853 The only options available from the `Custom...' option on the `Type'
854 menu are _Width_ and _Height_, which are self-explanatory.
855
856Chapter 11: Solo
857----------------
858
859 You have a square grid, which is divided into as many equally sized
860 sub-blocks as the grid has rows. Each square must be filled in with
861 a digit from 1 to the size of the grid, in such a way that
862
863 - every row contains only one occurrence of each digit
864
865 - every column contains only one occurrence of each digit
866
867 - every block contains only one occurrence of each digit.
868
869 - (optionally, by default off) each of the square's two main
870 diagonals contains only one occurrence of each digit.
871
872 You are given some of the numbers as clues; your aim is to place the
873 rest of the numbers correctly.
874
875 Under the default settings, the sub-blocks are square or
876 rectangular. The default puzzle size is 3x3 (a 9x9 actual grid,
877 divided into nine 3x3 blocks). You can also select sizes with
878 rectangular blocks instead of square ones, such as 2x3 (a 6x6 grid
879 divided into six 3x2 blocks). Alternatively, you can select `jigsaw'
880 mode, in which the sub-blocks are arbitrary shapes which differ
881 between individual puzzles.
882
883 Another available mode is `killer'. In this mode, clues are not
884 given in the form of filled-in squares; instead, the grid is divided
885 into `cages' by coloured lines, and for each cage the game tells
886 you what the sum of all the digits in that cage should be. Also,
887 no digit may appear more than once within a cage, even if the cage
888 crosses the boundaries of existing regions.
889
890 If you select a puzzle size which requires more than 9 digits, the
891 additional digits will be letters of the alphabet. For example, if
892 you select 3x4 then the digits which go in your grid will be 1 to 9,
893 plus `a', `b' and `c'. This cannot be selected for killer puzzles.
894
895 I first saw this puzzle in Nikoli [5], although it's also been
896 popularised by various newspapers under the name `Sudoku' or `Su
897 Doku'. Howard Garns is considered the inventor of the modern form of
898 the puzzle, and it was first published in _Dell Pencil Puzzles and
899 Word Games_. A more elaborate treatment of the history of the puzzle
900 can be found on Wikipedia [6].
901
902 [5] http://www.nikoli.co.jp/en/puzzles/sudoku.html (beware of Flash)
903
904 [6] http://en.wikipedia.org/wiki/Sudoku
905
906 11.1 Solo controls
907
908 To play Solo, simply click the mouse in any empty square and then
909 type a digit or letter on the keyboard to fill that square. If you
910 make a mistake, click the mouse in the incorrect square and press
911 Space to clear it again (or use the Undo feature).
912
913 If you _right_-click in a square and then type a number, that
914 number will be entered in the square as a `pencil mark'. You can
915 have pencil marks for multiple numbers in the same square. Squares
916 containing filled-in numbers cannot also contain pencil marks.
917
918 The game pays no attention to pencil marks, so exactly what you
919 use them for is up to you: you can use them as reminders that a
920 particular square needs to be re-examined once you know more about
921 a particular number, or you can use them as lists of the possible
922 numbers in a given square, or anything else you feel like.
923
924 To erase a single pencil mark, right-click in the square and type
925 the same number again.
926
927 All pencil marks in a square are erased when you left-click and type
928 a number, or when you left-click and press space. Right-clicking and
929 pressing space will also erase pencil marks.
930
931 Alternatively, use the cursor keys to move the mark around the grid.
932 Pressing the return key toggles the mark (from a normal mark to a
933 pencil mark), and typing a number in is entered in the square in the
934 appropriate way; typing in a 0 or using the space bar will clear a
935 filled square.
936
937 (All the actions described in section 2.1 are also available.)
938
939 11.2 Solo parameters
940
941 Solo allows you to configure two separate dimensions of the puzzle
942 grid on the `Type' menu: the number of columns, and the number of
943 rows, into which the main grid is divided. (The size of a block is
944 the inverse of this: for example, if you select 2 columns and 3
945 rows, each actual block will have 3 columns and 2 rows.)
946
947 If you tick the `X' checkbox, Solo will apply the optional extra
948 constraint that the two main diagonals of the grid also contain
949 one of every digit. (This is sometimes known as `Sudoku-X' in
950 newspapers.) In this mode, the squares on the two main diagonals
951 will be shaded slightly so that you know it's enabled.
952
953 If you tick the `Jigsaw' checkbox, Solo will generate randomly
954 shaped sub-blocks. In this mode, the actual grid size will be taken
955 to be the product of the numbers entered in the `Columns' and `Rows'
956 boxes. There is no reason why you have to enter a number greater
957 than 1 in both boxes; Jigsaw mode has no constraint on the grid
958 size, and it can even be a prime number if you feel like it.
959
960 If you tick the `Killer' checkbox, Solo will generate a set of
961 of cages, which are randomly shaped and drawn in an outline of a
962 different colour. Each of these regions contains a smaller clue
963 which shows the digit sum of all the squares in this region.
964
965 You can also configure the type of symmetry shown in the generated
966 puzzles. More symmetry makes the puzzles look prettier but may also
967 make them easier, since the symmetry constraints can force more
968 clues than necessary to be present. Completely asymmetric puzzles
969 have the freedom to contain as few clues as possible.
970
971 Finally, you can configure the difficulty of the generated puzzles.
972 Difficulty levels are judged by the complexity of the techniques
973 of deduction required to solve the puzzle: each level requires a
974 mode of reasoning which was not necessary in the previous one. In
975 particular, on difficulty levels `Trivial' and `Basic' there will be
976 a square you can fill in with a single number at all times, whereas
977 at `Intermediate' level and beyond you will have to make partial
978 deductions about the _set_ of squares a number could be in (or the
979 set of numbers that could be in a square). At `Unreasonable' level,
980 even this is not enough, and you will eventually have to make a
981 guess, and then backtrack if it turns out to be wrong.
982
983 Generating difficult puzzles is itself difficult: if you select one
984 of the higher difficulty levels, Solo may have to make many attempts
985 at generating a puzzle before it finds one hard enough for you. Be
986 prepared to wait, especially if you have also configured a large
987 puzzle size.
988
989Chapter 12: Mines
990-----------------
991
992 You have a grid of covered squares, some of which contain mines, but
993 you don't know which. Your job is to uncover every square which does
994 _not_ contain a mine. If you uncover a square containing a mine, you
995 lose. If you uncover a square which does not contain a mine, you
996 are told how many mines are contained within the eight surrounding
997 squares.
998
999 This game needs no introduction; popularised by Windows, it is
1000 perhaps the single best known desktop puzzle game in existence.
1001
1002 This version of it has an unusual property. By default, it will
1003 generate its mine positions in such a way as to ensure that you
1004 never need to _guess_ where a mine is: you will always be able
1005 to deduce it somehow. So you will never, as can happen in other
1006 versions, get to the last four squares and discover that there are
1007 two mines left but you have no way of knowing for sure where they
1008 are.
1009
1010 12.1 Mines controls
1011
1012 This game is played with the mouse.
1013
1014 If you left-click in a covered square, it will be uncovered.
1015
1016 If you right-click in a covered square, it will place a flag which
1017 indicates that the square is believed to be a mine. Left-clicking in
1018 a marked square will not uncover it, for safety. You can right-click
1019 again to remove a mark placed in error.
1020
1021 If you left-click in an _uncovered_ square, it will `clear around'
1022 the square. This means: if the square has exactly as many flags
1023 surrounding it as it should have mines, then all the covered squares
1024 next to it which are _not_ flagged will be uncovered. So once you
1025 think you know the location of all the mines around a square, you
1026 can use this function as a shortcut to avoid having to click on each
1027 of the remaining squares one by one.
1028
1029 If you uncover a square which has _no_ mines in the surrounding
1030 eight squares, then it is obviously safe to uncover those squares in
1031 turn, and so on if any of them also has no surrounding mines. This
1032 will be done for you automatically; so sometimes when you uncover a
1033 square, a whole new area will open up to be explored.
1034
1035 You can also use the cursor keys to move around the minefield.
1036 Pressing the return key in a covered square uncovers it, and in
1037 an uncovered square will clear around it (so it acts as the left
1038 button), pressing the space bar in a covered square will place a
1039 flag (similarly, it acts as the right button).
1040
1041 All the actions described in section 2.1 are also available.
1042
1043 Even Undo is available, although you might consider it cheating to
1044 use it. If you step on a mine, the program will only reveal the mine
1045 in question (unlike most other implementations, which reveal all of
1046 them). You can then Undo your fatal move and continue playing if you
1047 like. The program will track the number of times you died (and Undo
1048 will not reduce that counter), so when you get to the end of the
1049 game you know whether or not you did it without making any errors.
1050
1051 (If you really want to know the full layout of the grid, which other
1052 implementations will show you after you die, you can always use the
1053 Solve menu option.)
1054
1055 12.2 Mines parameters
1056
1057 The options available from the `Custom...' option on the `Type' menu
1058 are:
1059
1060 _Width_, _Height_
1061
1062 Size of grid in squares.
1063
1064 _Mines_
1065
1066 Number of mines in the grid. You can enter this as an absolute
1067 mine count, or alternatively you can put a % sign on the end
1068 in which case the game will arrange for that proportion of the
1069 squares in the grid to be mines.
1070
1071 Beware of setting the mine count too high. At very high
1072 densities, the program may spend forever searching for a
1073 solvable grid.
1074
1075 _Ensure solubility_
1076
1077 When this option is enabled (as it is by default), Mines will
1078 ensure that the entire grid can be fully deduced starting
1079 from the initial open space. If you prefer the riskier grids
1080 generated by other implementations, you can switch off this
1081 option.
1082
1083Chapter 13: Same Game
1084---------------------
1085
1086 You have a grid of coloured squares, which you have to clear by
1087 highlighting contiguous regions of more than one coloured square;
1088 the larger the region you highlight, the more points you get (and
1089 the faster you clear the arena).
1090
1091 If you clear the grid you win. If you end up with nothing but single
1092 squares (i.e., there are no more clickable regions left) you lose.
1093
1094 Removing a region causes the rest of the grid to shuffle up: blocks
1095 that are suspended will fall down (first), and then empty columns
1096 are filled from the right.
1097
1098 Same Game was contributed to this collection by James Harvey.
1099
1100 13.1 Same Game controls
1101
1102 This game can be played with either the keyboard or the mouse.
1103
1104 If you left-click an unselected region, it becomes selected
1105 (possibly clearing the current selection).
1106
1107 If you left-click the selected region, it will be removed (and the
1108 rest of the grid shuffled immediately).
1109
1110 If you right-click the selected region, it will be unselected.
1111
1112 The cursor keys move a cursor around the grid. Pressing the Space or
1113 Enter keys while the cursor is in an unselected region selects it;
1114 pressing Space or Enter again removes it as above.
1115
1116 (All the actions described in section 2.1 are also available.)
1117
1118 13.2 Same Game parameters
1119
1120 These parameters are available from the `Custom...' option on the
1121 `Type' menu.
1122
1123 _Width_, _Height_
1124
1125 Size of grid in squares.
1126
1127 _No. of colours_
1128
1129 Number of different colours used to fill the grid; the more
1130 colours, the fewer large regions of colour and thus the more
1131 difficult it is to successfully clear the grid.
1132
1133 _Scoring system_
1134
1135 Controls the precise mechanism used for scoring. With the
1136 default system, `(n-2)^2', only regions of three squares or more
1137 will score any points at all. With the alternative `(n-1)^2'
1138 system, regions of two squares score a point each, and larger
1139 regions score relatively more points.
1140
1141 _Ensure solubility_
1142
1143 If this option is ticked (the default state), generated grids
1144 will be guaranteed to have at least one solution.
1145
1146 If you turn it off, the game generator will not try to guarantee
1147 soluble grids; it will, however, still ensure that there are at
1148 least 2 squares of each colour on the grid at the start (since a
1149 grid with exactly one square of a given colour is _definitely_
1150 insoluble). Grids generated with this option disabled may
1151 contain more large areas of contiguous colour, leading to
1152 opportunities for higher scores; they can also take less time to
1153 generate.
1154
1155Chapter 14: Flip
1156----------------
1157
1158 You have a grid of squares, some light and some dark. Your aim is to
1159 light all the squares up at the same time. You can choose any square
1160 and flip its state from light to dark or dark to light, but when you
1161 do so, other squares around it change state as well.
1162
1163 Each square contains a small diagram showing which other squares
1164 change when you flip it.
1165
1166 14.1 Flip controls
1167
1168 This game can be played with either the keyboard or the mouse.
1169
1170 Left-click in a square to flip it and its associated squares, or use
1171 the cursor keys to choose a square and the space bar or Enter key to
1172 flip.
1173
1174 If you use the `Solve' function on this game, it will mark some of
1175 the squares in red. If you click once in every square with a red
1176 mark, the game should be solved. (If you click in a square _without_
1177 a red mark, a red mark will appear in it to indicate that you will
1178 need to reverse that operation to reach the solution.)
1179
1180 (All the actions described in section 2.1 are also available.)
1181
1182 14.2 Flip parameters
1183
1184 These parameters are available from the `Custom...' option on the
1185 `Type' menu.
1186
1187 _Width_, _Height_
1188
1189 Size of grid in squares.
1190
1191 _Shape type_
1192
1193 This control determines the shape of the region which is flipped
1194 by clicking in any given square. The default setting, `Crosses',
1195 causes every square to flip itself and its four immediate
1196 neighbours (or three or two if it's at an edge or corner). The
1197 other setting, `Random', causes a random shape to be chosen for
1198 every square, so the game is different every time.
1199
1200Chapter 15: Guess
1201-----------------
1202
1203 You have a set of coloured pegs, and have to reproduce a
1204 predetermined sequence of them (chosen by the computer) within a
1205 certain number of guesses.
1206
1207 Each guess gets marked with the number of correctly-coloured pegs
1208 in the correct places (in black), and also the number of correctly-
1209 coloured pegs in the wrong places (in white).
1210
1211 This game is also known (and marketed, by Hasbro, mainly) as a board
1212 game `Mastermind', with 6 colours, 4 pegs per row, and 10 guesses.
1213 However, this version allows custom settings of number of colours
1214 (up to 10), number of pegs per row, and number of guesses.
1215
1216 Guess was contributed to this collection by James Harvey.
1217
1218 15.1 Guess controls
1219
1220 This game can be played with either the keyboard or the mouse.
1221
1222 With the mouse, drag a coloured peg from the tray on the left-hand
1223 side to its required position in the current guess; pegs may also
1224 be dragged from current and past guesses to copy them elsewhere. To
1225 remove a peg, drag it off its current position to somewhere invalid.
1226
1227 Right-clicking in the current guess adds a `hold' marker; pegs that
1228 have hold markers will be automatically added to the next guess
1229 after marking.
1230
1231 Alternatively, with the keyboard, the up and down cursor keys can
1232 be used to select a peg colour, the left and right keys to select a
1233 peg position, and the space bar or Enter key to place a peg of the
1234 selected colour in the chosen position. `D' or Backspace removes a
1235 peg, and Space adds a hold marker.
1236
1237 Pressing `h' or `?' will fill the current guess with a suggested
1238 guess. Using this is not recommended for 10 or more pegs as it is
1239 slow.
1240
1241 When the guess is complete, the smaller feedback pegs will be
1242 highlighted; clicking on these (or moving the peg cursor to them
1243 with the arrow keys and pressing the space bar or Enter key) will
1244 mark the current guess, copy any held pegs to the next guess, and
1245 move the `current guess' marker.
1246
1247 If you correctly position all the pegs the solution will be
1248 displayed below; if you run out of guesses (or select `Solve...')
1249 the solution will also be revealed.
1250
1251 (All the actions described in section 2.1 are also available.)
1252
1253 15.2 Guess parameters
1254
1255 These parameters are available from the `Custom...' option on the
1256 `Type' menu. The default game matches the parameters for the board
1257 game `Mastermind'.
1258
1259 _Colours_
1260
1261 Number of colours the solution is chosen from; from 2 to 10
1262 (more is harder).
1263
1264 _Pegs per guess_
1265
1266 Number of pegs per guess (more is harder).
1267
1268 _Guesses_
1269
1270 Number of guesses you have to find the solution in (fewer is
1271 harder).
1272
1273 _Allow blanks_
1274
1275 Allows blank pegs to be given as part of a guess (makes it
1276 easier, because you know that those will never be counted as
1277 part of the solution). This is turned off by default.
1278
1279 Note that this doesn't allow blank pegs in the solution; if you
1280 really wanted that, use one extra colour.
1281
1282 _Allow duplicates_
1283
1284 Allows the solution (and the guesses) to contain colours more
1285 than once; this increases the search space (making things
1286 harder), and is turned on by default.
1287
1288Chapter 16: Pegs
1289----------------
1290
1291 A number of pegs are placed in holes on a board. You can remove a
1292 peg by jumping an adjacent peg over it (horizontally or vertically)
1293 to a vacant hole on the other side. Your aim is to remove all but
1294 one of the pegs initially present.
1295
1296 This game, best known as `Peg Solitaire', is possibly one of the
1297 oldest puzzle games still commonly known.
1298
1299 16.1 Pegs controls
1300
1301 To move a peg, drag it with the mouse from its current position to
1302 its final position. If the final position is exactly two holes away
1303 from the initial position, is currently unoccupied by a peg, and
1304 there is a peg in the intervening square, the move will be permitted
1305 and the intervening peg will be removed.
1306
1307 Vacant spaces which you can move a peg into are marked with holes. A
1308 space with no peg and no hole is not available for moving at all: it
1309 is an obstacle which you must work around.
1310
1311 You can also use the cursor keys to move a position indicator around
1312 the board. Pressing the return key while over a peg, followed by a
1313 cursor key, will jump the peg in that direction (if that is a legal
1314 move).
1315
1316 (All the actions described in section 2.1 are also available.)
1317
1318 16.2 Pegs parameters
1319
1320 These parameters are available from the `Custom...' option on the
1321 `Type' menu.
1322
1323 _Width_, _Height_
1324
1325 Size of grid in holes.
1326
1327 _Board type_
1328
1329 Controls whether you are given a board of a standard shape or
1330 a randomly generated shape. The two standard shapes currently
1331 supported are `Cross' and `Octagon' (also commonly known as the
1332 English and European traditional board layouts respectively).
1333 Selecting `Random' will give you a different board shape every
1334 time (but always one that is known to have a solution).
1335
1336Chapter 17: Dominosa
1337--------------------
1338
1339 A normal set of dominoes - that is, one instance of every
1340 (unordered) pair of numbers from 0 to 6 - has been arranged
1341 irregularly into a rectangle; then the number in each square has
1342 been written down and the dominoes themselves removed. Your task is
1343 to reconstruct the pattern by arranging the set of dominoes to match
1344 the provided array of numbers.
1345
1346 This puzzle is widely credited to O. S. Adler, and takes part of its
1347 name from those initials.
1348
1349 17.1 Dominosa controls
1350
1351 Left-clicking between any two adjacent numbers places a domino
1352 covering them, or removes one if it is already present. Trying to
1353 place a domino which overlaps existing dominoes will remove the ones
1354 it overlaps.
1355
1356 Right-clicking between two adjacent numbers draws a line between
1357 them, which you can use to remind yourself that you know those two
1358 numbers are _not_ covered by a single domino. Right-clicking again
1359 removes the line.
1360
1361 You can also use the cursor keys to move a cursor around the grid.
1362 When the cursor is half way between two adjacent numbers, pressing
1363 the return key will place a domino covering those numbers, or
1364 pressing the space bar will lay a line between the two squares.
1365 Repeating either action removes the domino or line.
1366
1367 Pressing a number key will highlight all occurrences of that number.
1368 Pressing that number again will clear the highlighting. Up to two
1369 different numbers can be highlighted at any given time.
1370
1371 (All the actions described in section 2.1 are also available.)
1372
1373 17.2 Dominosa parameters
1374
1375 These parameters are available from the `Custom...' option on the
1376 `Type' menu.
1377
1378 _Maximum number on dominoes_
1379
1380 Controls the size of the puzzle, by controlling the size of the
1381 set of dominoes used to make it. Dominoes with numbers going
1382 up to N will give rise to an (N+2) x (N+1) rectangle; so, in
1383 particular, the default value of 6 gives an 8x7 grid.
1384
1385 _Ensure unique solution_
1386
1387 Normally, Dominosa will make sure that the puzzles it presents
1388 have only one solution. Puzzles with ambiguous sections can be
1389 more difficult and sometimes more subtle, so if you like you
1390 can turn off this feature. Also, finding _all_ the possible
1391 solutions can be an additional challenge for an advanced player.
1392 Turning off this option can also speed up puzzle generation.
1393
1394Chapter 18: Untangle
1395--------------------
1396
1397 You are given a number of points, some of which have lines drawn
1398 between them. You can move the points about arbitrarily; your aim is
1399 to position the points so that no line crosses another.
1400
1401 I originally saw this in the form of a Flash game called Planarity
1402 [7], written by John Tantalo.
1403
1404 [7] http://planarity.net
1405
1406 18.1 Untangle controls
1407
1408 To move a point, click on it with the left mouse button and drag it
1409 into a new position.
1410
1411 (All the actions described in section 2.1 are also available.)
1412
1413 18.2 Untangle parameters
1414
1415 There is only one parameter available from the `Custom...' option on
1416 the `Type' menu:
1417
1418 _Number of points_
1419
1420 Controls the size of the puzzle, by specifying the number of
1421 points in the generated graph.
1422
1423Chapter 19: Black Box
1424---------------------
1425
1426 A number of balls are hidden in a rectangular arena. You have to
1427 deduce the positions of the balls by firing lasers positioned at the
1428 edges of the arena and observing how their beams are deflected.
1429
1430 Beams will travel straight from their origin until they hit the
1431 opposite side of the arena (at which point they emerge), unless
1432 affected by balls in one of the following ways:
1433
1434 - A beam that hits a ball head-on is absorbed and will never re-
1435 emerge. This includes beams that meet a ball on the first rank
1436 of the arena.
1437
1438 - A beam with a ball in its front-left square and no ball ahead of
1439 it gets deflected 90 degrees to the right.
1440
1441 - A beam with a ball in its front-right square and no ball ahead
1442 of it gets similarly deflected to the left.
1443
1444 - A beam that would re-emerge from its entry location is
1445 considered to be `reflected'.
1446
1447 - A beam which would get deflected before entering the arena by a
1448 ball to the front-left or front-right of its entry point is also
1449 considered to be `reflected'.
1450
1451 Beams that are reflected appear as a `R'; beams that hit balls head-
1452 on appear as `H'. Otherwise, a number appears at the firing point
1453 and the location where the beam emerges (this number is unique to
1454 that shot).
1455
1456 You can place guesses as to the location of the balls, based on the
1457 entry and exit patterns of the beams; once you have placed enough
1458 balls a button appears enabling you to have your guesses checked.
1459
1460 Here is a diagram showing how the positions of balls can create each
1461 of the beam behaviours shown above:
1462
1463 1RHR----
1464 |..O.O...|
1465 2........3
1466 |........|
1467 |........|
1468 3........|
1469 |......O.|
1470 H........|
1471 |.....O..|
1472 12-RR---
1473
1474 As shown, it is possible for a beam to receive multiple reflections
1475 before re-emerging (see turn 3). Similarly, a beam may be reflected
1476 (possibly more than once) before receiving a hit (the `H' on the
1477 left side of the example).
1478
1479 Note that any layout with more than 4 balls may have a non-unique
1480 solution. The following diagram illustrates this; if you know the
1481 board contains 5 balls, it is impossible to determine where the
1482 fifth ball is (possible positions marked with an x):
1483
1484 --------
1485 |........|
1486 |........|
1487 |..O..O..|
1488 |...xx...|
1489 |...xx...|
1490 |..O..O..|
1491 |........|
1492 |........|
1493 --------
1494
1495 For this reason, when you have your guesses checked, the game
1496 will check that your solution _produces the same results_ as the
1497 computer's, rather than that your solution is identical to the
1498 computer's. So in the above example, you could put the fifth ball at
1499 _any_ of the locations marked with an x, and you would still win.
1500
1501 Black Box was contributed to this collection by James Harvey.
1502
1503 19.1 Black Box controls
1504
1505 To fire a laser beam, left-click in a square around the edge of
1506 the arena. The results will be displayed immediately. Clicking or
1507 holding the left button on one of these squares will highlight the
1508 current go (or a previous go) to confirm the exit point for that
1509 laser, if applicable.
1510
1511 To guess the location of a ball, left-click within the arena and a
1512 black circle will appear marking the guess; click again to remove
1513 the guessed ball.
1514
1515 Locations in the arena may be locked against modification by right-
1516 clicking; whole rows and columns may be similarly locked by right-
1517 clicking in the laser square above/below that column, or to the
1518 left/right of that row.
1519
1520 The cursor keys may also be used to move around the grid. Pressing
1521 the Enter key will fire a laser or add a new ball-location guess,
1522 and pressing Space will lock a cell, row, or column.
1523
1524 When an appropriate number of balls have been guessed, a button will
1525 appear at the top-left corner of the grid; clicking that (with mouse
1526 or cursor) will check your guesses.
1527
1528 If you click the `check' button and your guesses are not correct,
1529 the game will show you the minimum information necessary to
1530 demonstrate this to you, so you can try again. If your ball
1531 positions are not consistent with the beam paths you already know
1532 about, one beam path will be circled to indicate that it proves you
1533 wrong. If your positions match all the existing beam paths but are
1534 still wrong, one new beam path will be revealed (written in red)
1535 which is not consistent with your current guesses.
1536
1537 If you decide to give up completely, you can select Solve to reveal
1538 the actual ball positions. At this point, correctly-placed balls
1539 will be displayed as filled black circles, incorrectly-placed balls
1540 as filled black circles with red crosses, and missing balls as
1541 filled red circles. In addition, a red circle marks any laser you
1542 had already fired which is not consistent with your ball layout
1543 (just as when you press the `check' button), and red text marks
1544 any laser you _could_ have fired in order to distinguish your ball
1545 layout from the correct one.
1546
1547 (All the actions described in section 2.1 are also available.)
1548
1549 19.2 Black Box parameters
1550
1551 These parameters are available from the `Custom...' option on the
1552 `Type' menu.
1553
1554 _Width_, _Height_
1555
1556 Size of grid in squares. There are 2 x _Width_ x _Height_ lasers
1557 per grid, two per row and two per column.
1558
1559 _No. of balls_
1560
1561 Number of balls to place in the grid. This can be a single
1562 number, or a range (separated with a hyphen, like `2-6'),
1563 and determines the number of balls to place on the grid.
1564 The `reveal' button is only enabled if you have guessed an
1565 appropriate number of balls; a guess using a different number
1566 to the original solution is still acceptable, if all the beam
1567 inputs and outputs match.
1568
1569Chapter 20: Slant
1570-----------------
1571
1572 You have a grid of squares. Your aim is to draw a diagonal line
1573 through each square, and choose which way each line slants so that
1574 the following conditions are met:
1575
1576 - The diagonal lines never form a loop.
1577
1578 - Any point with a circled number has precisely that many lines
1579 meeting at it. (Thus, a 4 is the centre of a cross shape,
1580 whereas a zero is the centre of a diamond shape - or rather, a
1581 partial diamond shape, because a zero can never appear in the
1582 middle of the grid because that would immediately cause a loop.)
1583
1584 Credit for this puzzle goes to Nikoli [8].
1585
1586 [8] http://www.nikoli.co.jp/ja/puzzles/gokigen_naname (in Japanese)
1587
1588 20.1 Slant controls
1589
1590 Left-clicking in a blank square will place a \ in it (a line leaning
1591 to the left, i.e. running from the top left of the square to the
1592 bottom right). Right-clicking in a blank square will place a / in it
1593 (leaning to the right, running from top right to bottom left).
1594
1595 Continuing to click either button will cycle between the three
1596 possible square contents. Thus, if you left-click repeatedly in a
1597 blank square it will change from blank to \ to / back to blank, and
1598 if you right-click repeatedly the square will change from blank to /
1599 to \ back to blank. (Therefore, you can play the game entirely with
1600 one button if you need to.)
1601
1602 You can also use the cursor keys to move around the grid. Pressing
1603 the return or space keys will place a \ or a /, respectively, and
1604 will then cycle them as above. You can also press / or \ to place a
1605 / or \, respectively, independent of what is already in the cursor
1606 square. Backspace removes any line from the cursor square.
1607
1608 (All the actions described in section 2.1 are also available.)
1609
1610 20.2 Slant parameters
1611
1612 These parameters are available from the `Custom...' option on the
1613 `Type' menu.
1614
1615 _Width_, _Height_
1616
1617 Size of grid in squares.
1618
1619 _Difficulty_
1620
1621 Controls the difficulty of the generated puzzle. At Hard
1622 level, you are required to do deductions based on knowledge of
1623 _relationships_ between squares rather than always being able to
1624 deduce the exact contents of one square at a time. (For example,
1625 you might know that two squares slant in the same direction,
1626 even if you don't yet know what that direction is, and this
1627 might enable you to deduce something about still other squares.)
1628 Even at Hard level, guesswork and backtracking should never be
1629 necessary.
1630
1631Chapter 21: Light Up
1632--------------------
1633
1634 You have a grid of squares. Some are filled in black; some of the
1635 black squares are numbered. Your aim is to `light up' all the empty
1636 squares by placing light bulbs in some of them.
1637
1638 Each light bulb illuminates the square it is on, plus all squares
1639 in line with it horizontally or vertically unless a black square is
1640 blocking the way.
1641
1642 To win the game, you must satisfy the following conditions:
1643
1644 - All non-black squares are lit.
1645
1646 - No light is lit by another light.
1647
1648 - All numbered black squares have exactly that number of lights
1649 adjacent to them (in the four squares above, below, and to the
1650 side).
1651
1652 Non-numbered black squares may have any number of lights adjacent to
1653 them.
1654
1655 Credit for this puzzle goes to Nikoli [9].
1656
1657 Light Up was contributed to this collection by James Harvey.
1658
1659 [9] http://www.nikoli.co.jp/en/puzzles/akari.html (beware of Flash)
1660
1661 21.1 Light Up controls
1662
1663 Left-clicking in a non-black square will toggle the presence of a
1664 light in that square. Right-clicking in a non-black square toggles a
1665 mark there to aid solving; it can be used to highlight squares that
1666 cannot be lit, for example.
1667
1668 You may not place a light in a marked square, nor place a mark in a
1669 lit square.
1670
1671 The game will highlight obvious errors in red. Lights lit by other
1672 lights are highlighted in this way, as are numbered squares which do
1673 not (or cannot) have the right number of lights next to them.
1674
1675 Thus, the grid is solved when all non-black squares have yellow
1676 highlights and there are no red lights.
1677
1678 (All the actions described in section 2.1 are also available.)
1679
1680 21.2 Light Up parameters
1681
1682 These parameters are available from the `Custom...' option on the
1683 `Type' menu.
1684
1685 _Width_, _Height_
1686
1687 Size of grid in squares.
1688
1689 _%age of black squares_
1690
1691 Rough percentage of black squares in the grid.
1692
1693 This is a hint rather than an instruction. If the grid generator
1694 is unable to generate a puzzle to this precise specification, it
1695 will increase the proportion of black squares until it can.
1696
1697 _Symmetry_
1698
1699 Allows you to specify the required symmetry of the black squares
1700 in the grid. (This does not affect the difficulty of the puzzles
1701 noticeably.)
1702
1703 _Difficulty_
1704
1705 `Easy' means that the puzzles should be soluble without
1706 backtracking or guessing, `Hard' means that some guesses will
1707 probably be necessary.
1708
1709Chapter 22: Map
1710---------------
1711
1712 You are given a map consisting of a number of regions. Your task is
1713 to colour each region with one of four colours, in such a way that
1714 no two regions sharing a boundary have the same colour. You are
1715 provided with some regions already coloured, sufficient to make the
1716 remainder of the solution unique.
1717
1718 Only regions which share a length of border are required to be
1719 different colours. Two regions which meet at only one _point_ (i.e.
1720 are diagonally separated) may be the same colour.
1721
1722 I believe this puzzle is original; I've never seen an implementation
1723 of it anywhere else. The concept of a four-colouring puzzle was
1724 suggested by Owen Dunn; credit must also go to Nikoli and to Verity
1725 Allan for inspiring the train of thought that led to me realising
1726 Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor
1727 for many detailed suggestions.
1728
1729 22.1 Map controls
1730
1731 To colour a region, click the left mouse button on an existing
1732 region of the desired colour and drag that colour into the new
1733 region.
1734
1735 (The program will always ensure the starting puzzle has at least one
1736 region of each colour, so that this is always possible!)
1737
1738 If you need to clear a region, you can drag from an empty region, or
1739 from the puzzle boundary if there are no empty regions left.
1740
1741 Dragging a colour using the _right_ mouse button will stipple the
1742 region in that colour, which you can use as a note to yourself that
1743 you think the region _might_ be that colour. A region can contain
1744 stipples in multiple colours at once. (This is often useful at the
1745 harder difficulty levels.)
1746
1747 You can also use the cursor keys to move around the map: the colour
1748 of the cursor indicates the position of the colour you would drag
1749 (which is not obvious if you're on a region's boundary, since it
1750 depends on the direction from which you approached the boundary).
1751 Pressing the return key starts a drag of that colour, as above,
1752 which you control with the cursor keys; pressing the return key
1753 again finishes the drag. The space bar can be used similarly to
1754 create a stippled region. Double-pressing the return key (without
1755 moving the cursor) will clear the region, as a drag from an empty
1756 region does: this is useful with the cursor mode if you have filled
1757 the entire map in but need to correct the layout.
1758
1759 If you press L during play, the game will toggle display of a number
1760 in each region of the map. This is useful if you want to discuss a
1761 particular puzzle instance with a friend - having an unambiguous
1762 name for each region is much easier than trying to refer to them all
1763 by names such as `the one down and right of the brown one on the top
1764 border'.
1765
1766 (All the actions described in section 2.1 are also available.)
1767
1768 22.2 Map parameters
1769
1770 These parameters are available from the `Custom...' option on the
1771 `Type' menu.
1772
1773 _Width_, _Height_
1774
1775 Size of grid in squares.
1776
1777 _Regions_
1778
1779 Number of regions in the generated map.
1780
1781 _Difficulty_
1782
1783 In `Easy' mode, there should always be at least one region whose
1784 colour can be determined trivially. In `Normal' and `Hard'
1785 modes, you will have to use increasingly complex logic to deduce
1786 the colour of some regions. However, it will always be possible
1787 without having to guess or backtrack.
1788
1789 In `Unreasonable' mode, the program will feel free to generate
1790 puzzles which are as hard as it can possibly make them: the
1791 only constraint is that they should still have a unique
1792 solution. Solving Unreasonable puzzles may require guessing and
1793 backtracking.
1794
1795Chapter 23: Loopy
1796-----------------
1797
1798 You are given a grid of dots, marked with yellow lines to indicate
1799 which dots you are allowed to connect directly together. Your aim is
1800 to use some subset of those yellow lines to draw a single unbroken
1801 loop from dot to dot within the grid.
1802
1803 Some of the spaces between the lines contain numbers. These numbers
1804 indicate how many of the lines around that space form part of the
1805 loop. The loop you draw must correctly satisfy all of these clues to
1806 be considered a correct solution.
1807
1808 In the default mode, the dots are arranged in a grid of squares;
1809 however, you can also play on triangular or hexagonal grids, or even
1810 more exotic ones.
1811
1812 Credit for the basic puzzle idea goes to Nikoli [10].
1813
1814 Loopy was originally contributed to this collection by Mike Pinna,
1815 and subsequently enhanced to handle various types of non-square grid
1816 by Lambros Lambrou.
1817
1818 [10] http://www.nikoli.co.jp/en/puzzles/slitherlink.html (beware of
1819 Flash)
1820
1821 23.1 Loopy controls
1822
1823 Click the left mouse button on a yellow line to turn it black,
1824 indicating that you think it is part of the loop. Click again to
1825 turn the line yellow again (meaning you aren't sure yet).
1826
1827 If you are sure that a particular line segment is _not_ part of the
1828 loop, you can click the right mouse button to remove it completely.
1829 Again, clicking a second time will turn the line back to yellow.
1830
1831 (All the actions described in section 2.1 are also available.)
1832
1833 23.2 Loopy parameters
1834
1835 These parameters are available from the `Custom...' option on the
1836 `Type' menu.
1837
1838 _Width_, _Height_
1839
1840 Size of grid, measured in number of regions across and down. For
1841 square grids, it's clear how this is counted; for other types of
1842 grid you may have to think a bit to see how the dimensions are
1843 measured.
1844
1845 _Grid type_
1846
1847 Allows you to choose between a selection of types of tiling.
1848 Some have all the faces the same but may have multiple different
1849 types of vertex (e.g. the _Cairo_ or _Kites_ mode); others
1850 have all the vertices the same but may have different types of
1851 face (e.g. the _Great Hexagonal_). The square, triangular and
1852 honeycomb grids are fully regular, and have all their vertices
1853 _and_ faces the same; this makes them the least confusing to
1854 play.
1855
1856 _Difficulty_
1857
1858 Controls the difficulty of the generated puzzle.
1859
1860Chapter 24: Inertia
1861-------------------
1862
1863 You are a small green ball sitting in a grid full of obstacles. Your
1864 aim is to collect all the gems without running into any mines.
1865
1866 You can move the ball in any orthogonal _or diagonal_ direction.
1867 Once the ball starts moving, it will continue until something stops
1868 it. A wall directly in its path will stop it (but if it is moving
1869 diagonally, it will move through a diagonal gap between two other
1870 walls without stopping). Also, some of the squares are `stops'; when
1871 the ball moves on to a stop, it will stop moving no matter what
1872 direction it was going in. Gems do _not_ stop the ball; it picks
1873 them up and keeps on going.
1874
1875 Running into a mine is fatal. Even if you picked up the last gem in
1876 the same move which then hit a mine, the game will count you as dead
1877 rather than victorious.
1878
1879 This game was originally implemented for Windows by Ben Olmstead
1880 [11], who was kind enough to release his source code on request so
1881 that it could be re-implemented for this collection.
1882
1883 [11] http://xn13.com/
1884
1885 24.1 Inertia controls
1886
1887 You can move the ball in any of the eight directions using the
1888 numeric keypad. Alternatively, if you click the left mouse button
1889 on the grid, the ball will begin a move in the general direction of
1890 where you clicked.
1891
1892 If you use the `Solve' function on this game, the program will
1893 compute a path through the grid which collects all the remaining
1894 gems and returns to the current position. A hint arrow will appear
1895 on the ball indicating the direction in which you should move to
1896 begin on this path. If you then move in that direction, the arrow
1897 will update to indicate the next direction on the path. You can
1898 also press Space to automatically move in the direction of the hint
1899 arrow. If you move in a different direction from the one shown
1900 by the arrow, arrows will be shown only if the puzzle is still
1901 solvable.
1902
1903 All the actions described in section 2.1 are also available. In
1904 particular, if you do run into a mine and die, you can use the Undo
1905 function and resume playing from before the fatal move. The game
1906 will keep track of the number of times you have done this.
1907
1908 24.2 Inertia parameters
1909
1910 These parameters are available from the `Custom...' option on the
1911 `Type' menu.
1912
1913 _Width_, _Height_
1914
1915 Size of grid in squares.
1916
1917Chapter 25: Tents
1918-----------------
1919
1920 You have a grid of squares, some of which contain trees. Your aim is
1921 to place tents in some of the remaining squares, in such a way that
1922 the following conditions are met:
1923
1924 - There are exactly as many tents as trees.
1925
1926 - The tents and trees can be matched up in such a way that each
1927 tent is directly adjacent (horizontally or vertically, but not
1928 diagonally) to its own tree. However, a tent may be adjacent to
1929 other trees as well as its own.
1930
1931 - No two tents are adjacent horizontally, vertically _or
1932 diagonally_.
1933
1934 - The number of tents in each row, and in each column, matches the
1935 numbers given round the sides of the grid.
1936
1937 This puzzle can be found in several places on the Internet, and was
1938 brought to my attention by e-mail. I don't know who I should credit
1939 for inventing it.
1940
1941 25.1 Tents controls
1942
1943 Left-clicking in a blank square will place a tent in it. Right-
1944 clicking in a blank square will colour it green, indicating that you
1945 are sure it _isn't_ a tent. Clicking either button in an occupied
1946 square will clear it.
1947
1948 If you _drag_ with the right button along a row or column, every
1949 blank square in the region you cover will be turned green, and no
1950 other squares will be affected. (This is useful for clearing the
1951 remainder of a row once you have placed all its tents.)
1952
1953 You can also use the cursor keys to move around the grid. Pressing
1954 the return key over an empty square will place a tent, and pressing
1955 the space bar over an empty square will colour it green; either key
1956 will clear an occupied square. Holding Shift and pressing the cursor
1957 keys will colour empty squares green. Holding Control and pressing
1958 the cursor keys will colour green both empty squares and squares
1959 with tents.
1960
1961 (All the actions described in section 2.1 are also available.)
1962
1963 25.2 Tents parameters
1964
1965 These parameters are available from the `Custom...' option on the
1966 `Type' menu.
1967
1968 _Width_, _Height_
1969
1970 Size of grid in squares.
1971
1972 _Difficulty_
1973
1974 Controls the difficulty of the generated puzzle. More difficult
1975 puzzles require more complex deductions, but at present none
1976 of the available difficulty levels requires guesswork or
1977 backtracking.
1978
1979Chapter 26: Bridges
1980-------------------
1981
1982 You have a set of islands distributed across the playing area.
1983 Each island contains a number. Your aim is to connect the islands
1984 together with bridges, in such a way that:
1985
1986 - Bridges run horizontally or vertically.
1987
1988 - The number of bridges terminating at any island is equal to the
1989 number written in that island.
1990
1991 - Two bridges may run in parallel between the same two islands,
1992 but no more than two may do so.
1993
1994 - No bridge crosses another bridge.
1995
1996 - All the islands are connected together.
1997
1998 There are some configurable alternative modes, which involve
1999 changing the parallel-bridge limit to something other than 2, and
2000 introducing the additional constraint that no sequence of bridges
2001 may form a loop from one island back to the same island. The rules
2002 stated above are the default ones.
2003
2004 Credit for this puzzle goes to Nikoli [12].
2005
2006 Bridges was contributed to this collection by James Harvey.
2007
2008 [12] http://www.nikoli.co.jp/en/puzzles/hashiwokakero.html (beware
2009 of Flash)
2010
2011 26.1 Bridges controls
2012
2013 To place a bridge between two islands, click the mouse down on one
2014 island and drag it towards the other. You do not need to drag all
2015 the way to the other island; you only need to move the mouse far
2016 enough for the intended bridge direction to be unambiguous. (So you
2017 can keep the mouse near the starting island and conveniently throw
2018 bridges out from it in many directions.)
2019
2020 Doing this again when a bridge is already present will add another
2021 parallel bridge. If there are already as many bridges between the
2022 two islands as permitted by the current game rules (i.e. two by
2023 default), the same dragging action will remove all of them.
2024
2025 If you want to remind yourself that two islands definitely _do not_
2026 have a bridge between them, you can right-drag between them in the
2027 same way to draw a `non-bridge' marker.
2028
2029 If you think you have finished with an island (i.e. you have placed
2030 all its bridges and are confident that they are in the right
2031 places), you can mark the island as finished by left-clicking on it.
2032 This will highlight it and all the bridges connected to it, and you
2033 will be prevented from accidentally modifying any of those bridges
2034 in future. Left-clicking again on a highlighted island will unmark
2035 it and restore your ability to modify it.
2036
2037 You can also use the cursor keys to move around the grid: if
2038 possible the cursor will always move orthogonally, otherwise it
2039 will move towards the nearest island to the indicated direction.
2040 Holding Control and pressing a cursor key will lay a bridge in that
2041 direction (if available); Shift and a cursor key will lay a `non-
2042 bridge' marker. Pressing the return key followed by a cursor key
2043 will also lay a bridge in that direction.
2044
2045 You can mark an island as finished by pressing the space bar or by
2046 pressing the return key twice.
2047
2048 By pressing a number key, you can jump to the nearest island with
2049 that number. Letters `a', ..., `f' count as 10, ..., 15 and `0' as
2050 16.
2051
2052 Violations of the puzzle rules will be marked in red:
2053
2054 - An island with too many bridges will be highlighted in red.
2055
2056 - An island with too few bridges will be highlighted in red if it
2057 is definitely an error (as opposed to merely not being finished
2058 yet): if adding enough bridges would involve having to cross
2059 another bridge or remove a non-bridge marker, or if the island
2060 has been highlighted as complete.
2061
2062 - A group of islands and bridges may be highlighted in red if it
2063 is a closed subset of the puzzle with no way to connect it to
2064 the rest of the islands. For example, if you directly connect
2065 two 1s together with a bridge and they are not the only two
2066 islands on the grid, they will light up red to indicate that
2067 such a group cannot be contained in any valid solution.
2068
2069 - If you have selected the (non-default) option to disallow loops
2070 in the solution, a group of bridges which forms a loop will be
2071 highlighted.
2072
2073 (All the actions described in section 2.1 are also available.)
2074
2075 26.2 Bridges parameters
2076
2077 These parameters are available from the `Custom...' option on the
2078 `Type' menu.
2079
2080 _Width_, _Height_
2081
2082 Size of grid in squares.
2083
2084 _Difficulty_
2085
2086 Difficulty level of puzzle.
2087
2088 _Allow loops_
2089
2090 This is set by default. If cleared, puzzles will be generated in
2091 such a way that they are always soluble without creating a loop,
2092 and solutions which do involve a loop will be disallowed.
2093
2094 _Max. bridges per direction_
2095
2096 Maximum number of bridges in any particular direction. The
2097 default is 2, but you can change it to 1, 3 or 4. In general,
2098 fewer is easier.
2099
2100 _%age of island squares_
2101
2102 Gives a rough percentage of islands the generator will try and
2103 lay before finishing the puzzle. Certain layouts will not manage
2104 to lay enough islands; this is an upper bound.
2105
2106 _Expansion factor (%age)_
2107
2108 The grid generator works by picking an existing island at random
2109 (after first creating an initial island somewhere). It then
2110 decides on a direction (at random), and then works out how far
2111 it could extend before creating another island. This parameter
2112 determines how likely it is to extend as far as it can, rather
2113 than choosing somewhere closer.
2114
2115 High expansion factors usually mean easier puzzles with fewer
2116 possible islands; low expansion factors can create lots of
2117 tightly-packed islands.
2118
2119Chapter 27: Unequal
2120-------------------
2121
2122 You have a square grid; each square may contain a digit from 1 to
2123 the size of the grid, and some squares have clue signs between them.
2124 Your aim is to fully populate the grid with numbers such that:
2125
2126 - Each row contains only one occurrence of each digit
2127
2128 - Each column contains only one occurrence of each digit
2129
2130 - All the clue signs are satisfied.
2131
2132 There are two modes for this game, `Unequal' and `Adjacent'.
2133
2134 In `Unequal' mode, the clue signs are greater-than symbols
2135 indicating one square's value is greater than its neighbour's. In
2136 this mode not all clues may be visible, particularly at higher
2137 difficulty levels.
2138
2139 In `Adjacent' mode, the clue signs are bars indicating one square's
2140 value is numerically adjacent (i.e. one higher or one lower) than
2141 its neighbour. In this mode all clues are always visible: absence of
2142 a bar thus means that a square's value is definitely not numerically
2143 adjacent to that neighbour's.
2144
2145 In `Trivial' difficulty level (available via the `Custom' game type
2146 selector), there are no greater-than signs in `Unequal' mode; the
2147 puzzle is to solve the Latin square only.
2148
2149 At the time of writing, the `Unequal' mode of this puzzle is
2150 appearing in the Guardian weekly under the name `Futoshiki'.
2151
2152 Unequal was contributed to this collection by James Harvey.
2153
2154 27.1 Unequal controls
2155
2156 Unequal shares much of its control system with Solo.
2157
2158 To play Unequal, simply click the mouse in any empty square and then
2159 type a digit or letter on the keyboard to fill that square. If you
2160 make a mistake, click the mouse in the incorrect square and press
2161 Space to clear it again (or use the Undo feature).
2162
2163 If you _right_-click in a square and then type a number, that
2164 number will be entered in the square as a `pencil mark'. You can
2165 have pencil marks for multiple numbers in the same square. Squares
2166 containing filled-in numbers cannot also contain pencil marks.
2167
2168 The game pays no attention to pencil marks, so exactly what you
2169 use them for is up to you: you can use them as reminders that a
2170 particular square needs to be re-examined once you know more about
2171 a particular number, or you can use them as lists of the possible
2172 numbers in a given square, or anything else you feel like.
2173
2174 To erase a single pencil mark, right-click in the square and type
2175 the same number again.
2176
2177 All pencil marks in a square are erased when you left-click and type
2178 a number, or when you left-click and press space. Right-clicking and
2179 pressing space will also erase pencil marks.
2180
2181 As for Solo, the cursor keys can be used in conjunction with the
2182 digit keys to set numbers or pencil marks. You can also use the `M'
2183 key to auto-fill every numeric hint, ready for removal as required,
2184 or the `H' key to do the same but also to remove all obvious hints.
2185
2186 Alternatively, use the cursor keys to move the mark around the grid.
2187 Pressing the return key toggles the mark (from a normal mark to a
2188 pencil mark), and typing a number in is entered in the square in the
2189 appropriate way; typing in a 0 or using the space bar will clear a
2190 filled square.
2191
2192 Left-clicking a clue will mark it as done (grey it out), or unmark
2193 it if it is already marked. Holding Control or Shift and pressing
2194 an arrow key likewise marks any clue adjacent to the cursor in the
2195 given direction.
2196
2197 (All the actions described in section 2.1 are also available.)
2198
2199 27.2 Unequal parameters
2200
2201 These parameters are available from the `Custom...' option on the
2202 `Type' menu.
2203
2204 _Mode_
2205
2206 Mode of the puzzle (`Unequal' or `Adjacent')
2207
2208 _Size (s*s)_
2209
2210 Size of grid.
2211
2212 _Difficulty_
2213
2214 Controls the difficulty of the generated puzzle. At Trivial
2215 level, there are no greater-than signs; the puzzle is to solve
2216 the Latin square only. At Recursive level (only available via
2217 the `Custom' game type selector) backtracking will be required,
2218 but the solution should still be unique. The levels in between
2219 require increasingly complex reasoning to avoid having to
2220 backtrack.
2221
2222Chapter 28: Galaxies
2223--------------------
2224
2225 You have a rectangular grid containing a number of dots. Your aim is
2226 to draw edges along the grid lines which divide the rectangle into
2227 regions in such a way that every region is 180-degree rotationally
2228 symmetric, and contains exactly one dot which is located at its
2229 centre of symmetry.
2230
2231 This puzzle was invented by Nikoli [13], under the name `Tentai
2232 Show'; its name is commonly translated into English as `Spiral
2233 Galaxies'.
2234
2235 Galaxies was contributed to this collection by James Harvey.
2236
2237 [13] http://www.nikoli.co.jp/en/puzzles/astronomical_show.html
2238
2239 28.1 Galaxies controls
2240
2241 Left-click on any grid line to draw an edge if there isn't one
2242 already, or to remove one if there is. When you create a valid
2243 region (one which is closed, contains exactly one dot, is 180-degree
2244 symmetric about that dot, and contains no extraneous edges inside
2245 it) it will be highlighted automatically; so your aim is to have the
2246 whole grid highlighted in that way.
2247
2248 During solving, you might know that a particular grid square belongs
2249 to a specific dot, but not be sure of where the edges go and which
2250 other squares are connected to the dot. In order to mark this so you
2251 don't forget, you can right-click on the dot and drag, which will
2252 create an arrow marker pointing at the dot. Drop that in a square of
2253 your choice and it will remind you which dot it's associated with.
2254 You can also right-click on existing arrows to pick them up and move
2255 them, or destroy them by dropping them off the edge of the grid.
2256 (Also, if you're not sure which dot an arrow is pointing at, you can
2257 pick it up and move it around to make it clearer. It will swivel
2258 constantly as you drag it, to stay pointed at its parent dot.)
2259
2260 You can also use the cursor keys to move around the grid squares and
2261 lines. Pressing the return key when over a grid line will draw or
2262 clear its edge, as above. Pressing the return key when over a dot
2263 will pick up an arrow, to be dropped the next time the return key
2264 is pressed; this can also be used to move existing arrows around,
2265 removing them by dropping them on a dot or another arrow.
2266
2267 (All the actions described in section 2.1 are also available.)
2268
2269 28.2 Galaxies parameters
2270
2271 These parameters are available from the `Custom...' option on the
2272 `Type' menu.
2273
2274 _Width_, _Height_
2275
2276 Size of grid in squares.
2277
2278 _Difficulty_
2279
2280 Controls the difficulty of the generated puzzle. More difficult
2281 puzzles require more complex deductions, and the `Unreasonable'
2282 difficulty level may require backtracking.
2283
2284Chapter 29: Filling
2285-------------------
2286
2287 You have a grid of squares, some of which contain digits, and the
2288 rest of which are empty. Your job is to fill in digits in the empty
2289 squares, in such a way that each connected region of squares all
2290 containing the same digit has an area equal to that digit.
2291
2292 (`Connected region', for the purposes of this game, does not count
2293 diagonally separated squares as adjacent.)
2294
2295 For example, it follows that no square can contain a zero, and that
2296 two adjacent squares can not both contain a one. No region has an
2297 area greater than 9 (because then its area would not be a single
2298 digit).
2299
2300 Credit for this puzzle goes to Nikoli [14].
2301
2302 Filling was contributed to this collection by Jonas Koelker.
2303
2304 [14] http://www.nikoli.co.jp/en/puzzles/fillomino.html
2305
2306 29.1 Filling controls
2307
2308 To play Filling, simply click the mouse in any empty square and
2309 then type a digit on the keyboard to fill that square. By dragging
2310 the mouse, you can select multiple squares to fill with a single
2311 keypress. If you make a mistake, click the mouse in the incorrect
2312 square and press 0, Space, Backspace or Enter to clear it again (or
2313 use the Undo feature).
2314
2315 You can also move around the grid with the cursor keys; typing a
2316 digit will fill the square containing the cursor with that number;
2317 typing 0 will clear it. You can also select multiple squares for
2318 numbering or clearing with the return and arrow keys, before typing
2319 a digit to fill or clear the highlighted squares (as above). The
2320 space bar adds and removes single squares to and from the selection.
2321 Backspace and escape remove all squares from the selection.
2322
2323 (All the actions described in section 2.1 are also available.)
2324
2325 29.2 Filling parameters
2326
2327 Filling allows you to configure the number of rows and columns of
2328 the grid, through the `Type' menu.
2329
2330Chapter 30: Keen
2331----------------
2332
2333 You have a square grid; each square may contain a digit from 1 to
2334 the size of the grid. The grid is divided into blocks of varying
2335 shape and size, with arithmetic clues written in them. Your aim is
2336 to fully populate the grid with digits such that:
2337
2338 - Each row contains only one occurrence of each digit
2339
2340 - Each column contains only one occurrence of each digit
2341
2342 - The digits in each block can be combined to form the number
2343 stated in the clue, using the arithmetic operation given in the
2344 clue. That is:
2345
2346 - An addition clue means that the sum of the digits in the
2347 block must be the given number. For example, `15+' means the
2348 contents of the block adds up to fifteen.
2349
2350 - A multiplication clue (e.g. `60*'), similarly, means that
2351 the product of the digits in the block must be the given
2352 number.
2353
2354 - A subtraction clue will always be written in a block of
2355 size two, and it means that one of the digits in the block
2356 is greater than the other by the given amount. For example,
2357 `2-' means that one of the digits in the block is 2 more
2358 than the other, or equivalently that one digit minus the
2359 other one is 2. The two digits could be either way round,
2360 though.
2361
2362 - A division clue (e.g. `3/'), similarly, is always in a block
2363 of size two and means that one digit divided by the other is
2364 equal to the given amount.
2365
2366 Note that a block may contain the same digit more than once
2367 (provided the identical ones are not in the same row and
2368 column). This rule is precisely the opposite of the rule in
2369 Solo's `Killer' mode (see chapter 11).
2370
2371 This puzzle appears in the Times under the name `KenKen'.
2372
2373 30.1 Keen controls
2374
2375 Keen shares much of its control system with Solo (and Unequal).
2376
2377 To play Keen, simply click the mouse in any empty square and then
2378 type a digit on the keyboard to fill that square. If you make a
2379 mistake, click the mouse in the incorrect square and press Space to
2380 clear it again (or use the Undo feature).
2381
2382 If you _right_-click in a square and then type a number, that
2383 number will be entered in the square as a `pencil mark'. You can
2384 have pencil marks for multiple numbers in the same square. Squares
2385 containing filled-in numbers cannot also contain pencil marks.
2386
2387 The game pays no attention to pencil marks, so exactly what you
2388 use them for is up to you: you can use them as reminders that a
2389 particular square needs to be re-examined once you know more about
2390 a particular number, or you can use them as lists of the possible
2391 numbers in a given square, or anything else you feel like.
2392
2393 To erase a single pencil mark, right-click in the square and type
2394 the same number again.
2395
2396 All pencil marks in a square are erased when you left-click and type
2397 a number, or when you left-click and press space. Right-clicking and
2398 pressing space will also erase pencil marks.
2399
2400 As for Solo, the cursor keys can be used in conjunction with the
2401 digit keys to set numbers or pencil marks. Use the cursor keys to
2402 move a highlight around the grid, and type a digit to enter it in
2403 the highlighted square. Pressing return toggles the highlight into a
2404 mode in which you can enter or remove pencil marks.
2405
2406 Pressing M will fill in a full set of pencil marks in every square
2407 that does not have a main digit in it.
2408
2409 (All the actions described in section 2.1 are also available.)
2410
2411 30.2 Keen parameters
2412
2413 These parameters are available from the `Custom...' option on the
2414 `Type' menu.
2415
2416 _Grid size_
2417
2418 Specifies the size of the grid. Lower limit is 3; upper limit is
2419 9 (because the user interface would become more difficult with
2420 `digits' bigger than 9!).
2421
2422 _Difficulty_
2423
2424 Controls the difficulty of the generated puzzle. At Unreasonable
2425 level, some backtracking will be required, but the solution
2426 should still be unique. The remaining levels require
2427 increasingly complex reasoning to avoid having to backtrack.
2428
2429 _Multiplication only_
2430
2431 If this is enabled, all boxes will be multiplication boxes. With
2432 this rule, the puzzle is known as `Inshi No Heya'.
2433
2434Chapter 31: Towers
2435------------------
2436
2437 You have a square grid. On each square of the grid you can build
2438 a tower, with its height ranging from 1 to the size of the grid.
2439 Around the edge of the grid are some numeric clues.
2440
2441 Your task is to build a tower on every square, in such a way that:
2442
2443 - Each row contains every possible height of tower once
2444
2445 - Each column contains every possible height of tower once
2446
2447 - Each numeric clue describes the number of towers that can be
2448 seen if you look into the square from that direction, assuming
2449 that shorter towers are hidden behind taller ones. For example,
2450 in a 5x5 grid, a clue marked `5' indicates that the five tower
2451 heights must appear in increasing order (otherwise you would
2452 not be able to see all five towers), whereas a clue marked `1'
2453 indicates that the tallest tower (the one marked 5) must come
2454 first.
2455
2456 In harder or larger puzzles, some towers will be specified for you
2457 as well as the clues round the edge, and some edge clues may be
2458 missing.
2459
2460 This puzzle appears on the web under various names, particularly
2461 `Skyscrapers', but I don't know who first invented it.
2462
2463 31.1 Towers controls
2464
2465 Towers shares much of its control system with Solo, Unequal and
2466 Keen.
2467
2468 To play Towers, simply click the mouse in any empty square and then
2469 type a digit on the keyboard to fill that square with a tower of
2470 the given height. If you make a mistake, click the mouse in the
2471 incorrect square and press Space to clear it again (or use the Undo
2472 feature).
2473
2474 If you _right_-click in a square and then type a number, that
2475 number will be entered in the square as a `pencil mark'. You can
2476 have pencil marks for multiple numbers in the same square. A square
2477 containing a tower cannot also contain pencil marks.
2478
2479 The game pays no attention to pencil marks, so exactly what you
2480 use them for is up to you: you can use them as reminders that a
2481 particular square needs to be re-examined once you know more about
2482 a particular number, or you can use them as lists of the possible
2483 numbers in a given square, or anything else you feel like.
2484
2485 To erase a single pencil mark, right-click in the square and type
2486 the same number again.
2487
2488 All pencil marks in a square are erased when you left-click and type
2489 a number, or when you left-click and press space. Right-clicking and
2490 pressing space will also erase pencil marks.
2491
2492 As for Solo, the cursor keys can be used in conjunction with the
2493 digit keys to set numbers or pencil marks. Use the cursor keys to
2494 move a highlight around the grid, and type a digit to enter it in
2495 the highlighted square. Pressing return toggles the highlight into a
2496 mode in which you can enter or remove pencil marks.
2497
2498 Pressing M will fill in a full set of pencil marks in every square
2499 that does not have a main digit in it.
2500
2501 Left-clicking a clue will mark it as done (grey it out), or unmark
2502 it if it is already marked. Holding Control or Shift and pressing an
2503 arrow key likewise marks any clue in the given direction.
2504
2505 (All the actions described in section 2.1 are also available.)
2506
2507 31.2 Towers parameters
2508
2509 These parameters are available from the `Custom...' option on the
2510 `Type' menu.
2511
2512 _Grid size_
2513
2514 Specifies the size of the grid. Lower limit is 3; upper limit is
2515 9 (because the user interface would become more difficult with
2516 `digits' bigger than 9!).
2517
2518 _Difficulty_
2519
2520 Controls the difficulty of the generated puzzle. At Unreasonable
2521 level, some backtracking will be required, but the solution
2522 should still be unique. The remaining levels require
2523 increasingly complex reasoning to avoid having to backtrack.
2524
2525Chapter 32: Singles
2526-------------------
2527
2528 You have a grid of white squares, all of which contain numbers. Your
2529 task is to colour some of the squares black (removing the number) so
2530 as to satisfy all of the following conditions:
2531
2532 - No number occurs more than once in any row or column.
2533
2534 - No black square is horizontally or vertically adjacent to any
2535 other black square.
2536
2537 - The remaining white squares must all form one contiguous region
2538 (connected by edges, not just touching at corners).
2539
2540 Credit for this puzzle goes to Nikoli [15] who call it Hitori.
2541
2542 Singles was contributed to this collection by James Harvey.
2543
2544 [15] http://www.nikoli.com/en/puzzles/hitori.html (beware of Flash)
2545
2546 32.1 Singles controls
2547
2548 Left-clicking on an empty square will colour it black; left-clicking
2549 again will restore the number. Right-clicking will add a circle
2550 (useful for indicating that a cell is definitely not black).
2551
2552 You can also use the cursor keys to move around the grid. Pressing
2553 the return or space keys will turn a square black or add a circle
2554 respectively, and pressing the key again will restore the number or
2555 remove the circle.
2556
2557 (All the actions described in section 2.1 are also available.)
2558
2559 32.2 Singles parameters
2560
2561 These parameters are available from the `Custom...' option on the
2562 `Type' menu.
2563
2564 _Width_, _Height_
2565
2566 Size of grid in squares.
2567
2568 _Difficulty_
2569
2570 Controls the difficulty of the generated puzzle.
2571
2572Chapter 33: Magnets
2573-------------------
2574
2575 A rectangular grid has been filled with a mixture of magnets (that
2576 is, dominoes with one positive end and one negative end) and blank
2577 dominoes (that is, dominoes with two neutral poles). These dominoes
2578 are initially only seen in silhouette. Around the grid are placed a
2579 number of clues indicating the number of positive and negative poles
2580 contained in certain columns and rows.
2581
2582 Your aim is to correctly place the magnets and blank dominoes such
2583 that all the clues are satisfied, with the additional constraint
2584 that no two similar magnetic poles may be orthogonally adjacent
2585 (since they repel). Neutral poles do not repel, and can be adjacent
2586 to any other pole.
2587
2588 Credit for this puzzle goes to Janko [16].
2589
2590 Magnets was contributed to this collection by James Harvey.
2591
2592 [16] http://www.janko.at/Raetsel/Magnete/index.htm
2593
2594 33.1 Magnets controls
2595
2596 Left-clicking on an empty square places a magnet at that position
2597 with the positive pole on the square and the negative pole on the
2598 other half of the magnet; left-clicking again reverses the polarity,
2599 and a third click removes the magnet.
2600
2601 Right-clicking on an empty square places a blank domino there.
2602 Right-clicking again places two question marks on the domino,
2603 signifying `this cannot be blank' (which can be useful to note
2604 deductions while solving), and right-clicking again empties the
2605 domino.
2606
2607 Left-clicking a clue will mark it as done (grey it out), or unmark
2608 it if it is already marked.
2609
2610 You can also use the cursor keys to move a cursor around the grid.
2611 Pressing the return key will lay a domino with a positive pole at
2612 that position; pressing again reverses the polarity and then removes
2613 the domino, as with left-clicking. Using the space bar allows
2614 placement of blank dominoes and cannot-be-blank hints, as for right-
2615 clicking.
2616
2617 (All the actions described in section 2.1 are also available.)
2618
2619 33.2 Magnets parameters
2620
2621 These parameters are available from the `Custom...' option on the
2622 `Type' menu.
2623
2624 _Width_, _Height_
2625
2626 Size of grid in squares. There will be half _Width_ x _Height_
2627 dominoes in the grid: if this number is odd then one square will
2628 be blank.
2629
2630 (Grids with at least one odd dimension tend to be easier to
2631 solve.)
2632
2633 _Difficulty_
2634
2635 Controls the difficulty of the generated puzzle. At Tricky
2636 level, you are required to make more deductions about empty
2637 dominoes and row/column counts.
2638
2639 _Strip clues_
2640
2641 If true, some of the clues around the grid are removed at
2642 generation time, making the puzzle more difficult.
2643
2644Chapter 34: Signpost
2645--------------------
2646
2647 You have a grid of squares; each square (except the last one)
2648 contains an arrow, and some squares also contain numbers. Your job
2649 is to connect the squares to form a continuous list of numbers
2650 starting at 1 and linked in the direction of the arrows - so the
2651 arrow inside the square with the number 1 will point to the square
2652 containing the number 2, which will point to the square containing
2653 the number 3, etc. Each square can be any distance away from the
2654 previous one, as long as it is somewhere in the direction of the
2655 arrow.
2656
2657 By convention the first and last numbers are shown; one or more
2658 interim numbers may also appear at the beginning.
2659
2660 Credit for this puzzle goes to Janko [17], who call it `Pfeilpfad'
2661 (`arrow path').
2662
2663 Signpost was contributed to this collection by James Harvey.
2664
2665 [17] http://janko.at/Raetsel/Pfeilpfad/index.htm
2666
2667 34.1 Signpost controls
2668
2669 To play Signpost, you connect squares together by dragging from
2670 one square to another, indicating that they are adjacent in the
2671 sequence. Drag with the left button from a square to its successor,
2672 or with the right button from a square to its predecessor.
2673
2674 If you connect together two squares in this way and one of them has
2675 a number in it, the appropriate number will appear in the other
2676 square. If you connect two non-numbered squares, they will be
2677 assigned temporary algebraic labels: on the first occasion, they
2678 will be labelled `a' and `a+1', and then `b' and `b+1', and so on.
2679 Connecting more squares on to the ends of such a chain will cause
2680 them all to be labelled with the same letter.
2681
2682 When you left-click or right-click in a square, the legal squares to
2683 connect it to will be shown.
2684
2685 The arrow in each square starts off black, and goes grey once you
2686 connect the square to its successor. Also, each square which needs
2687 a predecessor has a small dot in the bottom left corner, which
2688 vanishes once you link a square to it. So your aim is always to
2689 connect a square with a black arrow to a square with a dot.
2690
2691 To remove any links for a particular square (both incoming and
2692 outgoing), left-drag it off the grid. To remove a whole chain,
2693 right-drag any square in the chain off the grid.
2694
2695 You can also use the cursor keys to move around the grid squares
2696 and lines. Pressing the return key when over a square starts a link
2697 operation, and pressing the return key again over a square will
2698 finish the link, if allowable. Pressing the space bar over a square
2699 will show the other squares pointing to it, and allow you to form a
2700 backward link, and pressing the space bar again cancels this.
2701
2702 (All the actions described in section 2.1 are also available.)
2703
2704 34.2 Signpost parameters
2705
2706 These parameters are available from the `Custom...' option on the
2707 `Type' menu.
2708
2709 _Width_, _Height_
2710
2711 Size of grid in squares.
2712
2713 _Force start/end to corners_
2714
2715 If true, the start and end squares are always placed in opposite
2716 corners (the start at the top left, and the end at the bottom
2717 right). If false the start and end squares are placed randomly
2718 (although always both shown).
2719
2720Chapter 35: Range
2721-----------------
2722
2723 You have a grid of squares; some squares contain numbers. Your job
2724 is to colour some of the squares black, such that several criteria
2725 are satisfied:
2726
2727 - no square with a number is coloured black.
2728
2729 - no two black squares are adjacent (horizontally or vertically).
2730
2731 - for any two white squares, there is a path between them using
2732 only white squares.
2733
2734 - for each square with a number, that number denotes the total
2735 number of white squares reachable from that square going in a
2736 straight line in any horizontal or vertical direction until
2737 hitting a wall or a black square; the square with the number is
2738 included in the total (once).
2739
2740 For instance, a square containing the number one must have four
2741 black squares as its neighbours by the last criterion; but then it's
2742 impossible for it to be connected to any outside white square, which
2743 violates the second to last criterion. So no square will contain the
2744 number one.
2745
2746 Credit for this puzzle goes to Nikoli, who have variously called it
2747 `Kurodoko', `Kuromasu' or `Where is Black Cells'. [18].
2748
2749 Range was contributed to this collection by Jonas Koelker.
2750
2751 [18] http://www.nikoli.co.jp/en/puzzles/where_is_black_cells.html
2752
2753 35.1 Range controls
2754
2755 Click with the left button to paint a square black, or with the
2756 right button to mark a square with a dot to indicate that you are
2757 sure it should _not_ be painted black. Repeated clicking with either
2758 button will cycle the square through the three possible states
2759 (filled, dotted or empty) in opposite directions.
2760
2761 You can also use the cursor keys to move around the grid squares.
2762 Pressing Return does the same as clicking with the left button,
2763 while pressing Space does the same as a right button click. Moving
2764 with the cursor keys while holding Shift will place dots in all
2765 squares that are moved through.
2766
2767 (All the actions described in section 2.1 are also available.)
2768
2769 35.2 Range parameters
2770
2771 These parameters are available from the `Custom...' option on the
2772 `Type' menu.
2773
2774 _Width_, _Height_
2775
2776 Size of grid in squares.
2777
2778Chapter 36: Pearl
2779-----------------
2780
2781 You have a grid of squares. Your job is to draw lines between the
2782 centres of horizontally or vertically adjacent squares, so that the
2783 lines form a single closed loop. In the resulting grid, some of the
2784 squares that the loop passes through will contain corners, and some
2785 will be straight horizontal or vertical lines. (And some squares can
2786 be completely empty - the loop doesn't have to pass through every
2787 square.)
2788
2789 Some of the squares contain black and white circles, which are clues
2790 that the loop must satisfy.
2791
2792 A black circle in a square indicates that that square is a corner,
2793 but neither of the squares adjacent to it in the loop is also a
2794 corner.
2795
2796 A white circle indicates that the square is a straight edge, but _at
2797 least one_ of the squares adjacent to it in the loop is a corner.
2798
2799 (In both cases, the clue only constrains the two squares adjacent
2800 _in the loop_, that is, the squares that the loop passes into after
2801 leaving the clue square. The squares that are only adjacent _in the
2802 grid_ are not constrained.)
2803
2804 Credit for this puzzle goes to Nikoli, who call it `Masyu'. [19]
2805
2806 Thanks to James Harvey for assistance with the implementation.
2807
2808 [19] http://www.nikoli.co.jp/en/puzzles/masyu.html (beware of Flash)
2809
2810 36.1 Pearl controls
2811
2812 Click with the left button on a grid edge to draw a segment of the
2813 loop through that edge, or to remove a segment once it is drawn.
2814
2815 Drag with the left button through a series of squares to draw more
2816 than one segment of the loop in one go. Alternatively, drag over an
2817 existing part of the loop to undraw it, or to undraw part of it and
2818 then go in a different direction.
2819
2820 Click with the right button on a grid edge to mark it with a cross,
2821 indicating that you are sure the loop does not go through that edge.
2822 (For instance, if you have decided which of the squares adjacent
2823 to a white clue has to be a corner, but don't yet know which way
2824 the corner turns, you might mark the one way it _can't_ go with a
2825 cross.)
2826
2827 Alternatively, use the cursor keys to move the cursor. Use the Enter
2828 key to begin and end keyboard `drag' operations. Use the Space,
2829 Escape or Backspace keys to cancel the drag. Or, hold Control while
2830 dragging with the cursor keys to toggle segments as you move between
2831 squares.
2832
2833 Pressing Control-Shift-arrowkey or Shift-arrowkey simulates a left
2834 or right click, respectively, on the edge in the direction of the
2835 key.
2836
2837 (All the actions described in section 2.1 are also available.)
2838
2839 36.2 Pearl parameters
2840
2841 These parameters are available from the `Custom...' option on the
2842 `Type' menu.
2843
2844Chapter 37: Undead
2845------------------
2846
2847 You are given a grid of squares, some of which contain diagonal
2848 mirrors. Every square which is not a mirror must be filled with one
2849 of three types of undead monster: a ghost, a vampire, or a zombie.
2850
2851 Vampires can be seen directly, but are invisible when reflected in
2852 mirrors. Ghosts are the opposite way round: they can be seen in
2853 mirrors, but are invisible when looked at directly. Zombies are
2854 visible by any means.
2855
2856 You are also told the total number of each type of monster in the
2857 grid. Also around the edge of the grid are written numbers, which
2858 indicate how many monsters can be seen if you look into the grid
2859 along a row or column starting from that position. (The diagonal
2860 mirrors are reflective on both sides. If your reflected line of
2861 sight crosses the same monster more than once, the number will count
2862 it each time it is visible, not just once.)
2863
2864 This puzzle type was invented by David Millar, under the name
2865 `Haunted Mirror Maze'. See [20] for more details.
2866
2867 Undead was contributed to this collection by Steffen Bauer.
2868
2869 [20] http://www.janko.at/Raetsel/Spukschloss/index.htm
2870
2871 37.1 Undead controls
2872
2873 Undead has a similar control system to Solo, Unequal and Keen.
2874
2875 To play Undead, click the mouse in any empty square and then type
2876 a letter on the keyboard indicating the type of monster: `G' for
2877 a ghost, `V' for a vampire, or `Z' for a zombie. If you make a
2878 mistake, click the mouse in the incorrect square and press Space to
2879 clear it again (or use the Undo feature).
2880
2881 If you _right_-click in a square and then type a letter, the
2882 corresponding monster will be shown in reduced size in that square,
2883 as a `pencil mark'. You can have pencil marks for multiple monsters
2884 in the same square. A square containing a full-size monster cannot
2885 also contain pencil marks.
2886
2887 The game pays no attention to pencil marks, so exactly what you
2888 use them for is up to you: you can use them as reminders that a
2889 particular square needs to be re-examined once you know more about
2890 a particular monster, or you can use them as lists of the possible
2891 monster in a given square, or anything else you feel like.
2892
2893 To erase a single pencil mark, right-click in the square and type
2894 the same letter again.
2895
2896 All pencil marks in a square are erased when you left-click and type
2897 a monster letter, or when you left-click and press Space. Right-
2898 clicking and pressing space will also erase pencil marks.
2899
2900 As for Solo, the cursor keys can be used in conjunction with the
2901 letter keys to place monsters or pencil marks. Use the cursor keys
2902 to move a highlight around the grid, and type a monster letter to
2903 enter it in the highlighted square. Pressing return toggles the
2904 highlight into a mode in which you can enter or remove pencil marks.
2905
2906 If you prefer plain letters of the alphabet to cute monster
2907 pictures, you can press `A' to toggle between showing the monsters
2908 as monsters or showing them as letters.
2909
2910 Left-clicking a clue will mark it as done (grey it out), or unmark
2911 it if it is already marked.
2912
2913 (All the actions described in section 2.1 are also available.)
2914
2915 37.2 Undead parameters
2916
2917 These parameters are available from the `Custom...' option on the
2918 `Type' menu.
2919
2920 _Width_, _Height_
2921
2922 Size of grid in squares.
2923
2924 _Difficulty_
2925
2926 Controls the difficulty of the generated puzzle.
2927
2928Chapter 38: Unruly
2929------------------
2930
2931 You are given a grid of squares, which you must colour either black
2932 or white. Some squares are provided as clues; the rest are left for
2933 you to fill in. Each row and column must contain the same number
2934 of black and white squares, and no row or column may contain three
2935 consecutive squares of the same colour.
2936
2937 This puzzle type was invented by Adolfo Zanellati, under the name
2938 `Tohu wa Vohu'. See [21] for more details.
2939
2940 Unruly was contributed to this collection by Lennard Sprong.
2941
2942 [21] http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm
2943
2944 38.1 Unruly controls
2945
2946 To play Unruly, click the mouse in a square to change its colour.
2947 Left-clicking an empty square will turn it black, and right-clicking
2948 will turn it white. Keep clicking the same button to cycle through
2949 the three possible states for the square. If you middle-click in a
2950 square it will be reset to empty.
2951
2952 You can also use the cursor keys to move around the grid. Pressing
2953 the return or space keys will turn an empty square black or white
2954 respectively (and then cycle the colours in the same way as the
2955 mouse buttons), and pressing Backspace will reset a square to empty.
2956
2957 (All the actions described in section 2.1 are also available.)
2958
2959 38.2 Unruly parameters
2960
2961 These parameters are available from the `Custom...' option on the
2962 `Type' menu.
2963
2964 _Width_, _Height_
2965
2966 Size of grid in squares. (Note that the rules of the game
2967 require both the width and height to be even numbers.)
2968
2969 _Difficulty_
2970
2971 Controls the difficulty of the generated puzzle.
2972
2973 _Unique rows and columns_
2974
2975 If enabled, no two rows are permitted to have exactly the same
2976 pattern, and likewise columns. (A row and a column can match,
2977 though.)
2978
2979Chapter 39: Flood
2980-----------------
2981
2982 You are given a grid of squares, coloured at random in multiple
2983 colours. In each move, you can flood-fill the top left square in a
2984 colour of your choice (i.e. every square reachable from the starting
2985 square by an orthogonally connected path of squares all the same
2986 colour will be filled in the new colour). As you do this, more and
2987 more of the grid becomes connected to the starting square.
2988
2989 Your aim is to make the whole grid the same colour, in as few moves
2990 as possible. The game will set a limit on the number of moves, based
2991 on running its own internal solver. You win if you can make the
2992 whole grid the same colour in that many moves or fewer.
2993
2994 I saw this game (with a fixed grid size, fixed number of colours,
2995 and fixed move limit) at http://floodit.appspot.com (no longer
2996 accessible).
2997
2998 39.1 Flood controls
2999
3000 To play Flood, click the mouse in a square. The top left corner and
3001 everything connected to it will be flood-filled with the colour of
3002 the square you clicked. Clicking a square the same colour as the top
3003 left corner has no effect, and therefore does not count as a move.
3004
3005 You can also use the cursor keys to move a cursor (outline black
3006 square) around the grid. Pressing the return key will fill the top
3007 left corner in the colour of the square under the cursor.
3008
3009 (All the actions described in section 2.1 are also available.)
3010
3011 39.2 Flood parameters
3012
3013 These parameters are available from the `Custom...' option on the
3014 `Type' menu.
3015
3016 _Width_, _Height_
3017
3018 Size of the grid, in squares.
3019
3020 _Colours_
3021
3022 Number of colours used to fill the grid. Must be at least 3
3023 (with two colours there would only be one legal move at any
3024 stage, hence no choice to make at all), and at most 10.
3025
3026 _Extra moves permitted_
3027
3028 Controls the difficulty of the puzzle, by increasing the move
3029 limit. In each new grid, Flood will run an internal solver to
3030 generate its own solution, and then the value in this field
3031 will be added to the length of Flood's solution to generate the
3032 game's move limit. So a value of 0 requires you to be just as
3033 efficient as Flood's automated solver, and a larger value makes
3034 it easier.
3035
3036 (Note that Flood's internal solver will not necessarily find the
3037 shortest possible solution, though I believe it's pretty close.
3038 For a real challenge, set this value to 0 and then try to solve
3039 a grid in _strictly fewer_ moves than the limit you're given!)
3040
3041Chapter 40: Tracks
3042------------------
3043
3044 You are given a grid of squares, some of which are filled with train
3045 tracks. You need to complete the track from A to B so that the
3046 rows and columns contain the same number of track segments as are
3047 indicated in the clues to the top and right of the grid.
3048
3049 There are only straight and 90 degree curved rails, and the track
3050 may not cross itself.
3051
3052 Tracks was contributed to this collection by James Harvey.
3053
3054 40.1 Tracks controls
3055
3056 Left-clicking on an edge between two squares adds a track segment
3057 between the two squares. Right-clicking on an edge adds a cross on
3058 the edge, indicating no track is possible there.
3059
3060 Left-clicking in a square adds a colour indicator showing that
3061 you know the square must contain a track, even if you don't know
3062 which edges it crosses yet. Right-clicking in a square adds a cross
3063 indicating it contains no track segment.
3064
3065 Left- or right-dragging between squares allows you to lay a straight
3066 line of is-track or is-not-track indicators, useful for filling in
3067 rows or columns to match the clue.
3068
3069 (All the actions described in section 2.1 are also available.)
3070
3071 40.2 Tracks parameters
3072
3073 These parameters are available from the `Custom...' option on the
3074 `Type' menu.
3075
3076 _Width_, _Height_
3077
3078 Size of the grid, in squares.
3079
3080 _Difficulty_
3081
3082 Controls the difficulty of the generated puzzle: at Tricky
3083 level, you are required to make more deductions regarding
3084 disregarding moves that would lead to impossible crossings
3085 later.
3086
3087 _Disallow consecutive 1 clues_
3088
3089 Controls whether the Tracks game generation permits two adjacent
3090 rows or columns to have a 1 clue, or permits the row or column
3091 of the track's endpoint to have a 1 clue. By default this is
3092 not permitted, to avoid long straight boring segments of track
3093 and make the games more twiddly and interesting. If you want to
3094 restore the possibility, turn this option off.
3095
3096Chapter 41: Palisade
3097--------------------
3098
3099 You're given a grid of squares, some of which contain numbers. Your
3100 goal is to subdivide the grid into contiguous regions, all of the
3101 same (given) size, such that each square containing a number is
3102 adjacent to exactly that many edges (including those between the
3103 inside and the outside of the grid).
3104
3105 Credit for this puzzle goes to Nikoli, who call it `Five Cells'.
3106 [22].
3107
3108 Palisade was contributed to this collection by Jonas Koelker.
3109
3110 [22] http://nikoli.co.jp/en/puzzles/five_cells.html
3111
3112 41.1 Palisade controls
3113
3114 Left-click to place an edge. Right-click to indicate `no edge'.
3115 Alternatively, the arrow keys will move a keyboard cursor. Holding
3116 Control while pressing an arrow key will place an edge. Press Shift-
3117 arrowkey to switch off an edge. Repeat an action to perform its
3118 inverse.
3119
3120 (All the actions described in section 2.1 are also available.)
3121
3122 41.2 Palisade parameters
3123
3124 These parameters are available from the `Custom...' option on the
3125 `Type' menu.
3126
3127 _Width_, _Height_
3128
3129 Size of grid in squares.
3130
3131 _Region size_
3132
3133 The size of the regions into which the grid must be subdivided.
3134
3135Appendix A: Licence
3136-------------------
3137
3138 This software is copyright 2004-2014 Simon Tatham.
3139
3140 Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas
3141 Koelker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd
3142 Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens.
3143
3144 Permission is hereby granted, free of charge, to any person
3145 obtaining a copy of this software and associated documentation files
3146 (the `Software'), to deal in the Software without restriction,
3147 including without limitation the rights to use, copy, modify, merge,
3148 publish, distribute, sublicense, and/or sell copies of the Software,
3149 and to permit persons to whom the Software is furnished to do so,
3150 subject to the following conditions:
3151
3152 The above copyright notice and this permission notice shall be
3153 included in all copies or substantial portions of the Software.
3154
3155 THE SOFTWARE IS PROVIDED `AS IS', WITHOUT WARRANTY OF ANY KIND,
3156 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
3157 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
3158 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
3159 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3160 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3161 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3162 SOFTWARE.
3163
diff --git a/apps/plugins/puzzles/src/random.c b/apps/plugins/puzzles/src/random.c
new file mode 100644
index 0000000000..fb54560c95
--- /dev/null
+++ b/apps/plugins/puzzles/src/random.c
@@ -0,0 +1,351 @@
1/*
2 * random.c: Internal random number generator, guaranteed to work
3 * the same way on all platforms. Used when generating an initial
4 * game state from a random game seed; required to ensure that game
5 * seeds can be exchanged between versions of a puzzle compiled for
6 * different platforms.
7 *
8 * The generator is based on SHA-1. This is almost certainly
9 * overkill, but I had the SHA-1 code kicking around and it was
10 * easier to reuse it than to do anything else!
11 */
12
13#include <assert.h>
14#include <string.h>
15#include <stdio.h>
16
17#include "puzzles.h"
18
19/* ----------------------------------------------------------------------
20 * Core SHA algorithm: processes 16-word blocks into a message digest.
21 */
22
23#define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) )
24
25static void SHA_Core_Init(uint32 h[5])
26{
27 h[0] = 0x67452301;
28 h[1] = 0xefcdab89;
29 h[2] = 0x98badcfe;
30 h[3] = 0x10325476;
31 h[4] = 0xc3d2e1f0;
32}
33
34static void SHATransform(uint32 * digest, uint32 * block)
35{
36 uint32 w[80];
37 uint32 a, b, c, d, e;
38 int t;
39
40 for (t = 0; t < 16; t++)
41 w[t] = block[t];
42
43 for (t = 16; t < 80; t++) {
44 uint32 tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
45 w[t] = rol(tmp, 1);
46 }
47
48 a = digest[0];
49 b = digest[1];
50 c = digest[2];
51 d = digest[3];
52 e = digest[4];
53
54 for (t = 0; t < 20; t++) {
55 uint32 tmp =
56 rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999;
57 e = d;
58 d = c;
59 c = rol(b, 30);
60 b = a;
61 a = tmp;
62 }
63 for (t = 20; t < 40; t++) {
64 uint32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0x6ed9eba1;
65 e = d;
66 d = c;
67 c = rol(b, 30);
68 b = a;
69 a = tmp;
70 }
71 for (t = 40; t < 60; t++) {
72 uint32 tmp = rol(a,
73 5) + ((b & c) | (b & d) | (c & d)) + e + w[t] +
74 0x8f1bbcdc;
75 e = d;
76 d = c;
77 c = rol(b, 30);
78 b = a;
79 a = tmp;
80 }
81 for (t = 60; t < 80; t++) {
82 uint32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0xca62c1d6;
83 e = d;
84 d = c;
85 c = rol(b, 30);
86 b = a;
87 a = tmp;
88 }
89
90 digest[0] += a;
91 digest[1] += b;
92 digest[2] += c;
93 digest[3] += d;
94 digest[4] += e;
95}
96
97/* ----------------------------------------------------------------------
98 * Outer SHA algorithm: take an arbitrary length byte string,
99 * convert it into 16-word blocks with the prescribed padding at
100 * the end, and pass those blocks to the core SHA algorithm.
101 */
102
103void SHA_Init(SHA_State * s)
104{
105 SHA_Core_Init(s->h);
106 s->blkused = 0;
107 s->lenhi = s->lenlo = 0;
108}
109
110void SHA_Bytes(SHA_State * s, const void *p, int len)
111{
112 unsigned char *q = (unsigned char *) p;
113 uint32 wordblock[16];
114 uint32 lenw = len;
115 int i;
116
117 /*
118 * Update the length field.
119 */
120 s->lenlo += lenw;
121 s->lenhi += (s->lenlo < lenw);
122
123 if (s->blkused && s->blkused + len < 64) {
124 /*
125 * Trivial case: just add to the block.
126 */
127 memcpy(s->block + s->blkused, q, len);
128 s->blkused += len;
129 } else {
130 /*
131 * We must complete and process at least one block.
132 */
133 while (s->blkused + len >= 64) {
134 memcpy(s->block + s->blkused, q, 64 - s->blkused);
135 q += 64 - s->blkused;
136 len -= 64 - s->blkused;
137 /* Now process the block. Gather bytes big-endian into words */
138 for (i = 0; i < 16; i++) {
139 wordblock[i] =
140 (((uint32) s->block[i * 4 + 0]) << 24) |
141 (((uint32) s->block[i * 4 + 1]) << 16) |
142 (((uint32) s->block[i * 4 + 2]) << 8) |
143 (((uint32) s->block[i * 4 + 3]) << 0);
144 }
145 SHATransform(s->h, wordblock);
146 s->blkused = 0;
147 }
148 memcpy(s->block, q, len);
149 s->blkused = len;
150 }
151}
152
153void SHA_Final(SHA_State * s, unsigned char *output)
154{
155 int i;
156 int pad;
157 unsigned char c[64];
158 uint32 lenhi, lenlo;
159
160 if (s->blkused >= 56)
161 pad = 56 + 64 - s->blkused;
162 else
163 pad = 56 - s->blkused;
164
165 lenhi = (s->lenhi << 3) | (s->lenlo >> (32 - 3));
166 lenlo = (s->lenlo << 3);
167
168 memset(c, 0, pad);
169 c[0] = 0x80;
170 SHA_Bytes(s, &c, pad);
171
172 c[0] = (unsigned char)((lenhi >> 24) & 0xFF);
173 c[1] = (unsigned char)((lenhi >> 16) & 0xFF);
174 c[2] = (unsigned char)((lenhi >> 8) & 0xFF);
175 c[3] = (unsigned char)((lenhi >> 0) & 0xFF);
176 c[4] = (unsigned char)((lenlo >> 24) & 0xFF);
177 c[5] = (unsigned char)((lenlo >> 16) & 0xFF);
178 c[6] = (unsigned char)((lenlo >> 8) & 0xFF);
179 c[7] = (unsigned char)((lenlo >> 0) & 0xFF);
180
181 SHA_Bytes(s, &c, 8);
182
183 for (i = 0; i < 5; i++) {
184 output[i * 4] = (unsigned char)((s->h[i] >> 24) & 0xFF);
185 output[i * 4 + 1] = (unsigned char)((s->h[i] >> 16) & 0xFF);
186 output[i * 4 + 2] = (unsigned char)((s->h[i] >> 8) & 0xFF);
187 output[i * 4 + 3] = (unsigned char)((s->h[i]) & 0xFF);
188 }
189}
190
191void SHA_Simple(const void *p, int len, unsigned char *output)
192{
193 SHA_State s;
194
195 SHA_Init(&s);
196 SHA_Bytes(&s, p, len);
197 SHA_Final(&s, output);
198}
199
200/* ----------------------------------------------------------------------
201 * The random number generator.
202 */
203
204struct random_state {
205 unsigned char seedbuf[40];
206 unsigned char databuf[20];
207 int pos;
208};
209
210random_state *random_new(const char *seed, int len)
211{
212 random_state *state;
213
214 state = snew(random_state);
215
216 SHA_Simple(seed, len, state->seedbuf);
217 SHA_Simple(state->seedbuf, 20, state->seedbuf + 20);
218 SHA_Simple(state->seedbuf, 40, state->databuf);
219 state->pos = 0;
220
221 return state;
222}
223
224random_state *random_copy(random_state *tocopy)
225{
226 random_state *result;
227 result = snew(random_state);
228 memcpy(result->seedbuf, tocopy->seedbuf, sizeof(result->seedbuf));
229 memcpy(result->databuf, tocopy->databuf, sizeof(result->databuf));
230 result->pos = tocopy->pos;
231 return result;
232}
233
234unsigned long random_bits(random_state *state, int bits)
235{
236 unsigned long ret = 0;
237 int n;
238
239 for (n = 0; n < bits; n += 8) {
240 if (state->pos >= 20) {
241 int i;
242
243 for (i = 0; i < 20; i++) {
244 if (state->seedbuf[i] != 0xFF) {
245 state->seedbuf[i]++;
246 break;
247 } else
248 state->seedbuf[i] = 0;
249 }
250 SHA_Simple(state->seedbuf, 40, state->databuf);
251 state->pos = 0;
252 }
253 ret = (ret << 8) | state->databuf[state->pos++];
254 }
255
256 /*
257 * `(1 << bits) - 1' is not good enough, since if bits==32 on a
258 * 32-bit machine, behaviour is undefined and Intel has a nasty
259 * habit of shifting left by zero instead. We'll shift by
260 * bits-1 and then separately shift by one.
261 */
262 ret &= (1 << (bits-1)) * 2 - 1;
263 return ret;
264}
265
266unsigned long random_upto(random_state *state, unsigned long limit)
267{
268 int bits = 0;
269 unsigned long max, divisor, data;
270
271 while ((limit >> bits) != 0)
272 bits++;
273
274 bits += 3;
275 assert(bits < 32);
276
277 max = 1L << bits;
278 divisor = max / limit;
279 max = limit * divisor;
280
281 do {
282 data = random_bits(state, bits);
283 } while (data >= max);
284
285 return data / divisor;
286}
287
288void random_free(random_state *state)
289{
290 sfree(state);
291}
292
293char *random_state_encode(random_state *state)
294{
295 char retbuf[256];
296 int len = 0, i;
297
298 for (i = 0; i < lenof(state->seedbuf); i++)
299 len += sprintf(retbuf+len, "%02x", state->seedbuf[i]);
300 for (i = 0; i < lenof(state->databuf); i++)
301 len += sprintf(retbuf+len, "%02x", state->databuf[i]);
302 len += sprintf(retbuf+len, "%02x", state->pos);
303
304 return dupstr(retbuf);
305}
306
307random_state *random_state_decode(const char *input)
308{
309 random_state *state;
310 int pos, byte, digits;
311
312 state = snew(random_state);
313
314 memset(state->seedbuf, 0, sizeof(state->seedbuf));
315 memset(state->databuf, 0, sizeof(state->databuf));
316 state->pos = 0;
317
318 byte = digits = 0;
319 pos = 0;
320 while (*input) {
321 int v = *input++;
322
323 if (v >= '0' && v <= '9')
324 v = v - '0';
325 else if (v >= 'A' && v <= 'F')
326 v = v - 'A' + 10;
327 else if (v >= 'a' && v <= 'f')
328 v = v - 'a' + 10;
329 else
330 v = 0;
331
332 byte = (byte << 4) | v;
333 digits++;
334
335 if (digits == 2) {
336 /*
337 * We have a byte. Put it somewhere.
338 */
339 if (pos < lenof(state->seedbuf))
340 state->seedbuf[pos++] = byte;
341 else if (pos < lenof(state->seedbuf) + lenof(state->databuf))
342 state->databuf[pos++ - lenof(state->seedbuf)] = byte;
343 else if (pos == lenof(state->seedbuf) + lenof(state->databuf) &&
344 byte <= lenof(state->databuf))
345 state->pos = byte;
346 byte = digits = 0;
347 }
348 }
349
350 return state;
351}
diff --git a/apps/plugins/puzzles/src/range.R b/apps/plugins/puzzles/src/range.R
new file mode 100644
index 0000000000..f1256efd1e
--- /dev/null
+++ b/apps/plugins/puzzles/src/range.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3RANGE_EXTRA = dsf
4
5range : [X] GTK COMMON range RANGE_EXTRA range-icon|no-icon
6
7range : [G] WINDOWS COMMON range RANGE_EXTRA range.res|noicon.res
8
9ALL += range[COMBINED] RANGE_EXTRA
10
11!begin am gtk
12GAMES += range
13!end
14
15!begin >list.c
16 A(range) \
17!end
18
19!begin >gamedesc.txt
20range:range.exe:Range:Visible-distance puzzle:Place black squares to limit the visible distance from each numbered cell.
21!end
diff --git a/apps/plugins/puzzles/src/range.c b/apps/plugins/puzzles/src/range.c
new file mode 100644
index 0000000000..588178c003
--- /dev/null
+++ b/apps/plugins/puzzles/src/range.c
@@ -0,0 +1,1833 @@
1/*
2 * range.c: implementation of the Nikoli game 'Kurodoko' / 'Kuromasu'.
3 */
4
5/*
6 * Puzzle rules: the player is given a WxH grid of white squares, some
7 * of which contain numbers. The goal is to paint some of the squares
8 * black, such that:
9 *
10 * - no cell (err, cell = square) with a number is painted black
11 * - no black cells have an adjacent (horz/vert) black cell
12 * - the white cells are all connected (through other white cells)
13 * - if a cell contains a number n, let h and v be the lengths of the
14 * maximal horizontal and vertical white sequences containing that
15 * cell. Then n must equal h + v - 1.
16 */
17
18/* example instance with its encoding and textual representation, both
19 * solved and unsolved (made by thegame.solve and thegame.text_format)
20 *
21 * +--+--+--+--+--+--+--+
22 * | | | | | 7| | |
23 * +--+--+--+--+--+--+--+
24 * | 3| | | | | | 8|
25 * +--+--+--+--+--+--+--+
26 * | | | | | | 5| |
27 * +--+--+--+--+--+--+--+
28 * | | | 7| | 7| | |
29 * +--+--+--+--+--+--+--+
30 * | |13| | | | | |
31 * +--+--+--+--+--+--+--+
32 * | 4| | | | | | 8|
33 * +--+--+--+--+--+--+--+
34 * | | | 4| | | | |
35 * +--+--+--+--+--+--+--+
36 *
37 * 7x7:d7b3e8e5c7a7c13e4d8b4d
38 *
39 * +--+--+--+--+--+--+--+
40 * |..|..|..|..| 7|..|..|
41 * +--+--+--+--+--+--+--+
42 * | 3|..|##|..|##|..| 8|
43 * +--+--+--+--+--+--+--+
44 * |##|..|..|##|..| 5|..|
45 * +--+--+--+--+--+--+--+
46 * |..|..| 7|..| 7|##|..|
47 * +--+--+--+--+--+--+--+
48 * |..|13|..|..|..|..|..|
49 * +--+--+--+--+--+--+--+
50 * | 4|..|##|..|##|..| 8|
51 * +--+--+--+--+--+--+--+
52 * |##|..| 4|..|..|##|..|
53 * +--+--+--+--+--+--+--+
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <assert.h>
60#include <ctype.h>
61#include <math.h>
62
63#include "puzzles.h"
64
65#include <stdarg.h>
66
67#define setmember(obj, field) ( (obj) . field = field )
68
69static char *nfmtstr(int n, char *fmt, ...) {
70 va_list va;
71 char *ret = snewn(n+1, char);
72 va_start(va, fmt);
73 vsprintf(ret, fmt, va);
74 va_end(va);
75 return ret;
76}
77
78#define SWAP(type, lvar1, lvar2) do { \
79 type tmp = (lvar1); \
80 (lvar1) = (lvar2); \
81 (lvar2) = tmp; \
82} while (0)
83
84/* ----------------------------------------------------------------------
85 * Game parameters, presets, states
86 */
87
88typedef signed char puzzle_size;
89
90struct game_params {
91 puzzle_size w;
92 puzzle_size h;
93};
94
95struct game_state {
96 struct game_params params;
97 unsigned int has_cheated: 1;
98 unsigned int was_solved: 1;
99 puzzle_size *grid;
100};
101
102#define DEFAULT_PRESET 0
103static struct game_params range_presets[] = {{9, 6}, {12, 8}, {13, 9}, {16, 11}};
104/* rationale: I want all four combinations of {odd/even, odd/even}, as
105 * they play out differently with respect to two-way symmetry. I also
106 * want them to be generated relatively fast yet still be large enough
107 * to be entertaining for a decent amount of time, and I want them to
108 * make good use of monitor real estate (the typical screen resolution
109 * is why I do 13x9 and not 9x13).
110 */
111
112static game_params *default_params(void)
113{
114 game_params *ret = snew(game_params);
115 *ret = range_presets[DEFAULT_PRESET]; /* structure copy */
116 return ret;
117}
118
119static game_params *dup_params(const game_params *params)
120{
121 game_params *ret = snew(game_params);
122 *ret = *params; /* structure copy */
123 return ret;
124}
125
126static int game_fetch_preset(int i, char **name, game_params **params)
127{
128 game_params *ret;
129
130 if (i < 0 || i >= lenof(range_presets)) return FALSE;
131
132 ret = default_params();
133 *ret = range_presets[i]; /* struct copy */
134 *params = ret;
135
136 *name = nfmtstr(40, "%d x %d", range_presets[i].w, range_presets[i].h);
137
138 return TRUE;
139}
140
141static void free_params(game_params *params)
142{
143 sfree(params);
144}
145
146static void decode_params(game_params *params, char const *string)
147{
148 /* FIXME check for puzzle_size overflow and decoding issues */
149 params->w = params->h = atoi(string);
150 while (*string && isdigit((unsigned char) *string)) ++string;
151 if (*string == 'x') {
152 string++;
153 params->h = atoi(string);
154 while (*string && isdigit((unsigned char)*string)) string++;
155 }
156}
157
158static char *encode_params(const game_params *params, int full)
159{
160 char str[80];
161 sprintf(str, "%dx%d", params->w, params->h);
162 return dupstr(str);
163}
164
165static config_item *game_configure(const game_params *params)
166{
167 config_item *ret;
168
169 ret = snewn(3, config_item);
170
171 ret[0].name = "Width";
172 ret[0].type = C_STRING;
173 ret[0].sval = nfmtstr(10, "%d", params->w);
174 ret[0].ival = 0;
175
176 ret[1].name = "Height";
177 ret[1].type = C_STRING;
178 ret[1].sval = nfmtstr(10, "%d", params->h);
179 ret[1].ival = 0;
180
181 ret[2].name = NULL;
182 ret[2].type = C_END;
183 ret[2].sval = NULL;
184 ret[2].ival = 0;
185
186 return ret;
187}
188
189static game_params *custom_params(const config_item *configuration)
190{
191 game_params *ret = snew(game_params);
192 ret->w = atoi(configuration[0].sval);
193 ret->h = atoi(configuration[1].sval);
194 return ret;
195}
196
197#define memdup(dst, src, n, type) do { \
198 dst = snewn(n, type); \
199 memcpy(dst, src, n * sizeof (type)); \
200} while (0)
201
202static game_state *dup_game(const game_state *state)
203{
204 game_state *ret = snew(game_state);
205 int const n = state->params.w * state->params.h;
206
207 *ret = *state; /* structure copy */
208
209 /* copy the poin_tee_, set a new value of the poin_ter_ */
210 memdup(ret->grid, state->grid, n, puzzle_size);
211
212 return ret;
213}
214
215static void free_game(game_state *state)
216{
217 sfree(state->grid);
218 sfree(state);
219}
220
221
222/* ----------------------------------------------------------------------
223 * The solver subsystem.
224 *
225 * The solver is used for two purposes:
226 * - To solve puzzles when the user selects `Solve'.
227 * - To test solubility of a grid as clues are being removed from it
228 * during the puzzle generation.
229 *
230 * It supports the following ways of reasoning:
231 *
232 * - A cell adjacent to a black cell must be white.
233 *
234 * - If painting a square black would bisect the white regions, that
235 * square is white (by finding biconnected components' cut points)
236 *
237 * - A cell with number n, covering at most k white squares in three
238 * directions must white-cover n-k squares in the last direction.
239 *
240 * - A cell with number n known to cover k squares, if extending the
241 * cover by one square in a given direction causes the cell to
242 * cover _more_ than n squares, that extension cell must be black.
243 *
244 * (either if the square already covers n, or if it extends into a
245 * chunk of size > n - k)
246 *
247 * - Recursion. Pick any cell and see if this leads to either a
248 * contradiction or a solution (and then act appropriately).
249 *
250 *
251 * TODO:
252 *
253 * (propagation upper limit)
254 * - If one has two numbers on the same line, the smaller limits the
255 * larger. Example: in |b|_|_|8|4|_|_|b|, only two _'s can be both
256 * white and connected to the "8" cell; so that cell will propagate
257 * at least four cells orthogonally to the displayed line (which is
258 * better than the current "at least 2").
259 *
260 * (propagation upper limit)
261 * - cells can't propagate into other cells if doing so exceeds that
262 * number. Example: in |b|4|.|.|2|b|, at most one _ can be white;
263 * otherwise, the |2| would have too many reaching white cells.
264 *
265 * (propagation lower and upper limit)
266 * - `Full Combo': in each four directions d_1 ... d_4, find a set of
267 * possible propagation distances S_1 ... S_4. For each i=1..4,
268 * for each x in S_i: if not exists (y, z, w) in the other sets
269 * such that (x+y+z+w+1 == clue value): then remove x from S_i.
270 * Repeat until this stabilizes. If any cell would contradict
271 */
272
273#define idx(i, j, w) ((i)*(w) + (j))
274#define out_of_bounds(r, c, w, h) \
275 ((r) < 0 || (r) >= h || (c) < 0 || (c) >= w)
276
277typedef struct square {
278 puzzle_size r, c;
279} square;
280
281enum {BLACK = -2, WHITE, EMPTY};
282/* white is for pencil marks, empty is undecided */
283
284static int const dr[4] = {+1, 0, -1, 0};
285static int const dc[4] = { 0, +1, 0, -1};
286static int const cursors[4] = /* must match dr and dc */
287{CURSOR_DOWN, CURSOR_RIGHT, CURSOR_UP, CURSOR_LEFT};
288
289typedef struct move {
290 square square;
291 unsigned int colour: 1;
292} move;
293enum {M_BLACK = 0, M_WHITE = 1};
294
295typedef move *(reasoning)(game_state *state,
296 int nclues,
297 const square *clues,
298 move *buf);
299
300static reasoning solver_reasoning_not_too_big;
301static reasoning solver_reasoning_adjacency;
302static reasoning solver_reasoning_connectedness;
303static reasoning solver_reasoning_recursion;
304
305enum {
306 DIFF_NOT_TOO_BIG,
307 DIFF_ADJACENCY,
308 DIFF_CONNECTEDNESS,
309 DIFF_RECURSION
310};
311
312static move *solve_internal(const game_state *state, move *base, int diff);
313
314static char *solve_game(const game_state *orig, const game_state *curpos,
315 const char *aux, char **error)
316{
317 int const n = orig->params.w * orig->params.h;
318 move *const base = snewn(n, move);
319 move *moves = solve_internal(orig, base, DIFF_RECURSION);
320
321 char *ret = NULL;
322
323 if (moves != NULL) {
324 int const k = moves - base;
325 char *str = ret = snewn(15*k + 2, char);
326 char colour[2] = "BW";
327 move *it;
328 *str++ = 'S';
329 *str = '\0';
330 for (it = base; it < moves; ++it)
331 str += sprintf(str, "%c,%d,%d", colour[it->colour],
332 it->square.r, it->square.c);
333 } else *error = "This puzzle instance contains a contradiction";
334
335 sfree(base);
336 return ret;
337}
338
339static square *find_clues(const game_state *state, int *ret_nclues);
340static move *do_solve(game_state *state,
341 int nclues,
342 const square *clues,
343 move *move_buffer,
344 int difficulty);
345
346/* new_game_desc entry point in the solver subsystem */
347static move *solve_internal(const game_state *state, move *base, int diff)
348{
349 int nclues;
350 square *const clues = find_clues(state, &nclues);
351 game_state *dup = dup_game(state);
352 move *const moves = do_solve(dup, nclues, clues, base, diff);
353 free_game(dup);
354 sfree(clues);
355 return moves;
356}
357
358static reasoning *const reasonings[] = {
359 solver_reasoning_not_too_big,
360 solver_reasoning_adjacency,
361 solver_reasoning_connectedness,
362 solver_reasoning_recursion
363};
364
365static move *do_solve(game_state *state,
366 int nclues,
367 const square *clues,
368 move *move_buffer,
369 int difficulty)
370{
371 struct move *buf = move_buffer, *oldbuf;
372 int i;
373
374 do {
375 oldbuf = buf;
376 for (i = 0; i < lenof(reasonings) && i <= difficulty; ++i) {
377 /* only recurse if all else fails */
378 if (i == DIFF_RECURSION && buf > oldbuf) continue;
379 buf = (*reasonings[i])(state, nclues, clues, buf);
380 if (buf == NULL) return NULL;
381 }
382 } while (buf > oldbuf);
383
384 return buf;
385}
386
387#define MASK(n) (1 << ((n) + 2))
388
389static int runlength(puzzle_size r, puzzle_size c,
390 puzzle_size dr, puzzle_size dc,
391 const game_state *state, int colourmask)
392{
393 int const w = state->params.w, h = state->params.h;
394 int sz = 0;
395 while (TRUE) {
396 int cell = idx(r, c, w);
397 if (out_of_bounds(r, c, w, h)) break;
398 if (state->grid[cell] > 0) {
399 if (!(colourmask & ~(MASK(BLACK) | MASK(WHITE) | MASK(EMPTY))))
400 break;
401 } else if (!(MASK(state->grid[cell]) & colourmask)) break;
402 ++sz;
403 r += dr;
404 c += dc;
405 }
406 return sz;
407}
408
409static void solver_makemove(puzzle_size r, puzzle_size c, int colour,
410 game_state *state, move **buffer_ptr)
411{
412 int const cell = idx(r, c, state->params.w);
413 if (out_of_bounds(r, c, state->params.w, state->params.h)) return;
414 if (state->grid[cell] != EMPTY) return;
415 setmember((*buffer_ptr)->square, r);
416 setmember((*buffer_ptr)->square, c);
417 setmember(**buffer_ptr, colour);
418 ++*buffer_ptr;
419 state->grid[cell] = (colour == M_BLACK ? BLACK : WHITE);
420}
421
422static move *solver_reasoning_adjacency(game_state *state,
423 int nclues,
424 const square *clues,
425 move *buf)
426{
427 int r, c, i;
428 for (r = 0; r < state->params.h; ++r)
429 for (c = 0; c < state->params.w; ++c) {
430 int const cell = idx(r, c, state->params.w);
431 if (state->grid[cell] != BLACK) continue;
432 for (i = 0; i < 4; ++i)
433 solver_makemove(r + dr[i], c + dc[i], M_WHITE, state, &buf);
434 }
435 return buf;
436}
437
438enum {NOT_VISITED = -1};
439
440static int dfs_biconnect_visit(puzzle_size r, puzzle_size c,
441 game_state *state,
442 square *dfs_parent, int *dfs_depth,
443 move **buf);
444
445static move *solver_reasoning_connectedness(game_state *state,
446 int nclues,
447 const square *clues,
448 move *buf)
449{
450 int const w = state->params.w, h = state->params.h, n = w * h;
451
452 square *const dfs_parent = snewn(n, square);
453 int *const dfs_depth = snewn(n, int);
454
455 int i;
456 for (i = 0; i < n; ++i) {
457 dfs_parent[i].r = NOT_VISITED;
458 dfs_depth[i] = -n;
459 }
460
461 for (i = 0; i < n && state->grid[i] == BLACK; ++i);
462
463 dfs_parent[i].r = i / w;
464 dfs_parent[i].c = i % w; /* `dfs root`.parent == `dfs root` */
465 dfs_depth[i] = 0;
466
467 dfs_biconnect_visit(i / w, i % w, state, dfs_parent, dfs_depth, &buf);
468
469 sfree(dfs_parent);
470 sfree(dfs_depth);
471
472 return buf;
473}
474
475/* returns the `lowpoint` of (r, c) */
476static int dfs_biconnect_visit(puzzle_size r, puzzle_size c,
477 game_state *state,
478 square *dfs_parent, int *dfs_depth,
479 move **buf)
480{
481 const puzzle_size w = state->params.w, h = state->params.h;
482 int const i = idx(r, c, w), mydepth = dfs_depth[i];
483 int lowpoint = mydepth, j, nchildren = 0;
484
485 for (j = 0; j < 4; ++j) {
486 const puzzle_size rr = r + dr[j], cc = c + dc[j];
487 int const cell = idx(rr, cc, w);
488
489 if (out_of_bounds(rr, cc, w, h)) continue;
490 if (state->grid[cell] == BLACK) continue;
491
492 if (dfs_parent[cell].r == NOT_VISITED) {
493 int child_lowpoint;
494 dfs_parent[cell].r = r;
495 dfs_parent[cell].c = c;
496 dfs_depth[cell] = mydepth + 1;
497 child_lowpoint = dfs_biconnect_visit(rr, cc, state, dfs_parent,
498 dfs_depth, buf);
499
500 if (child_lowpoint >= mydepth && mydepth > 0)
501 solver_makemove(r, c, M_WHITE, state, buf);
502
503 lowpoint = min(lowpoint, child_lowpoint);
504 ++nchildren;
505 } else if (rr != dfs_parent[i].r || cc != dfs_parent[i].c) {
506 lowpoint = min(lowpoint, dfs_depth[cell]);
507 }
508 }
509
510 if (mydepth == 0 && nchildren >= 2)
511 solver_makemove(r, c, M_WHITE, state, buf);
512
513 return lowpoint;
514}
515
516static move *solver_reasoning_not_too_big(game_state *state,
517 int nclues,
518 const square *clues,
519 move *buf)
520{
521 int const w = state->params.w, runmasks[4] = {
522 ~(MASK(BLACK) | MASK(EMPTY)),
523 MASK(EMPTY),
524 ~(MASK(BLACK) | MASK(EMPTY)),
525 ~(MASK(BLACK))
526 };
527 enum {RUN_WHITE, RUN_EMPTY, RUN_BEYOND, RUN_SPACE};
528
529 int i, runlengths[4][4];
530
531 for (i = 0; i < nclues; ++i) {
532 int j, k, whites, space;
533
534 const puzzle_size row = clues[i].r, col = clues[i].c;
535 int const clue = state->grid[idx(row, col, w)];
536
537 for (j = 0; j < 4; ++j) {
538 puzzle_size r = row + dr[j], c = col + dc[j];
539 runlengths[RUN_SPACE][j] = 0;
540 for (k = 0; k <= RUN_SPACE; ++k) {
541 int l = runlength(r, c, dr[j], dc[j], state, runmasks[k]);
542 if (k < RUN_SPACE) {
543 runlengths[k][j] = l;
544 r += dr[j] * l;
545 c += dc[j] * l;
546 }
547 runlengths[RUN_SPACE][j] += l;
548 }
549 }
550
551 whites = 1;
552 for (j = 0; j < 4; ++j) whites += runlengths[RUN_WHITE][j];
553
554 for (j = 0; j < 4; ++j) {
555 int const delta = 1 + runlengths[RUN_WHITE][j];
556 const puzzle_size r = row + delta * dr[j];
557 const puzzle_size c = col + delta * dc[j];
558
559 if (whites == clue) {
560 solver_makemove(r, c, M_BLACK, state, &buf);
561 continue;
562 }
563
564 if (runlengths[RUN_EMPTY][j] == 1 &&
565 whites
566 + runlengths[RUN_EMPTY][j]
567 + runlengths[RUN_BEYOND][j]
568 > clue) {
569 solver_makemove(r, c, M_BLACK, state, &buf);
570 continue;
571 }
572
573 if (whites
574 + runlengths[RUN_EMPTY][j]
575 + runlengths[RUN_BEYOND][j]
576 > clue) {
577 runlengths[RUN_SPACE][j] =
578 runlengths[RUN_WHITE][j] +
579 runlengths[RUN_EMPTY][j] - 1;
580
581 if (runlengths[RUN_EMPTY][j] == 1)
582 solver_makemove(r, c, M_BLACK, state, &buf);
583 }
584 }
585
586 space = 1;
587 for (j = 0; j < 4; ++j) space += runlengths[RUN_SPACE][j];
588 for (j = 0; j < 4; ++j) {
589 puzzle_size r = row + dr[j], c = col + dc[j];
590
591 int k = space - runlengths[RUN_SPACE][j];
592 if (k >= clue) continue;
593
594 for (; k < clue; ++k, r += dr[j], c += dc[j])
595 solver_makemove(r, c, M_WHITE, state, &buf);
596 }
597 }
598 return buf;
599}
600
601static move *solver_reasoning_recursion(game_state *state,
602 int nclues,
603 const square *clues,
604 move *buf)
605{
606 int const w = state->params.w, n = w * state->params.h;
607 int cell, colour;
608
609 for (cell = 0; cell < n; ++cell) {
610 int const r = cell / w, c = cell % w;
611 int i;
612 game_state *newstate;
613 move *recursive_result;
614
615 if (state->grid[cell] != EMPTY) continue;
616
617 /* FIXME: add enum alias for smallest and largest (or N) */
618 for (colour = M_BLACK; colour <= M_WHITE; ++colour) {
619 newstate = dup_game(state);
620 newstate->grid[cell] = colour;
621 recursive_result = do_solve(newstate, nclues, clues, buf,
622 DIFF_RECURSION);
623 free_game(newstate);
624 if (recursive_result == NULL) {
625 solver_makemove(r, c, M_BLACK + M_WHITE - colour, state, &buf);
626 return buf;
627 }
628 for (i = 0; i < n && newstate->grid[i] != EMPTY; ++i);
629 if (i == n) return buf;
630 }
631 }
632 return buf;
633}
634
635static square *find_clues(const game_state *state, int *ret_nclues)
636{
637 int r, c, i, nclues = 0;
638 square *ret = snewn(state->params.w * state->params.h, struct square);
639
640 for (i = r = 0; r < state->params.h; ++r)
641 for (c = 0; c < state->params.w; ++c, ++i)
642 if (state->grid[i] > 0) {
643 ret[nclues].r = r;
644 ret[nclues].c = c;
645 ++nclues;
646 }
647
648 *ret_nclues = nclues;
649 return sresize(ret, nclues + (nclues == 0), square);
650}
651
652/* ----------------------------------------------------------------------
653 * Puzzle generation
654 *
655 * Generating kurodoko instances is rather straightforward:
656 *
657 * - Start with a white grid and add black squares at randomly chosen
658 * locations, unless colouring that square black would violate
659 * either the adjacency or connectedness constraints.
660 *
661 * - For each white square, compute the number it would contain if it
662 * were given as a clue.
663 *
664 * - From a starting point of "give _every_ white square as a clue",
665 * for each white square (in a random order), see if the board is
666 * solvable when that square is not given as a clue. If not, don't
667 * give it as a clue, otherwise do.
668 *
669 * This never fails, but it's only _almost_ what I do. The real final
670 * step is this:
671 *
672 * - From a starting point of "give _every_ white square as a clue",
673 * first remove all clues that are two-way rotationally symmetric
674 * to a black square. If this leaves the puzzle unsolvable, throw
675 * it out and try again. Otherwise, remove all _pairs_ of clues
676 * (that are rotationally symmetric) which can be removed without
677 * rendering the puzzle unsolvable.
678 *
679 * This can fail even if one only removes the black and symmetric
680 * clues; indeed it happens often (avg. once or twice per puzzle) when
681 * generating 1xN instances. (If you add black cells they must be in
682 * the end, and if you only add one, it's ambiguous where).
683 */
684
685/* forward declarations of internal calls */
686static void newdesc_choose_black_squares(game_state *state,
687 const int *shuffle_1toN);
688static void newdesc_compute_clues(game_state *state);
689static int newdesc_strip_clues(game_state *state, int *shuffle_1toN);
690static char *newdesc_encode_game_description(int n, puzzle_size *grid);
691
692static char *new_game_desc(const game_params *params, random_state *rs,
693 char **aux, int interactive)
694{
695 int const w = params->w, h = params->h, n = w * h;
696
697 puzzle_size *const grid = snewn(n, puzzle_size);
698 int *const shuffle_1toN = snewn(n, int);
699
700 int i, clues_removed;
701
702 char *encoding;
703
704 game_state state;
705 state.params = *params;
706 state.grid = grid;
707
708 interactive = 0; /* I don't need it, I shouldn't use it*/
709
710 for (i = 0; i < n; ++i) shuffle_1toN[i] = i;
711
712 while (TRUE) {
713 shuffle(shuffle_1toN, n, sizeof (int), rs);
714 newdesc_choose_black_squares(&state, shuffle_1toN);
715
716 newdesc_compute_clues(&state);
717
718 shuffle(shuffle_1toN, n, sizeof (int), rs);
719 clues_removed = newdesc_strip_clues(&state, shuffle_1toN);
720
721 if (clues_removed < 0) continue; else break;
722 }
723
724 encoding = newdesc_encode_game_description(n, grid);
725
726 sfree(grid);
727 sfree(shuffle_1toN);
728
729 return encoding;
730}
731
732static int dfs_count_white(game_state *state, int cell);
733
734static void newdesc_choose_black_squares(game_state *state,
735 const int *shuffle_1toN)
736{
737 int const w = state->params.w, h = state->params.h, n = w * h;
738
739 int k, any_white_cell, n_black_cells;
740
741 for (k = 0; k < n; ++k) state->grid[k] = WHITE;
742
743 any_white_cell = shuffle_1toN[n - 1];
744 n_black_cells = 0;
745
746 /* I like the puzzles that result from n / 3, but maybe this
747 * could be made a (generation, i.e. non-full) parameter? */
748 for (k = 0; k < n / 3; ++k) {
749 int const i = shuffle_1toN[k], c = i % w, r = i / w;
750
751 int j;
752 for (j = 0; j < 4; ++j) {
753 int const rr = r + dr[j], cc = c + dc[j], cell = idx(rr, cc, w);
754 /* if you're out of bounds, we skip you */
755 if (out_of_bounds(rr, cc, w, h)) continue;
756 if (state->grid[cell] == BLACK) break; /* I can't be black */
757 } if (j < 4) continue; /* I have black neighbour: I'm white */
758
759 state->grid[i] = BLACK;
760 ++n_black_cells;
761
762 j = dfs_count_white(state, any_white_cell);
763 if (j + n_black_cells < n) {
764 state->grid[i] = WHITE;
765 --n_black_cells;
766 }
767 }
768}
769
770static void newdesc_compute_clues(game_state *state)
771{
772 int const w = state->params.w, h = state->params.h;
773 int r, c;
774
775 for (r = 0; r < h; ++r) {
776 int run_size = 0, c, cc;
777 for (c = 0; c <= w; ++c) {
778 if (c == w || state->grid[idx(r, c, w)] == BLACK) {
779 for (cc = c - run_size; cc < c; ++cc)
780 state->grid[idx(r, cc, w)] += run_size;
781 run_size = 0;
782 } else ++run_size;
783 }
784 }
785
786 for (c = 0; c < w; ++c) {
787 int run_size = 0, r, rr;
788 for (r = 0; r <= h; ++r) {
789 if (r == h || state->grid[idx(r, c, w)] == BLACK) {
790 for (rr = r - run_size; rr < r; ++rr)
791 state->grid[idx(rr, c, w)] += run_size;
792 run_size = 0;
793 } else ++run_size;
794 }
795 }
796}
797
798#define rotate(x) (n - 1 - (x))
799
800static int newdesc_strip_clues(game_state *state, int *shuffle_1toN)
801{
802 int const w = state->params.w, n = w * state->params.h;
803
804 move *const move_buffer = snewn(n, move);
805 move *buf;
806 game_state *dupstate;
807
808 /*
809 * do a partition/pivot of shuffle_1toN into three groups:
810 * (1) squares rotationally-symmetric to (3)
811 * (2) squares not in (1) or (3)
812 * (3) black squares
813 *
814 * They go from [0, left), [left, right) and [right, n) in
815 * shuffle_1toN (and from there into state->grid[ ])
816 *
817 * Then, remove clues from the grid one by one in shuffle_1toN
818 * order, until the solver becomes unhappy. If we didn't remove
819 * all of (1), return (-1). Else, we're happy.
820 */
821
822 /* do the partition */
823 int clues_removed, k = 0, left = 0, right = n;
824
825 for (;; ++k) {
826 while (k < right && state->grid[shuffle_1toN[k]] == BLACK) {
827 --right;
828 SWAP(int, shuffle_1toN[right], shuffle_1toN[k]);
829 assert(state->grid[shuffle_1toN[right]] == BLACK);
830 }
831 if (k >= right) break;
832 assert (k >= left);
833 if (state->grid[rotate(shuffle_1toN[k])] == BLACK) {
834 SWAP(int, shuffle_1toN[k], shuffle_1toN[left]);
835 ++left;
836 }
837 assert (state->grid[rotate(shuffle_1toN[k])] != BLACK
838 || k == left - 1);
839 }
840
841 for (k = 0; k < left; ++k) {
842 assert (state->grid[rotate(shuffle_1toN[k])] == BLACK);
843 state->grid[shuffle_1toN[k]] = EMPTY;
844 }
845 for (k = left; k < right; ++k) {
846 assert (state->grid[rotate(shuffle_1toN[k])] != BLACK);
847 assert (state->grid[shuffle_1toN[k]] != BLACK);
848 }
849 for (k = right; k < n; ++k) {
850 assert (state->grid[shuffle_1toN[k]] == BLACK);
851 state->grid[shuffle_1toN[k]] = EMPTY;
852 }
853
854 clues_removed = (left - 0) + (n - right);
855
856 dupstate = dup_game(state);
857 buf = solve_internal(dupstate, move_buffer, DIFF_RECURSION - 1);
858 free_game(dupstate);
859 if (buf - move_buffer < clues_removed) {
860 /* branch prediction: I don't think I'll go here */
861 clues_removed = -1;
862 goto ret;
863 }
864
865 for (k = left; k < right; ++k) {
866 const int i = shuffle_1toN[k], j = rotate(i);
867 int const clue = state->grid[i], clue_rot = state->grid[j];
868 if (clue == BLACK) continue;
869 state->grid[i] = state->grid[j] = EMPTY;
870 dupstate = dup_game(state);
871 buf = solve_internal(dupstate, move_buffer, DIFF_RECURSION - 1);
872 free_game(dupstate);
873 clues_removed += 2 - (i == j);
874 /* if i is the center square, then i == (j = rotate(i))
875 * when i and j are one, removing i and j removes only one */
876 if (buf - move_buffer == clues_removed) continue;
877 /* if the solver is sound, refilling all removed clues means
878 * we have filled all squares, i.e. solved the puzzle. */
879 state->grid[i] = clue;
880 state->grid[j] = clue_rot;
881 clues_removed -= 2 - (i == j);
882 }
883
884ret:
885 sfree(move_buffer);
886 return clues_removed;
887}
888
889static int dfs_count_rec(puzzle_size *grid, int r, int c, int w, int h)
890{
891 int const cell = idx(r, c, w);
892 if (out_of_bounds(r, c, w, h)) return 0;
893 if (grid[cell] != WHITE) return 0;
894 grid[cell] = EMPTY;
895 return 1 +
896 dfs_count_rec(grid, r + 0, c + 1, w, h) +
897 dfs_count_rec(grid, r + 0, c - 1, w, h) +
898 dfs_count_rec(grid, r + 1, c + 0, w, h) +
899 dfs_count_rec(grid, r - 1, c + 0, w, h);
900}
901
902static int dfs_count_white(game_state *state, int cell)
903{
904 int const w = state->params.w, h = state->params.h, n = w * h;
905 int const r = cell / w, c = cell % w;
906 int i, k = dfs_count_rec(state->grid, r, c, w, h);
907 for (i = 0; i < n; ++i)
908 if (state->grid[i] == EMPTY)
909 state->grid[i] = WHITE;
910 return k;
911}
912
913static char *validate_params(const game_params *params, int full)
914{
915 int const w = params->w, h = params->h;
916 if (w < 1) return "Error: width is less than 1";
917 if (h < 1) return "Error: height is less than 1";
918 if (w * h < 1) return "Error: size is less than 1";
919 if (w + h - 1 > SCHAR_MAX) return "Error: w + h is too big";
920 /* I might be unable to store clues in my puzzle_size *grid; */
921 if (full) {
922 if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles";
923 if (w == 1 && h == 2) return "Error: can't create 1x2 puzzles";
924 if (w == 2 && h == 1) return "Error: can't create 2x1 puzzles";
925 if (w == 1 && h == 1) return "Error: can't create 1x1 puzzles";
926 }
927 return NULL;
928}
929
930/* Definition: a puzzle instance is _good_ if:
931 * - it has a unique solution
932 * - the solver can find this solution without using recursion
933 * - the solution contains at least one black square
934 * - the clues are 2-way rotationally symmetric
935 *
936 * (the idea being: the generator can not output any _bad_ puzzles)
937 *
938 * Theorem: validate_params, when full != 0, discards exactly the set
939 * of parameters for which there are _no_ good puzzle instances.
940 *
941 * Proof: it's an immediate consequence of the five lemmas below.
942 *
943 * Observation: not only do puzzles on non-tiny grids exist, the
944 * generator is pretty fast about coming up with them. On my pre-2004
945 * desktop box, it generates 100 puzzles on the highest preset (16x11)
946 * in 8.383 seconds, or <= 0.1 second per puzzle.
947 *
948 * ----------------------------------------------------------------------
949 *
950 * Lemma: On a 1x1 grid, there are no good puzzles.
951 *
952 * Proof: the one square can't be a clue because at least one square
953 * is black. But both a white square and a black square satisfy the
954 * solution criteria, so the puzzle is ambiguous (and hence bad).
955 *
956 * Lemma: On a 1x2 grid, there are no good puzzles.
957 *
958 * Proof: let's name the squares l and r. Note that there can be at
959 * most one black square, or adjacency is violated. By assumption at
960 * least one square is black, so let's call that one l. By clue
961 * symmetry, neither l nor r can be given as a clue, so the puzzle
962 * instance is blank and thus ambiguous.
963 *
964 * Corollary: On a 2x1 grid, there are no good puzzles.
965 * Proof: rotate the above proof 90 degrees ;-)
966 *
967 * ----------------------------------------------------------------------
968 *
969 * Lemma: On a 2x2 grid, there are no soluble puzzles with 2-way
970 * rotational symmetric clues and at least one black square.
971 *
972 * Proof: Let's name the squares a, b, c, and d, with a and b on the
973 * top row, a and c in the left column. Let's consider the case where
974 * a is black. Then no other square can be black: b and c would both
975 * violate the adjacency constraint; d would disconnect b from c.
976 *
977 * So exactly one square is black (and by 4-way rotation symmetry of
978 * the 2x2 square, it doesn't matter which one, so let's stick to a).
979 * By 2-way rotational symmetry of the clues and the rule about not
980 * painting numbers black, neither a nor d can be clues. A blank
981 * puzzle would be ambiguous, so one of {b, c} is a clue; by symmetry,
982 * so is the other one.
983 *
984 * It is readily seen that their clue value is 2. But "a is black"
985 * and "d is black" are both valid solutions in this case, so the
986 * puzzle is ambiguous (and hence bad).
987 *
988 * ----------------------------------------------------------------------
989 *
990 * Lemma: On a wxh grid with w, h >= 1 and (w > 2 or h > 2), there is
991 * at least one good puzzle.
992 *
993 * Proof: assume that w > h (otherwise rotate the proof again). Paint
994 * the top left and bottom right corners black, and fill a clue into
995 * all the other squares. Present this board to the solver code (or
996 * player, hypothetically), except with the two black squares as blank
997 * squares.
998 *
999 * For an Nx1 puzzle, observe that every clue is N - 2, and there are
1000 * N - 2 of them in one connected sequence, so the remaining two
1001 * squares can be deduced to be black, which solves the puzzle.
1002 *
1003 * For any other puzzle, let j be a cell in the same row as a black
1004 * cell, but not in the same column (such a cell doesn't exist in 2x3
1005 * puzzles, but we assume w > h and such cells exist in 3x2 puzzles).
1006 *
1007 * Note that the number of cells in axis parallel `rays' going out
1008 * from j exceeds j's clue value by one. Only one such cell is a
1009 * non-clue, so it must be black. Similarly for the other corner (let
1010 * j' be a cell in the same row as the _other_ black cell, but not in
1011 * the same column as _any_ black cell; repeat this argument at j').
1012 *
1013 * This fills the grid and satisfies all clues and the adjacency
1014 * constraint and doesn't paint on top of any clues. All that is left
1015 * to see is connectedness.
1016 *
1017 * Observe that the white cells in each column form a single connected
1018 * `run', and each column contains a white cell adjacent to a white
1019 * cell in the column to the right, if that column exists.
1020 *
1021 * Thus, any cell in the left-most column can reach any other cell:
1022 * first go to the target column (by repeatedly going to the cell in
1023 * your current column that lets you go right, then going right), then
1024 * go up or down to the desired cell.
1025 *
1026 * As reachability is symmetric (in undirected graphs) and transitive,
1027 * any cell can reach any left-column cell, and from there any other
1028 * cell.
1029 */
1030
1031/* ----------------------------------------------------------------------
1032 * Game encoding and decoding
1033 */
1034
1035#define NDIGITS_BASE '!'
1036
1037static char *newdesc_encode_game_description(int area, puzzle_size *grid)
1038{
1039 char *desc = NULL;
1040 int desclen = 0, descsize = 0;
1041 int run, i;
1042
1043 run = 0;
1044 for (i = 0; i <= area; i++) {
1045 int n = (i < area ? grid[i] : -1);
1046
1047 if (!n)
1048 run++;
1049 else {
1050 if (descsize < desclen + 40) {
1051 descsize = desclen * 3 / 2 + 40;
1052 desc = sresize(desc, descsize, char);
1053 }
1054 if (run) {
1055 while (run > 0) {
1056 int c = 'a' - 1 + run;
1057 if (run > 26)
1058 c = 'z';
1059 desc[desclen++] = c;
1060 run -= c - ('a' - 1);
1061 }
1062 } else {
1063 /*
1064 * If there's a number in the very top left or
1065 * bottom right, there's no point putting an
1066 * unnecessary _ before or after it.
1067 */
1068 if (desclen > 0 && n > 0)
1069 desc[desclen++] = '_';
1070 }
1071 if (n > 0)
1072 desclen += sprintf(desc+desclen, "%d", n);
1073 run = 0;
1074 }
1075 }
1076 desc[desclen] = '\0';
1077 return desc;
1078}
1079
1080static char *validate_desc(const game_params *params, const char *desc)
1081{
1082 int const n = params->w * params->h;
1083 int squares = 0;
1084 int range = params->w + params->h - 1; /* maximum cell value */
1085
1086 while (*desc && *desc != ',') {
1087 int c = *desc++;
1088 if (c >= 'a' && c <= 'z') {
1089 squares += c - 'a' + 1;
1090 } else if (c == '_') {
1091 /* do nothing */;
1092 } else if (c > '0' && c <= '9') {
1093 int val = atoi(desc-1);
1094 if (val < 1 || val > range)
1095 return "Out-of-range number in game description";
1096 squares++;
1097 while (*desc >= '0' && *desc <= '9')
1098 desc++;
1099 } else
1100 return "Invalid character in game description";
1101 }
1102
1103 if (squares < n)
1104 return "Not enough data to fill grid";
1105
1106 if (squares > n)
1107 return "Too much data to fit in grid";
1108
1109 return NULL;
1110}
1111
1112static game_state *new_game(midend *me, const game_params *params,
1113 const char *desc)
1114{
1115 int i;
1116 const char *p;
1117
1118 int const n = params->w * params->h;
1119 game_state *state = snew(game_state);
1120
1121 me = NULL; /* I don't need it, I shouldn't use it */
1122
1123 state->params = *params; /* structure copy */
1124 state->grid = snewn(n, puzzle_size);
1125
1126 p = desc;
1127 i = 0;
1128 while (i < n && *p) {
1129 int c = *p++;
1130 if (c >= 'a' && c <= 'z') {
1131 int squares = c - 'a' + 1;
1132 while (squares--)
1133 state->grid[i++] = 0;
1134 } else if (c == '_') {
1135 /* do nothing */;
1136 } else if (c > '0' && c <= '9') {
1137 int val = atoi(p-1);
1138 assert(val >= 1 && val <= params->w+params->h-1);
1139 state->grid[i++] = val;
1140 while (*p >= '0' && *p <= '9')
1141 p++;
1142 }
1143 }
1144 assert(i == n);
1145 state->has_cheated = FALSE;
1146 state->was_solved = FALSE;
1147
1148 return state;
1149}
1150
1151/* ----------------------------------------------------------------------
1152 * User interface: ascii
1153 */
1154
1155static int game_can_format_as_text_now(const game_params *params)
1156{
1157 return TRUE;
1158}
1159
1160static char *game_text_format(const game_state *state)
1161{
1162 int cellsize, r, c, i, w_string, h_string, n_string;
1163 char *ret, *buf, *gridline;
1164
1165 int const w = state->params.w, h = state->params.h;
1166
1167 cellsize = 0; /* or may be used uninitialized */
1168
1169 for (c = 0; c < w; ++c) {
1170 for (r = 0; r < h; ++r) {
1171 puzzle_size k = state->grid[idx(r, c, w)];
1172 int d;
1173 for (d = 0; k; k /= 10, ++d);
1174 cellsize = max(cellsize, d);
1175 }
1176 }
1177
1178 ++cellsize;
1179
1180 w_string = w * cellsize + 2; /* "|%d|%d|...|\n" */
1181 h_string = 2 * h + 1; /* "+--+--+...+\n%s\n+--+--+...+\n" */
1182 n_string = w_string * h_string;
1183
1184 gridline = snewn(w_string + 1, char); /* +1: NUL terminator */
1185 memset(gridline, '-', w_string);
1186 for (c = 0; c <= w; ++c) gridline[c * cellsize] = '+';
1187 gridline[w_string - 1] = '\n';
1188 gridline[w_string - 0] = '\0';
1189
1190 buf = ret = snewn(n_string + 1, char); /* +1: NUL terminator */
1191 for (i = r = 0; r < h; ++r) {
1192 memcpy(buf, gridline, w_string);
1193 buf += w_string;
1194 for (c = 0; c < w; ++c, ++i) {
1195 char ch;
1196 switch (state->grid[i]) {
1197 case BLACK: ch = '#'; break;
1198 case WHITE: ch = '.'; break;
1199 case EMPTY: ch = ' '; break;
1200 default:
1201 buf += sprintf(buf, "|%*d", cellsize - 1, state->grid[i]);
1202 continue;
1203 }
1204 *buf++ = '|';
1205 memset(buf, ch, cellsize - 1);
1206 buf += cellsize - 1;
1207 }
1208 buf += sprintf(buf, "|\n");
1209 }
1210 memcpy(buf, gridline, w_string);
1211 buf += w_string;
1212 assert (buf - ret == n_string);
1213 *buf = '\0';
1214
1215 sfree(gridline);
1216
1217 return ret;
1218}
1219
1220/* ----------------------------------------------------------------------
1221 * User interfaces: interactive
1222 */
1223
1224struct game_ui {
1225 puzzle_size r, c; /* cursor position */
1226 unsigned int cursor_show: 1;
1227};
1228
1229static game_ui *new_ui(const game_state *state)
1230{
1231 struct game_ui *ui = snew(game_ui);
1232 ui->r = ui->c = 0;
1233 ui->cursor_show = FALSE;
1234 return ui;
1235}
1236
1237static void free_ui(game_ui *ui)
1238{
1239 sfree(ui);
1240}
1241
1242static char *encode_ui(const game_ui *ui)
1243{
1244 return NULL;
1245}
1246
1247static void decode_ui(game_ui *ui, const char *encoding)
1248{
1249}
1250
1251typedef struct drawcell {
1252 puzzle_size value;
1253 unsigned int error: 1;
1254 unsigned int cursor: 1;
1255 unsigned int flash: 1;
1256} drawcell;
1257
1258struct game_drawstate {
1259 int tilesize;
1260 drawcell *grid;
1261 unsigned int started: 1;
1262};
1263
1264#define TILESIZE (ds->tilesize)
1265#define BORDER (TILESIZE / 2)
1266#define COORD(x) ((x) * TILESIZE + BORDER)
1267#define FROMCOORD(x) (((x) - BORDER) / TILESIZE)
1268
1269static char *interpret_move(const game_state *state, game_ui *ui,
1270 const game_drawstate *ds,
1271 int x, int y, int button)
1272{
1273 enum {none, forwards, backwards, hint};
1274 int const w = state->params.w, h = state->params.h;
1275 int r = ui->r, c = ui->c, action = none, cell;
1276 int shift = button & MOD_SHFT;
1277 button &= ~shift;
1278
1279 if (IS_CURSOR_SELECT(button) && !ui->cursor_show) return NULL;
1280
1281 if (IS_MOUSE_DOWN(button)) {
1282 r = FROMCOORD(y + TILESIZE) - 1; /* or (x, y) < TILESIZE) */
1283 c = FROMCOORD(x + TILESIZE) - 1; /* are considered inside */
1284 if (out_of_bounds(r, c, w, h)) return NULL;
1285 ui->r = r;
1286 ui->c = c;
1287 ui->cursor_show = FALSE;
1288 }
1289
1290 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1291 /*
1292 * Utterly awful hack, exactly analogous to the one in Slant,
1293 * to configure the left and right mouse buttons the opposite
1294 * way round.
1295 *
1296 * The original puzzle submitter thought it would be more
1297 * useful to have the left button turn an empty square into a
1298 * dotted one, on the grounds that that was what you did most
1299 * often; I (SGT) felt instinctively that the left button
1300 * ought to place black squares and the right button place
1301 * dots, on the grounds that that was consistent with many
1302 * other puzzles in which the left button fills in the data
1303 * used by the solution checker while the right button places
1304 * pencil marks for the user's convenience.
1305 *
1306 * My first beta-player wasn't sure either, so I thought I'd
1307 * pre-emptively put in a 'configuration' mechanism just in
1308 * case.
1309 */
1310 {
1311 static int swap_buttons = -1;
1312 if (swap_buttons < 0) {
1313 char *env = getenv("RANGE_SWAP_BUTTONS");
1314 swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y'));
1315 }
1316 if (swap_buttons) {
1317 if (button == LEFT_BUTTON)
1318 button = RIGHT_BUTTON;
1319 else
1320 button = LEFT_BUTTON;
1321 }
1322 }
1323 }
1324
1325 switch (button) {
1326 case CURSOR_SELECT : case LEFT_BUTTON: action = backwards; break;
1327 case CURSOR_SELECT2: case RIGHT_BUTTON: action = forwards; break;
1328 case 'h': case 'H' : action = hint; break;
1329 case CURSOR_UP: case CURSOR_DOWN:
1330 case CURSOR_LEFT: case CURSOR_RIGHT:
1331 if (ui->cursor_show) {
1332 int i;
1333 for (i = 0; i < 4 && cursors[i] != button; ++i);
1334 assert (i < 4);
1335 if (shift) {
1336 int pre_r = r, pre_c = c, do_pre, do_post;
1337 cell = state->grid[idx(r, c, state->params.w)];
1338 do_pre = (cell == EMPTY);
1339
1340 if (out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) {
1341 if (do_pre)
1342 return nfmtstr(40, "W,%d,%d", pre_r, pre_c);
1343 else
1344 return NULL;
1345 }
1346
1347 ui->r += dr[i];
1348 ui->c += dc[i];
1349
1350 cell = state->grid[idx(ui->r, ui->c, state->params.w)];
1351 do_post = (cell == EMPTY);
1352
1353 /* (do_pre ? "..." : "") concat (do_post ? "..." : "") */
1354 if (do_pre && do_post)
1355 return nfmtstr(80, "W,%d,%dW,%d,%d",
1356 pre_r, pre_c, ui->r, ui->c);
1357 else if (do_pre)
1358 return nfmtstr(40, "W,%d,%d", pre_r, pre_c);
1359 else if (do_post)
1360 return nfmtstr(40, "W,%d,%d", ui->r, ui->c);
1361 else
1362 return "";
1363
1364 } else if (!out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) {
1365 ui->r += dr[i];
1366 ui->c += dc[i];
1367 }
1368 } else ui->cursor_show = TRUE;
1369 return "";
1370 }
1371
1372 if (action == hint) {
1373 move *end, *buf = snewn(state->params.w * state->params.h,
1374 struct move);
1375 char *ret = NULL;
1376 end = solve_internal(state, buf, DIFF_RECURSION);
1377 if (end != NULL && end > buf) {
1378 ret = nfmtstr(40, "%c,%d,%d",
1379 buf->colour == M_BLACK ? 'B' : 'W',
1380 buf->square.r, buf->square.c);
1381 /* We used to set a flag here in the game_ui indicating
1382 * that the player had used the hint function. I (SGT)
1383 * retired it, on grounds of consistency with other games
1384 * (most of these games will still flash to indicate
1385 * completion if you solved and undid it, so why not if
1386 * you got a hint?) and because the flash is as much about
1387 * checking you got it all right than about congratulating
1388 * you on a job well done. */
1389 }
1390 sfree(buf);
1391 return ret;
1392 }
1393
1394 cell = state->grid[idx(r, c, state->params.w)];
1395 if (cell > 0) return NULL;
1396
1397 if (action == forwards) switch (cell) {
1398 case EMPTY: return nfmtstr(40, "W,%d,%d", r, c);
1399 case WHITE: return nfmtstr(40, "B,%d,%d", r, c);
1400 case BLACK: return nfmtstr(40, "E,%d,%d", r, c);
1401 }
1402
1403 else if (action == backwards) switch (cell) {
1404 case BLACK: return nfmtstr(40, "W,%d,%d", r, c);
1405 case WHITE: return nfmtstr(40, "E,%d,%d", r, c);
1406 case EMPTY: return nfmtstr(40, "B,%d,%d", r, c);
1407 }
1408
1409 return NULL;
1410}
1411
1412static int find_errors(const game_state *state, int *report)
1413{
1414 int const w = state->params.w, h = state->params.h, n = w * h;
1415 int *dsf;
1416
1417 int r, c, i;
1418
1419 int nblack = 0, any_white_cell = -1;
1420 game_state *dup = dup_game(state);
1421
1422 for (i = r = 0; r < h; ++r)
1423 for (c = 0; c < w; ++c, ++i) {
1424 switch (state->grid[i]) {
1425
1426 case BLACK:
1427 {
1428 int j;
1429 ++nblack;
1430 for (j = 0; j < 4; ++j) {
1431 int const rr = r + dr[j], cc = c + dc[j];
1432 if (out_of_bounds(rr, cc, w, h)) continue;
1433 if (state->grid[idx(rr, cc, w)] != BLACK) continue;
1434 if (!report) goto found_error;
1435 report[i] = TRUE;
1436 break;
1437 }
1438 }
1439 break;
1440 default:
1441 {
1442 int j, runs;
1443 for (runs = 1, j = 0; j < 4; ++j) {
1444 int const rr = r + dr[j], cc = c + dc[j];
1445 runs += runlength(rr, cc, dr[j], dc[j], state,
1446 ~MASK(BLACK));
1447 }
1448 if (!report) {
1449 if (runs != state->grid[i]) goto found_error;
1450 } else if (runs < state->grid[i]) report[i] = TRUE;
1451 else {
1452 for (runs = 1, j = 0; j < 4; ++j) {
1453 int const rr = r + dr[j], cc = c + dc[j];
1454 runs += runlength(rr, cc, dr[j], dc[j], state,
1455 ~(MASK(BLACK) | MASK(EMPTY)));
1456 }
1457 if (runs > state->grid[i]) report[i] = TRUE;
1458 }
1459 }
1460
1461 /* note: fallthrough _into_ these cases */
1462 case EMPTY:
1463 case WHITE: any_white_cell = i;
1464 }
1465 }
1466
1467 /*
1468 * Check that all the white cells form a single connected component.
1469 */
1470 dsf = snew_dsf(n);
1471 for (r = 0; r < h-1; ++r)
1472 for (c = 0; c < w; ++c)
1473 if (state->grid[r*w+c] != BLACK &&
1474 state->grid[(r+1)*w+c] != BLACK)
1475 dsf_merge(dsf, r*w+c, (r+1)*w+c);
1476 for (r = 0; r < h; ++r)
1477 for (c = 0; c < w-1; ++c)
1478 if (state->grid[r*w+c] != BLACK &&
1479 state->grid[r*w+(c+1)] != BLACK)
1480 dsf_merge(dsf, r*w+c, r*w+(c+1));
1481 if (nblack + dsf_size(dsf, any_white_cell) < n) {
1482 int biggest, canonical;
1483
1484 if (!report) {
1485 sfree(dsf);
1486 goto found_error;
1487 }
1488
1489 /*
1490 * Report this error by choosing one component to be the
1491 * canonical one (we pick the largest, arbitrarily
1492 * tie-breaking towards lower array indices) and highlighting
1493 * as an error any square in a different component.
1494 */
1495 canonical = -1;
1496 biggest = 0;
1497 for (i = 0; i < n; ++i)
1498 if (state->grid[i] != BLACK) {
1499 int size = dsf_size(dsf, i);
1500 if (size > biggest) {
1501 biggest = size;
1502 canonical = dsf_canonify(dsf, i);
1503 }
1504 }
1505
1506 for (i = 0; i < n; ++i)
1507 if (state->grid[i] != BLACK && dsf_canonify(dsf, i) != canonical)
1508 report[i] = TRUE;
1509 }
1510 sfree(dsf);
1511
1512 free_game(dup);
1513 return FALSE; /* if report != NULL, this is ignored */
1514
1515found_error:
1516 free_game(dup);
1517 return TRUE;
1518}
1519
1520static game_state *execute_move(const game_state *state, const char *move)
1521{
1522 signed int r, c, value, nchars, ntok;
1523 signed char what_to_do;
1524 game_state *ret;
1525
1526 assert (move);
1527
1528 ret = dup_game(state);
1529
1530 if (*move == 'S') {
1531 ++move;
1532 ret->has_cheated = ret->was_solved = TRUE;
1533 }
1534
1535 for (; *move; move += nchars) {
1536 ntok = sscanf(move, "%c,%d,%d%n", &what_to_do, &r, &c, &nchars);
1537 if (ntok < 3) goto failure;
1538 switch (what_to_do) {
1539 case 'W': value = WHITE; break;
1540 case 'E': value = EMPTY; break;
1541 case 'B': value = BLACK; break;
1542 default: goto failure;
1543 }
1544 if (out_of_bounds(r, c, ret->params.w, ret->params.h)) goto failure;
1545 ret->grid[idx(r, c, ret->params.w)] = value;
1546 }
1547
1548 if (ret->was_solved == FALSE)
1549 ret->was_solved = !find_errors(ret, NULL);
1550
1551 return ret;
1552
1553failure:
1554 free_game(ret);
1555 return NULL;
1556}
1557
1558static void game_changed_state(game_ui *ui, const game_state *oldstate,
1559 const game_state *newstate)
1560{
1561}
1562
1563static float game_anim_length(const game_state *oldstate,
1564 const game_state *newstate, int dir, game_ui *ui)
1565{
1566 return 0.0F;
1567}
1568
1569#define FLASH_TIME 0.7F
1570
1571static float game_flash_length(const game_state *from,
1572 const game_state *to, int dir, game_ui *ui)
1573{
1574 if (!from->was_solved && to->was_solved && !to->has_cheated)
1575 return FLASH_TIME;
1576 return 0.0F;
1577}
1578
1579static int game_status(const game_state *state)
1580{
1581 return state->was_solved ? +1 : 0;
1582}
1583
1584/* ----------------------------------------------------------------------
1585 * Drawing routines.
1586 */
1587
1588#define PREFERRED_TILE_SIZE 32
1589
1590enum {
1591 COL_BACKGROUND = 0,
1592 COL_GRID,
1593 COL_BLACK = COL_GRID,
1594 COL_TEXT = COL_GRID,
1595 COL_USER = COL_GRID,
1596 COL_ERROR,
1597 COL_LOWLIGHT,
1598 COL_HIGHLIGHT = COL_ERROR, /* mkhighlight needs it, I don't */
1599 COL_CURSOR = COL_LOWLIGHT,
1600 NCOLOURS
1601};
1602
1603static void game_compute_size(const game_params *params, int tilesize,
1604 int *x, int *y)
1605{
1606 *x = (1 + params->w) * tilesize;
1607 *y = (1 + params->h) * tilesize;
1608}
1609
1610static void game_set_size(drawing *dr, game_drawstate *ds,
1611 const game_params *params, int tilesize)
1612{
1613 ds->tilesize = tilesize;
1614}
1615
1616#define COLOUR(ret, i, r, g, b) \
1617 ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b)))
1618
1619static float *game_colours(frontend *fe, int *ncolours)
1620{
1621 float *ret = snewn(3 * NCOLOURS, float);
1622
1623 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1624 COLOUR(ret, COL_GRID, 0.0F, 0.0F, 0.0F);
1625 COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F);
1626
1627 *ncolours = NCOLOURS;
1628 return ret;
1629}
1630
1631static drawcell makecell(puzzle_size value, int error, int cursor, int flash)
1632{
1633 drawcell ret;
1634 setmember(ret, value);
1635 setmember(ret, error);
1636 setmember(ret, cursor);
1637 setmember(ret, flash);
1638 return ret;
1639}
1640
1641static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1642{
1643 int const w = state->params.w, h = state->params.h, n = w * h;
1644 struct game_drawstate *ds = snew(struct game_drawstate);
1645 int i;
1646
1647 ds->tilesize = 0;
1648 ds->started = FALSE;
1649
1650 ds->grid = snewn(n, drawcell);
1651 for (i = 0; i < n; ++i)
1652 ds->grid[i] = makecell(w + h, FALSE, FALSE, FALSE);
1653
1654 return ds;
1655}
1656
1657static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1658{
1659 sfree(ds->grid);
1660 sfree(ds);
1661}
1662
1663#define cmpmember(a, b, field) ((a) . field == (b) . field)
1664
1665static int cell_eq(drawcell a, drawcell b)
1666{
1667 return
1668 cmpmember(a, b, value) &&
1669 cmpmember(a, b, error) &&
1670 cmpmember(a, b, cursor) &&
1671 cmpmember(a, b, flash);
1672}
1673
1674static void draw_cell(drawing *dr, game_drawstate *ds, int r, int c,
1675 drawcell cell);
1676
1677static void game_redraw(drawing *dr, game_drawstate *ds,
1678 const game_state *oldstate, const game_state *state,
1679 int dir, const game_ui *ui,
1680 float animtime, float flashtime)
1681{
1682 int const w = state->params.w, h = state->params.h, n = w * h;
1683 int const wpx = (w+1) * ds->tilesize, hpx = (h+1) * ds->tilesize;
1684 int const flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
1685
1686 int r, c, i;
1687
1688 int *errors = snewn(n, int);
1689 memset(errors, FALSE, n * sizeof (int));
1690 find_errors(state, errors);
1691
1692 assert (oldstate == NULL); /* only happens if animating moves */
1693
1694 if (!ds->started) {
1695 ds->started = TRUE;
1696 draw_rect(dr, 0, 0, wpx, hpx, COL_BACKGROUND);
1697 draw_update(dr, 0, 0, wpx, hpx);
1698 }
1699
1700 for (i = r = 0; r < h; ++r) {
1701 for (c = 0; c < w; ++c, ++i) {
1702 drawcell cell = makecell(state->grid[i], errors[i], FALSE, flash);
1703 if (r == ui->r && c == ui->c && ui->cursor_show)
1704 cell.cursor = TRUE;
1705 if (!cell_eq(cell, ds->grid[i])) {
1706 draw_cell(dr, ds, r, c, cell);
1707 ds->grid[i] = cell;
1708 }
1709 }
1710 }
1711
1712 sfree(errors);
1713}
1714
1715static void draw_cell(drawing *draw, game_drawstate *ds, int r, int c,
1716 drawcell cell)
1717{
1718 int const ts = ds->tilesize;
1719 int const y = BORDER + ts * r, x = BORDER + ts * c;
1720 int const tx = x + (ts / 2), ty = y + (ts / 2);
1721 int const dotsz = (ds->tilesize + 9) / 10;
1722
1723 int const colour = (cell.value == BLACK ?
1724 cell.error ? COL_ERROR : COL_BLACK :
1725 cell.flash || cell.cursor ?
1726 COL_LOWLIGHT : COL_BACKGROUND);
1727
1728 draw_rect_outline(draw, x, y, ts + 1, ts + 1, COL_GRID);
1729 draw_rect (draw, x + 1, y + 1, ts - 1, ts - 1, colour);
1730 if (cell.error)
1731 draw_rect_outline(draw, x + 1, y + 1, ts - 1, ts - 1, COL_ERROR);
1732
1733 switch (cell.value) {
1734 case WHITE: draw_rect(draw, tx - dotsz / 2, ty - dotsz / 2, dotsz, dotsz,
1735 cell.error ? COL_ERROR : COL_USER);
1736 case BLACK: case EMPTY: break;
1737 default:
1738 {
1739 int const colour = (cell.error ? COL_ERROR : COL_GRID);
1740 char *msg = nfmtstr(10, "%d", cell.value);
1741 draw_text(draw, tx, ty, FONT_VARIABLE, ts * 3 / 5,
1742 ALIGN_VCENTRE | ALIGN_HCENTRE, colour, msg);
1743 sfree(msg);
1744 }
1745 }
1746
1747 draw_update(draw, x, y, ts + 1, ts + 1);
1748}
1749
1750static int game_timing_state(const game_state *state, game_ui *ui)
1751{
1752 puts("warning: game_timing_state was called (this shouldn't happen)");
1753 return FALSE; /* the (non-existing) timer should not be running */
1754}
1755
1756/* ----------------------------------------------------------------------
1757 * User interface: print
1758 */
1759
1760static void game_print_size(const game_params *params, float *x, float *y)
1761{
1762 int print_width, print_height;
1763 game_compute_size(params, 800, &print_width, &print_height);
1764 *x = print_width / 100.0F;
1765 *y = print_height / 100.0F;
1766}
1767
1768static void game_print(drawing *dr, const game_state *state, int tilesize)
1769{
1770 int const w = state->params.w, h = state->params.h;
1771 game_drawstate ds_obj, *ds = &ds_obj;
1772 int r, c, i, colour;
1773
1774 ds->tilesize = tilesize;
1775
1776 colour = print_mono_colour(dr, 1); assert(colour == COL_BACKGROUND);
1777 colour = print_mono_colour(dr, 0); assert(colour == COL_GRID);
1778 colour = print_mono_colour(dr, 1); assert(colour == COL_ERROR);
1779 colour = print_mono_colour(dr, 0); assert(colour == COL_LOWLIGHT);
1780 colour = print_mono_colour(dr, 0); assert(colour == NCOLOURS);
1781
1782 for (i = r = 0; r < h; ++r)
1783 for (c = 0; c < w; ++c, ++i)
1784 draw_cell(dr, ds, r, c,
1785 makecell(state->grid[i], FALSE, FALSE, FALSE));
1786
1787 print_line_width(dr, 3 * tilesize / 40);
1788 draw_rect_outline(dr, BORDER, BORDER, w*TILESIZE, h*TILESIZE, COL_GRID);
1789}
1790
1791/* And that's about it ;-) **************************************************/
1792
1793#ifdef COMBINED
1794#define thegame range
1795#endif
1796
1797struct game const thegame = {
1798 "Range", "games.range", "range",
1799 default_params,
1800 game_fetch_preset, NULL,
1801 decode_params,
1802 encode_params,
1803 free_params,
1804 dup_params,
1805 TRUE, game_configure, custom_params,
1806 validate_params,
1807 new_game_desc,
1808 validate_desc,
1809 new_game,
1810 dup_game,
1811 free_game,
1812 TRUE, solve_game,
1813 TRUE, game_can_format_as_text_now, game_text_format,
1814 new_ui,
1815 free_ui,
1816 encode_ui,
1817 decode_ui,
1818 game_changed_state,
1819 interpret_move,
1820 execute_move,
1821 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1822 game_colours,
1823 game_new_drawstate,
1824 game_free_drawstate,
1825 game_redraw,
1826 game_anim_length,
1827 game_flash_length,
1828 game_status,
1829 TRUE, FALSE, game_print_size, game_print,
1830 FALSE, /* wants_statusbar */
1831 FALSE, game_timing_state,
1832 0, /* flags */
1833};
diff --git a/apps/plugins/puzzles/src/range.html b/apps/plugins/puzzles/src/range.html
new file mode 100644
index 0000000000..7a93216805
--- /dev/null
+++ b/apps/plugins/puzzles/src/range.html
@@ -0,0 +1,67 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Range</title>
7<link rel="previous" href="signpost.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="pearl.html">
12</head>
13<body>
14<p><a href="signpost.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="pearl.html">Next</a></p>
15<h1><a name="C35"></a>Chapter 35: <a name="i0"></a>Range</h1>
16<p>
17You have a grid of squares; some squares contain numbers. Your job is to colour some of the squares black, such that several criteria are satisfied:
18</p>
19<ul><li>
20no square with a number is coloured black.
21</li>
22<li>
23no two black squares are adjacent (horizontally or vertically).
24</li>
25<li>
26for any two white squares, there is a path between them using only white squares.
27</li>
28<li>
29for each square with a number, that number denotes the total number of white squares reachable from that square going in a straight line in any horizontal or vertical direction until hitting a wall or a black square; the square with the number is included in the total (once).
30</li>
31</ul>
32<p>
33For instance, a square containing the number one must have four black squares as its neighbours by the last criterion; but then it's impossible for it to be connected to any outside white square, which violates the second to last criterion. So no square will contain the number one.
34</p>
35<p>
36Credit for this puzzle goes to <a name="i1"></a>Nikoli, who have variously called it &#8216;Kurodoko&#8217;, &#8216;Kuromasu&#8217; or &#8216;Where is Black Cells&#8217;. <a href="#p0">[18]</a>.
37</p>
38<p>
39Range was contributed to this collection by Jonas K&#246;lker.
40</p>
41<p><a name="p0"></a>
42[18] <a href="http://www.nikoli.co.jp/en/puzzles/where_is_black_cells.html"><code>http://www.nikoli.co.jp/en/puzzles/where_is_black_cells.html</code></a>
43</p>
44<h2><a name="S35.1"></a>35.1 <a name="i2"></a>Range controls</h2>
45<p>
46Click with the left button to paint a square black, or with the right button to mark a square with a dot to indicate that you are sure it should <em>not</em> be painted black. Repeated clicking with either button will cycle the square through the three possible states (filled, dotted or empty) in opposite directions.
47</p>
48<p>
49You can also use the cursor keys to move around the grid squares. Pressing Return does the same as clicking with the left button, while pressing Space does the same as a right button click. Moving with the cursor keys while holding Shift will place dots in all squares that are moved through.
50</p>
51<p>
52(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
53</p>
54<h2><a name="S35.2"></a>35.2 <a name="i3"></a>Range parameters</h2>
55<p>
56These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
57</p>
58<dl><dt>
59<em>Width</em>, <em>Height</em>
60</dt>
61<dd>
62Size of grid in squares.
63</dd>
64</dl>
65
66<hr><address></address></body>
67</html>
diff --git a/apps/plugins/puzzles/src/rect.R b/apps/plugins/puzzles/src/rect.R
new file mode 100644
index 0000000000..1448c0fa63
--- /dev/null
+++ b/apps/plugins/puzzles/src/rect.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3rect : [X] GTK COMMON rect rect-icon|no-icon
4
5rect : [G] WINDOWS COMMON rect rect.res|noicon.res
6
7ALL += rect[COMBINED]
8
9!begin am gtk
10GAMES += rect
11!end
12
13!begin >list.c
14 A(rect) \
15!end
16
17!begin >gamedesc.txt
18rect:rect.exe:Rectangles:Rectangles puzzle:Divide the grid into rectangles with areas equal to the numbers.
19!end
diff --git a/apps/plugins/puzzles/src/rect.c b/apps/plugins/puzzles/src/rect.c
new file mode 100644
index 0000000000..465e1436fa
--- /dev/null
+++ b/apps/plugins/puzzles/src/rect.c
@@ -0,0 +1,3000 @@
1/*
2 * rect.c: Puzzle from nikoli.co.jp. You have a square grid with
3 * numbers in some squares; you must divide the square grid up into
4 * variously sized rectangles, such that every rectangle contains
5 * exactly one numbered square and the area of each rectangle is
6 * equal to the number contained in it.
7 */
8
9/*
10 * TODO:
11 *
12 * - Improve singleton removal.
13 * + It would be nice to limit the size of the generated
14 * rectangles in accordance with existing constraints such as
15 * the maximum rectangle size and the one about not
16 * generating a rectangle the full width or height of the
17 * grid.
18 * + This could be achieved by making a less random choice
19 * about which of the available options to use.
20 * + Alternatively, we could create our rectangle and then
21 * split it up.
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <assert.h>
28#include <ctype.h>
29#include <math.h>
30
31#include "puzzles.h"
32
33enum {
34 COL_BACKGROUND,
35 COL_CORRECT,
36 COL_LINE,
37 COL_TEXT,
38 COL_GRID,
39 COL_DRAG, COL_DRAGERASE,
40 COL_CURSOR,
41 NCOLOURS
42};
43
44struct game_params {
45 int w, h;
46 float expandfactor;
47 int unique;
48};
49
50#define INDEX(state, x, y) (((y) * (state)->w) + (x))
51#define index(state, a, x, y) ((a) [ INDEX(state,x,y) ])
52#define grid(state,x,y) index(state, (state)->grid, x, y)
53#define vedge(state,x,y) index(state, (state)->vedge, x, y)
54#define hedge(state,x,y) index(state, (state)->hedge, x, y)
55
56#define CRANGE(state,x,y,dx,dy) ( (x) >= dx && (x) < (state)->w && \
57 (y) >= dy && (y) < (state)->h )
58#define RANGE(state,x,y) CRANGE(state,x,y,0,0)
59#define HRANGE(state,x,y) CRANGE(state,x,y,0,1)
60#define VRANGE(state,x,y) CRANGE(state,x,y,1,0)
61
62#define PREFERRED_TILE_SIZE 24
63#define TILE_SIZE (ds->tilesize)
64#ifdef SMALL_SCREEN
65#define BORDER (2)
66#else
67#define BORDER (TILE_SIZE * 3 / 4)
68#endif
69
70#define CORNER_TOLERANCE 0.15F
71#define CENTRE_TOLERANCE 0.15F
72
73#define FLASH_TIME 0.13F
74
75#define COORD(x) ( (x) * TILE_SIZE + BORDER )
76#define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE )
77
78struct game_state {
79 int w, h;
80 int *grid; /* contains the numbers */
81 unsigned char *vedge; /* (w+1) x h */
82 unsigned char *hedge; /* w x (h+1) */
83 int completed, cheated;
84 unsigned char *correct;
85};
86
87static game_params *default_params(void)
88{
89 game_params *ret = snew(game_params);
90
91 ret->w = ret->h = 7;
92 ret->expandfactor = 0.0F;
93 ret->unique = TRUE;
94
95 return ret;
96}
97
98static int game_fetch_preset(int i, char **name, game_params **params)
99{
100 game_params *ret;
101 int w, h;
102 char buf[80];
103
104 switch (i) {
105 case 0: w = 7, h = 7; break;
106 case 1: w = 9, h = 9; break;
107 case 2: w = 11, h = 11; break;
108 case 3: w = 13, h = 13; break;
109 case 4: w = 15, h = 15; break;
110#ifndef SMALL_SCREEN
111 case 5: w = 17, h = 17; break;
112 case 6: w = 19, h = 19; break;
113#endif
114 default: return FALSE;
115 }
116
117 sprintf(buf, "%dx%d", w, h);
118 *name = dupstr(buf);
119 *params = ret = snew(game_params);
120 ret->w = w;
121 ret->h = h;
122 ret->expandfactor = 0.0F;
123 ret->unique = TRUE;
124 return TRUE;
125}
126
127static void free_params(game_params *params)
128{
129 sfree(params);
130}
131
132static game_params *dup_params(const game_params *params)
133{
134 game_params *ret = snew(game_params);
135 *ret = *params; /* structure copy */
136 return ret;
137}
138
139static void decode_params(game_params *ret, char const *string)
140{
141 ret->w = ret->h = atoi(string);
142 while (*string && isdigit((unsigned char)*string)) string++;
143 if (*string == 'x') {
144 string++;
145 ret->h = atoi(string);
146 while (*string && isdigit((unsigned char)*string)) string++;
147 }
148 if (*string == 'e') {
149 string++;
150 ret->expandfactor = (float)atof(string);
151 while (*string &&
152 (*string == '.' || isdigit((unsigned char)*string))) string++;
153 }
154 if (*string == 'a') {
155 string++;
156 ret->unique = FALSE;
157 }
158}
159
160static char *encode_params(const game_params *params, int full)
161{
162 char data[256];
163
164 sprintf(data, "%dx%d", params->w, params->h);
165 if (full && params->expandfactor)
166 sprintf(data + strlen(data), "e%g", params->expandfactor);
167 if (full && !params->unique)
168 strcat(data, "a");
169
170 return dupstr(data);
171}
172
173static config_item *game_configure(const game_params *params)
174{
175 config_item *ret;
176 char buf[80];
177
178 ret = snewn(5, config_item);
179
180 ret[0].name = "Width";
181 ret[0].type = C_STRING;
182 sprintf(buf, "%d", params->w);
183 ret[0].sval = dupstr(buf);
184 ret[0].ival = 0;
185
186 ret[1].name = "Height";
187 ret[1].type = C_STRING;
188 sprintf(buf, "%d", params->h);
189 ret[1].sval = dupstr(buf);
190 ret[1].ival = 0;
191
192 ret[2].name = "Expansion factor";
193 ret[2].type = C_STRING;
194 sprintf(buf, "%g", params->expandfactor);
195 ret[2].sval = dupstr(buf);
196 ret[2].ival = 0;
197
198 ret[3].name = "Ensure unique solution";
199 ret[3].type = C_BOOLEAN;
200 ret[3].sval = NULL;
201 ret[3].ival = params->unique;
202
203 ret[4].name = NULL;
204 ret[4].type = C_END;
205 ret[4].sval = NULL;
206 ret[4].ival = 0;
207
208 return ret;
209}
210
211static game_params *custom_params(const config_item *cfg)
212{
213 game_params *ret = snew(game_params);
214
215 ret->w = atoi(cfg[0].sval);
216 ret->h = atoi(cfg[1].sval);
217 ret->expandfactor = (float)atof(cfg[2].sval);
218 ret->unique = cfg[3].ival;
219
220 return ret;
221}
222
223static char *validate_params(const game_params *params, int full)
224{
225 if (params->w <= 0 || params->h <= 0)
226 return "Width and height must both be greater than zero";
227 if (params->w*params->h < 2)
228 return "Grid area must be greater than one";
229 if (params->expandfactor < 0.0F)
230 return "Expansion factor may not be negative";
231 return NULL;
232}
233
234struct point {
235 int x, y;
236};
237
238struct rect {
239 int x, y;
240 int w, h;
241};
242
243struct rectlist {
244 struct rect *rects;
245 int n;
246};
247
248struct numberdata {
249 int area;
250 int npoints;
251 struct point *points;
252};
253
254/* ----------------------------------------------------------------------
255 * Solver for Rectangles games.
256 *
257 * This solver is souped up beyond the needs of actually _solving_
258 * a puzzle. It is also designed to cope with uncertainty about
259 * where the numbers have been placed. This is because I run it on
260 * my generated grids _before_ placing the numbers, and have it
261 * tell me where I need to place the numbers to ensure a unique
262 * solution.
263 */
264
265static void remove_rect_placement(int w, int h,
266 struct rectlist *rectpositions,
267 int *overlaps,
268 int rectnum, int placement)
269{
270 int x, y, xx, yy;
271
272#ifdef SOLVER_DIAGNOSTICS
273 printf("ruling out rect %d placement at %d,%d w=%d h=%d\n", rectnum,
274 rectpositions[rectnum].rects[placement].x,
275 rectpositions[rectnum].rects[placement].y,
276 rectpositions[rectnum].rects[placement].w,
277 rectpositions[rectnum].rects[placement].h);
278#endif
279
280 /*
281 * Decrement each entry in the overlaps array to reflect the
282 * removal of this rectangle placement.
283 */
284 for (yy = 0; yy < rectpositions[rectnum].rects[placement].h; yy++) {
285 y = yy + rectpositions[rectnum].rects[placement].y;
286 for (xx = 0; xx < rectpositions[rectnum].rects[placement].w; xx++) {
287 x = xx + rectpositions[rectnum].rects[placement].x;
288
289 assert(overlaps[(rectnum * h + y) * w + x] != 0);
290
291 if (overlaps[(rectnum * h + y) * w + x] > 0)
292 overlaps[(rectnum * h + y) * w + x]--;
293 }
294 }
295
296 /*
297 * Remove the placement from the list of positions for that
298 * rectangle, by interchanging it with the one on the end.
299 */
300 if (placement < rectpositions[rectnum].n - 1) {
301 struct rect t;
302
303 t = rectpositions[rectnum].rects[rectpositions[rectnum].n - 1];
304 rectpositions[rectnum].rects[rectpositions[rectnum].n - 1] =
305 rectpositions[rectnum].rects[placement];
306 rectpositions[rectnum].rects[placement] = t;
307 }
308 rectpositions[rectnum].n--;
309}
310
311static void remove_number_placement(int w, int h, struct numberdata *number,
312 int index, int *rectbyplace)
313{
314 /*
315 * Remove the entry from the rectbyplace array.
316 */
317 rectbyplace[number->points[index].y * w + number->points[index].x] = -1;
318
319 /*
320 * Remove the placement from the list of candidates for that
321 * number, by interchanging it with the one on the end.
322 */
323 if (index < number->npoints - 1) {
324 struct point t;
325
326 t = number->points[number->npoints - 1];
327 number->points[number->npoints - 1] = number->points[index];
328 number->points[index] = t;
329 }
330 number->npoints--;
331}
332
333/*
334 * Returns 0 for failure to solve due to inconsistency; 1 for
335 * success; 2 for failure to complete a solution due to either
336 * ambiguity or it being too difficult.
337 */
338static int rect_solver(int w, int h, int nrects, struct numberdata *numbers,
339 unsigned char *hedge, unsigned char *vedge,
340 random_state *rs)
341{
342 struct rectlist *rectpositions;
343 int *overlaps, *rectbyplace, *workspace;
344 int i, ret;
345
346 /*
347 * Start by setting up a list of candidate positions for each
348 * rectangle.
349 */
350 rectpositions = snewn(nrects, struct rectlist);
351 for (i = 0; i < nrects; i++) {
352 int rw, rh, area = numbers[i].area;
353 int j, minx, miny, maxx, maxy;
354 struct rect *rlist;
355 int rlistn, rlistsize;
356
357 /*
358 * For each rectangle, begin by finding the bounding
359 * rectangle of its candidate number placements.
360 */
361 maxx = maxy = -1;
362 minx = w;
363 miny = h;
364 for (j = 0; j < numbers[i].npoints; j++) {
365 if (minx > numbers[i].points[j].x) minx = numbers[i].points[j].x;
366 if (miny > numbers[i].points[j].y) miny = numbers[i].points[j].y;
367 if (maxx < numbers[i].points[j].x) maxx = numbers[i].points[j].x;
368 if (maxy < numbers[i].points[j].y) maxy = numbers[i].points[j].y;
369 }
370
371 /*
372 * Now loop over all possible rectangle placements
373 * overlapping a point within that bounding rectangle;
374 * ensure each one actually contains a candidate number
375 * placement, and add it to the list.
376 */
377 rlist = NULL;
378 rlistn = rlistsize = 0;
379
380 for (rw = 1; rw <= area && rw <= w; rw++) {
381 int x, y;
382
383 if (area % rw)
384 continue;
385 rh = area / rw;
386 if (rh > h)
387 continue;
388
389 for (y = miny - rh + 1; y <= maxy; y++) {
390 if (y < 0 || y+rh > h)
391 continue;
392
393 for (x = minx - rw + 1; x <= maxx; x++) {
394 if (x < 0 || x+rw > w)
395 continue;
396
397 /*
398 * See if we can find a candidate number
399 * placement within this rectangle.
400 */
401 for (j = 0; j < numbers[i].npoints; j++)
402 if (numbers[i].points[j].x >= x &&
403 numbers[i].points[j].x < x+rw &&
404 numbers[i].points[j].y >= y &&
405 numbers[i].points[j].y < y+rh)
406 break;
407
408 if (j < numbers[i].npoints) {
409 /*
410 * Add this to the list of candidate
411 * placements for this rectangle.
412 */
413 if (rlistn >= rlistsize) {
414 rlistsize = rlistn + 32;
415 rlist = sresize(rlist, rlistsize, struct rect);
416 }
417 rlist[rlistn].x = x;
418 rlist[rlistn].y = y;
419 rlist[rlistn].w = rw;
420 rlist[rlistn].h = rh;
421#ifdef SOLVER_DIAGNOSTICS
422 printf("rect %d [area %d]: candidate position at"
423 " %d,%d w=%d h=%d\n",
424 i, area, x, y, rw, rh);
425#endif
426 rlistn++;
427 }
428 }
429 }
430 }
431
432 rectpositions[i].rects = rlist;
433 rectpositions[i].n = rlistn;
434 }
435
436 /*
437 * Next, construct a multidimensional array tracking how many
438 * candidate positions for each rectangle overlap each square.
439 *
440 * Indexing of this array is by the formula
441 *
442 * overlaps[(rectindex * h + y) * w + x]
443 *
444 * A positive or zero value indicates what it sounds as if it
445 * should; -1 indicates that this square _cannot_ be part of
446 * this rectangle; and -2 indicates that it _definitely_ is
447 * (which is distinct from 1, because one might very well know
448 * that _if_ square S is part of rectangle R then it must be
449 * because R is placed in a certain position without knowing
450 * that it definitely _is_).
451 */
452 overlaps = snewn(nrects * w * h, int);
453 memset(overlaps, 0, nrects * w * h * sizeof(int));
454 for (i = 0; i < nrects; i++) {
455 int j;
456
457 for (j = 0; j < rectpositions[i].n; j++) {
458 int xx, yy;
459
460 for (yy = 0; yy < rectpositions[i].rects[j].h; yy++)
461 for (xx = 0; xx < rectpositions[i].rects[j].w; xx++)
462 overlaps[(i * h + yy+rectpositions[i].rects[j].y) * w +
463 xx+rectpositions[i].rects[j].x]++;
464 }
465 }
466
467 /*
468 * Also we want an array covering the grid once, to make it
469 * easy to figure out which squares are candidate number
470 * placements for which rectangles. (The existence of this
471 * single array assumes that no square starts off as a
472 * candidate number placement for more than one rectangle. This
473 * assumption is justified, because this solver is _either_
474 * used to solve real problems - in which case there is a
475 * single placement for every number - _or_ used to decide on
476 * number placements for a new puzzle, in which case each
477 * number's placements are confined to the intended position of
478 * the rectangle containing that number.)
479 */
480 rectbyplace = snewn(w * h, int);
481 for (i = 0; i < w*h; i++)
482 rectbyplace[i] = -1;
483
484 for (i = 0; i < nrects; i++) {
485 int j;
486
487 for (j = 0; j < numbers[i].npoints; j++) {
488 int x = numbers[i].points[j].x;
489 int y = numbers[i].points[j].y;
490
491 assert(rectbyplace[y * w + x] == -1);
492 rectbyplace[y * w + x] = i;
493 }
494 }
495
496 workspace = snewn(nrects, int);
497
498 /*
499 * Now run the actual deduction loop.
500 */
501 while (1) {
502 int done_something = FALSE;
503
504#ifdef SOLVER_DIAGNOSTICS
505 printf("starting deduction loop\n");
506
507 for (i = 0; i < nrects; i++) {
508 printf("rect %d overlaps:\n", i);
509 {
510 int x, y;
511 for (y = 0; y < h; y++) {
512 for (x = 0; x < w; x++) {
513 printf("%3d", overlaps[(i * h + y) * w + x]);
514 }
515 printf("\n");
516 }
517 }
518 }
519 printf("rectbyplace:\n");
520 {
521 int x, y;
522 for (y = 0; y < h; y++) {
523 for (x = 0; x < w; x++) {
524 printf("%3d", rectbyplace[y * w + x]);
525 }
526 printf("\n");
527 }
528 }
529#endif
530
531 /*
532 * Housekeeping. Look for rectangles whose number has only
533 * one candidate position left, and mark that square as
534 * known if it isn't already.
535 */
536 for (i = 0; i < nrects; i++) {
537 if (numbers[i].npoints == 1) {
538 int x = numbers[i].points[0].x;
539 int y = numbers[i].points[0].y;
540 if (overlaps[(i * h + y) * w + x] >= -1) {
541 int j;
542
543 if (overlaps[(i * h + y) * w + x] <= 0) {
544 ret = 0; /* inconsistency */
545 goto cleanup;
546 }
547#ifdef SOLVER_DIAGNOSTICS
548 printf("marking %d,%d as known for rect %d"
549 " (sole remaining number position)\n", x, y, i);
550#endif
551
552 for (j = 0; j < nrects; j++)
553 overlaps[(j * h + y) * w + x] = -1;
554
555 overlaps[(i * h + y) * w + x] = -2;
556 }
557 }
558 }
559
560 /*
561 * Now look at the intersection of all possible placements
562 * for each rectangle, and mark all squares in that
563 * intersection as known for that rectangle if they aren't
564 * already.
565 */
566 for (i = 0; i < nrects; i++) {
567 int minx, miny, maxx, maxy, xx, yy, j;
568
569 minx = miny = 0;
570 maxx = w;
571 maxy = h;
572
573 for (j = 0; j < rectpositions[i].n; j++) {
574 int x = rectpositions[i].rects[j].x;
575 int y = rectpositions[i].rects[j].y;
576 int w = rectpositions[i].rects[j].w;
577 int h = rectpositions[i].rects[j].h;
578
579 if (minx < x) minx = x;
580 if (miny < y) miny = y;
581 if (maxx > x+w) maxx = x+w;
582 if (maxy > y+h) maxy = y+h;
583 }
584
585 for (yy = miny; yy < maxy; yy++)
586 for (xx = minx; xx < maxx; xx++)
587 if (overlaps[(i * h + yy) * w + xx] >= -1) {
588 if (overlaps[(i * h + yy) * w + xx] <= 0) {
589 ret = 0; /* inconsistency */
590 goto cleanup;
591 }
592#ifdef SOLVER_DIAGNOSTICS
593 printf("marking %d,%d as known for rect %d"
594 " (intersection of all placements)\n",
595 xx, yy, i);
596#endif
597
598 for (j = 0; j < nrects; j++)
599 overlaps[(j * h + yy) * w + xx] = -1;
600
601 overlaps[(i * h + yy) * w + xx] = -2;
602 }
603 }
604
605 /*
606 * Rectangle-focused deduction. Look at each rectangle in
607 * turn and try to rule out some of its candidate
608 * placements.
609 */
610 for (i = 0; i < nrects; i++) {
611 int j;
612
613 for (j = 0; j < rectpositions[i].n; j++) {
614 int xx, yy, k;
615 int del = FALSE;
616
617 for (k = 0; k < nrects; k++)
618 workspace[k] = 0;
619
620 for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) {
621 int y = yy + rectpositions[i].rects[j].y;
622 for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) {
623 int x = xx + rectpositions[i].rects[j].x;
624
625 if (overlaps[(i * h + y) * w + x] == -1) {
626 /*
627 * This placement overlaps a square
628 * which is _known_ to be part of
629 * another rectangle. Therefore we must
630 * rule it out.
631 */
632#ifdef SOLVER_DIAGNOSTICS
633 printf("rect %d placement at %d,%d w=%d h=%d "
634 "contains %d,%d which is known-other\n", i,
635 rectpositions[i].rects[j].x,
636 rectpositions[i].rects[j].y,
637 rectpositions[i].rects[j].w,
638 rectpositions[i].rects[j].h,
639 x, y);
640#endif
641 del = TRUE;
642 }
643
644 if (rectbyplace[y * w + x] != -1) {
645 /*
646 * This placement overlaps one of the
647 * candidate number placements for some
648 * rectangle. Count it.
649 */
650 workspace[rectbyplace[y * w + x]]++;
651 }
652 }
653 }
654
655 if (!del) {
656 /*
657 * If we haven't ruled this placement out
658 * already, see if it overlaps _all_ of the
659 * candidate number placements for any
660 * rectangle. If so, we can rule it out.
661 */
662 for (k = 0; k < nrects; k++)
663 if (k != i && workspace[k] == numbers[k].npoints) {
664#ifdef SOLVER_DIAGNOSTICS
665 printf("rect %d placement at %d,%d w=%d h=%d "
666 "contains all number points for rect %d\n",
667 i,
668 rectpositions[i].rects[j].x,
669 rectpositions[i].rects[j].y,
670 rectpositions[i].rects[j].w,
671 rectpositions[i].rects[j].h,
672 k);
673#endif
674 del = TRUE;
675 break;
676 }
677
678 /*
679 * Failing that, see if it overlaps at least
680 * one of the candidate number placements for
681 * itself! (This might not be the case if one
682 * of those number placements has been removed
683 * recently.).
684 */
685 if (!del && workspace[i] == 0) {
686#ifdef SOLVER_DIAGNOSTICS
687 printf("rect %d placement at %d,%d w=%d h=%d "
688 "contains none of its own number points\n",
689 i,
690 rectpositions[i].rects[j].x,
691 rectpositions[i].rects[j].y,
692 rectpositions[i].rects[j].w,
693 rectpositions[i].rects[j].h);
694#endif
695 del = TRUE;
696 }
697 }
698
699 if (del) {
700 remove_rect_placement(w, h, rectpositions, overlaps, i, j);
701
702 j--; /* don't skip over next placement */
703
704 done_something = TRUE;
705 }
706 }
707 }
708
709 /*
710 * Square-focused deduction. Look at each square not marked
711 * as known, and see if there are any which can only be
712 * part of a single rectangle.
713 */
714 {
715 int x, y, n, index;
716 for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
717 /* Known squares are marked as <0 everywhere, so we only need
718 * to check the overlaps entry for rect 0. */
719 if (overlaps[y * w + x] < 0)
720 continue; /* known already */
721
722 n = 0;
723 index = -1;
724 for (i = 0; i < nrects; i++)
725 if (overlaps[(i * h + y) * w + x] > 0)
726 n++, index = i;
727
728 if (n == 1) {
729 int j;
730
731 /*
732 * Now we can rule out all placements for
733 * rectangle `index' which _don't_ contain
734 * square x,y.
735 */
736#ifdef SOLVER_DIAGNOSTICS
737 printf("square %d,%d can only be in rectangle %d\n",
738 x, y, index);
739#endif
740 for (j = 0; j < rectpositions[index].n; j++) {
741 struct rect *r = &rectpositions[index].rects[j];
742 if (x >= r->x && x < r->x + r->w &&
743 y >= r->y && y < r->y + r->h)
744 continue; /* this one is OK */
745 remove_rect_placement(w, h, rectpositions, overlaps,
746 index, j);
747 j--; /* don't skip over next placement */
748 done_something = TRUE;
749 }
750 }
751 }
752 }
753
754 /*
755 * If we've managed to deduce anything by normal means,
756 * loop round again and see if there's more to be done.
757 * Only if normal deduction has completely failed us should
758 * we now move on to narrowing down the possible number
759 * placements.
760 */
761 if (done_something)
762 continue;
763
764 /*
765 * Now we have done everything we can with the current set
766 * of number placements. So we need to winnow the number
767 * placements so as to narrow down the possibilities. We do
768 * this by searching for a candidate placement (of _any_
769 * rectangle) which overlaps a candidate placement of the
770 * number for some other rectangle.
771 */
772 if (rs) {
773 struct rpn {
774 int rect;
775 int placement;
776 int number;
777 } *rpns = NULL;
778 size_t nrpns = 0, rpnsize = 0;
779 int j;
780
781 for (i = 0; i < nrects; i++) {
782 for (j = 0; j < rectpositions[i].n; j++) {
783 int xx, yy;
784
785 for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) {
786 int y = yy + rectpositions[i].rects[j].y;
787 for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) {
788 int x = xx + rectpositions[i].rects[j].x;
789
790 if (rectbyplace[y * w + x] >= 0 &&
791 rectbyplace[y * w + x] != i) {
792 /*
793 * Add this to the list of
794 * winnowing possibilities.
795 */
796 if (nrpns >= rpnsize) {
797 rpnsize = rpnsize * 3 / 2 + 32;
798 rpns = sresize(rpns, rpnsize, struct rpn);
799 }
800 rpns[nrpns].rect = i;
801 rpns[nrpns].placement = j;
802 rpns[nrpns].number = rectbyplace[y * w + x];
803 nrpns++;
804 }
805 }
806 }
807
808 }
809 }
810
811#ifdef SOLVER_DIAGNOSTICS
812 printf("%d candidate rect placements we could eliminate\n", nrpns);
813#endif
814 if (nrpns > 0) {
815 /*
816 * Now choose one of these unwanted rectangle
817 * placements, and eliminate it.
818 */
819 int index = random_upto(rs, nrpns);
820 int k, m;
821 struct rpn rpn = rpns[index];
822 struct rect r;
823 sfree(rpns);
824
825 i = rpn.rect;
826 j = rpn.placement;
827 k = rpn.number;
828 r = rectpositions[i].rects[j];
829
830 /*
831 * We rule out placement j of rectangle i by means
832 * of removing all of rectangle k's candidate
833 * number placements which do _not_ overlap it.
834 * This will ensure that it is eliminated during
835 * the next pass of rectangle-focused deduction.
836 */
837#ifdef SOLVER_DIAGNOSTICS
838 printf("ensuring number for rect %d is within"
839 " rect %d's placement at %d,%d w=%d h=%d\n",
840 k, i, r.x, r.y, r.w, r.h);
841#endif
842
843 for (m = 0; m < numbers[k].npoints; m++) {
844 int x = numbers[k].points[m].x;
845 int y = numbers[k].points[m].y;
846
847 if (x < r.x || x >= r.x + r.w ||
848 y < r.y || y >= r.y + r.h) {
849#ifdef SOLVER_DIAGNOSTICS
850 printf("eliminating number for rect %d at %d,%d\n",
851 k, x, y);
852#endif
853 remove_number_placement(w, h, &numbers[k],
854 m, rectbyplace);
855 m--; /* don't skip the next one */
856 done_something = TRUE;
857 }
858 }
859 }
860 }
861
862 if (!done_something) {
863#ifdef SOLVER_DIAGNOSTICS
864 printf("terminating deduction loop\n");
865#endif
866 break;
867 }
868 }
869
870 cleanup:
871 ret = 1;
872 for (i = 0; i < nrects; i++) {
873#ifdef SOLVER_DIAGNOSTICS
874 printf("rect %d has %d possible placements\n",
875 i, rectpositions[i].n);
876#endif
877 if (rectpositions[i].n <= 0) {
878 ret = 0; /* inconsistency */
879 } else if (rectpositions[i].n > 1) {
880 ret = 2; /* remaining uncertainty */
881 } else if (hedge && vedge) {
882 /*
883 * Place the rectangle in its only possible position.
884 */
885 int x, y;
886 struct rect *r = &rectpositions[i].rects[0];
887
888 for (y = 0; y < r->h; y++) {
889 if (r->x > 0)
890 vedge[(r->y+y) * w + r->x] = 1;
891 if (r->x+r->w < w)
892 vedge[(r->y+y) * w + r->x+r->w] = 1;
893 }
894 for (x = 0; x < r->w; x++) {
895 if (r->y > 0)
896 hedge[r->y * w + r->x+x] = 1;
897 if (r->y+r->h < h)
898 hedge[(r->y+r->h) * w + r->x+x] = 1;
899 }
900 }
901 }
902
903 /*
904 * Free up all allocated storage.
905 */
906 sfree(workspace);
907 sfree(rectbyplace);
908 sfree(overlaps);
909 for (i = 0; i < nrects; i++)
910 sfree(rectpositions[i].rects);
911 sfree(rectpositions);
912
913 return ret;
914}
915
916/* ----------------------------------------------------------------------
917 * Grid generation code.
918 */
919
920/*
921 * This function does one of two things. If passed r==NULL, it
922 * counts the number of possible rectangles which cover the given
923 * square, and returns it in *n. If passed r!=NULL then it _reads_
924 * *n to find an index, counts the possible rectangles until it
925 * reaches the nth, and writes it into r.
926 *
927 * `scratch' is expected to point to an array of 2 * params->w
928 * ints, used internally as scratch space (and passed in like this
929 * to avoid re-allocating and re-freeing it every time round a
930 * tight loop).
931 */
932static void enum_rects(game_params *params, int *grid, struct rect *r, int *n,
933 int sx, int sy, int *scratch)
934{
935 int rw, rh, mw, mh;
936 int x, y, dx, dy;
937 int maxarea, realmaxarea;
938 int index = 0;
939 int *top, *bottom;
940
941 /*
942 * Maximum rectangle area is 1/6 of total grid size, unless
943 * this means we can't place any rectangles at all in which
944 * case we set it to 2 at minimum.
945 */
946 maxarea = params->w * params->h / 6;
947 if (maxarea < 2)
948 maxarea = 2;
949
950 /*
951 * Scan the grid to find the limits of the region within which
952 * any rectangle containing this point must fall. This will
953 * save us trawling the inside of every rectangle later on to
954 * see if it contains any used squares.
955 */
956 top = scratch;
957 bottom = scratch + params->w;
958 for (dy = -1; dy <= +1; dy += 2) {
959 int *array = (dy == -1 ? top : bottom);
960 for (dx = -1; dx <= +1; dx += 2) {
961 for (x = sx; x >= 0 && x < params->w; x += dx) {
962 array[x] = -2 * params->h * dy;
963 for (y = sy; y >= 0 && y < params->h; y += dy) {
964 if (index(params, grid, x, y) == -1 &&
965 (x == sx || dy*y <= dy*array[x-dx]))
966 array[x] = y;
967 else
968 break;
969 }
970 }
971 }
972 }
973
974 /*
975 * Now scan again to work out the largest rectangles we can fit
976 * in the grid, so that we can terminate the following loops
977 * early once we get down to not having much space left in the
978 * grid.
979 */
980 realmaxarea = 0;
981 for (x = 0; x < params->w; x++) {
982 int x2;
983
984 rh = bottom[x] - top[x] + 1;
985 if (rh <= 0)
986 continue; /* no rectangles can start here */
987
988 dx = (x > sx ? -1 : +1);
989 for (x2 = x; x2 >= 0 && x2 < params->w; x2 += dx)
990 if (bottom[x2] < bottom[x] || top[x2] > top[x])
991 break;
992
993 rw = abs(x2 - x);
994 if (realmaxarea < rw * rh)
995 realmaxarea = rw * rh;
996 }
997
998 if (realmaxarea > maxarea)
999 realmaxarea = maxarea;
1000
1001 /*
1002 * Rectangles which go right the way across the grid are
1003 * boring, although they can't be helped in the case of
1004 * extremely small grids. (Also they might be generated later
1005 * on by the singleton-removal process; we can't help that.)
1006 */
1007 mw = params->w - 1;
1008 if (mw < 3) mw++;
1009 mh = params->h - 1;
1010 if (mh < 3) mh++;
1011
1012 for (rw = 1; rw <= mw; rw++)
1013 for (rh = 1; rh <= mh; rh++) {
1014 if (rw * rh > realmaxarea)
1015 continue;
1016 if (rw * rh == 1)
1017 continue;
1018 for (x = max(sx - rw + 1, 0); x <= min(sx, params->w - rw); x++)
1019 for (y = max(sy - rh + 1, 0); y <= min(sy, params->h - rh);
1020 y++) {
1021 /*
1022 * Check this rectangle against the region we
1023 * defined above.
1024 */
1025 if (top[x] <= y && top[x+rw-1] <= y &&
1026 bottom[x] >= y+rh-1 && bottom[x+rw-1] >= y+rh-1) {
1027 if (r && index == *n) {
1028 r->x = x;
1029 r->y = y;
1030 r->w = rw;
1031 r->h = rh;
1032 return;
1033 }
1034 index++;
1035 }
1036 }
1037 }
1038
1039 assert(!r);
1040 *n = index;
1041}
1042
1043static void place_rect(game_params *params, int *grid, struct rect r)
1044{
1045 int idx = INDEX(params, r.x, r.y);
1046 int x, y;
1047
1048 for (x = r.x; x < r.x+r.w; x++)
1049 for (y = r.y; y < r.y+r.h; y++) {
1050 index(params, grid, x, y) = idx;
1051 }
1052#ifdef GENERATION_DIAGNOSTICS
1053 printf(" placing rectangle at (%d,%d) size %d x %d\n",
1054 r.x, r.y, r.w, r.h);
1055#endif
1056}
1057
1058static struct rect find_rect(game_params *params, int *grid, int x, int y)
1059{
1060 int idx, w, h;
1061 struct rect r;
1062
1063 /*
1064 * Find the top left of the rectangle.
1065 */
1066 idx = index(params, grid, x, y);
1067
1068 if (idx < 0) {
1069 r.x = x;
1070 r.y = y;
1071 r.w = r.h = 1;
1072 return r; /* 1x1 singleton here */
1073 }
1074
1075 y = idx / params->w;
1076 x = idx % params->w;
1077
1078 /*
1079 * Find the width and height of the rectangle.
1080 */
1081 for (w = 1;
1082 (x+w < params->w && index(params,grid,x+w,y)==idx);
1083 w++);
1084 for (h = 1;
1085 (y+h < params->h && index(params,grid,x,y+h)==idx);
1086 h++);
1087
1088 r.x = x;
1089 r.y = y;
1090 r.w = w;
1091 r.h = h;
1092
1093 return r;
1094}
1095
1096#ifdef GENERATION_DIAGNOSTICS
1097static void display_grid(game_params *params, int *grid, int *numbers, int all)
1098{
1099 unsigned char *egrid = snewn((params->w*2+3) * (params->h*2+3),
1100 unsigned char);
1101 int x, y;
1102 int r = (params->w*2+3);
1103
1104 memset(egrid, 0, (params->w*2+3) * (params->h*2+3));
1105
1106 for (x = 0; x < params->w; x++)
1107 for (y = 0; y < params->h; y++) {
1108 int i = index(params, grid, x, y);
1109 if (x == 0 || index(params, grid, x-1, y) != i)
1110 egrid[(2*y+2) * r + (2*x+1)] = 1;
1111 if (x == params->w-1 || index(params, grid, x+1, y) != i)
1112 egrid[(2*y+2) * r + (2*x+3)] = 1;
1113 if (y == 0 || index(params, grid, x, y-1) != i)
1114 egrid[(2*y+1) * r + (2*x+2)] = 1;
1115 if (y == params->h-1 || index(params, grid, x, y+1) != i)
1116 egrid[(2*y+3) * r + (2*x+2)] = 1;
1117 }
1118
1119 for (y = 1; y < 2*params->h+2; y++) {
1120 for (x = 1; x < 2*params->w+2; x++) {
1121 if (!((y|x)&1)) {
1122 int k = numbers ? index(params, numbers, x/2-1, y/2-1) : 0;
1123 if (k || (all && numbers)) printf("%2d", k); else printf(" ");
1124 } else if (!((y&x)&1)) {
1125 int v = egrid[y*r+x];
1126 if ((y&1) && v) v = '-';
1127 if ((x&1) && v) v = '|';
1128 if (!v) v = ' ';
1129 putchar(v);
1130 if (!(x&1)) putchar(v);
1131 } else {
1132 int c, d = 0;
1133 if (egrid[y*r+(x+1)]) d |= 1;
1134 if (egrid[(y-1)*r+x]) d |= 2;
1135 if (egrid[y*r+(x-1)]) d |= 4;
1136 if (egrid[(y+1)*r+x]) d |= 8;
1137 c = " ??+?-++?+|+++++"[d];
1138 putchar(c);
1139 if (!(x&1)) putchar(c);
1140 }
1141 }
1142 putchar('\n');
1143 }
1144
1145 sfree(egrid);
1146}
1147#endif
1148
1149static char *new_game_desc(const game_params *params_in, random_state *rs,
1150 char **aux, int interactive)
1151{
1152 game_params params_copy = *params_in; /* structure copy */
1153 game_params *params = &params_copy;
1154 int *grid, *numbers = NULL;
1155 int x, y, y2, y2last, yx, run, i, nsquares;
1156 char *desc, *p;
1157 int *enum_rects_scratch;
1158 game_params params2real, *params2 = &params2real;
1159
1160 while (1) {
1161 /*
1162 * Set up the smaller width and height which we will use to
1163 * generate the base grid.
1164 */
1165 params2->w = (int)((float)params->w / (1.0F + params->expandfactor));
1166 if (params2->w < 2 && params->w >= 2) params2->w = 2;
1167 params2->h = (int)((float)params->h / (1.0F + params->expandfactor));
1168 if (params2->h < 2 && params->h >= 2) params2->h = 2;
1169
1170 grid = snewn(params2->w * params2->h, int);
1171
1172 enum_rects_scratch = snewn(2 * params2->w, int);
1173
1174 nsquares = 0;
1175 for (y = 0; y < params2->h; y++)
1176 for (x = 0; x < params2->w; x++) {
1177 index(params2, grid, x, y) = -1;
1178 nsquares++;
1179 }
1180
1181 /*
1182 * Place rectangles until we can't any more. We do this by
1183 * finding a square we haven't yet covered, and randomly
1184 * choosing a rectangle to cover it.
1185 */
1186
1187 while (nsquares > 0) {
1188 int square = random_upto(rs, nsquares);
1189 int n;
1190 struct rect r;
1191
1192 x = params2->w;
1193 y = params2->h;
1194 for (y = 0; y < params2->h; y++) {
1195 for (x = 0; x < params2->w; x++) {
1196 if (index(params2, grid, x, y) == -1 && square-- == 0)
1197 break;
1198 }
1199 if (x < params2->w)
1200 break;
1201 }
1202 assert(x < params2->w && y < params2->h);
1203
1204 /*
1205 * Now see how many rectangles fit around this one.
1206 */
1207 enum_rects(params2, grid, NULL, &n, x, y, enum_rects_scratch);
1208
1209 if (!n) {
1210 /*
1211 * There are no possible rectangles covering this
1212 * square, meaning it must be a singleton. Mark it
1213 * -2 so we know not to keep trying.
1214 */
1215 index(params2, grid, x, y) = -2;
1216 nsquares--;
1217 } else {
1218 /*
1219 * Pick one at random.
1220 */
1221 n = random_upto(rs, n);
1222 enum_rects(params2, grid, &r, &n, x, y, enum_rects_scratch);
1223
1224 /*
1225 * Place it.
1226 */
1227 place_rect(params2, grid, r);
1228 nsquares -= r.w * r.h;
1229 }
1230 }
1231
1232 sfree(enum_rects_scratch);
1233
1234 /*
1235 * Deal with singleton spaces remaining in the grid, one by
1236 * one.
1237 *
1238 * We do this by making a local change to the layout. There are
1239 * several possibilities:
1240 *
1241 * +-----+-----+ Here, we can remove the singleton by
1242 * | | | extending the 1x2 rectangle below it
1243 * +--+--+-----+ into a 1x3.
1244 * | | | |
1245 * | +--+ |
1246 * | | | |
1247 * | | | |
1248 * | | | |
1249 * +--+--+-----+
1250 *
1251 * +--+--+--+ Here, that trick doesn't work: there's no
1252 * | | | 1 x n rectangle with the singleton at one
1253 * | | | end. Instead, we extend a 1 x n rectangle
1254 * | | | _out_ from the singleton, shaving a layer
1255 * +--+--+ | off the end of another rectangle. So if we
1256 * | | | | extended up, we'd make our singleton part
1257 * | +--+--+ of a 1x3 and generate a 1x2 where the 2x2
1258 * | | | used to be; or we could extend right into
1259 * +--+-----+ a 2x1, turning the 1x3 into a 1x2.
1260 *
1261 * +-----+--+ Here, we can't even do _that_, since any
1262 * | | | direction we choose to extend the singleton
1263 * +--+--+ | will produce a new singleton as a result of
1264 * | | | | truncating one of the size-2 rectangles.
1265 * | +--+--+ Fortunately, this case can _only_ occur when
1266 * | | | a singleton is surrounded by four size-2s
1267 * +--+-----+ in this fashion; so instead we can simply
1268 * replace the whole section with a single 3x3.
1269 */
1270 for (x = 0; x < params2->w; x++) {
1271 for (y = 0; y < params2->h; y++) {
1272 if (index(params2, grid, x, y) < 0) {
1273 int dirs[4], ndirs;
1274
1275#ifdef GENERATION_DIAGNOSTICS
1276 display_grid(params2, grid, NULL, FALSE);
1277 printf("singleton at %d,%d\n", x, y);
1278#endif
1279
1280 /*
1281 * Check in which directions we can feasibly extend
1282 * the singleton. We can extend in a particular
1283 * direction iff either:
1284 *
1285 * - the rectangle on that side of the singleton
1286 * is not 2x1, and we are at one end of the edge
1287 * of it we are touching
1288 *
1289 * - it is 2x1 but we are on its short side.
1290 *
1291 * FIXME: we could plausibly choose between these
1292 * based on the sizes of the rectangles they would
1293 * create?
1294 */
1295 ndirs = 0;
1296 if (x < params2->w-1) {
1297 struct rect r = find_rect(params2, grid, x+1, y);
1298 if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1)
1299 dirs[ndirs++] = 1; /* right */
1300 }
1301 if (y > 0) {
1302 struct rect r = find_rect(params2, grid, x, y-1);
1303 if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1)
1304 dirs[ndirs++] = 2; /* up */
1305 }
1306 if (x > 0) {
1307 struct rect r = find_rect(params2, grid, x-1, y);
1308 if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1)
1309 dirs[ndirs++] = 4; /* left */
1310 }
1311 if (y < params2->h-1) {
1312 struct rect r = find_rect(params2, grid, x, y+1);
1313 if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1)
1314 dirs[ndirs++] = 8; /* down */
1315 }
1316
1317 if (ndirs > 0) {
1318 int which, dir;
1319 struct rect r1, r2;
1320 memset(&r1, 0, sizeof(struct rect));
1321 memset(&r2, 0, sizeof(struct rect));
1322 which = random_upto(rs, ndirs);
1323 dir = dirs[which];
1324
1325 switch (dir) {
1326 case 1: /* right */
1327 assert(x < params2->w+1);
1328#ifdef GENERATION_DIAGNOSTICS
1329 printf("extending right\n");
1330#endif
1331 r1 = find_rect(params2, grid, x+1, y);
1332 r2.x = x;
1333 r2.y = y;
1334 r2.w = 1 + r1.w;
1335 r2.h = 1;
1336 if (r1.y == y)
1337 r1.y++;
1338 r1.h--;
1339 break;
1340 case 2: /* up */
1341 assert(y > 0);
1342#ifdef GENERATION_DIAGNOSTICS
1343 printf("extending up\n");
1344#endif
1345 r1 = find_rect(params2, grid, x, y-1);
1346 r2.x = x;
1347 r2.y = r1.y;
1348 r2.w = 1;
1349 r2.h = 1 + r1.h;
1350 if (r1.x == x)
1351 r1.x++;
1352 r1.w--;
1353 break;
1354 case 4: /* left */
1355 assert(x > 0);
1356#ifdef GENERATION_DIAGNOSTICS
1357 printf("extending left\n");
1358#endif
1359 r1 = find_rect(params2, grid, x-1, y);
1360 r2.x = r1.x;
1361 r2.y = y;
1362 r2.w = 1 + r1.w;
1363 r2.h = 1;
1364 if (r1.y == y)
1365 r1.y++;
1366 r1.h--;
1367 break;
1368 case 8: /* down */
1369 assert(y < params2->h+1);
1370#ifdef GENERATION_DIAGNOSTICS
1371 printf("extending down\n");
1372#endif
1373 r1 = find_rect(params2, grid, x, y+1);
1374 r2.x = x;
1375 r2.y = y;
1376 r2.w = 1;
1377 r2.h = 1 + r1.h;
1378 if (r1.x == x)
1379 r1.x++;
1380 r1.w--;
1381 break;
1382 default: /* should never happen */
1383 assert(!"invalid direction");
1384 }
1385 if (r1.h > 0 && r1.w > 0)
1386 place_rect(params2, grid, r1);
1387 place_rect(params2, grid, r2);
1388 } else {
1389#ifndef NDEBUG
1390 /*
1391 * Sanity-check that there really is a 3x3
1392 * rectangle surrounding this singleton and it
1393 * contains absolutely everything we could
1394 * possibly need.
1395 */
1396 {
1397 int xx, yy;
1398 assert(x > 0 && x < params2->w-1);
1399 assert(y > 0 && y < params2->h-1);
1400
1401 for (xx = x-1; xx <= x+1; xx++)
1402 for (yy = y-1; yy <= y+1; yy++) {
1403 struct rect r = find_rect(params2,grid,xx,yy);
1404 assert(r.x >= x-1);
1405 assert(r.y >= y-1);
1406 assert(r.x+r.w-1 <= x+1);
1407 assert(r.y+r.h-1 <= y+1);
1408 }
1409 }
1410#endif
1411
1412#ifdef GENERATION_DIAGNOSTICS
1413 printf("need the 3x3 trick\n");
1414#endif
1415
1416 /*
1417 * FIXME: If the maximum rectangle area for
1418 * this grid is less than 9, we ought to
1419 * subdivide the 3x3 in some fashion. There are
1420 * five other possibilities:
1421 *
1422 * - a 6 and a 3
1423 * - a 4, a 3 and a 2
1424 * - three 3s
1425 * - a 3 and three 2s (two different arrangements).
1426 */
1427
1428 {
1429 struct rect r;
1430 r.x = x-1;
1431 r.y = y-1;
1432 r.w = r.h = 3;
1433 place_rect(params2, grid, r);
1434 }
1435 }
1436 }
1437 }
1438 }
1439
1440 /*
1441 * We have now constructed a grid of the size specified in
1442 * params2. Now we extend it into a grid of the size specified
1443 * in params. We do this in two passes: we extend it vertically
1444 * until it's the right height, then we transpose it, then
1445 * extend it vertically again (getting it effectively the right
1446 * width), then finally transpose again.
1447 */
1448 for (i = 0; i < 2; i++) {
1449 int *grid2, *expand, *where;
1450 game_params params3real, *params3 = &params3real;
1451
1452#ifdef GENERATION_DIAGNOSTICS
1453 printf("before expansion:\n");
1454 display_grid(params2, grid, NULL, TRUE);
1455#endif
1456
1457 /*
1458 * Set up the new grid.
1459 */
1460 grid2 = snewn(params2->w * params->h, int);
1461 expand = snewn(params2->h-1, int);
1462 where = snewn(params2->w, int);
1463 params3->w = params2->w;
1464 params3->h = params->h;
1465
1466 /*
1467 * Decide which horizontal edges are going to get expanded,
1468 * and by how much.
1469 */
1470 for (y = 0; y < params2->h-1; y++)
1471 expand[y] = 0;
1472 for (y = params2->h; y < params->h; y++) {
1473 x = random_upto(rs, params2->h-1);
1474 expand[x]++;
1475 }
1476
1477#ifdef GENERATION_DIAGNOSTICS
1478 printf("expand[] = {");
1479 for (y = 0; y < params2->h-1; y++)
1480 printf(" %d", expand[y]);
1481 printf(" }\n");
1482#endif
1483
1484 /*
1485 * Perform the expansion. The way this works is that we
1486 * alternately:
1487 *
1488 * - copy a row from grid into grid2
1489 *
1490 * - invent some number of additional rows in grid2 where
1491 * there was previously only a horizontal line between
1492 * rows in grid, and make random decisions about where
1493 * among these to place each rectangle edge that ran
1494 * along this line.
1495 */
1496 for (y = y2 = y2last = 0; y < params2->h; y++) {
1497 /*
1498 * Copy a single line from row y of grid into row y2 of
1499 * grid2.
1500 */
1501 for (x = 0; x < params2->w; x++) {
1502 int val = index(params2, grid, x, y);
1503 if (val / params2->w == y && /* rect starts on this line */
1504 (y2 == 0 || /* we're at the very top, or... */
1505 index(params3, grid2, x, y2-1) / params3->w < y2last
1506 /* this rect isn't already started */))
1507 index(params3, grid2, x, y2) =
1508 INDEX(params3, val % params2->w, y2);
1509 else
1510 index(params3, grid2, x, y2) =
1511 index(params3, grid2, x, y2-1);
1512 }
1513
1514 /*
1515 * If that was the last line, terminate the loop early.
1516 */
1517 if (++y2 == params3->h)
1518 break;
1519
1520 y2last = y2;
1521
1522 /*
1523 * Invent some number of additional lines. First walk
1524 * along this line working out where to put all the
1525 * edges that coincide with it.
1526 */
1527 yx = -1;
1528 for (x = 0; x < params2->w; x++) {
1529 if (index(params2, grid, x, y) !=
1530 index(params2, grid, x, y+1)) {
1531 /*
1532 * This is a horizontal edge, so it needs
1533 * placing.
1534 */
1535 if (x == 0 ||
1536 (index(params2, grid, x-1, y) !=
1537 index(params2, grid, x, y) &&
1538 index(params2, grid, x-1, y+1) !=
1539 index(params2, grid, x, y+1))) {
1540 /*
1541 * Here we have the chance to make a new
1542 * decision.
1543 */
1544 yx = random_upto(rs, expand[y]+1);
1545 } else {
1546 /*
1547 * Here we just reuse the previous value of
1548 * yx.
1549 */
1550 }
1551 } else
1552 yx = -1;
1553 where[x] = yx;
1554 }
1555
1556 for (yx = 0; yx < expand[y]; yx++) {
1557 /*
1558 * Invent a single row. For each square in the row,
1559 * we copy the grid entry from the square above it,
1560 * unless we're starting the new rectangle here.
1561 */
1562 for (x = 0; x < params2->w; x++) {
1563 if (yx == where[x]) {
1564 int val = index(params2, grid, x, y+1);
1565 val %= params2->w;
1566 val = INDEX(params3, val, y2);
1567 index(params3, grid2, x, y2) = val;
1568 } else
1569 index(params3, grid2, x, y2) =
1570 index(params3, grid2, x, y2-1);
1571 }
1572
1573 y2++;
1574 }
1575 }
1576
1577 sfree(expand);
1578 sfree(where);
1579
1580#ifdef GENERATION_DIAGNOSTICS
1581 printf("after expansion:\n");
1582 display_grid(params3, grid2, NULL, TRUE);
1583#endif
1584 /*
1585 * Transpose.
1586 */
1587 params2->w = params3->h;
1588 params2->h = params3->w;
1589 sfree(grid);
1590 grid = snewn(params2->w * params2->h, int);
1591 for (x = 0; x < params2->w; x++)
1592 for (y = 0; y < params2->h; y++) {
1593 int idx1 = INDEX(params2, x, y);
1594 int idx2 = INDEX(params3, y, x);
1595 int tmp;
1596
1597 tmp = grid2[idx2];
1598 tmp = (tmp % params3->w) * params2->w + (tmp / params3->w);
1599 grid[idx1] = tmp;
1600 }
1601
1602 sfree(grid2);
1603
1604 {
1605 int tmp;
1606 tmp = params->w;
1607 params->w = params->h;
1608 params->h = tmp;
1609 }
1610
1611#ifdef GENERATION_DIAGNOSTICS
1612 printf("after transposition:\n");
1613 display_grid(params2, grid, NULL, TRUE);
1614#endif
1615 }
1616
1617 /*
1618 * Run the solver to narrow down the possible number
1619 * placements.
1620 */
1621 {
1622 struct numberdata *nd;
1623 int nnumbers, i, ret;
1624
1625 /* Count the rectangles. */
1626 nnumbers = 0;
1627 for (y = 0; y < params->h; y++) {
1628 for (x = 0; x < params->w; x++) {
1629 int idx = INDEX(params, x, y);
1630 if (index(params, grid, x, y) == idx)
1631 nnumbers++;
1632 }
1633 }
1634
1635 nd = snewn(nnumbers, struct numberdata);
1636
1637 /* Now set up each number's candidate position list. */
1638 i = 0;
1639 for (y = 0; y < params->h; y++) {
1640 for (x = 0; x < params->w; x++) {
1641 int idx = INDEX(params, x, y);
1642 if (index(params, grid, x, y) == idx) {
1643 struct rect r = find_rect(params, grid, x, y);
1644 int j, k, m;
1645
1646 nd[i].area = r.w * r.h;
1647 nd[i].npoints = nd[i].area;
1648 nd[i].points = snewn(nd[i].npoints, struct point);
1649 m = 0;
1650 for (j = 0; j < r.h; j++)
1651 for (k = 0; k < r.w; k++) {
1652 nd[i].points[m].x = k + r.x;
1653 nd[i].points[m].y = j + r.y;
1654 m++;
1655 }
1656 assert(m == nd[i].npoints);
1657
1658 i++;
1659 }
1660 }
1661 }
1662
1663 if (params->unique)
1664 ret = rect_solver(params->w, params->h, nnumbers, nd,
1665 NULL, NULL, rs);
1666 else
1667 ret = 1; /* allow any number placement at all */
1668
1669 if (ret == 1) {
1670 /*
1671 * Now place the numbers according to the solver's
1672 * recommendations.
1673 */
1674 numbers = snewn(params->w * params->h, int);
1675
1676 for (y = 0; y < params->h; y++)
1677 for (x = 0; x < params->w; x++) {
1678 index(params, numbers, x, y) = 0;
1679 }
1680
1681 for (i = 0; i < nnumbers; i++) {
1682 int idx = random_upto(rs, nd[i].npoints);
1683 int x = nd[i].points[idx].x;
1684 int y = nd[i].points[idx].y;
1685 index(params,numbers,x,y) = nd[i].area;
1686 }
1687 }
1688
1689 /*
1690 * Clean up.
1691 */
1692 for (i = 0; i < nnumbers; i++)
1693 sfree(nd[i].points);
1694 sfree(nd);
1695
1696 /*
1697 * If we've succeeded, then terminate the loop.
1698 */
1699 if (ret == 1)
1700 break;
1701 }
1702
1703 /*
1704 * Give up and go round again.
1705 */
1706 sfree(grid);
1707 }
1708
1709 /*
1710 * Store the solution in aux.
1711 */
1712 {
1713 char *ai;
1714 int len;
1715
1716 len = 2 + (params->w-1)*params->h + (params->h-1)*params->w;
1717 ai = snewn(len, char);
1718
1719 ai[0] = 'S';
1720
1721 p = ai+1;
1722
1723 for (y = 0; y < params->h; y++)
1724 for (x = 1; x < params->w; x++)
1725 *p++ = (index(params, grid, x, y) !=
1726 index(params, grid, x-1, y) ? '1' : '0');
1727
1728 for (y = 1; y < params->h; y++)
1729 for (x = 0; x < params->w; x++)
1730 *p++ = (index(params, grid, x, y) !=
1731 index(params, grid, x, y-1) ? '1' : '0');
1732
1733 assert(p - ai == len-1);
1734 *p = '\0';
1735
1736 *aux = ai;
1737 }
1738
1739#ifdef GENERATION_DIAGNOSTICS
1740 display_grid(params, grid, numbers, FALSE);
1741#endif
1742
1743 desc = snewn(11 * params->w * params->h, char);
1744 p = desc;
1745 run = 0;
1746 for (i = 0; i <= params->w * params->h; i++) {
1747 int n = (i < params->w * params->h ? numbers[i] : -1);
1748
1749 if (!n)
1750 run++;
1751 else {
1752 if (run) {
1753 while (run > 0) {
1754 int c = 'a' - 1 + run;
1755 if (run > 26)
1756 c = 'z';
1757 *p++ = c;
1758 run -= c - ('a' - 1);
1759 }
1760 } else {
1761 /*
1762 * If there's a number in the very top left or
1763 * bottom right, there's no point putting an
1764 * unnecessary _ before or after it.
1765 */
1766 if (p > desc && n > 0)
1767 *p++ = '_';
1768 }
1769 if (n > 0)
1770 p += sprintf(p, "%d", n);
1771 run = 0;
1772 }
1773 }
1774 *p = '\0';
1775
1776 sfree(grid);
1777 sfree(numbers);
1778
1779 return desc;
1780}
1781
1782static char *validate_desc(const game_params *params, const char *desc)
1783{
1784 int area = params->w * params->h;
1785 int squares = 0;
1786
1787 while (*desc) {
1788 int n = *desc++;
1789 if (n >= 'a' && n <= 'z') {
1790 squares += n - 'a' + 1;
1791 } else if (n == '_') {
1792 /* do nothing */;
1793 } else if (n > '0' && n <= '9') {
1794 squares++;
1795 while (*desc >= '0' && *desc <= '9')
1796 desc++;
1797 } else
1798 return "Invalid character in game description";
1799 }
1800
1801 if (squares < area)
1802 return "Not enough data to fill grid";
1803
1804 if (squares > area)
1805 return "Too much data to fit in grid";
1806
1807 return NULL;
1808}
1809
1810static unsigned char *get_correct(game_state *state)
1811{
1812 unsigned char *ret;
1813 int x, y;
1814
1815 ret = snewn(state->w * state->h, unsigned char);
1816 memset(ret, 0xFF, state->w * state->h);
1817
1818 for (x = 0; x < state->w; x++)
1819 for (y = 0; y < state->h; y++)
1820 if (index(state,ret,x,y) == 0xFF) {
1821 int rw, rh;
1822 int xx, yy;
1823 int num, area, valid;
1824
1825 /*
1826 * Find a rectangle starting at this point.
1827 */
1828 rw = 1;
1829 while (x+rw < state->w && !vedge(state,x+rw,y))
1830 rw++;
1831 rh = 1;
1832 while (y+rh < state->h && !hedge(state,x,y+rh))
1833 rh++;
1834
1835 /*
1836 * We know what the dimensions of the rectangle
1837 * should be if it's there at all. Find out if we
1838 * really have a valid rectangle.
1839 */
1840 valid = TRUE;
1841 /* Check the horizontal edges. */
1842 for (xx = x; xx < x+rw; xx++) {
1843 for (yy = y; yy <= y+rh; yy++) {
1844 int e = !HRANGE(state,xx,yy) || hedge(state,xx,yy);
1845 int ec = (yy == y || yy == y+rh);
1846 if (e != ec)
1847 valid = FALSE;
1848 }
1849 }
1850 /* Check the vertical edges. */
1851 for (yy = y; yy < y+rh; yy++) {
1852 for (xx = x; xx <= x+rw; xx++) {
1853 int e = !VRANGE(state,xx,yy) || vedge(state,xx,yy);
1854 int ec = (xx == x || xx == x+rw);
1855 if (e != ec)
1856 valid = FALSE;
1857 }
1858 }
1859
1860 /*
1861 * If this is not a valid rectangle with no other
1862 * edges inside it, we just mark this square as not
1863 * complete and proceed to the next square.
1864 */
1865 if (!valid) {
1866 index(state, ret, x, y) = 0;
1867 continue;
1868 }
1869
1870 /*
1871 * We have a rectangle. Now see what its area is,
1872 * and how many numbers are in it.
1873 */
1874 num = 0;
1875 area = 0;
1876 for (xx = x; xx < x+rw; xx++) {
1877 for (yy = y; yy < y+rh; yy++) {
1878 area++;
1879 if (grid(state,xx,yy)) {
1880 if (num > 0)
1881 valid = FALSE; /* two numbers */
1882 num = grid(state,xx,yy);
1883 }
1884 }
1885 }
1886 if (num != area)
1887 valid = FALSE;
1888
1889 /*
1890 * Now fill in the whole rectangle based on the
1891 * value of `valid'.
1892 */
1893 for (xx = x; xx < x+rw; xx++) {
1894 for (yy = y; yy < y+rh; yy++) {
1895 index(state, ret, xx, yy) = valid;
1896 }
1897 }
1898 }
1899
1900 return ret;
1901}
1902
1903static game_state *new_game(midend *me, const game_params *params,
1904 const char *desc)
1905{
1906 game_state *state = snew(game_state);
1907 int x, y, i, area;
1908
1909 state->w = params->w;
1910 state->h = params->h;
1911
1912 area = state->w * state->h;
1913
1914 state->grid = snewn(area, int);
1915 state->vedge = snewn(area, unsigned char);
1916 state->hedge = snewn(area, unsigned char);
1917 state->completed = state->cheated = FALSE;
1918
1919 i = 0;
1920 while (*desc) {
1921 int n = *desc++;
1922 if (n >= 'a' && n <= 'z') {
1923 int run = n - 'a' + 1;
1924 assert(i + run <= area);
1925 while (run-- > 0)
1926 state->grid[i++] = 0;
1927 } else if (n == '_') {
1928 /* do nothing */;
1929 } else if (n > '0' && n <= '9') {
1930 assert(i < area);
1931 state->grid[i++] = atoi(desc-1);
1932 while (*desc >= '0' && *desc <= '9')
1933 desc++;
1934 } else {
1935 assert(!"We can't get here");
1936 }
1937 }
1938 assert(i == area);
1939
1940 for (y = 0; y < state->h; y++)
1941 for (x = 0; x < state->w; x++)
1942 vedge(state,x,y) = hedge(state,x,y) = 0;
1943
1944 state->correct = get_correct(state);
1945
1946 return state;
1947}
1948
1949static game_state *dup_game(const game_state *state)
1950{
1951 game_state *ret = snew(game_state);
1952
1953 ret->w = state->w;
1954 ret->h = state->h;
1955
1956 ret->vedge = snewn(state->w * state->h, unsigned char);
1957 ret->hedge = snewn(state->w * state->h, unsigned char);
1958 ret->grid = snewn(state->w * state->h, int);
1959 ret->correct = snewn(ret->w * ret->h, unsigned char);
1960
1961 ret->completed = state->completed;
1962 ret->cheated = state->cheated;
1963
1964 memcpy(ret->grid, state->grid, state->w * state->h * sizeof(int));
1965 memcpy(ret->vedge, state->vedge, state->w*state->h*sizeof(unsigned char));
1966 memcpy(ret->hedge, state->hedge, state->w*state->h*sizeof(unsigned char));
1967
1968 memcpy(ret->correct, state->correct, state->w*state->h*sizeof(unsigned char));
1969
1970 return ret;
1971}
1972
1973static void free_game(game_state *state)
1974{
1975 sfree(state->grid);
1976 sfree(state->vedge);
1977 sfree(state->hedge);
1978 sfree(state->correct);
1979 sfree(state);
1980}
1981
1982static char *solve_game(const game_state *state, const game_state *currstate,
1983 const char *ai, char **error)
1984{
1985 unsigned char *vedge, *hedge;
1986 int x, y, len;
1987 char *ret, *p;
1988 int i, j, n;
1989 struct numberdata *nd;
1990
1991 if (ai)
1992 return dupstr(ai);
1993
1994 /*
1995 * Attempt the in-built solver.
1996 */
1997
1998 /* Set up each number's (very short) candidate position list. */
1999 for (i = n = 0; i < state->h * state->w; i++)
2000 if (state->grid[i])
2001 n++;
2002
2003 nd = snewn(n, struct numberdata);
2004
2005 for (i = j = 0; i < state->h * state->w; i++)
2006 if (state->grid[i]) {
2007 nd[j].area = state->grid[i];
2008 nd[j].npoints = 1;
2009 nd[j].points = snewn(1, struct point);
2010 nd[j].points[0].x = i % state->w;
2011 nd[j].points[0].y = i / state->w;
2012 j++;
2013 }
2014
2015 assert(j == n);
2016
2017 vedge = snewn(state->w * state->h, unsigned char);
2018 hedge = snewn(state->w * state->h, unsigned char);
2019 memset(vedge, 0, state->w * state->h);
2020 memset(hedge, 0, state->w * state->h);
2021
2022 rect_solver(state->w, state->h, n, nd, hedge, vedge, NULL);
2023
2024 /*
2025 * Clean up.
2026 */
2027 for (i = 0; i < n; i++)
2028 sfree(nd[i].points);
2029 sfree(nd);
2030
2031 len = 2 + (state->w-1)*state->h + (state->h-1)*state->w;
2032 ret = snewn(len, char);
2033
2034 p = ret;
2035 *p++ = 'S';
2036 for (y = 0; y < state->h; y++)
2037 for (x = 1; x < state->w; x++)
2038 *p++ = vedge[y*state->w+x] ? '1' : '0';
2039 for (y = 1; y < state->h; y++)
2040 for (x = 0; x < state->w; x++)
2041 *p++ = hedge[y*state->w+x] ? '1' : '0';
2042 *p++ = '\0';
2043 assert(p - ret == len);
2044
2045 sfree(vedge);
2046 sfree(hedge);
2047
2048 return ret;
2049}
2050
2051static int game_can_format_as_text_now(const game_params *params)
2052{
2053 return TRUE;
2054}
2055
2056static char *game_text_format(const game_state *state)
2057{
2058 char *ret, *p, buf[80];
2059 int i, x, y, col, maxlen;
2060
2061 /*
2062 * First determine the number of spaces required to display a
2063 * number. We'll use at least two, because one looks a bit
2064 * silly.
2065 */
2066 col = 2;
2067 for (i = 0; i < state->w * state->h; i++) {
2068 x = sprintf(buf, "%d", state->grid[i]);
2069 if (col < x) col = x;
2070 }
2071
2072 /*
2073 * Now we know the exact total size of the grid we're going to
2074 * produce: it's got 2*h+1 rows, each containing w lots of col,
2075 * w+1 boundary characters and a trailing newline.
2076 */
2077 maxlen = (2*state->h+1) * (state->w * (col+1) + 2);
2078
2079 ret = snewn(maxlen+1, char);
2080 p = ret;
2081
2082 for (y = 0; y <= 2*state->h; y++) {
2083 for (x = 0; x <= 2*state->w; x++) {
2084 if (x & y & 1) {
2085 /*
2086 * Display a number.
2087 */
2088 int v = grid(state, x/2, y/2);
2089 if (v)
2090 sprintf(buf, "%*d", col, v);
2091 else
2092 sprintf(buf, "%*s", col, "");
2093 memcpy(p, buf, col);
2094 p += col;
2095 } else if (x & 1) {
2096 /*
2097 * Display a horizontal edge or nothing.
2098 */
2099 int h = (y==0 || y==2*state->h ? 1 :
2100 HRANGE(state, x/2, y/2) && hedge(state, x/2, y/2));
2101 int i;
2102 if (h)
2103 h = '-';
2104 else
2105 h = ' ';
2106 for (i = 0; i < col; i++)
2107 *p++ = h;
2108 } else if (y & 1) {
2109 /*
2110 * Display a vertical edge or nothing.
2111 */
2112 int v = (x==0 || x==2*state->w ? 1 :
2113 VRANGE(state, x/2, y/2) && vedge(state, x/2, y/2));
2114 if (v)
2115 *p++ = '|';
2116 else
2117 *p++ = ' ';
2118 } else {
2119 /*
2120 * Display a corner, or a vertical edge, or a
2121 * horizontal edge, or nothing.
2122 */
2123 int hl = (y==0 || y==2*state->h ? 1 :
2124 HRANGE(state, (x-1)/2, y/2) && hedge(state, (x-1)/2, y/2));
2125 int hr = (y==0 || y==2*state->h ? 1 :
2126 HRANGE(state, (x+1)/2, y/2) && hedge(state, (x+1)/2, y/2));
2127 int vu = (x==0 || x==2*state->w ? 1 :
2128 VRANGE(state, x/2, (y-1)/2) && vedge(state, x/2, (y-1)/2));
2129 int vd = (x==0 || x==2*state->w ? 1 :
2130 VRANGE(state, x/2, (y+1)/2) && vedge(state, x/2, (y+1)/2));
2131 if (!hl && !hr && !vu && !vd)
2132 *p++ = ' ';
2133 else if (hl && hr && !vu && !vd)
2134 *p++ = '-';
2135 else if (!hl && !hr && vu && vd)
2136 *p++ = '|';
2137 else
2138 *p++ = '+';
2139 }
2140 }
2141 *p++ = '\n';
2142 }
2143
2144 assert(p - ret == maxlen);
2145 *p = '\0';
2146 return ret;
2147}
2148
2149struct game_ui {
2150 /*
2151 * These coordinates are 2 times the obvious grid coordinates.
2152 * Hence, the top left of the grid is (0,0), the grid point to
2153 * the right of that is (2,0), the one _below that_ is (2,2)
2154 * and so on. This is so that we can specify a drag start point
2155 * on an edge (one odd coordinate) or in the middle of a square
2156 * (two odd coordinates) rather than always at a corner.
2157 *
2158 * -1,-1 means no drag is in progress.
2159 */
2160 int drag_start_x;
2161 int drag_start_y;
2162 int drag_end_x;
2163 int drag_end_y;
2164 /*
2165 * This flag is set as soon as a dragging action moves the
2166 * mouse pointer away from its starting point, so that even if
2167 * the pointer _returns_ to its starting point the action is
2168 * treated as a small drag rather than a click.
2169 */
2170 int dragged;
2171 /* This flag is set if we're doing an erase operation (i.e.
2172 * removing edges in the centre of the rectangle without altering
2173 * the outlines).
2174 */
2175 int erasing;
2176 /*
2177 * These are the co-ordinates of the top-left and bottom-right squares
2178 * in the drag box, respectively, or -1 otherwise.
2179 */
2180 int x1;
2181 int y1;
2182 int x2;
2183 int y2;
2184 /*
2185 * These are the coordinates of a cursor, whether it's visible, and
2186 * whether it was used to start a drag.
2187 */
2188 int cur_x, cur_y, cur_visible, cur_dragging;
2189};
2190
2191static void reset_ui(game_ui *ui)
2192{
2193 ui->drag_start_x = -1;
2194 ui->drag_start_y = -1;
2195 ui->drag_end_x = -1;
2196 ui->drag_end_y = -1;
2197 ui->x1 = -1;
2198 ui->y1 = -1;
2199 ui->x2 = -1;
2200 ui->y2 = -1;
2201 ui->dragged = FALSE;
2202}
2203
2204static game_ui *new_ui(const game_state *state)
2205{
2206 game_ui *ui = snew(game_ui);
2207 reset_ui(ui);
2208 ui->erasing = FALSE;
2209 ui->cur_x = ui->cur_y = ui->cur_visible = ui->cur_dragging = 0;
2210 return ui;
2211}
2212
2213static void free_ui(game_ui *ui)
2214{
2215 sfree(ui);
2216}
2217
2218static char *encode_ui(const game_ui *ui)
2219{
2220 return NULL;
2221}
2222
2223static void decode_ui(game_ui *ui, const char *encoding)
2224{
2225}
2226
2227static void coord_round(float x, float y, int *xr, int *yr)
2228{
2229 float xs, ys, xv, yv, dx, dy, dist;
2230
2231 /*
2232 * Find the nearest square-centre.
2233 */
2234 xs = (float)floor(x) + 0.5F;
2235 ys = (float)floor(y) + 0.5F;
2236
2237 /*
2238 * And find the nearest grid vertex.
2239 */
2240 xv = (float)floor(x + 0.5F);
2241 yv = (float)floor(y + 0.5F);
2242
2243 /*
2244 * We allocate clicks in parts of the grid square to either
2245 * corners, edges or square centres, as follows:
2246 *
2247 * +--+--------+--+
2248 * | | | |
2249 * +--+ +--+
2250 * | `. ,' |
2251 * | +--+ |
2252 * | | | |
2253 * | +--+ |
2254 * | ,' `. |
2255 * +--+ +--+
2256 * | | | |
2257 * +--+--------+--+
2258 *
2259 * (Not to scale!)
2260 *
2261 * In other words: we measure the square distance (i.e.
2262 * max(dx,dy)) from the click to the nearest corner, and if
2263 * it's within CORNER_TOLERANCE then we return a corner click.
2264 * We measure the square distance from the click to the nearest
2265 * centre, and if that's within CENTRE_TOLERANCE we return a
2266 * centre click. Failing that, we find which of the two edge
2267 * centres is nearer to the click and return that edge.
2268 */
2269
2270 /*
2271 * Check for corner click.
2272 */
2273 dx = (float)fabs(x - xv);
2274 dy = (float)fabs(y - yv);
2275 dist = (dx > dy ? dx : dy);
2276 if (dist < CORNER_TOLERANCE) {
2277 *xr = 2 * (int)xv;
2278 *yr = 2 * (int)yv;
2279 } else {
2280 /*
2281 * Check for centre click.
2282 */
2283 dx = (float)fabs(x - xs);
2284 dy = (float)fabs(y - ys);
2285 dist = (dx > dy ? dx : dy);
2286 if (dist < CENTRE_TOLERANCE) {
2287 *xr = 1 + 2 * (int)xs;
2288 *yr = 1 + 2 * (int)ys;
2289 } else {
2290 /*
2291 * Failing both of those, see which edge we're closer to.
2292 * Conveniently, this is simply done by testing the relative
2293 * magnitude of dx and dy (which are currently distances from
2294 * the square centre).
2295 */
2296 if (dx > dy) {
2297 /* Vertical edge: x-coord of corner,
2298 * y-coord of square centre. */
2299 *xr = 2 * (int)xv;
2300 *yr = 1 + 2 * (int)floor(ys);
2301 } else {
2302 /* Horizontal edge: x-coord of square centre,
2303 * y-coord of corner. */
2304 *xr = 1 + 2 * (int)floor(xs);
2305 *yr = 2 * (int)yv;
2306 }
2307 }
2308 }
2309}
2310
2311/*
2312 * Returns TRUE if it has made any change to the grid.
2313 */
2314static int grid_draw_rect(const game_state *state,
2315 unsigned char *hedge, unsigned char *vedge,
2316 int c, int really, int outline,
2317 int x1, int y1, int x2, int y2)
2318{
2319 int x, y;
2320 int changed = FALSE;
2321
2322 /*
2323 * Draw horizontal edges of rectangles.
2324 */
2325 for (x = x1; x < x2; x++)
2326 for (y = y1; y <= y2; y++)
2327 if (HRANGE(state,x,y)) {
2328 int val = index(state,hedge,x,y);
2329 if (y == y1 || y == y2) {
2330 if (!outline) continue;
2331 val = c;
2332 } else if (c == 1)
2333 val = 0;
2334 changed = changed || (index(state,hedge,x,y) != val);
2335 if (really)
2336 index(state,hedge,x,y) = val;
2337 }
2338
2339 /*
2340 * Draw vertical edges of rectangles.
2341 */
2342 for (y = y1; y < y2; y++)
2343 for (x = x1; x <= x2; x++)
2344 if (VRANGE(state,x,y)) {
2345 int val = index(state,vedge,x,y);
2346 if (x == x1 || x == x2) {
2347 if (!outline) continue;
2348 val = c;
2349 } else if (c == 1)
2350 val = 0;
2351 changed = changed || (index(state,vedge,x,y) != val);
2352 if (really)
2353 index(state,vedge,x,y) = val;
2354 }
2355
2356 return changed;
2357}
2358
2359static int ui_draw_rect(const game_state *state, const game_ui *ui,
2360 unsigned char *hedge, unsigned char *vedge, int c,
2361 int really, int outline)
2362{
2363 return grid_draw_rect(state, hedge, vedge, c, really, outline,
2364 ui->x1, ui->y1, ui->x2, ui->y2);
2365}
2366
2367static void game_changed_state(game_ui *ui, const game_state *oldstate,
2368 const game_state *newstate)
2369{
2370}
2371
2372struct game_drawstate {
2373 int started;
2374 int w, h, tilesize;
2375 unsigned long *visible;
2376};
2377
2378static char *interpret_move(const game_state *from, game_ui *ui,
2379 const game_drawstate *ds,
2380 int x, int y, int button)
2381{
2382 int xc, yc;
2383 int startdrag = FALSE, enddrag = FALSE, active = FALSE, erasing = FALSE;
2384 char buf[80], *ret;
2385
2386 button &= ~MOD_MASK;
2387
2388 coord_round(FROMCOORD((float)x), FROMCOORD((float)y), &xc, &yc);
2389
2390 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2391 if (ui->drag_start_x >= 0 && ui->cur_dragging)
2392 reset_ui(ui); /* cancel keyboard dragging */
2393 startdrag = TRUE;
2394 ui->cur_visible = ui->cur_dragging = FALSE;
2395 active = TRUE;
2396 erasing = (button == RIGHT_BUTTON);
2397 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) {
2398 /* We assert we should have had a LEFT_BUTTON first. */
2399 if (ui->cur_visible) {
2400 ui->cur_visible = FALSE;
2401 active = TRUE;
2402 }
2403 assert(!ui->cur_dragging);
2404 enddrag = TRUE;
2405 erasing = (button == RIGHT_RELEASE);
2406 } else if (IS_CURSOR_MOVE(button)) {
2407 move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, 0);
2408 ui->cur_visible = TRUE;
2409 active = TRUE;
2410 if (!ui->cur_dragging) return "";
2411 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc);
2412 } else if (IS_CURSOR_SELECT(button)) {
2413 if (ui->drag_start_x >= 0 && !ui->cur_dragging) {
2414 /*
2415 * If a mouse drag is in progress, ignore attempts to
2416 * start a keyboard one.
2417 */
2418 return NULL;
2419 }
2420 if (!ui->cur_visible) {
2421 assert(!ui->cur_dragging);
2422 ui->cur_visible = TRUE;
2423 return "";
2424 }
2425 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc);
2426 erasing = (button == CURSOR_SELECT2);
2427 if (ui->cur_dragging) {
2428 ui->cur_dragging = FALSE;
2429 enddrag = TRUE;
2430 active = TRUE;
2431 } else {
2432 ui->cur_dragging = TRUE;
2433 startdrag = TRUE;
2434 active = TRUE;
2435 }
2436 } else if (button == '\b' || button == 27) {
2437 if (!ui->cur_dragging) {
2438 ui->cur_visible = FALSE;
2439 } else {
2440 assert(ui->cur_visible);
2441 reset_ui(ui); /* cancel keyboard dragging */
2442 ui->cur_dragging = FALSE;
2443 }
2444 return "";
2445 } else if (button != LEFT_DRAG && button != RIGHT_DRAG) {
2446 return NULL;
2447 }
2448
2449 if (startdrag &&
2450 xc >= 0 && xc <= 2*from->w &&
2451 yc >= 0 && yc <= 2*from->h) {
2452
2453 ui->drag_start_x = xc;
2454 ui->drag_start_y = yc;
2455 ui->drag_end_x = -1;
2456 ui->drag_end_y = -1;
2457 ui->dragged = FALSE;
2458 ui->erasing = erasing;
2459 active = TRUE;
2460 }
2461
2462 if (ui->drag_start_x >= 0 &&
2463 (xc != ui->drag_end_x || yc != ui->drag_end_y)) {
2464 int t;
2465
2466 if (ui->drag_end_x != -1 && ui->drag_end_y != -1)
2467 ui->dragged = TRUE;
2468 ui->drag_end_x = xc;
2469 ui->drag_end_y = yc;
2470 active = TRUE;
2471
2472 if (xc >= 0 && xc <= 2*from->w &&
2473 yc >= 0 && yc <= 2*from->h) {
2474 ui->x1 = ui->drag_start_x;
2475 ui->x2 = ui->drag_end_x;
2476 if (ui->x2 < ui->x1) { t = ui->x1; ui->x1 = ui->x2; ui->x2 = t; }
2477
2478 ui->y1 = ui->drag_start_y;
2479 ui->y2 = ui->drag_end_y;
2480 if (ui->y2 < ui->y1) { t = ui->y1; ui->y1 = ui->y2; ui->y2 = t; }
2481
2482 ui->x1 = ui->x1 / 2; /* rounds down */
2483 ui->x2 = (ui->x2+1) / 2; /* rounds up */
2484 ui->y1 = ui->y1 / 2; /* rounds down */
2485 ui->y2 = (ui->y2+1) / 2; /* rounds up */
2486 } else {
2487 ui->x1 = -1;
2488 ui->y1 = -1;
2489 ui->x2 = -1;
2490 ui->y2 = -1;
2491 }
2492 }
2493
2494 ret = NULL;
2495
2496 if (enddrag && (ui->drag_start_x >= 0)) {
2497 if (xc >= 0 && xc <= 2*from->w &&
2498 yc >= 0 && yc <= 2*from->h &&
2499 erasing == ui->erasing) {
2500
2501 if (ui->dragged) {
2502 if (ui_draw_rect(from, ui, from->hedge,
2503 from->vedge, 1, FALSE, !ui->erasing)) {
2504 sprintf(buf, "%c%d,%d,%d,%d",
2505 (int)(ui->erasing ? 'E' : 'R'),
2506 ui->x1, ui->y1, ui->x2 - ui->x1, ui->y2 - ui->y1);
2507 ret = dupstr(buf);
2508 }
2509 } else {
2510 if ((xc & 1) && !(yc & 1) && HRANGE(from,xc/2,yc/2)) {
2511 sprintf(buf, "H%d,%d", xc/2, yc/2);
2512 ret = dupstr(buf);
2513 }
2514 if ((yc & 1) && !(xc & 1) && VRANGE(from,xc/2,yc/2)) {
2515 sprintf(buf, "V%d,%d", xc/2, yc/2);
2516 ret = dupstr(buf);
2517 }
2518 }
2519 }
2520
2521 reset_ui(ui);
2522 active = TRUE;
2523 }
2524
2525 if (ret)
2526 return ret; /* a move has been made */
2527 else if (active)
2528 return ""; /* UI activity has occurred */
2529 else
2530 return NULL;
2531}
2532
2533static game_state *execute_move(const game_state *from, const char *move)
2534{
2535 game_state *ret;
2536 int x1, y1, x2, y2, mode;
2537
2538 if (move[0] == 'S') {
2539 const char *p = move+1;
2540 int x, y;
2541
2542 ret = dup_game(from);
2543 ret->cheated = TRUE;
2544
2545 for (y = 0; y < ret->h; y++)
2546 for (x = 1; x < ret->w; x++) {
2547 vedge(ret, x, y) = (*p == '1');
2548 if (*p) p++;
2549 }
2550 for (y = 1; y < ret->h; y++)
2551 for (x = 0; x < ret->w; x++) {
2552 hedge(ret, x, y) = (*p == '1');
2553 if (*p) p++;
2554 }
2555
2556 sfree(ret->correct);
2557 ret->correct = get_correct(ret);
2558
2559 return ret;
2560
2561 } else if ((move[0] == 'R' || move[0] == 'E') &&
2562 sscanf(move+1, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4 &&
2563 x1 >= 0 && x2 >= 0 && x1+x2 <= from->w &&
2564 y1 >= 0 && y2 >= 0 && y1+y2 <= from->h) {
2565 x2 += x1;
2566 y2 += y1;
2567 mode = move[0];
2568 } else if ((move[0] == 'H' || move[0] == 'V') &&
2569 sscanf(move+1, "%d,%d", &x1, &y1) == 2 &&
2570 (move[0] == 'H' ? HRANGE(from, x1, y1) :
2571 VRANGE(from, x1, y1))) {
2572 mode = move[0];
2573 } else
2574 return NULL; /* can't parse move string */
2575
2576 ret = dup_game(from);
2577
2578 if (mode == 'R' || mode == 'E') {
2579 grid_draw_rect(ret, ret->hedge, ret->vedge, 1, TRUE,
2580 mode == 'R', x1, y1, x2, y2);
2581 } else if (mode == 'H') {
2582 hedge(ret,x1,y1) = !hedge(ret,x1,y1);
2583 } else if (mode == 'V') {
2584 vedge(ret,x1,y1) = !vedge(ret,x1,y1);
2585 }
2586
2587 sfree(ret->correct);
2588 ret->correct = get_correct(ret);
2589
2590 /*
2591 * We've made a real change to the grid. Check to see
2592 * if the game has been completed.
2593 */
2594 if (!ret->completed) {
2595 int x, y, ok;
2596
2597 ok = TRUE;
2598 for (x = 0; x < ret->w; x++)
2599 for (y = 0; y < ret->h; y++)
2600 if (!index(ret, ret->correct, x, y))
2601 ok = FALSE;
2602
2603 if (ok)
2604 ret->completed = TRUE;
2605 }
2606
2607 return ret;
2608}
2609
2610/* ----------------------------------------------------------------------
2611 * Drawing routines.
2612 */
2613
2614#define CORRECT (1L<<16)
2615#define CURSOR (1L<<17)
2616
2617#define COLOUR(k) ( (k)==1 ? COL_LINE : (k)==2 ? COL_DRAG : COL_DRAGERASE )
2618#define MAX4(x,y,z,w) ( max(max(x,y),max(z,w)) )
2619
2620static void game_compute_size(const game_params *params, int tilesize,
2621 int *x, int *y)
2622{
2623 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2624 struct { int tilesize; } ads, *ds = &ads;
2625 ads.tilesize = tilesize;
2626
2627 *x = params->w * TILE_SIZE + 2*BORDER + 1;
2628 *y = params->h * TILE_SIZE + 2*BORDER + 1;
2629}
2630
2631static void game_set_size(drawing *dr, game_drawstate *ds,
2632 const game_params *params, int tilesize)
2633{
2634 ds->tilesize = tilesize;
2635}
2636
2637static float *game_colours(frontend *fe, int *ncolours)
2638{
2639 float *ret = snewn(3 * NCOLOURS, float);
2640
2641 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2642
2643 ret[COL_GRID * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
2644 ret[COL_GRID * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
2645 ret[COL_GRID * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
2646
2647 ret[COL_DRAG * 3 + 0] = 1.0F;
2648 ret[COL_DRAG * 3 + 1] = 0.0F;
2649 ret[COL_DRAG * 3 + 2] = 0.0F;
2650
2651 ret[COL_DRAGERASE * 3 + 0] = 0.2F;
2652 ret[COL_DRAGERASE * 3 + 1] = 0.2F;
2653 ret[COL_DRAGERASE * 3 + 2] = 1.0F;
2654
2655 ret[COL_CORRECT * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
2656 ret[COL_CORRECT * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
2657 ret[COL_CORRECT * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
2658
2659 ret[COL_LINE * 3 + 0] = 0.0F;
2660 ret[COL_LINE * 3 + 1] = 0.0F;
2661 ret[COL_LINE * 3 + 2] = 0.0F;
2662
2663 ret[COL_TEXT * 3 + 0] = 0.0F;
2664 ret[COL_TEXT * 3 + 1] = 0.0F;
2665 ret[COL_TEXT * 3 + 2] = 0.0F;
2666
2667 ret[COL_CURSOR * 3 + 0] = 1.0F;
2668 ret[COL_CURSOR * 3 + 1] = 0.5F;
2669 ret[COL_CURSOR * 3 + 2] = 0.5F;
2670
2671 *ncolours = NCOLOURS;
2672 return ret;
2673}
2674
2675static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2676{
2677 struct game_drawstate *ds = snew(struct game_drawstate);
2678 int i;
2679
2680 ds->started = FALSE;
2681 ds->w = state->w;
2682 ds->h = state->h;
2683 ds->visible = snewn(ds->w * ds->h, unsigned long);
2684 ds->tilesize = 0; /* not decided yet */
2685 for (i = 0; i < ds->w * ds->h; i++)
2686 ds->visible[i] = 0xFFFF;
2687
2688 return ds;
2689}
2690
2691static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2692{
2693 sfree(ds->visible);
2694 sfree(ds);
2695}
2696
2697static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
2698 int x, int y, unsigned char *hedge, unsigned char *vedge,
2699 unsigned char *corners, unsigned long bgflags)
2700{
2701 int cx = COORD(x), cy = COORD(y);
2702 char str[80];
2703
2704 draw_rect(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1, COL_GRID);
2705 draw_rect(dr, cx+1, cy+1, TILE_SIZE-1, TILE_SIZE-1,
2706 (bgflags & CURSOR) ? COL_CURSOR :
2707 (bgflags & CORRECT) ? COL_CORRECT : COL_BACKGROUND);
2708
2709 if (grid(state,x,y)) {
2710 sprintf(str, "%d", grid(state,x,y));
2711 draw_text(dr, cx+TILE_SIZE/2, cy+TILE_SIZE/2, FONT_VARIABLE,
2712 TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, COL_TEXT, str);
2713 }
2714
2715 /*
2716 * Draw edges.
2717 */
2718 if (!HRANGE(state,x,y) || index(state,hedge,x,y))
2719 draw_rect(dr, cx, cy, TILE_SIZE+1, 2,
2720 HRANGE(state,x,y) ? COLOUR(index(state,hedge,x,y)) :
2721 COL_LINE);
2722 if (!HRANGE(state,x,y+1) || index(state,hedge,x,y+1))
2723 draw_rect(dr, cx, cy+TILE_SIZE-1, TILE_SIZE+1, 2,
2724 HRANGE(state,x,y+1) ? COLOUR(index(state,hedge,x,y+1)) :
2725 COL_LINE);
2726 if (!VRANGE(state,x,y) || index(state,vedge,x,y))
2727 draw_rect(dr, cx, cy, 2, TILE_SIZE+1,
2728 VRANGE(state,x,y) ? COLOUR(index(state,vedge,x,y)) :
2729 COL_LINE);
2730 if (!VRANGE(state,x+1,y) || index(state,vedge,x+1,y))
2731 draw_rect(dr, cx+TILE_SIZE-1, cy, 2, TILE_SIZE+1,
2732 VRANGE(state,x+1,y) ? COLOUR(index(state,vedge,x+1,y)) :
2733 COL_LINE);
2734
2735 /*
2736 * Draw corners.
2737 */
2738 if (index(state,corners,x,y))
2739 draw_rect(dr, cx, cy, 2, 2,
2740 COLOUR(index(state,corners,x,y)));
2741 if (x+1 < state->w && index(state,corners,x+1,y))
2742 draw_rect(dr, cx+TILE_SIZE-1, cy, 2, 2,
2743 COLOUR(index(state,corners,x+1,y)));
2744 if (y+1 < state->h && index(state,corners,x,y+1))
2745 draw_rect(dr, cx, cy+TILE_SIZE-1, 2, 2,
2746 COLOUR(index(state,corners,x,y+1)));
2747 if (x+1 < state->w && y+1 < state->h && index(state,corners,x+1,y+1))
2748 draw_rect(dr, cx+TILE_SIZE-1, cy+TILE_SIZE-1, 2, 2,
2749 COLOUR(index(state,corners,x+1,y+1)));
2750
2751 draw_update(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1);
2752}
2753
2754static void game_redraw(drawing *dr, game_drawstate *ds,
2755 const game_state *oldstate, const game_state *state,
2756 int dir, const game_ui *ui,
2757 float animtime, float flashtime)
2758{
2759 int x, y;
2760 unsigned char *hedge, *vedge, *corners;
2761
2762 if (ui->dragged) {
2763 hedge = snewn(state->w*state->h, unsigned char);
2764 vedge = snewn(state->w*state->h, unsigned char);
2765 memcpy(hedge, state->hedge, state->w*state->h);
2766 memcpy(vedge, state->vedge, state->w*state->h);
2767 ui_draw_rect(state, ui, hedge, vedge, ui->erasing ? 3 : 2, TRUE, TRUE);
2768 } else {
2769 hedge = state->hedge;
2770 vedge = state->vedge;
2771 }
2772
2773 corners = snewn(state->w * state->h, unsigned char);
2774 memset(corners, 0, state->w * state->h);
2775 for (x = 0; x < state->w; x++)
2776 for (y = 0; y < state->h; y++) {
2777 if (x > 0) {
2778 int e = index(state, vedge, x, y);
2779 if (index(state,corners,x,y) < e)
2780 index(state,corners,x,y) = e;
2781 if (y+1 < state->h &&
2782 index(state,corners,x,y+1) < e)
2783 index(state,corners,x,y+1) = e;
2784 }
2785 if (y > 0) {
2786 int e = index(state, hedge, x, y);
2787 if (index(state,corners,x,y) < e)
2788 index(state,corners,x,y) = e;
2789 if (x+1 < state->w &&
2790 index(state,corners,x+1,y) < e)
2791 index(state,corners,x+1,y) = e;
2792 }
2793 }
2794
2795 if (!ds->started) {
2796 draw_rect(dr, 0, 0,
2797 state->w * TILE_SIZE + 2*BORDER + 1,
2798 state->h * TILE_SIZE + 2*BORDER + 1, COL_BACKGROUND);
2799 draw_rect(dr, COORD(0)-1, COORD(0)-1,
2800 ds->w*TILE_SIZE+3, ds->h*TILE_SIZE+3, COL_LINE);
2801 ds->started = TRUE;
2802 draw_update(dr, 0, 0,
2803 state->w * TILE_SIZE + 2*BORDER + 1,
2804 state->h * TILE_SIZE + 2*BORDER + 1);
2805 }
2806
2807 for (x = 0; x < state->w; x++)
2808 for (y = 0; y < state->h; y++) {
2809 unsigned long c = 0;
2810
2811 if (HRANGE(state,x,y))
2812 c |= index(state,hedge,x,y);
2813 if (HRANGE(state,x,y+1))
2814 c |= index(state,hedge,x,y+1) << 2;
2815 if (VRANGE(state,x,y))
2816 c |= index(state,vedge,x,y) << 4;
2817 if (VRANGE(state,x+1,y))
2818 c |= index(state,vedge,x+1,y) << 6;
2819 c |= index(state,corners,x,y) << 8;
2820 if (x+1 < state->w)
2821 c |= index(state,corners,x+1,y) << 10;
2822 if (y+1 < state->h)
2823 c |= index(state,corners,x,y+1) << 12;
2824 if (x+1 < state->w && y+1 < state->h)
2825 /* cast to prevent 2<<14 sign-extending on promotion to long */
2826 c |= (unsigned long)index(state,corners,x+1,y+1) << 14;
2827 if (index(state, state->correct, x, y) && !flashtime)
2828 c |= CORRECT;
2829 if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y)
2830 c |= CURSOR;
2831
2832 if (index(ds,ds->visible,x,y) != c) {
2833 draw_tile(dr, ds, state, x, y, hedge, vedge, corners,
2834 (c & (CORRECT|CURSOR)) );
2835 index(ds,ds->visible,x,y) = c;
2836 }
2837 }
2838
2839 {
2840 char buf[256];
2841
2842 if (ui->dragged &&
2843 ui->x1 >= 0 && ui->y1 >= 0 &&
2844 ui->x2 >= 0 && ui->y2 >= 0) {
2845 sprintf(buf, "%dx%d ",
2846 ui->x2-ui->x1,
2847 ui->y2-ui->y1);
2848 } else {
2849 buf[0] = '\0';
2850 }
2851
2852 if (state->cheated)
2853 strcat(buf, "Auto-solved.");
2854 else if (state->completed)
2855 strcat(buf, "COMPLETED!");
2856
2857 status_bar(dr, buf);
2858 }
2859
2860 if (hedge != state->hedge) {
2861 sfree(hedge);
2862 sfree(vedge);
2863 }
2864
2865 sfree(corners);
2866}
2867
2868static float game_anim_length(const game_state *oldstate,
2869 const game_state *newstate, int dir, game_ui *ui)
2870{
2871 return 0.0F;
2872}
2873
2874static float game_flash_length(const game_state *oldstate,
2875 const game_state *newstate, int dir, game_ui *ui)
2876{
2877 if (!oldstate->completed && newstate->completed &&
2878 !oldstate->cheated && !newstate->cheated)
2879 return FLASH_TIME;
2880 return 0.0F;
2881}
2882
2883static int game_status(const game_state *state)
2884{
2885 return state->completed ? +1 : 0;
2886}
2887
2888static int game_timing_state(const game_state *state, game_ui *ui)
2889{
2890 return TRUE;
2891}
2892
2893static void game_print_size(const game_params *params, float *x, float *y)
2894{
2895 int pw, ph;
2896
2897 /*
2898 * I'll use 5mm squares by default.
2899 */
2900 game_compute_size(params, 500, &pw, &ph);
2901 *x = pw / 100.0F;
2902 *y = ph / 100.0F;
2903}
2904
2905static void game_print(drawing *dr, const game_state *state, int tilesize)
2906{
2907 int w = state->w, h = state->h;
2908 int ink = print_mono_colour(dr, 0);
2909 int x, y;
2910
2911 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2912 game_drawstate ads, *ds = &ads;
2913 game_set_size(dr, ds, NULL, tilesize);
2914
2915 /*
2916 * Border.
2917 */
2918 print_line_width(dr, TILE_SIZE / 10);
2919 draw_rect_outline(dr, COORD(0), COORD(0), w*TILE_SIZE, h*TILE_SIZE, ink);
2920
2921 /*
2922 * Grid. We have to make the grid lines particularly thin,
2923 * because users will be drawing lines _along_ them and we want
2924 * those lines to be visible.
2925 */
2926 print_line_width(dr, TILE_SIZE / 256);
2927 for (x = 1; x < w; x++)
2928 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink);
2929 for (y = 1; y < h; y++)
2930 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink);
2931
2932 /*
2933 * Solution.
2934 */
2935 print_line_width(dr, TILE_SIZE / 10);
2936 for (y = 0; y <= h; y++)
2937 for (x = 0; x <= w; x++) {
2938 if (HRANGE(state,x,y) && hedge(state,x,y))
2939 draw_line(dr, COORD(x), COORD(y), COORD(x+1), COORD(y), ink);
2940 if (VRANGE(state,x,y) && vedge(state,x,y))
2941 draw_line(dr, COORD(x), COORD(y), COORD(x), COORD(y+1), ink);
2942 }
2943
2944 /*
2945 * Clues.
2946 */
2947 for (y = 0; y < h; y++)
2948 for (x = 0; x < w; x++)
2949 if (grid(state,x,y)) {
2950 char str[80];
2951 sprintf(str, "%d", grid(state,x,y));
2952 draw_text(dr, COORD(x)+TILE_SIZE/2, COORD(y)+TILE_SIZE/2,
2953 FONT_VARIABLE, TILE_SIZE/2,
2954 ALIGN_HCENTRE | ALIGN_VCENTRE, ink, str);
2955 }
2956}
2957
2958#ifdef COMBINED
2959#define thegame rect
2960#endif
2961
2962const struct game thegame = {
2963 "Rectangles", "games.rectangles", "rect",
2964 default_params,
2965 game_fetch_preset, NULL,
2966 decode_params,
2967 encode_params,
2968 free_params,
2969 dup_params,
2970 TRUE, game_configure, custom_params,
2971 validate_params,
2972 new_game_desc,
2973 validate_desc,
2974 new_game,
2975 dup_game,
2976 free_game,
2977 TRUE, solve_game,
2978 TRUE, game_can_format_as_text_now, game_text_format,
2979 new_ui,
2980 free_ui,
2981 encode_ui,
2982 decode_ui,
2983 game_changed_state,
2984 interpret_move,
2985 execute_move,
2986 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2987 game_colours,
2988 game_new_drawstate,
2989 game_free_drawstate,
2990 game_redraw,
2991 game_anim_length,
2992 game_flash_length,
2993 game_status,
2994 TRUE, FALSE, game_print_size, game_print,
2995 TRUE, /* wants_statusbar */
2996 FALSE, game_timing_state,
2997 0, /* flags */
2998};
2999
3000/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/rect.html b/apps/plugins/puzzles/src/rect.html
new file mode 100644
index 0000000000..923b34cefd
--- /dev/null
+++ b/apps/plugins/puzzles/src/rect.html
@@ -0,0 +1,76 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Rectangles</title>
7<link rel="previous" href="twiddle.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="netslide.html">
12</head>
13<body>
14<p><a href="twiddle.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="netslide.html">Next</a></p>
15<h1><a name="C8"></a>Chapter 8: <a name="i0"></a>Rectangles</h1>
16<p>
17You have a grid of squares, with numbers written in some (but not all) of the squares. Your task is to subdivide the grid into rectangles of various sizes, such that (a) every rectangle contains exactly one numbered square, and (b) the area of each rectangle is equal to the number written in its numbered square.
18</p>
19<p>
20Credit for this game goes to the Japanese puzzle magazine <a name="i1"></a>Nikoli <a href="#p0">[3]</a>; I've also seen a Palm implementation at <a name="i2"></a>Puzzle Palace <a href="#p1">[4]</a>. Unlike Puzzle Palace's implementation, my version automatically generates random grids of any size you like. The quality of puzzle design is therefore not quite as good as hand-crafted puzzles would be, but on the plus side you get an inexhaustible supply of puzzles tailored to your own specification.
21</p>
22<p><a name="p0"></a>
23[3] <a href="http://www.nikoli.co.jp/en/puzzles/shikaku.html"><code>http://www.nikoli.co.jp/en/puzzles/shikaku.html</code></a> (beware of Flash)
24</p>
25<p><a name="p1"></a>
26[4] <a href="https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en"><code>https://web.archive.org/web/20041024001459/http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en</code></a>
27</p>
28<h2><a name="S8.1"></a>8.1 <a name="i3"></a>Rectangles controls</h2>
29<p>
30This game is played with the mouse or cursor keys.
31</p>
32<p>
33Left-click any edge to toggle it on or off, or left-click and drag to draw an entire rectangle (or line) on the grid in one go (removing any existing edges within that rectangle). Right-clicking and dragging will allow you to erase the contents of a rectangle without affecting its edges.
34</p>
35<p>
36Alternatively, use the cursor keys to move the position indicator around the board. Pressing the return key then allows you to use the cursor keys to drag a rectangle out from that position, and pressing the return key again completes the rectangle. Using the space bar instead of the return key allows you to erase the contents of a rectangle without affecting its edges, as above. Pressing escape cancels a drag.
37</p>
38<p>
39When a rectangle of the correct size is completed, it will be shaded.
40</p>
41<p>
42(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
43</p>
44<h2><a name="S8.2"></a>8.2 <a name="i4"></a>Rectangles parameters</h2>
45<p>
46These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
47</p>
48<dl><dt>
49<em>Width</em>, <em>Height</em>
50</dt>
51<dd>
52Size of grid, in squares.
53</dd>
54<dt>
55<em>Expansion factor</em>
56</dt>
57<dd>
58This is a mechanism for changing the type of grids generated by the program. Some people prefer a grid containing a few large rectangles to one containing many small ones. So you can ask Rectangles to essentially generate a <em>smaller</em> grid than the size you specified, and then to expand it by adding rows and columns.
59<p>
60The default expansion factor of zero means that Rectangles will simply generate a grid of the size you ask for, and do nothing further. If you set an expansion factor of (say) 0.5, it means that each dimension of the grid will be expanded to half again as big after generation. In other words, the initial grid will be 2/3 the size in each dimension, and will be expanded to its full size without adding any more rectangles.
61</p>
62<p>
63Setting an expansion factor of around 0.5 tends to make the game more difficult, and also (in my experience) rewards a less deductive and more intuitive playing style. If you set it <em>too</em> high, though, the game simply cannot generate more than a few rectangles to cover the entire grid, and the game becomes trivial.
64</p>
65
66</dd>
67<dt>
68<em>Ensure unique solution</em>
69</dt>
70<dd>
71Normally, Rectangles will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. Also, finding <em>all</em> the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation.
72</dd>
73</dl>
74
75<hr><address></address></body>
76</html>
diff --git a/apps/plugins/puzzles/src/resource.h b/apps/plugins/puzzles/src/resource.h
new file mode 100644
index 0000000000..f0bfa16d6d
--- /dev/null
+++ b/apps/plugins/puzzles/src/resource.h
@@ -0,0 +1,20 @@
1
2#define IDR_MENUBAR1 101
3
4#define ID_GAME 40005
5#define ID_TYPE 40006
6
7#define IDS_CAP_GAME 40105
8#define IDS_CAP_TYPE 40106
9
10#define IDD_ABOUT 2000
11#define IDC_ABOUT_CAPTION 2001
12#define IDC_ABOUT_LINE 2002
13#define IDC_ABOUT_GAME 2003
14#define IDC_ABOUT_VERSION 2004
15
16#define IDD_CONFIG 2100
17#define IDC_CONFIG_CAPTION 2101
18#define IDC_CONFIG_LINE 2102
19
20#define IDR_PADTOOLBAR 4000
diff --git a/apps/plugins/puzzles/src/samegame.R b/apps/plugins/puzzles/src/samegame.R
new file mode 100644
index 0000000000..cc0d350041
--- /dev/null
+++ b/apps/plugins/puzzles/src/samegame.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3samegame : [X] GTK COMMON samegame samegame-icon|no-icon
4
5samegame : [G] WINDOWS COMMON samegame samegame.res|noicon.res
6
7ALL += samegame[COMBINED]
8
9!begin am gtk
10GAMES += samegame
11!end
12
13!begin >list.c
14 A(samegame) \
15!end
16
17!begin >gamedesc.txt
18samegame:samegame.exe:Same Game:Block-clearing puzzle:Clear the grid by removing touching groups of the same colour squares.
19!end
diff --git a/apps/plugins/puzzles/src/samegame.c b/apps/plugins/puzzles/src/samegame.c
new file mode 100644
index 0000000000..88edad32b1
--- /dev/null
+++ b/apps/plugins/puzzles/src/samegame.c
@@ -0,0 +1,1679 @@
1/*
2 * 'same game' -- try to remove all the coloured squares by
3 * selecting regions of contiguous colours.
4 */
5
6/*
7 * TODO on grid generation:
8 *
9 * - Generation speed could still be improved.
10 * * 15x10c3 is the only really difficult one of the existing
11 * presets. The others are all either small enough, or have
12 * the great flexibility given by four colours, that they
13 * don't take long at all.
14 * * I still suspect many problems arise from separate
15 * subareas. I wonder if we can also somehow prioritise left-
16 * or rightmost insertions so as to avoid area splitting at
17 * all where feasible? It's not easy, though, because the
18 * current shuffle-then-try-all-options approach to move
19 * choice doesn't leave room for `soft' probabilistic
20 * prioritisation: we either try all class A moves before any
21 * class B ones, or we don't.
22 *
23 * - The current generation algorithm inserts exactly two squares
24 * at a time, with a single exception at the beginning of
25 * generation for grids of odd overall size. An obvious
26 * extension would be to permit larger inverse moves during
27 * generation.
28 * * this might reduce the number of failed generations by
29 * making the insertion algorithm more flexible
30 * * on the other hand, it would be significantly more complex
31 * * if I do this I'll need to take out the odd-subarea
32 * avoidance
33 * * a nice feature of the current algorithm is that the
34 * computer's `intended' solution always receives the minimum
35 * possible score, so that pretty much the player's entire
36 * score represents how much better they did than the
37 * computer.
38 *
39 * - Is it possible we can _temporarily_ tolerate neighbouring
40 * squares of the same colour, until we've finished setting up
41 * our inverse move?
42 * * or perhaps even not choose the colour of our inserted
43 * region until we have finished placing it, and _then_ look
44 * at what colours border on it?
45 * * I don't think this is currently meaningful unless we're
46 * placing more than a domino at a time.
47 *
48 * - possibly write out a full solution so that Solve can somehow
49 * show it step by step?
50 * * aux_info would have to encode the click points
51 * * solve_game() would have to encode not only those click
52 * points but also give a move string which reconstructed the
53 * initial state
54 * * the game_state would include a pointer to a solution move
55 * list, plus an index into that list
56 * * game_changed_state would auto-select the next move if
57 * handed a new state which had a solution move list active
58 * * execute_move, if passed such a state as input, would check
59 * to see whether the move being made was the same as the one
60 * stated by the solution, and if so would advance the move
61 * index. Failing that it would return a game_state without a
62 * solution move list active at all.
63 */
64
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <assert.h>
69#include <ctype.h>
70#include <math.h>
71
72#include "puzzles.h"
73
74#define TILE_INNER (ds->tileinner)
75#define TILE_GAP (ds->tilegap)
76#define TILE_SIZE (TILE_INNER + TILE_GAP)
77#define PREFERRED_TILE_SIZE 32
78#define BORDER (TILE_SIZE / 2)
79#define HIGHLIGHT_WIDTH 2
80
81#define FLASH_FRAME 0.13F
82
83#define COORD(x) ( (x) * TILE_SIZE + BORDER )
84#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
85
86#define X(state, i) ( (i) % (state)->params.w )
87#define Y(state, i) ( (i) / (state)->params.w )
88#define C(state, x, y) ( (y) * (state)->w + (x) )
89
90enum {
91 COL_BACKGROUND,
92 COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9,
93 COL_IMPOSSIBLE, COL_SEL, COL_HIGHLIGHT, COL_LOWLIGHT,
94 NCOLOURS
95};
96
97/* scoresub is 1 or 2 (for (n-1)^2 or (n-2)^2) */
98struct game_params {
99 int w, h, ncols, scoresub;
100 int soluble; /* choose generation algorithm */
101};
102
103/* These flags must be unique across all uses; in the game_state,
104 * the game_ui, and the drawstate (as they all get combined in the
105 * drawstate). */
106#define TILE_COLMASK 0x00ff
107#define TILE_SELECTED 0x0100 /* used in ui and drawstate */
108#define TILE_JOINRIGHT 0x0200 /* used in drawstate */
109#define TILE_JOINDOWN 0x0400 /* used in drawstate */
110#define TILE_JOINDIAG 0x0800 /* used in drawstate */
111#define TILE_HASSEL 0x1000 /* used in drawstate */
112#define TILE_IMPOSSIBLE 0x2000 /* used in drawstate */
113
114#define TILE(gs,x,y) ((gs)->tiles[(gs)->params.w*(y)+(x)])
115#define COL(gs,x,y) (TILE(gs,x,y) & TILE_COLMASK)
116#define ISSEL(gs,x,y) (TILE(gs,x,y) & TILE_SELECTED)
117
118#define SWAPTILE(gs,x1,y1,x2,y2) do { \
119 int t = TILE(gs,x1,y1); \
120 TILE(gs,x1,y1) = TILE(gs,x2,y2); \
121 TILE(gs,x2,y2) = t; \
122} while (0)
123
124static int npoints(const game_params *params, int nsel)
125{
126 int sdiff = nsel - params->scoresub;
127 return (sdiff > 0) ? sdiff * sdiff : 0;
128}
129
130struct game_state {
131 struct game_params params;
132 int n;
133 int *tiles; /* colour only */
134 int score;
135 int complete, impossible;
136};
137
138static game_params *default_params(void)
139{
140 game_params *ret = snew(game_params);
141 ret->w = 5;
142 ret->h = 5;
143 ret->ncols = 3;
144 ret->scoresub = 2;
145 ret->soluble = TRUE;
146 return ret;
147}
148
149static const struct game_params samegame_presets[] = {
150 { 5, 5, 3, 2, TRUE },
151 { 10, 5, 3, 2, TRUE },
152#ifdef SLOW_SYSTEM
153 { 10, 10, 3, 2, TRUE },
154#else
155 { 15, 10, 3, 2, TRUE },
156#endif
157 { 15, 10, 4, 2, TRUE },
158 { 20, 15, 4, 2, TRUE }
159};
160
161static int game_fetch_preset(int i, char **name, game_params **params)
162{
163 game_params *ret;
164 char str[80];
165
166 if (i < 0 || i >= lenof(samegame_presets))
167 return FALSE;
168
169 ret = snew(game_params);
170 *ret = samegame_presets[i];
171
172 sprintf(str, "%dx%d, %d colours", ret->w, ret->h, ret->ncols);
173
174 *name = dupstr(str);
175 *params = ret;
176 return TRUE;
177}
178
179static void free_params(game_params *params)
180{
181 sfree(params);
182}
183
184static game_params *dup_params(const game_params *params)
185{
186 game_params *ret = snew(game_params);
187 *ret = *params; /* structure copy */
188 return ret;
189}
190
191static void decode_params(game_params *params, char const *string)
192{
193 char const *p = string;
194
195 params->w = atoi(p);
196 while (*p && isdigit((unsigned char)*p)) p++;
197 if (*p == 'x') {
198 p++;
199 params->h = atoi(p);
200 while (*p && isdigit((unsigned char)*p)) p++;
201 } else {
202 params->h = params->w;
203 }
204 if (*p == 'c') {
205 p++;
206 params->ncols = atoi(p);
207 while (*p && isdigit((unsigned char)*p)) p++;
208 } else {
209 params->ncols = 3;
210 }
211 if (*p == 's') {
212 p++;
213 params->scoresub = atoi(p);
214 while (*p && isdigit((unsigned char)*p)) p++;
215 } else {
216 params->scoresub = 2;
217 }
218 if (*p == 'r') {
219 p++;
220 params->soluble = FALSE;
221 }
222}
223
224static char *encode_params(const game_params *params, int full)
225{
226 char ret[80];
227
228 sprintf(ret, "%dx%dc%ds%d%s",
229 params->w, params->h, params->ncols, params->scoresub,
230 full && !params->soluble ? "r" : "");
231 return dupstr(ret);
232}
233
234static config_item *game_configure(const game_params *params)
235{
236 config_item *ret;
237 char buf[80];
238
239 ret = snewn(6, config_item);
240
241 ret[0].name = "Width";
242 ret[0].type = C_STRING;
243 sprintf(buf, "%d", params->w);
244 ret[0].sval = dupstr(buf);
245 ret[0].ival = 0;
246
247 ret[1].name = "Height";
248 ret[1].type = C_STRING;
249 sprintf(buf, "%d", params->h);
250 ret[1].sval = dupstr(buf);
251 ret[1].ival = 0;
252
253 ret[2].name = "No. of colours";
254 ret[2].type = C_STRING;
255 sprintf(buf, "%d", params->ncols);
256 ret[2].sval = dupstr(buf);
257 ret[2].ival = 0;
258
259 ret[3].name = "Scoring system";
260 ret[3].type = C_CHOICES;
261 ret[3].sval = ":(n-1)^2:(n-2)^2";
262 ret[3].ival = params->scoresub-1;
263
264 ret[4].name = "Ensure solubility";
265 ret[4].type = C_BOOLEAN;
266 ret[4].sval = NULL;
267 ret[4].ival = params->soluble;
268
269 ret[5].name = NULL;
270 ret[5].type = C_END;
271 ret[5].sval = NULL;
272 ret[5].ival = 0;
273
274 return ret;
275}
276
277static game_params *custom_params(const config_item *cfg)
278{
279 game_params *ret = snew(game_params);
280
281 ret->w = atoi(cfg[0].sval);
282 ret->h = atoi(cfg[1].sval);
283 ret->ncols = atoi(cfg[2].sval);
284 ret->scoresub = cfg[3].ival + 1;
285 ret->soluble = cfg[4].ival;
286
287 return ret;
288}
289
290static char *validate_params(const game_params *params, int full)
291{
292 if (params->w < 1 || params->h < 1)
293 return "Width and height must both be positive";
294
295 if (params->ncols > 9)
296 return "Maximum of 9 colours";
297
298 if (params->soluble) {
299 if (params->ncols < 3)
300 return "Number of colours must be at least three";
301 if (params->w * params->h <= 1)
302 return "Grid area must be greater than 1";
303 } else {
304 if (params->ncols < 2)
305 return "Number of colours must be at least three";
306 /* ...and we must make sure we can generate at least 2 squares
307 * of each colour so it's theoretically soluble. */
308 if ((params->w * params->h) < (params->ncols * 2))
309 return "Too many colours makes given grid size impossible";
310 }
311
312 if ((params->scoresub < 1) || (params->scoresub > 2))
313 return "Scoring system not recognised";
314
315 return NULL;
316}
317
318/*
319 * Guaranteed-soluble grid generator.
320 */
321static void gen_grid(int w, int h, int nc, int *grid, random_state *rs)
322{
323 int wh = w*h, tc = nc+1;
324 int i, j, k, c, x, y, pos, n;
325 int *list, *grid2;
326 int ok, failures = 0;
327
328 /*
329 * We'll use `list' to track the possible places to put our
330 * next insertion. There are up to h places to insert in each
331 * column: in a column of height n there are n+1 places because
332 * we can insert at the very bottom or the very top, but a
333 * column of height h can't have anything at all inserted in it
334 * so we have up to h in each column. Likewise, with n columns
335 * present there are n+1 places to fit a new one in between but
336 * we can't insert a column if there are already w; so there
337 * are a maximum of w new columns too. Total is wh + w.
338 */
339 list = snewn(wh + w, int);
340 grid2 = snewn(wh, int);
341
342 do {
343 /*
344 * Start with two or three squares - depending on parity of w*h
345 * - of a random colour.
346 */
347 for (i = 0; i < wh; i++)
348 grid[i] = 0;
349 j = 2 + (wh % 2);
350 c = 1 + random_upto(rs, nc);
351 if (j <= w) {
352 for (i = 0; i < j; i++)
353 grid[(h-1)*w+i] = c;
354 } else {
355 assert(j <= h);
356 for (i = 0; i < j; i++)
357 grid[(h-1-i)*w] = c;
358 }
359
360 /*
361 * Now repeatedly insert a two-square blob in the grid, of
362 * whatever colour will go at the position we chose.
363 */
364 while (1) {
365 n = 0;
366
367 /*
368 * Build up a list of insertion points. Each point is
369 * encoded as y*w+x; insertion points between columns are
370 * encoded as h*w+x.
371 */
372
373 if (grid[wh - 1] == 0) {
374 /*
375 * The final column is empty, so we can insert new
376 * columns.
377 */
378 for (i = 0; i < w; i++) {
379 list[n++] = wh + i;
380 if (grid[(h-1)*w + i] == 0)
381 break;
382 }
383 }
384
385 /*
386 * Now look for places to insert within columns.
387 */
388 for (i = 0; i < w; i++) {
389 if (grid[(h-1)*w+i] == 0)
390 break; /* no more columns */
391
392 if (grid[i] != 0)
393 continue; /* this column is full */
394
395 for (j = h; j-- > 0 ;) {
396 list[n++] = j*w+i;
397 if (grid[j*w+i] == 0)
398 break; /* this column is exhausted */
399 }
400 }
401
402 if (n == 0)
403 break; /* we're done */
404
405#ifdef GENERATION_DIAGNOSTICS
406 printf("initial grid:\n");
407 {
408 int x,y;
409 for (y = 0; y < h; y++) {
410 for (x = 0; x < w; x++) {
411 if (grid[y*w+x] == 0)
412 printf("-");
413 else
414 printf("%d", grid[y*w+x]);
415 }
416 printf("\n");
417 }
418 }
419#endif
420
421 /*
422 * Now go through the list one element at a time in
423 * random order, and actually attempt to insert
424 * something there.
425 */
426 while (n-- > 0) {
427 int dirs[4], ndirs, dir;
428
429 i = random_upto(rs, n+1);
430 pos = list[i];
431 list[i] = list[n];
432
433 x = pos % w;
434 y = pos / w;
435
436 memcpy(grid2, grid, wh * sizeof(int));
437
438 if (y == h) {
439 /*
440 * Insert a column at position x.
441 */
442 for (i = w-1; i > x; i--)
443 for (j = 0; j < h; j++)
444 grid2[j*w+i] = grid2[j*w+(i-1)];
445 /*
446 * Clear the new column.
447 */
448 for (j = 0; j < h; j++)
449 grid2[j*w+x] = 0;
450 /*
451 * Decrement y so that our first square is actually
452 * inserted _in_ the grid rather than just below it.
453 */
454 y--;
455 }
456
457 /*
458 * Insert a square within column x at position y.
459 */
460 for (i = 0; i+1 <= y; i++)
461 grid2[i*w+x] = grid2[(i+1)*w+x];
462
463#ifdef GENERATION_DIAGNOSTICS
464 printf("trying at n=%d (%d,%d)\n", n, x, y);
465 grid2[y*w+x] = tc;
466 {
467 int x,y;
468 for (y = 0; y < h; y++) {
469 for (x = 0; x < w; x++) {
470 if (grid2[y*w+x] == 0)
471 printf("-");
472 else if (grid2[y*w+x] <= nc)
473 printf("%d", grid2[y*w+x]);
474 else
475 printf("*");
476 }
477 printf("\n");
478 }
479 }
480#endif
481
482 /*
483 * Pick our square colour so that it doesn't match any
484 * of its neighbours.
485 */
486 {
487 int wrongcol[4], nwrong = 0;
488
489 /*
490 * List the neighbouring colours.
491 */
492 if (x > 0)
493 wrongcol[nwrong++] = grid2[y*w+(x-1)];
494 if (x+1 < w)
495 wrongcol[nwrong++] = grid2[y*w+(x+1)];
496 if (y > 0)
497 wrongcol[nwrong++] = grid2[(y-1)*w+x];
498 if (y+1 < h)
499 wrongcol[nwrong++] = grid2[(y+1)*w+x];
500
501 /*
502 * Eliminate duplicates. We can afford a shoddy
503 * algorithm here because the problem size is
504 * bounded.
505 */
506 for (i = j = 0 ;; i++) {
507 int pos = -1, min = 0;
508 if (j > 0)
509 min = wrongcol[j-1];
510 for (k = i; k < nwrong; k++)
511 if (wrongcol[k] > min &&
512 (pos == -1 || wrongcol[k] < wrongcol[pos]))
513 pos = k;
514 if (pos >= 0) {
515 int v = wrongcol[pos];
516 wrongcol[pos] = wrongcol[j];
517 wrongcol[j++] = v;
518 } else
519 break;
520 }
521 nwrong = j;
522
523 /*
524 * If no colour will go here, stop trying.
525 */
526 if (nwrong == nc)
527 continue;
528
529 /*
530 * Otherwise, pick a colour from the remaining
531 * ones.
532 */
533 c = 1 + random_upto(rs, nc - nwrong);
534 for (i = 0; i < nwrong; i++) {
535 if (c >= wrongcol[i])
536 c++;
537 else
538 break;
539 }
540 }
541
542 /*
543 * Place the new square.
544 *
545 * Although I've _chosen_ the new region's colour
546 * (so that we can check adjacency), I'm going to
547 * actually place it as an invalid colour (tc)
548 * until I'm sure it's viable. This is so that I
549 * can conveniently check that I really have made a
550 * _valid_ inverse move later on.
551 */
552#ifdef GENERATION_DIAGNOSTICS
553 printf("picked colour %d\n", c);
554#endif
555 grid2[y*w+x] = tc;
556
557 /*
558 * Now attempt to extend it in one of three ways: left,
559 * right or up.
560 */
561 ndirs = 0;
562 if (x > 0 &&
563 grid2[y*w+(x-1)] != c &&
564 grid2[x-1] == 0 &&
565 (y+1 >= h || grid2[(y+1)*w+(x-1)] != c) &&
566 (y+1 >= h || grid2[(y+1)*w+(x-1)] != 0) &&
567 (x <= 1 || grid2[y*w+(x-2)] != c))
568 dirs[ndirs++] = -1; /* left */
569 if (x+1 < w &&
570 grid2[y*w+(x+1)] != c &&
571 grid2[x+1] == 0 &&
572 (y+1 >= h || grid2[(y+1)*w+(x+1)] != c) &&
573 (y+1 >= h || grid2[(y+1)*w+(x+1)] != 0) &&
574 (x+2 >= w || grid2[y*w+(x+2)] != c))
575 dirs[ndirs++] = +1; /* right */
576 if (y > 0 &&
577 grid2[x] == 0 &&
578 (x <= 0 || grid2[(y-1)*w+(x-1)] != c) &&
579 (x+1 >= w || grid2[(y-1)*w+(x+1)] != c)) {
580 /*
581 * We add this possibility _twice_, so that the
582 * probability of placing a vertical domino is
583 * about the same as that of a horizontal. This
584 * should yield less bias in the generated
585 * grids.
586 */
587 dirs[ndirs++] = 0; /* up */
588 dirs[ndirs++] = 0; /* up */
589 }
590
591 if (ndirs == 0)
592 continue;
593
594 dir = dirs[random_upto(rs, ndirs)];
595
596#ifdef GENERATION_DIAGNOSTICS
597 printf("picked dir %d\n", dir);
598#endif
599
600 /*
601 * Insert a square within column (x+dir) at position y.
602 */
603 for (i = 0; i+1 <= y; i++)
604 grid2[i*w+x+dir] = grid2[(i+1)*w+x+dir];
605 grid2[y*w+x+dir] = tc;
606
607 /*
608 * See if we've divided the remaining grid squares
609 * into sub-areas. If so, we need every sub-area to
610 * have an even area or we won't be able to
611 * complete generation.
612 *
613 * If the height is odd and not all columns are
614 * present, we can increase the area of a subarea
615 * by adding a new column in it, so in that
616 * situation we don't mind having as many odd
617 * subareas as there are spare columns.
618 *
619 * If the height is even, we can't fix it at all.
620 */
621 {
622 int nerrs = 0, nfix = 0;
623 k = 0; /* current subarea size */
624 for (i = 0; i < w; i++) {
625 if (grid2[(h-1)*w+i] == 0) {
626 if (h % 2)
627 nfix++;
628 continue;
629 }
630 for (j = 0; j < h && grid2[j*w+i] == 0; j++);
631 assert(j < h);
632 if (j == 0) {
633 /*
634 * End of previous subarea.
635 */
636 if (k % 2)
637 nerrs++;
638 k = 0;
639 } else {
640 k += j;
641 }
642 }
643 if (k % 2)
644 nerrs++;
645 if (nerrs > nfix)
646 continue; /* try a different placement */
647 }
648
649 /*
650 * We've made a move. Verify that it is a valid
651 * move and that if made it would indeed yield the
652 * previous grid state. The criteria are:
653 *
654 * (a) removing all the squares of colour tc (and
655 * shuffling the columns up etc) from grid2
656 * would yield grid
657 * (b) no square of colour tc is adjacent to one
658 * of colour c
659 * (c) all the squares of colour tc form a single
660 * connected component
661 *
662 * We verify the latter property at the same time
663 * as checking that removing all the tc squares
664 * would yield the previous grid. Then we colour
665 * the tc squares in colour c by breadth-first
666 * search, which conveniently permits us to test
667 * that they're all connected.
668 */
669 {
670 int x1, x2, y1, y2;
671 int ok = TRUE;
672 int fillstart = -1, ntc = 0;
673
674#ifdef GENERATION_DIAGNOSTICS
675 {
676 int x,y;
677 printf("testing move (new, old):\n");
678 for (y = 0; y < h; y++) {
679 for (x = 0; x < w; x++) {
680 if (grid2[y*w+x] == 0)
681 printf("-");
682 else if (grid2[y*w+x] <= nc)
683 printf("%d", grid2[y*w+x]);
684 else
685 printf("*");
686 }
687 printf(" ");
688 for (x = 0; x < w; x++) {
689 if (grid[y*w+x] == 0)
690 printf("-");
691 else
692 printf("%d", grid[y*w+x]);
693 }
694 printf("\n");
695 }
696 }
697#endif
698
699 for (x1 = x2 = 0; x2 < w; x2++) {
700 int usedcol = FALSE;
701
702 for (y1 = y2 = h-1; y2 >= 0; y2--) {
703 if (grid2[y2*w+x2] == tc) {
704 ntc++;
705 if (fillstart == -1)
706 fillstart = y2*w+x2;
707 if ((y2+1 < h && grid2[(y2+1)*w+x2] == c) ||
708 (y2-1 >= 0 && grid2[(y2-1)*w+x2] == c) ||
709 (x2+1 < w && grid2[y2*w+x2+1] == c) ||
710 (x2-1 >= 0 && grid2[y2*w+x2-1] == c)) {
711#ifdef GENERATION_DIAGNOSTICS
712 printf("adjacency failure at %d,%d\n",
713 x2, y2);
714#endif
715 ok = FALSE;
716 }
717 continue;
718 }
719 if (grid2[y2*w+x2] == 0)
720 break;
721 usedcol = TRUE;
722 if (grid2[y2*w+x2] != grid[y1*w+x1]) {
723#ifdef GENERATION_DIAGNOSTICS
724 printf("matching failure at %d,%d vs %d,%d\n",
725 x2, y2, x1, y1);
726#endif
727 ok = FALSE;
728 }
729 y1--;
730 }
731
732 /*
733 * If we've reached the top of the column
734 * in grid2, verify that we've also reached
735 * the top of the column in `grid'.
736 */
737 if (usedcol) {
738 while (y1 >= 0) {
739 if (grid[y1*w+x1] != 0) {
740#ifdef GENERATION_DIAGNOSTICS
741 printf("junk at column top (%d,%d)\n",
742 x1, y1);
743#endif
744 ok = FALSE;
745 }
746 y1--;
747 }
748 }
749
750 if (!ok)
751 break;
752
753 if (usedcol)
754 x1++;
755 }
756
757 if (!ok) {
758 assert(!"This should never happen");
759
760 /*
761 * If this game is compiled NDEBUG so that
762 * the assertion doesn't bring it to a
763 * crashing halt, the only thing we can do
764 * is to give up, loop round again, and
765 * hope to randomly avoid making whatever
766 * type of move just caused this failure.
767 */
768 continue;
769 }
770
771 /*
772 * Now use bfs to fill in the tc section as
773 * colour c. We use `list' to store the set of
774 * squares we have to process.
775 */
776 i = j = 0;
777 assert(fillstart >= 0);
778 list[i++] = fillstart;
779#ifdef OUTPUT_SOLUTION
780 printf("M");
781#endif
782 while (j < i) {
783 k = list[j];
784 x = k % w;
785 y = k / w;
786#ifdef OUTPUT_SOLUTION
787 printf("%s%d", j ? "," : "", k);
788#endif
789 j++;
790
791 assert(grid2[k] == tc);
792 grid2[k] = c;
793
794 if (x > 0 && grid2[k-1] == tc)
795 list[i++] = k-1;
796 if (x+1 < w && grid2[k+1] == tc)
797 list[i++] = k+1;
798 if (y > 0 && grid2[k-w] == tc)
799 list[i++] = k-w;
800 if (y+1 < h && grid2[k+w] == tc)
801 list[i++] = k+w;
802 }
803#ifdef OUTPUT_SOLUTION
804 printf("\n");
805#endif
806
807 /*
808 * Check that we've filled the same number of
809 * tc squares as we originally found.
810 */
811 assert(j == ntc);
812 }
813
814 memcpy(grid, grid2, wh * sizeof(int));
815
816 break; /* done it! */
817 }
818
819#ifdef GENERATION_DIAGNOSTICS
820 {
821 int x,y;
822 printf("n=%d\n", n);
823 for (y = 0; y < h; y++) {
824 for (x = 0; x < w; x++) {
825 if (grid[y*w+x] == 0)
826 printf("-");
827 else
828 printf("%d", grid[y*w+x]);
829 }
830 printf("\n");
831 }
832 }
833#endif
834
835 if (n < 0)
836 break;
837 }
838
839 ok = TRUE;
840 for (i = 0; i < wh; i++)
841 if (grid[i] == 0) {
842 ok = FALSE;
843 failures++;
844#if defined GENERATION_DIAGNOSTICS || defined SHOW_INCOMPLETE
845 {
846 int x,y;
847 printf("incomplete grid:\n");
848 for (y = 0; y < h; y++) {
849 for (x = 0; x < w; x++) {
850 if (grid[y*w+x] == 0)
851 printf("-");
852 else
853 printf("%d", grid[y*w+x]);
854 }
855 printf("\n");
856 }
857 }
858#endif
859 break;
860 }
861
862 } while (!ok);
863
864#if defined GENERATION_DIAGNOSTICS || defined COUNT_FAILURES
865 printf("%d failures\n", failures);
866#endif
867#ifdef GENERATION_DIAGNOSTICS
868 {
869 int x,y;
870 printf("final grid:\n");
871 for (y = 0; y < h; y++) {
872 for (x = 0; x < w; x++) {
873 printf("%d", grid[y*w+x]);
874 }
875 printf("\n");
876 }
877 }
878#endif
879
880 sfree(grid2);
881 sfree(list);
882}
883
884/*
885 * Not-guaranteed-soluble grid generator; kept as a legacy, and in
886 * case someone finds the slightly odd quality of the guaranteed-
887 * soluble grids to be aesthetically displeasing or finds its CPU
888 * utilisation to be excessive.
889 */
890static void gen_grid_random(int w, int h, int nc, int *grid, random_state *rs)
891{
892 int i, j, c;
893 int n = w * h;
894
895 for (i = 0; i < n; i++)
896 grid[i] = 0;
897
898 /*
899 * Our sole concession to not gratuitously generating insoluble
900 * grids is to ensure we have at least two of every colour.
901 */
902 for (c = 1; c <= nc; c++) {
903 for (j = 0; j < 2; j++) {
904 do {
905 i = (int)random_upto(rs, n);
906 } while (grid[i] != 0);
907 grid[i] = c;
908 }
909 }
910
911 /*
912 * Fill in the rest of the grid at random.
913 */
914 for (i = 0; i < n; i++) {
915 if (grid[i] == 0)
916 grid[i] = (int)random_upto(rs, nc)+1;
917 }
918}
919
920static char *new_game_desc(const game_params *params, random_state *rs,
921 char **aux, int interactive)
922{
923 char *ret;
924 int n, i, retlen, *tiles;
925
926 n = params->w * params->h;
927 tiles = snewn(n, int);
928
929 if (params->soluble)
930 gen_grid(params->w, params->h, params->ncols, tiles, rs);
931 else
932 gen_grid_random(params->w, params->h, params->ncols, tiles, rs);
933
934 ret = NULL;
935 retlen = 0;
936 for (i = 0; i < n; i++) {
937 char buf[80];
938 int k;
939
940 k = sprintf(buf, "%d,", tiles[i]);
941 ret = sresize(ret, retlen + k + 1, char);
942 strcpy(ret + retlen, buf);
943 retlen += k;
944 }
945 ret[retlen-1] = '\0'; /* delete last comma */
946
947 sfree(tiles);
948 return ret;
949}
950
951static char *validate_desc(const game_params *params, const char *desc)
952{
953 int area = params->w * params->h, i;
954 const char *p = desc;
955
956 for (i = 0; i < area; i++) {
957 const char *q = p;
958 int n;
959
960 if (!isdigit((unsigned char)*p))
961 return "Not enough numbers in string";
962 while (isdigit((unsigned char)*p)) p++;
963
964 if (i < area-1 && *p != ',')
965 return "Expected comma after number";
966 else if (i == area-1 && *p)
967 return "Excess junk at end of string";
968
969 n = atoi(q);
970 if (n < 0 || n > params->ncols)
971 return "Colour out of range";
972
973 if (*p) p++; /* eat comma */
974 }
975 return NULL;
976}
977
978static game_state *new_game(midend *me, const game_params *params,
979 const char *desc)
980{
981 game_state *state = snew(game_state);
982 const char *p = desc;
983 int i;
984
985 state->params = *params; /* struct copy */
986 state->n = state->params.w * state->params.h;
987 state->tiles = snewn(state->n, int);
988
989 for (i = 0; i < state->n; i++) {
990 assert(*p);
991 state->tiles[i] = atoi(p);
992 while (*p && *p != ',')
993 p++;
994 if (*p) p++; /* eat comma */
995 }
996 state->complete = state->impossible = 0;
997 state->score = 0;
998
999 return state;
1000}
1001
1002static game_state *dup_game(const game_state *state)
1003{
1004 game_state *ret = snew(game_state);
1005
1006 *ret = *state; /* structure copy, except... */
1007
1008 ret->tiles = snewn(state->n, int);
1009 memcpy(ret->tiles, state->tiles, state->n * sizeof(int));
1010
1011 return ret;
1012}
1013
1014static void free_game(game_state *state)
1015{
1016 sfree(state->tiles);
1017 sfree(state);
1018}
1019
1020static char *solve_game(const game_state *state, const game_state *currstate,
1021 const char *aux, char **error)
1022{
1023 return NULL;
1024}
1025
1026static int game_can_format_as_text_now(const game_params *params)
1027{
1028 return TRUE;
1029}
1030
1031static char *game_text_format(const game_state *state)
1032{
1033 char *ret, *p;
1034 int x, y, maxlen;
1035
1036 maxlen = state->params.h * (state->params.w + 1);
1037 ret = snewn(maxlen+1, char);
1038 p = ret;
1039
1040 for (y = 0; y < state->params.h; y++) {
1041 for (x = 0; x < state->params.w; x++) {
1042 int t = TILE(state,x,y);
1043 if (t <= 0) *p++ = ' ';
1044 else if (t < 10) *p++ = '0'+t;
1045 else *p++ = 'a'+(t-10);
1046 }
1047 *p++ = '\n';
1048 }
1049 assert(p - ret == maxlen);
1050 *p = '\0';
1051 return ret;
1052}
1053
1054struct game_ui {
1055 struct game_params params;
1056 int *tiles; /* selected-ness only */
1057 int nselected;
1058 int xsel, ysel, displaysel;
1059};
1060
1061static game_ui *new_ui(const game_state *state)
1062{
1063 game_ui *ui = snew(game_ui);
1064
1065 ui->params = state->params; /* structure copy */
1066 ui->tiles = snewn(state->n, int);
1067 memset(ui->tiles, 0, state->n*sizeof(int));
1068 ui->nselected = 0;
1069
1070 ui->xsel = ui->ysel = ui->displaysel = 0;
1071
1072 return ui;
1073}
1074
1075static void free_ui(game_ui *ui)
1076{
1077 sfree(ui->tiles);
1078 sfree(ui);
1079}
1080
1081static char *encode_ui(const game_ui *ui)
1082{
1083 return NULL;
1084}
1085
1086static void decode_ui(game_ui *ui, const char *encoding)
1087{
1088}
1089
1090static void sel_clear(game_ui *ui, const game_state *state)
1091{
1092 int i;
1093
1094 for (i = 0; i < state->n; i++)
1095 ui->tiles[i] &= ~TILE_SELECTED;
1096 ui->nselected = 0;
1097}
1098
1099
1100static void game_changed_state(game_ui *ui, const game_state *oldstate,
1101 const game_state *newstate)
1102{
1103 sel_clear(ui, newstate);
1104
1105 /*
1106 * If the game state has just changed into an unplayable one
1107 * (either completed or impossible), we vanish the keyboard-
1108 * control cursor.
1109 */
1110 if (newstate->complete || newstate->impossible)
1111 ui->displaysel = 0;
1112}
1113
1114static char *sel_movedesc(game_ui *ui, const game_state *state)
1115{
1116 int i;
1117 char *ret, *sep, buf[80];
1118 int retlen, retsize;
1119
1120 retsize = 256;
1121 ret = snewn(retsize, char);
1122 retlen = 0;
1123 ret[retlen++] = 'M';
1124 sep = "";
1125
1126 for (i = 0; i < state->n; i++) {
1127 if (ui->tiles[i] & TILE_SELECTED) {
1128 sprintf(buf, "%s%d", sep, i);
1129 sep = ",";
1130 if (retlen + (int)strlen(buf) >= retsize) {
1131 retsize = retlen + strlen(buf) + 256;
1132 ret = sresize(ret, retsize, char);
1133 }
1134 strcpy(ret + retlen, buf);
1135 retlen += strlen(buf);
1136
1137 ui->tiles[i] &= ~TILE_SELECTED;
1138 }
1139 }
1140 ui->nselected = 0;
1141
1142 assert(retlen < retsize);
1143 ret[retlen++] = '\0';
1144 return sresize(ret, retlen, char);
1145}
1146
1147static void sel_expand(game_ui *ui, const game_state *state, int tx, int ty)
1148{
1149 int ns = 1, nadded, x, y, c;
1150
1151 TILE(ui,tx,ty) |= TILE_SELECTED;
1152 do {
1153 nadded = 0;
1154
1155 for (x = 0; x < state->params.w; x++) {
1156 for (y = 0; y < state->params.h; y++) {
1157 if (x == tx && y == ty) continue;
1158 if (ISSEL(ui,x,y)) continue;
1159
1160 c = COL(state,x,y);
1161 if ((x > 0) &&
1162 ISSEL(ui,x-1,y) && COL(state,x-1,y) == c) {
1163 TILE(ui,x,y) |= TILE_SELECTED;
1164 nadded++;
1165 continue;
1166 }
1167
1168 if ((x+1 < state->params.w) &&
1169 ISSEL(ui,x+1,y) && COL(state,x+1,y) == c) {
1170 TILE(ui,x,y) |= TILE_SELECTED;
1171 nadded++;
1172 continue;
1173 }
1174
1175 if ((y > 0) &&
1176 ISSEL(ui,x,y-1) && COL(state,x,y-1) == c) {
1177 TILE(ui,x,y) |= TILE_SELECTED;
1178 nadded++;
1179 continue;
1180 }
1181
1182 if ((y+1 < state->params.h) &&
1183 ISSEL(ui,x,y+1) && COL(state,x,y+1) == c) {
1184 TILE(ui,x,y) |= TILE_SELECTED;
1185 nadded++;
1186 continue;
1187 }
1188 }
1189 }
1190 ns += nadded;
1191 } while (nadded > 0);
1192
1193 if (ns > 1) {
1194 ui->nselected = ns;
1195 } else {
1196 sel_clear(ui, state);
1197 }
1198}
1199
1200static int sg_emptycol(game_state *ret, int x)
1201{
1202 int y;
1203 for (y = 0; y < ret->params.h; y++) {
1204 if (COL(ret,x,y)) return 0;
1205 }
1206 return 1;
1207}
1208
1209
1210static void sg_snuggle(game_state *ret)
1211{
1212 int x,y, ndone;
1213
1214 /* make all unsupported tiles fall down. */
1215 do {
1216 ndone = 0;
1217 for (x = 0; x < ret->params.w; x++) {
1218 for (y = ret->params.h-1; y > 0; y--) {
1219 if (COL(ret,x,y) != 0) continue;
1220 if (COL(ret,x,y-1) != 0) {
1221 SWAPTILE(ret,x,y,x,y-1);
1222 ndone++;
1223 }
1224 }
1225 }
1226 } while (ndone);
1227
1228 /* shuffle all columns as far left as they can go. */
1229 do {
1230 ndone = 0;
1231 for (x = 0; x < ret->params.w-1; x++) {
1232 if (sg_emptycol(ret,x) && !sg_emptycol(ret,x+1)) {
1233 ndone++;
1234 for (y = 0; y < ret->params.h; y++) {
1235 SWAPTILE(ret,x,y,x+1,y);
1236 }
1237 }
1238 }
1239 } while (ndone);
1240}
1241
1242static void sg_check(game_state *ret)
1243{
1244 int x,y, complete = 1, impossible = 1;
1245
1246 for (x = 0; x < ret->params.w; x++) {
1247 for (y = 0; y < ret->params.h; y++) {
1248 if (COL(ret,x,y) == 0)
1249 continue;
1250 complete = 0;
1251 if (x+1 < ret->params.w) {
1252 if (COL(ret,x,y) == COL(ret,x+1,y))
1253 impossible = 0;
1254 }
1255 if (y+1 < ret->params.h) {
1256 if (COL(ret,x,y) == COL(ret,x,y+1))
1257 impossible = 0;
1258 }
1259 }
1260 }
1261 ret->complete = complete;
1262 ret->impossible = impossible;
1263}
1264
1265struct game_drawstate {
1266 int started, bgcolour;
1267 int tileinner, tilegap;
1268 int *tiles; /* contains colour and SELECTED. */
1269};
1270
1271static char *interpret_move(const game_state *state, game_ui *ui,
1272 const game_drawstate *ds,
1273 int x, int y, int button)
1274{
1275 int tx, ty;
1276 char *ret = "";
1277
1278 ui->displaysel = 0;
1279
1280 if (button == RIGHT_BUTTON || button == LEFT_BUTTON) {
1281 tx = FROMCOORD(x); ty= FROMCOORD(y);
1282 } else if (IS_CURSOR_MOVE(button)) {
1283 int dx = 0, dy = 0;
1284 ui->displaysel = 1;
1285 dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0);
1286 dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0);
1287 ui->xsel = (ui->xsel + state->params.w + dx) % state->params.w;
1288 ui->ysel = (ui->ysel + state->params.h + dy) % state->params.h;
1289 return ret;
1290 } else if (IS_CURSOR_SELECT(button)) {
1291 ui->displaysel = 1;
1292 tx = ui->xsel;
1293 ty = ui->ysel;
1294 } else
1295 return NULL;
1296
1297 if (tx < 0 || tx >= state->params.w || ty < 0 || ty >= state->params.h)
1298 return NULL;
1299 if (COL(state, tx, ty) == 0) return NULL;
1300
1301 if (ISSEL(ui,tx,ty)) {
1302 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2)
1303 sel_clear(ui, state);
1304 else
1305 ret = sel_movedesc(ui, state);
1306 } else {
1307 sel_clear(ui, state); /* might be no-op */
1308 sel_expand(ui, state, tx, ty);
1309 }
1310
1311 return ret;
1312}
1313
1314static game_state *execute_move(const game_state *from, const char *move)
1315{
1316 int i, n;
1317 game_state *ret;
1318
1319 if (move[0] == 'M') {
1320 ret = dup_game(from);
1321
1322 n = 0;
1323 move++;
1324
1325 while (*move) {
1326 i = atoi(move);
1327 if (i < 0 || i >= ret->n) {
1328 free_game(ret);
1329 return NULL;
1330 }
1331 n++;
1332 ret->tiles[i] = 0;
1333
1334 while (*move && isdigit((unsigned char)*move)) move++;
1335 if (*move == ',') move++;
1336 }
1337
1338 ret->score += npoints(&ret->params, n);
1339
1340 sg_snuggle(ret); /* shifts blanks down and to the left */
1341 sg_check(ret); /* checks for completeness or impossibility */
1342
1343 return ret;
1344 } else
1345 return NULL; /* couldn't parse move string */
1346}
1347
1348/* ----------------------------------------------------------------------
1349 * Drawing routines.
1350 */
1351
1352static void game_set_size(drawing *dr, game_drawstate *ds,
1353 const game_params *params, int tilesize)
1354{
1355 ds->tilegap = 2;
1356 ds->tileinner = tilesize - ds->tilegap;
1357}
1358
1359static void game_compute_size(const game_params *params, int tilesize,
1360 int *x, int *y)
1361{
1362 /* Ick: fake up tile size variables for macro expansion purposes */
1363 game_drawstate ads, *ds = &ads;
1364 game_set_size(NULL, ds, params, tilesize);
1365
1366 *x = TILE_SIZE * params->w + 2 * BORDER - TILE_GAP;
1367 *y = TILE_SIZE * params->h + 2 * BORDER - TILE_GAP;
1368}
1369
1370static float *game_colours(frontend *fe, int *ncolours)
1371{
1372 float *ret = snewn(3 * NCOLOURS, float);
1373
1374 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1375
1376 ret[COL_1 * 3 + 0] = 0.0F;
1377 ret[COL_1 * 3 + 1] = 0.0F;
1378 ret[COL_1 * 3 + 2] = 1.0F;
1379
1380 ret[COL_2 * 3 + 0] = 0.0F;
1381 ret[COL_2 * 3 + 1] = 0.5F;
1382 ret[COL_2 * 3 + 2] = 0.0F;
1383
1384 ret[COL_3 * 3 + 0] = 1.0F;
1385 ret[COL_3 * 3 + 1] = 0.0F;
1386 ret[COL_3 * 3 + 2] = 0.0F;
1387
1388 ret[COL_4 * 3 + 0] = 1.0F;
1389 ret[COL_4 * 3 + 1] = 1.0F;
1390 ret[COL_4 * 3 + 2] = 0.0F;
1391
1392 ret[COL_5 * 3 + 0] = 1.0F;
1393 ret[COL_5 * 3 + 1] = 0.0F;
1394 ret[COL_5 * 3 + 2] = 1.0F;
1395
1396 ret[COL_6 * 3 + 0] = 0.0F;
1397 ret[COL_6 * 3 + 1] = 1.0F;
1398 ret[COL_6 * 3 + 2] = 1.0F;
1399
1400 ret[COL_7 * 3 + 0] = 0.5F;
1401 ret[COL_7 * 3 + 1] = 0.5F;
1402 ret[COL_7 * 3 + 2] = 1.0F;
1403
1404 ret[COL_8 * 3 + 0] = 0.5F;
1405 ret[COL_8 * 3 + 1] = 1.0F;
1406 ret[COL_8 * 3 + 2] = 0.5F;
1407
1408 ret[COL_9 * 3 + 0] = 1.0F;
1409 ret[COL_9 * 3 + 1] = 0.5F;
1410 ret[COL_9 * 3 + 2] = 0.5F;
1411
1412 ret[COL_IMPOSSIBLE * 3 + 0] = 0.0F;
1413 ret[COL_IMPOSSIBLE * 3 + 1] = 0.0F;
1414 ret[COL_IMPOSSIBLE * 3 + 2] = 0.0F;
1415
1416 ret[COL_SEL * 3 + 0] = 1.0F;
1417 ret[COL_SEL * 3 + 1] = 1.0F;
1418 ret[COL_SEL * 3 + 2] = 1.0F;
1419
1420 ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
1421 ret[COL_HIGHLIGHT * 3 + 1] = 1.0F;
1422 ret[COL_HIGHLIGHT * 3 + 2] = 1.0F;
1423
1424 ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F;
1425 ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F;
1426 ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F;
1427
1428 *ncolours = NCOLOURS;
1429 return ret;
1430}
1431
1432static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1433{
1434 struct game_drawstate *ds = snew(struct game_drawstate);
1435 int i;
1436
1437 ds->started = 0;
1438 ds->tileinner = ds->tilegap = 0; /* not decided yet */
1439 ds->tiles = snewn(state->n, int);
1440 ds->bgcolour = -1;
1441 for (i = 0; i < state->n; i++)
1442 ds->tiles[i] = -1;
1443
1444 return ds;
1445}
1446
1447static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1448{
1449 sfree(ds->tiles);
1450 sfree(ds);
1451}
1452
1453/* Drawing routing for the tile at (x,y) is responsible for drawing
1454 * itself and the gaps to its right and below. If we're the same colour
1455 * as the tile to our right, then we fill in the gap; ditto below, and if
1456 * both then we fill the teeny tiny square in the corner as well.
1457 */
1458
1459static void tile_redraw(drawing *dr, game_drawstate *ds,
1460 int x, int y, int dright, int dbelow,
1461 int tile, int bgcolour)
1462{
1463 int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK;
1464
1465 if (col) {
1466 if (tile & TILE_IMPOSSIBLE) {
1467 outer = col;
1468 inner = COL_IMPOSSIBLE;
1469 } else if (tile & TILE_SELECTED) {
1470 outer = COL_SEL;
1471 inner = col;
1472 } else {
1473 outer = inner = col;
1474 }
1475 }
1476 draw_rect(dr, COORD(x), COORD(y), TILE_INNER, TILE_INNER, outer);
1477 draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4,
1478 TILE_INNER/2, TILE_INNER/2, inner);
1479
1480 if (dright)
1481 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y), TILE_GAP, TILE_INNER,
1482 (tile & TILE_JOINRIGHT) ? outer : bgcolour);
1483 if (dbelow)
1484 draw_rect(dr, COORD(x), COORD(y)+TILE_INNER, TILE_INNER, TILE_GAP,
1485 (tile & TILE_JOINDOWN) ? outer : bgcolour);
1486 if (dright && dbelow)
1487 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER, TILE_GAP, TILE_GAP,
1488 (tile & TILE_JOINDIAG) ? outer : bgcolour);
1489
1490 if (tile & TILE_HASSEL) {
1491 int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5;
1492 int scol = (outer == COL_SEL) ? COL_LOWLIGHT : COL_HIGHLIGHT;
1493 draw_line(dr, sx, sy, sx+ssz, sy, scol);
1494 draw_line(dr, sx+ssz, sy, sx+ssz, sy+ssz, scol);
1495 draw_line(dr, sx+ssz, sy+ssz, sx, sy+ssz, scol);
1496 draw_line(dr, sx, sy+ssz, sx, sy, scol);
1497 }
1498
1499 draw_update(dr, COORD(x), COORD(y), TILE_SIZE, TILE_SIZE);
1500}
1501
1502static void game_redraw(drawing *dr, game_drawstate *ds,
1503 const game_state *oldstate, const game_state *state,
1504 int dir, const game_ui *ui,
1505 float animtime, float flashtime)
1506{
1507 int bgcolour, x, y;
1508
1509 /* This was entirely cloned from fifteen.c; it should probably be
1510 * moved into some generic 'draw-recessed-rectangle' utility fn. */
1511 if (!ds->started) {
1512 int coords[10];
1513
1514 draw_rect(dr, 0, 0,
1515 TILE_SIZE * state->params.w + 2 * BORDER,
1516 TILE_SIZE * state->params.h + 2 * BORDER, COL_BACKGROUND);
1517 draw_update(dr, 0, 0,
1518 TILE_SIZE * state->params.w + 2 * BORDER,
1519 TILE_SIZE * state->params.h + 2 * BORDER);
1520
1521 /*
1522 * Recessed area containing the whole puzzle.
1523 */
1524 coords[0] = COORD(state->params.w) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
1525 coords[1] = COORD(state->params.h) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
1526 coords[2] = COORD(state->params.w) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
1527 coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
1528 coords[4] = coords[2] - TILE_SIZE;
1529 coords[5] = coords[3] + TILE_SIZE;
1530 coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
1531 coords[9] = COORD(state->params.h) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
1532 coords[6] = coords[8] + TILE_SIZE;
1533 coords[7] = coords[9] - TILE_SIZE;
1534 draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
1535
1536 coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
1537 coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
1538 draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
1539
1540 ds->started = 1;
1541 }
1542
1543 if (flashtime > 0.0) {
1544 int frame = (int)(flashtime / FLASH_FRAME);
1545 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
1546 } else
1547 bgcolour = COL_BACKGROUND;
1548
1549 for (x = 0; x < state->params.w; x++) {
1550 for (y = 0; y < state->params.h; y++) {
1551 int i = (state->params.w * y) + x;
1552 int col = COL(state,x,y), tile = col;
1553 int dright = (x+1 < state->params.w);
1554 int dbelow = (y+1 < state->params.h);
1555
1556 tile |= ISSEL(ui,x,y);
1557 if (state->impossible)
1558 tile |= TILE_IMPOSSIBLE;
1559 if (dright && COL(state,x+1,y) == col)
1560 tile |= TILE_JOINRIGHT;
1561 if (dbelow && COL(state,x,y+1) == col)
1562 tile |= TILE_JOINDOWN;
1563 if ((tile & TILE_JOINRIGHT) && (tile & TILE_JOINDOWN) &&
1564 COL(state,x+1,y+1) == col)
1565 tile |= TILE_JOINDIAG;
1566
1567 if (ui->displaysel && ui->xsel == x && ui->ysel == y)
1568 tile |= TILE_HASSEL;
1569
1570 /* For now we're never expecting oldstate at all (because we have
1571 * no animation); when we do we might well want to be looking
1572 * at the tile colours from oldstate, not state. */
1573 if ((oldstate && COL(oldstate,x,y) != col) ||
1574 (ds->bgcolour != bgcolour) ||
1575 (tile != ds->tiles[i])) {
1576 tile_redraw(dr, ds, x, y, dright, dbelow, tile, bgcolour);
1577 ds->tiles[i] = tile;
1578 }
1579 }
1580 }
1581 ds->bgcolour = bgcolour;
1582
1583 {
1584 char status[255], score[80];
1585
1586 sprintf(score, "Score: %d", state->score);
1587
1588 if (state->complete)
1589 sprintf(status, "COMPLETE! %s", score);
1590 else if (state->impossible)
1591 sprintf(status, "Cannot move! %s", score);
1592 else if (ui->nselected)
1593 sprintf(status, "%s Selected: %d (%d)",
1594 score, ui->nselected, npoints(&state->params, ui->nselected));
1595 else
1596 sprintf(status, "%s", score);
1597 status_bar(dr, status);
1598 }
1599}
1600
1601static float game_anim_length(const game_state *oldstate,
1602 const game_state *newstate, int dir, game_ui *ui)
1603{
1604 return 0.0F;
1605}
1606
1607static float game_flash_length(const game_state *oldstate,
1608 const game_state *newstate, int dir, game_ui *ui)
1609{
1610 if ((!oldstate->complete && newstate->complete) ||
1611 (!oldstate->impossible && newstate->impossible))
1612 return 2 * FLASH_FRAME;
1613 else
1614 return 0.0F;
1615}
1616
1617static int game_status(const game_state *state)
1618{
1619 /*
1620 * Dead-end situations are assumed to be rescuable by Undo, so we
1621 * don't bother to identify them and return -1.
1622 */
1623 return state->complete ? +1 : 0;
1624}
1625
1626static int game_timing_state(const game_state *state, game_ui *ui)
1627{
1628 return TRUE;
1629}
1630
1631static void game_print_size(const game_params *params, float *x, float *y)
1632{
1633}
1634
1635static void game_print(drawing *dr, const game_state *state, int tilesize)
1636{
1637}
1638
1639#ifdef COMBINED
1640#define thegame samegame
1641#endif
1642
1643const struct game thegame = {
1644 "Same Game", "games.samegame", "samegame",
1645 default_params,
1646 game_fetch_preset, NULL,
1647 decode_params,
1648 encode_params,
1649 free_params,
1650 dup_params,
1651 TRUE, game_configure, custom_params,
1652 validate_params,
1653 new_game_desc,
1654 validate_desc,
1655 new_game,
1656 dup_game,
1657 free_game,
1658 FALSE, solve_game,
1659 TRUE, game_can_format_as_text_now, game_text_format,
1660 new_ui,
1661 free_ui,
1662 encode_ui,
1663 decode_ui,
1664 game_changed_state,
1665 interpret_move,
1666 execute_move,
1667 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1668 game_colours,
1669 game_new_drawstate,
1670 game_free_drawstate,
1671 game_redraw,
1672 game_anim_length,
1673 game_flash_length,
1674 game_status,
1675 FALSE, FALSE, game_print_size, game_print,
1676 TRUE, /* wants_statusbar */
1677 FALSE, game_timing_state,
1678 0, /* flags */
1679};
diff --git a/apps/plugins/puzzles/src/samegame.html b/apps/plugins/puzzles/src/samegame.html
new file mode 100644
index 0000000000..8861e4b77b
--- /dev/null
+++ b/apps/plugins/puzzles/src/samegame.html
@@ -0,0 +1,82 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Same Game</title>
7<link rel="previous" href="mines.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="flip.html">
12</head>
13<body>
14<p><a href="mines.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="flip.html">Next</a></p>
15<h1><a name="C13"></a>Chapter 13: <a name="i0"></a>Same Game</h1>
16<p>
17You have a grid of coloured squares, which you have to clear by highlighting contiguous regions of more than one coloured square; the larger the region you highlight, the more points you get (and the faster you clear the arena).
18</p>
19<p>
20If you clear the grid you win. If you end up with nothing but single squares (i.e., there are no more clickable regions left) you lose.
21</p>
22<p>
23Removing a region causes the rest of the grid to shuffle up: blocks that are suspended will fall down (first), and then empty columns are filled from the right.
24</p>
25<p>
26Same Game was contributed to this collection by James Harvey.
27</p>
28<h2><a name="S13.1"></a>13.1 <a name="i1"></a>Same Game controls</h2>
29<p>
30This game can be played with either the keyboard or the mouse.
31</p>
32<p>
33If you left-click an unselected region, it becomes selected (possibly clearing the current selection).
34</p>
35<p>
36If you left-click the selected region, it will be removed (and the rest of the grid shuffled immediately).
37</p>
38<p>
39If you right-click the selected region, it will be unselected.
40</p>
41<p>
42The cursor keys move a cursor around the grid. Pressing the Space or Enter keys while the cursor is in an unselected region selects it; pressing Space or Enter again removes it as above.
43</p>
44<p>
45(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
46</p>
47<h2><a name="S13.2"></a>13.2 <a name="i2"></a>Same Game parameters</h2>
48<p>
49These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
50</p>
51<dl><dt>
52<em>Width</em>, <em>Height</em>
53</dt>
54<dd>
55Size of grid in squares.
56</dd>
57<dt>
58<em>No. of colours</em>
59</dt>
60<dd>
61Number of different colours used to fill the grid; the more colours, the fewer large regions of colour and thus the more difficult it is to successfully clear the grid.
62</dd>
63<dt>
64<em>Scoring system</em>
65</dt>
66<dd>
67Controls the precise mechanism used for scoring. With the default system, &#8216;(n-2)^2&#8217;, only regions of three squares or more will score any points at all. With the alternative &#8216;(n-1)^2&#8217; system, regions of two squares score a point each, and larger regions score relatively more points.
68</dd>
69<dt>
70<em>Ensure solubility</em>
71</dt>
72<dd>
73If this option is ticked (the default state), generated grids will be guaranteed to have at least one solution.
74<p>
75If you turn it off, the game generator will not try to guarantee soluble grids; it will, however, still ensure that there are at least 2 squares of each colour on the grid at the start (since a grid with exactly one square of a given colour is <em>definitely</em> insoluble). Grids generated with this option disabled may contain more large areas of contiguous colour, leading to opportunities for higher scores; they can also take less time to generate.
76</p>
77
78</dd>
79</dl>
80
81<hr><address></address></body>
82</html>
diff --git a/apps/plugins/puzzles/src/signpost.R b/apps/plugins/puzzles/src/signpost.R
new file mode 100644
index 0000000000..09ea367d57
--- /dev/null
+++ b/apps/plugins/puzzles/src/signpost.R
@@ -0,0 +1,23 @@
1# -*- makefile -*-
2
3SIGNPOST_EXTRA = dsf
4
5signpost : [X] GTK COMMON signpost SIGNPOST_EXTRA signpost-icon|no-icon
6signpost : [G] WINDOWS COMMON signpost SIGNPOST_EXTRA signpost.res|noicon.res
7
8signpostsolver : [U] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE m.lib
9signpostsolver : [C] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE
10
11ALL += signpost[COMBINED] SIGNPOST_EXTRA
12
13!begin am gtk
14GAMES += signpost
15!end
16
17!begin >list.c
18 A(signpost) \
19!end
20
21!begin >gamedesc.txt
22signpost:signpost.exe:Signpost:Square-connecting puzzle:Connect the squares into a path following the arrows.
23!end
diff --git a/apps/plugins/puzzles/src/signpost.c b/apps/plugins/puzzles/src/signpost.c
new file mode 100644
index 0000000000..ca72768c27
--- /dev/null
+++ b/apps/plugins/puzzles/src/signpost.c
@@ -0,0 +1,2480 @@
1/*
2 * signpost.c: implementation of the janko game 'arrow path'
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13
14#define PREFERRED_TILE_SIZE 48
15#define TILE_SIZE (ds->tilesize)
16#define BLITTER_SIZE TILE_SIZE
17#define BORDER (TILE_SIZE / 2)
18
19#define COORD(x) ( (x) * TILE_SIZE + BORDER )
20#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
21
22#define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
23
24#define FLASH_SPIN 0.7F
25
26#define NBACKGROUNDS 16
27
28enum {
29 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
30 COL_GRID, COL_CURSOR, COL_ERROR, COL_DRAG_ORIGIN,
31 COL_ARROW, COL_ARROW_BG_DIM,
32 COL_NUMBER, COL_NUMBER_SET, COL_NUMBER_SET_MID,
33 COL_B0, /* background colours */
34 COL_M0 = COL_B0 + 1*NBACKGROUNDS, /* mid arrow colours */
35 COL_D0 = COL_B0 + 2*NBACKGROUNDS, /* dim arrow colours */
36 COL_X0 = COL_B0 + 3*NBACKGROUNDS, /* dim arrow colours */
37 NCOLOURS = COL_B0 + 4*NBACKGROUNDS
38};
39
40struct game_params {
41 int w, h;
42 int force_corner_start;
43};
44
45enum { DIR_N = 0, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW, DIR_MAX };
46static const char *dirstrings[8] = { "N ", "NE", "E ", "SE", "S ", "SW", "W ", "NW" };
47
48static const int dxs[DIR_MAX] = { 0, 1, 1, 1, 0, -1, -1, -1 };
49static const int dys[DIR_MAX] = { -1, -1, 0, 1, 1, 1, 0, -1 };
50
51#define DIR_OPPOSITE(d) ((d+4)%8)
52
53struct game_state {
54 int w, h, n;
55 int completed, used_solve, impossible;
56 int *dirs; /* direction enums, size n */
57 int *nums; /* numbers, size n */
58 unsigned int *flags; /* flags, size n */
59 int *next, *prev; /* links to other cell indexes, size n (-1 absent) */
60 int *dsf; /* connects regions with a dsf. */
61 int *numsi; /* for each number, which index is it in? (-1 absent) */
62};
63
64#define FLAG_IMMUTABLE 1
65#define FLAG_ERROR 2
66
67/* --- Generally useful functions --- */
68
69#define ISREALNUM(state, num) ((num) > 0 && (num) <= (state)->n)
70
71static int whichdir(int fromx, int fromy, int tox, int toy)
72{
73 int i, dx, dy;
74
75 dx = tox - fromx;
76 dy = toy - fromy;
77
78 if (dx && dy && abs(dx) != abs(dy)) return -1;
79
80 if (dx) dx = dx / abs(dx); /* limit to (-1, 0, 1) */
81 if (dy) dy = dy / abs(dy); /* ditto */
82
83 for (i = 0; i < DIR_MAX; i++) {
84 if (dx == dxs[i] && dy == dys[i]) return i;
85 }
86 return -1;
87}
88
89static int whichdiri(game_state *state, int fromi, int toi)
90{
91 int w = state->w;
92 return whichdir(fromi%w, fromi/w, toi%w, toi/w);
93}
94
95static int ispointing(const game_state *state, int fromx, int fromy,
96 int tox, int toy)
97{
98 int w = state->w, dir = state->dirs[fromy*w+fromx];
99
100 /* (by convention) squares do not point to themselves. */
101 if (fromx == tox && fromy == toy) return 0;
102
103 /* the final number points to nothing. */
104 if (state->nums[fromy*w + fromx] == state->n) return 0;
105
106 while (1) {
107 if (!INGRID(state, fromx, fromy)) return 0;
108 if (fromx == tox && fromy == toy) return 1;
109 fromx += dxs[dir]; fromy += dys[dir];
110 }
111 return 0; /* not reached */
112}
113
114static int ispointingi(game_state *state, int fromi, int toi)
115{
116 int w = state->w;
117 return ispointing(state, fromi%w, fromi/w, toi%w, toi/w);
118}
119
120/* Taking the number 'num', work out the gap between it and the next
121 * available number up or down (depending on d). Return 1 if the region
122 * at (x,y) will fit in that gap, or 0 otherwise. */
123static int move_couldfit(const game_state *state, int num, int d, int x, int y)
124{
125 int n, gap, i = y*state->w+x, sz;
126
127 assert(d != 0);
128 /* The 'gap' is the number of missing numbers in the grid between
129 * our number and the next one in the sequence (up or down), or
130 * the end of the sequence (if we happen not to have 1/n present) */
131 for (n = num + d, gap = 0;
132 ISREALNUM(state, n) && state->numsi[n] == -1;
133 n += d, gap++) ; /* empty loop */
134
135 if (gap == 0) {
136 /* no gap, so the only allowable move is that that directly
137 * links the two numbers. */
138 n = state->nums[i];
139 return (n == num+d) ? 0 : 1;
140 }
141 if (state->prev[i] == -1 && state->next[i] == -1)
142 return 1; /* single unconnected square, always OK */
143
144 sz = dsf_size(state->dsf, i);
145 return (sz > gap) ? 0 : 1;
146}
147
148static int isvalidmove(const game_state *state, int clever,
149 int fromx, int fromy, int tox, int toy)
150{
151 int w = state->w, from = fromy*w+fromx, to = toy*w+tox;
152 int nfrom, nto;
153
154 if (!INGRID(state, fromx, fromy) || !INGRID(state, tox, toy))
155 return 0;
156
157 /* can only move where we point */
158 if (!ispointing(state, fromx, fromy, tox, toy))
159 return 0;
160
161 nfrom = state->nums[from]; nto = state->nums[to];
162
163 /* can't move _from_ the preset final number, or _to_ the preset 1. */
164 if (((nfrom == state->n) && (state->flags[from] & FLAG_IMMUTABLE)) ||
165 ((nto == 1) && (state->flags[to] & FLAG_IMMUTABLE)))
166 return 0;
167
168 /* can't create a new connection between cells in the same region
169 * as that would create a loop. */
170 if (dsf_canonify(state->dsf, from) == dsf_canonify(state->dsf, to))
171 return 0;
172
173 /* if both cells are actual numbers, can't drag if we're not
174 * one digit apart. */
175 if (ISREALNUM(state, nfrom) && ISREALNUM(state, nto)) {
176 if (nfrom != nto-1)
177 return 0;
178 } else if (clever && ISREALNUM(state, nfrom)) {
179 if (!move_couldfit(state, nfrom, +1, tox, toy))
180 return 0;
181 } else if (clever && ISREALNUM(state, nto)) {
182 if (!move_couldfit(state, nto, -1, fromx, fromy))
183 return 0;
184 }
185
186 return 1;
187}
188
189static void makelink(game_state *state, int from, int to)
190{
191 if (state->next[from] != -1)
192 state->prev[state->next[from]] = -1;
193 state->next[from] = to;
194
195 if (state->prev[to] != -1)
196 state->next[state->prev[to]] = -1;
197 state->prev[to] = from;
198}
199
200static int game_can_format_as_text_now(const game_params *params)
201{
202 if (params->w * params->h >= 100) return 0;
203 return 1;
204}
205
206static char *game_text_format(const game_state *state)
207{
208 int len = state->h * 2 * (4*state->w + 1) + state->h + 2;
209 int x, y, i, num, n, set;
210 char *ret, *p;
211
212 p = ret = snewn(len, char);
213
214 for (y = 0; y < state->h; y++) {
215 for (x = 0; x < state->h; x++) {
216 i = y*state->w+x;
217 *p++ = dirstrings[state->dirs[i]][0];
218 *p++ = dirstrings[state->dirs[i]][1];
219 *p++ = (state->flags[i] & FLAG_IMMUTABLE) ? 'I' : ' ';
220 *p++ = ' ';
221 }
222 *p++ = '\n';
223 for (x = 0; x < state->h; x++) {
224 i = y*state->w+x;
225 num = state->nums[i];
226 if (num == 0) {
227 *p++ = ' ';
228 *p++ = ' ';
229 *p++ = ' ';
230 } else {
231 n = num % (state->n+1);
232 set = num / (state->n+1);
233
234 assert(n <= 99); /* two digits only! */
235
236 if (set != 0)
237 *p++ = set+'a'-1;
238
239 *p++ = (n >= 10) ? ('0' + (n/10)) : ' ';
240 *p++ = '0' + (n%10);
241
242 if (set == 0)
243 *p++ = ' ';
244 }
245 *p++ = ' ';
246 }
247 *p++ = '\n';
248 *p++ = '\n';
249 }
250 *p++ = '\0';
251
252 return ret;
253}
254
255static void debug_state(const char *desc, game_state *state)
256{
257#ifdef DEBUGGING
258 char *dbg;
259 if (state->n >= 100) {
260 debug(("[ no game_text_format for this size ]"));
261 return;
262 }
263 dbg = game_text_format(state);
264 debug(("%s\n%s", desc, dbg));
265 sfree(dbg);
266#endif
267}
268
269
270static void strip_nums(game_state *state) {
271 int i;
272 for (i = 0; i < state->n; i++) {
273 if (!(state->flags[i] & FLAG_IMMUTABLE))
274 state->nums[i] = 0;
275 }
276 memset(state->next, -1, state->n*sizeof(int));
277 memset(state->prev, -1, state->n*sizeof(int));
278 memset(state->numsi, -1, (state->n+1)*sizeof(int));
279 dsf_init(state->dsf, state->n);
280}
281
282static int check_nums(game_state *orig, game_state *copy, int only_immutable)
283{
284 int i, ret = 1;
285 assert(copy->n == orig->n);
286 for (i = 0; i < copy->n; i++) {
287 if (only_immutable && !(copy->flags[i] & FLAG_IMMUTABLE)) continue;
288 assert(copy->nums[i] >= 0);
289 assert(copy->nums[i] <= copy->n);
290 if (copy->nums[i] != orig->nums[i]) {
291 debug(("check_nums: (%d,%d) copy=%d, orig=%d.",
292 i%orig->w, i/orig->w, copy->nums[i], orig->nums[i]));
293 ret = 0;
294 }
295 }
296 return ret;
297}
298
299/* --- Game parameter/presets functions --- */
300
301static game_params *default_params(void)
302{
303 game_params *ret = snew(game_params);
304 ret->w = ret->h = 4;
305 ret->force_corner_start = 1;
306
307 return ret;
308}
309
310static const struct game_params signpost_presets[] = {
311 { 4, 4, 1 },
312 { 4, 4, 0 },
313 { 5, 5, 1 },
314 { 5, 5, 0 },
315 { 6, 6, 1 },
316 { 7, 7, 1 }
317};
318
319static int game_fetch_preset(int i, char **name, game_params **params)
320{
321 game_params *ret;
322 char buf[80];
323
324 if (i < 0 || i >= lenof(signpost_presets))
325 return FALSE;
326
327 ret = default_params();
328 *ret = signpost_presets[i];
329 *params = ret;
330
331 sprintf(buf, "%dx%d%s", ret->w, ret->h,
332 ret->force_corner_start ? "" : ", free ends");
333 *name = dupstr(buf);
334
335 return TRUE;
336}
337
338static void free_params(game_params *params)
339{
340 sfree(params);
341}
342
343static game_params *dup_params(const game_params *params)
344{
345 game_params *ret = snew(game_params);
346 *ret = *params; /* structure copy */
347 return ret;
348}
349
350static void decode_params(game_params *ret, char const *string)
351{
352 ret->w = ret->h = atoi(string);
353 while (*string && isdigit((unsigned char)*string)) string++;
354 if (*string == 'x') {
355 string++;
356 ret->h = atoi(string);
357 while (*string && isdigit((unsigned char)*string)) string++;
358 }
359 ret->force_corner_start = 0;
360 if (*string == 'c') {
361 string++;
362 ret->force_corner_start = 1;
363 }
364
365}
366
367static char *encode_params(const game_params *params, int full)
368{
369 char data[256];
370
371 if (full)
372 sprintf(data, "%dx%d%s", params->w, params->h,
373 params->force_corner_start ? "c" : "");
374 else
375 sprintf(data, "%dx%d", params->w, params->h);
376
377 return dupstr(data);
378}
379
380static config_item *game_configure(const game_params *params)
381{
382 config_item *ret;
383 char buf[80];
384
385 ret = snewn(4, config_item);
386
387 ret[0].name = "Width";
388 ret[0].type = C_STRING;
389 sprintf(buf, "%d", params->w);
390 ret[0].sval = dupstr(buf);
391 ret[0].ival = 0;
392
393 ret[1].name = "Height";
394 ret[1].type = C_STRING;
395 sprintf(buf, "%d", params->h);
396 ret[1].sval = dupstr(buf);
397 ret[1].ival = 0;
398
399 ret[2].name = "Start and end in corners";
400 ret[2].type = C_BOOLEAN;
401 ret[2].sval = NULL;
402 ret[2].ival = params->force_corner_start;
403
404 ret[3].name = NULL;
405 ret[3].type = C_END;
406 ret[3].sval = NULL;
407 ret[3].ival = 0;
408
409 return ret;
410}
411
412static game_params *custom_params(const config_item *cfg)
413{
414 game_params *ret = snew(game_params);
415
416 ret->w = atoi(cfg[0].sval);
417 ret->h = atoi(cfg[1].sval);
418 ret->force_corner_start = cfg[2].ival;
419
420 return ret;
421}
422
423static char *validate_params(const game_params *params, int full)
424{
425 if (params->w < 1) return "Width must be at least one";
426 if (params->h < 1) return "Height must be at least one";
427 if (full && params->w == 1 && params->h == 1)
428 /* The UI doesn't let us move these from unsolved to solved,
429 * so we disallow generating (but not playing) them. */
430 return "Width and height cannot both be one";
431 return NULL;
432}
433
434/* --- Game description string generation and unpicking --- */
435
436static void blank_game_into(game_state *state)
437{
438 memset(state->dirs, 0, state->n*sizeof(int));
439 memset(state->nums, 0, state->n*sizeof(int));
440 memset(state->flags, 0, state->n*sizeof(unsigned int));
441 memset(state->next, -1, state->n*sizeof(int));
442 memset(state->prev, -1, state->n*sizeof(int));
443 memset(state->numsi, -1, (state->n+1)*sizeof(int));
444}
445
446static game_state *blank_game(int w, int h)
447{
448 game_state *state = snew(game_state);
449
450 memset(state, 0, sizeof(game_state));
451 state->w = w;
452 state->h = h;
453 state->n = w*h;
454
455 state->dirs = snewn(state->n, int);
456 state->nums = snewn(state->n, int);
457 state->flags = snewn(state->n, unsigned int);
458 state->next = snewn(state->n, int);
459 state->prev = snewn(state->n, int);
460 state->dsf = snew_dsf(state->n);
461 state->numsi = snewn(state->n+1, int);
462
463 blank_game_into(state);
464
465 return state;
466}
467
468static void dup_game_to(game_state *to, const game_state *from)
469{
470 to->completed = from->completed;
471 to->used_solve = from->used_solve;
472 to->impossible = from->impossible;
473
474 memcpy(to->dirs, from->dirs, to->n*sizeof(int));
475 memcpy(to->flags, from->flags, to->n*sizeof(unsigned int));
476 memcpy(to->nums, from->nums, to->n*sizeof(int));
477
478 memcpy(to->next, from->next, to->n*sizeof(int));
479 memcpy(to->prev, from->prev, to->n*sizeof(int));
480
481 memcpy(to->dsf, from->dsf, to->n*sizeof(int));
482 memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int));
483}
484
485static game_state *dup_game(const game_state *state)
486{
487 game_state *ret = blank_game(state->w, state->h);
488 dup_game_to(ret, state);
489 return ret;
490}
491
492static void free_game(game_state *state)
493{
494 sfree(state->dirs);
495 sfree(state->nums);
496 sfree(state->flags);
497 sfree(state->next);
498 sfree(state->prev);
499 sfree(state->dsf);
500 sfree(state->numsi);
501 sfree(state);
502}
503
504static void unpick_desc(const game_params *params, const char *desc,
505 game_state **sout, char **mout)
506{
507 game_state *state = blank_game(params->w, params->h);
508 char *msg = NULL, c;
509 int num = 0, i = 0;
510
511 while (*desc) {
512 if (i >= state->n) {
513 msg = "Game description longer than expected";
514 goto done;
515 }
516
517 c = *desc;
518 if (isdigit((unsigned char)c)) {
519 num = (num*10) + (int)(c-'0');
520 if (num > state->n) {
521 msg = "Number too large";
522 goto done;
523 }
524 } else if ((c-'a') >= 0 && (c-'a') < DIR_MAX) {
525 state->nums[i] = num;
526 state->flags[i] = num ? FLAG_IMMUTABLE : 0;
527 num = 0;
528
529 state->dirs[i] = c - 'a';
530 i++;
531 } else if (!*desc) {
532 msg = "Game description shorter than expected";
533 goto done;
534 } else {
535 msg = "Game description contains unexpected characters";
536 goto done;
537 }
538 desc++;
539 }
540 if (i < state->n) {
541 msg = "Game description shorter than expected";
542 goto done;
543 }
544
545done:
546 if (msg) { /* sth went wrong. */
547 if (mout) *mout = msg;
548 free_game(state);
549 } else {
550 if (mout) *mout = NULL;
551 if (sout) *sout = state;
552 else free_game(state);
553 }
554}
555
556static char *generate_desc(game_state *state, int issolve)
557{
558 char *ret, buf[80];
559 int retlen, i, k;
560
561 ret = NULL; retlen = 0;
562 if (issolve) {
563 ret = sresize(ret, 2, char);
564 ret[0] = 'S'; ret[1] = '\0';
565 retlen += 1;
566 }
567 for (i = 0; i < state->n; i++) {
568 if (state->nums[i])
569 k = sprintf(buf, "%d%c", state->nums[i], (int)(state->dirs[i]+'a'));
570 else
571 k = sprintf(buf, "%c", (int)(state->dirs[i]+'a'));
572 ret = sresize(ret, retlen + k + 1, char);
573 strcpy(ret + retlen, buf);
574 retlen += k;
575 }
576 return ret;
577}
578
579/* --- Game generation --- */
580
581/* Fills in preallocated arrays ai (indices) and ad (directions)
582 * showing all non-numbered cells adjacent to index i, returns length */
583/* This function has been somewhat optimised... */
584static int cell_adj(game_state *state, int i, int *ai, int *ad)
585{
586 int n = 0, a, x, y, sx, sy, dx, dy, newi;
587 int w = state->w, h = state->h;
588
589 sx = i % w; sy = i / w;
590
591 for (a = 0; a < DIR_MAX; a++) {
592 x = sx; y = sy;
593 dx = dxs[a]; dy = dys[a];
594 while (1) {
595 x += dx; y += dy;
596 if (x < 0 || y < 0 || x >= w || y >= h) break;
597
598 newi = y*w + x;
599 if (state->nums[newi] == 0) {
600 ai[n] = newi;
601 ad[n] = a;
602 n++;
603 }
604 }
605 }
606 return n;
607}
608
609static int new_game_fill(game_state *state, random_state *rs,
610 int headi, int taili)
611{
612 int nfilled, an, ret = 0, j;
613 int *aidx, *adir;
614
615 aidx = snewn(state->n, int);
616 adir = snewn(state->n, int);
617
618 debug(("new_game_fill: headi=%d, taili=%d.", headi, taili));
619
620 memset(state->nums, 0, state->n*sizeof(int));
621
622 state->nums[headi] = 1;
623 state->nums[taili] = state->n;
624
625 state->dirs[taili] = 0;
626 nfilled = 2;
627 assert(state->n > 1);
628
629 while (nfilled < state->n) {
630 /* Try and expand _from_ headi; keep going if there's only one
631 * place to go to. */
632 an = cell_adj(state, headi, aidx, adir);
633 do {
634 if (an == 0) goto done;
635 j = random_upto(rs, an);
636 state->dirs[headi] = adir[j];
637 state->nums[aidx[j]] = state->nums[headi] + 1;
638 nfilled++;
639 headi = aidx[j];
640 an = cell_adj(state, headi, aidx, adir);
641 } while (an == 1);
642
643 if (nfilled == state->n) break;
644
645 /* Try and expand _to_ taili; keep going if there's only one
646 * place to go to. */
647 an = cell_adj(state, taili, aidx, adir);
648 do {
649 if (an == 0) goto done;
650 j = random_upto(rs, an);
651 state->dirs[aidx[j]] = DIR_OPPOSITE(adir[j]);
652 state->nums[aidx[j]] = state->nums[taili] - 1;
653 nfilled++;
654 taili = aidx[j];
655 an = cell_adj(state, taili, aidx, adir);
656 } while (an == 1);
657 }
658 /* If we get here we have headi and taili set but unconnected
659 * by direction: we need to set headi's direction so as to point
660 * at taili. */
661 state->dirs[headi] = whichdiri(state, headi, taili);
662
663 /* it could happen that our last two weren't in line; if that's the
664 * case, we have to start again. */
665 if (state->dirs[headi] != -1) ret = 1;
666
667done:
668 sfree(aidx);
669 sfree(adir);
670 return ret;
671}
672
673/* Better generator: with the 'generate, sprinkle numbers, solve,
674 * repeat' algorithm we're _never_ generating anything greater than
675 * 6x6, and spending all of our time in new_game_fill (and very little
676 * in solve_state).
677 *
678 * So, new generator steps:
679 * generate the grid, at random (same as now). Numbers 1 and N get
680 immutable flag immediately.
681 * squirrel that away for the solved state.
682 *
683 * (solve:) Try and solve it.
684 * If we solved it, we're done:
685 * generate the description from current immutable numbers,
686 * free stuff that needs freeing,
687 * return description + solved state.
688 * If we didn't solve it:
689 * count #tiles in state we've made deductions about.
690 * while (1):
691 * randomise a scratch array.
692 * for each index in scratch (in turn):
693 * if the cell isn't empty, continue (through scratch array)
694 * set number + immutable in state.
695 * try and solve state.
696 * if we've solved it, we're done.
697 * otherwise, count #tiles. If it's more than we had before:
698 * good, break from this loop and re-randomise.
699 * otherwise (number didn't help):
700 * remove number and try next in scratch array.
701 * if we've got to the end of the scratch array, no luck:
702 free everything we need to, and go back to regenerate the grid.
703 */
704
705static int solve_state(game_state *state);
706
707static void debug_desc(const char *what, game_state *state)
708{
709#if DEBUGGING
710 {
711 char *desc = generate_desc(state, 0);
712 debug(("%s game state: %dx%d:%s", what, state->w, state->h, desc));
713 sfree(desc);
714 }
715#endif
716}
717
718/* Expects a fully-numbered game_state on input, and makes sure
719 * FLAG_IMMUTABLE is only set on those numbers we need to solve
720 * (as for a real new-game); returns 1 if it managed
721 * this (such that it could solve it), or 0 if not. */
722static int new_game_strip(game_state *state, random_state *rs)
723{
724 int *scratch, i, j, ret = 1;
725 game_state *copy = dup_game(state);
726
727 debug(("new_game_strip."));
728
729 strip_nums(copy);
730 debug_desc("Stripped", copy);
731
732 if (solve_state(copy) > 0) {
733 debug(("new_game_strip: soluble immediately after strip."));
734 free_game(copy);
735 return 1;
736 }
737
738 scratch = snewn(state->n, int);
739 for (i = 0; i < state->n; i++) scratch[i] = i;
740 shuffle(scratch, state->n, sizeof(int), rs);
741
742 /* This is scungy. It might just be quick enough.
743 * It goes through, adding set numbers in empty squares
744 * until either we run out of empty squares (in the one
745 * we're half-solving) or else we solve it properly.
746 * NB that we run the entire solver each time, which
747 * strips the grid beforehand; we will save time if we
748 * avoid that. */
749 for (i = 0; i < state->n; i++) {
750 j = scratch[i];
751 if (copy->nums[j] > 0 && copy->nums[j] <= state->n)
752 continue; /* already solved to a real number here. */
753 assert(state->nums[j] <= state->n);
754 debug(("new_game_strip: testing add IMMUTABLE number %d at square (%d,%d).",
755 state->nums[j], j%state->w, j/state->w));
756 copy->nums[j] = state->nums[j];
757 copy->flags[j] |= FLAG_IMMUTABLE;
758 state->flags[j] |= FLAG_IMMUTABLE;
759 debug_state("Copy of state: ", copy);
760 strip_nums(copy);
761 if (solve_state(copy) > 0) goto solved;
762 assert(check_nums(state, copy, 1));
763 }
764 ret = 0;
765 goto done;
766
767solved:
768 debug(("new_game_strip: now solved."));
769 /* Since we added basically at random, try now to remove numbers
770 * and see if we can still solve it; if we can (still), really
771 * remove the number. Make sure we don't remove the anchor numbers
772 * 1 and N. */
773 for (i = 0; i < state->n; i++) {
774 j = scratch[i];
775 if ((state->flags[j] & FLAG_IMMUTABLE) &&
776 (state->nums[j] != 1 && state->nums[j] != state->n)) {
777 debug(("new_game_strip: testing remove IMMUTABLE number %d at square (%d,%d).",
778 state->nums[j], j%state->w, j/state->w));
779 state->flags[j] &= ~FLAG_IMMUTABLE;
780 dup_game_to(copy, state);
781 strip_nums(copy);
782 if (solve_state(copy) > 0) {
783 assert(check_nums(state, copy, 0));
784 debug(("new_game_strip: OK, removing number"));
785 } else {
786 assert(state->nums[j] <= state->n);
787 debug(("new_game_strip: cannot solve, putting IMMUTABLE back."));
788 copy->nums[j] = state->nums[j];
789 state->flags[j] |= FLAG_IMMUTABLE;
790 }
791 }
792 }
793
794done:
795 debug(("new_game_strip: %ssuccessful.", ret ? "" : "not "));
796 sfree(scratch);
797 free_game(copy);
798 return ret;
799}
800
801static char *new_game_desc(const game_params *params, random_state *rs,
802 char **aux, int interactive)
803{
804 game_state *state = blank_game(params->w, params->h);
805 char *ret;
806 int headi, taili;
807
808 /* this shouldn't happen (validate_params), but let's play it safe */
809 if (params->w == 1 && params->h == 1) return dupstr("1a");
810
811generate:
812 blank_game_into(state);
813
814 /* keep trying until we fill successfully. */
815 do {
816 if (params->force_corner_start) {
817 headi = 0;
818 taili = state->n-1;
819 } else {
820 do {
821 headi = random_upto(rs, state->n);
822 taili = random_upto(rs, state->n);
823 } while (headi == taili);
824 }
825 } while (!new_game_fill(state, rs, headi, taili));
826
827 debug_state("Filled game:", state);
828
829 assert(state->nums[headi] <= state->n);
830 assert(state->nums[taili] <= state->n);
831
832 state->flags[headi] |= FLAG_IMMUTABLE;
833 state->flags[taili] |= FLAG_IMMUTABLE;
834
835 /* This will have filled in directions and _all_ numbers.
836 * Store the game definition for this, as the solved-state. */
837 if (!new_game_strip(state, rs)) {
838 goto generate;
839 }
840 strip_nums(state);
841 {
842 game_state *tosolve = dup_game(state);
843 assert(solve_state(tosolve) > 0);
844 free_game(tosolve);
845 }
846 ret = generate_desc(state, 0);
847 free_game(state);
848 return ret;
849}
850
851static char *validate_desc(const game_params *params, const char *desc)
852{
853 char *ret = NULL;
854
855 unpick_desc(params, desc, NULL, &ret);
856 return ret;
857}
858
859/* --- Linked-list and numbers array --- */
860
861/* Assuming numbers are always up-to-date, there are only four possibilities
862 * for regions changing after a single valid move:
863 *
864 * 1) two differently-coloured regions being combined (the resulting colouring
865 * should be based on the larger of the two regions)
866 * 2) a numbered region having a single number added to the start (the
867 * region's colour will remain, and the numbers will shift by 1)
868 * 3) a numbered region having a single number added to the end (the
869 * region's colour and numbering remains as-is)
870 * 4) two unnumbered squares being joined (will pick the smallest unused set
871 * of colours to use for the new region).
872 *
873 * There should never be any complications with regions containing 3 colours
874 * being combined, since two of those colours should have been merged on a
875 * previous move.
876 *
877 * Most of the complications are in ensuring we don't accidentally set two
878 * regions with the same colour (e.g. if a region was split). If this happens
879 * we always try and give the largest original portion the original colour.
880 */
881
882#define COLOUR(a) ((a) / (state->n+1))
883#define START(c) ((c) * (state->n+1))
884
885struct head_meta {
886 int i; /* position */
887 int sz; /* size of region */
888 int start; /* region start number preferred, or 0 if !preference */
889 int preference; /* 0 if we have no preference (and should just pick one) */
890 const char *why;
891};
892
893static void head_number(game_state *state, int i, struct head_meta *head)
894{
895 int off = 0, ss, j = i, c, n, sz;
896
897 /* Insist we really were passed the head of a chain. */
898 assert(state->prev[i] == -1 && state->next[i] != -1);
899
900 head->i = i;
901 head->sz = dsf_size(state->dsf, i);
902 head->why = NULL;
903
904 /* Search through this chain looking for real numbers, checking that
905 * they match up (if there are more than one). */
906 head->preference = 0;
907 while (j != -1) {
908 if (state->flags[j] & FLAG_IMMUTABLE) {
909 ss = state->nums[j] - off;
910 if (!head->preference) {
911 head->start = ss;
912 head->preference = 1;
913 head->why = "contains cell with immutable number";
914 } else if (head->start != ss) {
915 debug(("head_number: chain with non-sequential numbers!"));
916 state->impossible = 1;
917 }
918 }
919 off++;
920 j = state->next[j];
921 assert(j != i); /* we have created a loop, obviously wrong */
922 }
923 if (head->preference) goto done;
924
925 if (state->nums[i] == 0 && state->nums[state->next[i]] > state->n) {
926 /* (probably) empty cell onto the head of a coloured region:
927 * make sure we start at a 0 offset. */
928 head->start = START(COLOUR(state->nums[state->next[i]]));
929 head->preference = 1;
930 head->why = "adding blank cell to head of numbered region";
931 } else if (state->nums[i] <= state->n) {
932 /* if we're 0 we're probably just blank -- but even if we're a
933 * (real) numbered region, we don't have an immutable number
934 * in it (any more) otherwise it'd have been caught above, so
935 * reassign the colour. */
936 head->start = 0;
937 head->preference = 0;
938 head->why = "lowest available colour group";
939 } else {
940 c = COLOUR(state->nums[i]);
941 n = 1;
942 sz = dsf_size(state->dsf, i);
943 j = i;
944 while (state->next[j] != -1) {
945 j = state->next[j];
946 if (state->nums[j] == 0 && state->next[j] == -1) {
947 head->start = START(c);
948 head->preference = 1;
949 head->why = "adding blank cell to end of numbered region";
950 goto done;
951 }
952 if (COLOUR(state->nums[j]) == c)
953 n++;
954 else {
955 int start_alternate = START(COLOUR(state->nums[j]));
956 if (n < (sz - n)) {
957 head->start = start_alternate;
958 head->preference = 1;
959 head->why = "joining two coloured regions, swapping to larger colour";
960 } else {
961 head->start = START(c);
962 head->preference = 1;
963 head->why = "joining two coloured regions, taking largest";
964 }
965 goto done;
966 }
967 }
968 /* If we got here then we may have split a region into
969 * two; make sure we don't assign a colour we've already used. */
970 if (c == 0) {
971 /* not convinced this shouldn't be an assertion failure here. */
972 head->start = 0;
973 head->preference = 0;
974 } else {
975 head->start = START(c);
976 head->preference = 1;
977 }
978 head->why = "got to end of coloured region";
979 }
980
981done:
982 assert(head->why != NULL);
983 if (head->preference)
984 debug(("Chain at (%d,%d) numbered for preference at %d (colour %d): %s.",
985 head->i%state->w, head->i/state->w,
986 head->start, COLOUR(head->start), head->why));
987 else
988 debug(("Chain at (%d,%d) using next available colour: %s.",
989 head->i%state->w, head->i/state->w,
990 head->why));
991}
992
993#if 0
994static void debug_numbers(game_state *state)
995{
996 int i, w=state->w;
997
998 for (i = 0; i < state->n; i++) {
999 debug(("(%d,%d) --> (%d,%d) --> (%d,%d)",
1000 state->prev[i]==-1 ? -1 : state->prev[i]%w,
1001 state->prev[i]==-1 ? -1 : state->prev[i]/w,
1002 i%w, i/w,
1003 state->next[i]==-1 ? -1 : state->next[i]%w,
1004 state->next[i]==-1 ? -1 : state->next[i]/w));
1005 }
1006 w = w+1;
1007}
1008#endif
1009
1010static void connect_numbers(game_state *state)
1011{
1012 int i, di, dni;
1013
1014 dsf_init(state->dsf, state->n);
1015 for (i = 0; i < state->n; i++) {
1016 if (state->next[i] != -1) {
1017 assert(state->prev[state->next[i]] == i);
1018 di = dsf_canonify(state->dsf, i);
1019 dni = dsf_canonify(state->dsf, state->next[i]);
1020 if (di == dni) {
1021 debug(("connect_numbers: chain forms a loop."));
1022 state->impossible = 1;
1023 }
1024 dsf_merge(state->dsf, di, dni);
1025 }
1026 }
1027}
1028
1029static int compare_heads(const void *a, const void *b)
1030{
1031 struct head_meta *ha = (struct head_meta *)a;
1032 struct head_meta *hb = (struct head_meta *)b;
1033
1034 /* Heads with preferred colours first... */
1035 if (ha->preference && !hb->preference) return -1;
1036 if (hb->preference && !ha->preference) return 1;
1037
1038 /* ...then heads with low colours first... */
1039 if (ha->start < hb->start) return -1;
1040 if (ha->start > hb->start) return 1;
1041
1042 /* ... then large regions first... */
1043 if (ha->sz > hb->sz) return -1;
1044 if (ha->sz < hb->sz) return 1;
1045
1046 /* ... then position. */
1047 if (ha->i > hb->i) return -1;
1048 if (ha->i < hb->i) return 1;
1049
1050 return 0;
1051}
1052
1053static int lowest_start(game_state *state, struct head_meta *heads, int nheads)
1054{
1055 int n, c;
1056
1057 /* NB start at 1: colour 0 is real numbers */
1058 for (c = 1; c < state->n; c++) {
1059 for (n = 0; n < nheads; n++) {
1060 if (COLOUR(heads[n].start) == c)
1061 goto used;
1062 }
1063 return c;
1064used:
1065 ;
1066 }
1067 assert(!"No available colours!");
1068 return 0;
1069}
1070
1071static void update_numbers(game_state *state)
1072{
1073 int i, j, n, nnum, nheads;
1074 struct head_meta *heads = snewn(state->n, struct head_meta);
1075
1076 for (n = 0; n < state->n; n++)
1077 state->numsi[n] = -1;
1078
1079 for (i = 0; i < state->n; i++) {
1080 if (state->flags[i] & FLAG_IMMUTABLE) {
1081 assert(state->nums[i] > 0);
1082 assert(state->nums[i] <= state->n);
1083 state->numsi[state->nums[i]] = i;
1084 }
1085 else if (state->prev[i] == -1 && state->next[i] == -1)
1086 state->nums[i] = 0;
1087 }
1088 connect_numbers(state);
1089
1090 /* Construct an array of the heads of all current regions, together
1091 * with their preferred colours. */
1092 nheads = 0;
1093 for (i = 0; i < state->n; i++) {
1094 /* Look for a cell that is the start of a chain
1095 * (has a next but no prev). */
1096 if (state->prev[i] != -1 || state->next[i] == -1) continue;
1097
1098 head_number(state, i, &heads[nheads++]);
1099 }
1100
1101 /* Sort that array:
1102 * - heads with preferred colours first, then
1103 * - heads with low colours first, then
1104 * - large regions first
1105 */
1106 qsort(heads, nheads, sizeof(struct head_meta), compare_heads);
1107
1108 /* Remove duplicate-coloured regions. */
1109 for (n = nheads-1; n >= 0; n--) { /* order is important! */
1110 if ((n != 0) && (heads[n].start == heads[n-1].start)) {
1111 /* We have a duplicate-coloured region: since we're
1112 * sorted in size order and this is not the first
1113 * of its colour it's not the largest: recolour it. */
1114 heads[n].start = START(lowest_start(state, heads, nheads));
1115 heads[n].preference = -1; /* '-1' means 'was duplicate' */
1116 }
1117 else if (!heads[n].preference) {
1118 assert(heads[n].start == 0);
1119 heads[n].start = START(lowest_start(state, heads, nheads));
1120 }
1121 }
1122
1123 debug(("Region colouring after duplicate removal:"));
1124
1125 for (n = 0; n < nheads; n++) {
1126 debug((" Chain at (%d,%d) sz %d numbered at %d (colour %d): %s%s",
1127 heads[n].i % state->w, heads[n].i / state->w, heads[n].sz,
1128 heads[n].start, COLOUR(heads[n].start), heads[n].why,
1129 heads[n].preference == 0 ? " (next available)" :
1130 heads[n].preference < 0 ? " (duplicate, next available)" : ""));
1131
1132 nnum = heads[n].start;
1133 j = heads[n].i;
1134 while (j != -1) {
1135 if (!(state->flags[j] & FLAG_IMMUTABLE)) {
1136 if (nnum > 0 && nnum <= state->n)
1137 state->numsi[nnum] = j;
1138 state->nums[j] = nnum;
1139 }
1140 nnum++;
1141 j = state->next[j];
1142 assert(j != heads[n].i); /* loop?! */
1143 }
1144 }
1145 /*debug_numbers(state);*/
1146 sfree(heads);
1147}
1148
1149static int check_completion(game_state *state, int mark_errors)
1150{
1151 int n, j, k, error = 0, complete;
1152
1153 /* NB This only marks errors that are possible to perpetrate with
1154 * the current UI in interpret_move. Things like forming loops in
1155 * linked sections and having numbers not add up should be forbidden
1156 * by the code elsewhere, so we don't bother marking those (because
1157 * it would add lots of tricky drawing code for very little gain). */
1158 if (mark_errors) {
1159 for (j = 0; j < state->n; j++)
1160 state->flags[j] &= ~FLAG_ERROR;
1161 }
1162
1163 /* Search for repeated numbers. */
1164 for (j = 0; j < state->n; j++) {
1165 if (state->nums[j] > 0 && state->nums[j] <= state->n) {
1166 for (k = j+1; k < state->n; k++) {
1167 if (state->nums[k] == state->nums[j]) {
1168 if (mark_errors) {
1169 state->flags[j] |= FLAG_ERROR;
1170 state->flags[k] |= FLAG_ERROR;
1171 }
1172 error = 1;
1173 }
1174 }
1175 }
1176 }
1177
1178 /* Search and mark numbers n not pointing to n+1; if any numbers
1179 * are missing we know we've not completed. */
1180 complete = 1;
1181 for (n = 1; n < state->n; n++) {
1182 if (state->numsi[n] == -1 || state->numsi[n+1] == -1)
1183 complete = 0;
1184 else if (!ispointingi(state, state->numsi[n], state->numsi[n+1])) {
1185 if (mark_errors) {
1186 state->flags[state->numsi[n]] |= FLAG_ERROR;
1187 state->flags[state->numsi[n+1]] |= FLAG_ERROR;
1188 }
1189 error = 1;
1190 } else {
1191 /* make sure the link is explicitly made here; for instance, this
1192 * is nice if the user drags from 2 out (making 3) and a 4 is also
1193 * visible; this ensures that the link from 3 to 4 is also made. */
1194 if (mark_errors)
1195 makelink(state, state->numsi[n], state->numsi[n+1]);
1196 }
1197 }
1198
1199 /* Search and mark numbers less than 0, or 0 with links. */
1200 for (n = 1; n < state->n; n++) {
1201 if ((state->nums[n] < 0) ||
1202 (state->nums[n] == 0 &&
1203 (state->next[n] != -1 || state->prev[n] != -1))) {
1204 error = 1;
1205 if (mark_errors)
1206 state->flags[n] |= FLAG_ERROR;
1207 }
1208 }
1209
1210 if (error) return 0;
1211 return complete;
1212}
1213static game_state *new_game(midend *me, const game_params *params,
1214 const char *desc)
1215{
1216 game_state *state = NULL;
1217
1218 unpick_desc(params, desc, &state, NULL);
1219 if (!state) assert(!"new_game failed to unpick");
1220
1221 update_numbers(state);
1222 check_completion(state, 1); /* update any auto-links */
1223
1224 return state;
1225}
1226
1227/* --- Solver --- */
1228
1229/* If a tile has a single tile it can link _to_, or there's only a single
1230 * location that can link to a given tile, fill that link in. */
1231static int solve_single(game_state *state, game_state *copy, int *from)
1232{
1233 int i, j, sx, sy, x, y, d, poss, w=state->w, nlinks = 0;
1234
1235 /* The from array is a list of 'which square can link _to_ us';
1236 * we start off with from as '-1' (meaning 'not found'); if we find
1237 * something that can link to us it is set to that index, and then if
1238 * we find another we set it to -2. */
1239
1240 memset(from, -1, state->n*sizeof(int));
1241
1242 /* poss is 'can I link to anything' with the same meanings. */
1243
1244 for (i = 0; i < state->n; i++) {
1245 if (state->next[i] != -1) continue;
1246 if (state->nums[i] == state->n) continue; /* no next from last no. */
1247
1248 d = state->dirs[i];
1249 poss = -1;
1250 sx = x = i%w; sy = y = i/w;
1251 while (1) {
1252 x += dxs[d]; y += dys[d];
1253 if (!INGRID(state, x, y)) break;
1254 if (!isvalidmove(state, 1, sx, sy, x, y)) continue;
1255
1256 /* can't link to somewhere with a back-link we would have to
1257 * break (the solver just doesn't work like this). */
1258 j = y*w+x;
1259 if (state->prev[j] != -1) continue;
1260
1261 if (state->nums[i] > 0 && state->nums[j] > 0 &&
1262 state->nums[i] <= state->n && state->nums[j] <= state->n &&
1263 state->nums[j] == state->nums[i]+1) {
1264 debug(("Solver: forcing link through existing consecutive numbers."));
1265 poss = j;
1266 from[j] = i;
1267 break;
1268 }
1269
1270 /* if there's been a valid move already, we have to move on;
1271 * we can't make any deductions here. */
1272 poss = (poss == -1) ? j : -2;
1273
1274 /* Modify the from array as described above (which is enumerating
1275 * what points to 'j' in a similar way). */
1276 from[j] = (from[j] == -1) ? i : -2;
1277 }
1278 if (poss == -2) {
1279 /*debug(("Solver: (%d,%d) has multiple possible next squares.", sx, sy));*/
1280 ;
1281 } else if (poss == -1) {
1282 debug(("Solver: nowhere possible for (%d,%d) to link to.", sx, sy));
1283 copy->impossible = 1;
1284 return -1;
1285 } else {
1286 debug(("Solver: linking (%d,%d) to only possible next (%d,%d).",
1287 sx, sy, poss%w, poss/w));
1288 makelink(copy, i, poss);
1289 nlinks++;
1290 }
1291 }
1292
1293 for (i = 0; i < state->n; i++) {
1294 if (state->prev[i] != -1) continue;
1295 if (state->nums[i] == 1) continue; /* no prev from 1st no. */
1296
1297 x = i%w; y = i/w;
1298 if (from[i] == -1) {
1299 debug(("Solver: nowhere possible to link to (%d,%d)", x, y));
1300 copy->impossible = 1;
1301 return -1;
1302 } else if (from[i] == -2) {
1303 /*debug(("Solver: (%d,%d) has multiple possible prev squares.", x, y));*/
1304 ;
1305 } else {
1306 debug(("Solver: linking only possible prev (%d,%d) to (%d,%d).",
1307 from[i]%w, from[i]/w, x, y));
1308 makelink(copy, from[i], i);
1309 nlinks++;
1310 }
1311 }
1312
1313 return nlinks;
1314}
1315
1316/* Returns 1 if we managed to solve it, 0 otherwise. */
1317static int solve_state(game_state *state)
1318{
1319 game_state *copy = dup_game(state);
1320 int *scratch = snewn(state->n, int), ret;
1321
1322 debug_state("Before solver: ", state);
1323
1324 while (1) {
1325 update_numbers(state);
1326
1327 if (solve_single(state, copy, scratch)) {
1328 dup_game_to(state, copy);
1329 if (state->impossible) break; else continue;
1330 }
1331 break;
1332 }
1333 free_game(copy);
1334 sfree(scratch);
1335
1336 update_numbers(state);
1337 ret = state->impossible ? -1 : check_completion(state, 0);
1338 debug(("Solver finished: %s",
1339 ret < 0 ? "impossible" : ret > 0 ? "solved" : "not solved"));
1340 debug_state("After solver: ", state);
1341 return ret;
1342}
1343
1344static char *solve_game(const game_state *state, const game_state *currstate,
1345 const char *aux, char **error)
1346{
1347 game_state *tosolve;
1348 char *ret = NULL;
1349 int result;
1350
1351 tosolve = dup_game(currstate);
1352 result = solve_state(tosolve);
1353 if (result > 0)
1354 ret = generate_desc(tosolve, 1);
1355 free_game(tosolve);
1356 if (ret) return ret;
1357
1358 tosolve = dup_game(state);
1359 result = solve_state(tosolve);
1360 if (result < 0)
1361 *error = "Puzzle is impossible.";
1362 else if (result == 0)
1363 *error = "Unable to solve puzzle.";
1364 else
1365 ret = generate_desc(tosolve, 1);
1366
1367 free_game(tosolve);
1368
1369 return ret;
1370}
1371
1372/* --- UI and move routines. --- */
1373
1374
1375struct game_ui {
1376 int cx, cy, cshow;
1377
1378 int dragging, drag_is_from;
1379 int sx, sy; /* grid coords of start cell */
1380 int dx, dy; /* pixel coords of drag posn */
1381};
1382
1383static game_ui *new_ui(const game_state *state)
1384{
1385 game_ui *ui = snew(game_ui);
1386
1387 /* NB: if this is ever changed to as to require more than a structure
1388 * copy to clone, there's code that needs fixing in game_redraw too. */
1389
1390 ui->cx = ui->cy = ui->cshow = 0;
1391
1392 ui->dragging = 0;
1393 ui->sx = ui->sy = ui->dx = ui->dy = 0;
1394
1395 return ui;
1396}
1397
1398static void free_ui(game_ui *ui)
1399{
1400 sfree(ui);
1401}
1402
1403static char *encode_ui(const game_ui *ui)
1404{
1405 return NULL;
1406}
1407
1408static void decode_ui(game_ui *ui, const char *encoding)
1409{
1410}
1411
1412static void game_changed_state(game_ui *ui, const game_state *oldstate,
1413 const game_state *newstate)
1414{
1415 if (!oldstate->completed && newstate->completed)
1416 ui->cshow = ui->dragging = 0;
1417}
1418
1419struct game_drawstate {
1420 int tilesize, started, solved;
1421 int w, h, n;
1422 int *nums, *dirp;
1423 unsigned int *f;
1424 double angle_offset;
1425
1426 int dragging, dx, dy;
1427 blitter *dragb;
1428};
1429
1430static char *interpret_move(const game_state *state, game_ui *ui,
1431 const game_drawstate *ds,
1432 int mx, int my, int button)
1433{
1434 int x = FROMCOORD(mx), y = FROMCOORD(my), w = state->w;
1435 char buf[80];
1436
1437 if (IS_CURSOR_MOVE(button)) {
1438 move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, 0);
1439 ui->cshow = 1;
1440 if (ui->dragging) {
1441 ui->dx = COORD(ui->cx) + TILE_SIZE/2;
1442 ui->dy = COORD(ui->cy) + TILE_SIZE/2;
1443 }
1444 return "";
1445 } else if (IS_CURSOR_SELECT(button)) {
1446 if (!ui->cshow)
1447 ui->cshow = 1;
1448 else if (ui->dragging) {
1449 ui->dragging = FALSE;
1450 if (ui->sx == ui->cx && ui->sy == ui->cy) return "";
1451 if (ui->drag_is_from) {
1452 if (!isvalidmove(state, 0, ui->sx, ui->sy, ui->cx, ui->cy)) return "";
1453 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy);
1454 } else {
1455 if (!isvalidmove(state, 0, ui->cx, ui->cy, ui->sx, ui->sy)) return "";
1456 sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy);
1457 }
1458 return dupstr(buf);
1459 } else {
1460 ui->dragging = TRUE;
1461 ui->sx = ui->cx;
1462 ui->sy = ui->cy;
1463 ui->dx = COORD(ui->cx) + TILE_SIZE/2;
1464 ui->dy = COORD(ui->cy) + TILE_SIZE/2;
1465 ui->drag_is_from = (button == CURSOR_SELECT) ? 1 : 0;
1466 }
1467 return "";
1468 }
1469 if (IS_MOUSE_DOWN(button)) {
1470 if (ui->cshow) {
1471 ui->cshow = ui->dragging = 0;
1472 }
1473 assert(!ui->dragging);
1474 if (!INGRID(state, x, y)) return NULL;
1475
1476 if (button == LEFT_BUTTON) {
1477 /* disallow dragging from the final number. */
1478 if ((state->nums[y*w+x] == state->n) &&
1479 (state->flags[y*w+x] & FLAG_IMMUTABLE))
1480 return NULL;
1481 } else if (button == RIGHT_BUTTON) {
1482 /* disallow dragging to the first number. */
1483 if ((state->nums[y*w+x] == 1) &&
1484 (state->flags[y*w+x] & FLAG_IMMUTABLE))
1485 return NULL;
1486 }
1487
1488 ui->dragging = TRUE;
1489 ui->drag_is_from = (button == LEFT_BUTTON) ? 1 : 0;
1490 ui->sx = x;
1491 ui->sy = y;
1492 ui->dx = mx;
1493 ui->dy = my;
1494 ui->cshow = 0;
1495 return "";
1496 } else if (IS_MOUSE_DRAG(button) && ui->dragging) {
1497 ui->dx = mx;
1498 ui->dy = my;
1499 return "";
1500 } else if (IS_MOUSE_RELEASE(button) && ui->dragging) {
1501 ui->dragging = FALSE;
1502 if (ui->sx == x && ui->sy == y) return ""; /* single click */
1503
1504 if (!INGRID(state, x, y)) {
1505 int si = ui->sy*w+ui->sx;
1506 if (state->prev[si] == -1 && state->next[si] == -1)
1507 return "";
1508 sprintf(buf, "%c%d,%d",
1509 (int)(ui->drag_is_from ? 'C' : 'X'), ui->sx, ui->sy);
1510 return dupstr(buf);
1511 }
1512
1513 if (ui->drag_is_from) {
1514 if (!isvalidmove(state, 0, ui->sx, ui->sy, x, y)) return "";
1515 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y);
1516 } else {
1517 if (!isvalidmove(state, 0, x, y, ui->sx, ui->sy)) return "";
1518 sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy);
1519 }
1520 return dupstr(buf);
1521 } /* else if (button == 'H' || button == 'h')
1522 return dupstr("H"); */
1523 else if ((button == 'x' || button == 'X') && ui->cshow) {
1524 int si = ui->cy*w + ui->cx;
1525 if (state->prev[si] == -1 && state->next[si] == -1)
1526 return "";
1527 sprintf(buf, "%c%d,%d",
1528 (int)((button == 'x') ? 'C' : 'X'), ui->cx, ui->cy);
1529 return dupstr(buf);
1530 }
1531
1532 return NULL;
1533}
1534
1535static void unlink_cell(game_state *state, int si)
1536{
1537 debug(("Unlinking (%d,%d).", si%state->w, si/state->w));
1538 if (state->prev[si] != -1) {
1539 debug((" ... removing prev link from (%d,%d).",
1540 state->prev[si]%state->w, state->prev[si]/state->w));
1541 state->next[state->prev[si]] = -1;
1542 state->prev[si] = -1;
1543 }
1544 if (state->next[si] != -1) {
1545 debug((" ... removing next link to (%d,%d).",
1546 state->next[si]%state->w, state->next[si]/state->w));
1547 state->prev[state->next[si]] = -1;
1548 state->next[si] = -1;
1549 }
1550}
1551
1552static game_state *execute_move(const game_state *state, const char *move)
1553{
1554 game_state *ret = NULL;
1555 int sx, sy, ex, ey, si, ei, w = state->w;
1556 char c;
1557
1558 debug(("move: %s", move));
1559
1560 if (move[0] == 'S') {
1561 game_params p;
1562 game_state *tmp;
1563 char *valid;
1564 int i;
1565
1566 p.w = state->w; p.h = state->h;
1567 valid = validate_desc(&p, move+1);
1568 if (valid) {
1569 debug(("execute_move: move not valid: %s", valid));
1570 return NULL;
1571 }
1572 ret = dup_game(state);
1573 tmp = new_game(NULL, &p, move+1);
1574 for (i = 0; i < state->n; i++) {
1575 ret->prev[i] = tmp->prev[i];
1576 ret->next[i] = tmp->next[i];
1577 }
1578 free_game(tmp);
1579 ret->used_solve = 1;
1580 } else if (sscanf(move, "L%d,%d-%d,%d", &sx, &sy, &ex, &ey) == 4) {
1581 if (!isvalidmove(state, 0, sx, sy, ex, ey)) return NULL;
1582
1583 ret = dup_game(state);
1584
1585 si = sy*w+sx; ei = ey*w+ex;
1586 makelink(ret, si, ei);
1587 } else if (sscanf(move, "%c%d,%d", &c, &sx, &sy) == 3) {
1588 int sset;
1589
1590 if (c != 'C' && c != 'X') return NULL;
1591 if (!INGRID(state, sx, sy)) return NULL;
1592 si = sy*w+sx;
1593 if (state->prev[si] == -1 && state->next[si] == -1)
1594 return NULL;
1595
1596 ret = dup_game(state);
1597
1598 sset = state->nums[si] / (state->n+1);
1599 if (c == 'C' || (c == 'X' && sset == 0)) {
1600 /* Unlink the single cell we dragged from the board. */
1601 unlink_cell(ret, si);
1602 } else {
1603 int i, set;
1604 for (i = 0; i < state->n; i++) {
1605 /* Unlink all cells in the same set as the one we dragged
1606 * from the board. */
1607
1608 if (state->nums[i] == 0) continue;
1609 set = state->nums[i] / (state->n+1);
1610 if (set != sset) continue;
1611
1612 unlink_cell(ret, i);
1613 }
1614 }
1615 } else if (strcmp(move, "H") == 0) {
1616 ret = dup_game(state);
1617 solve_state(ret);
1618 }
1619 if (ret) {
1620 update_numbers(ret);
1621 if (check_completion(ret, 1)) ret->completed = 1;
1622 }
1623
1624 return ret;
1625}
1626
1627/* ----------------------------------------------------------------------
1628 * Drawing routines.
1629 */
1630
1631static void game_compute_size(const game_params *params, int tilesize,
1632 int *x, int *y)
1633{
1634 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1635 struct { int tilesize, order; } ads, *ds = &ads;
1636 ads.tilesize = tilesize;
1637
1638 *x = TILE_SIZE * params->w + 2 * BORDER;
1639 *y = TILE_SIZE * params->h + 2 * BORDER;
1640}
1641
1642static void game_set_size(drawing *dr, game_drawstate *ds,
1643 const game_params *params, int tilesize)
1644{
1645 ds->tilesize = tilesize;
1646 assert(TILE_SIZE > 0);
1647
1648 assert(!ds->dragb);
1649 ds->dragb = blitter_new(dr, BLITTER_SIZE, BLITTER_SIZE);
1650}
1651
1652/* Colours chosen from the webby palette to work as a background to black text,
1653 * W then some plausible approximation to pastelly ROYGBIV; we then interpolate
1654 * between consecutive pairs to give another 8 (and then the drawing routine
1655 * will reuse backgrounds). */
1656static const unsigned long bgcols[8] = {
1657 0xffffff, /* white */
1658 0xffa07a, /* lightsalmon */
1659 0x98fb98, /* green */
1660 0x7fffd4, /* aquamarine */
1661 0x9370db, /* medium purple */
1662 0xffa500, /* orange */
1663 0x87cefa, /* lightskyblue */
1664 0xffff00, /* yellow */
1665};
1666
1667static float *game_colours(frontend *fe, int *ncolours)
1668{
1669 float *ret = snewn(3 * NCOLOURS, float);
1670 int c, i;
1671
1672 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1673
1674 for (i = 0; i < 3; i++) {
1675 ret[COL_NUMBER * 3 + i] = 0.0F;
1676 ret[COL_ARROW * 3 + i] = 0.0F;
1677 ret[COL_CURSOR * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 2.0F;
1678 ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 1.3F;
1679 }
1680 ret[COL_NUMBER_SET * 3 + 0] = 0.0F;
1681 ret[COL_NUMBER_SET * 3 + 1] = 0.0F;
1682 ret[COL_NUMBER_SET * 3 + 2] = 0.9F;
1683
1684 ret[COL_ERROR * 3 + 0] = 1.0F;
1685 ret[COL_ERROR * 3 + 1] = 0.0F;
1686 ret[COL_ERROR * 3 + 2] = 0.0F;
1687
1688 ret[COL_DRAG_ORIGIN * 3 + 0] = 0.2F;
1689 ret[COL_DRAG_ORIGIN * 3 + 1] = 1.0F;
1690 ret[COL_DRAG_ORIGIN * 3 + 2] = 0.2F;
1691
1692 for (c = 0; c < 8; c++) {
1693 ret[(COL_B0 + c) * 3 + 0] = (float)((bgcols[c] & 0xff0000) >> 16) / 256.0F;
1694 ret[(COL_B0 + c) * 3 + 1] = (float)((bgcols[c] & 0xff00) >> 8) / 256.0F;
1695 ret[(COL_B0 + c) * 3 + 2] = (float)((bgcols[c] & 0xff)) / 256.0F;
1696 }
1697 for (c = 0; c < 8; c++) {
1698 for (i = 0; i < 3; i++) {
1699 ret[(COL_B0 + 8 + c) * 3 + i] =
1700 (ret[(COL_B0 + c) * 3 + i] + ret[(COL_B0 + c + 1) * 3 + i]) / 2.0F;
1701 }
1702 }
1703
1704#define average(r,a,b,w) do { \
1705 for (i = 0; i < 3; i++) \
1706 ret[(r)*3+i] = ret[(a)*3+i] + w * (ret[(b)*3+i] - ret[(a)*3+i]); \
1707} while (0)
1708 average(COL_ARROW_BG_DIM, COL_BACKGROUND, COL_ARROW, 0.1F);
1709 average(COL_NUMBER_SET_MID, COL_B0, COL_NUMBER_SET, 0.3F);
1710 for (c = 0; c < NBACKGROUNDS; c++) {
1711 /* I assume here that COL_ARROW and COL_NUMBER are the same.
1712 * Otherwise I'd need two sets of COL_M*. */
1713 average(COL_M0 + c, COL_B0 + c, COL_NUMBER, 0.3F);
1714 average(COL_D0 + c, COL_B0 + c, COL_NUMBER, 0.1F);
1715 average(COL_X0 + c, COL_BACKGROUND, COL_B0 + c, 0.5F);
1716 }
1717
1718 *ncolours = NCOLOURS;
1719 return ret;
1720}
1721
1722static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1723{
1724 struct game_drawstate *ds = snew(struct game_drawstate);
1725 int i;
1726
1727 ds->tilesize = ds->started = ds->solved = 0;
1728 ds->w = state->w;
1729 ds->h = state->h;
1730 ds->n = state->n;
1731
1732 ds->nums = snewn(state->n, int);
1733 ds->dirp = snewn(state->n, int);
1734 ds->f = snewn(state->n, unsigned int);
1735 for (i = 0; i < state->n; i++) {
1736 ds->nums[i] = 0;
1737 ds->dirp[i] = -1;
1738 ds->f[i] = 0;
1739 }
1740
1741 ds->angle_offset = 0.0F;
1742
1743 ds->dragging = ds->dx = ds->dy = 0;
1744 ds->dragb = NULL;
1745
1746 return ds;
1747}
1748
1749static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1750{
1751 sfree(ds->nums);
1752 sfree(ds->dirp);
1753 sfree(ds->f);
1754 if (ds->dragb) blitter_free(dr, ds->dragb);
1755
1756 sfree(ds);
1757}
1758
1759/* cx, cy are top-left corner. sz is the 'radius' of the arrow.
1760 * ang is in radians, clockwise from 0 == straight up. */
1761static void draw_arrow(drawing *dr, int cx, int cy, int sz, double ang,
1762 int cfill, int cout)
1763{
1764 int coords[14];
1765 int xdx, ydx, xdy, ydy, xdx3, xdy3;
1766 double s = sin(ang), c = cos(ang);
1767
1768 xdx3 = (int)(sz * (c/3 + 1) + 0.5) - sz;
1769 xdy3 = (int)(sz * (s/3 + 1) + 0.5) - sz;
1770 xdx = (int)(sz * (c + 1) + 0.5) - sz;
1771 xdy = (int)(sz * (s + 1) + 0.5) - sz;
1772 ydx = -xdy;
1773 ydy = xdx;
1774
1775
1776 coords[2*0 + 0] = cx - ydx;
1777 coords[2*0 + 1] = cy - ydy;
1778 coords[2*1 + 0] = cx + xdx;
1779 coords[2*1 + 1] = cy + xdy;
1780 coords[2*2 + 0] = cx + xdx3;
1781 coords[2*2 + 1] = cy + xdy3;
1782 coords[2*3 + 0] = cx + xdx3 + ydx;
1783 coords[2*3 + 1] = cy + xdy3 + ydy;
1784 coords[2*4 + 0] = cx - xdx3 + ydx;
1785 coords[2*4 + 1] = cy - xdy3 + ydy;
1786 coords[2*5 + 0] = cx - xdx3;
1787 coords[2*5 + 1] = cy - xdy3;
1788 coords[2*6 + 0] = cx - xdx;
1789 coords[2*6 + 1] = cy - xdy;
1790
1791 draw_polygon(dr, coords, 7, cfill, cout);
1792}
1793
1794static void draw_arrow_dir(drawing *dr, int cx, int cy, int sz, int dir,
1795 int cfill, int cout, double angle_offset)
1796{
1797 double ang = 2.0 * PI * (double)dir / 8.0 + angle_offset;
1798 draw_arrow(dr, cx, cy, sz, ang, cfill, cout);
1799}
1800
1801/* cx, cy are centre coordinates.. */
1802static void draw_star(drawing *dr, int cx, int cy, int rad, int npoints,
1803 int cfill, int cout, double angle_offset)
1804{
1805 int *coords, n;
1806 double a, r;
1807
1808 assert(npoints > 0);
1809
1810 coords = snewn(npoints * 2 * 2, int);
1811
1812 for (n = 0; n < npoints * 2; n++) {
1813 a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset;
1814 r = (n % 2) ? (double)rad/2.0 : (double)rad;
1815
1816 /* We're rotating the point at (0, -r) by a degrees */
1817 coords[2*n+0] = cx + (int)( r * sin(a));
1818 coords[2*n+1] = cy + (int)(-r * cos(a));
1819 }
1820 draw_polygon(dr, coords, npoints*2, cfill, cout);
1821 sfree(coords);
1822}
1823
1824static int num2col(game_drawstate *ds, int num)
1825{
1826 int set = num / (ds->n+1);
1827
1828 if (num <= 0 || set == 0) return COL_B0;
1829 return COL_B0 + 1 + ((set-1) % 15);
1830}
1831
1832#define ARROW_HALFSZ (7 * TILE_SIZE / 32)
1833
1834#define F_CUR 0x001 /* Cursor on this tile. */
1835#define F_DRAG_SRC 0x002 /* Tile is source of a drag. */
1836#define F_ERROR 0x004 /* Tile marked in error. */
1837#define F_IMMUTABLE 0x008 /* Tile (number) is immutable. */
1838#define F_ARROW_POINT 0x010 /* Tile points to other tile */
1839#define F_ARROW_INPOINT 0x020 /* Other tile points in here. */
1840#define F_DIM 0x040 /* Tile is dim */
1841
1842static void tile_redraw(drawing *dr, game_drawstate *ds, int tx, int ty,
1843 int dir, int dirp, int num, unsigned int f,
1844 double angle_offset, int print_ink)
1845{
1846 int cb = TILE_SIZE / 16, textsz;
1847 char buf[20];
1848 int arrowcol, sarrowcol, setcol, textcol;
1849 int acx, acy, asz, empty = 0;
1850
1851 if (num == 0 && !(f & F_ARROW_POINT) && !(f & F_ARROW_INPOINT)) {
1852 empty = 1;
1853 /*
1854 * We don't display text in empty cells: typically these are
1855 * signified by num=0. However, in some cases a cell could
1856 * have had the number 0 assigned to it if the user made an
1857 * error (e.g. tried to connect a chain of length 5 to the
1858 * immutable number 4) so we _do_ display the 0 if the cell
1859 * has a link in or a link out.
1860 */
1861 }
1862
1863 /* Calculate colours. */
1864
1865 if (print_ink >= 0) {
1866 /*
1867 * We're printing, so just do everything in black.
1868 */
1869 arrowcol = textcol = print_ink;
1870 setcol = sarrowcol = -1; /* placate optimiser */
1871 } else {
1872
1873 setcol = empty ? COL_BACKGROUND : num2col(ds, num);
1874
1875#define dim(fg,bg) ( \
1876 (bg)==COL_BACKGROUND ? COL_ARROW_BG_DIM : \
1877 (bg) + COL_D0 - COL_B0 \
1878 )
1879
1880#define mid(fg,bg) ( \
1881 (fg)==COL_NUMBER_SET ? COL_NUMBER_SET_MID : \
1882 (bg) + COL_M0 - COL_B0 \
1883 )
1884
1885#define dimbg(bg) ( \
1886 (bg)==COL_BACKGROUND ? COL_BACKGROUND : \
1887 (bg) + COL_X0 - COL_B0 \
1888 )
1889
1890 if (f & F_DRAG_SRC) arrowcol = COL_DRAG_ORIGIN;
1891 else if (f & F_DIM) arrowcol = dim(COL_ARROW, setcol);
1892 else if (f & F_ARROW_POINT) arrowcol = mid(COL_ARROW, setcol);
1893 else arrowcol = COL_ARROW;
1894
1895 if ((f & F_ERROR) && !(f & F_IMMUTABLE)) textcol = COL_ERROR;
1896 else {
1897 if (f & F_IMMUTABLE) textcol = COL_NUMBER_SET;
1898 else textcol = COL_NUMBER;
1899
1900 if (f & F_DIM) textcol = dim(textcol, setcol);
1901 else if (((f & F_ARROW_POINT) || num==ds->n) &&
1902 ((f & F_ARROW_INPOINT) || num==1))
1903 textcol = mid(textcol, setcol);
1904 }
1905
1906 if (f & F_DIM) sarrowcol = dim(COL_ARROW, setcol);
1907 else sarrowcol = COL_ARROW;
1908 }
1909
1910 /* Clear tile background */
1911
1912 if (print_ink < 0) {
1913 draw_rect(dr, tx, ty, TILE_SIZE, TILE_SIZE,
1914 (f & F_DIM) ? dimbg(setcol) : setcol);
1915 }
1916
1917 /* Draw large (outwards-pointing) arrow. */
1918
1919 asz = ARROW_HALFSZ; /* 'radius' of arrow/star. */
1920 acx = tx+TILE_SIZE/2+asz; /* centre x */
1921 acy = ty+TILE_SIZE/2+asz; /* centre y */
1922
1923 if (num == ds->n && (f & F_IMMUTABLE))
1924 draw_star(dr, acx, acy, asz, 5, arrowcol, arrowcol, angle_offset);
1925 else
1926 draw_arrow_dir(dr, acx, acy, asz, dir, arrowcol, arrowcol, angle_offset);
1927 if (print_ink < 0 && (f & F_CUR))
1928 draw_rect_corners(dr, acx, acy, asz+1, COL_CURSOR);
1929
1930 /* Draw dot iff this tile requires a predecessor and doesn't have one. */
1931
1932 if (print_ink < 0) {
1933 acx = tx+TILE_SIZE/2-asz;
1934 acy = ty+TILE_SIZE/2+asz;
1935
1936 if (!(f & F_ARROW_INPOINT) && num != 1) {
1937 draw_circle(dr, acx, acy, asz / 4, sarrowcol, sarrowcol);
1938 }
1939 }
1940
1941 /* Draw text (number or set). */
1942
1943 if (!empty) {
1944 int set = (num <= 0) ? 0 : num / (ds->n+1);
1945
1946 char *p = buf;
1947 if (set == 0 || num <= 0) {
1948 sprintf(buf, "%d", num);
1949 } else {
1950 int n = num % (ds->n+1);
1951 p += sizeof(buf) - 1;
1952
1953 if (n != 0) {
1954 sprintf(buf, "+%d", n); /* Just to get the length... */
1955 p -= strlen(buf);
1956 sprintf(p, "+%d", n);
1957 } else {
1958 *p = '\0';
1959 }
1960 do {
1961 set--;
1962 p--;
1963 *p = (char)((set % 26)+'a');
1964 set /= 26;
1965 } while (set);
1966 }
1967 textsz = min(2*asz, (TILE_SIZE - 2 * cb) / (int)strlen(p));
1968 draw_text(dr, tx + cb, ty + TILE_SIZE/4, FONT_VARIABLE, textsz,
1969 ALIGN_VCENTRE | ALIGN_HLEFT, textcol, p);
1970 }
1971
1972 if (print_ink < 0) {
1973 draw_rect_outline(dr, tx, ty, TILE_SIZE, TILE_SIZE, COL_GRID);
1974 draw_update(dr, tx, ty, TILE_SIZE, TILE_SIZE);
1975 }
1976}
1977
1978static void draw_drag_indicator(drawing *dr, game_drawstate *ds,
1979 const game_state *state, const game_ui *ui,
1980 int validdrag)
1981{
1982 int dir, w = ds->w, acol = COL_ARROW;
1983 int fx = FROMCOORD(ui->dx), fy = FROMCOORD(ui->dy);
1984 double ang;
1985
1986 if (validdrag) {
1987 /* If we could move here, lock the arrow to the appropriate direction. */
1988 dir = ui->drag_is_from ? state->dirs[ui->sy*w+ui->sx] : state->dirs[fy*w+fx];
1989
1990 ang = (2.0 * PI * dir) / 8.0; /* similar to calculation in draw_arrow_dir. */
1991 } else {
1992 /* Draw an arrow pointing away from/towards the origin cell. */
1993 int ox = COORD(ui->sx) + TILE_SIZE/2, oy = COORD(ui->sy) + TILE_SIZE/2;
1994 double tana, offset;
1995 double xdiff = abs(ox - ui->dx), ydiff = abs(oy - ui->dy);
1996
1997 if (xdiff == 0) {
1998 ang = (oy > ui->dy) ? 0.0F : PI;
1999 } else if (ydiff == 0) {
2000 ang = (ox > ui->dx) ? 3.0F*PI/2.0F : PI/2.0F;
2001 } else {
2002 if (ui->dx > ox && ui->dy < oy) {
2003 tana = xdiff / ydiff;
2004 offset = 0.0F;
2005 } else if (ui->dx > ox && ui->dy > oy) {
2006 tana = ydiff / xdiff;
2007 offset = PI/2.0F;
2008 } else if (ui->dx < ox && ui->dy > oy) {
2009 tana = xdiff / ydiff;
2010 offset = PI;
2011 } else {
2012 tana = ydiff / xdiff;
2013 offset = 3.0F * PI / 2.0F;
2014 }
2015 ang = atan(tana) + offset;
2016 }
2017
2018 if (!ui->drag_is_from) ang += PI; /* point to origin, not away from. */
2019
2020 }
2021 draw_arrow(dr, ui->dx, ui->dy, ARROW_HALFSZ, ang, acol, acol);
2022}
2023
2024static void game_redraw(drawing *dr, game_drawstate *ds,
2025 const game_state *oldstate, const game_state *state,
2026 int dir, const game_ui *ui,
2027 float animtime, float flashtime)
2028{
2029 int x, y, i, w = ds->w, dirp, force = 0;
2030 unsigned int f;
2031 double angle_offset = 0.0;
2032 game_state *postdrop = NULL;
2033
2034 if (flashtime > 0.0F)
2035 angle_offset = 2.0 * PI * (flashtime / FLASH_SPIN);
2036 if (angle_offset != ds->angle_offset) {
2037 ds->angle_offset = angle_offset;
2038 force = 1;
2039 }
2040
2041 if (ds->dragging) {
2042 assert(ds->dragb);
2043 blitter_load(dr, ds->dragb, ds->dx, ds->dy);
2044 draw_update(dr, ds->dx, ds->dy, BLITTER_SIZE, BLITTER_SIZE);
2045 ds->dragging = FALSE;
2046 }
2047
2048 /* If an in-progress drag would make a valid move if finished, we
2049 * reflect that move in the board display. We let interpret_move do
2050 * most of the heavy lifting for us: we have to copy the game_ui so
2051 * as not to stomp on the real UI's drag state. */
2052 if (ui->dragging) {
2053 game_ui uicopy = *ui;
2054 char *movestr = interpret_move(state, &uicopy, ds, ui->dx, ui->dy, LEFT_RELEASE);
2055
2056 if (movestr != NULL && strcmp(movestr, "") != 0) {
2057 postdrop = execute_move(state, movestr);
2058 sfree(movestr);
2059
2060 state = postdrop;
2061 }
2062 }
2063
2064 if (!ds->started) {
2065 int aw = TILE_SIZE * state->w;
2066 int ah = TILE_SIZE * state->h;
2067 draw_rect(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER, COL_BACKGROUND);
2068 draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID);
2069 draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER);
2070 }
2071 for (x = 0; x < state->w; x++) {
2072 for (y = 0; y < state->h; y++) {
2073 i = y*w + x;
2074 f = 0;
2075 dirp = -1;
2076
2077 if (ui->cshow && x == ui->cx && y == ui->cy)
2078 f |= F_CUR;
2079
2080 if (ui->dragging) {
2081 if (x == ui->sx && y == ui->sy)
2082 f |= F_DRAG_SRC;
2083 else if (ui->drag_is_from) {
2084 if (!ispointing(state, ui->sx, ui->sy, x, y))
2085 f |= F_DIM;
2086 } else {
2087 if (!ispointing(state, x, y, ui->sx, ui->sy))
2088 f |= F_DIM;
2089 }
2090 }
2091
2092 if (state->impossible ||
2093 state->nums[i] < 0 || state->flags[i] & FLAG_ERROR)
2094 f |= F_ERROR;
2095 if (state->flags[i] & FLAG_IMMUTABLE)
2096 f |= F_IMMUTABLE;
2097
2098 if (state->next[i] != -1)
2099 f |= F_ARROW_POINT;
2100
2101 if (state->prev[i] != -1) {
2102 /* Currently the direction here is from our square _back_
2103 * to its previous. We could change this to give the opposite
2104 * sense to the direction. */
2105 f |= F_ARROW_INPOINT;
2106 dirp = whichdir(x, y, state->prev[i]%w, state->prev[i]/w);
2107 }
2108
2109 if (state->nums[i] != ds->nums[i] ||
2110 f != ds->f[i] || dirp != ds->dirp[i] ||
2111 force || !ds->started) {
2112 int sign;
2113 {
2114 /*
2115 * Trivial and foolish configurable option done on
2116 * purest whim. With this option enabled, the
2117 * victory flash is done by rotating each square
2118 * in the opposite direction from its immediate
2119 * neighbours, so that they behave like a field of
2120 * interlocking gears. With it disabled, they all
2121 * rotate in the same direction. Choose for
2122 * yourself which is more brain-twisting :-)
2123 */
2124 static int gear_mode = -1;
2125 if (gear_mode < 0) {
2126 char *env = getenv("SIGNPOST_GEARS");
2127 gear_mode = (env && (env[0] == 'y' || env[0] == 'Y'));
2128 }
2129 if (gear_mode)
2130 sign = 1 - 2 * ((x ^ y) & 1);
2131 else
2132 sign = 1;
2133 }
2134 tile_redraw(dr, ds,
2135 BORDER + x * TILE_SIZE,
2136 BORDER + y * TILE_SIZE,
2137 state->dirs[i], dirp, state->nums[i], f,
2138 sign * angle_offset, -1);
2139 ds->nums[i] = state->nums[i];
2140 ds->f[i] = f;
2141 ds->dirp[i] = dirp;
2142 }
2143 }
2144 }
2145 if (ui->dragging) {
2146 ds->dragging = TRUE;
2147 ds->dx = ui->dx - BLITTER_SIZE/2;
2148 ds->dy = ui->dy - BLITTER_SIZE/2;
2149 blitter_save(dr, ds->dragb, ds->dx, ds->dy);
2150
2151 draw_drag_indicator(dr, ds, state, ui, postdrop ? 1 : 0);
2152 }
2153 if (postdrop) free_game(postdrop);
2154 if (!ds->started) ds->started = TRUE;
2155}
2156
2157static float game_anim_length(const game_state *oldstate,
2158 const game_state *newstate, int dir, game_ui *ui)
2159{
2160 return 0.0F;
2161}
2162
2163static float game_flash_length(const game_state *oldstate,
2164 const game_state *newstate, int dir, game_ui *ui)
2165{
2166 if (!oldstate->completed &&
2167 newstate->completed && !newstate->used_solve)
2168 return FLASH_SPIN;
2169 else
2170 return 0.0F;
2171}
2172
2173static int game_status(const game_state *state)
2174{
2175 return state->completed ? +1 : 0;
2176}
2177
2178static int game_timing_state(const game_state *state, game_ui *ui)
2179{
2180 return TRUE;
2181}
2182
2183static void game_print_size(const game_params *params, float *x, float *y)
2184{
2185 int pw, ph;
2186
2187 game_compute_size(params, 1300, &pw, &ph);
2188 *x = pw / 100.0F;
2189 *y = ph / 100.0F;
2190}
2191
2192static void game_print(drawing *dr, const game_state *state, int tilesize)
2193{
2194 int ink = print_mono_colour(dr, 0);
2195 int x, y;
2196
2197 /* Fake up just enough of a drawstate */
2198 game_drawstate ads, *ds = &ads;
2199 ds->tilesize = tilesize;
2200 ds->n = state->n;
2201
2202 /*
2203 * Border and grid.
2204 */
2205 print_line_width(dr, TILE_SIZE / 40);
2206 for (x = 1; x < state->w; x++)
2207 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(state->h), ink);
2208 for (y = 1; y < state->h; y++)
2209 draw_line(dr, COORD(0), COORD(y), COORD(state->w), COORD(y), ink);
2210 print_line_width(dr, 2*TILE_SIZE / 40);
2211 draw_rect_outline(dr, COORD(0), COORD(0), TILE_SIZE*state->w,
2212 TILE_SIZE*state->h, ink);
2213
2214 /*
2215 * Arrows and numbers.
2216 */
2217 print_line_width(dr, 0);
2218 for (y = 0; y < state->h; y++)
2219 for (x = 0; x < state->w; x++)
2220 tile_redraw(dr, ds, COORD(x), COORD(y), state->dirs[y*state->w+x],
2221 0, state->nums[y*state->w+x], 0, 0.0, ink);
2222}
2223
2224#ifdef COMBINED
2225#define thegame signpost
2226#endif
2227
2228const struct game thegame = {
2229 "Signpost", "games.signpost", "signpost",
2230 default_params,
2231 game_fetch_preset, NULL,
2232 decode_params,
2233 encode_params,
2234 free_params,
2235 dup_params,
2236 TRUE, game_configure, custom_params,
2237 validate_params,
2238 new_game_desc,
2239 validate_desc,
2240 new_game,
2241 dup_game,
2242 free_game,
2243 TRUE, solve_game,
2244 TRUE, game_can_format_as_text_now, game_text_format,
2245 new_ui,
2246 free_ui,
2247 encode_ui,
2248 decode_ui,
2249 game_changed_state,
2250 interpret_move,
2251 execute_move,
2252 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2253 game_colours,
2254 game_new_drawstate,
2255 game_free_drawstate,
2256 game_redraw,
2257 game_anim_length,
2258 game_flash_length,
2259 game_status,
2260 TRUE, FALSE, game_print_size, game_print,
2261 FALSE, /* wants_statusbar */
2262 FALSE, game_timing_state,
2263 REQUIRE_RBUTTON, /* flags */
2264};
2265
2266#ifdef STANDALONE_SOLVER
2267
2268#include <time.h>
2269#include <stdarg.h>
2270
2271const char *quis = NULL;
2272int verbose = 0;
2273
2274void usage(FILE *out) {
2275 fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis);
2276}
2277
2278static void cycle_seed(char **seedstr, random_state *rs)
2279{
2280 char newseed[16];
2281 int j;
2282
2283 newseed[15] = '\0';
2284 newseed[0] = '1' + (char)random_upto(rs, 9);
2285 for (j = 1; j < 15; j++)
2286 newseed[j] = '0' + (char)random_upto(rs, 10);
2287 sfree(*seedstr);
2288 *seedstr = dupstr(newseed);
2289}
2290
2291static void start_soak(game_params *p, char *seedstr)
2292{
2293 time_t tt_start, tt_now, tt_last;
2294 char *desc, *aux;
2295 random_state *rs;
2296 long n = 0, nnums = 0, i;
2297 game_state *state;
2298
2299 tt_start = tt_now = time(NULL);
2300 printf("Soak-generating a %dx%d grid.\n", p->w, p->h);
2301
2302 while (1) {
2303 rs = random_new(seedstr, strlen(seedstr));
2304 desc = thegame.new_desc(p, rs, &aux, 0);
2305
2306 state = thegame.new_game(NULL, p, desc);
2307 for (i = 0; i < state->n; i++) {
2308 if (state->flags[i] & FLAG_IMMUTABLE)
2309 nnums++;
2310 }
2311 thegame.free_game(state);
2312
2313 sfree(desc);
2314 cycle_seed(&seedstr, rs);
2315 random_free(rs);
2316
2317 n++;
2318 tt_last = time(NULL);
2319 if (tt_last > tt_now) {
2320 tt_now = tt_last;
2321 printf("%ld total, %3.1f/s, %3.1f nums/grid (%3.1f%%).\n",
2322 n,
2323 (double)n / ((double)tt_now - tt_start),
2324 (double)nnums / (double)n,
2325 ((double)nnums * 100.0) / ((double)n * (double)p->w * (double)p->h) );
2326 }
2327 }
2328}
2329
2330static void process_desc(char *id)
2331{
2332 char *desc, *err, *solvestr;
2333 game_params *p;
2334 game_state *s;
2335
2336 printf("%s\n ", id);
2337
2338 desc = strchr(id, ':');
2339 if (!desc) {
2340 fprintf(stderr, "%s: expecting game description.", quis);
2341 exit(1);
2342 }
2343
2344 *desc++ = '\0';
2345
2346 p = thegame.default_params();
2347 thegame.decode_params(p, id);
2348 err = thegame.validate_params(p, 1);
2349 if (err) {
2350 fprintf(stderr, "%s: %s", quis, err);
2351 thegame.free_params(p);
2352 return;
2353 }
2354
2355 err = thegame.validate_desc(p, desc);
2356 if (err) {
2357 fprintf(stderr, "%s: %s\nDescription: %s\n", quis, err, desc);
2358 thegame.free_params(p);
2359 return;
2360 }
2361
2362 s = thegame.new_game(NULL, p, desc);
2363
2364 solvestr = thegame.solve(s, s, NULL, &err);
2365 if (!solvestr)
2366 fprintf(stderr, "%s\n", err);
2367 else
2368 printf("Puzzle is soluble.\n");
2369
2370 thegame.free_game(s);
2371 thegame.free_params(p);
2372}
2373
2374int main(int argc, const char *argv[])
2375{
2376 char *id = NULL, *desc, *err, *aux = NULL;
2377 int soak = 0, verbose = 0, stdin_desc = 0, n = 1, i;
2378 char *seedstr = NULL, newseed[16];
2379
2380 setvbuf(stdout, NULL, _IONBF, 0);
2381
2382 quis = argv[0];
2383 while (--argc > 0) {
2384 char *p = (char*)(*++argv);
2385 if (!strcmp(p, "-v") || !strcmp(p, "--verbose"))
2386 verbose = 1;
2387 else if (!strcmp(p, "--stdin"))
2388 stdin_desc = 1;
2389 else if (!strcmp(p, "-e") || !strcmp(p, "--seed")) {
2390 seedstr = dupstr(*++argv);
2391 argc--;
2392 } else if (!strcmp(p, "-n") || !strcmp(p, "--number")) {
2393 n = atoi(*++argv);
2394 argc--;
2395 } else if (!strcmp(p, "-s") || !strcmp(p, "--soak")) {
2396 soak = 1;
2397 } else if (*p == '-') {
2398 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2399 usage(stderr);
2400 exit(1);
2401 } else {
2402 id = p;
2403 }
2404 }
2405
2406 sprintf(newseed, "%lu", (unsigned long) time(NULL));
2407 seedstr = dupstr(newseed);
2408
2409 if (id || !stdin_desc) {
2410 if (id && strchr(id, ':')) {
2411 /* Parameters and description passed on cmd-line:
2412 * try and solve it. */
2413 process_desc(id);
2414 } else {
2415 /* No description passed on cmd-line: decode parameters
2416 * (with optional seed too) */
2417
2418 game_params *p = thegame.default_params();
2419
2420 if (id) {
2421 char *cmdseed = strchr(id, '#');
2422 if (cmdseed) {
2423 *cmdseed++ = '\0';
2424 sfree(seedstr);
2425 seedstr = dupstr(cmdseed);
2426 }
2427
2428 thegame.decode_params(p, id);
2429 }
2430
2431 err = thegame.validate_params(p, 1);
2432 if (err) {
2433 fprintf(stderr, "%s: %s", quis, err);
2434 thegame.free_params(p);
2435 exit(1);
2436 }
2437
2438 /* We have a set of valid parameters; either soak with it
2439 * or generate a single game description and print to stdout. */
2440 if (soak)
2441 start_soak(p, seedstr);
2442 else {
2443 char *pstring = thegame.encode_params(p, 0);
2444
2445 for (i = 0; i < n; i++) {
2446 random_state *rs = random_new(seedstr, strlen(seedstr));
2447
2448 if (verbose) printf("%s#%s\n", pstring, seedstr);
2449 desc = thegame.new_desc(p, rs, &aux, 0);
2450 printf("%s:%s\n", pstring, desc);
2451 sfree(desc);
2452
2453 cycle_seed(&seedstr, rs);
2454
2455 random_free(rs);
2456 }
2457
2458 sfree(pstring);
2459 }
2460 thegame.free_params(p);
2461 }
2462 }
2463
2464 if (stdin_desc) {
2465 char buf[4096];
2466
2467 while (fgets(buf, sizeof(buf), stdin)) {
2468 buf[strcspn(buf, "\r\n")] = '\0';
2469 process_desc(buf);
2470 }
2471 }
2472 sfree(seedstr);
2473
2474 return 0;
2475}
2476
2477#endif
2478
2479
2480/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/signpost.html b/apps/plugins/puzzles/src/signpost.html
new file mode 100644
index 0000000000..8a30030124
--- /dev/null
+++ b/apps/plugins/puzzles/src/signpost.html
@@ -0,0 +1,72 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Signpost</title>
7<link rel="previous" href="magnets.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="range.html">
12</head>
13<body>
14<p><a href="magnets.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="range.html">Next</a></p>
15<h1><a name="C34"></a>Chapter 34: <a name="i0"></a>Signpost</h1>
16<p>
17You have a grid of squares; each square (except the last one) contains an arrow, and some squares also contain numbers. Your job is to connect the squares to form a continuous list of numbers starting at 1 and linked in the direction of the arrows &#8211; so the arrow inside the square with the number 1 will point to the square containing the number 2, which will point to the square containing the number 3, etc. Each square can be any distance away from the previous one, as long as it is somewhere in the direction of the arrow.
18</p>
19<p>
20By convention the first and last numbers are shown; one or more interim numbers may also appear at the beginning.
21</p>
22<p>
23Credit for this puzzle goes to <a name="i1"></a>Janko <a href="#p0">[17]</a>, who call it &#8216;Pfeilpfad&#8217; (&#8216;arrow path&#8217;).
24</p>
25<p>
26Signpost was contributed to this collection by James Harvey.
27</p>
28<p><a name="p0"></a>
29[17] <a href="http://janko.at/Raetsel/Pfeilpfad/index.htm"><code>http://janko.at/Raetsel/Pfeilpfad/index.htm</code></a>
30</p>
31<h2><a name="S34.1"></a>34.1 <a name="i2"></a>Signpost controls</h2>
32<p>
33To play Signpost, you connect squares together by dragging from one square to another, indicating that they are adjacent in the sequence. Drag with the left button from a square to its successor, or with the right button from a square to its predecessor.
34</p>
35<p>
36If you connect together two squares in this way and one of them has a number in it, the appropriate number will appear in the other square. If you connect two non-numbered squares, they will be assigned temporary algebraic labels: on the first occasion, they will be labelled &#8216;<code>a</code>&#8217; and &#8216;<code>a+1</code>&#8217;, and then &#8216;<code>b</code>&#8217; and &#8216;<code>b+1</code>&#8217;, and so on. Connecting more squares on to the ends of such a chain will cause them all to be labelled with the same letter.
37</p>
38<p>
39When you left-click or right-click in a square, the legal squares to connect it to will be shown.
40</p>
41<p>
42The arrow in each square starts off black, and goes grey once you connect the square to its successor. Also, each square which needs a predecessor has a small dot in the bottom left corner, which vanishes once you link a square to it. So your aim is always to connect a square with a black arrow to a square with a dot.
43</p>
44<p>
45To remove any links for a particular square (both incoming and outgoing), left-drag it off the grid. To remove a whole chain, right-drag any square in the chain off the grid.
46</p>
47<p>
48You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a square starts a link operation, and pressing the return key again over a square will finish the link, if allowable. Pressing the space bar over a square will show the other squares pointing to it, and allow you to form a backward link, and pressing the space bar again cancels this.
49</p>
50<p>
51(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
52</p>
53<h2><a name="S34.2"></a>34.2 <a name="i3"></a>Signpost parameters</h2>
54<p>
55These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
56</p>
57<dl><dt>
58<em>Width</em>, <em>Height</em>
59</dt>
60<dd>
61Size of grid in squares.
62</dd>
63<dt>
64<em>Force start/end to corners</em>
65</dt>
66<dd>
67If true, the start and end squares are always placed in opposite corners (the start at the top left, and the end at the bottom right). If false the start and end squares are placed randomly (although always both shown).
68</dd>
69</dl>
70
71<hr><address></address></body>
72</html>
diff --git a/apps/plugins/puzzles/src/singles.R b/apps/plugins/puzzles/src/singles.R
new file mode 100644
index 0000000000..2d10c4b388
--- /dev/null
+++ b/apps/plugins/puzzles/src/singles.R
@@ -0,0 +1,23 @@
1# -*- makefile -*-
2
3SINGLES_EXTRA = dsf latin maxflow tree234
4
5singles : [X] GTK COMMON singles SINGLES_EXTRA singles-icon|no-icon
6singles : [G] WINDOWS COMMON singles SINGLES_EXTRA singles.res|noicon.res
7
8ALL += singles[COMBINED] SINGLES_EXTRA
9
10singlessolver : [U] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE
11singlessolver : [C] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE
12
13!begin am gtk
14GAMES += singles
15!end
16
17!begin >list.c
18 A(singles) \
19!end
20
21!begin >gamedesc.txt
22singles:singles.exe:Singles:Number-removing puzzle:Black out the right set of duplicate numbers.
23!end
diff --git a/apps/plugins/puzzles/src/singles.c b/apps/plugins/puzzles/src/singles.c
new file mode 100644
index 0000000000..5fe054c663
--- /dev/null
+++ b/apps/plugins/puzzles/src/singles.c
@@ -0,0 +1,2004 @@
1/*
2 * singles.c: implementation of Hitori ('let me alone') from Nikoli.
3 *
4 * Make single-get able to fetch a specific puzzle ID from menneske.no?
5 *
6 * www.menneske.no solving methods:
7 *
8 * Done:
9 * SC: if you circle a cell, any cells in same row/col with same no --> black
10 * -- solver_op_circle
11 * SB: if you make a cell black, any cells around it --> white
12 * -- solver_op_blacken
13 * ST: 3 identical cells in row, centre is white and outer two black.
14 * SP: 2 identical cells with single-cell gap, middle cell is white.
15 * -- solver_singlesep (both ST and SP)
16 * PI: if you have a pair of same number in row/col, any other
17 * cells of same number must be black.
18 * -- solve_doubles
19 * CC: if you have a black on edge one cell away from corner, cell
20 * on edge diag. adjacent must be white.
21 * CE: if you have 2 black cells of triangle on edge, third cell must
22 * be white.
23 * QM: if you have 3 black cells of diagonal square in middle, fourth
24 * cell must be white.
25 * -- solve_allblackbutone (CC, CE, and QM).
26 * QC: a corner with 4 identical numbers (or 2 and 2) must have the
27 * corner cell (and cell diagonal to that) black.
28 * TC: a corner with 3 identical numbers (with the L either way)
29 * must have the apex of L black, and other two white.
30 * DC: a corner with 2 identical numbers in domino can set a white
31 * cell along wall.
32 * -- solve_corners (QC, TC, DC)
33 * IP: pair with one-offset-pair force whites by offset pair
34 * -- solve_offsetpair
35 * MC: any cells diag. adjacent to black cells that would split board
36 * into separate white regions must be white.
37 * -- solve_removesplits
38 *
39 * Still to do:
40 *
41 * TEP: 3 pairs of dominos parallel to side, can mark 4 white cells
42 * alongside.
43 * DEP: 2 pairs of dominos parallel to side, can mark 2 white cells.
44 * FI: if you have two sets of double-cells packed together, singles
45 * in that row/col must be white (qv. PI)
46 * QuM: four identical cells (or 2 and 2) in middle of grid only have
47 * two possible solutions each.
48 * FDE: doubles one row/column away from edge can force a white cell.
49 * FDM: doubles in centre (next to bits of diag. square) can force a white cell.
50 * MP: two pairs with same number between force number to black.
51 * CnC: if circling a cell leads to impossible board, cell is black.
52 * MC: if we have two possiblilities, can we force a white circle?
53 *
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <assert.h>
60#include <ctype.h>
61#include <math.h>
62
63#include "puzzles.h"
64#include "latin.h"
65
66#ifdef STANDALONE_SOLVER
67int verbose = 0;
68#endif
69
70#define PREFERRED_TILE_SIZE 32
71#define TILE_SIZE (ds->tilesize)
72#define BORDER (TILE_SIZE / 2)
73
74#define CRAD ((TILE_SIZE / 2) - 1)
75#define TEXTSZ ((14*CRAD/10) - 1) /* 2 * sqrt(2) of CRAD */
76
77#define COORD(x) ( (x) * TILE_SIZE + BORDER )
78#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
79
80#define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
81
82#define FLASH_TIME 0.7F
83
84enum {
85 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
86 COL_BLACK, COL_WHITE, COL_BLACKNUM, COL_GRID,
87 COL_CURSOR, COL_ERROR,
88 NCOLOURS
89};
90
91struct game_params {
92 int w, h, diff;
93};
94
95#define F_BLACK 0x1
96#define F_CIRCLE 0x2
97#define F_ERROR 0x4
98#define F_SCRATCH 0x8
99
100struct game_state {
101 int w, h, n, o; /* n = w*h; o = max(w, h) */
102 int completed, used_solve, impossible;
103 int *nums; /* size w*h */
104 unsigned int *flags; /* size w*h */
105};
106
107/* top, right, bottom, left */
108static const int dxs[4] = { 0, 1, 0, -1 };
109static const int dys[4] = { -1, 0, 1, 0 };
110
111/* --- Game parameters and preset functions --- */
112
113#define DIFFLIST(A) \
114 A(EASY,Easy,e) \
115 A(TRICKY,Tricky,k)
116
117#define ENUM(upper,title,lower) DIFF_ ## upper,
118#define TITLE(upper,title,lower) #title,
119#define ENCODE(upper,title,lower) #lower
120#define CONFIG(upper,title,lower) ":" #title
121
122enum { DIFFLIST(ENUM) DIFF_MAX, DIFF_ANY };
123static char const *const singles_diffnames[] = { DIFFLIST(TITLE) };
124static char const singles_diffchars[] = DIFFLIST(ENCODE);
125#define DIFFCOUNT lenof(singles_diffchars)
126#define DIFFCONFIG DIFFLIST(CONFIG)
127
128static game_params *default_params(void)
129{
130 game_params *ret = snew(game_params);
131 ret->w = ret->h = 5;
132 ret->diff = DIFF_EASY;
133
134 return ret;
135}
136
137static const struct game_params singles_presets[] = {
138 { 5, 5, DIFF_EASY },
139 { 5, 5, DIFF_TRICKY },
140 { 6, 6, DIFF_EASY },
141 { 6, 6, DIFF_TRICKY },
142 { 8, 8, DIFF_EASY },
143 { 8, 8, DIFF_TRICKY },
144 { 10, 10, DIFF_EASY },
145 { 10, 10, DIFF_TRICKY },
146 { 12, 12, DIFF_EASY },
147 { 12, 12, DIFF_TRICKY }
148};
149
150static int game_fetch_preset(int i, char **name, game_params **params)
151{
152 game_params *ret;
153 char buf[80];
154
155 if (i < 0 || i >= lenof(singles_presets))
156 return FALSE;
157
158 ret = default_params();
159 *ret = singles_presets[i];
160 *params = ret;
161
162 sprintf(buf, "%dx%d %s", ret->w, ret->h, singles_diffnames[ret->diff]);
163 *name = dupstr(buf);
164
165 return TRUE;
166}
167
168static void free_params(game_params *params)
169{
170 sfree(params);
171}
172
173static game_params *dup_params(const game_params *params)
174{
175 game_params *ret = snew(game_params);
176 *ret = *params; /* structure copy */
177 return ret;
178}
179
180static void decode_params(game_params *ret, char const *string)
181{
182 char const *p = string;
183 int i;
184
185 ret->w = ret->h = atoi(p);
186 while (*p && isdigit((unsigned char)*p)) p++;
187 if (*p == 'x') {
188 p++;
189 ret->h = atoi(p);
190 while (*p && isdigit((unsigned char)*p)) p++;
191 }
192 if (*p == 'd') {
193 ret->diff = DIFF_MAX; /* which is invalid */
194 p++;
195 for (i = 0; i < DIFFCOUNT; i++) {
196 if (*p == singles_diffchars[i])
197 ret->diff = i;
198 }
199 p++;
200 }
201}
202
203static char *encode_params(const game_params *params, int full)
204{
205 char data[256];
206
207 if (full)
208 sprintf(data, "%dx%dd%c", params->w, params->h, singles_diffchars[params->diff]);
209 else
210 sprintf(data, "%dx%d", params->w, params->h);
211
212 return dupstr(data);
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 = "Width";
223 ret[0].type = C_STRING;
224 sprintf(buf, "%d", params->w);
225 ret[0].sval = dupstr(buf);
226 ret[0].ival = 0;
227
228 ret[1].name = "Height";
229 ret[1].type = C_STRING;
230 sprintf(buf, "%d", params->h);
231 ret[1].sval = dupstr(buf);
232 ret[1].ival = 0;
233
234 ret[2].name = "Difficulty";
235 ret[2].type = C_CHOICES;
236 ret[2].sval = DIFFCONFIG;
237 ret[2].ival = params->diff;
238
239 ret[3].name = NULL;
240 ret[3].type = C_END;
241 ret[3].sval = NULL;
242 ret[3].ival = 0;
243
244 return ret;
245}
246
247static game_params *custom_params(const config_item *cfg)
248{
249 game_params *ret = snew(game_params);
250
251 ret->w = atoi(cfg[0].sval);
252 ret->h = atoi(cfg[1].sval);
253 ret->diff = cfg[2].ival;
254
255 return ret;
256}
257
258static char *validate_params(const game_params *params, int full)
259{
260 if (params->w < 2 || params->h < 2)
261 return "Width and neight must be at least two";
262 if (params->w > 10+26+26 || params->h > 10+26+26)
263 return "Puzzle is too large";
264 if (full) {
265 if (params->diff < 0 || params->diff >= DIFF_MAX)
266 return "Unknown difficulty rating";
267 }
268
269 return NULL;
270}
271
272/* --- Game description string generation and unpicking --- */
273
274static game_state *blank_game(int w, int h)
275{
276 game_state *state = snew(game_state);
277
278 memset(state, 0, sizeof(game_state));
279 state->w = w;
280 state->h = h;
281 state->n = w*h;
282 state->o = max(w,h);
283
284 state->completed = state->used_solve = state->impossible = 0;
285
286 state->nums = snewn(state->n, int);
287 state->flags = snewn(state->n, unsigned int);
288
289 memset(state->nums, 0, state->n*sizeof(int));
290 memset(state->flags, 0, state->n*sizeof(unsigned int));
291
292 return state;
293}
294
295static game_state *dup_game(const game_state *state)
296{
297 game_state *ret = blank_game(state->w, state->h);
298
299 ret->completed = state->completed;
300 ret->used_solve = state->used_solve;
301 ret->impossible = state->impossible;
302
303 memcpy(ret->nums, state->nums, state->n*sizeof(int));
304 memcpy(ret->flags, state->flags, state->n*sizeof(unsigned int));
305
306 return ret;
307}
308
309static void free_game(game_state *state)
310{
311 sfree(state->nums);
312 sfree(state->flags);
313 sfree(state);
314}
315
316static char n2c(int num) {
317 if (num < 10)
318 return '0' + num;
319 else if (num < 10+26)
320 return 'a' + num - 10;
321 else
322 return 'A' + num - 10 - 26;
323 return '?';
324}
325
326static int c2n(char c) {
327 if (isdigit((unsigned char)c))
328 return (int)(c - '0');
329 else if (c >= 'a' && c <= 'z')
330 return (int)(c - 'a' + 10);
331 else if (c >= 'A' && c <= 'Z')
332 return (int)(c - 'A' + 10 + 26);
333 return -1;
334}
335
336static void unpick_desc(const game_params *params, const char *desc,
337 game_state **sout, char **mout)
338{
339 game_state *state = blank_game(params->w, params->h);
340 char *msg = NULL;
341 int num = 0, i = 0;
342
343 if (strlen(desc) != state->n) {
344 msg = "Game description is wrong length";
345 goto done;
346 }
347 for (i = 0; i < state->n; i++) {
348 num = c2n(desc[i]);
349 if (num <= 0 || num > state->o) {
350 msg = "Game description contains unexpected characters";
351 goto done;
352 }
353 state->nums[i] = num;
354 }
355done:
356 if (msg) { /* sth went wrong. */
357 if (mout) *mout = msg;
358 free_game(state);
359 } else {
360 if (mout) *mout = NULL;
361 if (sout) *sout = state;
362 else free_game(state);
363 }
364}
365
366static char *generate_desc(game_state *state, int issolve)
367{
368 char *ret = snewn(state->n+1+(issolve?1:0), char);
369 int i, p=0;
370
371 if (issolve)
372 ret[p++] = 'S';
373 for (i = 0; i < state->n; i++)
374 ret[p++] = n2c(state->nums[i]);
375 ret[p] = '\0';
376 return ret;
377}
378
379/* --- Useful game functions (completion, etc.) --- */
380
381static int game_can_format_as_text_now(const game_params *params)
382{
383 return TRUE;
384}
385
386static char *game_text_format(const game_state *state)
387{
388 int len, x, y, i;
389 char *ret, *p;
390
391 len = (state->w)*2; /* one row ... */
392 len = len * (state->h*2); /* ... h rows, including gaps ... */
393 len += 1; /* ... final NL */
394 p = ret = snewn(len, char);
395
396 for (y = 0; y < state->h; y++) {
397 for (x = 0; x < state->w; x++) {
398 i = y*state->w + x;
399 if (x > 0) *p++ = ' ';
400 *p++ = (state->flags[i] & F_BLACK) ? '*' : n2c(state->nums[i]);
401 }
402 *p++ = '\n';
403 for (x = 0; x < state->w; x++) {
404 i = y*state->w + x;
405 if (x > 0) *p++ = ' ';
406 *p++ = (state->flags[i] & F_CIRCLE) ? '~' : ' ';
407 }
408 *p++ = '\n';
409 }
410 *p++ = '\0';
411 assert(p - ret == len);
412
413 return ret;
414}
415
416static void debug_state(const char *desc, game_state *state) {
417 char *dbg = game_text_format(state);
418 debug(("%s:\n%s", desc, dbg));
419 sfree(dbg);
420}
421
422static void connect_if_same(game_state *state, int *dsf, int i1, int i2)
423{
424 int c1, c2;
425
426 if ((state->flags[i1] & F_BLACK) != (state->flags[i2] & F_BLACK))
427 return;
428
429 c1 = dsf_canonify(dsf, i1);
430 c2 = dsf_canonify(dsf, i2);
431 dsf_merge(dsf, c1, c2);
432}
433
434static void connect_dsf(game_state *state, int *dsf)
435{
436 int x, y, i;
437
438 /* Construct a dsf array for connected blocks; connections
439 * tracked to right and down. */
440 dsf_init(dsf, state->n);
441 for (x = 0; x < state->w; x++) {
442 for (y = 0; y < state->h; y++) {
443 i = y*state->w + x;
444
445 if (x < state->w-1)
446 connect_if_same(state, dsf, i, i+1); /* right */
447 if (y < state->h-1)
448 connect_if_same(state, dsf, i, i+state->w); /* down */
449 }
450 }
451}
452
453#define CC_MARK_ERRORS 1
454#define CC_MUST_FILL 2
455
456static int check_rowcol(game_state *state, int starti, int di, int sz, unsigned flags)
457{
458 int nerr = 0, n, m, i, j;
459
460 /* if any circled numbers have identical non-circled numbers on
461 * same row/column, error (non-circled)
462 * if any circled numbers in same column are same number, highlight them.
463 * if any rows/columns have >1 of same number, not complete. */
464
465 for (n = 0, i = starti; n < sz; n++, i += di) {
466 if (state->flags[i] & F_BLACK) continue;
467 for (m = n+1, j = i+di; m < sz; m++, j += di) {
468 if (state->flags[j] & F_BLACK) continue;
469 if (state->nums[i] != state->nums[j]) continue;
470
471 nerr++; /* ok, we have two numbers the same in a row. */
472 if (!(flags & CC_MARK_ERRORS)) continue;
473
474 /* If we have two circles in the same row around
475 * two identical numbers, they are _both_ wrong. */
476 if ((state->flags[i] & F_CIRCLE) &&
477 (state->flags[j] & F_CIRCLE)) {
478 state->flags[i] |= F_ERROR;
479 state->flags[j] |= F_ERROR;
480 }
481 /* Otherwise, if we have a circle, any other identical
482 * numbers in that row are obviously wrong. We don't
483 * highlight this, however, since it makes the process
484 * of solving the puzzle too easy (you circle a number
485 * and it promptly tells you which numbers to blacken! */
486#if 0
487 else if (state->flags[i] & F_CIRCLE)
488 state->flags[j] |= F_ERROR;
489 else if (state->flags[j] & F_CIRCLE)
490 state->flags[i] |= F_ERROR;
491#endif
492 }
493 }
494 return nerr;
495}
496
497static int check_complete(game_state *state, unsigned flags)
498{
499 int *dsf = snewn(state->n, int);
500 int x, y, i, error = 0, nwhite, w = state->w, h = state->h;
501
502 if (flags & CC_MARK_ERRORS) {
503 for (i = 0; i < state->n; i++)
504 state->flags[i] &= ~F_ERROR;
505 }
506 connect_dsf(state, dsf);
507
508 /* If we're the solver we need the grid all to be definitively
509 * black or definitively white (i.e. circled) otherwise the solver
510 * has found an ambiguous grid. */
511 if (flags & CC_MUST_FILL) {
512 for (i = 0; i < state->n; i++) {
513 if (!(state->flags[i] & F_BLACK) && !(state->flags[i] & F_CIRCLE))
514 error += 1;
515 }
516 }
517
518 /* Mark any black squares in groups of >1 as errors.
519 * Count number of white squares. */
520 nwhite = 0;
521 for (i = 0; i < state->n; i++) {
522 if (state->flags[i] & F_BLACK) {
523 if (dsf_size(dsf, i) > 1) {
524 error += 1;
525 if (flags & CC_MARK_ERRORS)
526 state->flags[i] |= F_ERROR;
527 }
528 } else
529 nwhite += 1;
530 }
531
532 /* Check attributes of white squares, row- and column-wise. */
533 for (x = 0; x < w; x++) /* check cols from (x,0) */
534 error += check_rowcol(state, x, w, h, flags);
535 for (y = 0; y < h; y++) /* check rows from (0,y) */
536 error += check_rowcol(state, y*w, 1, w, flags);
537
538 /* If there's more than one white region, pick the largest one to
539 * be the canonical one (arbitrarily tie-breaking towards lower
540 * array indices), and mark all the others as erroneous. */
541 {
542 int largest = 0, canonical = -1;
543 for (i = 0; i < state->n; i++)
544 if (!(state->flags[i] & F_BLACK)) {
545 int size = dsf_size(dsf, i);
546 if (largest < size) {
547 largest = size;
548 canonical = i;
549 }
550 }
551
552 if (largest < nwhite) {
553 for (i = 0; i < state->n; i++)
554 if (!(state->flags[i] & F_BLACK) &&
555 dsf_canonify(dsf, i) != canonical) {
556 error += 1;
557 if (flags & CC_MARK_ERRORS)
558 state->flags[i] |= F_ERROR;
559 }
560 }
561 }
562
563 sfree(dsf);
564 return (error > 0) ? 0 : 1;
565}
566
567static char *game_state_diff(const game_state *src, const game_state *dst,
568 int issolve)
569{
570 char *ret = NULL, buf[80], c;
571 int retlen = 0, x, y, i, k;
572 unsigned int fmask = F_BLACK | F_CIRCLE;
573
574 assert(src->n == dst->n);
575
576 if (issolve) {
577 ret = sresize(ret, 3, char);
578 ret[0] = 'S'; ret[1] = ';'; ret[2] = '\0';
579 retlen += 2;
580 }
581
582 for (x = 0; x < dst->w; x++) {
583 for (y = 0; y < dst->h; y++) {
584 i = y*dst->w + x;
585 if ((src->flags[i] & fmask) != (dst->flags[i] & fmask)) {
586 assert((dst->flags[i] & fmask) != fmask);
587 if (dst->flags[i] & F_BLACK)
588 c = 'B';
589 else if (dst->flags[i] & F_CIRCLE)
590 c = 'C';
591 else
592 c = 'E';
593 k = sprintf(buf, "%c%d,%d;", (int)c, x, y);
594 ret = sresize(ret, retlen + k + 1, char);
595 strcpy(ret + retlen, buf);
596 retlen += k;
597 }
598 }
599 }
600 return ret;
601}
602
603/* --- Solver --- */
604
605enum { BLACK, CIRCLE };
606
607struct solver_op {
608 int x, y, op; /* op one of BLACK or CIRCLE. */
609 const char *desc; /* must be non-malloced. */
610};
611
612struct solver_state {
613 struct solver_op *ops;
614 int n_ops, n_alloc;
615 int *scratch;
616};
617
618static struct solver_state *solver_state_new(game_state *state)
619{
620 struct solver_state *ss = snew(struct solver_state);
621
622 ss->ops = NULL;
623 ss->n_ops = ss->n_alloc = 0;
624 ss->scratch = snewn(state->n, int);
625
626 return ss;
627}
628
629static void solver_state_free(struct solver_state *ss)
630{
631 sfree(ss->scratch);
632 if (ss->ops) sfree(ss->ops);
633 sfree(ss);
634}
635
636static void solver_op_add(struct solver_state *ss, int x, int y, int op, const char *desc)
637{
638 struct solver_op *sop;
639
640 if (ss->n_alloc < ss->n_ops + 1) {
641 ss->n_alloc = (ss->n_alloc + 1) * 2;
642 ss->ops = sresize(ss->ops, ss->n_alloc, struct solver_op);
643 }
644 sop = &(ss->ops[ss->n_ops++]);
645 sop->x = x; sop->y = y; sop->op = op; sop->desc = desc;
646 debug(("added solver op %s ('%s') at (%d,%d)\n",
647 op == BLACK ? "BLACK" : "CIRCLE", desc, x, y));
648}
649
650static void solver_op_circle(game_state *state, struct solver_state *ss,
651 int x, int y)
652{
653 int i = y*state->w + x;
654
655 if (!INGRID(state, x, y)) return;
656 if (state->flags[i] & F_BLACK) {
657 debug(("... solver wants to add auto-circle on black (%d,%d)\n", x, y));
658 state->impossible = 1;
659 return;
660 }
661 /* Only add circle op if it's not already circled. */
662 if (!(state->flags[i] & F_CIRCLE)) {
663 solver_op_add(ss, x, y, CIRCLE, "SB - adjacent to black square");
664 }
665}
666
667static void solver_op_blacken(game_state *state, struct solver_state *ss,
668 int x, int y, int num)
669{
670 int i = y*state->w + x;
671
672 if (!INGRID(state, x, y)) return;
673 if (state->nums[i] != num) return;
674 if (state->flags[i] & F_CIRCLE) {
675 debug(("... solver wants to add auto-black on circled(%d,%d)\n", x, y));
676 state->impossible = 1;
677 return;
678 }
679 /* Only add black op if it's not already black. */
680 if (!(state->flags[i] & F_BLACK)) {
681 solver_op_add(ss, x, y, BLACK, "SC - number on same row/col as circled");
682 }
683}
684
685static int solver_ops_do(game_state *state, struct solver_state *ss)
686{
687 int next_op = 0, i, x, y, n_ops = 0;
688 struct solver_op op;
689
690 /* Care here: solver_op_* may call solver_op_add which may extend the
691 * ss->n_ops. */
692
693 while (next_op < ss->n_ops) {
694 op = ss->ops[next_op++]; /* copy this away, it may get reallocated. */
695 i = op.y*state->w + op.x;
696
697 if (op.op == BLACK) {
698 if (state->flags[i] & F_CIRCLE) {
699 debug(("Solver wants to blacken circled square (%d,%d)!\n", op.x, op.y));
700 state->impossible = 1;
701 return n_ops;
702 }
703 if (!(state->flags[i] & F_BLACK)) {
704 debug(("... solver adding black at (%d,%d): %s\n", op.x, op.y, op.desc));
705#ifdef STANDALONE_SOLVER
706 if (verbose)
707 printf("Adding black at (%d,%d): %s\n", op.x, op.y, op.desc);
708#endif
709 state->flags[i] |= F_BLACK;
710 /*debug_state("State after adding black", state);*/
711 n_ops++;
712 solver_op_circle(state, ss, op.x-1, op.y);
713 solver_op_circle(state, ss, op.x+1, op.y);
714 solver_op_circle(state, ss, op.x, op.y-1);
715 solver_op_circle(state, ss, op.x, op.y+1);
716 }
717 } else {
718 if (state->flags[i] & F_BLACK) {
719 debug(("Solver wants to circle blackened square (%d,%d)!\n", op.x, op.y));
720 state->impossible = 1;
721 return n_ops;
722 }
723 if (!(state->flags[i] & F_CIRCLE)) {
724 debug(("... solver adding circle at (%d,%d): %s\n", op.x, op.y, op.desc));
725#ifdef STANDALONE_SOLVER
726 if (verbose)
727 printf("Adding circle at (%d,%d): %s\n", op.x, op.y, op.desc);
728#endif
729 state->flags[i] |= F_CIRCLE;
730 /*debug_state("State after adding circle", state);*/
731 n_ops++;
732 for (x = 0; x < state->w; x++) {
733 if (x != op.x)
734 solver_op_blacken(state, ss, x, op.y, state->nums[i]);
735 }
736 for (y = 0; y < state->h; y++) {
737 if (y != op.y)
738 solver_op_blacken(state, ss, op.x, y, state->nums[i]);
739 }
740 }
741 }
742 }
743 ss->n_ops = 0;
744 return n_ops;
745}
746
747/* If the grid has two identical numbers with one cell between them, the inner
748 * cell _must_ be white (and thus circled); (at least) one of the two must be
749 * black (since they're in the same column or row) and thus the middle cell is
750 * next to a black cell. */
751static int solve_singlesep(game_state *state, struct solver_state *ss)
752{
753 int x, y, i, ir, irr, id, idd, n_ops = ss->n_ops;
754
755 for (x = 0; x < state->w; x++) {
756 for (y = 0; y < state->h; y++) {
757 i = y*state->w + x;
758
759 /* Cell two to our right? */
760 ir = i + 1; irr = ir + 1;
761 if (x < (state->w-2) &&
762 state->nums[i] == state->nums[irr] &&
763 !(state->flags[ir] & F_CIRCLE)) {
764 solver_op_add(ss, x+1, y, CIRCLE, "SP/ST - between identical nums");
765 }
766 /* Cell two below us? */
767 id = i + state->w; idd = id + state->w;
768 if (y < (state->h-2) &&
769 state->nums[i] == state->nums[idd] &&
770 !(state->flags[id] & F_CIRCLE)) {
771 solver_op_add(ss, x, y+1, CIRCLE, "SP/ST - between identical nums");
772 }
773 }
774 }
775 return ss->n_ops - n_ops;
776}
777
778/* If we have two identical numbers next to each other (in a row or column),
779 * any other identical numbers in that column must be black. */
780static int solve_doubles(game_state *state, struct solver_state *ss)
781{
782 int x, y, i, ii, n_ops = ss->n_ops, xy;
783
784 for (y = 0, i = 0; y < state->h; y++) {
785 for (x = 0; x < state->w; x++, i++) {
786 assert(i == y*state->w+x);
787 if (state->flags[i] & F_BLACK) continue;
788
789 ii = i+1; /* check cell to our right. */
790 if (x < (state->w-1) &&
791 !(state->flags[ii] & F_BLACK) &&
792 state->nums[i] == state->nums[ii]) {
793 for (xy = 0; xy < state->w; xy++) {
794 if (xy == x || xy == (x+1)) continue;
795 if (state->nums[y*state->w + xy] == state->nums[i] &&
796 !(state->flags[y*state->w + xy] & F_BLACK))
797 solver_op_add(ss, xy, y, BLACK, "PI - same row as pair");
798 }
799 }
800
801 ii = i+state->w; /* check cell below us */
802 if (y < (state->h-1) &&
803 !(state->flags[ii] & F_BLACK) &&
804 state->nums[i] == state->nums[ii]) {
805 for (xy = 0; xy < state->h; xy++) {
806 if (xy == y || xy == (y+1)) continue;
807 if (state->nums[xy*state->w + x] == state->nums[i] &&
808 !(state->flags[xy*state->w + x] & F_BLACK))
809 solver_op_add(ss, x, xy, BLACK, "PI - same col as pair");
810 }
811 }
812 }
813 }
814 return ss->n_ops - n_ops;
815}
816
817/* If a white square has all-but-one possible adjacent squares black, the
818 * one square left over must be white. */
819static int solve_allblackbutone(game_state *state, struct solver_state *ss)
820{
821 int x, y, i, n_ops = ss->n_ops, xd, yd, id, ifree;
822 int dis[4], d;
823
824 dis[0] = -state->w;
825 dis[1] = 1;
826 dis[2] = state->w;
827 dis[3] = -1;
828
829 for (y = 0, i = 0; y < state->h; y++) {
830 for (x = 0; x < state->w; x++, i++) {
831 assert(i == y*state->w+x);
832 if (state->flags[i] & F_BLACK) continue;
833
834 ifree = -1;
835 for (d = 0; d < 4; d++) {
836 xd = x + dxs[d]; yd = y + dys[d]; id = i + dis[d];
837 if (!INGRID(state, xd, yd)) continue;
838
839 if (state->flags[id] & F_CIRCLE)
840 goto skip; /* this cell already has a way out */
841 if (!(state->flags[id] & F_BLACK)) {
842 if (ifree != -1)
843 goto skip; /* this cell has >1 white cell around it. */
844 ifree = id;
845 }
846 }
847 if (ifree != -1)
848 solver_op_add(ss, ifree%state->w, ifree/state->w, CIRCLE,
849 "CC/CE/QM: white cell with single non-black around it");
850 else {
851 debug(("White cell with no escape at (%d,%d)\n", x, y));
852 state->impossible = 1;
853 return 0;
854 }
855skip: ;
856 }
857 }
858 return ss->n_ops - n_ops;
859}
860
861/* If we have 4 numbers the same in a 2x2 corner, the far corner and the
862 * diagonally-adjacent square must both be black.
863 * If we have 3 numbers the same in a 2x2 corner, the apex of the L
864 * thus formed must be black.
865 * If we have 2 numbers the same in a 2x2 corner, the non-same cell
866 * one away from the corner must be white. */
867static void solve_corner(game_state *state, struct solver_state *ss,
868 int x, int y, int dx, int dy)
869{
870 int is[4], ns[4], xx, yy, w = state->w;
871
872 for (yy = 0; yy < 2; yy++) {
873 for (xx = 0; xx < 2; xx++) {
874 is[yy*2+xx] = (y + dy*yy) * w + (x + dx*xx);
875 ns[yy*2+xx] = state->nums[is[yy*2+xx]];
876 }
877 } /* order is now (corner, side 1, side 2, inner) */
878
879 if (ns[0] == ns[1] && ns[0] == ns[2] && ns[0] == ns[3]) {
880 solver_op_add(ss, is[0]%w, is[0]/w, BLACK, "QC: corner with 4 matching");
881 solver_op_add(ss, is[3]%w, is[3]/w, BLACK, "QC: corner with 4 matching");
882 } else if (ns[0] == ns[1] && ns[0] == ns[2]) {
883 /* corner and 2 sides: apex is corner. */
884 solver_op_add(ss, is[0]%w, is[0]/w, BLACK, "TC: corner apex from 3 matching");
885 } else if (ns[1] == ns[2] && ns[1] == ns[3]) {
886 /* side, side, fourth: apex is fourth. */
887 solver_op_add(ss, is[3]%w, is[3]/w, BLACK, "TC: inside apex from 3 matching");
888 } else if (ns[0] == ns[1] || ns[1] == ns[3]) {
889 /* either way here we match the non-identical side. */
890 solver_op_add(ss, is[2]%w, is[2]/w, CIRCLE, "DC: corner with 2 matching");
891 } else if (ns[0] == ns[2] || ns[2] == ns[3]) {
892 /* ditto */
893 solver_op_add(ss, is[1]%w, is[1]/w, CIRCLE, "DC: corner with 2 matching");
894 }
895}
896
897static int solve_corners(game_state *state, struct solver_state *ss)
898{
899 int n_ops = ss->n_ops;
900
901 solve_corner(state, ss, 0, 0, 1, 1);
902 solve_corner(state, ss, state->w-1, 0, -1, 1);
903 solve_corner(state, ss, state->w-1, state->h-1, -1, -1);
904 solve_corner(state, ss, 0, state->h-1, 1, -1);
905
906 return ss->n_ops - n_ops;
907}
908
909/* If you have the following situation:
910 * ...
911 * ...x A x x y A x...
912 * ...x B x x B y x...
913 * ...
914 * then both squares marked 'y' must be white. One of the left-most A or B must
915 * be white (since two side-by-side black cells are disallowed), which means
916 * that the corresponding right-most A or B must be black (since you can't
917 * have two of the same number on one line); thus, the adjacent squares
918 * to that right-most A or B must be white, which include the two marked 'y'
919 * in either case.
920 * Obviously this works in any row or column. It also works if A == B.
921 * It doesn't work for the degenerate case:
922 * ...x A A x x
923 * ...x B y x x
924 * where the square marked 'y' isn't necessarily white (consider the left-most A
925 * is black).
926 *
927 * */
928static void solve_offsetpair_pair(game_state *state, struct solver_state *ss,
929 int x1, int y1, int x2, int y2)
930{
931 int ox, oy, w = state->w, ax, ay, an, d, dx[2], dy[2], dn, xd, yd;
932
933 if (x1 == x2) { /* same column */
934 ox = 1; oy = 0;
935 } else {
936 assert(y1 == y2);
937 ox = 0; oy = 1;
938 }
939
940 /* We try adjacent to (x1,y1) and the two diag. adjacent to (x2, y2).
941 * We expect to be called twice, once each way around. */
942 ax = x1+ox; ay = y1+oy;
943 assert(INGRID(state, ax, ay));
944 an = state->nums[ay*w + ax];
945
946 dx[0] = x2 + ox + oy; dx[1] = x2 + ox - oy;
947 dy[0] = y2 + oy + ox; dy[1] = y2 + oy - ox;
948
949 for (d = 0; d < 2; d++) {
950 if (INGRID(state, dx[d], dy[d]) && (dx[d] != ax || dy[d] != ay)) {
951 /* The 'dx != ax || dy != ay' removes the degenerate case,
952 * mentioned above. */
953 dn = state->nums[dy[d]*w + dx[d]];
954 if (an == dn) {
955 /* We have a match; so (WLOG) the 'A' marked above are at
956 * (x1,y1) and (x2,y2), and the 'B' are at (ax,ay) and (dx,dy). */
957 debug(("Found offset-pair: %d at (%d,%d) and (%d,%d)\n",
958 state->nums[y1*w + x1], x1, y1, x2, y2));
959 debug((" and: %d at (%d,%d) and (%d,%d)\n",
960 an, ax, ay, dx[d], dy[d]));
961
962 xd = dx[d] - x2; yd = dy[d] - y2;
963 solver_op_add(ss, x2 + xd, y2, CIRCLE, "IP: next to offset-pair");
964 solver_op_add(ss, x2, y2 + yd, CIRCLE, "IP: next to offset-pair");
965 }
966 }
967 }
968}
969
970static int solve_offsetpair(game_state *state, struct solver_state *ss)
971{
972 int n_ops = ss->n_ops, x, xx, y, yy, n1, n2;
973
974 for (x = 0; x < state->w-1; x++) {
975 for (y = 0; y < state->h; y++) {
976 n1 = state->nums[y*state->w + x];
977 for (yy = y+1; yy < state->h; yy++) {
978 n2 = state->nums[yy*state->w + x];
979 if (n1 == n2) {
980 solve_offsetpair_pair(state, ss, x, y, x, yy);
981 solve_offsetpair_pair(state, ss, x, yy, x, y);
982 }
983 }
984 }
985 }
986 for (y = 0; y < state->h-1; y++) {
987 for (x = 0; x < state->w; x++) {
988 n1 = state->nums[y*state->w + x];
989 for (xx = x+1; xx < state->w; xx++) {
990 n2 = state->nums[y*state->w + xx];
991 if (n1 == n2) {
992 solve_offsetpair_pair(state, ss, x, y, xx, y);
993 solve_offsetpair_pair(state, ss, xx, y, x, y);
994 }
995 }
996 }
997 }
998 return ss->n_ops - n_ops;
999}
1000
1001static int solve_hassinglewhiteregion(game_state *state, struct solver_state *ss)
1002{
1003 int i, j, nwhite = 0, lwhite = -1, szwhite, start, end, next, a, d, x, y;
1004
1005 for (i = 0; i < state->n; i++) {
1006 if (!(state->flags[i] & F_BLACK)) {
1007 nwhite++;
1008 lwhite = i;
1009 }
1010 state->flags[i] &= ~F_SCRATCH;
1011 }
1012 if (lwhite == -1) {
1013 debug(("solve_hassinglewhite: no white squares found!\n"));
1014 state->impossible = 1;
1015 return 0;
1016 }
1017 /* We don't use connect_dsf here; it's too slow, and there's a quicker
1018 * algorithm if all we want is the size of one region. */
1019 /* Having written this, this algorithm is only about 5% faster than
1020 * using a dsf. */
1021 memset(ss->scratch, -1, state->n * sizeof(int));
1022 ss->scratch[0] = lwhite;
1023 state->flags[lwhite] |= F_SCRATCH;
1024 start = 0; end = next = 1;
1025 while (start < end) {
1026 for (a = start; a < end; a++) {
1027 i = ss->scratch[a]; assert(i != -1);
1028 for (d = 0; d < 4; d++) {
1029 x = (i % state->w) + dxs[d];
1030 y = (i / state->w) + dys[d];
1031 j = y*state->w + x;
1032 if (!INGRID(state, x, y)) continue;
1033 if (state->flags[j] & (F_BLACK | F_SCRATCH)) continue;
1034 ss->scratch[next++] = j;
1035 state->flags[j] |= F_SCRATCH;
1036 }
1037 }
1038 start = end; end = next;
1039 }
1040 szwhite = next;
1041 return (szwhite == nwhite) ? 1 : 0;
1042}
1043
1044static void solve_removesplits_check(game_state *state, struct solver_state *ss,
1045 int x, int y)
1046{
1047 int i = y*state->w + x, issingle;
1048
1049 if (!INGRID(state, x, y)) return;
1050 if ((state->flags[i] & F_CIRCLE) || (state->flags[i] & F_BLACK))
1051 return;
1052
1053 /* If putting a black square at (x,y) would make the white region
1054 * non-contiguous, it must be circled. */
1055 state->flags[i] |= F_BLACK;
1056 issingle = solve_hassinglewhiteregion(state, ss);
1057 state->flags[i] &= ~F_BLACK;
1058
1059 if (!issingle)
1060 solver_op_add(ss, x, y, CIRCLE, "MC: black square here would split white region");
1061}
1062
1063/* For all black squares, search in squares diagonally adjacent to see if
1064 * we can rule out putting a black square there (because it would make the
1065 * white region non-contiguous). */
1066/* This function is likely to be somewhat slow. */
1067static int solve_removesplits(game_state *state, struct solver_state *ss)
1068{
1069 int i, x, y, n_ops = ss->n_ops;
1070
1071 if (!solve_hassinglewhiteregion(state, ss)) {
1072 debug(("solve_removesplits: white region is not contiguous at start!\n"));
1073 state->impossible = 1;
1074 return 0;
1075 }
1076
1077 for (i = 0; i < state->n; i++) {
1078 if (!(state->flags[i] & F_BLACK)) continue;
1079
1080 x = i%state->w; y = i/state->w;
1081 solve_removesplits_check(state, ss, x-1, y-1);
1082 solve_removesplits_check(state, ss, x+1, y-1);
1083 solve_removesplits_check(state, ss, x+1, y+1);
1084 solve_removesplits_check(state, ss, x-1, y+1);
1085 }
1086 return ss->n_ops - n_ops;
1087}
1088
1089/*
1090 * This function performs a solver step that isn't implicit in the rules
1091 * of the game and is thus treated somewhat differently.
1092 *
1093 * It marks cells whose number does not exist elsewhere in its row/column
1094 * with circles. As it happens the game generator here does mean that this
1095 * is always correct, but it's a solving method that people should not have
1096 * to rely upon (except in the hidden 'sneaky' difficulty setting) and so
1097 * all grids at 'tricky' and above are checked to make sure that the grid
1098 * is no easier if this solving step is performed beforehand.
1099 *
1100 * Calling with ss=NULL just returns the number of sneaky deductions that
1101 * would have been made.
1102 */
1103static int solve_sneaky(game_state *state, struct solver_state *ss)
1104{
1105 int i, ii, x, xx, y, yy, nunique = 0;
1106
1107 /* Clear SCRATCH flags. */
1108 for (i = 0; i < state->n; i++) state->flags[i] &= ~F_SCRATCH;
1109
1110 for (x = 0; x < state->w; x++) {
1111 for (y = 0; y < state->h; y++) {
1112 i = y*state->w + x;
1113
1114 /* Check for duplicate numbers on our row, mark (both) if so */
1115 for (xx = x; xx < state->w; xx++) {
1116 ii = y*state->w + xx;
1117 if (i == ii) continue;
1118
1119 if (state->nums[i] == state->nums[ii]) {
1120 state->flags[i] |= F_SCRATCH;
1121 state->flags[ii] |= F_SCRATCH;
1122 }
1123 }
1124
1125 /* Check for duplicate numbers on our col, mark (both) if so */
1126 for (yy = y; yy < state->h; yy++) {
1127 ii = yy*state->w + x;
1128 if (i == ii) continue;
1129
1130 if (state->nums[i] == state->nums[ii]) {
1131 state->flags[i] |= F_SCRATCH;
1132 state->flags[ii] |= F_SCRATCH;
1133 }
1134 }
1135 }
1136 }
1137
1138 /* Any cell with no marking has no duplicates on its row or column:
1139 * set its CIRCLE. */
1140 for (i = 0; i < state->n; i++) {
1141 if (!(state->flags[i] & F_SCRATCH)) {
1142 if (ss) solver_op_add(ss, i%state->w, i/state->w, CIRCLE,
1143 "SNEAKY: only one of its number in row and col");
1144 nunique += 1;
1145 } else
1146 state->flags[i] &= ~F_SCRATCH;
1147 }
1148 return nunique;
1149}
1150
1151static int solve_specific(game_state *state, int diff, int sneaky)
1152{
1153 struct solver_state *ss = solver_state_new(state);
1154
1155 if (sneaky) solve_sneaky(state, ss);
1156
1157 /* Some solver operations we only have to perform once --
1158 * they're only based on the numbers available, and not black
1159 * squares or circles which may be added later. */
1160
1161 solve_singlesep(state, ss); /* never sets impossible */
1162 solve_doubles(state, ss); /* ditto */
1163 solve_corners(state, ss); /* ditto */
1164
1165 if (diff >= DIFF_TRICKY)
1166 solve_offsetpair(state, ss); /* ditto */
1167
1168 while (1) {
1169 if (ss->n_ops > 0) solver_ops_do(state, ss);
1170 if (state->impossible) break;
1171
1172 if (solve_allblackbutone(state, ss) > 0) continue;
1173 if (state->impossible) break;
1174
1175 if (diff >= DIFF_TRICKY) {
1176 if (solve_removesplits(state, ss) > 0) continue;
1177 if (state->impossible) break;
1178 }
1179
1180 break;
1181 }
1182
1183 solver_state_free(ss);
1184 return state->impossible ? -1 : check_complete(state, CC_MUST_FILL);
1185}
1186
1187static char *solve_game(const game_state *state, const game_state *currstate,
1188 const char *aux, char **error)
1189{
1190 game_state *solved = dup_game(currstate);
1191 char *move = NULL;
1192
1193 if (solve_specific(solved, DIFF_ANY, 0) > 0) goto solved;
1194 free_game(solved);
1195
1196 solved = dup_game(state);
1197 if (solve_specific(solved, DIFF_ANY, 0) > 0) goto solved;
1198 free_game(solved);
1199
1200 *error = "Unable to solve puzzle.";
1201 return NULL;
1202
1203solved:
1204 move = game_state_diff(currstate, solved, 1);
1205 free_game(solved);
1206 return move;
1207}
1208
1209/* --- Game generation --- */
1210
1211/* A correctly completed Hitori board is essentially a latin square
1212 * (no duplicated numbers in any row or column) with black squares
1213 * added such that no black square touches another, and the white
1214 * squares make a contiguous region.
1215 *
1216 * So we can generate it by:
1217 * constructing a latin square
1218 * adding black squares at random (minding the constraints)
1219 * altering the numbers under the new black squares such that
1220 the solver gets a headstart working out where they are.
1221 */
1222
1223static int new_game_is_good(const game_params *params,
1224 game_state *state, game_state *tosolve)
1225{
1226 int sret, sret_easy = 0;
1227
1228 memcpy(tosolve->nums, state->nums, state->n * sizeof(int));
1229 memset(tosolve->flags, 0, state->n * sizeof(unsigned int));
1230 tosolve->completed = tosolve->impossible = 0;
1231
1232 /*
1233 * We try and solve it twice, once at our requested difficulty level
1234 * (ensuring it's soluble at all) and once at the level below (if
1235 * it exists), which we hope to fail: if you can also solve it at
1236 * the level below then it's too easy and we have to try again.
1237 *
1238 * With this puzzle in particular there's an extra finesse, which is
1239 * that we check that the generated puzzle isn't too easy _with
1240 * an extra solver step first_, which is the 'sneaky' mode of deductions
1241 * (asserting that any number which fulfils the latin-square rules
1242 * on its row/column must be white). This is an artefact of the
1243 * generation process and not implicit in the rules, so we don't want
1244 * people to be able to use it to make the puzzle easier.
1245 */
1246
1247 assert(params->diff < DIFF_MAX);
1248 sret = solve_specific(tosolve, params->diff, 0);
1249 if (params->diff > DIFF_EASY) {
1250 memset(tosolve->flags, 0, state->n * sizeof(unsigned int));
1251 tosolve->completed = tosolve->impossible = 0;
1252
1253 /* this is the only time the 'sneaky' flag is set to 1. */
1254 sret_easy = solve_specific(tosolve, params->diff-1, 1);
1255 }
1256
1257 if (sret <= 0 || sret_easy > 0) {
1258 debug(("Generated puzzle %s at chosen difficulty %s\n",
1259 sret <= 0 ? "insoluble" : "too easy",
1260 singles_diffnames[params->diff]));
1261 return 0;
1262 }
1263 return 1;
1264}
1265
1266#define MAXTRIES 20
1267
1268static int best_black_col(game_state *state, random_state *rs, int *scratch,
1269 int i, int *rownums, int *colnums)
1270{
1271 int w = state->w, x = i%w, y = i/w, j, o = state->o;
1272
1273 /* Randomise the list of numbers to try. */
1274 for (i = 0; i < o; i++) scratch[i] = i;
1275 shuffle(scratch, o, sizeof(int), rs);
1276
1277 /* Try each number in turn, first giving preference to removing
1278 * latin-square characteristics (i.e. those numbers which only
1279 * occur once in a row/column). The '&&' here, although intuitively
1280 * wrong, results in a smaller number of 'sneaky' deductions on
1281 * solvable boards. */
1282 for (i = 0; i < o; i++) {
1283 j = scratch[i] + 1;
1284 if (rownums[y*o + j-1] == 1 && colnums[x*o + j-1] == 1)
1285 goto found;
1286 }
1287
1288 /* Then try each number in turn returning the first one that's
1289 * not actually unique in its row/column (see comment below) */
1290 for (i = 0; i < o; i++) {
1291 j = scratch[i] + 1;
1292 if (rownums[y*o + j-1] != 0 || colnums[x*o + j-1] != 0)
1293 goto found;
1294 }
1295 assert(!"unable to place number under black cell.");
1296 return 0;
1297
1298found:
1299 /* Update column and row counts assuming this number will be placed. */
1300 rownums[y*o + j-1] += 1;
1301 colnums[x*o + j-1] += 1;
1302 return j;
1303}
1304
1305static char *new_game_desc(const game_params *params, random_state *rs,
1306 char **aux, int interactive)
1307{
1308 game_state *state = blank_game(params->w, params->h);
1309 game_state *tosolve = blank_game(params->w, params->h);
1310 int i, j, *scratch, *rownums, *colnums, x, y, ntries;
1311 int w = state->w, h = state->h, o = state->o;
1312 char *ret;
1313 digit *latin;
1314 struct solver_state *ss = solver_state_new(state);
1315
1316 scratch = snewn(state->n, int);
1317 rownums = snewn(h*o, int);
1318 colnums = snewn(w*o, int);
1319
1320generate:
1321 ss->n_ops = 0;
1322 debug(("Starting game generation, size %dx%d\n", w, h));
1323
1324 memset(state->flags, 0, state->n*sizeof(unsigned int));
1325
1326 /* First, generate the latin rectangle.
1327 * The order of this, o, is max(w,h). */
1328 latin = latin_generate_rect(w, h, rs);
1329 for (i = 0; i < state->n; i++)
1330 state->nums[i] = (int)latin[i];
1331 sfree(latin);
1332 debug_state("State after latin square", state);
1333
1334 /* Add black squares at random, using bits of solver as we go (to lay
1335 * white squares), until we can lay no more blacks. */
1336 for (i = 0; i < state->n; i++)
1337 scratch[i] = i;
1338 shuffle(scratch, state->n, sizeof(int), rs);
1339 for (j = 0; j < state->n; j++) {
1340 i = scratch[j];
1341 if ((state->flags[i] & F_CIRCLE) || (state->flags[i] & F_BLACK)) {
1342 debug(("generator skipping (%d,%d): %s\n", i%w, i/w,
1343 (state->flags[i] & F_CIRCLE) ? "CIRCLE" : "BLACK"));
1344 continue; /* solver knows this must be one or the other already. */
1345 }
1346
1347 /* Add a random black cell... */
1348 solver_op_add(ss, i%w, i/w, BLACK, "Generator: adding random black cell");
1349 solver_ops_do(state, ss);
1350
1351 /* ... and do as well as we know how to lay down whites that are now forced. */
1352 solve_allblackbutone(state, ss);
1353 solver_ops_do(state, ss);
1354
1355 solve_removesplits(state, ss);
1356 solver_ops_do(state, ss);
1357
1358 if (state->impossible) {
1359 debug(("generator made impossible, restarting...\n"));
1360 goto generate;
1361 }
1362 }
1363 debug_state("State after adding blacks", state);
1364
1365 /* Now we know which squares are white and which are black, we lay numbers
1366 * under black squares at random, except that the number must appear in
1367 * white cells at least once more in the same column or row as that [black]
1368 * square. That's necessary to avoid multiple solutions, where blackening
1369 * squares in the finished puzzle becomes optional. We use two arrays:
1370 *
1371 * rownums[ROW * o + NUM-1] is the no. of white cells containing NUM in y=ROW
1372 * colnums[COL * o + NUM-1] is the no. of white cells containing NUM in x=COL
1373 */
1374
1375 memset(rownums, 0, h*o * sizeof(int));
1376 memset(colnums, 0, w*o * sizeof(int));
1377 for (i = 0; i < state->n; i++) {
1378 if (state->flags[i] & F_BLACK) continue;
1379 j = state->nums[i];
1380 x = i%w; y = i/w;
1381 rownums[y * o + j-1] += 1;
1382 colnums[x * o + j-1] += 1;
1383 }
1384
1385 ntries = 0;
1386randomise:
1387 for (i = 0; i < state->n; i++) {
1388 if (!(state->flags[i] & F_BLACK)) continue;
1389 state->nums[i] = best_black_col(state, rs, scratch, i, rownums, colnums);
1390 }
1391 debug_state("State after adding numbers", state);
1392
1393 /* DIFF_ANY just returns whatever we first generated, for testing purposes. */
1394 if (params->diff != DIFF_ANY &&
1395 !new_game_is_good(params, state, tosolve)) {
1396 ntries++;
1397 if (ntries > MAXTRIES) {
1398 debug(("Ran out of randomisation attempts, re-generating.\n"));
1399 goto generate;
1400 }
1401 debug(("Re-randomising numbers under black squares.\n"));
1402 goto randomise;
1403 }
1404
1405 ret = generate_desc(state, 0);
1406
1407 free_game(tosolve);
1408 free_game(state);
1409 solver_state_free(ss);
1410 sfree(scratch);
1411 sfree(rownums);
1412 sfree(colnums);
1413
1414 return ret;
1415}
1416
1417static char *validate_desc(const game_params *params, const char *desc)
1418{
1419 char *ret = NULL;
1420
1421 unpick_desc(params, desc, NULL, &ret);
1422 return ret;
1423}
1424
1425static game_state *new_game(midend *me, const game_params *params,
1426 const char *desc)
1427{
1428 game_state *state = NULL;
1429
1430 unpick_desc(params, desc, &state, NULL);
1431 if (!state) assert(!"new_game failed to unpick");
1432 return state;
1433}
1434
1435/* --- Game UI and move routines --- */
1436
1437struct game_ui {
1438 int cx, cy, cshow;
1439 int show_black_nums;
1440};
1441
1442static game_ui *new_ui(const game_state *state)
1443{
1444 game_ui *ui = snew(game_ui);
1445
1446 ui->cx = ui->cy = ui->cshow = 0;
1447 ui->show_black_nums = 0;
1448
1449 return ui;
1450}
1451
1452static void free_ui(game_ui *ui)
1453{
1454 sfree(ui);
1455}
1456
1457static char *encode_ui(const game_ui *ui)
1458{
1459 return NULL;
1460}
1461
1462static void decode_ui(game_ui *ui, const char *encoding)
1463{
1464}
1465
1466static void game_changed_state(game_ui *ui, const game_state *oldstate,
1467 const game_state *newstate)
1468{
1469 if (!oldstate->completed && newstate->completed)
1470 ui->cshow = 0;
1471}
1472
1473#define DS_BLACK 0x1
1474#define DS_CIRCLE 0x2
1475#define DS_CURSOR 0x4
1476#define DS_BLACK_NUM 0x8
1477#define DS_ERROR 0x10
1478#define DS_FLASH 0x20
1479#define DS_IMPOSSIBLE 0x40
1480
1481struct game_drawstate {
1482 int tilesize, started, solved;
1483 int w, h, n;
1484
1485 unsigned int *flags;
1486};
1487
1488static char *interpret_move(const game_state *state, game_ui *ui,
1489 const game_drawstate *ds,
1490 int mx, int my, int button)
1491{
1492 char buf[80], c;
1493 int i, x = FROMCOORD(mx), y = FROMCOORD(my);
1494 enum { NONE, TOGGLE_BLACK, TOGGLE_CIRCLE, UI } action = NONE;
1495
1496 if (IS_CURSOR_MOVE(button)) {
1497 move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, 1);
1498 ui->cshow = 1;
1499 action = UI;
1500 } else if (IS_CURSOR_SELECT(button)) {
1501 x = ui->cx; y = ui->cy;
1502 if (!ui->cshow) {
1503 action = UI;
1504 ui->cshow = 1;
1505 }
1506 if (button == CURSOR_SELECT) {
1507 action = TOGGLE_BLACK;
1508 } else if (button == CURSOR_SELECT2) {
1509 action = TOGGLE_CIRCLE;
1510 }
1511 } else if (IS_MOUSE_DOWN(button)) {
1512 if (ui->cshow) {
1513 ui->cshow = 0;
1514 action = UI;
1515 }
1516 if (!INGRID(state, x, y)) {
1517 ui->show_black_nums = 1 - ui->show_black_nums;
1518 action = UI; /* this wants to be a per-game option. */
1519 } else if (button == LEFT_BUTTON) {
1520 action = TOGGLE_BLACK;
1521 } else if (button == RIGHT_BUTTON) {
1522 action = TOGGLE_CIRCLE;
1523 }
1524 }
1525 if (action == UI) return "";
1526
1527 if (action == TOGGLE_BLACK || action == TOGGLE_CIRCLE) {
1528 i = y * state->w + x;
1529 if (state->flags[i] & (F_BLACK | F_CIRCLE))
1530 c = 'E';
1531 else
1532 c = (action == TOGGLE_BLACK) ? 'B' : 'C';
1533 sprintf(buf, "%c%d,%d", (int)c, x, y);
1534 return dupstr(buf);
1535 }
1536
1537 return NULL;
1538}
1539
1540static game_state *execute_move(const game_state *state, const char *move)
1541{
1542 game_state *ret = dup_game(state);
1543 int x, y, i, n;
1544
1545 debug(("move: %s\n", move));
1546
1547 while (*move) {
1548 char c = *move;
1549 if (c == 'B' || c == 'C' || c == 'E') {
1550 move++;
1551 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
1552 !INGRID(state, x, y))
1553 goto badmove;
1554
1555 i = y*ret->w + x;
1556 ret->flags[i] &= ~(F_CIRCLE | F_BLACK); /* empty first, always. */
1557 if (c == 'B')
1558 ret->flags[i] |= F_BLACK;
1559 else if (c == 'C')
1560 ret->flags[i] |= F_CIRCLE;
1561 move += n;
1562 } else if (c == 'S') {
1563 move++;
1564 ret->used_solve = 1;
1565 } else
1566 goto badmove;
1567
1568 if (*move == ';')
1569 move++;
1570 else if (*move)
1571 goto badmove;
1572 }
1573 if (check_complete(ret, CC_MARK_ERRORS)) ret->completed = 1;
1574 return ret;
1575
1576badmove:
1577 free_game(ret);
1578 return NULL;
1579}
1580
1581/* ----------------------------------------------------------------------
1582 * Drawing routines.
1583 */
1584
1585static void game_compute_size(const game_params *params, int tilesize,
1586 int *x, int *y)
1587{
1588 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1589 struct { int tilesize; } ads, *ds = &ads;
1590 ads.tilesize = tilesize;
1591
1592 *x = TILE_SIZE * params->w + 2 * BORDER;
1593 *y = TILE_SIZE * params->h + 2 * BORDER;
1594}
1595
1596static void game_set_size(drawing *dr, game_drawstate *ds,
1597 const game_params *params, int tilesize)
1598{
1599 ds->tilesize = tilesize;
1600}
1601
1602static float *game_colours(frontend *fe, int *ncolours)
1603{
1604 float *ret = snewn(3 * NCOLOURS, float);
1605 int i;
1606
1607 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1608 for (i = 0; i < 3; i++) {
1609 ret[COL_BLACK * 3 + i] = 0.0F;
1610 ret[COL_BLACKNUM * 3 + i] = 0.4F;
1611 ret[COL_WHITE * 3 + i] = 1.0F;
1612 ret[COL_GRID * 3 + i] = ret[COL_LOWLIGHT * 3 + i];
1613 }
1614 ret[COL_CURSOR * 3 + 0] = 0.2F;
1615 ret[COL_CURSOR * 3 + 1] = 0.8F;
1616 ret[COL_CURSOR * 3 + 2] = 0.0F;
1617
1618 ret[COL_ERROR * 3 + 0] = 1.0F;
1619 ret[COL_ERROR * 3 + 1] = 0.0F;
1620 ret[COL_ERROR * 3 + 2] = 0.0F;
1621
1622 *ncolours = NCOLOURS;
1623 return ret;
1624}
1625
1626static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1627{
1628 struct game_drawstate *ds = snew(struct game_drawstate);
1629
1630 ds->tilesize = ds->started = ds->solved = 0;
1631 ds->w = state->w;
1632 ds->h = state->h;
1633 ds->n = state->n;
1634
1635 ds->flags = snewn(state->n, unsigned int);
1636
1637 memset(ds->flags, 0, state->n*sizeof(unsigned int));
1638
1639 return ds;
1640}
1641
1642static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1643{
1644 sfree(ds->flags);
1645 sfree(ds);
1646}
1647
1648static void tile_redraw(drawing *dr, game_drawstate *ds, int x, int y,
1649 int num, unsigned int f)
1650{
1651 int tcol, bg, dnum, cx, cy, tsz;
1652 char buf[32];
1653
1654 if (f & DS_BLACK) {
1655 bg = (f & DS_ERROR) ? COL_ERROR : COL_BLACK;
1656 tcol = COL_BLACKNUM;
1657 dnum = (f & DS_BLACK_NUM) ? 1 : 0;
1658 } else {
1659 bg = (f & DS_FLASH) ? COL_LOWLIGHT : COL_BACKGROUND;
1660 tcol = (f & DS_ERROR) ? COL_ERROR : COL_BLACK;
1661 dnum = 1;
1662 }
1663
1664 cx = x + TILE_SIZE/2; cy = y + TILE_SIZE/2;
1665
1666 draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE, bg);
1667 draw_rect_outline(dr, x, y, TILE_SIZE, TILE_SIZE,
1668 (f & DS_IMPOSSIBLE) ? COL_ERROR : COL_GRID);
1669
1670 if (f & DS_CIRCLE) {
1671 draw_circle(dr, cx, cy, CRAD, tcol, tcol);
1672 draw_circle(dr, cx, cy, CRAD-1, bg, tcol);
1673 }
1674
1675 if (dnum) {
1676 sprintf(buf, "%d", num);
1677 if (strlen(buf) == 1)
1678 tsz = TEXTSZ;
1679 else
1680 tsz = (CRAD*2 - 1) / strlen(buf);
1681 draw_text(dr, cx, cy, FONT_VARIABLE, tsz,
1682 ALIGN_VCENTRE | ALIGN_HCENTRE, tcol, buf);
1683 }
1684
1685 if (f & DS_CURSOR)
1686 draw_rect_corners(dr, cx, cy, TEXTSZ/2, COL_CURSOR);
1687
1688 draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
1689}
1690
1691static void game_redraw(drawing *dr, game_drawstate *ds,
1692 const game_state *oldstate, const game_state *state,
1693 int dir, const game_ui *ui,
1694 float animtime, float flashtime)
1695{
1696 int x, y, i, flash;
1697 unsigned int f;
1698
1699 flash = (int)(flashtime * 5 / FLASH_TIME) % 2;
1700
1701 if (!ds->started) {
1702 int wsz = TILE_SIZE * state->w + 2 * BORDER;
1703 int hsz = TILE_SIZE * state->h + 2 * BORDER;
1704 draw_rect(dr, 0, 0, wsz, hsz, COL_BACKGROUND);
1705 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1,
1706 TILE_SIZE * state->w + 2, TILE_SIZE * state->h + 2,
1707 COL_GRID);
1708 draw_update(dr, 0, 0, wsz, hsz);
1709 }
1710 for (x = 0; x < state->w; x++) {
1711 for (y = 0; y < state->h; y++) {
1712 i = y*state->w + x;
1713 f = 0;
1714
1715 if (flash) f |= DS_FLASH;
1716 if (state->impossible) f |= DS_IMPOSSIBLE;
1717
1718 if (ui->cshow && x == ui->cx && y == ui->cy)
1719 f |= DS_CURSOR;
1720 if (state->flags[i] & F_BLACK) {
1721 f |= DS_BLACK;
1722 if (ui->show_black_nums) f |= DS_BLACK_NUM;
1723 }
1724 if (state->flags[i] & F_CIRCLE)
1725 f |= DS_CIRCLE;
1726 if (state->flags[i] & F_ERROR)
1727 f |= DS_ERROR;
1728
1729 if (!ds->started || ds->flags[i] != f) {
1730 tile_redraw(dr, ds, COORD(x), COORD(y),
1731 state->nums[i], f);
1732 ds->flags[i] = f;
1733 }
1734 }
1735 }
1736 ds->started = 1;
1737}
1738
1739static float game_anim_length(const game_state *oldstate,
1740 const game_state *newstate, int dir, game_ui *ui)
1741{
1742 return 0.0F;
1743}
1744
1745static float game_flash_length(const game_state *oldstate,
1746 const game_state *newstate, int dir, game_ui *ui)
1747{
1748 if (!oldstate->completed &&
1749 newstate->completed && !newstate->used_solve)
1750 return FLASH_TIME;
1751 return 0.0F;
1752}
1753
1754static int game_status(const game_state *state)
1755{
1756 return state->completed ? +1 : 0;
1757}
1758
1759static int game_timing_state(const game_state *state, game_ui *ui)
1760{
1761 return TRUE;
1762}
1763
1764static void game_print_size(const game_params *params, float *x, float *y)
1765{
1766 int pw, ph;
1767
1768 /* 8mm squares by default. */
1769 game_compute_size(params, 800, &pw, &ph);
1770 *x = pw / 100.0F;
1771 *y = ph / 100.0F;
1772}
1773
1774static void game_print(drawing *dr, const game_state *state, int tilesize)
1775{
1776 int ink = print_mono_colour(dr, 0);
1777 int paper = print_mono_colour(dr, 1);
1778 int x, y, ox, oy, i;
1779 char buf[32];
1780
1781 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1782 game_drawstate ads, *ds = &ads;
1783 game_set_size(dr, ds, NULL, tilesize);
1784
1785 print_line_width(dr, 2 * TILE_SIZE / 40);
1786
1787 for (x = 0; x < state->w; x++) {
1788 for (y = 0; y < state->h; y++) {
1789 ox = COORD(x); oy = COORD(y);
1790 i = y*state->w+x;
1791
1792 if (state->flags[i] & F_BLACK) {
1793 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink);
1794 } else {
1795 draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink);
1796
1797 if (state->flags[i] & DS_CIRCLE)
1798 draw_circle(dr, ox+TILE_SIZE/2, oy+TILE_SIZE/2, CRAD,
1799 paper, ink);
1800
1801 sprintf(buf, "%d", state->nums[i]);
1802 draw_text(dr, ox+TILE_SIZE/2, oy+TILE_SIZE/2, FONT_VARIABLE,
1803 TEXTSZ/strlen(buf), ALIGN_VCENTRE | ALIGN_HCENTRE,
1804 ink, buf);
1805 }
1806 }
1807 }
1808}
1809
1810#ifdef COMBINED
1811#define thegame singles
1812#endif
1813
1814const struct game thegame = {
1815 "Singles", "games.singles", "singles",
1816 default_params,
1817 game_fetch_preset, NULL,
1818 decode_params,
1819 encode_params,
1820 free_params,
1821 dup_params,
1822 TRUE, game_configure, custom_params,
1823 validate_params,
1824 new_game_desc,
1825 validate_desc,
1826 new_game,
1827 dup_game,
1828 free_game,
1829 TRUE, solve_game,
1830 TRUE, game_can_format_as_text_now, game_text_format,
1831 new_ui,
1832 free_ui,
1833 encode_ui,
1834 decode_ui,
1835 game_changed_state,
1836 interpret_move,
1837 execute_move,
1838 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1839 game_colours,
1840 game_new_drawstate,
1841 game_free_drawstate,
1842 game_redraw,
1843 game_anim_length,
1844 game_flash_length,
1845 game_status,
1846 TRUE, FALSE, game_print_size, game_print,
1847 FALSE, /* wants_statusbar */
1848 FALSE, game_timing_state,
1849 REQUIRE_RBUTTON, /* flags */
1850};
1851
1852#ifdef STANDALONE_SOLVER
1853
1854#include <time.h>
1855#include <stdarg.h>
1856
1857static void start_soak(game_params *p, random_state *rs)
1858{
1859 time_t tt_start, tt_now, tt_last;
1860 char *desc, *aux;
1861 game_state *s;
1862 int i, n = 0, ndiff[DIFF_MAX], diff, sret, nblack = 0, nsneaky = 0;
1863
1864 tt_start = tt_now = time(NULL);
1865
1866 printf("Soak-testing a %dx%d grid.\n", p->w, p->h);
1867 p->diff = DIFF_ANY;
1868
1869 memset(ndiff, 0, DIFF_MAX * sizeof(int));
1870
1871 while (1) {
1872 n++;
1873 desc = new_game_desc(p, rs, &aux, 0);
1874 s = new_game(NULL, p, desc);
1875 nsneaky += solve_sneaky(s, NULL);
1876
1877 for (diff = 0; diff < DIFF_MAX; diff++) {
1878 memset(s->flags, 0, s->n * sizeof(unsigned int));
1879 s->completed = s->impossible = 0;
1880 sret = solve_specific(s, diff, 0);
1881 if (sret > 0) {
1882 ndiff[diff]++;
1883 break;
1884 } else if (sret < 0)
1885 fprintf(stderr, "Impossible! %s\n", desc);
1886 }
1887 for (i = 0; i < s->n; i++) {
1888 if (s->flags[i] & F_BLACK) nblack++;
1889 }
1890 free_game(s);
1891 sfree(desc);
1892
1893 tt_last = time(NULL);
1894 if (tt_last > tt_now) {
1895 tt_now = tt_last;
1896 printf("%d total, %3.1f/s, bl/sn %3.1f%%/%3.1f%%: ",
1897 n, (double)n / ((double)tt_now - tt_start),
1898 ((double)nblack * 100.0) / (double)(n * p->w * p->h),
1899 ((double)nsneaky * 100.0) / (double)(n * p->w * p->h));
1900 for (diff = 0; diff < DIFF_MAX; diff++) {
1901 if (diff > 0) printf(", ");
1902 printf("%d (%3.1f%%) %s",
1903 ndiff[diff], (double)ndiff[diff] * 100.0 / (double)n,
1904 singles_diffnames[diff]);
1905 }
1906 printf("\n");
1907 }
1908 }
1909}
1910
1911int main(int argc, char **argv)
1912{
1913 char *id = NULL, *desc, *desc_gen = NULL, *tgame, *err, *aux;
1914 game_state *s = NULL;
1915 game_params *p = NULL;
1916 int soln, soak = 0, ret = 1;
1917 time_t seed = time(NULL);
1918 random_state *rs = NULL;
1919
1920 setvbuf(stdout, NULL, _IONBF, 0);
1921
1922 while (--argc > 0) {
1923 char *p = *++argv;
1924 if (!strcmp(p, "-v")) {
1925 verbose = 1;
1926 } else if (!strcmp(p, "--soak")) {
1927 soak = 1;
1928 } else if (!strcmp(p, "--seed")) {
1929 if (argc == 0) {
1930 fprintf(stderr, "%s: --seed needs an argument", argv[0]);
1931 goto done;
1932 }
1933 seed = (time_t)atoi(*++argv);
1934 argc--;
1935 } else if (*p == '-') {
1936 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
1937 return 1;
1938 } else {
1939 id = p;
1940 }
1941 }
1942
1943 rs = random_new((void*)&seed, sizeof(time_t));
1944
1945 if (!id) {
1946 fprintf(stderr, "usage: %s [-v] [--soak] <params> | <game_id>\n", argv[0]);
1947 goto done;
1948 }
1949 desc = strchr(id, ':');
1950 if (desc) *desc++ = '\0';
1951
1952 p = default_params();
1953 decode_params(p, id);
1954 err = validate_params(p, 1);
1955 if (err) {
1956 fprintf(stderr, "%s: %s", argv[0], err);
1957 goto done;
1958 }
1959
1960 if (soak) {
1961 if (desc) {
1962 fprintf(stderr, "%s: --soak only needs params, not game desc.\n", argv[0]);
1963 goto done;
1964 }
1965 start_soak(p, rs);
1966 } else {
1967 if (!desc) desc = desc_gen = new_game_desc(p, rs, &aux, 0);
1968
1969 err = validate_desc(p, desc);
1970 if (err) {
1971 fprintf(stderr, "%s: %s\n", argv[0], err);
1972 free_params(p);
1973 goto done;
1974 }
1975 s = new_game(NULL, p, desc);
1976
1977 if (verbose) {
1978 tgame = game_text_format(s);
1979 fputs(tgame, stdout);
1980 sfree(tgame);
1981 }
1982
1983 soln = solve_specific(s, DIFF_ANY, 0);
1984 tgame = game_text_format(s);
1985 fputs(tgame, stdout);
1986 sfree(tgame);
1987 printf("Game was %s.\n\n",
1988 soln < 0 ? "impossible" : soln > 0 ? "solved" : "not solved");
1989 }
1990 ret = 0;
1991
1992done:
1993 if (desc_gen) sfree(desc_gen);
1994 if (p) free_params(p);
1995 if (s) free_game(s);
1996 if (rs) random_free(rs);
1997
1998 return ret;
1999}
2000
2001#endif
2002
2003
2004/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/singles.html b/apps/plugins/puzzles/src/singles.html
new file mode 100644
index 0000000000..ee353a5962
--- /dev/null
+++ b/apps/plugins/puzzles/src/singles.html
@@ -0,0 +1,67 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Singles</title>
7<link rel="previous" href="towers.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="magnets.html">
12</head>
13<body>
14<p><a href="towers.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="magnets.html">Next</a></p>
15<h1><a name="C32"></a>Chapter 32: <a name="i0"></a>Singles</h1>
16<p>
17You have a grid of white squares, all of which contain numbers. Your task is to colour some of the squares black (removing the number) so as to satisfy all of the following conditions:
18</p>
19<ul><li>
20No number occurs more than once in any row or column.
21</li>
22<li>
23No black square is horizontally or vertically adjacent to any other black square.
24</li>
25<li>
26The remaining white squares must all form one contiguous region (connected by edges, not just touching at corners).
27</li>
28</ul>
29<p>
30Credit for this puzzle goes to <a name="i1"></a>Nikoli <a href="#p0">[15]</a> who call it <a name="i2"></a>Hitori.
31</p>
32<p>
33Singles was contributed to this collection by James Harvey.
34</p>
35<p><a name="p0"></a>
36[15] <a href="http://www.nikoli.com/en/puzzles/hitori.html"><code>http://www.nikoli.com/en/puzzles/hitori.html</code></a> (beware of Flash)
37</p>
38<h2><a name="S32.1"></a>32.1 <a name="i3"></a>Singles controls</h2>
39<p>
40Left-clicking on an empty square will colour it black; left-clicking again will restore the number. Right-clicking will add a circle (useful for indicating that a cell is definitely not black).
41</p>
42<p>
43You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn a square black or add a circle respectively, and pressing the key again will restore the number or remove the circle.
44</p>
45<p>
46(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
47</p>
48<h2><a name="S32.2"></a>32.2 <a name="i4"></a>Singles parameters</h2>
49<p>
50These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
51</p>
52<dl><dt>
53<em>Width</em>, <em>Height</em>
54</dt>
55<dd>
56Size of grid in squares.
57</dd>
58<dt>
59<em>Difficulty</em>
60</dt>
61<dd>
62Controls the difficulty of the generated puzzle.
63</dd>
64</dl>
65
66<hr><address></address></body>
67</html>
diff --git a/apps/plugins/puzzles/src/sixteen.R b/apps/plugins/puzzles/src/sixteen.R
new file mode 100644
index 0000000000..c63a27cef8
--- /dev/null
+++ b/apps/plugins/puzzles/src/sixteen.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3sixteen : [X] GTK COMMON sixteen sixteen-icon|no-icon
4
5sixteen : [G] WINDOWS COMMON sixteen sixteen.res|noicon.res
6
7ALL += sixteen[COMBINED]
8
9!begin am gtk
10GAMES += sixteen
11!end
12
13!begin >list.c
14 A(sixteen) \
15!end
16
17!begin >gamedesc.txt
18sixteen:sixteen.exe:Sixteen:Toroidal sliding block puzzle:Slide a row at a time to arrange the tiles into order.
19!end
diff --git a/apps/plugins/puzzles/src/sixteen.c b/apps/plugins/puzzles/src/sixteen.c
new file mode 100644
index 0000000000..edc9771867
--- /dev/null
+++ b/apps/plugins/puzzles/src/sixteen.c
@@ -0,0 +1,1214 @@
1/*
2 * sixteen.c: `16-puzzle', a sliding-tiles jigsaw which differs
3 * from the 15-puzzle in that you toroidally rotate a row or column
4 * at a time.
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <assert.h>
11#include <ctype.h>
12#include <math.h>
13
14#include "puzzles.h"
15
16#define PREFERRED_TILE_SIZE 48
17#define TILE_SIZE (ds->tilesize)
18#define BORDER TILE_SIZE
19#define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
20#define COORD(x) ( (x) * TILE_SIZE + BORDER )
21#define FROMCOORD(x) ( ((x) - BORDER + 2*TILE_SIZE) / TILE_SIZE - 2 )
22
23#define ANIM_TIME 0.13F
24#define FLASH_FRAME 0.13F
25
26#define X(state, i) ( (i) % (state)->w )
27#define Y(state, i) ( (i) / (state)->w )
28#define C(state, x, y) ( (y) * (state)->w + (x) )
29
30#define TILE_CURSOR(i, state, x, y) ((i) == C((state), (x), (y)) && \
31 0 <= (x) && (x) < (state)->w && \
32 0 <= (y) && (y) < (state)->h)
33enum {
34 COL_BACKGROUND,
35 COL_TEXT,
36 COL_HIGHLIGHT,
37 COL_LOWLIGHT,
38 NCOLOURS
39};
40
41struct game_params {
42 int w, h;
43 int movetarget;
44};
45
46struct game_state {
47 int w, h, n;
48 int *tiles;
49 int completed;
50 int used_solve; /* used to suppress completion flash */
51 int movecount, movetarget;
52 int last_movement_sense;
53};
54
55static game_params *default_params(void)
56{
57 game_params *ret = snew(game_params);
58
59 ret->w = ret->h = 4;
60 ret->movetarget = 0;
61
62 return ret;
63}
64
65static int game_fetch_preset(int i, char **name, game_params **params)
66{
67 game_params *ret;
68 int w, h;
69 char buf[80];
70
71 switch (i) {
72 case 0: w = 3, h = 3; break;
73 case 1: w = 4, h = 3; break;
74 case 2: w = 4, h = 4; break;
75 case 3: w = 5, h = 4; break;
76 case 4: w = 5, h = 5; break;
77 default: return FALSE;
78 }
79
80 sprintf(buf, "%dx%d", w, h);
81 *name = dupstr(buf);
82 *params = ret = snew(game_params);
83 ret->w = w;
84 ret->h = h;
85 ret->movetarget = 0;
86 return TRUE;
87}
88
89static void free_params(game_params *params)
90{
91 sfree(params);
92}
93
94static game_params *dup_params(const game_params *params)
95{
96 game_params *ret = snew(game_params);
97 *ret = *params; /* structure copy */
98 return ret;
99}
100
101static void decode_params(game_params *ret, char const *string)
102{
103 ret->w = ret->h = atoi(string);
104 ret->movetarget = 0;
105 while (*string && isdigit((unsigned char)*string)) string++;
106 if (*string == 'x') {
107 string++;
108 ret->h = atoi(string);
109 while (*string && isdigit((unsigned char)*string))
110 string++;
111 }
112 if (*string == 'm') {
113 string++;
114 ret->movetarget = atoi(string);
115 while (*string && isdigit((unsigned char)*string))
116 string++;
117 }
118}
119
120static char *encode_params(const game_params *params, int full)
121{
122 char data[256];
123
124 sprintf(data, "%dx%d", params->w, params->h);
125 /* Shuffle limit is part of the limited parameters, because we have to
126 * supply the target move count. */
127 if (params->movetarget)
128 sprintf(data + strlen(data), "m%d", params->movetarget);
129
130 return dupstr(data);
131}
132
133static config_item *game_configure(const game_params *params)
134{
135 config_item *ret;
136 char buf[80];
137
138 ret = snewn(4, config_item);
139
140 ret[0].name = "Width";
141 ret[0].type = C_STRING;
142 sprintf(buf, "%d", params->w);
143 ret[0].sval = dupstr(buf);
144 ret[0].ival = 0;
145
146 ret[1].name = "Height";
147 ret[1].type = C_STRING;
148 sprintf(buf, "%d", params->h);
149 ret[1].sval = dupstr(buf);
150 ret[1].ival = 0;
151
152 ret[2].name = "Number of shuffling moves";
153 ret[2].type = C_STRING;
154 sprintf(buf, "%d", params->movetarget);
155 ret[2].sval = dupstr(buf);
156 ret[2].ival = 0;
157
158 ret[3].name = NULL;
159 ret[3].type = C_END;
160 ret[3].sval = NULL;
161 ret[3].ival = 0;
162
163 return ret;
164}
165
166static game_params *custom_params(const config_item *cfg)
167{
168 game_params *ret = snew(game_params);
169
170 ret->w = atoi(cfg[0].sval);
171 ret->h = atoi(cfg[1].sval);
172 ret->movetarget = atoi(cfg[2].sval);
173
174 return ret;
175}
176
177static char *validate_params(const game_params *params, int full)
178{
179 if (params->w < 2 || params->h < 2)
180 return "Width and height must both be at least two";
181
182 return NULL;
183}
184
185static int perm_parity(int *perm, int n)
186{
187 int i, j, ret;
188
189 ret = 0;
190
191 for (i = 0; i < n-1; i++)
192 for (j = i+1; j < n; j++)
193 if (perm[i] > perm[j])
194 ret = !ret;
195
196 return ret;
197}
198
199static char *new_game_desc(const game_params *params, random_state *rs,
200 char **aux, int interactive)
201{
202 int stop, n, i, x;
203 int x1, x2, p1, p2;
204 int *tiles, *used;
205 char *ret;
206 int retlen;
207
208 n = params->w * params->h;
209
210 tiles = snewn(n, int);
211
212 if (params->movetarget) {
213 int prevoffset = -1;
214 int max = (params->w > params->h ? params->w : params->h);
215 int *prevmoves = snewn(max, int);
216
217 /*
218 * Shuffle the old-fashioned way, by making a series of
219 * single moves on the grid.
220 */
221
222 for (i = 0; i < n; i++)
223 tiles[i] = i;
224
225 for (i = 0; i < params->movetarget; i++) {
226 int start, offset, len, direction, index;
227 int j, tmp;
228
229 /*
230 * Choose a move to make. We can choose from any row
231 * or any column.
232 */
233 while (1) {
234 j = random_upto(rs, params->w + params->h);
235
236 if (j < params->w) {
237 /* Column. */
238 index = j;
239 start = j;
240 offset = params->w;
241 len = params->h;
242 } else {
243 /* Row. */
244 index = j - params->w;
245 start = index * params->w;
246 offset = 1;
247 len = params->w;
248 }
249
250 direction = -1 + 2 * random_upto(rs, 2);
251
252 /*
253 * To at least _try_ to avoid boring cases, check
254 * that this move doesn't directly undo a previous
255 * one, or repeat it so many times as to turn it
256 * into fewer moves in the opposite direction. (For
257 * example, in a row of length 4, we're allowed to
258 * move it the same way twice, but not three
259 * times.)
260 *
261 * We track this for each individual row/column,
262 * and clear all the counters as soon as a
263 * perpendicular move is made. This isn't perfect
264 * (it _can't_ guaranteeably be perfect - there
265 * will always come a move count beyond which a
266 * shorter solution will be possible than the one
267 * which constructed the position) but it should
268 * sort out all the obvious cases.
269 */
270 if (offset == prevoffset) {
271 tmp = prevmoves[index] + direction;
272 if (abs(2*tmp) > len || abs(tmp) < abs(prevmoves[index]))
273 continue;
274 }
275
276 /* If we didn't `continue', we've found an OK move to make. */
277 if (offset != prevoffset) {
278 int i;
279 for (i = 0; i < max; i++)
280 prevmoves[i] = 0;
281 prevoffset = offset;
282 }
283 prevmoves[index] += direction;
284 break;
285 }
286
287 /*
288 * Make the move.
289 */
290 if (direction < 0) {
291 start += (len-1) * offset;
292 offset = -offset;
293 }
294 tmp = tiles[start];
295 for (j = 0; j+1 < len; j++)
296 tiles[start + j*offset] = tiles[start + (j+1)*offset];
297 tiles[start + (len-1) * offset] = tmp;
298 }
299
300 sfree(prevmoves);
301
302 } else {
303
304 used = snewn(n, int);
305
306 for (i = 0; i < n; i++) {
307 tiles[i] = -1;
308 used[i] = FALSE;
309 }
310
311 /*
312 * If both dimensions are odd, there is a parity
313 * constraint.
314 */
315 if (params->w & params->h & 1)
316 stop = 2;
317 else
318 stop = 0;
319
320 /*
321 * Place everything except (possibly) the last two tiles.
322 */
323 for (x = 0, i = n; i > stop; i--) {
324 int k = i > 1 ? random_upto(rs, i) : 0;
325 int j;
326
327 for (j = 0; j < n; j++)
328 if (!used[j] && (k-- == 0))
329 break;
330
331 assert(j < n && !used[j]);
332 used[j] = TRUE;
333
334 while (tiles[x] >= 0)
335 x++;
336 assert(x < n);
337 tiles[x] = j;
338 }
339
340 if (stop) {
341 /*
342 * Find the last two locations, and the last two
343 * pieces.
344 */
345 while (tiles[x] >= 0)
346 x++;
347 assert(x < n);
348 x1 = x;
349 x++;
350 while (tiles[x] >= 0)
351 x++;
352 assert(x < n);
353 x2 = x;
354
355 for (i = 0; i < n; i++)
356 if (!used[i])
357 break;
358 p1 = i;
359 for (i = p1+1; i < n; i++)
360 if (!used[i])
361 break;
362 p2 = i;
363
364 /*
365 * Try the last two tiles one way round. If that fails,
366 * swap them.
367 */
368 tiles[x1] = p1;
369 tiles[x2] = p2;
370 if (perm_parity(tiles, n) != 0) {
371 tiles[x1] = p2;
372 tiles[x2] = p1;
373 assert(perm_parity(tiles, n) == 0);
374 }
375 }
376
377 sfree(used);
378 }
379
380 /*
381 * Now construct the game description, by describing the tile
382 * array as a simple sequence of comma-separated integers.
383 */
384 ret = NULL;
385 retlen = 0;
386 for (i = 0; i < n; i++) {
387 char buf[80];
388 int k;
389
390 k = sprintf(buf, "%d,", tiles[i]+1);
391
392 ret = sresize(ret, retlen + k + 1, char);
393 strcpy(ret + retlen, buf);
394 retlen += k;
395 }
396 ret[retlen-1] = '\0'; /* delete last comma */
397
398 sfree(tiles);
399
400 return ret;
401}
402
403
404static char *validate_desc(const game_params *params, const char *desc)
405{
406 const char *p;
407 char *err;
408 int i, area;
409 int *used;
410
411 area = params->w * params->h;
412 p = desc;
413 err = NULL;
414
415 used = snewn(area, int);
416 for (i = 0; i < area; i++)
417 used[i] = FALSE;
418
419 for (i = 0; i < area; i++) {
420 const char *q = p;
421 int n;
422
423 if (*p < '0' || *p > '9') {
424 err = "Not enough numbers in string";
425 goto leave;
426 }
427 while (*p >= '0' && *p <= '9')
428 p++;
429 if (i < area-1 && *p != ',') {
430 err = "Expected comma after number";
431 goto leave;
432 }
433 else if (i == area-1 && *p) {
434 err = "Excess junk at end of string";
435 goto leave;
436 }
437 n = atoi(q);
438 if (n < 1 || n > area) {
439 err = "Number out of range";
440 goto leave;
441 }
442 if (used[n-1]) {
443 err = "Number used twice";
444 goto leave;
445 }
446 used[n-1] = TRUE;
447
448 if (*p) p++; /* eat comma */
449 }
450
451 leave:
452 sfree(used);
453 return err;
454}
455
456static game_state *new_game(midend *me, const game_params *params,
457 const char *desc)
458{
459 game_state *state = snew(game_state);
460 int i;
461 const char *p;
462
463 state->w = params->w;
464 state->h = params->h;
465 state->n = params->w * params->h;
466 state->tiles = snewn(state->n, int);
467
468 p = desc;
469 i = 0;
470 for (i = 0; i < state->n; i++) {
471 assert(*p);
472 state->tiles[i] = atoi(p);
473 while (*p && *p != ',')
474 p++;
475 if (*p) p++; /* eat comma */
476 }
477 assert(!*p);
478
479 state->completed = state->movecount = 0;
480 state->movetarget = params->movetarget;
481 state->used_solve = FALSE;
482 state->last_movement_sense = 0;
483
484 return state;
485}
486
487static game_state *dup_game(const game_state *state)
488{
489 game_state *ret = snew(game_state);
490
491 ret->w = state->w;
492 ret->h = state->h;
493 ret->n = state->n;
494 ret->tiles = snewn(state->w * state->h, int);
495 memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int));
496 ret->completed = state->completed;
497 ret->movecount = state->movecount;
498 ret->movetarget = state->movetarget;
499 ret->used_solve = state->used_solve;
500 ret->last_movement_sense = state->last_movement_sense;
501
502 return ret;
503}
504
505static void free_game(game_state *state)
506{
507 sfree(state->tiles);
508 sfree(state);
509}
510
511static char *solve_game(const game_state *state, const game_state *currstate,
512 const char *aux, char **error)
513{
514 return dupstr("S");
515}
516
517static int game_can_format_as_text_now(const game_params *params)
518{
519 return TRUE;
520}
521
522static char *game_text_format(const game_state *state)
523{
524 char *ret, *p, buf[80];
525 int x, y, col, maxlen;
526
527 /*
528 * First work out how many characters we need to display each
529 * number.
530 */
531 col = sprintf(buf, "%d", state->n);
532
533 /*
534 * Now we know the exact total size of the grid we're going to
535 * produce: it's got h rows, each containing w lots of col, w-1
536 * spaces and a trailing newline.
537 */
538 maxlen = state->h * state->w * (col+1);
539
540 ret = snewn(maxlen+1, char);
541 p = ret;
542
543 for (y = 0; y < state->h; y++) {
544 for (x = 0; x < state->w; x++) {
545 int v = state->tiles[state->w*y+x];
546 sprintf(buf, "%*d", col, v);
547 memcpy(p, buf, col);
548 p += col;
549 if (x+1 == state->w)
550 *p++ = '\n';
551 else
552 *p++ = ' ';
553 }
554 }
555
556 assert(p - ret == maxlen);
557 *p = '\0';
558 return ret;
559}
560
561enum cursor_mode { unlocked, lock_tile, lock_position };
562
563struct game_ui {
564 int cur_x, cur_y;
565 int cur_visible;
566 enum cursor_mode cur_mode;
567};
568
569static game_ui *new_ui(const game_state *state)
570{
571 game_ui *ui = snew(game_ui);
572 ui->cur_x = 0;
573 ui->cur_y = 0;
574 ui->cur_visible = FALSE;
575 ui->cur_mode = unlocked;
576
577 return ui;
578}
579
580static void free_ui(game_ui *ui)
581{
582 sfree(ui);
583}
584
585static char *encode_ui(const game_ui *ui)
586{
587 return NULL;
588}
589
590static void decode_ui(game_ui *ui, const char *encoding)
591{
592}
593
594static void game_changed_state(game_ui *ui, const game_state *oldstate,
595 const game_state *newstate)
596{
597}
598
599struct game_drawstate {
600 int started;
601 int w, h, bgcolour;
602 int *tiles;
603 int tilesize;
604 int cur_x, cur_y;
605};
606
607static char *interpret_move(const game_state *state, game_ui *ui,
608 const game_drawstate *ds,
609 int x, int y, int button)
610{
611 int cx = -1, cy = -1, dx, dy;
612 char buf[80];
613 int shift = button & MOD_SHFT, control = button & MOD_CTRL,
614 pad = button & MOD_NUM_KEYPAD;
615
616 button &= ~MOD_MASK;
617
618 if (IS_CURSOR_MOVE(button) || pad) {
619 if (!ui->cur_visible) {
620 ui->cur_visible = 1;
621 return "";
622 }
623
624 if (control || shift || ui->cur_mode) {
625 int x = ui->cur_x, y = ui->cur_y, xwrap = x, ywrap = y;
626 if (x < 0 || x >= state->w || y < 0 || y >= state->h)
627 return NULL;
628 move_cursor(button | pad, &x, &y,
629 state->w, state->h, FALSE);
630 move_cursor(button | pad, &xwrap, &ywrap,
631 state->w, state->h, TRUE);
632
633 if (x != xwrap) {
634 sprintf(buf, "R%d,%c1", y, x ? '+' : '-');
635 } else if (y != ywrap) {
636 sprintf(buf, "C%d,%c1", x, y ? '+' : '-');
637 } else if (x == ui->cur_x)
638 sprintf(buf, "C%d,%d", x, y - ui->cur_y);
639 else
640 sprintf(buf, "R%d,%d", y, x - ui->cur_x);
641
642 if (control || (!shift && ui->cur_mode == lock_tile)) {
643 ui->cur_x = xwrap;
644 ui->cur_y = ywrap;
645 }
646
647 return dupstr(buf);
648 } else {
649 int x = ui->cur_x + 1, y = ui->cur_y + 1;
650
651 move_cursor(button | pad, &x, &y,
652 state->w + 2, state->h + 2, FALSE);
653
654 if (x == 0 && y == 0) {
655 int t = ui->cur_x;
656 ui->cur_x = ui->cur_y;
657 ui->cur_y = t;
658 } else if (x == 0 && y == state->h + 1) {
659 int t = ui->cur_x;
660 ui->cur_x = (state->h - 1) - ui->cur_y;
661 ui->cur_y = (state->h - 1) - t;
662 } else if (x == state->w + 1 && y == 0) {
663 int t = ui->cur_x;
664 ui->cur_x = (state->w - 1) - ui->cur_y;
665 ui->cur_y = (state->w - 1) - t;
666 } else if (x == state->w + 1 && y == state->h + 1) {
667 int t = ui->cur_x;
668 ui->cur_x = state->w - state->h + ui->cur_y;
669 ui->cur_y = state->h - state->w + t;
670 } else {
671 ui->cur_x = x - 1;
672 ui->cur_y = y - 1;
673 }
674
675 ui->cur_visible = 1;
676 return "";
677 }
678 }
679
680 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
681 cx = FROMCOORD(x);
682 cy = FROMCOORD(y);
683 ui->cur_visible = 0;
684 } else if (IS_CURSOR_SELECT(button)) {
685 if (ui->cur_visible) {
686 if (ui->cur_x == -1 || ui->cur_x == state->w ||
687 ui->cur_y == -1 || ui->cur_y == state->h) {
688 cx = ui->cur_x;
689 cy = ui->cur_y;
690 } else {
691 const enum cursor_mode m = (button == CURSOR_SELECT2 ?
692 lock_position : lock_tile);
693 ui->cur_mode = (ui->cur_mode == m ? unlocked : m);
694 return "";
695 }
696 } else {
697 ui->cur_visible = 1;
698 return "";
699 }
700 } else {
701 return NULL;
702 }
703
704 if (cx == -1 && cy >= 0 && cy < state->h)
705 dx = -1, dy = 0;
706 else if (cx == state->w && cy >= 0 && cy < state->h)
707 dx = +1, dy = 0;
708 else if (cy == -1 && cx >= 0 && cx < state->w)
709 dy = -1, dx = 0;
710 else if (cy == state->h && cx >= 0 && cx < state->w)
711 dy = +1, dx = 0;
712 else
713 return ""; /* invalid click location */
714
715 /* reverse direction if right hand button is pressed */
716 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) {
717 dx = -dx;
718 dy = -dy;
719 }
720
721 if (dx)
722 sprintf(buf, "R%d,%d", cy, dx);
723 else
724 sprintf(buf, "C%d,%d", cx, dy);
725 return dupstr(buf);
726}
727
728static game_state *execute_move(const game_state *from, const char *move)
729{
730 int cx, cy, dx, dy;
731 int tx, ty, n;
732 game_state *ret;
733
734 if (!strcmp(move, "S")) {
735 int i;
736
737 ret = dup_game(from);
738
739 /*
740 * Simply replace the grid with a solved one. For this game,
741 * this isn't a useful operation for actually telling the user
742 * what they should have done, but it is useful for
743 * conveniently being able to get hold of a clean state from
744 * which to practise manoeuvres.
745 */
746 for (i = 0; i < ret->n; i++)
747 ret->tiles[i] = i+1;
748 ret->used_solve = TRUE;
749 ret->completed = ret->movecount = 1;
750
751 return ret;
752 }
753
754 if (move[0] == 'R' && sscanf(move+1, "%d,%d", &cy, &dx) == 2 &&
755 cy >= 0 && cy < from->h) {
756 cx = dy = 0;
757 n = from->w;
758 } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &dy) == 2 &&
759 cx >= 0 && cx < from->w) {
760 cy = dx = 0;
761 n = from->h;
762 } else
763 return NULL;
764
765 ret = dup_game(from);
766
767 do {
768 tx = (cx - dx + from->w) % from->w;
769 ty = (cy - dy + from->h) % from->h;
770 ret->tiles[C(ret, cx, cy)] = from->tiles[C(from, tx, ty)];
771 cx = tx;
772 cy = ty;
773 } while (--n > 0);
774
775 ret->movecount++;
776
777 ret->last_movement_sense = dx+dy;
778
779 /*
780 * See if the game has been completed.
781 */
782 if (!ret->completed) {
783 ret->completed = ret->movecount;
784 for (n = 0; n < ret->n; n++)
785 if (ret->tiles[n] != n+1)
786 ret->completed = FALSE;
787 }
788
789 return ret;
790}
791
792/* ----------------------------------------------------------------------
793 * Drawing routines.
794 */
795
796static void game_compute_size(const game_params *params, int tilesize,
797 int *x, int *y)
798{
799 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
800 struct { int tilesize; } ads, *ds = &ads;
801 ads.tilesize = tilesize;
802
803 *x = TILE_SIZE * params->w + 2 * BORDER;
804 *y = TILE_SIZE * params->h + 2 * BORDER;
805}
806
807static void game_set_size(drawing *dr, game_drawstate *ds,
808 const game_params *params, int tilesize)
809{
810 ds->tilesize = tilesize;
811}
812
813static float *game_colours(frontend *fe, int *ncolours)
814{
815 float *ret = snewn(3 * NCOLOURS, float);
816 int i;
817
818 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
819
820 for (i = 0; i < 3; i++)
821 ret[COL_TEXT * 3 + i] = 0.0;
822
823 *ncolours = NCOLOURS;
824 return ret;
825}
826
827static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
828{
829 struct game_drawstate *ds = snew(struct game_drawstate);
830 int i;
831
832 ds->started = FALSE;
833 ds->w = state->w;
834 ds->h = state->h;
835 ds->bgcolour = COL_BACKGROUND;
836 ds->tiles = snewn(ds->w*ds->h, int);
837 ds->tilesize = 0; /* haven't decided yet */
838 for (i = 0; i < ds->w*ds->h; i++)
839 ds->tiles[i] = -1;
840 ds->cur_x = ds->cur_y = -1;
841
842 return ds;
843}
844
845static void game_free_drawstate(drawing *dr, game_drawstate *ds)
846{
847 sfree(ds->tiles);
848 sfree(ds);
849}
850
851static void draw_tile(drawing *dr, game_drawstate *ds,
852 const game_state *state, int x, int y,
853 int tile, int flash_colour)
854{
855 if (tile == 0) {
856 draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
857 flash_colour);
858 } else {
859 int coords[6];
860 char str[40];
861
862 coords[0] = x + TILE_SIZE - 1;
863 coords[1] = y + TILE_SIZE - 1;
864 coords[2] = x + TILE_SIZE - 1;
865 coords[3] = y;
866 coords[4] = x;
867 coords[5] = y + TILE_SIZE - 1;
868 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
869
870 coords[0] = x;
871 coords[1] = y;
872 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
873
874 draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
875 TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
876 flash_colour);
877
878 sprintf(str, "%d", tile);
879 draw_text(dr, x + TILE_SIZE/2, y + TILE_SIZE/2,
880 FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE,
881 COL_TEXT, str);
882 }
883 draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
884}
885
886static void draw_arrow(drawing *dr, game_drawstate *ds,
887 int x, int y, int xdx, int xdy, int cur)
888{
889 int coords[14];
890 int ydy = -xdx, ydx = xdy;
891
892#define POINT(n, xx, yy) ( \
893 coords[2*(n)+0] = x + (xx)*xdx + (yy)*ydx, \
894 coords[2*(n)+1] = y + (xx)*xdy + (yy)*ydy)
895
896 POINT(0, TILE_SIZE / 2, 3 * TILE_SIZE / 4); /* top of arrow */
897 POINT(1, 3 * TILE_SIZE / 4, TILE_SIZE / 2); /* right corner */
898 POINT(2, 5 * TILE_SIZE / 8, TILE_SIZE / 2); /* right concave */
899 POINT(3, 5 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom right */
900 POINT(4, 3 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom left */
901 POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */
902 POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */
903
904 draw_polygon(dr, coords, 7, cur ? COL_HIGHLIGHT : COL_LOWLIGHT, COL_TEXT);
905}
906
907static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds,
908 int cur_x, int cur_y, int cur)
909{
910 if (cur_x == -1 && cur_y == -1)
911 return; /* 'no cursur here */
912 else if (cur_x == -1) /* LH column. */
913 draw_arrow(dr, ds, COORD(0), COORD(cur_y+1), 0, -1, cur);
914 else if (cur_x == ds->w) /* RH column */
915 draw_arrow(dr, ds, COORD(ds->w), COORD(cur_y), 0, +1, cur);
916 else if (cur_y == -1) /* Top row */
917 draw_arrow(dr, ds, COORD(cur_x), COORD(0), +1, 0, cur);
918 else if (cur_y == ds->h) /* Bottom row */
919 draw_arrow(dr, ds, COORD(cur_x+1), COORD(ds->h), -1, 0, cur);
920 else
921 return;
922
923 draw_update(dr, COORD(cur_x), COORD(cur_y),
924 TILE_SIZE, TILE_SIZE);
925}
926
927static void game_redraw(drawing *dr, game_drawstate *ds,
928 const game_state *oldstate, const game_state *state,
929 int dir, const game_ui *ui,
930 float animtime, float flashtime)
931{
932 int i, bgcolour;
933 int cur_x = -1, cur_y = -1;
934
935 if (flashtime > 0) {
936 int frame = (int)(flashtime / FLASH_FRAME);
937 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
938 } else
939 bgcolour = COL_BACKGROUND;
940
941 if (!ds->started) {
942 int coords[10];
943
944 draw_rect(dr, 0, 0,
945 TILE_SIZE * state->w + 2 * BORDER,
946 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
947 draw_update(dr, 0, 0,
948 TILE_SIZE * state->w + 2 * BORDER,
949 TILE_SIZE * state->h + 2 * BORDER);
950
951 /*
952 * Recessed area containing the whole puzzle.
953 */
954 coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
955 coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
956 coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
957 coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
958 coords[4] = coords[2] - TILE_SIZE;
959 coords[5] = coords[3] + TILE_SIZE;
960 coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
961 coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
962 coords[6] = coords[8] + TILE_SIZE;
963 coords[7] = coords[9] - TILE_SIZE;
964 draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
965
966 coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
967 coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
968 draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
969
970 /*
971 * Arrows for making moves.
972 */
973 for (i = 0; i < state->w; i++) {
974 draw_arrow(dr, ds, COORD(i), COORD(0), +1, 0, 0);
975 draw_arrow(dr, ds, COORD(i+1), COORD(state->h), -1, 0, 0);
976 }
977 for (i = 0; i < state->h; i++) {
978 draw_arrow(dr, ds, COORD(state->w), COORD(i), 0, +1, 0);
979 draw_arrow(dr, ds, COORD(0), COORD(i+1), 0, -1, 0);
980 }
981
982 ds->started = TRUE;
983 }
984 /*
985 * Cursor (highlighted arrow around edge)
986 */
987 if (ui->cur_visible) {
988 cur_x = ui->cur_x; cur_y = ui->cur_y;
989 }
990
991 if (cur_x != ds->cur_x || cur_y != ds->cur_y) {
992 /* Cursor has changed; redraw two (prev and curr) arrows. */
993 draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1);
994 draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0);
995 }
996
997 /*
998 * Now draw each tile.
999 */
1000
1001 clip(dr, COORD(0), COORD(0), TILE_SIZE*state->w, TILE_SIZE*state->h);
1002
1003 for (i = 0; i < state->n; i++) {
1004 int t, t0;
1005 /*
1006 * Figure out what should be displayed at this
1007 * location. It's either a simple tile, or it's a
1008 * transition between two tiles (in which case we say
1009 * -1 because it must always be drawn).
1010 */
1011
1012 if (oldstate && oldstate->tiles[i] != state->tiles[i])
1013 t = -1;
1014 else
1015 t = state->tiles[i];
1016
1017 t0 = t;
1018
1019 if (ds->bgcolour != bgcolour || /* always redraw when flashing */
1020 ds->tiles[i] != t || ds->tiles[i] == -1 || t == -1 ||
1021 ((ds->cur_x != cur_x || ds->cur_y != cur_y) && /* cursor moved */
1022 (TILE_CURSOR(i, state, ds->cur_x, ds->cur_y) ||
1023 TILE_CURSOR(i, state, cur_x, cur_y)))) {
1024 int x, y, x2, y2;
1025
1026 /*
1027 * Figure out what to _actually_ draw, and where to
1028 * draw it.
1029 */
1030 if (t == -1) {
1031 int x0, y0, x1, y1, dx, dy;
1032 int j;
1033 float c;
1034 int sense;
1035
1036 if (dir < 0) {
1037 assert(oldstate);
1038 sense = -oldstate->last_movement_sense;
1039 } else {
1040 sense = state->last_movement_sense;
1041 }
1042
1043 t = state->tiles[i];
1044
1045 /*
1046 * FIXME: must be prepared to draw a double
1047 * tile in some situations.
1048 */
1049
1050 /*
1051 * Find the coordinates of this tile in the old and
1052 * new states.
1053 */
1054 x1 = COORD(X(state, i));
1055 y1 = COORD(Y(state, i));
1056 for (j = 0; j < oldstate->n; j++)
1057 if (oldstate->tiles[j] == state->tiles[i])
1058 break;
1059 assert(j < oldstate->n);
1060 x0 = COORD(X(state, j));
1061 y0 = COORD(Y(state, j));
1062
1063 dx = (x1 - x0);
1064 if (dx != 0 &&
1065 dx != TILE_SIZE * sense) {
1066 dx = (dx < 0 ? dx + TILE_SIZE * state->w :
1067 dx - TILE_SIZE * state->w);
1068 assert(abs(dx) == TILE_SIZE);
1069 }
1070 dy = (y1 - y0);
1071 if (dy != 0 &&
1072 dy != TILE_SIZE * sense) {
1073 dy = (dy < 0 ? dy + TILE_SIZE * state->h :
1074 dy - TILE_SIZE * state->h);
1075 assert(abs(dy) == TILE_SIZE);
1076 }
1077
1078 c = (animtime / ANIM_TIME);
1079 if (c < 0.0F) c = 0.0F;
1080 if (c > 1.0F) c = 1.0F;
1081
1082 x = x0 + (int)(c * dx);
1083 y = y0 + (int)(c * dy);
1084 x2 = x1 - dx + (int)(c * dx);
1085 y2 = y1 - dy + (int)(c * dy);
1086 } else {
1087 x = COORD(X(state, i));
1088 y = COORD(Y(state, i));
1089 x2 = y2 = -1;
1090 }
1091
1092 draw_tile(dr, ds, state, x, y, t,
1093 (x2 == -1 && TILE_CURSOR(i, state, cur_x, cur_y)) ?
1094 COL_LOWLIGHT : bgcolour);
1095
1096 if (x2 != -1 || y2 != -1)
1097 draw_tile(dr, ds, state, x2, y2, t, bgcolour);
1098 }
1099 ds->tiles[i] = t0;
1100 }
1101
1102 ds->cur_x = cur_x;
1103 ds->cur_y = cur_y;
1104
1105 unclip(dr);
1106
1107 ds->bgcolour = bgcolour;
1108
1109 /*
1110 * Update the status bar.
1111 */
1112 {
1113 char statusbuf[256];
1114
1115 /*
1116 * Don't show the new status until we're also showing the
1117 * new _state_ - after the game animation is complete.
1118 */
1119 if (oldstate)
1120 state = oldstate;
1121
1122 if (state->used_solve)
1123 sprintf(statusbuf, "Moves since auto-solve: %d",
1124 state->movecount - state->completed);
1125 else {
1126 sprintf(statusbuf, "%sMoves: %d",
1127 (state->completed ? "COMPLETED! " : ""),
1128 (state->completed ? state->completed : state->movecount));
1129 if (state->movetarget)
1130 sprintf(statusbuf+strlen(statusbuf), " (target %d)",
1131 state->movetarget);
1132 }
1133
1134 status_bar(dr, statusbuf);
1135 }
1136}
1137
1138static float game_anim_length(const game_state *oldstate,
1139 const game_state *newstate, int dir, game_ui *ui)
1140{
1141 return ANIM_TIME;
1142}
1143
1144static float game_flash_length(const game_state *oldstate,
1145 const game_state *newstate, int dir, game_ui *ui)
1146{
1147 if (!oldstate->completed && newstate->completed &&
1148 !oldstate->used_solve && !newstate->used_solve)
1149 return 2 * FLASH_FRAME;
1150 else
1151 return 0.0F;
1152}
1153
1154static int game_status(const game_state *state)
1155{
1156 return state->completed ? +1 : 0;
1157}
1158
1159static int game_timing_state(const game_state *state, game_ui *ui)
1160{
1161 return TRUE;
1162}
1163
1164static void game_print_size(const game_params *params, float *x, float *y)
1165{
1166}
1167
1168static void game_print(drawing *dr, const game_state *state, int tilesize)
1169{
1170}
1171
1172#ifdef COMBINED
1173#define thegame sixteen
1174#endif
1175
1176const struct game thegame = {
1177 "Sixteen", "games.sixteen", "sixteen",
1178 default_params,
1179 game_fetch_preset, NULL,
1180 decode_params,
1181 encode_params,
1182 free_params,
1183 dup_params,
1184 TRUE, game_configure, custom_params,
1185 validate_params,
1186 new_game_desc,
1187 validate_desc,
1188 new_game,
1189 dup_game,
1190 free_game,
1191 TRUE, solve_game,
1192 TRUE, game_can_format_as_text_now, game_text_format,
1193 new_ui,
1194 free_ui,
1195 encode_ui,
1196 decode_ui,
1197 game_changed_state,
1198 interpret_move,
1199 execute_move,
1200 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1201 game_colours,
1202 game_new_drawstate,
1203 game_free_drawstate,
1204 game_redraw,
1205 game_anim_length,
1206 game_flash_length,
1207 game_status,
1208 FALSE, FALSE, game_print_size, game_print,
1209 TRUE, /* wants_statusbar */
1210 FALSE, game_timing_state,
1211 0, /* flags */
1212};
1213
1214/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/sixteen.html b/apps/plugins/puzzles/src/sixteen.html
new file mode 100644
index 0000000000..cda88d5e6c
--- /dev/null
+++ b/apps/plugins/puzzles/src/sixteen.html
@@ -0,0 +1,48 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Sixteen</title>
7<link rel="previous" href="fifteen.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="twiddle.html">
12</head>
13<body>
14<p><a href="fifteen.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="twiddle.html">Next</a></p>
15<h1><a name="C6"></a>Chapter 6: <a name="i0"></a>Sixteen</h1>
16<p>
17Another sliding tile puzzle, visually similar to Fifteen (see <a href="fifteen.html#C5">chapter 5</a>) but with a different type of move. This time, there is no hole: all 16 squares on the grid contain numbered squares. Your move is to shift an entire row left or right, or shift an entire column up or down; every time you do that, the tile you shift off the grid re-appears at the other end of the same row, in the space you just vacated. To win, arrange the tiles into numerical order (1,2,3,4 on the top row, 13,14,15,16 on the bottom). When you've done that, try playing on different sizes of grid.
18</p>
19<p>
20I <em>might</em> have invented this game myself, though only by accident if so (and I'm sure other people have independently invented it). I thought I was imitating a screensaver I'd seen, but I have a feeling that the screensaver might actually have been a Fifteen-type puzzle rather than this slightly different kind. So this might be the one thing in my puzzle collection which represents creativity on my part rather than just engineering.
21</p>
22<h2><a name="S6.1"></a>6.1 <a name="i1"></a>Sixteen controls</h2>
23<p>
24Left-clicking on an arrow will move the appropriate row or column in the direction indicated. Right-clicking will move it in the opposite direction.
25</p>
26<p>
27Alternatively, use the cursor keys to move the position indicator around the edge of the grid, and use the return key to move the row/column in the direction indicated.
28</p>
29<p>
30You can also move the tiles directly. Move the cursor onto a tile, hold Control and press an arrow key to move the tile under the cursor and move the cursor along with the tile. Or, hold Shift to move only the tile. Pressing Enter simulates holding down Control (press Enter again to release), while pressing Space simulates holding down shift.
31</p>
32<p>
33(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
34</p>
35<h2><a name="S6.2"></a>6.2 <a name="i2"></a>Sixteen parameters</h2>
36<p>
37The parameters available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu are:
38</p>
39<ul><li>
40<em>Width</em> and <em>Height</em>, which are self-explanatory.
41</li>
42<li>
43You can ask for a limited shuffling operation to be performed on the grid. By default, Sixteen will shuffle the grid in such a way that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible.
44</li>
45</ul>
46
47<hr><address></address></body>
48</html>
diff --git a/apps/plugins/puzzles/src/slant.R b/apps/plugins/puzzles/src/slant.R
new file mode 100644
index 0000000000..ff0d21f1eb
--- /dev/null
+++ b/apps/plugins/puzzles/src/slant.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3SLANT_EXTRA = dsf findloop
4
5slant : [X] GTK COMMON slant SLANT_EXTRA slant-icon|no-icon
6
7slant : [G] WINDOWS COMMON slant SLANT_EXTRA slant.res|noicon.res
8
9slantsolver : [U] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE
10slantsolver : [C] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE
11
12ALL += slant[COMBINED] SLANT_EXTRA
13
14!begin am gtk
15GAMES += slant
16!end
17
18!begin >list.c
19 A(slant) \
20!end
21
22!begin >gamedesc.txt
23slant:slant.exe:Slant:Maze-drawing puzzle:Draw a maze of slanting lines that matches the clues.
24!end
diff --git a/apps/plugins/puzzles/src/slant.c b/apps/plugins/puzzles/src/slant.c
new file mode 100644
index 0000000000..5f9f4f6fed
--- /dev/null
+++ b/apps/plugins/puzzles/src/slant.c
@@ -0,0 +1,2278 @@
1/*
2 * slant.c: Puzzle from nikoli.co.jp involving drawing a diagonal
3 * line through each square of a grid.
4 */
5
6/*
7 * In this puzzle you have a grid of squares, each of which must
8 * contain a diagonal line; you also have clue numbers placed at
9 * _points_ of that grid, which means there's a (w+1) x (h+1) array
10 * of possible clue positions.
11 *
12 * I'm therefore going to adopt a rigid convention throughout this
13 * source file of using w and h for the dimensions of the grid of
14 * squares, and W and H for the dimensions of the grid of points.
15 * Thus, W == w+1 and H == h+1 always.
16 *
17 * Clue arrays will be W*H `signed char's, and the clue at each
18 * point will be a number from 0 to 4, or -1 if there's no clue.
19 *
20 * Solution arrays will be W*H `signed char's, and the number at
21 * each point will be +1 for a forward slash (/), -1 for a
22 * backslash (\), and 0 for unknown.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdarg.h>
28#include <string.h>
29#include <assert.h>
30#include <ctype.h>
31#include <math.h>
32
33#include "puzzles.h"
34
35enum {
36 COL_BACKGROUND,
37 COL_GRID,
38 COL_INK,
39 COL_SLANT1,
40 COL_SLANT2,
41 COL_ERROR,
42 COL_CURSOR,
43 COL_FILLEDSQUARE,
44 NCOLOURS
45};
46
47/*
48 * In standalone solver mode, `verbose' is a variable which can be
49 * set by command-line option; in debugging mode it's simply always
50 * true.
51 */
52#if defined STANDALONE_SOLVER
53#define SOLVER_DIAGNOSTICS
54int verbose = FALSE;
55#elif defined SOLVER_DIAGNOSTICS
56#define verbose TRUE
57#endif
58
59/*
60 * Difficulty levels. I do some macro ickery here to ensure that my
61 * enum and the various forms of my name list always match up.
62 */
63#define DIFFLIST(A) \
64 A(EASY,Easy,e) \
65 A(HARD,Hard,h)
66#define ENUM(upper,title,lower) DIFF_ ## upper,
67#define TITLE(upper,title,lower) #title,
68#define ENCODE(upper,title,lower) #lower
69#define CONFIG(upper,title,lower) ":" #title
70enum { DIFFLIST(ENUM) DIFFCOUNT };
71static char const *const slant_diffnames[] = { DIFFLIST(TITLE) };
72static char const slant_diffchars[] = DIFFLIST(ENCODE);
73#define DIFFCONFIG DIFFLIST(CONFIG)
74
75struct game_params {
76 int w, h, diff;
77};
78
79typedef struct game_clues {
80 int w, h;
81 signed char *clues;
82 int *tmpdsf;
83 int refcount;
84} game_clues;
85
86#define ERR_VERTEX 1
87#define ERR_SQUARE 2
88
89struct game_state {
90 struct game_params p;
91 game_clues *clues;
92 signed char *soln;
93 unsigned char *errors;
94 int completed;
95 int used_solve; /* used to suppress completion flash */
96};
97
98static game_params *default_params(void)
99{
100 game_params *ret = snew(game_params);
101
102 ret->w = ret->h = 8;
103 ret->diff = DIFF_EASY;
104
105 return ret;
106}
107
108static const struct game_params slant_presets[] = {
109 {5, 5, DIFF_EASY},
110 {5, 5, DIFF_HARD},
111 {8, 8, DIFF_EASY},
112 {8, 8, DIFF_HARD},
113 {12, 10, DIFF_EASY},
114 {12, 10, DIFF_HARD},
115};
116
117static int game_fetch_preset(int i, char **name, game_params **params)
118{
119 game_params *ret;
120 char str[80];
121
122 if (i < 0 || i >= lenof(slant_presets))
123 return FALSE;
124
125 ret = snew(game_params);
126 *ret = slant_presets[i];
127
128 sprintf(str, "%dx%d %s", ret->w, ret->h, slant_diffnames[ret->diff]);
129
130 *name = dupstr(str);
131 *params = ret;
132 return TRUE;
133}
134
135static void free_params(game_params *params)
136{
137 sfree(params);
138}
139
140static game_params *dup_params(const game_params *params)
141{
142 game_params *ret = snew(game_params);
143 *ret = *params; /* structure copy */
144 return ret;
145}
146
147static void decode_params(game_params *ret, char const *string)
148{
149 ret->w = ret->h = atoi(string);
150 while (*string && isdigit((unsigned char)*string)) string++;
151 if (*string == 'x') {
152 string++;
153 ret->h = atoi(string);
154 while (*string && isdigit((unsigned char)*string)) string++;
155 }
156 if (*string == 'd') {
157 int i;
158 string++;
159 for (i = 0; i < DIFFCOUNT; i++)
160 if (*string == slant_diffchars[i])
161 ret->diff = i;
162 if (*string) string++;
163 }
164}
165
166static char *encode_params(const game_params *params, int full)
167{
168 char data[256];
169
170 sprintf(data, "%dx%d", params->w, params->h);
171 if (full)
172 sprintf(data + strlen(data), "d%c", slant_diffchars[params->diff]);
173
174 return dupstr(data);
175}
176
177static config_item *game_configure(const game_params *params)
178{
179 config_item *ret;
180 char buf[80];
181
182 ret = snewn(4, config_item);
183
184 ret[0].name = "Width";
185 ret[0].type = C_STRING;
186 sprintf(buf, "%d", params->w);
187 ret[0].sval = dupstr(buf);
188 ret[0].ival = 0;
189
190 ret[1].name = "Height";
191 ret[1].type = C_STRING;
192 sprintf(buf, "%d", params->h);
193 ret[1].sval = dupstr(buf);
194 ret[1].ival = 0;
195
196 ret[2].name = "Difficulty";
197 ret[2].type = C_CHOICES;
198 ret[2].sval = DIFFCONFIG;
199 ret[2].ival = params->diff;
200
201 ret[3].name = NULL;
202 ret[3].type = C_END;
203 ret[3].sval = NULL;
204 ret[3].ival = 0;
205
206 return ret;
207}
208
209static game_params *custom_params(const config_item *cfg)
210{
211 game_params *ret = snew(game_params);
212
213 ret->w = atoi(cfg[0].sval);
214 ret->h = atoi(cfg[1].sval);
215 ret->diff = cfg[2].ival;
216
217 return ret;
218}
219
220static char *validate_params(const game_params *params, int full)
221{
222 /*
223 * (At least at the time of writing this comment) The grid
224 * generator is actually capable of handling even zero grid
225 * dimensions without crashing. Puzzles with a zero-area grid
226 * are a bit boring, though, because they're already solved :-)
227 * And puzzles with a dimension of 1 can't be made Hard, which
228 * means the simplest thing is to forbid them altogether.
229 */
230
231 if (params->w < 2 || params->h < 2)
232 return "Width and height must both be at least two";
233
234 return NULL;
235}
236
237/*
238 * Scratch space for solver.
239 */
240struct solver_scratch {
241 /*
242 * Disjoint set forest which tracks the connected sets of
243 * points.
244 */
245 int *connected;
246
247 /*
248 * Counts the number of possible exits from each connected set
249 * of points. (That is, the number of possible _simultaneous_
250 * exits: an unconnected point labelled 2 has an exit count of
251 * 2 even if all four possible edges are still under
252 * consideration.)
253 */
254 int *exits;
255
256 /*
257 * Tracks whether each connected set of points includes a
258 * border point.
259 */
260 unsigned char *border;
261
262 /*
263 * Another disjoint set forest. This one tracks _squares_ which
264 * are known to slant in the same direction.
265 */
266 int *equiv;
267
268 /*
269 * Stores slash values which we know for an equivalence class.
270 * When we fill in a square, we set slashval[canonify(x)] to
271 * the same value as soln[x], so that we can then spot other
272 * squares equivalent to it and fill them in immediately via
273 * their known equivalence.
274 */
275 signed char *slashval;
276
277 /*
278 * Stores possible v-shapes. This array is w by h in size, but
279 * not every bit of every entry is meaningful. The bits mean:
280 *
281 * - bit 0 for a square means that that square and the one to
282 * its right might form a v-shape between them
283 * - bit 1 for a square means that that square and the one to
284 * its right might form a ^-shape between them
285 * - bit 2 for a square means that that square and the one
286 * below it might form a >-shape between them
287 * - bit 3 for a square means that that square and the one
288 * below it might form a <-shape between them
289 *
290 * Any starting 1 or 3 clue rules out four bits in this array
291 * immediately; a 2 clue propagates any ruled-out bit past it
292 * (if the two squares on one side of a 2 cannot be a v-shape,
293 * then neither can the two on the other side be the same
294 * v-shape); we can rule out further bits during play using
295 * partially filled 2 clues; whenever a pair of squares is
296 * known not to be _either_ kind of v-shape, we can mark them
297 * as equivalent.
298 */
299 unsigned char *vbitmap;
300
301 /*
302 * Useful to have this information automatically passed to
303 * solver subroutines. (This pointer is not dynamically
304 * allocated by new_scratch and free_scratch.)
305 */
306 const signed char *clues;
307};
308
309static struct solver_scratch *new_scratch(int w, int h)
310{
311 int W = w+1, H = h+1;
312 struct solver_scratch *ret = snew(struct solver_scratch);
313 ret->connected = snewn(W*H, int);
314 ret->exits = snewn(W*H, int);
315 ret->border = snewn(W*H, unsigned char);
316 ret->equiv = snewn(w*h, int);
317 ret->slashval = snewn(w*h, signed char);
318 ret->vbitmap = snewn(w*h, unsigned char);
319 return ret;
320}
321
322static void free_scratch(struct solver_scratch *sc)
323{
324 sfree(sc->vbitmap);
325 sfree(sc->slashval);
326 sfree(sc->equiv);
327 sfree(sc->border);
328 sfree(sc->exits);
329 sfree(sc->connected);
330 sfree(sc);
331}
332
333/*
334 * Wrapper on dsf_merge() which updates the `exits' and `border'
335 * arrays.
336 */
337static void merge_vertices(int *connected,
338 struct solver_scratch *sc, int i, int j)
339{
340 int exits = -1, border = FALSE; /* initialise to placate optimiser */
341
342 if (sc) {
343 i = dsf_canonify(connected, i);
344 j = dsf_canonify(connected, j);
345
346 /*
347 * We have used one possible exit from each of the two
348 * classes. Thus, the viable exit count of the new class is
349 * the sum of the old exit counts minus two.
350 */
351 exits = sc->exits[i] + sc->exits[j] - 2;
352
353 border = sc->border[i] || sc->border[j];
354 }
355
356 dsf_merge(connected, i, j);
357
358 if (sc) {
359 i = dsf_canonify(connected, i);
360 sc->exits[i] = exits;
361 sc->border[i] = border;
362 }
363}
364
365/*
366 * Called when we have just blocked one way out of a particular
367 * point. If that point is a non-clue point (thus has a variable
368 * number of exits), we have therefore decreased its potential exit
369 * count, so we must decrement the exit count for the group as a
370 * whole.
371 */
372static void decr_exits(struct solver_scratch *sc, int i)
373{
374 if (sc->clues[i] < 0) {
375 i = dsf_canonify(sc->connected, i);
376 sc->exits[i]--;
377 }
378}
379
380static void fill_square(int w, int h, int x, int y, int v,
381 signed char *soln,
382 int *connected, struct solver_scratch *sc)
383{
384 int W = w+1 /*, H = h+1 */;
385
386 assert(x >= 0 && x < w && y >= 0 && y < h);
387
388 if (soln[y*w+x] != 0) {
389 return; /* do nothing */
390 }
391
392#ifdef SOLVER_DIAGNOSTICS
393 if (verbose)
394 printf(" placing %c in %d,%d\n", v == -1 ? '\\' : '/', x, y);
395#endif
396
397 soln[y*w+x] = v;
398
399 if (sc) {
400 int c = dsf_canonify(sc->equiv, y*w+x);
401 sc->slashval[c] = v;
402 }
403
404 if (v < 0) {
405 merge_vertices(connected, sc, y*W+x, (y+1)*W+(x+1));
406 if (sc) {
407 decr_exits(sc, y*W+(x+1));
408 decr_exits(sc, (y+1)*W+x);
409 }
410 } else {
411 merge_vertices(connected, sc, y*W+(x+1), (y+1)*W+x);
412 if (sc) {
413 decr_exits(sc, y*W+x);
414 decr_exits(sc, (y+1)*W+(x+1));
415 }
416 }
417}
418
419static int vbitmap_clear(int w, int h, struct solver_scratch *sc,
420 int x, int y, int vbits, char *reason, ...)
421{
422 int done_something = FALSE;
423 int vbit;
424
425 for (vbit = 1; vbit <= 8; vbit <<= 1)
426 if (vbits & sc->vbitmap[y*w+x] & vbit) {
427 done_something = TRUE;
428#ifdef SOLVER_DIAGNOSTICS
429 if (verbose) {
430 va_list ap;
431
432 printf("ruling out %c shape at (%d,%d)-(%d,%d) (",
433 "!v^!>!!!<"[vbit], x, y,
434 x+((vbit&0x3)!=0), y+((vbit&0xC)!=0));
435
436 va_start(ap, reason);
437 vprintf(reason, ap);
438 va_end(ap);
439
440 printf(")\n");
441 }
442#endif
443 sc->vbitmap[y*w+x] &= ~vbit;
444 }
445
446 return done_something;
447}
448
449/*
450 * Solver. Returns 0 for impossibility, 1 for success, 2 for
451 * ambiguity or failure to converge.
452 */
453static int slant_solve(int w, int h, const signed char *clues,
454 signed char *soln, struct solver_scratch *sc,
455 int difficulty)
456{
457 int W = w+1, H = h+1;
458 int x, y, i, j;
459 int done_something;
460
461 /*
462 * Clear the output.
463 */
464 memset(soln, 0, w*h);
465
466 sc->clues = clues;
467
468 /*
469 * Establish a disjoint set forest for tracking connectedness
470 * between grid points.
471 */
472 dsf_init(sc->connected, W*H);
473
474 /*
475 * Establish a disjoint set forest for tracking which squares
476 * are known to slant in the same direction.
477 */
478 dsf_init(sc->equiv, w*h);
479
480 /*
481 * Clear the slashval array.
482 */
483 memset(sc->slashval, 0, w*h);
484
485 /*
486 * Set up the vbitmap array. Initially all types of v are possible.
487 */
488 memset(sc->vbitmap, 0xF, w*h);
489
490 /*
491 * Initialise the `exits' and `border' arrays. These are used
492 * to do second-order loop avoidance: the dual of the no loops
493 * constraint is that every point must be somehow connected to
494 * the border of the grid (otherwise there would be a solid
495 * loop around it which prevented this).
496 *
497 * I define a `dead end' to be a connected group of points
498 * which contains no border point, and which can form at most
499 * one new connection outside itself. Then I forbid placing an
500 * edge so that it connects together two dead-end groups, since
501 * this would yield a non-border-connected isolated subgraph
502 * with no further scope to extend it.
503 */
504 for (y = 0; y < H; y++)
505 for (x = 0; x < W; x++) {
506 if (y == 0 || y == H-1 || x == 0 || x == W-1)
507 sc->border[y*W+x] = TRUE;
508 else
509 sc->border[y*W+x] = FALSE;
510
511 if (clues[y*W+x] < 0)
512 sc->exits[y*W+x] = 4;
513 else
514 sc->exits[y*W+x] = clues[y*W+x];
515 }
516
517 /*
518 * Repeatedly try to deduce something until we can't.
519 */
520 do {
521 done_something = FALSE;
522
523 /*
524 * Any clue point with the number of remaining lines equal
525 * to zero or to the number of remaining undecided
526 * neighbouring squares can be filled in completely.
527 */
528 for (y = 0; y < H; y++)
529 for (x = 0; x < W; x++) {
530 struct {
531 int pos, slash;
532 } neighbours[4];
533 int nneighbours;
534 int nu, nl, c, s, eq, eq2, last, meq, mj1, mj2;
535
536 if ((c = clues[y*W+x]) < 0)
537 continue;
538
539 /*
540 * We have a clue point. Start by listing its
541 * neighbouring squares, in order around the point,
542 * together with the type of slash that would be
543 * required in that square to connect to the point.
544 */
545 nneighbours = 0;
546 if (x > 0 && y > 0) {
547 neighbours[nneighbours].pos = (y-1)*w+(x-1);
548 neighbours[nneighbours].slash = -1;
549 nneighbours++;
550 }
551 if (x > 0 && y < h) {
552 neighbours[nneighbours].pos = y*w+(x-1);
553 neighbours[nneighbours].slash = +1;
554 nneighbours++;
555 }
556 if (x < w && y < h) {
557 neighbours[nneighbours].pos = y*w+x;
558 neighbours[nneighbours].slash = -1;
559 nneighbours++;
560 }
561 if (x < w && y > 0) {
562 neighbours[nneighbours].pos = (y-1)*w+x;
563 neighbours[nneighbours].slash = +1;
564 nneighbours++;
565 }
566
567 /*
568 * Count up the number of undecided neighbours, and
569 * also the number of lines already present.
570 *
571 * If we're not on DIFF_EASY, then in this loop we
572 * also track whether we've seen two adjacent empty
573 * squares belonging to the same equivalence class
574 * (meaning they have the same type of slash). If
575 * so, we count them jointly as one line.
576 */
577 nu = 0;
578 nl = c;
579 last = neighbours[nneighbours-1].pos;
580 if (soln[last] == 0)
581 eq = dsf_canonify(sc->equiv, last);
582 else
583 eq = -1;
584 meq = mj1 = mj2 = -1;
585 for (i = 0; i < nneighbours; i++) {
586 j = neighbours[i].pos;
587 s = neighbours[i].slash;
588 if (soln[j] == 0) {
589 nu++; /* undecided */
590 if (meq < 0 && difficulty > DIFF_EASY) {
591 eq2 = dsf_canonify(sc->equiv, j);
592 if (eq == eq2 && last != j) {
593 /*
594 * We've found an equivalent pair.
595 * Mark it. This also inhibits any
596 * further equivalence tracking
597 * around this square, since we can
598 * only handle one pair (and in
599 * particular we want to avoid
600 * being misled by two overlapping
601 * equivalence pairs).
602 */
603 meq = eq;
604 mj1 = last;
605 mj2 = j;
606 nl--; /* count one line */
607 nu -= 2; /* and lose two undecideds */
608 } else
609 eq = eq2;
610 }
611 } else {
612 eq = -1;
613 if (soln[j] == s)
614 nl--; /* here's a line */
615 }
616 last = j;
617 }
618
619 /*
620 * Check the counts.
621 */
622 if (nl < 0 || nl > nu) {
623 /*
624 * No consistent value for this at all!
625 */
626#ifdef SOLVER_DIAGNOSTICS
627 if (verbose)
628 printf("need %d / %d lines around clue point at %d,%d!\n",
629 nl, nu, x, y);
630#endif
631 return 0; /* impossible */
632 }
633
634 if (nu > 0 && (nl == 0 || nl == nu)) {
635#ifdef SOLVER_DIAGNOSTICS
636 if (verbose) {
637 if (meq >= 0)
638 printf("partially (since %d,%d == %d,%d) ",
639 mj1%w, mj1/w, mj2%w, mj2/w);
640 printf("%s around clue point at %d,%d\n",
641 nl ? "filling" : "emptying", x, y);
642 }
643#endif
644 for (i = 0; i < nneighbours; i++) {
645 j = neighbours[i].pos;
646 s = neighbours[i].slash;
647 if (soln[j] == 0 && j != mj1 && j != mj2)
648 fill_square(w, h, j%w, j/w, (nl ? s : -s), soln,
649 sc->connected, sc);
650 }
651
652 done_something = TRUE;
653 } else if (nu == 2 && nl == 1 && difficulty > DIFF_EASY) {
654 /*
655 * If we have precisely two undecided squares
656 * and precisely one line to place between
657 * them, _and_ those squares are adjacent, then
658 * we can mark them as equivalent to one
659 * another.
660 *
661 * This even applies if meq >= 0: if we have a
662 * 2 clue point and two of its neighbours are
663 * already marked equivalent, we can indeed
664 * mark the other two as equivalent.
665 *
666 * We don't bother with this on DIFF_EASY,
667 * since we wouldn't have used the results
668 * anyway.
669 */
670 last = -1;
671 for (i = 0; i < nneighbours; i++) {
672 j = neighbours[i].pos;
673 if (soln[j] == 0 && j != mj1 && j != mj2) {
674 if (last < 0)
675 last = i;
676 else if (last == i-1 || (last == 0 && i == 3))
677 break; /* found a pair */
678 }
679 }
680 if (i < nneighbours) {
681 int sv1, sv2;
682
683 assert(last >= 0);
684 /*
685 * neighbours[last] and neighbours[i] are
686 * the pair. Mark them equivalent.
687 */
688#ifdef SOLVER_DIAGNOSTICS
689 if (verbose) {
690 if (meq >= 0)
691 printf("since %d,%d == %d,%d, ",
692 mj1%w, mj1/w, mj2%w, mj2/w);
693 }
694#endif
695 mj1 = neighbours[last].pos;
696 mj2 = neighbours[i].pos;
697#ifdef SOLVER_DIAGNOSTICS
698 if (verbose)
699 printf("clue point at %d,%d implies %d,%d == %d,"
700 "%d\n", x, y, mj1%w, mj1/w, mj2%w, mj2/w);
701#endif
702 mj1 = dsf_canonify(sc->equiv, mj1);
703 sv1 = sc->slashval[mj1];
704 mj2 = dsf_canonify(sc->equiv, mj2);
705 sv2 = sc->slashval[mj2];
706 if (sv1 != 0 && sv2 != 0 && sv1 != sv2) {
707#ifdef SOLVER_DIAGNOSTICS
708 if (verbose)
709 printf("merged two equivalence classes with"
710 " different slash values!\n");
711#endif
712 return 0;
713 }
714 sv1 = sv1 ? sv1 : sv2;
715 dsf_merge(sc->equiv, mj1, mj2);
716 mj1 = dsf_canonify(sc->equiv, mj1);
717 sc->slashval[mj1] = sv1;
718 }
719 }
720 }
721
722 if (done_something)
723 continue;
724
725 /*
726 * Failing that, we now apply the second condition, which
727 * is that no square may be filled in such a way as to form
728 * a loop. Also in this loop (since it's over squares
729 * rather than points), we check slashval to see if we've
730 * already filled in another square in the same equivalence
731 * class.
732 *
733 * The slashval check is disabled on DIFF_EASY, as is dead
734 * end avoidance. Only _immediate_ loop avoidance remains.
735 */
736 for (y = 0; y < h; y++)
737 for (x = 0; x < w; x++) {
738 int fs, bs, v;
739 int c1, c2;
740#ifdef SOLVER_DIAGNOSTICS
741 char *reason = "<internal error>";
742#endif
743
744 if (soln[y*w+x])
745 continue; /* got this one already */
746
747 fs = FALSE;
748 bs = FALSE;
749
750 if (difficulty > DIFF_EASY)
751 v = sc->slashval[dsf_canonify(sc->equiv, y*w+x)];
752 else
753 v = 0;
754
755 /*
756 * Try to rule out connectivity between (x,y) and
757 * (x+1,y+1); if successful, we will deduce that we
758 * must have a forward slash.
759 */
760 c1 = dsf_canonify(sc->connected, y*W+x);
761 c2 = dsf_canonify(sc->connected, (y+1)*W+(x+1));
762 if (c1 == c2) {
763 fs = TRUE;
764#ifdef SOLVER_DIAGNOSTICS
765 reason = "simple loop avoidance";
766#endif
767 }
768 if (difficulty > DIFF_EASY &&
769 !sc->border[c1] && !sc->border[c2] &&
770 sc->exits[c1] <= 1 && sc->exits[c2] <= 1) {
771 fs = TRUE;
772#ifdef SOLVER_DIAGNOSTICS
773 reason = "dead end avoidance";
774#endif
775 }
776 if (v == +1) {
777 fs = TRUE;
778#ifdef SOLVER_DIAGNOSTICS
779 reason = "equivalence to an already filled square";
780#endif
781 }
782
783 /*
784 * Now do the same between (x+1,y) and (x,y+1), to
785 * see if we are required to have a backslash.
786 */
787 c1 = dsf_canonify(sc->connected, y*W+(x+1));
788 c2 = dsf_canonify(sc->connected, (y+1)*W+x);
789 if (c1 == c2) {
790 bs = TRUE;
791#ifdef SOLVER_DIAGNOSTICS
792 reason = "simple loop avoidance";
793#endif
794 }
795 if (difficulty > DIFF_EASY &&
796 !sc->border[c1] && !sc->border[c2] &&
797 sc->exits[c1] <= 1 && sc->exits[c2] <= 1) {
798 bs = TRUE;
799#ifdef SOLVER_DIAGNOSTICS
800 reason = "dead end avoidance";
801#endif
802 }
803 if (v == -1) {
804 bs = TRUE;
805#ifdef SOLVER_DIAGNOSTICS
806 reason = "equivalence to an already filled square";
807#endif
808 }
809
810 if (fs && bs) {
811 /*
812 * No consistent value for this at all!
813 */
814#ifdef SOLVER_DIAGNOSTICS
815 if (verbose)
816 printf("%d,%d has no consistent slash!\n", x, y);
817#endif
818 return 0; /* impossible */
819 }
820
821 if (fs) {
822#ifdef SOLVER_DIAGNOSTICS
823 if (verbose)
824 printf("employing %s\n", reason);
825#endif
826 fill_square(w, h, x, y, +1, soln, sc->connected, sc);
827 done_something = TRUE;
828 } else if (bs) {
829#ifdef SOLVER_DIAGNOSTICS
830 if (verbose)
831 printf("employing %s\n", reason);
832#endif
833 fill_square(w, h, x, y, -1, soln, sc->connected, sc);
834 done_something = TRUE;
835 }
836 }
837
838 if (done_something)
839 continue;
840
841 /*
842 * Now see what we can do with the vbitmap array. All
843 * vbitmap deductions are disabled at Easy level.
844 */
845 if (difficulty <= DIFF_EASY)
846 continue;
847
848 for (y = 0; y < h; y++)
849 for (x = 0; x < w; x++) {
850 int s, c;
851
852 /*
853 * Any line already placed in a square must rule
854 * out any type of v which contradicts it.
855 */
856 if ((s = soln[y*w+x]) != 0) {
857 if (x > 0)
858 done_something |=
859 vbitmap_clear(w, h, sc, x-1, y, (s < 0 ? 0x1 : 0x2),
860 "contradicts known edge at (%d,%d)",x,y);
861 if (x+1 < w)
862 done_something |=
863 vbitmap_clear(w, h, sc, x, y, (s < 0 ? 0x2 : 0x1),
864 "contradicts known edge at (%d,%d)",x,y);
865 if (y > 0)
866 done_something |=
867 vbitmap_clear(w, h, sc, x, y-1, (s < 0 ? 0x4 : 0x8),
868 "contradicts known edge at (%d,%d)",x,y);
869 if (y+1 < h)
870 done_something |=
871 vbitmap_clear(w, h, sc, x, y, (s < 0 ? 0x8 : 0x4),
872 "contradicts known edge at (%d,%d)",x,y);
873 }
874
875 /*
876 * If both types of v are ruled out for a pair of
877 * adjacent squares, mark them as equivalent.
878 */
879 if (x+1 < w && !(sc->vbitmap[y*w+x] & 0x3)) {
880 int n1 = y*w+x, n2 = y*w+(x+1);
881 if (dsf_canonify(sc->equiv, n1) !=
882 dsf_canonify(sc->equiv, n2)) {
883 dsf_merge(sc->equiv, n1, n2);
884 done_something = TRUE;
885#ifdef SOLVER_DIAGNOSTICS
886 if (verbose)
887 printf("(%d,%d) and (%d,%d) must be equivalent"
888 " because both v-shapes are ruled out\n",
889 x, y, x+1, y);
890#endif
891 }
892 }
893 if (y+1 < h && !(sc->vbitmap[y*w+x] & 0xC)) {
894 int n1 = y*w+x, n2 = (y+1)*w+x;
895 if (dsf_canonify(sc->equiv, n1) !=
896 dsf_canonify(sc->equiv, n2)) {
897 dsf_merge(sc->equiv, n1, n2);
898 done_something = TRUE;
899#ifdef SOLVER_DIAGNOSTICS
900 if (verbose)
901 printf("(%d,%d) and (%d,%d) must be equivalent"
902 " because both v-shapes are ruled out\n",
903 x, y, x, y+1);
904#endif
905 }
906 }
907
908 /*
909 * The remaining work in this loop only works
910 * around non-edge clue points.
911 */
912 if (y == 0 || x == 0)
913 continue;
914 if ((c = clues[y*W+x]) < 0)
915 continue;
916
917 /*
918 * x,y marks a clue point not on the grid edge. See
919 * if this clue point allows us to rule out any v
920 * shapes.
921 */
922
923 if (c == 1) {
924 /*
925 * A 1 clue can never have any v shape pointing
926 * at it.
927 */
928 done_something |=
929 vbitmap_clear(w, h, sc, x-1, y-1, 0x5,
930 "points at 1 clue at (%d,%d)", x, y);
931 done_something |=
932 vbitmap_clear(w, h, sc, x-1, y, 0x2,
933 "points at 1 clue at (%d,%d)", x, y);
934 done_something |=
935 vbitmap_clear(w, h, sc, x, y-1, 0x8,
936 "points at 1 clue at (%d,%d)", x, y);
937 } else if (c == 3) {
938 /*
939 * A 3 clue can never have any v shape pointing
940 * away from it.
941 */
942 done_something |=
943 vbitmap_clear(w, h, sc, x-1, y-1, 0xA,
944 "points away from 3 clue at (%d,%d)", x, y);
945 done_something |=
946 vbitmap_clear(w, h, sc, x-1, y, 0x1,
947 "points away from 3 clue at (%d,%d)", x, y);
948 done_something |=
949 vbitmap_clear(w, h, sc, x, y-1, 0x4,
950 "points away from 3 clue at (%d,%d)", x, y);
951 } else if (c == 2) {
952 /*
953 * If a 2 clue has any kind of v ruled out on
954 * one side of it, the same v is ruled out on
955 * the other side.
956 */
957 done_something |=
958 vbitmap_clear(w, h, sc, x-1, y-1,
959 (sc->vbitmap[(y )*w+(x-1)] & 0x3) ^ 0x3,
960 "propagated by 2 clue at (%d,%d)", x, y);
961 done_something |=
962 vbitmap_clear(w, h, sc, x-1, y-1,
963 (sc->vbitmap[(y-1)*w+(x )] & 0xC) ^ 0xC,
964 "propagated by 2 clue at (%d,%d)", x, y);
965 done_something |=
966 vbitmap_clear(w, h, sc, x-1, y,
967 (sc->vbitmap[(y-1)*w+(x-1)] & 0x3) ^ 0x3,
968 "propagated by 2 clue at (%d,%d)", x, y);
969 done_something |=
970 vbitmap_clear(w, h, sc, x, y-1,
971 (sc->vbitmap[(y-1)*w+(x-1)] & 0xC) ^ 0xC,
972 "propagated by 2 clue at (%d,%d)", x, y);
973 }
974
975#undef CLEARBITS
976
977 }
978
979 } while (done_something);
980
981 /*
982 * Solver can make no more progress. See if the grid is full.
983 */
984 for (i = 0; i < w*h; i++)
985 if (!soln[i])
986 return 2; /* failed to converge */
987 return 1; /* success */
988}
989
990/*
991 * Filled-grid generator.
992 */
993static void slant_generate(int w, int h, signed char *soln, random_state *rs)
994{
995 int W = w+1, H = h+1;
996 int x, y, i;
997 int *connected, *indices;
998
999 /*
1000 * Clear the output.
1001 */
1002 memset(soln, 0, w*h);
1003
1004 /*
1005 * Establish a disjoint set forest for tracking connectedness
1006 * between grid points.
1007 */
1008 connected = snew_dsf(W*H);
1009
1010 /*
1011 * Prepare a list of the squares in the grid, and fill them in
1012 * in a random order.
1013 */
1014 indices = snewn(w*h, int);
1015 for (i = 0; i < w*h; i++)
1016 indices[i] = i;
1017 shuffle(indices, w*h, sizeof(*indices), rs);
1018
1019 /*
1020 * Fill in each one in turn.
1021 */
1022 for (i = 0; i < w*h; i++) {
1023 int fs, bs, v;
1024
1025 y = indices[i] / w;
1026 x = indices[i] % w;
1027
1028 fs = (dsf_canonify(connected, y*W+x) ==
1029 dsf_canonify(connected, (y+1)*W+(x+1)));
1030 bs = (dsf_canonify(connected, (y+1)*W+x) ==
1031 dsf_canonify(connected, y*W+(x+1)));
1032
1033 /*
1034 * It isn't possible to get into a situation where we
1035 * aren't allowed to place _either_ type of slash in a
1036 * square. Thus, filled-grid generation never has to
1037 * backtrack.
1038 *
1039 * Proof (thanks to Gareth Taylor):
1040 *
1041 * If it were possible, it would have to be because there
1042 * was an existing path (not using this square) between the
1043 * top-left and bottom-right corners of this square, and
1044 * another between the other two. These two paths would
1045 * have to cross at some point.
1046 *
1047 * Obviously they can't cross in the middle of a square, so
1048 * they must cross by sharing a point in common. But this
1049 * isn't possible either: if you chessboard-colour all the
1050 * points on the grid, you find that any continuous
1051 * diagonal path is entirely composed of points of the same
1052 * colour. And one of our two hypothetical paths is between
1053 * two black points, and the other is between two white
1054 * points - therefore they can have no point in common. []
1055 */
1056 assert(!(fs && bs));
1057
1058 v = fs ? +1 : bs ? -1 : 2 * random_upto(rs, 2) - 1;
1059 fill_square(w, h, x, y, v, soln, connected, NULL);
1060 }
1061
1062 sfree(indices);
1063 sfree(connected);
1064}
1065
1066static char *new_game_desc(const game_params *params, random_state *rs,
1067 char **aux, int interactive)
1068{
1069 int w = params->w, h = params->h, W = w+1, H = h+1;
1070 signed char *soln, *tmpsoln, *clues;
1071 int *clueindices;
1072 struct solver_scratch *sc;
1073 int x, y, v, i, j;
1074 char *desc;
1075
1076 soln = snewn(w*h, signed char);
1077 tmpsoln = snewn(w*h, signed char);
1078 clues = snewn(W*H, signed char);
1079 clueindices = snewn(W*H, int);
1080 sc = new_scratch(w, h);
1081
1082 do {
1083 /*
1084 * Create the filled grid.
1085 */
1086 slant_generate(w, h, soln, rs);
1087
1088 /*
1089 * Fill in the complete set of clues.
1090 */
1091 for (y = 0; y < H; y++)
1092 for (x = 0; x < W; x++) {
1093 v = 0;
1094
1095 if (x > 0 && y > 0 && soln[(y-1)*w+(x-1)] == -1) v++;
1096 if (x > 0 && y < h && soln[y*w+(x-1)] == +1) v++;
1097 if (x < w && y > 0 && soln[(y-1)*w+x] == +1) v++;
1098 if (x < w && y < h && soln[y*w+x] == -1) v++;
1099
1100 clues[y*W+x] = v;
1101 }
1102
1103 /*
1104 * With all clue points filled in, all puzzles are easy: we can
1105 * simply process the clue points in lexicographic order, and
1106 * at each clue point we will always have at most one square
1107 * undecided, which we can then fill in uniquely.
1108 */
1109 assert(slant_solve(w, h, clues, tmpsoln, sc, DIFF_EASY) == 1);
1110
1111 /*
1112 * Remove as many clues as possible while retaining solubility.
1113 *
1114 * In DIFF_HARD mode, we prioritise the removal of obvious
1115 * starting points (4s, 0s, border 2s and corner 1s), on
1116 * the grounds that having as few of these as possible
1117 * seems like a good thing. In particular, we can often get
1118 * away without _any_ completely obvious starting points,
1119 * which is even better.
1120 */
1121 for (i = 0; i < W*H; i++)
1122 clueindices[i] = i;
1123 shuffle(clueindices, W*H, sizeof(*clueindices), rs);
1124 for (j = 0; j < 2; j++) {
1125 for (i = 0; i < W*H; i++) {
1126 int pass, yb, xb;
1127
1128 y = clueindices[i] / W;
1129 x = clueindices[i] % W;
1130 v = clues[y*W+x];
1131
1132 /*
1133 * Identify which pass we should process this point
1134 * in. If it's an obvious start point, _or_ we're
1135 * in DIFF_EASY, then it goes in pass 0; otherwise
1136 * pass 1.
1137 */
1138 xb = (x == 0 || x == W-1);
1139 yb = (y == 0 || y == H-1);
1140 if (params->diff == DIFF_EASY || v == 4 || v == 0 ||
1141 (v == 2 && (xb||yb)) || (v == 1 && xb && yb))
1142 pass = 0;
1143 else
1144 pass = 1;
1145
1146 if (pass == j) {
1147 clues[y*W+x] = -1;
1148 if (slant_solve(w, h, clues, tmpsoln, sc,
1149 params->diff) != 1)
1150 clues[y*W+x] = v; /* put it back */
1151 }
1152 }
1153 }
1154
1155 /*
1156 * And finally, verify that the grid is of _at least_ the
1157 * requested difficulty, by running the solver one level
1158 * down and verifying that it can't manage it.
1159 */
1160 } while (params->diff > 0 &&
1161 slant_solve(w, h, clues, tmpsoln, sc, params->diff - 1) <= 1);
1162
1163 /*
1164 * Now we have the clue set as it will be presented to the
1165 * user. Encode it in a game desc.
1166 */
1167 {
1168 char *p;
1169 int run, i;
1170
1171 desc = snewn(W*H+1, char);
1172 p = desc;
1173 run = 0;
1174 for (i = 0; i <= W*H; i++) {
1175 int n = (i < W*H ? clues[i] : -2);
1176
1177 if (n == -1)
1178 run++;
1179 else {
1180 if (run) {
1181 while (run > 0) {
1182 int c = 'a' - 1 + run;
1183 if (run > 26)
1184 c = 'z';
1185 *p++ = c;
1186 run -= c - ('a' - 1);
1187 }
1188 }
1189 if (n >= 0)
1190 *p++ = '0' + n;
1191 run = 0;
1192 }
1193 }
1194 assert(p - desc <= W*H);
1195 *p++ = '\0';
1196 desc = sresize(desc, p - desc, char);
1197 }
1198
1199 /*
1200 * Encode the solution as an aux_info.
1201 */
1202 {
1203 char *auxbuf;
1204 *aux = auxbuf = snewn(w*h+1, char);
1205 for (i = 0; i < w*h; i++)
1206 auxbuf[i] = soln[i] < 0 ? '\\' : '/';
1207 auxbuf[w*h] = '\0';
1208 }
1209
1210 free_scratch(sc);
1211 sfree(clueindices);
1212 sfree(clues);
1213 sfree(tmpsoln);
1214 sfree(soln);
1215
1216 return desc;
1217}
1218
1219static char *validate_desc(const game_params *params, const char *desc)
1220{
1221 int w = params->w, h = params->h, W = w+1, H = h+1;
1222 int area = W*H;
1223 int squares = 0;
1224
1225 while (*desc) {
1226 int n = *desc++;
1227 if (n >= 'a' && n <= 'z') {
1228 squares += n - 'a' + 1;
1229 } else if (n >= '0' && n <= '4') {
1230 squares++;
1231 } else
1232 return "Invalid character in game description";
1233 }
1234
1235 if (squares < area)
1236 return "Not enough data to fill grid";
1237
1238 if (squares > area)
1239 return "Too much data to fit in grid";
1240
1241 return NULL;
1242}
1243
1244static game_state *new_game(midend *me, const game_params *params,
1245 const char *desc)
1246{
1247 int w = params->w, h = params->h, W = w+1, H = h+1;
1248 game_state *state = snew(game_state);
1249 int area = W*H;
1250 int squares = 0;
1251
1252 state->p = *params;
1253 state->soln = snewn(w*h, signed char);
1254 memset(state->soln, 0, w*h);
1255 state->completed = state->used_solve = FALSE;
1256 state->errors = snewn(W*H, unsigned char);
1257 memset(state->errors, 0, W*H);
1258
1259 state->clues = snew(game_clues);
1260 state->clues->w = w;
1261 state->clues->h = h;
1262 state->clues->clues = snewn(W*H, signed char);
1263 state->clues->refcount = 1;
1264 state->clues->tmpdsf = snewn(W*H*2+W+H, int);
1265 memset(state->clues->clues, -1, W*H);
1266 while (*desc) {
1267 int n = *desc++;
1268 if (n >= 'a' && n <= 'z') {
1269 squares += n - 'a' + 1;
1270 } else if (n >= '0' && n <= '4') {
1271 state->clues->clues[squares++] = n - '0';
1272 } else
1273 assert(!"can't get here");
1274 }
1275 assert(squares == area);
1276
1277 return state;
1278}
1279
1280static game_state *dup_game(const game_state *state)
1281{
1282 int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
1283 game_state *ret = snew(game_state);
1284
1285 ret->p = state->p;
1286 ret->clues = state->clues;
1287 ret->clues->refcount++;
1288 ret->completed = state->completed;
1289 ret->used_solve = state->used_solve;
1290
1291 ret->soln = snewn(w*h, signed char);
1292 memcpy(ret->soln, state->soln, w*h);
1293
1294 ret->errors = snewn(W*H, unsigned char);
1295 memcpy(ret->errors, state->errors, W*H);
1296
1297 return ret;
1298}
1299
1300static void free_game(game_state *state)
1301{
1302 sfree(state->errors);
1303 sfree(state->soln);
1304 assert(state->clues);
1305 if (--state->clues->refcount <= 0) {
1306 sfree(state->clues->clues);
1307 sfree(state->clues->tmpdsf);
1308 sfree(state->clues);
1309 }
1310 sfree(state);
1311}
1312
1313/*
1314 * Utility function to return the current degree of a vertex. If
1315 * `anti' is set, it returns the number of filled-in edges
1316 * surrounding the point which _don't_ connect to it; thus 4 minus
1317 * its anti-degree is the maximum degree it could have if all the
1318 * empty spaces around it were filled in.
1319 *
1320 * (Yes, _4_ minus its anti-degree even if it's a border vertex.)
1321 *
1322 * If ret > 0, *sx and *sy are set to the coordinates of one of the
1323 * squares that contributed to it.
1324 */
1325static int vertex_degree(int w, int h, signed char *soln, int x, int y,
1326 int anti, int *sx, int *sy)
1327{
1328 int ret = 0;
1329
1330 assert(x >= 0 && x <= w && y >= 0 && y <= h);
1331 if (x > 0 && y > 0 && soln[(y-1)*w+(x-1)] - anti < 0) {
1332 if (sx) *sx = x-1;
1333 if (sy) *sy = y-1;
1334 ret++;
1335 }
1336 if (x > 0 && y < h && soln[y*w+(x-1)] + anti > 0) {
1337 if (sx) *sx = x-1;
1338 if (sy) *sy = y;
1339 ret++;
1340 }
1341 if (x < w && y > 0 && soln[(y-1)*w+x] + anti > 0) {
1342 if (sx) *sx = x;
1343 if (sy) *sy = y-1;
1344 ret++;
1345 }
1346 if (x < w && y < h && soln[y*w+x] - anti < 0) {
1347 if (sx) *sx = x;
1348 if (sy) *sy = y;
1349 ret++;
1350 }
1351
1352 return anti ? 4 - ret : ret;
1353}
1354
1355struct slant_neighbour_ctx {
1356 const game_state *state;
1357 int i, n, neighbours[4];
1358};
1359static int slant_neighbour(int vertex, void *vctx)
1360{
1361 struct slant_neighbour_ctx *ctx = (struct slant_neighbour_ctx *)vctx;
1362
1363 if (vertex >= 0) {
1364 int w = ctx->state->p.w, h = ctx->state->p.h, W = w+1;
1365 int x = vertex % W, y = vertex / W;
1366 ctx->n = ctx->i = 0;
1367 if (x < w && y < h && ctx->state->soln[y*w+x] < 0)
1368 ctx->neighbours[ctx->n++] = (y+1)*W+(x+1);
1369 if (x > 0 && y > 0 && ctx->state->soln[(y-1)*w+(x-1)] < 0)
1370 ctx->neighbours[ctx->n++] = (y-1)*W+(x-1);
1371 if (x > 0 && y < h && ctx->state->soln[y*w+(x-1)] > 0)
1372 ctx->neighbours[ctx->n++] = (y+1)*W+(x-1);
1373 if (x < w && y > 0 && ctx->state->soln[(y-1)*w+x] > 0)
1374 ctx->neighbours[ctx->n++] = (y-1)*W+(x+1);
1375 }
1376
1377 if (ctx->i < ctx->n)
1378 return ctx->neighbours[ctx->i++];
1379 else
1380 return -1;
1381}
1382
1383static int check_completion(game_state *state)
1384{
1385 int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
1386 int x, y, err = FALSE;
1387
1388 memset(state->errors, 0, W*H);
1389
1390 /*
1391 * Detect and error-highlight loops in the grid.
1392 */
1393 {
1394 struct findloopstate *fls = findloop_new_state(W*H);
1395 struct slant_neighbour_ctx ctx;
1396 ctx.state = state;
1397
1398 if (findloop_run(fls, W*H, slant_neighbour, &ctx))
1399 err = TRUE;
1400 for (y = 0; y < h; y++) {
1401 for (x = 0; x < w; x++) {
1402 int u, v;
1403 if (state->soln[y*w+x] == 0) {
1404 continue;
1405 } else if (state->soln[y*w+x] > 0) {
1406 u = y*W+(x+1);
1407 v = (y+1)*W+x;
1408 } else {
1409 u = (y+1)*W+(x+1);
1410 v = y*W+x;
1411 }
1412 if (findloop_is_loop_edge(fls, u, v))
1413 state->errors[y*W+x] |= ERR_SQUARE;
1414 }
1415 }
1416
1417 findloop_free_state(fls);
1418 }
1419
1420 /*
1421 * Now go through and check the degree of each clue vertex, and
1422 * mark it with ERR_VERTEX if it cannot be fulfilled.
1423 */
1424 for (y = 0; y < H; y++)
1425 for (x = 0; x < W; x++) {
1426 int c;
1427
1428 if ((c = state->clues->clues[y*W+x]) < 0)
1429 continue;
1430
1431 /*
1432 * Check to see if there are too many connections to
1433 * this vertex _or_ too many non-connections. Either is
1434 * grounds for marking the vertex as erroneous.
1435 */
1436 if (vertex_degree(w, h, state->soln, x, y,
1437 FALSE, NULL, NULL) > c ||
1438 vertex_degree(w, h, state->soln, x, y,
1439 TRUE, NULL, NULL) > 4-c) {
1440 state->errors[y*W+x] |= ERR_VERTEX;
1441 err = TRUE;
1442 }
1443 }
1444
1445 /*
1446 * Now our actual victory condition is that (a) none of the
1447 * above code marked anything as erroneous, and (b) every
1448 * square has an edge in it.
1449 */
1450
1451 if (err)
1452 return FALSE;
1453
1454 for (y = 0; y < h; y++)
1455 for (x = 0; x < w; x++)
1456 if (state->soln[y*w+x] == 0)
1457 return FALSE;
1458
1459 return TRUE;
1460}
1461
1462static char *solve_game(const game_state *state, const game_state *currstate,
1463 const char *aux, char **error)
1464{
1465 int w = state->p.w, h = state->p.h;
1466 signed char *soln;
1467 int bs, ret;
1468 int free_soln = FALSE;
1469 char *move, buf[80];
1470 int movelen, movesize;
1471 int x, y;
1472
1473 if (aux) {
1474 /*
1475 * If we already have the solution, save ourselves some
1476 * time.
1477 */
1478 soln = (signed char *)aux;
1479 bs = (signed char)'\\';
1480 free_soln = FALSE;
1481 } else {
1482 struct solver_scratch *sc = new_scratch(w, h);
1483 soln = snewn(w*h, signed char);
1484 bs = -1;
1485 ret = slant_solve(w, h, state->clues->clues, soln, sc, DIFF_HARD);
1486 free_scratch(sc);
1487 if (ret != 1) {
1488 sfree(soln);
1489 if (ret == 0)
1490 *error = "This puzzle is not self-consistent";
1491 else
1492 *error = "Unable to find a unique solution for this puzzle";
1493 return NULL;
1494 }
1495 free_soln = TRUE;
1496 }
1497
1498 /*
1499 * Construct a move string which turns the current state into
1500 * the solved state.
1501 */
1502 movesize = 256;
1503 move = snewn(movesize, char);
1504 movelen = 0;
1505 move[movelen++] = 'S';
1506 move[movelen] = '\0';
1507 for (y = 0; y < h; y++)
1508 for (x = 0; x < w; x++) {
1509 int v = (soln[y*w+x] == bs ? -1 : +1);
1510 if (state->soln[y*w+x] != v) {
1511 int len = sprintf(buf, ";%c%d,%d", (int)(v < 0 ? '\\' : '/'), x, y);
1512 if (movelen + len >= movesize) {
1513 movesize = movelen + len + 256;
1514 move = sresize(move, movesize, char);
1515 }
1516 strcpy(move + movelen, buf);
1517 movelen += len;
1518 }
1519 }
1520
1521 if (free_soln)
1522 sfree(soln);
1523
1524 return move;
1525}
1526
1527static int game_can_format_as_text_now(const game_params *params)
1528{
1529 return TRUE;
1530}
1531
1532static char *game_text_format(const game_state *state)
1533{
1534 int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
1535 int x, y, len;
1536 char *ret, *p;
1537
1538 /*
1539 * There are h+H rows of w+W columns.
1540 */
1541 len = (h+H) * (w+W+1) + 1;
1542 ret = snewn(len, char);
1543 p = ret;
1544
1545 for (y = 0; y < H; y++) {
1546 for (x = 0; x < W; x++) {
1547 if (state->clues->clues[y*W+x] >= 0)
1548 *p++ = state->clues->clues[y*W+x] + '0';
1549 else
1550 *p++ = '+';
1551 if (x < w)
1552 *p++ = '-';
1553 }
1554 *p++ = '\n';
1555 if (y < h) {
1556 for (x = 0; x < W; x++) {
1557 *p++ = '|';
1558 if (x < w) {
1559 if (state->soln[y*w+x] != 0)
1560 *p++ = (state->soln[y*w+x] < 0 ? '\\' : '/');
1561 else
1562 *p++ = ' ';
1563 }
1564 }
1565 *p++ = '\n';
1566 }
1567 }
1568 *p++ = '\0';
1569
1570 assert(p - ret == len);
1571 return ret;
1572}
1573
1574struct game_ui {
1575 int cur_x, cur_y, cur_visible;
1576};
1577
1578static game_ui *new_ui(const game_state *state)
1579{
1580 game_ui *ui = snew(game_ui);
1581 ui->cur_x = ui->cur_y = ui->cur_visible = 0;
1582 return ui;
1583}
1584
1585static void free_ui(game_ui *ui)
1586{
1587 sfree(ui);
1588}
1589
1590static char *encode_ui(const game_ui *ui)
1591{
1592 return NULL;
1593}
1594
1595static void decode_ui(game_ui *ui, const char *encoding)
1596{
1597}
1598
1599static void game_changed_state(game_ui *ui, const game_state *oldstate,
1600 const game_state *newstate)
1601{
1602}
1603
1604#define PREFERRED_TILESIZE 32
1605#define TILESIZE (ds->tilesize)
1606#define BORDER TILESIZE
1607#define CLUE_RADIUS (TILESIZE / 3)
1608#define CLUE_TEXTSIZE (TILESIZE / 2)
1609#define COORD(x) ( (x) * TILESIZE + BORDER )
1610#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1611
1612#define FLASH_TIME 0.30F
1613
1614/*
1615 * Bit fields in the `grid' and `todraw' elements of the drawstate.
1616 */
1617#define BACKSLASH 0x00000001L
1618#define FORWSLASH 0x00000002L
1619#define L_T 0x00000004L
1620#define ERR_L_T 0x00000008L
1621#define L_B 0x00000010L
1622#define ERR_L_B 0x00000020L
1623#define T_L 0x00000040L
1624#define ERR_T_L 0x00000080L
1625#define T_R 0x00000100L
1626#define ERR_T_R 0x00000200L
1627#define C_TL 0x00000400L
1628#define ERR_C_TL 0x00000800L
1629#define FLASH 0x00001000L
1630#define ERRSLASH 0x00002000L
1631#define ERR_TL 0x00004000L
1632#define ERR_TR 0x00008000L
1633#define ERR_BL 0x00010000L
1634#define ERR_BR 0x00020000L
1635#define CURSOR 0x00040000L
1636
1637struct game_drawstate {
1638 int tilesize;
1639 int started;
1640 long *grid;
1641 long *todraw;
1642};
1643
1644static char *interpret_move(const game_state *state, game_ui *ui,
1645 const game_drawstate *ds,
1646 int x, int y, int button)
1647{
1648 int w = state->p.w, h = state->p.h;
1649 int v;
1650 char buf[80];
1651 enum { CLOCKWISE, ANTICLOCKWISE, NONE } action = NONE;
1652
1653 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1654 /*
1655 * This is an utterly awful hack which I should really sort out
1656 * by means of a proper configuration mechanism. One Slant
1657 * player has observed that they prefer the mouse buttons to
1658 * function exactly the opposite way round, so here's a
1659 * mechanism for environment-based configuration. I cache the
1660 * result in a global variable - yuck! - to avoid repeated
1661 * lookups.
1662 */
1663 {
1664 static int swap_buttons = -1;
1665 if (swap_buttons < 0) {
1666 char *env = getenv("SLANT_SWAP_BUTTONS");
1667 swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y'));
1668 }
1669 if (swap_buttons) {
1670 if (button == LEFT_BUTTON)
1671 button = RIGHT_BUTTON;
1672 else
1673 button = LEFT_BUTTON;
1674 }
1675 }
1676 action = (button == LEFT_BUTTON) ? CLOCKWISE : ANTICLOCKWISE;
1677
1678 x = FROMCOORD(x);
1679 y = FROMCOORD(y);
1680 if (x < 0 || y < 0 || x >= w || y >= h)
1681 return NULL;
1682 ui->cur_visible = 0;
1683 } else if (IS_CURSOR_SELECT(button)) {
1684 if (!ui->cur_visible) {
1685 ui->cur_visible = 1;
1686 return "";
1687 }
1688 x = ui->cur_x;
1689 y = ui->cur_y;
1690
1691 action = (button == CURSOR_SELECT2) ? ANTICLOCKWISE : CLOCKWISE;
1692 } else if (IS_CURSOR_MOVE(button)) {
1693 move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, 0);
1694 ui->cur_visible = 1;
1695 return "";
1696 } else if (button == '\\' || button == '\b' || button == '/') {
1697 int x = ui->cur_x, y = ui->cur_y;
1698 if (button == ("\\" "\b" "/")[state->soln[y*w + x] + 1]) return NULL;
1699 sprintf(buf, "%c%d,%d", button == '\b' ? 'C' : button, x, y);
1700 return dupstr(buf);
1701 }
1702
1703 if (action != NONE) {
1704 if (action == CLOCKWISE) {
1705 /*
1706 * Left-clicking cycles blank -> \ -> / -> blank.
1707 */
1708 v = state->soln[y*w+x] - 1;
1709 if (v == -2)
1710 v = +1;
1711 } else {
1712 /*
1713 * Right-clicking cycles blank -> / -> \ -> blank.
1714 */
1715 v = state->soln[y*w+x] + 1;
1716 if (v == +2)
1717 v = -1;
1718 }
1719
1720 sprintf(buf, "%c%d,%d", (int)(v==-1 ? '\\' : v==+1 ? '/' : 'C'), x, y);
1721 return dupstr(buf);
1722 }
1723
1724 return NULL;
1725}
1726
1727static game_state *execute_move(const game_state *state, const char *move)
1728{
1729 int w = state->p.w, h = state->p.h;
1730 char c;
1731 int x, y, n;
1732 game_state *ret = dup_game(state);
1733
1734 while (*move) {
1735 c = *move;
1736 if (c == 'S') {
1737 ret->used_solve = TRUE;
1738 move++;
1739 } else if (c == '\\' || c == '/' || c == 'C') {
1740 move++;
1741 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
1742 x < 0 || y < 0 || x >= w || y >= h) {
1743 free_game(ret);
1744 return NULL;
1745 }
1746 ret->soln[y*w+x] = (c == '\\' ? -1 : c == '/' ? +1 : 0);
1747 move += n;
1748 } else {
1749 free_game(ret);
1750 return NULL;
1751 }
1752 if (*move == ';')
1753 move++;
1754 else if (*move) {
1755 free_game(ret);
1756 return NULL;
1757 }
1758 }
1759
1760 /*
1761 * We never clear the `completed' flag, but we must always
1762 * re-run the completion check because it also highlights
1763 * errors in the grid.
1764 */
1765 ret->completed = check_completion(ret) || ret->completed;
1766
1767 return ret;
1768}
1769
1770/* ----------------------------------------------------------------------
1771 * Drawing routines.
1772 */
1773
1774static void game_compute_size(const game_params *params, int tilesize,
1775 int *x, int *y)
1776{
1777 /* fool the macros */
1778 struct dummy { int tilesize; } dummy, *ds = &dummy;
1779 dummy.tilesize = tilesize;
1780
1781 *x = 2 * BORDER + params->w * TILESIZE + 1;
1782 *y = 2 * BORDER + params->h * TILESIZE + 1;
1783}
1784
1785static void game_set_size(drawing *dr, game_drawstate *ds,
1786 const game_params *params, int tilesize)
1787{
1788 ds->tilesize = tilesize;
1789}
1790
1791static float *game_colours(frontend *fe, int *ncolours)
1792{
1793 float *ret = snewn(3 * NCOLOURS, float);
1794
1795 /* CURSOR colour is a background highlight. */
1796 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_CURSOR, -1);
1797
1798 ret[COL_FILLEDSQUARE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1799 ret[COL_FILLEDSQUARE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1];
1800 ret[COL_FILLEDSQUARE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1801
1802 ret[COL_GRID * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.7F;
1803 ret[COL_GRID * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.7F;
1804 ret[COL_GRID * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.7F;
1805
1806 ret[COL_INK * 3 + 0] = 0.0F;
1807 ret[COL_INK * 3 + 1] = 0.0F;
1808 ret[COL_INK * 3 + 2] = 0.0F;
1809
1810 ret[COL_SLANT1 * 3 + 0] = 0.0F;
1811 ret[COL_SLANT1 * 3 + 1] = 0.0F;
1812 ret[COL_SLANT1 * 3 + 2] = 0.0F;
1813
1814 ret[COL_SLANT2 * 3 + 0] = 0.0F;
1815 ret[COL_SLANT2 * 3 + 1] = 0.0F;
1816 ret[COL_SLANT2 * 3 + 2] = 0.0F;
1817
1818 ret[COL_ERROR * 3 + 0] = 1.0F;
1819 ret[COL_ERROR * 3 + 1] = 0.0F;
1820 ret[COL_ERROR * 3 + 2] = 0.0F;
1821
1822 *ncolours = NCOLOURS;
1823 return ret;
1824}
1825
1826static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1827{
1828 int w = state->p.w, h = state->p.h;
1829 int i;
1830 struct game_drawstate *ds = snew(struct game_drawstate);
1831
1832 ds->tilesize = 0;
1833 ds->started = FALSE;
1834 ds->grid = snewn((w+2)*(h+2), long);
1835 ds->todraw = snewn((w+2)*(h+2), long);
1836 for (i = 0; i < (w+2)*(h+2); i++)
1837 ds->grid[i] = ds->todraw[i] = -1;
1838
1839 return ds;
1840}
1841
1842static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1843{
1844 sfree(ds->todraw);
1845 sfree(ds->grid);
1846 sfree(ds);
1847}
1848
1849static void draw_clue(drawing *dr, game_drawstate *ds,
1850 int x, int y, long v, long err, int bg, int colour)
1851{
1852 char p[2];
1853 int ccol = colour >= 0 ? colour : ((x ^ y) & 1) ? COL_SLANT1 : COL_SLANT2;
1854 int tcol = colour >= 0 ? colour : err ? COL_ERROR : COL_INK;
1855
1856 if (v < 0)
1857 return;
1858
1859 p[0] = (char)v + '0';
1860 p[1] = '\0';
1861 draw_circle(dr, COORD(x), COORD(y), CLUE_RADIUS,
1862 bg >= 0 ? bg : COL_BACKGROUND, ccol);
1863 draw_text(dr, COORD(x), COORD(y), FONT_VARIABLE,
1864 CLUE_TEXTSIZE, ALIGN_VCENTRE|ALIGN_HCENTRE, tcol, p);
1865}
1866
1867static void draw_tile(drawing *dr, game_drawstate *ds, game_clues *clues,
1868 int x, int y, long v)
1869{
1870 int w = clues->w, h = clues->h, W = w+1 /*, H = h+1 */;
1871 int chesscolour = (x ^ y) & 1;
1872 int fscol = chesscolour ? COL_SLANT2 : COL_SLANT1;
1873 int bscol = chesscolour ? COL_SLANT1 : COL_SLANT2;
1874
1875 clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
1876
1877 draw_rect(dr, COORD(x), COORD(y), TILESIZE, TILESIZE,
1878 (v & FLASH) ? COL_GRID :
1879 (v & CURSOR) ? COL_CURSOR :
1880 (v & (BACKSLASH | FORWSLASH)) ? COL_FILLEDSQUARE :
1881 COL_BACKGROUND);
1882
1883 /*
1884 * Draw the grid lines.
1885 */
1886 if (x >= 0 && x < w && y >= 0)
1887 draw_rect(dr, COORD(x), COORD(y), TILESIZE+1, 1, COL_GRID);
1888 if (x >= 0 && x < w && y < h)
1889 draw_rect(dr, COORD(x), COORD(y+1), TILESIZE+1, 1, COL_GRID);
1890 if (y >= 0 && y < h && x >= 0)
1891 draw_rect(dr, COORD(x), COORD(y), 1, TILESIZE+1, COL_GRID);
1892 if (y >= 0 && y < h && x < w)
1893 draw_rect(dr, COORD(x+1), COORD(y), 1, TILESIZE+1, COL_GRID);
1894 if (x == -1 && y == -1)
1895 draw_rect(dr, COORD(x+1), COORD(y+1), 1, 1, COL_GRID);
1896 if (x == -1 && y == h)
1897 draw_rect(dr, COORD(x+1), COORD(y), 1, 1, COL_GRID);
1898 if (x == w && y == -1)
1899 draw_rect(dr, COORD(x), COORD(y+1), 1, 1, COL_GRID);
1900 if (x == w && y == h)
1901 draw_rect(dr, COORD(x), COORD(y), 1, 1, COL_GRID);
1902
1903 /*
1904 * Draw the slash.
1905 */
1906 if (v & BACKSLASH) {
1907 int scol = (v & ERRSLASH) ? COL_ERROR : bscol;
1908 draw_line(dr, COORD(x), COORD(y), COORD(x+1), COORD(y+1), scol);
1909 draw_line(dr, COORD(x)+1, COORD(y), COORD(x+1), COORD(y+1)-1,
1910 scol);
1911 draw_line(dr, COORD(x), COORD(y)+1, COORD(x+1)-1, COORD(y+1),
1912 scol);
1913 } else if (v & FORWSLASH) {
1914 int scol = (v & ERRSLASH) ? COL_ERROR : fscol;
1915 draw_line(dr, COORD(x+1), COORD(y), COORD(x), COORD(y+1), scol);
1916 draw_line(dr, COORD(x+1)-1, COORD(y), COORD(x), COORD(y+1)-1,
1917 scol);
1918 draw_line(dr, COORD(x+1), COORD(y)+1, COORD(x)+1, COORD(y+1),
1919 scol);
1920 }
1921
1922 /*
1923 * Draw dots on the grid corners that appear if a slash is in a
1924 * neighbouring cell.
1925 */
1926 if (v & (L_T | BACKSLASH))
1927 draw_rect(dr, COORD(x), COORD(y)+1, 1, 1,
1928 (v & ERR_L_T ? COL_ERROR : bscol));
1929 if (v & (L_B | FORWSLASH))
1930 draw_rect(dr, COORD(x), COORD(y+1)-1, 1, 1,
1931 (v & ERR_L_B ? COL_ERROR : fscol));
1932 if (v & (T_L | BACKSLASH))
1933 draw_rect(dr, COORD(x)+1, COORD(y), 1, 1,
1934 (v & ERR_T_L ? COL_ERROR : bscol));
1935 if (v & (T_R | FORWSLASH))
1936 draw_rect(dr, COORD(x+1)-1, COORD(y), 1, 1,
1937 (v & ERR_T_R ? COL_ERROR : fscol));
1938 if (v & (C_TL | BACKSLASH))
1939 draw_rect(dr, COORD(x), COORD(y), 1, 1,
1940 (v & ERR_C_TL ? COL_ERROR : bscol));
1941
1942 /*
1943 * And finally the clues at the corners.
1944 */
1945 if (x >= 0 && y >= 0)
1946 draw_clue(dr, ds, x, y, clues->clues[y*W+x], v & ERR_TL, -1, -1);
1947 if (x < w && y >= 0)
1948 draw_clue(dr, ds, x+1, y, clues->clues[y*W+(x+1)], v & ERR_TR, -1, -1);
1949 if (x >= 0 && y < h)
1950 draw_clue(dr, ds, x, y+1, clues->clues[(y+1)*W+x], v & ERR_BL, -1, -1);
1951 if (x < w && y < h)
1952 draw_clue(dr, ds, x+1, y+1, clues->clues[(y+1)*W+(x+1)], v & ERR_BR,
1953 -1, -1);
1954
1955 unclip(dr);
1956 draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
1957}
1958
1959static void game_redraw(drawing *dr, game_drawstate *ds,
1960 const game_state *oldstate, const game_state *state,
1961 int dir, const game_ui *ui,
1962 float animtime, float flashtime)
1963{
1964 int w = state->p.w, h = state->p.h, W = w+1, H = h+1;
1965 int x, y;
1966 int flashing;
1967
1968 if (flashtime > 0)
1969 flashing = (int)(flashtime * 3 / FLASH_TIME) != 1;
1970 else
1971 flashing = FALSE;
1972
1973 if (!ds->started) {
1974 int ww, wh;
1975 game_compute_size(&state->p, TILESIZE, &ww, &wh);
1976 draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
1977 draw_update(dr, 0, 0, ww, wh);
1978 ds->started = TRUE;
1979 }
1980
1981 /*
1982 * Loop over the grid and work out where all the slashes are.
1983 * We need to do this because a slash in one square affects the
1984 * drawing of the next one along.
1985 */
1986 for (y = -1; y <= h; y++)
1987 for (x = -1; x <= w; x++) {
1988 if (x >= 0 && x < w && y >= 0 && y < h)
1989 ds->todraw[(y+1)*(w+2)+(x+1)] = flashing ? FLASH : 0;
1990 else
1991 ds->todraw[(y+1)*(w+2)+(x+1)] = 0;
1992 }
1993
1994 for (y = 0; y < h; y++) {
1995 for (x = 0; x < w; x++) {
1996 int err = state->errors[y*W+x] & ERR_SQUARE;
1997
1998 if (state->soln[y*w+x] < 0) {
1999 ds->todraw[(y+1)*(w+2)+(x+1)] |= BACKSLASH;
2000 ds->todraw[(y+2)*(w+2)+(x+1)] |= T_R;
2001 ds->todraw[(y+1)*(w+2)+(x+2)] |= L_B;
2002 ds->todraw[(y+2)*(w+2)+(x+2)] |= C_TL;
2003 if (err) {
2004 ds->todraw[(y+1)*(w+2)+(x+1)] |= ERRSLASH |
2005 ERR_T_L | ERR_L_T | ERR_C_TL;
2006 ds->todraw[(y+2)*(w+2)+(x+1)] |= ERR_T_R;
2007 ds->todraw[(y+1)*(w+2)+(x+2)] |= ERR_L_B;
2008 ds->todraw[(y+2)*(w+2)+(x+2)] |= ERR_C_TL;
2009 }
2010 } else if (state->soln[y*w+x] > 0) {
2011 ds->todraw[(y+1)*(w+2)+(x+1)] |= FORWSLASH;
2012 ds->todraw[(y+1)*(w+2)+(x+2)] |= L_T | C_TL;
2013 ds->todraw[(y+2)*(w+2)+(x+1)] |= T_L | C_TL;
2014 if (err) {
2015 ds->todraw[(y+1)*(w+2)+(x+1)] |= ERRSLASH |
2016 ERR_L_B | ERR_T_R;
2017 ds->todraw[(y+1)*(w+2)+(x+2)] |= ERR_L_T | ERR_C_TL;
2018 ds->todraw[(y+2)*(w+2)+(x+1)] |= ERR_T_L | ERR_C_TL;
2019 }
2020 }
2021 if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y)
2022 ds->todraw[(y+1)*(w+2)+(x+1)] |= CURSOR;
2023 }
2024 }
2025
2026 for (y = 0; y < H; y++)
2027 for (x = 0; x < W; x++)
2028 if (state->errors[y*W+x] & ERR_VERTEX) {
2029 ds->todraw[y*(w+2)+x] |= ERR_BR;
2030 ds->todraw[y*(w+2)+(x+1)] |= ERR_BL;
2031 ds->todraw[(y+1)*(w+2)+x] |= ERR_TR;
2032 ds->todraw[(y+1)*(w+2)+(x+1)] |= ERR_TL;
2033 }
2034
2035 /*
2036 * Now go through and draw the grid squares.
2037 */
2038 for (y = -1; y <= h; y++) {
2039 for (x = -1; x <= w; x++) {
2040 if (ds->todraw[(y+1)*(w+2)+(x+1)] != ds->grid[(y+1)*(w+2)+(x+1)]) {
2041 draw_tile(dr, ds, state->clues, x, y,
2042 ds->todraw[(y+1)*(w+2)+(x+1)]);
2043 ds->grid[(y+1)*(w+2)+(x+1)] = ds->todraw[(y+1)*(w+2)+(x+1)];
2044 }
2045 }
2046 }
2047}
2048
2049static float game_anim_length(const game_state *oldstate,
2050 const game_state *newstate, int dir, game_ui *ui)
2051{
2052 return 0.0F;
2053}
2054
2055static float game_flash_length(const game_state *oldstate,
2056 const game_state *newstate, int dir, game_ui *ui)
2057{
2058 if (!oldstate->completed && newstate->completed &&
2059 !oldstate->used_solve && !newstate->used_solve)
2060 return FLASH_TIME;
2061
2062 return 0.0F;
2063}
2064
2065static int game_status(const game_state *state)
2066{
2067 return state->completed ? +1 : 0;
2068}
2069
2070static int game_timing_state(const game_state *state, game_ui *ui)
2071{
2072 return TRUE;
2073}
2074
2075static void game_print_size(const game_params *params, float *x, float *y)
2076{
2077 int pw, ph;
2078
2079 /*
2080 * I'll use 6mm squares by default.
2081 */
2082 game_compute_size(params, 600, &pw, &ph);
2083 *x = pw / 100.0F;
2084 *y = ph / 100.0F;
2085}
2086
2087static void game_print(drawing *dr, const game_state *state, int tilesize)
2088{
2089 int w = state->p.w, h = state->p.h, W = w+1;
2090 int ink = print_mono_colour(dr, 0);
2091 int paper = print_mono_colour(dr, 1);
2092 int x, y;
2093
2094 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2095 game_drawstate ads, *ds = &ads;
2096 game_set_size(dr, ds, NULL, tilesize);
2097
2098 /*
2099 * Border.
2100 */
2101 print_line_width(dr, TILESIZE / 16);
2102 draw_rect_outline(dr, COORD(0), COORD(0), w*TILESIZE, h*TILESIZE, ink);
2103
2104 /*
2105 * Grid.
2106 */
2107 print_line_width(dr, TILESIZE / 24);
2108 for (x = 1; x < w; x++)
2109 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink);
2110 for (y = 1; y < h; y++)
2111 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink);
2112
2113 /*
2114 * Solution.
2115 */
2116 print_line_width(dr, TILESIZE / 12);
2117 for (y = 0; y < h; y++)
2118 for (x = 0; x < w; x++)
2119 if (state->soln[y*w+x]) {
2120 int ly, ry;
2121 /*
2122 * To prevent nasty line-ending artefacts at
2123 * corners, I'll do something slightly cunning
2124 * here.
2125 */
2126 clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
2127 if (state->soln[y*w+x] < 0)
2128 ly = y-1, ry = y+2;
2129 else
2130 ry = y-1, ly = y+2;
2131 draw_line(dr, COORD(x-1), COORD(ly), COORD(x+2), COORD(ry),
2132 ink);
2133 unclip(dr);
2134 }
2135
2136 /*
2137 * Clues.
2138 */
2139 print_line_width(dr, TILESIZE / 24);
2140 for (y = 0; y <= h; y++)
2141 for (x = 0; x <= w; x++)
2142 draw_clue(dr, ds, x, y, state->clues->clues[y*W+x],
2143 FALSE, paper, ink);
2144}
2145
2146#ifdef COMBINED
2147#define thegame slant
2148#endif
2149
2150const struct game thegame = {
2151 "Slant", "games.slant", "slant",
2152 default_params,
2153 game_fetch_preset, NULL,
2154 decode_params,
2155 encode_params,
2156 free_params,
2157 dup_params,
2158 TRUE, game_configure, custom_params,
2159 validate_params,
2160 new_game_desc,
2161 validate_desc,
2162 new_game,
2163 dup_game,
2164 free_game,
2165 TRUE, solve_game,
2166 TRUE, game_can_format_as_text_now, game_text_format,
2167 new_ui,
2168 free_ui,
2169 encode_ui,
2170 decode_ui,
2171 game_changed_state,
2172 interpret_move,
2173 execute_move,
2174 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2175 game_colours,
2176 game_new_drawstate,
2177 game_free_drawstate,
2178 game_redraw,
2179 game_anim_length,
2180 game_flash_length,
2181 game_status,
2182 TRUE, FALSE, game_print_size, game_print,
2183 FALSE, /* wants_statusbar */
2184 FALSE, game_timing_state,
2185 0, /* flags */
2186};
2187
2188#ifdef STANDALONE_SOLVER
2189
2190#include <stdarg.h>
2191
2192int main(int argc, char **argv)
2193{
2194 game_params *p;
2195 game_state *s;
2196 char *id = NULL, *desc, *err;
2197 int grade = FALSE;
2198 int ret, diff, really_verbose = FALSE;
2199 struct solver_scratch *sc;
2200
2201 while (--argc > 0) {
2202 char *p = *++argv;
2203 if (!strcmp(p, "-v")) {
2204 really_verbose = TRUE;
2205 } else if (!strcmp(p, "-g")) {
2206 grade = TRUE;
2207 } else if (*p == '-') {
2208 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2209 return 1;
2210 } else {
2211 id = p;
2212 }
2213 }
2214
2215 if (!id) {
2216 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2217 return 1;
2218 }
2219
2220 desc = strchr(id, ':');
2221 if (!desc) {
2222 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2223 return 1;
2224 }
2225 *desc++ = '\0';
2226
2227 p = default_params();
2228 decode_params(p, id);
2229 err = validate_desc(p, desc);
2230 if (err) {
2231 fprintf(stderr, "%s: %s\n", argv[0], err);
2232 return 1;
2233 }
2234 s = new_game(NULL, p, desc);
2235
2236 sc = new_scratch(p->w, p->h);
2237
2238 /*
2239 * When solving an Easy puzzle, we don't want to bother the
2240 * user with Hard-level deductions. For this reason, we grade
2241 * the puzzle internally before doing anything else.
2242 */
2243 ret = -1; /* placate optimiser */
2244 for (diff = 0; diff < DIFFCOUNT; diff++) {
2245 ret = slant_solve(p->w, p->h, s->clues->clues,
2246 s->soln, sc, diff);
2247 if (ret < 2)
2248 break;
2249 }
2250
2251 if (diff == DIFFCOUNT) {
2252 if (grade)
2253 printf("Difficulty rating: harder than Hard, or ambiguous\n");
2254 else
2255 printf("Unable to find a unique solution\n");
2256 } else {
2257 if (grade) {
2258 if (ret == 0)
2259 printf("Difficulty rating: impossible (no solution exists)\n");
2260 else if (ret == 1)
2261 printf("Difficulty rating: %s\n", slant_diffnames[diff]);
2262 } else {
2263 verbose = really_verbose;
2264 ret = slant_solve(p->w, p->h, s->clues->clues,
2265 s->soln, sc, diff);
2266 if (ret == 0)
2267 printf("Puzzle is inconsistent\n");
2268 else
2269 fputs(game_text_format(s), stdout);
2270 }
2271 }
2272
2273 return 0;
2274}
2275
2276#endif
2277
2278/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/slant.html b/apps/plugins/puzzles/src/slant.html
new file mode 100644
index 0000000000..79ba076d66
--- /dev/null
+++ b/apps/plugins/puzzles/src/slant.html
@@ -0,0 +1,64 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Slant</title>
7<link rel="previous" href="blackbox.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="lightup.html">
12</head>
13<body>
14<p><a href="blackbox.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="lightup.html">Next</a></p>
15<h1><a name="C20"></a>Chapter 20: <a name="i0"></a>Slant</h1>
16<p>
17You have a grid of squares. Your aim is to draw a diagonal line through each square, and choose which way each line slants so that the following conditions are met:
18</p>
19<ul><li>
20The diagonal lines never form a loop.
21</li>
22<li>
23Any point with a circled number has precisely that many lines meeting at it. (Thus, a 4 is the centre of a cross shape, whereas a zero is the centre of a diamond shape &#8211; or rather, a partial diamond shape, because a zero can never appear in the middle of the grid because that would immediately cause a loop.)
24</li>
25</ul>
26<p>
27Credit for this puzzle goes to <a name="i1"></a>Nikoli <a href="#p0">[8]</a>.
28</p>
29<p><a name="p0"></a>
30[8] <a href="http://www.nikoli.co.jp/ja/puzzles/gokigen_naname"><code>http://www.nikoli.co.jp/ja/puzzles/gokigen_naname</code></a> (in Japanese)
31</p>
32<h2><a name="S20.1"></a>20.1 <a name="i2"></a>Slant controls</h2>
33<p>
34Left-clicking in a blank square will place a <code>\</code> in it (a line leaning to the left, i.e. running from the top left of the square to the bottom right). Right-clicking in a blank square will place a <code>/</code> in it (leaning to the right, running from top right to bottom left).
35</p>
36<p>
37Continuing to click either button will cycle between the three possible square contents. Thus, if you left-click repeatedly in a blank square it will change from blank to <code>\</code> to <code>/</code> back to blank, and if you right-click repeatedly the square will change from blank to <code>/</code> to <code>\</code> back to blank. (Therefore, you can play the game entirely with one button if you need to.)
38</p>
39<p>
40You can also use the cursor keys to move around the grid. Pressing the return or space keys will place a <code>\</code> or a <code>/</code>, respectively, and will then cycle them as above. You can also press <code>/</code> or <code>\</code> to place a <code>/</code> or <code>\</code>, respectively, independent of what is already in the cursor square. Backspace removes any line from the cursor square.
41</p>
42<p>
43(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
44</p>
45<h2><a name="S20.2"></a>20.2 <a name="i3"></a>Slant parameters</h2>
46<p>
47These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
48</p>
49<dl><dt>
50<em>Width</em>, <em>Height</em>
51</dt>
52<dd>
53Size of grid in squares.
54</dd>
55<dt>
56<em>Difficulty</em>
57</dt>
58<dd>
59Controls the difficulty of the generated puzzle. At Hard level, you are required to do deductions based on knowledge of <em>relationships</em> between squares rather than always being able to deduce the exact contents of one square at a time. (For example, you might know that two squares slant in the same direction, even if you don't yet know what that direction is, and this might enable you to deduce something about still other squares.) Even at Hard level, guesswork and backtracking should never be necessary.
60</dd>
61</dl>
62
63<hr><address></address></body>
64</html>
diff --git a/apps/plugins/puzzles/src/solo.R b/apps/plugins/puzzles/src/solo.R
new file mode 100644
index 0000000000..081a76147e
--- /dev/null
+++ b/apps/plugins/puzzles/src/solo.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3SOLO_EXTRA = divvy dsf
4
5solo : [X] GTK COMMON solo SOLO_EXTRA solo-icon|no-icon
6
7solo : [G] WINDOWS COMMON solo SOLO_EXTRA solo.res|noicon.res
8
9solosolver : [U] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE
10solosolver : [C] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE
11
12ALL += solo[COMBINED] SOLO_EXTRA
13
14!begin am gtk
15GAMES += solo
16!end
17
18!begin >list.c
19 A(solo) \
20!end
21
22!begin >gamedesc.txt
23solo:solo.exe:Solo:Number placement puzzle:Fill in the grid so that each row, column and square block contains one of every digit.
24!end
diff --git a/apps/plugins/puzzles/src/solo.c b/apps/plugins/puzzles/src/solo.c
new file mode 100644
index 0000000000..0d383c39aa
--- /dev/null
+++ b/apps/plugins/puzzles/src/solo.c
@@ -0,0 +1,5656 @@
1/*
2 * solo.c: the number-placing puzzle most popularly known as `Sudoku'.
3 *
4 * TODO:
5 *
6 * - reports from users are that `Trivial'-mode puzzles are still
7 * rather hard compared to newspapers' easy ones, so some better
8 * low-end difficulty grading would be nice
9 * + it's possible that really easy puzzles always have
10 * _several_ things you can do, so don't make you hunt too
11 * hard for the one deduction you can currently make
12 * + it's also possible that easy puzzles require fewer
13 * cross-eliminations: perhaps there's a higher incidence of
14 * things you can deduce by looking only at (say) rows,
15 * rather than things you have to check both rows and columns
16 * for
17 * + but really, what I need to do is find some really easy
18 * puzzles and _play_ them, to see what's actually easy about
19 * them
20 * + while I'm revamping this area, filling in the _last_
21 * number in a nearly-full row or column should certainly be
22 * permitted even at the lowest difficulty level.
23 * + also Owen noticed that `Basic' grids requiring numeric
24 * elimination are actually very hard, so I wonder if a
25 * difficulty gradation between that and positional-
26 * elimination-only might be in order
27 * + but it's not good to have _too_ many difficulty levels, or
28 * it'll take too long to randomly generate a given level.
29 *
30 * - it might still be nice to do some prioritisation on the
31 * removal of numbers from the grid
32 * + one possibility is to try to minimise the maximum number
33 * of filled squares in any block, which in particular ought
34 * to enforce never leaving a completely filled block in the
35 * puzzle as presented.
36 *
37 * - alternative interface modes
38 * + sudoku.com's Windows program has a palette of possible
39 * entries; you select a palette entry first and then click
40 * on the square you want it to go in, thus enabling
41 * mouse-only play. Useful for PDAs! I don't think it's
42 * actually incompatible with the current highlight-then-type
43 * approach: you _either_ highlight a palette entry and then
44 * click, _or_ you highlight a square and then type. At most
45 * one thing is ever highlighted at a time, so there's no way
46 * to confuse the two.
47 * + then again, I don't actually like sudoku.com's interface;
48 * it's too much like a paint package whereas I prefer to
49 * think of Solo as a text editor.
50 * + another PDA-friendly possibility is a drag interface:
51 * _drag_ numbers from the palette into the grid squares.
52 * Thought experiments suggest I'd prefer that to the
53 * sudoku.com approach, but I haven't actually tried it.
54 */
55
56/*
57 * Solo puzzles need to be square overall (since each row and each
58 * column must contain one of every digit), but they need not be
59 * subdivided the same way internally. I am going to adopt a
60 * convention whereby I _always_ refer to `r' as the number of rows
61 * of _big_ divisions, and `c' as the number of columns of _big_
62 * divisions. Thus, a 2c by 3r puzzle looks something like this:
63 *
64 * 4 5 1 | 2 6 3
65 * 6 3 2 | 5 4 1
66 * ------+------ (Of course, you can't subdivide it the other way
67 * 1 4 5 | 6 3 2 or you'll get clashes; observe that the 4 in the
68 * 3 2 6 | 4 1 5 top left would conflict with the 4 in the second
69 * ------+------ box down on the left-hand side.)
70 * 5 1 4 | 3 2 6
71 * 2 6 3 | 1 5 4
72 *
73 * The need for a strong naming convention should now be clear:
74 * each small box is two rows of digits by three columns, while the
75 * overall puzzle has three rows of small boxes by two columns. So
76 * I will (hopefully) consistently use `r' to denote the number of
77 * rows _of small boxes_ (here 3), which is also the number of
78 * columns of digits in each small box; and `c' vice versa (here
79 * 2).
80 *
81 * I'm also going to choose arbitrarily to list c first wherever
82 * possible: the above is a 2x3 puzzle, not a 3x2 one.
83 */
84
85#include <stdio.h>
86#include <stdlib.h>
87#include <string.h>
88#include <assert.h>
89#include <ctype.h>
90#include <math.h>
91
92#ifdef STANDALONE_SOLVER
93#include <stdarg.h>
94int solver_show_working, solver_recurse_depth;
95#endif
96
97#include "puzzles.h"
98
99/*
100 * To save space, I store digits internally as unsigned char. This
101 * imposes a hard limit of 255 on the order of the puzzle. Since
102 * even a 5x5 takes unacceptably long to generate, I don't see this
103 * as a serious limitation unless something _really_ impressive
104 * happens in computing technology; but here's a typedef anyway for
105 * general good practice.
106 */
107typedef unsigned char digit;
108#define ORDER_MAX 255
109
110#define PREFERRED_TILE_SIZE 48
111#define TILE_SIZE (ds->tilesize)
112#define BORDER (TILE_SIZE / 2)
113#define GRIDEXTRA max((TILE_SIZE / 32),1)
114
115#define FLASH_TIME 0.4F
116
117enum { SYMM_NONE, SYMM_ROT2, SYMM_ROT4, SYMM_REF2, SYMM_REF2D, SYMM_REF4,
118 SYMM_REF4D, SYMM_REF8 };
119
120enum { DIFF_BLOCK,
121 DIFF_SIMPLE, DIFF_INTERSECT, DIFF_SET, DIFF_EXTREME, DIFF_RECURSIVE,
122 DIFF_AMBIGUOUS, DIFF_IMPOSSIBLE };
123
124enum { DIFF_KSINGLE, DIFF_KMINMAX, DIFF_KSUMS, DIFF_KINTERSECT };
125
126enum {
127 COL_BACKGROUND,
128 COL_XDIAGONALS,
129 COL_GRID,
130 COL_CLUE,
131 COL_USER,
132 COL_HIGHLIGHT,
133 COL_ERROR,
134 COL_PENCIL,
135 COL_KILLER,
136 NCOLOURS
137};
138
139/*
140 * To determine all possible ways to reach a given sum by adding two or
141 * three numbers from 1..9, each of which occurs exactly once in the sum,
142 * these arrays contain a list of bitmasks for each sum value, where if
143 * bit N is set, it means that N occurs in the sum. Each list is
144 * terminated by a zero if it is shorter than the size of the array.
145 */
146#define MAX_2SUMS 5
147#define MAX_3SUMS 8
148#define MAX_4SUMS 12
149unsigned long sum_bits2[18][MAX_2SUMS];
150unsigned long sum_bits3[25][MAX_3SUMS];
151unsigned long sum_bits4[31][MAX_4SUMS];
152
153static int find_sum_bits(unsigned long *array, int idx, int value_left,
154 int addends_left, int min_addend,
155 unsigned long bitmask_so_far)
156{
157 int i;
158 assert(addends_left >= 2);
159
160 for (i = min_addend; i < value_left; i++) {
161 unsigned long new_bitmask = bitmask_so_far | (1L << i);
162 assert(bitmask_so_far != new_bitmask);
163
164 if (addends_left == 2) {
165 int j = value_left - i;
166 if (j <= i)
167 break;
168 if (j > 9)
169 continue;
170 array[idx++] = new_bitmask | (1L << j);
171 } else
172 idx = find_sum_bits(array, idx, value_left - i,
173 addends_left - 1, i + 1,
174 new_bitmask);
175 }
176 return idx;
177}
178
179static void precompute_sum_bits(void)
180{
181 int i;
182 for (i = 3; i < 31; i++) {
183 int j;
184 if (i < 18) {
185 j = find_sum_bits(sum_bits2[i], 0, i, 2, 1, 0);
186 assert (j <= MAX_2SUMS);
187 if (j < MAX_2SUMS)
188 sum_bits2[i][j] = 0;
189 }
190 if (i < 25) {
191 j = find_sum_bits(sum_bits3[i], 0, i, 3, 1, 0);
192 assert (j <= MAX_3SUMS);
193 if (j < MAX_3SUMS)
194 sum_bits3[i][j] = 0;
195 }
196 j = find_sum_bits(sum_bits4[i], 0, i, 4, 1, 0);
197 assert (j <= MAX_4SUMS);
198 if (j < MAX_4SUMS)
199 sum_bits4[i][j] = 0;
200 }
201}
202
203struct game_params {
204 /*
205 * For a square puzzle, `c' and `r' indicate the puzzle
206 * parameters as described above.
207 *
208 * A jigsaw-style puzzle is indicated by r==1, in which case c
209 * can be whatever it likes (there is no constraint on
210 * compositeness - a 7x7 jigsaw sudoku makes perfect sense).
211 */
212 int c, r, symm, diff, kdiff;
213 int xtype; /* require all digits in X-diagonals */
214 int killer;
215};
216
217struct block_structure {
218 int refcount;
219
220 /*
221 * For text formatting, we do need c and r here.
222 */
223 int c, r, area;
224
225 /*
226 * For any square index, whichblock[i] gives its block index.
227 *
228 * For 0 <= b,i < cr, blocks[b][i] gives the index of the ith
229 * square in block b. nr_squares[b] gives the number of squares
230 * in block b (also the number of valid elements in blocks[b]).
231 *
232 * blocks_data holds the data pointed to by blocks.
233 *
234 * nr_squares may be NULL for block structures where all blocks are
235 * the same size.
236 */
237 int *whichblock, **blocks, *nr_squares, *blocks_data;
238 int nr_blocks, max_nr_squares;
239
240#ifdef STANDALONE_SOLVER
241 /*
242 * Textual descriptions of each block. For normal Sudoku these
243 * are of the form "(1,3)"; for jigsaw they are "starting at
244 * (5,7)". So the sensible usage in both cases is to say
245 * "elimination within block %s" with one of these strings.
246 *
247 * Only blocknames itself needs individually freeing; it's all
248 * one block.
249 */
250 char **blocknames;
251#endif
252};
253
254struct game_state {
255 /*
256 * For historical reasons, I use `cr' to denote the overall
257 * width/height of the puzzle. It was a natural notation when
258 * all puzzles were divided into blocks in a grid, but doesn't
259 * really make much sense given jigsaw puzzles. However, the
260 * obvious `n' is heavily used in the solver to describe the
261 * index of a number being placed, so `cr' will have to stay.
262 */
263 int cr;
264 struct block_structure *blocks;
265 struct block_structure *kblocks; /* Blocks for killer puzzles. */
266 int xtype, killer;
267 digit *grid, *kgrid;
268 unsigned char *pencil; /* c*r*c*r elements */
269 unsigned char *immutable; /* marks which digits are clues */
270 int completed, cheated;
271};
272
273static game_params *default_params(void)
274{
275 game_params *ret = snew(game_params);
276
277 ret->c = ret->r = 3;
278 ret->xtype = FALSE;
279 ret->killer = FALSE;
280 ret->symm = SYMM_ROT2; /* a plausible default */
281 ret->diff = DIFF_BLOCK; /* so is this */
282 ret->kdiff = DIFF_KINTERSECT; /* so is this */
283
284 return ret;
285}
286
287static void free_params(game_params *params)
288{
289 sfree(params);
290}
291
292static game_params *dup_params(const game_params *params)
293{
294 game_params *ret = snew(game_params);
295 *ret = *params; /* structure copy */
296 return ret;
297}
298
299static int game_fetch_preset(int i, char **name, game_params **params)
300{
301 static struct {
302 char *title;
303 game_params params;
304 } presets[] = {
305 { "2x2 Trivial", { 2, 2, SYMM_ROT2, DIFF_BLOCK, DIFF_KMINMAX, FALSE, FALSE } },
306 { "2x3 Basic", { 2, 3, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } },
307 { "3x3 Trivial", { 3, 3, SYMM_ROT2, DIFF_BLOCK, DIFF_KMINMAX, FALSE, FALSE } },
308 { "3x3 Basic", { 3, 3, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } },
309 { "3x3 Basic X", { 3, 3, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, TRUE } },
310 { "3x3 Intermediate", { 3, 3, SYMM_ROT2, DIFF_INTERSECT, DIFF_KMINMAX, FALSE, FALSE } },
311 { "3x3 Advanced", { 3, 3, SYMM_ROT2, DIFF_SET, DIFF_KMINMAX, FALSE, FALSE } },
312 { "3x3 Advanced X", { 3, 3, SYMM_ROT2, DIFF_SET, DIFF_KMINMAX, TRUE } },
313 { "3x3 Extreme", { 3, 3, SYMM_ROT2, DIFF_EXTREME, DIFF_KMINMAX, FALSE, FALSE } },
314 { "3x3 Unreasonable", { 3, 3, SYMM_ROT2, DIFF_RECURSIVE, DIFF_KMINMAX, FALSE, FALSE } },
315 { "3x3 Killer", { 3, 3, SYMM_NONE, DIFF_BLOCK, DIFF_KINTERSECT, FALSE, TRUE } },
316 { "9 Jigsaw Basic", { 9, 1, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } },
317 { "9 Jigsaw Basic X", { 9, 1, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, TRUE } },
318 { "9 Jigsaw Advanced", { 9, 1, SYMM_ROT2, DIFF_SET, DIFF_KMINMAX, FALSE, FALSE } },
319#ifndef SLOW_SYSTEM
320 { "3x4 Basic", { 3, 4, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } },
321 { "4x4 Basic", { 4, 4, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } },
322#endif
323 };
324
325 if (i < 0 || i >= lenof(presets))
326 return FALSE;
327
328 *name = dupstr(presets[i].title);
329 *params = dup_params(&presets[i].params);
330
331 return TRUE;
332}
333
334static void decode_params(game_params *ret, char const *string)
335{
336 int seen_r = FALSE;
337
338 ret->c = ret->r = atoi(string);
339 ret->xtype = FALSE;
340 ret->killer = FALSE;
341 while (*string && isdigit((unsigned char)*string)) string++;
342 if (*string == 'x') {
343 string++;
344 ret->r = atoi(string);
345 seen_r = TRUE;
346 while (*string && isdigit((unsigned char)*string)) string++;
347 }
348 while (*string) {
349 if (*string == 'j') {
350 string++;
351 if (seen_r)
352 ret->c *= ret->r;
353 ret->r = 1;
354 } else if (*string == 'x') {
355 string++;
356 ret->xtype = TRUE;
357 } else if (*string == 'k') {
358 string++;
359 ret->killer = TRUE;
360 } else if (*string == 'r' || *string == 'm' || *string == 'a') {
361 int sn, sc, sd;
362 sc = *string++;
363 if (sc == 'm' && *string == 'd') {
364 sd = TRUE;
365 string++;
366 } else {
367 sd = FALSE;
368 }
369 sn = atoi(string);
370 while (*string && isdigit((unsigned char)*string)) string++;
371 if (sc == 'm' && sn == 8)
372 ret->symm = SYMM_REF8;
373 if (sc == 'm' && sn == 4)
374 ret->symm = sd ? SYMM_REF4D : SYMM_REF4;
375 if (sc == 'm' && sn == 2)
376 ret->symm = sd ? SYMM_REF2D : SYMM_REF2;
377 if (sc == 'r' && sn == 4)
378 ret->symm = SYMM_ROT4;
379 if (sc == 'r' && sn == 2)
380 ret->symm = SYMM_ROT2;
381 if (sc == 'a')
382 ret->symm = SYMM_NONE;
383 } else if (*string == 'd') {
384 string++;
385 if (*string == 't') /* trivial */
386 string++, ret->diff = DIFF_BLOCK;
387 else if (*string == 'b') /* basic */
388 string++, ret->diff = DIFF_SIMPLE;
389 else if (*string == 'i') /* intermediate */
390 string++, ret->diff = DIFF_INTERSECT;
391 else if (*string == 'a') /* advanced */
392 string++, ret->diff = DIFF_SET;
393 else if (*string == 'e') /* extreme */
394 string++, ret->diff = DIFF_EXTREME;
395 else if (*string == 'u') /* unreasonable */
396 string++, ret->diff = DIFF_RECURSIVE;
397 } else
398 string++; /* eat unknown character */
399 }
400}
401
402static char *encode_params(const game_params *params, int full)
403{
404 char str[80];
405
406 if (params->r > 1)
407 sprintf(str, "%dx%d", params->c, params->r);
408 else
409 sprintf(str, "%dj", params->c);
410 if (params->xtype)
411 strcat(str, "x");
412 if (params->killer)
413 strcat(str, "k");
414
415 if (full) {
416 switch (params->symm) {
417 case SYMM_REF8: strcat(str, "m8"); break;
418 case SYMM_REF4: strcat(str, "m4"); break;
419 case SYMM_REF4D: strcat(str, "md4"); break;
420 case SYMM_REF2: strcat(str, "m2"); break;
421 case SYMM_REF2D: strcat(str, "md2"); break;
422 case SYMM_ROT4: strcat(str, "r4"); break;
423 /* case SYMM_ROT2: strcat(str, "r2"); break; [default] */
424 case SYMM_NONE: strcat(str, "a"); break;
425 }
426 switch (params->diff) {
427 /* case DIFF_BLOCK: strcat(str, "dt"); break; [default] */
428 case DIFF_SIMPLE: strcat(str, "db"); break;
429 case DIFF_INTERSECT: strcat(str, "di"); break;
430 case DIFF_SET: strcat(str, "da"); break;
431 case DIFF_EXTREME: strcat(str, "de"); break;
432 case DIFF_RECURSIVE: strcat(str, "du"); break;
433 }
434 }
435 return dupstr(str);
436}
437
438static config_item *game_configure(const game_params *params)
439{
440 config_item *ret;
441 char buf[80];
442
443 ret = snewn(8, config_item);
444
445 ret[0].name = "Columns of sub-blocks";
446 ret[0].type = C_STRING;
447 sprintf(buf, "%d", params->c);
448 ret[0].sval = dupstr(buf);
449 ret[0].ival = 0;
450
451 ret[1].name = "Rows of sub-blocks";
452 ret[1].type = C_STRING;
453 sprintf(buf, "%d", params->r);
454 ret[1].sval = dupstr(buf);
455 ret[1].ival = 0;
456
457 ret[2].name = "\"X\" (require every number in each main diagonal)";
458 ret[2].type = C_BOOLEAN;
459 ret[2].sval = NULL;
460 ret[2].ival = params->xtype;
461
462 ret[3].name = "Jigsaw (irregularly shaped sub-blocks)";
463 ret[3].type = C_BOOLEAN;
464 ret[3].sval = NULL;
465 ret[3].ival = (params->r == 1);
466
467 ret[4].name = "Killer (digit sums)";
468 ret[4].type = C_BOOLEAN;
469 ret[4].sval = NULL;
470 ret[4].ival = params->killer;
471
472 ret[5].name = "Symmetry";
473 ret[5].type = C_CHOICES;
474 ret[5].sval = ":None:2-way rotation:4-way rotation:2-way mirror:"
475 "2-way diagonal mirror:4-way mirror:4-way diagonal mirror:"
476 "8-way mirror";
477 ret[5].ival = params->symm;
478
479 ret[6].name = "Difficulty";
480 ret[6].type = C_CHOICES;
481 ret[6].sval = ":Trivial:Basic:Intermediate:Advanced:Extreme:Unreasonable";
482 ret[6].ival = params->diff;
483
484 ret[7].name = NULL;
485 ret[7].type = C_END;
486 ret[7].sval = NULL;
487 ret[7].ival = 0;
488
489 return ret;
490}
491
492static game_params *custom_params(const config_item *cfg)
493{
494 game_params *ret = snew(game_params);
495
496 ret->c = atoi(cfg[0].sval);
497 ret->r = atoi(cfg[1].sval);
498 ret->xtype = cfg[2].ival;
499 if (cfg[3].ival) {
500 ret->c *= ret->r;
501 ret->r = 1;
502 }
503 ret->killer = cfg[4].ival;
504 ret->symm = cfg[5].ival;
505 ret->diff = cfg[6].ival;
506 ret->kdiff = DIFF_KINTERSECT;
507
508 return ret;
509}
510
511static char *validate_params(const game_params *params, int full)
512{
513 if (params->c < 2)
514 return "Both dimensions must be at least 2";
515 if (params->c > ORDER_MAX || params->r > ORDER_MAX)
516 return "Dimensions greater than "STR(ORDER_MAX)" are not supported";
517 if ((params->c * params->r) > 31)
518 return "Unable to support more than 31 distinct symbols in a puzzle";
519 if (params->killer && params->c * params->r > 9)
520 return "Killer puzzle dimensions must be smaller than 10.";
521 return NULL;
522}
523
524/*
525 * ----------------------------------------------------------------------
526 * Block structure functions.
527 */
528
529static struct block_structure *alloc_block_structure(int c, int r, int area,
530 int max_nr_squares,
531 int nr_blocks)
532{
533 int i;
534 struct block_structure *b = snew(struct block_structure);
535
536 b->refcount = 1;
537 b->nr_blocks = nr_blocks;
538 b->max_nr_squares = max_nr_squares;
539 b->c = c; b->r = r; b->area = area;
540 b->whichblock = snewn(area, int);
541 b->blocks_data = snewn(nr_blocks * max_nr_squares, int);
542 b->blocks = snewn(nr_blocks, int *);
543 b->nr_squares = snewn(nr_blocks, int);
544
545 for (i = 0; i < nr_blocks; i++)
546 b->blocks[i] = b->blocks_data + i*max_nr_squares;
547
548#ifdef STANDALONE_SOLVER
549 b->blocknames = (char **)smalloc(c*r*(sizeof(char *)+80));
550 for (i = 0; i < c * r; i++)
551 b->blocknames[i] = NULL;
552#endif
553 return b;
554}
555
556static void free_block_structure(struct block_structure *b)
557{
558 if (--b->refcount == 0) {
559 sfree(b->whichblock);
560 sfree(b->blocks);
561 sfree(b->blocks_data);
562#ifdef STANDALONE_SOLVER
563 sfree(b->blocknames);
564#endif
565 sfree(b->nr_squares);
566 sfree(b);
567 }
568}
569
570static struct block_structure *dup_block_structure(struct block_structure *b)
571{
572 struct block_structure *nb;
573 int i;
574
575 nb = alloc_block_structure(b->c, b->r, b->area, b->max_nr_squares,
576 b->nr_blocks);
577 memcpy(nb->nr_squares, b->nr_squares, b->nr_blocks * sizeof *b->nr_squares);
578 memcpy(nb->whichblock, b->whichblock, b->area * sizeof *b->whichblock);
579 memcpy(nb->blocks_data, b->blocks_data,
580 b->nr_blocks * b->max_nr_squares * sizeof *b->blocks_data);
581 for (i = 0; i < b->nr_blocks; i++)
582 nb->blocks[i] = nb->blocks_data + i*nb->max_nr_squares;
583
584#ifdef STANDALONE_SOLVER
585 memcpy(nb->blocknames, b->blocknames, b->c * b->r *(sizeof(char *)+80));
586 {
587 int i;
588 for (i = 0; i < b->c * b->r; i++)
589 if (b->blocknames[i] == NULL)
590 nb->blocknames[i] = NULL;
591 else
592 nb->blocknames[i] = ((char *)nb->blocknames) + (b->blocknames[i] - (char *)b->blocknames);
593 }
594#endif
595 return nb;
596}
597
598static void split_block(struct block_structure *b, int *squares, int nr_squares)
599{
600 int i, j;
601 int previous_block = b->whichblock[squares[0]];
602 int newblock = b->nr_blocks;
603
604 assert(b->max_nr_squares >= nr_squares);
605 assert(b->nr_squares[previous_block] > nr_squares);
606
607 b->nr_blocks++;
608 b->blocks_data = sresize(b->blocks_data,
609 b->nr_blocks * b->max_nr_squares, int);
610 b->nr_squares = sresize(b->nr_squares, b->nr_blocks, int);
611 sfree(b->blocks);
612 b->blocks = snewn(b->nr_blocks, int *);
613 for (i = 0; i < b->nr_blocks; i++)
614 b->blocks[i] = b->blocks_data + i*b->max_nr_squares;
615 for (i = 0; i < nr_squares; i++) {
616 assert(b->whichblock[squares[i]] == previous_block);
617 b->whichblock[squares[i]] = newblock;
618 b->blocks[newblock][i] = squares[i];
619 }
620 for (i = j = 0; i < b->nr_squares[previous_block]; i++) {
621 int k;
622 int sq = b->blocks[previous_block][i];
623 for (k = 0; k < nr_squares; k++)
624 if (squares[k] == sq)
625 break;
626 if (k == nr_squares)
627 b->blocks[previous_block][j++] = sq;
628 }
629 b->nr_squares[previous_block] -= nr_squares;
630 b->nr_squares[newblock] = nr_squares;
631}
632
633static void remove_from_block(struct block_structure *blocks, int b, int n)
634{
635 int i, j;
636 blocks->whichblock[n] = -1;
637 for (i = j = 0; i < blocks->nr_squares[b]; i++)
638 if (blocks->blocks[b][i] != n)
639 blocks->blocks[b][j++] = blocks->blocks[b][i];
640 assert(j+1 == i);
641 blocks->nr_squares[b]--;
642}
643
644/* ----------------------------------------------------------------------
645 * Solver.
646 *
647 * This solver is used for two purposes:
648 * + to check solubility of a grid as we gradually remove numbers
649 * from it
650 * + to solve an externally generated puzzle when the user selects
651 * `Solve'.
652 *
653 * It supports a variety of specific modes of reasoning. By
654 * enabling or disabling subsets of these modes we can arrange a
655 * range of difficulty levels.
656 */
657
658/*
659 * Modes of reasoning currently supported:
660 *
661 * - Positional elimination: a number must go in a particular
662 * square because all the other empty squares in a given
663 * row/col/blk are ruled out.
664 *
665 * - Killer minmax elimination: for killer-type puzzles, a number
666 * is impossible if choosing it would cause the sum in a killer
667 * region to be guaranteed to be too large or too small.
668 *
669 * - Numeric elimination: a square must have a particular number
670 * in because all the other numbers that could go in it are
671 * ruled out.
672 *
673 * - Intersectional analysis: given two domains which overlap
674 * (hence one must be a block, and the other can be a row or
675 * col), if the possible locations for a particular number in
676 * one of the domains can be narrowed down to the overlap, then
677 * that number can be ruled out everywhere but the overlap in
678 * the other domain too.
679 *
680 * - Set elimination: if there is a subset of the empty squares
681 * within a domain such that the union of the possible numbers
682 * in that subset has the same size as the subset itself, then
683 * those numbers can be ruled out everywhere else in the domain.
684 * (For example, if there are five empty squares and the
685 * possible numbers in each are 12, 23, 13, 134 and 1345, then
686 * the first three empty squares form such a subset: the numbers
687 * 1, 2 and 3 _must_ be in those three squares in some
688 * permutation, and hence we can deduce none of them can be in
689 * the fourth or fifth squares.)
690 * + You can also see this the other way round, concentrating
691 * on numbers rather than squares: if there is a subset of
692 * the unplaced numbers within a domain such that the union
693 * of all their possible positions has the same size as the
694 * subset itself, then all other numbers can be ruled out for
695 * those positions. However, it turns out that this is
696 * exactly equivalent to the first formulation at all times:
697 * there is a 1-1 correspondence between suitable subsets of
698 * the unplaced numbers and suitable subsets of the unfilled
699 * places, found by taking the _complement_ of the union of
700 * the numbers' possible positions (or the spaces' possible
701 * contents).
702 *
703 * - Forcing chains (see comment for solver_forcing().)
704 *
705 * - Recursion. If all else fails, we pick one of the currently
706 * most constrained empty squares and take a random guess at its
707 * contents, then continue solving on that basis and see if we
708 * get any further.
709 */
710
711struct solver_usage {
712 int cr;
713 struct block_structure *blocks, *kblocks, *extra_cages;
714 /*
715 * We set up a cubic array, indexed by x, y and digit; each
716 * element of this array is TRUE or FALSE according to whether
717 * or not that digit _could_ in principle go in that position.
718 *
719 * The way to index this array is cube[(y*cr+x)*cr+n-1]; there
720 * are macros below to help with this.
721 */
722 unsigned char *cube;
723 /*
724 * This is the grid in which we write down our final
725 * deductions. y-coordinates in here are _not_ transformed.
726 */
727 digit *grid;
728 /*
729 * For killer-type puzzles, kclues holds the secondary clue for
730 * each cage. For derived cages, the clue is in extra_clues.
731 */
732 digit *kclues, *extra_clues;
733 /*
734 * Now we keep track, at a slightly higher level, of what we
735 * have yet to work out, to prevent doing the same deduction
736 * many times.
737 */
738 /* row[y*cr+n-1] TRUE if digit n has been placed in row y */
739 unsigned char *row;
740 /* col[x*cr+n-1] TRUE if digit n has been placed in row x */
741 unsigned char *col;
742 /* blk[i*cr+n-1] TRUE if digit n has been placed in block i */
743 unsigned char *blk;
744 /* diag[i*cr+n-1] TRUE if digit n has been placed in diagonal i */
745 unsigned char *diag; /* diag 0 is \, 1 is / */
746
747 int *regions;
748 int nr_regions;
749 int **sq2region;
750};
751#define cubepos2(xy,n) ((xy)*usage->cr+(n)-1)
752#define cubepos(x,y,n) cubepos2((y)*usage->cr+(x),n)
753#define cube(x,y,n) (usage->cube[cubepos(x,y,n)])
754#define cube2(xy,n) (usage->cube[cubepos2(xy,n)])
755
756#define ondiag0(xy) ((xy) % (cr+1) == 0)
757#define ondiag1(xy) ((xy) % (cr-1) == 0 && (xy) > 0 && (xy) < cr*cr-1)
758#define diag0(i) ((i) * (cr+1))
759#define diag1(i) ((i+1) * (cr-1))
760
761/*
762 * Function called when we are certain that a particular square has
763 * a particular number in it. The y-coordinate passed in here is
764 * transformed.
765 */
766static void solver_place(struct solver_usage *usage, int x, int y, int n)
767{
768 int cr = usage->cr;
769 int sqindex = y*cr+x;
770 int i, bi;
771
772 assert(cube(x,y,n));
773
774 /*
775 * Rule out all other numbers in this square.
776 */
777 for (i = 1; i <= cr; i++)
778 if (i != n)
779 cube(x,y,i) = FALSE;
780
781 /*
782 * Rule out this number in all other positions in the row.
783 */
784 for (i = 0; i < cr; i++)
785 if (i != y)
786 cube(x,i,n) = FALSE;
787
788 /*
789 * Rule out this number in all other positions in the column.
790 */
791 for (i = 0; i < cr; i++)
792 if (i != x)
793 cube(i,y,n) = FALSE;
794
795 /*
796 * Rule out this number in all other positions in the block.
797 */
798 bi = usage->blocks->whichblock[sqindex];
799 for (i = 0; i < cr; i++) {
800 int bp = usage->blocks->blocks[bi][i];
801 if (bp != sqindex)
802 cube2(bp,n) = FALSE;
803 }
804
805 /*
806 * Enter the number in the result grid.
807 */
808 usage->grid[sqindex] = n;
809
810 /*
811 * Cross out this number from the list of numbers left to place
812 * in its row, its column and its block.
813 */
814 usage->row[y*cr+n-1] = usage->col[x*cr+n-1] =
815 usage->blk[bi*cr+n-1] = TRUE;
816
817 if (usage->diag) {
818 if (ondiag0(sqindex)) {
819 for (i = 0; i < cr; i++)
820 if (diag0(i) != sqindex)
821 cube2(diag0(i),n) = FALSE;
822 usage->diag[n-1] = TRUE;
823 }
824 if (ondiag1(sqindex)) {
825 for (i = 0; i < cr; i++)
826 if (diag1(i) != sqindex)
827 cube2(diag1(i),n) = FALSE;
828 usage->diag[cr+n-1] = TRUE;
829 }
830 }
831}
832
833#if defined STANDALONE_SOLVER && defined __GNUC__
834/*
835 * Forward-declare the functions taking printf-like format arguments
836 * with __attribute__((format)) so as to ensure the argument syntax
837 * gets debugged.
838 */
839struct solver_scratch;
840static int solver_elim(struct solver_usage *usage, int *indices,
841 char *fmt, ...) __attribute__((format(printf,3,4)));
842static int solver_intersect(struct solver_usage *usage,
843 int *indices1, int *indices2, char *fmt, ...)
844 __attribute__((format(printf,4,5)));
845static int solver_set(struct solver_usage *usage,
846 struct solver_scratch *scratch,
847 int *indices, char *fmt, ...)
848 __attribute__((format(printf,4,5)));
849#endif
850
851static int solver_elim(struct solver_usage *usage, int *indices
852#ifdef STANDALONE_SOLVER
853 , char *fmt, ...
854#endif
855 )
856{
857 int cr = usage->cr;
858 int fpos, m, i;
859
860 /*
861 * Count the number of set bits within this section of the
862 * cube.
863 */
864 m = 0;
865 fpos = -1;
866 for (i = 0; i < cr; i++)
867 if (usage->cube[indices[i]]) {
868 fpos = indices[i];
869 m++;
870 }
871
872 if (m == 1) {
873 int x, y, n;
874 assert(fpos >= 0);
875
876 n = 1 + fpos % cr;
877 x = fpos / cr;
878 y = x / cr;
879 x %= cr;
880
881 if (!usage->grid[y*cr+x]) {
882#ifdef STANDALONE_SOLVER
883 if (solver_show_working) {
884 va_list ap;
885 printf("%*s", solver_recurse_depth*4, "");
886 va_start(ap, fmt);
887 vprintf(fmt, ap);
888 va_end(ap);
889 printf(":\n%*s placing %d at (%d,%d)\n",
890 solver_recurse_depth*4, "", n, 1+x, 1+y);
891 }
892#endif
893 solver_place(usage, x, y, n);
894 return +1;
895 }
896 } else if (m == 0) {
897#ifdef STANDALONE_SOLVER
898 if (solver_show_working) {
899 va_list ap;
900 printf("%*s", solver_recurse_depth*4, "");
901 va_start(ap, fmt);
902 vprintf(fmt, ap);
903 va_end(ap);
904 printf(":\n%*s no possibilities available\n",
905 solver_recurse_depth*4, "");
906 }
907#endif
908 return -1;
909 }
910
911 return 0;
912}
913
914static int solver_intersect(struct solver_usage *usage,
915 int *indices1, int *indices2
916#ifdef STANDALONE_SOLVER
917 , char *fmt, ...
918#endif
919 )
920{
921 int cr = usage->cr;
922 int ret, i, j;
923
924 /*
925 * Loop over the first domain and see if there's any set bit
926 * not also in the second.
927 */
928 for (i = j = 0; i < cr; i++) {
929 int p = indices1[i];
930 while (j < cr && indices2[j] < p)
931 j++;
932 if (usage->cube[p]) {
933 if (j < cr && indices2[j] == p)
934 continue; /* both domains contain this index */
935 else
936 return 0; /* there is, so we can't deduce */
937 }
938 }
939
940 /*
941 * We have determined that all set bits in the first domain are
942 * within its overlap with the second. So loop over the second
943 * domain and remove all set bits that aren't also in that
944 * overlap; return +1 iff we actually _did_ anything.
945 */
946 ret = 0;
947 for (i = j = 0; i < cr; i++) {
948 int p = indices2[i];
949 while (j < cr && indices1[j] < p)
950 j++;
951 if (usage->cube[p] && (j >= cr || indices1[j] != p)) {
952#ifdef STANDALONE_SOLVER
953 if (solver_show_working) {
954 int px, py, pn;
955
956 if (!ret) {
957 va_list ap;
958 printf("%*s", solver_recurse_depth*4, "");
959 va_start(ap, fmt);
960 vprintf(fmt, ap);
961 va_end(ap);
962 printf(":\n");
963 }
964
965 pn = 1 + p % cr;
966 px = p / cr;
967 py = px / cr;
968 px %= cr;
969
970 printf("%*s ruling out %d at (%d,%d)\n",
971 solver_recurse_depth*4, "", pn, 1+px, 1+py);
972 }
973#endif
974 ret = +1; /* we did something */
975 usage->cube[p] = 0;
976 }
977 }
978
979 return ret;
980}
981
982struct solver_scratch {
983 unsigned char *grid, *rowidx, *colidx, *set;
984 int *neighbours, *bfsqueue;
985 int *indexlist, *indexlist2;
986#ifdef STANDALONE_SOLVER
987 int *bfsprev;
988#endif
989};
990
991static int solver_set(struct solver_usage *usage,
992 struct solver_scratch *scratch,
993 int *indices
994#ifdef STANDALONE_SOLVER
995 , char *fmt, ...
996#endif
997 )
998{
999 int cr = usage->cr;
1000 int i, j, n, count;
1001 unsigned char *grid = scratch->grid;
1002 unsigned char *rowidx = scratch->rowidx;
1003 unsigned char *colidx = scratch->colidx;
1004 unsigned char *set = scratch->set;
1005
1006 /*
1007 * We are passed a cr-by-cr matrix of booleans. Our first job
1008 * is to winnow it by finding any definite placements - i.e.
1009 * any row with a solitary 1 - and discarding that row and the
1010 * column containing the 1.
1011 */
1012 memset(rowidx, TRUE, cr);
1013 memset(colidx, TRUE, cr);
1014 for (i = 0; i < cr; i++) {
1015 int count = 0, first = -1;
1016 for (j = 0; j < cr; j++)
1017 if (usage->cube[indices[i*cr+j]])
1018 first = j, count++;
1019
1020 /*
1021 * If count == 0, then there's a row with no 1s at all and
1022 * the puzzle is internally inconsistent. However, we ought
1023 * to have caught this already during the simpler reasoning
1024 * methods, so we can safely fail an assertion if we reach
1025 * this point here.
1026 */
1027 assert(count > 0);
1028 if (count == 1)
1029 rowidx[i] = colidx[first] = FALSE;
1030 }
1031
1032 /*
1033 * Convert each of rowidx/colidx from a list of 0s and 1s to a
1034 * list of the indices of the 1s.
1035 */
1036 for (i = j = 0; i < cr; i++)
1037 if (rowidx[i])
1038 rowidx[j++] = i;
1039 n = j;
1040 for (i = j = 0; i < cr; i++)
1041 if (colidx[i])
1042 colidx[j++] = i;
1043 assert(n == j);
1044
1045 /*
1046 * And create the smaller matrix.
1047 */
1048 for (i = 0; i < n; i++)
1049 for (j = 0; j < n; j++)
1050 grid[i*cr+j] = usage->cube[indices[rowidx[i]*cr+colidx[j]]];
1051
1052 /*
1053 * Having done that, we now have a matrix in which every row
1054 * has at least two 1s in. Now we search to see if we can find
1055 * a rectangle of zeroes (in the set-theoretic sense of
1056 * `rectangle', i.e. a subset of rows crossed with a subset of
1057 * columns) whose width and height add up to n.
1058 */
1059
1060 memset(set, 0, n);
1061 count = 0;
1062 while (1) {
1063 /*
1064 * We have a candidate set. If its size is <=1 or >=n-1
1065 * then we move on immediately.
1066 */
1067 if (count > 1 && count < n-1) {
1068 /*
1069 * The number of rows we need is n-count. See if we can
1070 * find that many rows which each have a zero in all
1071 * the positions listed in `set'.
1072 */
1073 int rows = 0;
1074 for (i = 0; i < n; i++) {
1075 int ok = TRUE;
1076 for (j = 0; j < n; j++)
1077 if (set[j] && grid[i*cr+j]) {
1078 ok = FALSE;
1079 break;
1080 }
1081 if (ok)
1082 rows++;
1083 }
1084
1085 /*
1086 * We expect never to be able to get _more_ than
1087 * n-count suitable rows: this would imply that (for
1088 * example) there are four numbers which between them
1089 * have at most three possible positions, and hence it
1090 * indicates a faulty deduction before this point or
1091 * even a bogus clue.
1092 */
1093 if (rows > n - count) {
1094#ifdef STANDALONE_SOLVER
1095 if (solver_show_working) {
1096 va_list ap;
1097 printf("%*s", solver_recurse_depth*4,
1098 "");
1099 va_start(ap, fmt);
1100 vprintf(fmt, ap);
1101 va_end(ap);
1102 printf(":\n%*s contradiction reached\n",
1103 solver_recurse_depth*4, "");
1104 }
1105#endif
1106 return -1;
1107 }
1108
1109 if (rows >= n - count) {
1110 int progress = FALSE;
1111
1112 /*
1113 * We've got one! Now, for each row which _doesn't_
1114 * satisfy the criterion, eliminate all its set
1115 * bits in the positions _not_ listed in `set'.
1116 * Return +1 (meaning progress has been made) if we
1117 * successfully eliminated anything at all.
1118 *
1119 * This involves referring back through
1120 * rowidx/colidx in order to work out which actual
1121 * positions in the cube to meddle with.
1122 */
1123 for (i = 0; i < n; i++) {
1124 int ok = TRUE;
1125 for (j = 0; j < n; j++)
1126 if (set[j] && grid[i*cr+j]) {
1127 ok = FALSE;
1128 break;
1129 }
1130 if (!ok) {
1131 for (j = 0; j < n; j++)
1132 if (!set[j] && grid[i*cr+j]) {
1133 int fpos = indices[rowidx[i]*cr+colidx[j]];
1134#ifdef STANDALONE_SOLVER
1135 if (solver_show_working) {
1136 int px, py, pn;
1137
1138 if (!progress) {
1139 va_list ap;
1140 printf("%*s", solver_recurse_depth*4,
1141 "");
1142 va_start(ap, fmt);
1143 vprintf(fmt, ap);
1144 va_end(ap);
1145 printf(":\n");
1146 }
1147
1148 pn = 1 + fpos % cr;
1149 px = fpos / cr;
1150 py = px / cr;
1151 px %= cr;
1152
1153 printf("%*s ruling out %d at (%d,%d)\n",
1154 solver_recurse_depth*4, "",
1155 pn, 1+px, 1+py);
1156 }
1157#endif
1158 progress = TRUE;
1159 usage->cube[fpos] = FALSE;
1160 }
1161 }
1162 }
1163
1164 if (progress) {
1165 return +1;
1166 }
1167 }
1168 }
1169
1170 /*
1171 * Binary increment: change the rightmost 0 to a 1, and
1172 * change all 1s to the right of it to 0s.
1173 */
1174 i = n;
1175 while (i > 0 && set[i-1])
1176 set[--i] = 0, count--;
1177 if (i > 0)
1178 set[--i] = 1, count++;
1179 else
1180 break; /* done */
1181 }
1182
1183 return 0;
1184}
1185
1186/*
1187 * Look for forcing chains. A forcing chain is a path of
1188 * pairwise-exclusive squares (i.e. each pair of adjacent squares
1189 * in the path are in the same row, column or block) with the
1190 * following properties:
1191 *
1192 * (a) Each square on the path has precisely two possible numbers.
1193 *
1194 * (b) Each pair of squares which are adjacent on the path share
1195 * at least one possible number in common.
1196 *
1197 * (c) Each square in the middle of the path shares _both_ of its
1198 * numbers with at least one of its neighbours (not the same
1199 * one with both neighbours).
1200 *
1201 * These together imply that at least one of the possible number
1202 * choices at one end of the path forces _all_ the rest of the
1203 * numbers along the path. In order to make real use of this, we
1204 * need further properties:
1205 *
1206 * (c) Ruling out some number N from the square at one end of the
1207 * path forces the square at the other end to take the same
1208 * number N.
1209 *
1210 * (d) The two end squares are both in line with some third
1211 * square.
1212 *
1213 * (e) That third square currently has N as a possibility.
1214 *
1215 * If we can find all of that lot, we can deduce that at least one
1216 * of the two ends of the forcing chain has number N, and that
1217 * therefore the mutually adjacent third square does not.
1218 *
1219 * To find forcing chains, we're going to start a bfs at each
1220 * suitable square, once for each of its two possible numbers.
1221 */
1222static int solver_forcing(struct solver_usage *usage,
1223 struct solver_scratch *scratch)
1224{
1225 int cr = usage->cr;
1226 int *bfsqueue = scratch->bfsqueue;
1227#ifdef STANDALONE_SOLVER
1228 int *bfsprev = scratch->bfsprev;
1229#endif
1230 unsigned char *number = scratch->grid;
1231 int *neighbours = scratch->neighbours;
1232 int x, y;
1233
1234 for (y = 0; y < cr; y++)
1235 for (x = 0; x < cr; x++) {
1236 int count, t, n;
1237
1238 /*
1239 * If this square doesn't have exactly two candidate
1240 * numbers, don't try it.
1241 *
1242 * In this loop we also sum the candidate numbers,
1243 * which is a nasty hack to allow us to quickly find
1244 * `the other one' (since we will shortly know there
1245 * are exactly two).
1246 */
1247 for (count = t = 0, n = 1; n <= cr; n++)
1248 if (cube(x, y, n))
1249 count++, t += n;
1250 if (count != 2)
1251 continue;
1252
1253 /*
1254 * Now attempt a bfs for each candidate.
1255 */
1256 for (n = 1; n <= cr; n++)
1257 if (cube(x, y, n)) {
1258 int orign, currn, head, tail;
1259
1260 /*
1261 * Begin a bfs.
1262 */
1263 orign = n;
1264
1265 memset(number, cr+1, cr*cr);
1266 head = tail = 0;
1267 bfsqueue[tail++] = y*cr+x;
1268#ifdef STANDALONE_SOLVER
1269 bfsprev[y*cr+x] = -1;
1270#endif
1271 number[y*cr+x] = t - n;
1272
1273 while (head < tail) {
1274 int xx, yy, nneighbours, xt, yt, i;
1275
1276 xx = bfsqueue[head++];
1277 yy = xx / cr;
1278 xx %= cr;
1279
1280 currn = number[yy*cr+xx];
1281
1282 /*
1283 * Find neighbours of yy,xx.
1284 */
1285 nneighbours = 0;
1286 for (yt = 0; yt < cr; yt++)
1287 neighbours[nneighbours++] = yt*cr+xx;
1288 for (xt = 0; xt < cr; xt++)
1289 neighbours[nneighbours++] = yy*cr+xt;
1290 xt = usage->blocks->whichblock[yy*cr+xx];
1291 for (yt = 0; yt < cr; yt++)
1292 neighbours[nneighbours++] = usage->blocks->blocks[xt][yt];
1293 if (usage->diag) {
1294 int sqindex = yy*cr+xx;
1295 if (ondiag0(sqindex)) {
1296 for (i = 0; i < cr; i++)
1297 neighbours[nneighbours++] = diag0(i);
1298 }
1299 if (ondiag1(sqindex)) {
1300 for (i = 0; i < cr; i++)
1301 neighbours[nneighbours++] = diag1(i);
1302 }
1303 }
1304
1305 /*
1306 * Try visiting each of those neighbours.
1307 */
1308 for (i = 0; i < nneighbours; i++) {
1309 int cc, tt, nn;
1310
1311 xt = neighbours[i] % cr;
1312 yt = neighbours[i] / cr;
1313
1314 /*
1315 * We need this square to not be
1316 * already visited, and to include
1317 * currn as a possible number.
1318 */
1319 if (number[yt*cr+xt] <= cr)
1320 continue;
1321 if (!cube(xt, yt, currn))
1322 continue;
1323
1324 /*
1325 * Don't visit _this_ square a second
1326 * time!
1327 */
1328 if (xt == xx && yt == yy)
1329 continue;
1330
1331 /*
1332 * To continue with the bfs, we need
1333 * this square to have exactly two
1334 * possible numbers.
1335 */
1336 for (cc = tt = 0, nn = 1; nn <= cr; nn++)
1337 if (cube(xt, yt, nn))
1338 cc++, tt += nn;
1339 if (cc == 2) {
1340 bfsqueue[tail++] = yt*cr+xt;
1341#ifdef STANDALONE_SOLVER
1342 bfsprev[yt*cr+xt] = yy*cr+xx;
1343#endif
1344 number[yt*cr+xt] = tt - currn;
1345 }
1346
1347 /*
1348 * One other possibility is that this
1349 * might be the square in which we can
1350 * make a real deduction: if it's
1351 * adjacent to x,y, and currn is equal
1352 * to the original number we ruled out.
1353 */
1354 if (currn == orign &&
1355 (xt == x || yt == y ||
1356 (usage->blocks->whichblock[yt*cr+xt] == usage->blocks->whichblock[y*cr+x]) ||
1357 (usage->diag && ((ondiag0(yt*cr+xt) && ondiag0(y*cr+x)) ||
1358 (ondiag1(yt*cr+xt) && ondiag1(y*cr+x)))))) {
1359#ifdef STANDALONE_SOLVER
1360 if (solver_show_working) {
1361 char *sep = "";
1362 int xl, yl;
1363 printf("%*sforcing chain, %d at ends of ",
1364 solver_recurse_depth*4, "", orign);
1365 xl = xx;
1366 yl = yy;
1367 while (1) {
1368 printf("%s(%d,%d)", sep, 1+xl,
1369 1+yl);
1370 xl = bfsprev[yl*cr+xl];
1371 if (xl < 0)
1372 break;
1373 yl = xl / cr;
1374 xl %= cr;
1375 sep = "-";
1376 }
1377 printf("\n%*s ruling out %d at (%d,%d)\n",
1378 solver_recurse_depth*4, "",
1379 orign, 1+xt, 1+yt);
1380 }
1381#endif
1382 cube(xt, yt, orign) = FALSE;
1383 return 1;
1384 }
1385 }
1386 }
1387 }
1388 }
1389
1390 return 0;
1391}
1392
1393static int solver_killer_minmax(struct solver_usage *usage,
1394 struct block_structure *cages, digit *clues,
1395 int b
1396#ifdef STANDALONE_SOLVER
1397 , const char *extra
1398#endif
1399 )
1400{
1401 int cr = usage->cr;
1402 int i;
1403 int ret = 0;
1404 int nsquares = cages->nr_squares[b];
1405
1406 if (clues[b] == 0)
1407 return 0;
1408
1409 for (i = 0; i < nsquares; i++) {
1410 int n, x = cages->blocks[b][i];
1411
1412 for (n = 1; n <= cr; n++)
1413 if (cube2(x, n)) {
1414 int maxval = 0, minval = 0;
1415 int j;
1416 for (j = 0; j < nsquares; j++) {
1417 int m;
1418 int y = cages->blocks[b][j];
1419 if (i == j)
1420 continue;
1421 for (m = 1; m <= cr; m++)
1422 if (cube2(y, m)) {
1423 minval += m;
1424 break;
1425 }
1426 for (m = cr; m > 0; m--)
1427 if (cube2(y, m)) {
1428 maxval += m;
1429 break;
1430 }
1431 }
1432 if (maxval + n < clues[b]) {
1433 cube2(x, n) = FALSE;
1434 ret = 1;
1435#ifdef STANDALONE_SOLVER
1436 if (solver_show_working)
1437 printf("%*s ruling out %d at (%d,%d) as too low %s\n",
1438 solver_recurse_depth*4, "killer minmax analysis",
1439 n, 1 + x%cr, 1 + x/cr, extra);
1440#endif
1441 }
1442 if (minval + n > clues[b]) {
1443 cube2(x, n) = FALSE;
1444 ret = 1;
1445#ifdef STANDALONE_SOLVER
1446 if (solver_show_working)
1447 printf("%*s ruling out %d at (%d,%d) as too high %s\n",
1448 solver_recurse_depth*4, "killer minmax analysis",
1449 n, 1 + x%cr, 1 + x/cr, extra);
1450#endif
1451 }
1452 }
1453 }
1454 return ret;
1455}
1456
1457static int solver_killer_sums(struct solver_usage *usage, int b,
1458 struct block_structure *cages, int clue,
1459 int cage_is_region
1460#ifdef STANDALONE_SOLVER
1461 , const char *cage_type
1462#endif
1463 )
1464{
1465 int cr = usage->cr;
1466 int i, ret, max_sums;
1467 int nsquares = cages->nr_squares[b];
1468 unsigned long *sumbits, possible_addends;
1469
1470 if (clue == 0) {
1471 assert(nsquares == 0);
1472 return 0;
1473 }
1474 assert(nsquares > 0);
1475
1476 if (nsquares < 2 || nsquares > 4)
1477 return 0;
1478
1479 if (!cage_is_region) {
1480 int known_row = -1, known_col = -1, known_block = -1;
1481 /*
1482 * Verify that the cage lies entirely within one region,
1483 * so that using the precomputed sums is valid.
1484 */
1485 for (i = 0; i < nsquares; i++) {
1486 int x = cages->blocks[b][i];
1487
1488 assert(usage->grid[x] == 0);
1489
1490 if (i == 0) {
1491 known_row = x/cr;
1492 known_col = x%cr;
1493 known_block = usage->blocks->whichblock[x];
1494 } else {
1495 if (known_row != x/cr)
1496 known_row = -1;
1497 if (known_col != x%cr)
1498 known_col = -1;
1499 if (known_block != usage->blocks->whichblock[x])
1500 known_block = -1;
1501 }
1502 }
1503 if (known_block == -1 && known_col == -1 && known_row == -1)
1504 return 0;
1505 }
1506 if (nsquares == 2) {
1507 if (clue < 3 || clue > 17)
1508 return -1;
1509
1510 sumbits = sum_bits2[clue];
1511 max_sums = MAX_2SUMS;
1512 } else if (nsquares == 3) {
1513 if (clue < 6 || clue > 24)
1514 return -1;
1515
1516 sumbits = sum_bits3[clue];
1517 max_sums = MAX_3SUMS;
1518 } else {
1519 if (clue < 10 || clue > 30)
1520 return -1;
1521
1522 sumbits = sum_bits4[clue];
1523 max_sums = MAX_4SUMS;
1524 }
1525 /*
1526 * For every possible way to get the sum, see if there is
1527 * one square in the cage that disallows all the required
1528 * addends. If we find one such square, this way to compute
1529 * the sum is impossible.
1530 */
1531 possible_addends = 0;
1532 for (i = 0; i < max_sums; i++) {
1533 int j;
1534 unsigned long bits = sumbits[i];
1535
1536 if (bits == 0)
1537 break;
1538
1539 for (j = 0; j < nsquares; j++) {
1540 int n;
1541 unsigned long square_bits = bits;
1542 int x = cages->blocks[b][j];
1543 for (n = 1; n <= cr; n++)
1544 if (!cube2(x, n))
1545 square_bits &= ~(1L << n);
1546 if (square_bits == 0) {
1547 break;
1548 }
1549 }
1550 if (j == nsquares)
1551 possible_addends |= bits;
1552 }
1553 /*
1554 * Now we know which addends can possibly be used to
1555 * compute the sum. Remove all other digits from the
1556 * set of possibilities.
1557 */
1558 if (possible_addends == 0)
1559 return -1;
1560
1561 ret = 0;
1562 for (i = 0; i < nsquares; i++) {
1563 int n;
1564 int x = cages->blocks[b][i];
1565 for (n = 1; n <= cr; n++) {
1566 if (!cube2(x, n))
1567 continue;
1568 if ((possible_addends & (1 << n)) == 0) {
1569 cube2(x, n) = FALSE;
1570 ret = 1;
1571#ifdef STANDALONE_SOLVER
1572 if (solver_show_working) {
1573 printf("%*s using %s\n",
1574 solver_recurse_depth*4, "killer sums analysis",
1575 cage_type);
1576 printf("%*s ruling out %d at (%d,%d) due to impossible %d-sum\n",
1577 solver_recurse_depth*4, "",
1578 n, 1 + x%cr, 1 + x/cr, nsquares);
1579 }
1580#endif
1581 }
1582 }
1583 }
1584 return ret;
1585}
1586
1587static int filter_whole_cages(struct solver_usage *usage, int *squares, int n,
1588 int *filtered_sum)
1589{
1590 int b, i, j, off;
1591 *filtered_sum = 0;
1592
1593 /* First, filter squares with a clue. */
1594 for (i = j = 0; i < n; i++)
1595 if (usage->grid[squares[i]])
1596 *filtered_sum += usage->grid[squares[i]];
1597 else
1598 squares[j++] = squares[i];
1599 n = j;
1600
1601 /*
1602 * Filter all cages that are covered entirely by the list of
1603 * squares.
1604 */
1605 off = 0;
1606 for (b = 0; b < usage->kblocks->nr_blocks && off < n; b++) {
1607 int b_squares = usage->kblocks->nr_squares[b];
1608 int matched = 0;
1609
1610 if (b_squares == 0)
1611 continue;
1612
1613 /*
1614 * Find all squares of block b that lie in our list,
1615 * and make them contiguous at off, which is the current position
1616 * in the output list.
1617 */
1618 for (i = 0; i < b_squares; i++) {
1619 for (j = off; j < n; j++)
1620 if (squares[j] == usage->kblocks->blocks[b][i]) {
1621 int t = squares[off + matched];
1622 squares[off + matched] = squares[j];
1623 squares[j] = t;
1624 matched++;
1625 break;
1626 }
1627 }
1628 /* If so, filter out all squares of b from the list. */
1629 if (matched != usage->kblocks->nr_squares[b]) {
1630 off += matched;
1631 continue;
1632 }
1633 memmove(squares + off, squares + off + matched,
1634 (n - off - matched) * sizeof *squares);
1635 n -= matched;
1636
1637 *filtered_sum += usage->kclues[b];
1638 }
1639 assert(off == n);
1640 return off;
1641}
1642
1643static struct solver_scratch *solver_new_scratch(struct solver_usage *usage)
1644{
1645 struct solver_scratch *scratch = snew(struct solver_scratch);
1646 int cr = usage->cr;
1647 scratch->grid = snewn(cr*cr, unsigned char);
1648 scratch->rowidx = snewn(cr, unsigned char);
1649 scratch->colidx = snewn(cr, unsigned char);
1650 scratch->set = snewn(cr, unsigned char);
1651 scratch->neighbours = snewn(5*cr, int);
1652 scratch->bfsqueue = snewn(cr*cr, int);
1653#ifdef STANDALONE_SOLVER
1654 scratch->bfsprev = snewn(cr*cr, int);
1655#endif
1656 scratch->indexlist = snewn(cr*cr, int); /* used for set elimination */
1657 scratch->indexlist2 = snewn(cr, int); /* only used for intersect() */
1658 return scratch;
1659}
1660
1661static void solver_free_scratch(struct solver_scratch *scratch)
1662{
1663#ifdef STANDALONE_SOLVER
1664 sfree(scratch->bfsprev);
1665#endif
1666 sfree(scratch->bfsqueue);
1667 sfree(scratch->neighbours);
1668 sfree(scratch->set);
1669 sfree(scratch->colidx);
1670 sfree(scratch->rowidx);
1671 sfree(scratch->grid);
1672 sfree(scratch->indexlist);
1673 sfree(scratch->indexlist2);
1674 sfree(scratch);
1675}
1676
1677/*
1678 * Used for passing information about difficulty levels between the solver
1679 * and its callers.
1680 */
1681struct difficulty {
1682 /* Maximum levels allowed. */
1683 int maxdiff, maxkdiff;
1684 /* Levels reached by the solver. */
1685 int diff, kdiff;
1686};
1687
1688static void solver(int cr, struct block_structure *blocks,
1689 struct block_structure *kblocks, int xtype,
1690 digit *grid, digit *kgrid, struct difficulty *dlev)
1691{
1692 struct solver_usage *usage;
1693 struct solver_scratch *scratch;
1694 int x, y, b, i, n, ret;
1695 int diff = DIFF_BLOCK;
1696 int kdiff = DIFF_KSINGLE;
1697
1698 /*
1699 * Set up a usage structure as a clean slate (everything
1700 * possible).
1701 */
1702 usage = snew(struct solver_usage);
1703 usage->cr = cr;
1704 usage->blocks = blocks;
1705 if (kblocks) {
1706 usage->kblocks = dup_block_structure(kblocks);
1707 usage->extra_cages = alloc_block_structure (kblocks->c, kblocks->r,
1708 cr * cr, cr, cr * cr);
1709 usage->extra_clues = snewn(cr*cr, digit);
1710 } else {
1711 usage->kblocks = usage->extra_cages = NULL;
1712 usage->extra_clues = NULL;
1713 }
1714 usage->cube = snewn(cr*cr*cr, unsigned char);
1715 usage->grid = grid; /* write straight back to the input */
1716 if (kgrid) {
1717 int nclues;
1718
1719 assert(kblocks);
1720 nclues = kblocks->nr_blocks;
1721 /*
1722 * Allow for expansion of the killer regions, the absolute
1723 * limit is obviously one region per square.
1724 */
1725 usage->kclues = snewn(cr*cr, digit);
1726 for (i = 0; i < nclues; i++) {
1727 for (n = 0; n < kblocks->nr_squares[i]; n++)
1728 if (kgrid[kblocks->blocks[i][n]] != 0)
1729 usage->kclues[i] = kgrid[kblocks->blocks[i][n]];
1730 assert(usage->kclues[i] > 0);
1731 }
1732 memset(usage->kclues + nclues, 0, cr*cr - nclues);
1733 } else {
1734 usage->kclues = NULL;
1735 }
1736
1737 memset(usage->cube, TRUE, cr*cr*cr);
1738
1739 usage->row = snewn(cr * cr, unsigned char);
1740 usage->col = snewn(cr * cr, unsigned char);
1741 usage->blk = snewn(cr * cr, unsigned char);
1742 memset(usage->row, FALSE, cr * cr);
1743 memset(usage->col, FALSE, cr * cr);
1744 memset(usage->blk, FALSE, cr * cr);
1745
1746 if (xtype) {
1747 usage->diag = snewn(cr * 2, unsigned char);
1748 memset(usage->diag, FALSE, cr * 2);
1749 } else
1750 usage->diag = NULL;
1751
1752 usage->nr_regions = cr * 3 + (xtype ? 2 : 0);
1753 usage->regions = snewn(cr * usage->nr_regions, int);
1754 usage->sq2region = snewn(cr * cr * 3, int *);
1755
1756 for (n = 0; n < cr; n++) {
1757 for (i = 0; i < cr; i++) {
1758 x = n*cr+i;
1759 y = i*cr+n;
1760 b = usage->blocks->blocks[n][i];
1761 usage->regions[cr*n*3 + i] = x;
1762 usage->regions[cr*n*3 + cr + i] = y;
1763 usage->regions[cr*n*3 + 2*cr + i] = b;
1764 usage->sq2region[x*3] = usage->regions + cr*n*3;
1765 usage->sq2region[y*3 + 1] = usage->regions + cr*n*3 + cr;
1766 usage->sq2region[b*3 + 2] = usage->regions + cr*n*3 + 2*cr;
1767 }
1768 }
1769
1770 scratch = solver_new_scratch(usage);
1771
1772 /*
1773 * Place all the clue numbers we are given.
1774 */
1775 for (x = 0; x < cr; x++)
1776 for (y = 0; y < cr; y++) {
1777 int n = grid[y*cr+x];
1778 if (n) {
1779 if (!cube(x,y,n)) {
1780 diff = DIFF_IMPOSSIBLE;
1781 goto got_result;
1782 }
1783 solver_place(usage, x, y, grid[y*cr+x]);
1784 }
1785 }
1786
1787 /*
1788 * Now loop over the grid repeatedly trying all permitted modes
1789 * of reasoning. The loop terminates if we complete an
1790 * iteration without making any progress; we then return
1791 * failure or success depending on whether the grid is full or
1792 * not.
1793 */
1794 while (1) {
1795 /*
1796 * I'd like to write `continue;' inside each of the
1797 * following loops, so that the solver returns here after
1798 * making some progress. However, I can't specify that I
1799 * want to continue an outer loop rather than the innermost
1800 * one, so I'm apologetically resorting to a goto.
1801 */
1802 cont:
1803
1804 /*
1805 * Blockwise positional elimination.
1806 */
1807 for (b = 0; b < cr; b++)
1808 for (n = 1; n <= cr; n++)
1809 if (!usage->blk[b*cr+n-1]) {
1810 for (i = 0; i < cr; i++)
1811 scratch->indexlist[i] = cubepos2(usage->blocks->blocks[b][i],n);
1812 ret = solver_elim(usage, scratch->indexlist
1813#ifdef STANDALONE_SOLVER
1814 , "positional elimination,"
1815 " %d in block %s", n,
1816 usage->blocks->blocknames[b]
1817#endif
1818 );
1819 if (ret < 0) {
1820 diff = DIFF_IMPOSSIBLE;
1821 goto got_result;
1822 } else if (ret > 0) {
1823 diff = max(diff, DIFF_BLOCK);
1824 goto cont;
1825 }
1826 }
1827
1828 if (usage->kclues != NULL) {
1829 int changed = FALSE;
1830
1831 /*
1832 * First, bring the kblocks into a more useful form: remove
1833 * all filled-in squares, and reduce the sum by their values.
1834 * Walk in reverse order, since otherwise remove_from_block
1835 * can move element past our loop counter.
1836 */
1837 for (b = 0; b < usage->kblocks->nr_blocks; b++)
1838 for (i = usage->kblocks->nr_squares[b] -1; i >= 0; i--) {
1839 int x = usage->kblocks->blocks[b][i];
1840 int t = usage->grid[x];
1841
1842 if (t == 0)
1843 continue;
1844 remove_from_block(usage->kblocks, b, x);
1845 if (t > usage->kclues[b]) {
1846 diff = DIFF_IMPOSSIBLE;
1847 goto got_result;
1848 }
1849 usage->kclues[b] -= t;
1850 /*
1851 * Since cages are regions, this tells us something
1852 * about the other squares in the cage.
1853 */
1854 for (n = 0; n < usage->kblocks->nr_squares[b]; n++) {
1855 cube2(usage->kblocks->blocks[b][n], t) = FALSE;
1856 }
1857 }
1858
1859 /*
1860 * The most trivial kind of solver for killer puzzles: fill
1861 * single-square cages.
1862 */
1863 for (b = 0; b < usage->kblocks->nr_blocks; b++) {
1864 int squares = usage->kblocks->nr_squares[b];
1865 if (squares == 1) {
1866 int v = usage->kclues[b];
1867 if (v < 1 || v > cr) {
1868 diff = DIFF_IMPOSSIBLE;
1869 goto got_result;
1870 }
1871 x = usage->kblocks->blocks[b][0] % cr;
1872 y = usage->kblocks->blocks[b][0] / cr;
1873 if (!cube(x, y, v)) {
1874 diff = DIFF_IMPOSSIBLE;
1875 goto got_result;
1876 }
1877 solver_place(usage, x, y, v);
1878
1879#ifdef STANDALONE_SOLVER
1880 if (solver_show_working) {
1881 printf("%*s placing %d at (%d,%d)\n",
1882 solver_recurse_depth*4, "killer single-square cage",
1883 v, 1 + x%cr, 1 + x/cr);
1884 }
1885#endif
1886 changed = TRUE;
1887 }
1888 }
1889
1890 if (changed) {
1891 kdiff = max(kdiff, DIFF_KSINGLE);
1892 goto cont;
1893 }
1894 }
1895 if (dlev->maxkdiff >= DIFF_KINTERSECT && usage->kclues != NULL) {
1896 int changed = FALSE;
1897 /*
1898 * Now, create the extra_cages information. Every full region
1899 * (row, column, or block) has the same sum total (45 for 3x3
1900 * puzzles. After we try to cover these regions with cages that
1901 * lie entirely within them, any squares that remain must bring
1902 * the total to this known value, and so they form additional
1903 * cages which aren't immediately evident in the displayed form
1904 * of the puzzle.
1905 */
1906 usage->extra_cages->nr_blocks = 0;
1907 for (i = 0; i < 3; i++) {
1908 for (n = 0; n < cr; n++) {
1909 int *region = usage->regions + cr*n*3 + i*cr;
1910 int sum = cr * (cr + 1) / 2;
1911 int nsquares = cr;
1912 int filtered;
1913 int n_extra = usage->extra_cages->nr_blocks;
1914 int *extra_list = usage->extra_cages->blocks[n_extra];
1915 memcpy(extra_list, region, cr * sizeof *extra_list);
1916
1917 nsquares = filter_whole_cages(usage, extra_list, nsquares, &filtered);
1918 sum -= filtered;
1919 if (nsquares == cr || nsquares == 0)
1920 continue;
1921 if (dlev->maxdiff >= DIFF_RECURSIVE) {
1922 if (sum <= 0) {
1923 dlev->diff = DIFF_IMPOSSIBLE;
1924 goto got_result;
1925 }
1926 }
1927 assert(sum > 0);
1928
1929 if (nsquares == 1) {
1930 if (sum > cr) {
1931 diff = DIFF_IMPOSSIBLE;
1932 goto got_result;
1933 }
1934 x = extra_list[0] % cr;
1935 y = extra_list[0] / cr;
1936 if (!cube(x, y, sum)) {
1937 diff = DIFF_IMPOSSIBLE;
1938 goto got_result;
1939 }
1940 solver_place(usage, x, y, sum);
1941 changed = TRUE;
1942#ifdef STANDALONE_SOLVER
1943 if (solver_show_working) {
1944 printf("%*s placing %d at (%d,%d)\n",
1945 solver_recurse_depth*4, "killer single-square deduced cage",
1946 sum, 1 + x, 1 + y);
1947 }
1948#endif
1949 }
1950
1951 b = usage->kblocks->whichblock[extra_list[0]];
1952 for (x = 1; x < nsquares; x++)
1953 if (usage->kblocks->whichblock[extra_list[x]] != b)
1954 break;
1955 if (x == nsquares) {
1956 assert(usage->kblocks->nr_squares[b] > nsquares);
1957 split_block(usage->kblocks, extra_list, nsquares);
1958 assert(usage->kblocks->nr_squares[usage->kblocks->nr_blocks - 1] == nsquares);
1959 usage->kclues[usage->kblocks->nr_blocks - 1] = sum;
1960 usage->kclues[b] -= sum;
1961 } else {
1962 usage->extra_cages->nr_squares[n_extra] = nsquares;
1963 usage->extra_cages->nr_blocks++;
1964 usage->extra_clues[n_extra] = sum;
1965 }
1966 }
1967 }
1968 if (changed) {
1969 kdiff = max(kdiff, DIFF_KINTERSECT);
1970 goto cont;
1971 }
1972 }
1973
1974 /*
1975 * Another simple killer-type elimination. For every square in a
1976 * cage, find the minimum and maximum possible sums of all the
1977 * other squares in the same cage, and rule out possibilities
1978 * for the given square based on whether they are guaranteed to
1979 * cause the sum to be either too high or too low.
1980 * This is a special case of trying all possible sums across a
1981 * region, which is a recursive algorithm. We should probably
1982 * implement it for a higher difficulty level.
1983 */
1984 if (dlev->maxkdiff >= DIFF_KMINMAX && usage->kclues != NULL) {
1985 int changed = FALSE;
1986 for (b = 0; b < usage->kblocks->nr_blocks; b++) {
1987 int ret = solver_killer_minmax(usage, usage->kblocks,
1988 usage->kclues, b
1989#ifdef STANDALONE_SOLVER
1990 , ""
1991#endif
1992 );
1993 if (ret < 0) {
1994 diff = DIFF_IMPOSSIBLE;
1995 goto got_result;
1996 } else if (ret > 0)
1997 changed = TRUE;
1998 }
1999 for (b = 0; b < usage->extra_cages->nr_blocks; b++) {
2000 int ret = solver_killer_minmax(usage, usage->extra_cages,
2001 usage->extra_clues, b
2002#ifdef STANDALONE_SOLVER
2003 , "using deduced cages"
2004#endif
2005 );
2006 if (ret < 0) {
2007 diff = DIFF_IMPOSSIBLE;
2008 goto got_result;
2009 } else if (ret > 0)
2010 changed = TRUE;
2011 }
2012 if (changed) {
2013 kdiff = max(kdiff, DIFF_KMINMAX);
2014 goto cont;
2015 }
2016 }
2017
2018 /*
2019 * Try to use knowledge of which numbers can be used to generate
2020 * a given sum.
2021 * This can only be used if a cage lies entirely within a region.
2022 */
2023 if (dlev->maxkdiff >= DIFF_KSUMS && usage->kclues != NULL) {
2024 int changed = FALSE;
2025
2026 for (b = 0; b < usage->kblocks->nr_blocks; b++) {
2027 int ret = solver_killer_sums(usage, b, usage->kblocks,
2028 usage->kclues[b], TRUE
2029#ifdef STANDALONE_SOLVER
2030 , "regular clues"
2031#endif
2032 );
2033 if (ret > 0) {
2034 changed = TRUE;
2035 kdiff = max(kdiff, DIFF_KSUMS);
2036 } else if (ret < 0) {
2037 diff = DIFF_IMPOSSIBLE;
2038 goto got_result;
2039 }
2040 }
2041
2042 for (b = 0; b < usage->extra_cages->nr_blocks; b++) {
2043 int ret = solver_killer_sums(usage, b, usage->extra_cages,
2044 usage->extra_clues[b], FALSE
2045#ifdef STANDALONE_SOLVER
2046 , "deduced clues"
2047#endif
2048 );
2049 if (ret > 0) {
2050 changed = TRUE;
2051 kdiff = max(kdiff, DIFF_KSUMS);
2052 } else if (ret < 0) {
2053 diff = DIFF_IMPOSSIBLE;
2054 goto got_result;
2055 }
2056 }
2057
2058 if (changed)
2059 goto cont;
2060 }
2061
2062 if (dlev->maxdiff <= DIFF_BLOCK)
2063 break;
2064
2065 /*
2066 * Row-wise positional elimination.
2067 */
2068 for (y = 0; y < cr; y++)
2069 for (n = 1; n <= cr; n++)
2070 if (!usage->row[y*cr+n-1]) {
2071 for (x = 0; x < cr; x++)
2072 scratch->indexlist[x] = cubepos(x, y, n);
2073 ret = solver_elim(usage, scratch->indexlist
2074#ifdef STANDALONE_SOLVER
2075 , "positional elimination,"
2076 " %d in row %d", n, 1+y
2077#endif
2078 );
2079 if (ret < 0) {
2080 diff = DIFF_IMPOSSIBLE;
2081 goto got_result;
2082 } else if (ret > 0) {
2083 diff = max(diff, DIFF_SIMPLE);
2084 goto cont;
2085 }
2086 }
2087 /*
2088 * Column-wise positional elimination.
2089 */
2090 for (x = 0; x < cr; x++)
2091 for (n = 1; n <= cr; n++)
2092 if (!usage->col[x*cr+n-1]) {
2093 for (y = 0; y < cr; y++)
2094 scratch->indexlist[y] = cubepos(x, y, n);
2095 ret = solver_elim(usage, scratch->indexlist
2096#ifdef STANDALONE_SOLVER
2097 , "positional elimination,"
2098 " %d in column %d", n, 1+x
2099#endif
2100 );
2101 if (ret < 0) {
2102 diff = DIFF_IMPOSSIBLE;
2103 goto got_result;
2104 } else if (ret > 0) {
2105 diff = max(diff, DIFF_SIMPLE);
2106 goto cont;
2107 }
2108 }
2109
2110 /*
2111 * X-diagonal positional elimination.
2112 */
2113 if (usage->diag) {
2114 for (n = 1; n <= cr; n++)
2115 if (!usage->diag[n-1]) {
2116 for (i = 0; i < cr; i++)
2117 scratch->indexlist[i] = cubepos2(diag0(i), n);
2118 ret = solver_elim(usage, scratch->indexlist
2119#ifdef STANDALONE_SOLVER
2120 , "positional elimination,"
2121 " %d in \\-diagonal", n
2122#endif
2123 );
2124 if (ret < 0) {
2125 diff = DIFF_IMPOSSIBLE;
2126 goto got_result;
2127 } else if (ret > 0) {
2128 diff = max(diff, DIFF_SIMPLE);
2129 goto cont;
2130 }
2131 }
2132 for (n = 1; n <= cr; n++)
2133 if (!usage->diag[cr+n-1]) {
2134 for (i = 0; i < cr; i++)
2135 scratch->indexlist[i] = cubepos2(diag1(i), n);
2136 ret = solver_elim(usage, scratch->indexlist
2137#ifdef STANDALONE_SOLVER
2138 , "positional elimination,"
2139 " %d in /-diagonal", n
2140#endif
2141 );
2142 if (ret < 0) {
2143 diff = DIFF_IMPOSSIBLE;
2144 goto got_result;
2145 } else if (ret > 0) {
2146 diff = max(diff, DIFF_SIMPLE);
2147 goto cont;
2148 }
2149 }
2150 }
2151
2152 /*
2153 * Numeric elimination.
2154 */
2155 for (x = 0; x < cr; x++)
2156 for (y = 0; y < cr; y++)
2157 if (!usage->grid[y*cr+x]) {
2158 for (n = 1; n <= cr; n++)
2159 scratch->indexlist[n-1] = cubepos(x, y, n);
2160 ret = solver_elim(usage, scratch->indexlist
2161#ifdef STANDALONE_SOLVER
2162 , "numeric elimination at (%d,%d)",
2163 1+x, 1+y
2164#endif
2165 );
2166 if (ret < 0) {
2167 diff = DIFF_IMPOSSIBLE;
2168 goto got_result;
2169 } else if (ret > 0) {
2170 diff = max(diff, DIFF_SIMPLE);
2171 goto cont;
2172 }
2173 }
2174
2175 if (dlev->maxdiff <= DIFF_SIMPLE)
2176 break;
2177
2178 /*
2179 * Intersectional analysis, rows vs blocks.
2180 */
2181 for (y = 0; y < cr; y++)
2182 for (b = 0; b < cr; b++)
2183 for (n = 1; n <= cr; n++) {
2184 if (usage->row[y*cr+n-1] ||
2185 usage->blk[b*cr+n-1])
2186 continue;
2187 for (i = 0; i < cr; i++) {
2188 scratch->indexlist[i] = cubepos(i, y, n);
2189 scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
2190 }
2191 /*
2192 * solver_intersect() never returns -1.
2193 */
2194 if (solver_intersect(usage, scratch->indexlist,
2195 scratch->indexlist2
2196#ifdef STANDALONE_SOLVER
2197 , "intersectional analysis,"
2198 " %d in row %d vs block %s",
2199 n, 1+y, usage->blocks->blocknames[b]
2200#endif
2201 ) ||
2202 solver_intersect(usage, scratch->indexlist2,
2203 scratch->indexlist
2204#ifdef STANDALONE_SOLVER
2205 , "intersectional analysis,"
2206 " %d in block %s vs row %d",
2207 n, usage->blocks->blocknames[b], 1+y
2208#endif
2209 )) {
2210 diff = max(diff, DIFF_INTERSECT);
2211 goto cont;
2212 }
2213 }
2214
2215 /*
2216 * Intersectional analysis, columns vs blocks.
2217 */
2218 for (x = 0; x < cr; x++)
2219 for (b = 0; b < cr; b++)
2220 for (n = 1; n <= cr; n++) {
2221 if (usage->col[x*cr+n-1] ||
2222 usage->blk[b*cr+n-1])
2223 continue;
2224 for (i = 0; i < cr; i++) {
2225 scratch->indexlist[i] = cubepos(x, i, n);
2226 scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
2227 }
2228 if (solver_intersect(usage, scratch->indexlist,
2229 scratch->indexlist2
2230#ifdef STANDALONE_SOLVER
2231 , "intersectional analysis,"
2232 " %d in column %d vs block %s",
2233 n, 1+x, usage->blocks->blocknames[b]
2234#endif
2235 ) ||
2236 solver_intersect(usage, scratch->indexlist2,
2237 scratch->indexlist
2238#ifdef STANDALONE_SOLVER
2239 , "intersectional analysis,"
2240 " %d in block %s vs column %d",
2241 n, usage->blocks->blocknames[b], 1+x
2242#endif
2243 )) {
2244 diff = max(diff, DIFF_INTERSECT);
2245 goto cont;
2246 }
2247 }
2248
2249 if (usage->diag) {
2250 /*
2251 * Intersectional analysis, \-diagonal vs blocks.
2252 */
2253 for (b = 0; b < cr; b++)
2254 for (n = 1; n <= cr; n++) {
2255 if (usage->diag[n-1] ||
2256 usage->blk[b*cr+n-1])
2257 continue;
2258 for (i = 0; i < cr; i++) {
2259 scratch->indexlist[i] = cubepos2(diag0(i), n);
2260 scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
2261 }
2262 if (solver_intersect(usage, scratch->indexlist,
2263 scratch->indexlist2
2264#ifdef STANDALONE_SOLVER
2265 , "intersectional analysis,"
2266 " %d in \\-diagonal vs block %s",
2267 n, usage->blocks->blocknames[b]
2268#endif
2269 ) ||
2270 solver_intersect(usage, scratch->indexlist2,
2271 scratch->indexlist
2272#ifdef STANDALONE_SOLVER
2273 , "intersectional analysis,"
2274 " %d in block %s vs \\-diagonal",
2275 n, usage->blocks->blocknames[b]
2276#endif
2277 )) {
2278 diff = max(diff, DIFF_INTERSECT);
2279 goto cont;
2280 }
2281 }
2282
2283 /*
2284 * Intersectional analysis, /-diagonal vs blocks.
2285 */
2286 for (b = 0; b < cr; b++)
2287 for (n = 1; n <= cr; n++) {
2288 if (usage->diag[cr+n-1] ||
2289 usage->blk[b*cr+n-1])
2290 continue;
2291 for (i = 0; i < cr; i++) {
2292 scratch->indexlist[i] = cubepos2(diag1(i), n);
2293 scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n);
2294 }
2295 if (solver_intersect(usage, scratch->indexlist,
2296 scratch->indexlist2
2297#ifdef STANDALONE_SOLVER
2298 , "intersectional analysis,"
2299 " %d in /-diagonal vs block %s",
2300 n, usage->blocks->blocknames[b]
2301#endif
2302 ) ||
2303 solver_intersect(usage, scratch->indexlist2,
2304 scratch->indexlist
2305#ifdef STANDALONE_SOLVER
2306 , "intersectional analysis,"
2307 " %d in block %s vs /-diagonal",
2308 n, usage->blocks->blocknames[b]
2309#endif
2310 )) {
2311 diff = max(diff, DIFF_INTERSECT);
2312 goto cont;
2313 }
2314 }
2315 }
2316
2317 if (dlev->maxdiff <= DIFF_INTERSECT)
2318 break;
2319
2320 /*
2321 * Blockwise set elimination.
2322 */
2323 for (b = 0; b < cr; b++) {
2324 for (i = 0; i < cr; i++)
2325 for (n = 1; n <= cr; n++)
2326 scratch->indexlist[i*cr+n-1] = cubepos2(usage->blocks->blocks[b][i], n);
2327 ret = solver_set(usage, scratch, scratch->indexlist
2328#ifdef STANDALONE_SOLVER
2329 , "set elimination, block %s",
2330 usage->blocks->blocknames[b]
2331#endif
2332 );
2333 if (ret < 0) {
2334 diff = DIFF_IMPOSSIBLE;
2335 goto got_result;
2336 } else if (ret > 0) {
2337 diff = max(diff, DIFF_SET);
2338 goto cont;
2339 }
2340 }
2341
2342 /*
2343 * Row-wise set elimination.
2344 */
2345 for (y = 0; y < cr; y++) {
2346 for (x = 0; x < cr; x++)
2347 for (n = 1; n <= cr; n++)
2348 scratch->indexlist[x*cr+n-1] = cubepos(x, y, n);
2349 ret = solver_set(usage, scratch, scratch->indexlist
2350#ifdef STANDALONE_SOLVER
2351 , "set elimination, row %d", 1+y
2352#endif
2353 );
2354 if (ret < 0) {
2355 diff = DIFF_IMPOSSIBLE;
2356 goto got_result;
2357 } else if (ret > 0) {
2358 diff = max(diff, DIFF_SET);
2359 goto cont;
2360 }
2361 }
2362
2363 /*
2364 * Column-wise set elimination.
2365 */
2366 for (x = 0; x < cr; x++) {
2367 for (y = 0; y < cr; y++)
2368 for (n = 1; n <= cr; n++)
2369 scratch->indexlist[y*cr+n-1] = cubepos(x, y, n);
2370 ret = solver_set(usage, scratch, scratch->indexlist
2371#ifdef STANDALONE_SOLVER
2372 , "set elimination, column %d", 1+x
2373#endif
2374 );
2375 if (ret < 0) {
2376 diff = DIFF_IMPOSSIBLE;
2377 goto got_result;
2378 } else if (ret > 0) {
2379 diff = max(diff, DIFF_SET);
2380 goto cont;
2381 }
2382 }
2383
2384 if (usage->diag) {
2385 /*
2386 * \-diagonal set elimination.
2387 */
2388 for (i = 0; i < cr; i++)
2389 for (n = 1; n <= cr; n++)
2390 scratch->indexlist[i*cr+n-1] = cubepos2(diag0(i), n);
2391 ret = solver_set(usage, scratch, scratch->indexlist
2392#ifdef STANDALONE_SOLVER
2393 , "set elimination, \\-diagonal"
2394#endif
2395 );
2396 if (ret < 0) {
2397 diff = DIFF_IMPOSSIBLE;
2398 goto got_result;
2399 } else if (ret > 0) {
2400 diff = max(diff, DIFF_SET);
2401 goto cont;
2402 }
2403
2404 /*
2405 * /-diagonal set elimination.
2406 */
2407 for (i = 0; i < cr; i++)
2408 for (n = 1; n <= cr; n++)
2409 scratch->indexlist[i*cr+n-1] = cubepos2(diag1(i), n);
2410 ret = solver_set(usage, scratch, scratch->indexlist
2411#ifdef STANDALONE_SOLVER
2412 , "set elimination, /-diagonal"
2413#endif
2414 );
2415 if (ret < 0) {
2416 diff = DIFF_IMPOSSIBLE;
2417 goto got_result;
2418 } else if (ret > 0) {
2419 diff = max(diff, DIFF_SET);
2420 goto cont;
2421 }
2422 }
2423
2424 if (dlev->maxdiff <= DIFF_SET)
2425 break;
2426
2427 /*
2428 * Row-vs-column set elimination on a single number.
2429 */
2430 for (n = 1; n <= cr; n++) {
2431 for (y = 0; y < cr; y++)
2432 for (x = 0; x < cr; x++)
2433 scratch->indexlist[y*cr+x] = cubepos(x, y, n);
2434 ret = solver_set(usage, scratch, scratch->indexlist
2435#ifdef STANDALONE_SOLVER
2436 , "positional set elimination, number %d", n
2437#endif
2438 );
2439 if (ret < 0) {
2440 diff = DIFF_IMPOSSIBLE;
2441 goto got_result;
2442 } else if (ret > 0) {
2443 diff = max(diff, DIFF_EXTREME);
2444 goto cont;
2445 }
2446 }
2447
2448 /*
2449 * Forcing chains.
2450 */
2451 if (solver_forcing(usage, scratch)) {
2452 diff = max(diff, DIFF_EXTREME);
2453 goto cont;
2454 }
2455
2456 /*
2457 * If we reach here, we have made no deductions in this
2458 * iteration, so the algorithm terminates.
2459 */
2460 break;
2461 }
2462
2463 /*
2464 * Last chance: if we haven't fully solved the puzzle yet, try
2465 * recursing based on guesses for a particular square. We pick
2466 * one of the most constrained empty squares we can find, which
2467 * has the effect of pruning the search tree as much as
2468 * possible.
2469 */
2470 if (dlev->maxdiff >= DIFF_RECURSIVE) {
2471 int best, bestcount;
2472
2473 best = -1;
2474 bestcount = cr+1;
2475
2476 for (y = 0; y < cr; y++)
2477 for (x = 0; x < cr; x++)
2478 if (!grid[y*cr+x]) {
2479 int count;
2480
2481 /*
2482 * An unfilled square. Count the number of
2483 * possible digits in it.
2484 */
2485 count = 0;
2486 for (n = 1; n <= cr; n++)
2487 if (cube(x,y,n))
2488 count++;
2489
2490 /*
2491 * We should have found any impossibilities
2492 * already, so this can safely be an assert.
2493 */
2494 assert(count > 1);
2495
2496 if (count < bestcount) {
2497 bestcount = count;
2498 best = y*cr+x;
2499 }
2500 }
2501
2502 if (best != -1) {
2503 int i, j;
2504 digit *list, *ingrid, *outgrid;
2505
2506 diff = DIFF_IMPOSSIBLE; /* no solution found yet */
2507
2508 /*
2509 * Attempt recursion.
2510 */
2511 y = best / cr;
2512 x = best % cr;
2513
2514 list = snewn(cr, digit);
2515 ingrid = snewn(cr * cr, digit);
2516 outgrid = snewn(cr * cr, digit);
2517 memcpy(ingrid, grid, cr * cr);
2518
2519 /* Make a list of the possible digits. */
2520 for (j = 0, n = 1; n <= cr; n++)
2521 if (cube(x,y,n))
2522 list[j++] = n;
2523
2524#ifdef STANDALONE_SOLVER
2525 if (solver_show_working) {
2526 char *sep = "";
2527 printf("%*srecursing on (%d,%d) [",
2528 solver_recurse_depth*4, "", x + 1, y + 1);
2529 for (i = 0; i < j; i++) {
2530 printf("%s%d", sep, list[i]);
2531 sep = " or ";
2532 }
2533 printf("]\n");
2534 }
2535#endif
2536
2537 /*
2538 * And step along the list, recursing back into the
2539 * main solver at every stage.
2540 */
2541 for (i = 0; i < j; i++) {
2542 memcpy(outgrid, ingrid, cr * cr);
2543 outgrid[y*cr+x] = list[i];
2544
2545#ifdef STANDALONE_SOLVER
2546 if (solver_show_working)
2547 printf("%*sguessing %d at (%d,%d)\n",
2548 solver_recurse_depth*4, "", list[i], x + 1, y + 1);
2549 solver_recurse_depth++;
2550#endif
2551
2552 solver(cr, blocks, kblocks, xtype, outgrid, kgrid, dlev);
2553
2554#ifdef STANDALONE_SOLVER
2555 solver_recurse_depth--;
2556 if (solver_show_working) {
2557 printf("%*sretracting %d at (%d,%d)\n",
2558 solver_recurse_depth*4, "", list[i], x + 1, y + 1);
2559 }
2560#endif
2561
2562 /*
2563 * If we have our first solution, copy it into the
2564 * grid we will return.
2565 */
2566 if (diff == DIFF_IMPOSSIBLE && dlev->diff != DIFF_IMPOSSIBLE)
2567 memcpy(grid, outgrid, cr*cr);
2568
2569 if (dlev->diff == DIFF_AMBIGUOUS)
2570 diff = DIFF_AMBIGUOUS;
2571 else if (dlev->diff == DIFF_IMPOSSIBLE)
2572 /* do not change our return value */;
2573 else {
2574 /* the recursion turned up exactly one solution */
2575 if (diff == DIFF_IMPOSSIBLE)
2576 diff = DIFF_RECURSIVE;
2577 else
2578 diff = DIFF_AMBIGUOUS;
2579 }
2580
2581 /*
2582 * As soon as we've found more than one solution,
2583 * give up immediately.
2584 */
2585 if (diff == DIFF_AMBIGUOUS)
2586 break;
2587 }
2588
2589 sfree(outgrid);
2590 sfree(ingrid);
2591 sfree(list);
2592 }
2593
2594 } else {
2595 /*
2596 * We're forbidden to use recursion, so we just see whether
2597 * our grid is fully solved, and return DIFF_IMPOSSIBLE
2598 * otherwise.
2599 */
2600 for (y = 0; y < cr; y++)
2601 for (x = 0; x < cr; x++)
2602 if (!grid[y*cr+x])
2603 diff = DIFF_IMPOSSIBLE;
2604 }
2605
2606 got_result:
2607 dlev->diff = diff;
2608 dlev->kdiff = kdiff;
2609
2610#ifdef STANDALONE_SOLVER
2611 if (solver_show_working)
2612 printf("%*s%s found\n",
2613 solver_recurse_depth*4, "",
2614 diff == DIFF_IMPOSSIBLE ? "no solution" :
2615 diff == DIFF_AMBIGUOUS ? "multiple solutions" :
2616 "one solution");
2617#endif
2618
2619 sfree(usage->sq2region);
2620 sfree(usage->regions);
2621 sfree(usage->cube);
2622 sfree(usage->row);
2623 sfree(usage->col);
2624 sfree(usage->blk);
2625 if (usage->kblocks) {
2626 free_block_structure(usage->kblocks);
2627 free_block_structure(usage->extra_cages);
2628 sfree(usage->extra_clues);
2629 }
2630 if (usage->kclues) sfree(usage->kclues);
2631 sfree(usage);
2632
2633 solver_free_scratch(scratch);
2634}
2635
2636/* ----------------------------------------------------------------------
2637 * End of solver code.
2638 */
2639
2640/* ----------------------------------------------------------------------
2641 * Killer set generator.
2642 */
2643
2644/* ----------------------------------------------------------------------
2645 * Solo filled-grid generator.
2646 *
2647 * This grid generator works by essentially trying to solve a grid
2648 * starting from no clues, and not worrying that there's more than
2649 * one possible solution. Unfortunately, it isn't computationally
2650 * feasible to do this by calling the above solver with an empty
2651 * grid, because that one needs to allocate a lot of scratch space
2652 * at every recursion level. Instead, I have a much simpler
2653 * algorithm which I shamelessly copied from a Python solver
2654 * written by Andrew Wilkinson (which is GPLed, but I've reused
2655 * only ideas and no code). It mostly just does the obvious
2656 * recursive thing: pick an empty square, put one of the possible
2657 * digits in it, recurse until all squares are filled, backtrack
2658 * and change some choices if necessary.
2659 *
2660 * The clever bit is that every time it chooses which square to
2661 * fill in next, it does so by counting the number of _possible_
2662 * numbers that can go in each square, and it prioritises so that
2663 * it picks a square with the _lowest_ number of possibilities. The
2664 * idea is that filling in lots of the obvious bits (particularly
2665 * any squares with only one possibility) will cut down on the list
2666 * of possibilities for other squares and hence reduce the enormous
2667 * search space as much as possible as early as possible.
2668 *
2669 * The use of bit sets implies that we support puzzles up to a size of
2670 * 32x32 (less if anyone finds a 16-bit machine to compile this on).
2671 */
2672
2673/*
2674 * Internal data structure used in gridgen to keep track of
2675 * progress.
2676 */
2677struct gridgen_coord { int x, y, r; };
2678struct gridgen_usage {
2679 int cr;
2680 struct block_structure *blocks, *kblocks;
2681 /* grid is a copy of the input grid, modified as we go along */
2682 digit *grid;
2683 /*
2684 * Bitsets. In each of them, bit n is set if digit n has been placed
2685 * in the corresponding region. row, col and blk are used for all
2686 * puzzles. cge is used only for killer puzzles, and diag is used
2687 * only for x-type puzzles.
2688 * All of these have cr entries, except diag which only has 2,
2689 * and cge, which has as many entries as kblocks.
2690 */
2691 unsigned int *row, *col, *blk, *cge, *diag;
2692 /* This lists all the empty spaces remaining in the grid. */
2693 struct gridgen_coord *spaces;
2694 int nspaces;
2695 /* If we need randomisation in the solve, this is our random state. */
2696 random_state *rs;
2697};
2698
2699static void gridgen_place(struct gridgen_usage *usage, int x, int y, digit n)
2700{
2701 unsigned int bit = 1 << n;
2702 int cr = usage->cr;
2703 usage->row[y] |= bit;
2704 usage->col[x] |= bit;
2705 usage->blk[usage->blocks->whichblock[y*cr+x]] |= bit;
2706 if (usage->cge)
2707 usage->cge[usage->kblocks->whichblock[y*cr+x]] |= bit;
2708 if (usage->diag) {
2709 if (ondiag0(y*cr+x))
2710 usage->diag[0] |= bit;
2711 if (ondiag1(y*cr+x))
2712 usage->diag[1] |= bit;
2713 }
2714 usage->grid[y*cr+x] = n;
2715}
2716
2717static void gridgen_remove(struct gridgen_usage *usage, int x, int y, digit n)
2718{
2719 unsigned int mask = ~(1 << n);
2720 int cr = usage->cr;
2721 usage->row[y] &= mask;
2722 usage->col[x] &= mask;
2723 usage->blk[usage->blocks->whichblock[y*cr+x]] &= mask;
2724 if (usage->cge)
2725 usage->cge[usage->kblocks->whichblock[y*cr+x]] &= mask;
2726 if (usage->diag) {
2727 if (ondiag0(y*cr+x))
2728 usage->diag[0] &= mask;
2729 if (ondiag1(y*cr+x))
2730 usage->diag[1] &= mask;
2731 }
2732 usage->grid[y*cr+x] = 0;
2733}
2734
2735#define N_SINGLE 32
2736
2737/*
2738 * The real recursive step in the generating function.
2739 *
2740 * Return values: 1 means solution found, 0 means no solution
2741 * found on this branch.
2742 */
2743static int gridgen_real(struct gridgen_usage *usage, digit *grid, int *steps)
2744{
2745 int cr = usage->cr;
2746 int i, j, n, sx, sy, bestm, bestr, ret;
2747 int *digits;
2748 unsigned int used;
2749
2750 /*
2751 * Firstly, check for completion! If there are no spaces left
2752 * in the grid, we have a solution.
2753 */
2754 if (usage->nspaces == 0)
2755 return TRUE;
2756
2757 /*
2758 * Next, abandon generation if we went over our steps limit.
2759 */
2760 if (*steps <= 0)
2761 return FALSE;
2762 (*steps)--;
2763
2764 /*
2765 * Otherwise, there must be at least one space. Find the most
2766 * constrained space, using the `r' field as a tie-breaker.
2767 */
2768 bestm = cr+1; /* so that any space will beat it */
2769 bestr = 0;
2770 used = ~0;
2771 i = sx = sy = -1;
2772 for (j = 0; j < usage->nspaces; j++) {
2773 int x = usage->spaces[j].x, y = usage->spaces[j].y;
2774 unsigned int used_xy;
2775 int m;
2776
2777 m = usage->blocks->whichblock[y*cr+x];
2778 used_xy = usage->row[y] | usage->col[x] | usage->blk[m];
2779 if (usage->cge != NULL)
2780 used_xy |= usage->cge[usage->kblocks->whichblock[y*cr+x]];
2781 if (usage->cge != NULL)
2782 used_xy |= usage->cge[usage->kblocks->whichblock[y*cr+x]];
2783 if (usage->diag != NULL) {
2784 if (ondiag0(y*cr+x))
2785 used_xy |= usage->diag[0];
2786 if (ondiag1(y*cr+x))
2787 used_xy |= usage->diag[1];
2788 }
2789
2790 /*
2791 * Find the number of digits that could go in this space.
2792 */
2793 m = 0;
2794 for (n = 1; n <= cr; n++) {
2795 unsigned int bit = 1 << n;
2796 if ((used_xy & bit) == 0)
2797 m++;
2798 }
2799 if (m < bestm || (m == bestm && usage->spaces[j].r < bestr)) {
2800 bestm = m;
2801 bestr = usage->spaces[j].r;
2802 sx = x;
2803 sy = y;
2804 i = j;
2805 used = used_xy;
2806 }
2807 }
2808
2809 /*
2810 * Swap that square into the final place in the spaces array,
2811 * so that decrementing nspaces will remove it from the list.
2812 */
2813 if (i != usage->nspaces-1) {
2814 struct gridgen_coord t;
2815 t = usage->spaces[usage->nspaces-1];
2816 usage->spaces[usage->nspaces-1] = usage->spaces[i];
2817 usage->spaces[i] = t;
2818 }
2819
2820 /*
2821 * Now we've decided which square to start our recursion at,
2822 * simply go through all possible values, shuffling them
2823 * randomly first if necessary.
2824 */
2825 digits = snewn(bestm, int);
2826
2827 j = 0;
2828 for (n = 1; n <= cr; n++) {
2829 unsigned int bit = 1 << n;
2830
2831 if ((used & bit) == 0)
2832 digits[j++] = n;
2833 }
2834
2835 if (usage->rs)
2836 shuffle(digits, j, sizeof(*digits), usage->rs);
2837
2838 /* And finally, go through the digit list and actually recurse. */
2839 ret = FALSE;
2840 for (i = 0; i < j; i++) {
2841 n = digits[i];
2842
2843 /* Update the usage structure to reflect the placing of this digit. */
2844 gridgen_place(usage, sx, sy, n);
2845 usage->nspaces--;
2846
2847 /* Call the solver recursively. Stop when we find a solution. */
2848 if (gridgen_real(usage, grid, steps)) {
2849 ret = TRUE;
2850 break;
2851 }
2852
2853 /* Revert the usage structure. */
2854 gridgen_remove(usage, sx, sy, n);
2855 usage->nspaces++;
2856 }
2857
2858 sfree(digits);
2859 return ret;
2860}
2861
2862/*
2863 * Entry point to generator. You give it parameters and a starting
2864 * grid, which is simply an array of cr*cr digits.
2865 */
2866static int gridgen(int cr, struct block_structure *blocks,
2867 struct block_structure *kblocks, int xtype,
2868 digit *grid, random_state *rs, int maxsteps)
2869{
2870 struct gridgen_usage *usage;
2871 int x, y, ret;
2872
2873 /*
2874 * Clear the grid to start with.
2875 */
2876 memset(grid, 0, cr*cr);
2877
2878 /*
2879 * Create a gridgen_usage structure.
2880 */
2881 usage = snew(struct gridgen_usage);
2882
2883 usage->cr = cr;
2884 usage->blocks = blocks;
2885
2886 usage->grid = grid;
2887
2888 usage->row = snewn(cr, unsigned int);
2889 usage->col = snewn(cr, unsigned int);
2890 usage->blk = snewn(cr, unsigned int);
2891 if (kblocks != NULL) {
2892 usage->kblocks = kblocks;
2893 usage->cge = snewn(usage->kblocks->nr_blocks, unsigned int);
2894 memset(usage->cge, FALSE, kblocks->nr_blocks * sizeof *usage->cge);
2895 } else {
2896 usage->cge = NULL;
2897 }
2898
2899 memset(usage->row, 0, cr * sizeof *usage->row);
2900 memset(usage->col, 0, cr * sizeof *usage->col);
2901 memset(usage->blk, 0, cr * sizeof *usage->blk);
2902
2903 if (xtype) {
2904 usage->diag = snewn(2, unsigned int);
2905 memset(usage->diag, 0, 2 * sizeof *usage->diag);
2906 } else {
2907 usage->diag = NULL;
2908 }
2909
2910 /*
2911 * Begin by filling in the whole top row with randomly chosen
2912 * numbers. This cannot introduce any bias or restriction on
2913 * the available grids, since we already know those numbers
2914 * are all distinct so all we're doing is choosing their
2915 * labels.
2916 */
2917 for (x = 0; x < cr; x++)
2918 grid[x] = x+1;
2919 shuffle(grid, cr, sizeof(*grid), rs);
2920 for (x = 0; x < cr; x++)
2921 gridgen_place(usage, x, 0, grid[x]);
2922
2923 usage->spaces = snewn(cr * cr, struct gridgen_coord);
2924 usage->nspaces = 0;
2925
2926 usage->rs = rs;
2927
2928 /*
2929 * Initialise the list of grid spaces, taking care to leave
2930 * out the row I've already filled in above.
2931 */
2932 for (y = 1; y < cr; y++) {
2933 for (x = 0; x < cr; x++) {
2934 usage->spaces[usage->nspaces].x = x;
2935 usage->spaces[usage->nspaces].y = y;
2936 usage->spaces[usage->nspaces].r = random_bits(rs, 31);
2937 usage->nspaces++;
2938 }
2939 }
2940
2941 /*
2942 * Run the real generator function.
2943 */
2944 ret = gridgen_real(usage, grid, &maxsteps);
2945
2946 /*
2947 * Clean up the usage structure now we have our answer.
2948 */
2949 sfree(usage->spaces);
2950 sfree(usage->cge);
2951 sfree(usage->blk);
2952 sfree(usage->col);
2953 sfree(usage->row);
2954 sfree(usage);
2955
2956 return ret;
2957}
2958
2959/* ----------------------------------------------------------------------
2960 * End of grid generator code.
2961 */
2962
2963static int check_killer_cage_sum(struct block_structure *kblocks,
2964 digit *kgrid, digit *grid, int blk)
2965{
2966 /*
2967 * Returns: -1 if the cage has any empty square; 0 if all squares
2968 * are full but the sum is wrong; +1 if all squares are full and
2969 * they have the right sum.
2970 *
2971 * Does not check uniqueness of numbers within the cage; that's
2972 * done elsewhere (because in error highlighting it needs to be
2973 * detected separately so as to flag the error in a visually
2974 * different way).
2975 */
2976 int n_squares = kblocks->nr_squares[blk];
2977 int sum = 0, clue = 0;
2978 int i;
2979
2980 for (i = 0; i < n_squares; i++) {
2981 int xy = kblocks->blocks[blk][i];
2982
2983 if (grid[xy] == 0)
2984 return -1;
2985 sum += grid[xy];
2986
2987 if (kgrid[xy]) {
2988 assert(clue == 0);
2989 clue = kgrid[xy];
2990 }
2991 }
2992
2993 assert(clue != 0);
2994 return sum == clue;
2995}
2996
2997/*
2998 * Check whether a grid contains a valid complete puzzle.
2999 */
3000static int check_valid(int cr, struct block_structure *blocks,
3001 struct block_structure *kblocks,
3002 digit *kgrid, int xtype, digit *grid)
3003{
3004 unsigned char *used;
3005 int x, y, i, j, n;
3006
3007 used = snewn(cr, unsigned char);
3008
3009 /*
3010 * Check that each row contains precisely one of everything.
3011 */
3012 for (y = 0; y < cr; y++) {
3013 memset(used, FALSE, cr);
3014 for (x = 0; x < cr; x++)
3015 if (grid[y*cr+x] > 0 && grid[y*cr+x] <= cr)
3016 used[grid[y*cr+x]-1] = TRUE;
3017 for (n = 0; n < cr; n++)
3018 if (!used[n]) {
3019 sfree(used);
3020 return FALSE;
3021 }
3022 }
3023
3024 /*
3025 * Check that each column contains precisely one of everything.
3026 */
3027 for (x = 0; x < cr; x++) {
3028 memset(used, FALSE, cr);
3029 for (y = 0; y < cr; y++)
3030 if (grid[y*cr+x] > 0 && grid[y*cr+x] <= cr)
3031 used[grid[y*cr+x]-1] = TRUE;
3032 for (n = 0; n < cr; n++)
3033 if (!used[n]) {
3034 sfree(used);
3035 return FALSE;
3036 }
3037 }
3038
3039 /*
3040 * Check that each block contains precisely one of everything.
3041 */
3042 for (i = 0; i < cr; i++) {
3043 memset(used, FALSE, cr);
3044 for (j = 0; j < cr; j++)
3045 if (grid[blocks->blocks[i][j]] > 0 &&
3046 grid[blocks->blocks[i][j]] <= cr)
3047 used[grid[blocks->blocks[i][j]]-1] = TRUE;
3048 for (n = 0; n < cr; n++)
3049 if (!used[n]) {
3050 sfree(used);
3051 return FALSE;
3052 }
3053 }
3054
3055 /*
3056 * Check that each Killer cage, if any, contains at most one of
3057 * everything. If we also know the clues for those cages (which we
3058 * might not, when this function is called early in puzzle
3059 * generation), we also check that they all have the right sum.
3060 */
3061 if (kblocks) {
3062 for (i = 0; i < kblocks->nr_blocks; i++) {
3063 memset(used, FALSE, cr);
3064 for (j = 0; j < kblocks->nr_squares[i]; j++)
3065 if (grid[kblocks->blocks[i][j]] > 0 &&
3066 grid[kblocks->blocks[i][j]] <= cr) {
3067 if (used[grid[kblocks->blocks[i][j]]-1]) {
3068 sfree(used);
3069 return FALSE;
3070 }
3071 used[grid[kblocks->blocks[i][j]]-1] = TRUE;
3072 }
3073
3074 if (kgrid && check_killer_cage_sum(kblocks, kgrid, grid, i) != 1) {
3075 sfree(used);
3076 return FALSE;
3077 }
3078 }
3079 }
3080
3081 /*
3082 * Check that each diagonal contains precisely one of everything.
3083 */
3084 if (xtype) {
3085 memset(used, FALSE, cr);
3086 for (i = 0; i < cr; i++)
3087 if (grid[diag0(i)] > 0 && grid[diag0(i)] <= cr)
3088 used[grid[diag0(i)]-1] = TRUE;
3089 for (n = 0; n < cr; n++)
3090 if (!used[n]) {
3091 sfree(used);
3092 return FALSE;
3093 }
3094 for (i = 0; i < cr; i++)
3095 if (grid[diag1(i)] > 0 && grid[diag1(i)] <= cr)
3096 used[grid[diag1(i)]-1] = TRUE;
3097 for (n = 0; n < cr; n++)
3098 if (!used[n]) {
3099 sfree(used);
3100 return FALSE;
3101 }
3102 }
3103
3104 sfree(used);
3105 return TRUE;
3106}
3107
3108static int symmetries(const game_params *params, int x, int y,
3109 int *output, int s)
3110{
3111 int c = params->c, r = params->r, cr = c*r;
3112 int i = 0;
3113
3114#define ADD(x,y) (*output++ = (x), *output++ = (y), i++)
3115
3116 ADD(x, y);
3117
3118 switch (s) {
3119 case SYMM_NONE:
3120 break; /* just x,y is all we need */
3121 case SYMM_ROT2:
3122 ADD(cr - 1 - x, cr - 1 - y);
3123 break;
3124 case SYMM_ROT4:
3125 ADD(cr - 1 - y, x);
3126 ADD(y, cr - 1 - x);
3127 ADD(cr - 1 - x, cr - 1 - y);
3128 break;
3129 case SYMM_REF2:
3130 ADD(cr - 1 - x, y);
3131 break;
3132 case SYMM_REF2D:
3133 ADD(y, x);
3134 break;
3135 case SYMM_REF4:
3136 ADD(cr - 1 - x, y);
3137 ADD(x, cr - 1 - y);
3138 ADD(cr - 1 - x, cr - 1 - y);
3139 break;
3140 case SYMM_REF4D:
3141 ADD(y, x);
3142 ADD(cr - 1 - x, cr - 1 - y);
3143 ADD(cr - 1 - y, cr - 1 - x);
3144 break;
3145 case SYMM_REF8:
3146 ADD(cr - 1 - x, y);
3147 ADD(x, cr - 1 - y);
3148 ADD(cr - 1 - x, cr - 1 - y);
3149 ADD(y, x);
3150 ADD(y, cr - 1 - x);
3151 ADD(cr - 1 - y, x);
3152 ADD(cr - 1 - y, cr - 1 - x);
3153 break;
3154 }
3155
3156#undef ADD
3157
3158 return i;
3159}
3160
3161static char *encode_solve_move(int cr, digit *grid)
3162{
3163 int i, len;
3164 char *ret, *p, *sep;
3165
3166 /*
3167 * It's surprisingly easy to work out _exactly_ how long this
3168 * string needs to be. To decimal-encode all the numbers from 1
3169 * to n:
3170 *
3171 * - every number has a units digit; total is n.
3172 * - all numbers above 9 have a tens digit; total is max(n-9,0).
3173 * - all numbers above 99 have a hundreds digit; total is max(n-99,0).
3174 * - and so on.
3175 */
3176 len = 0;
3177 for (i = 1; i <= cr; i *= 10)
3178 len += max(cr - i + 1, 0);
3179 len += cr; /* don't forget the commas */
3180 len *= cr; /* there are cr rows of these */
3181
3182 /*
3183 * Now len is one bigger than the total size of the
3184 * comma-separated numbers (because we counted an
3185 * additional leading comma). We need to have a leading S
3186 * and a trailing NUL, so we're off by one in total.
3187 */
3188 len++;
3189
3190 ret = snewn(len, char);
3191 p = ret;
3192 *p++ = 'S';
3193 sep = "";
3194 for (i = 0; i < cr*cr; i++) {
3195 p += sprintf(p, "%s%d", sep, grid[i]);
3196 sep = ",";
3197 }
3198 *p++ = '\0';
3199 assert(p - ret == len);
3200
3201 return ret;
3202}
3203
3204static void dsf_to_blocks(int *dsf, struct block_structure *blocks,
3205 int min_expected, int max_expected)
3206{
3207 int cr = blocks->c * blocks->r, area = cr * cr;
3208 int i, nb = 0;
3209
3210 for (i = 0; i < area; i++)
3211 blocks->whichblock[i] = -1;
3212 for (i = 0; i < area; i++) {
3213 int j = dsf_canonify(dsf, i);
3214 if (blocks->whichblock[j] < 0)
3215 blocks->whichblock[j] = nb++;
3216 blocks->whichblock[i] = blocks->whichblock[j];
3217 }
3218 assert(nb >= min_expected && nb <= max_expected);
3219 blocks->nr_blocks = nb;
3220}
3221
3222static void make_blocks_from_whichblock(struct block_structure *blocks)
3223{
3224 int i;
3225
3226 for (i = 0; i < blocks->nr_blocks; i++) {
3227 blocks->blocks[i][blocks->max_nr_squares-1] = 0;
3228 blocks->nr_squares[i] = 0;
3229 }
3230 for (i = 0; i < blocks->area; i++) {
3231 int b = blocks->whichblock[i];
3232 int j = blocks->blocks[b][blocks->max_nr_squares-1]++;
3233 assert(j < blocks->max_nr_squares);
3234 blocks->blocks[b][j] = i;
3235 blocks->nr_squares[b]++;
3236 }
3237}
3238
3239static char *encode_block_structure_desc(char *p, struct block_structure *blocks)
3240{
3241 int i, currrun = 0;
3242 int c = blocks->c, r = blocks->r, cr = c * r;
3243
3244 /*
3245 * Encode the block structure. We do this by encoding
3246 * the pattern of dividing lines: first we iterate
3247 * over the cr*(cr-1) internal vertical grid lines in
3248 * ordinary reading order, then over the cr*(cr-1)
3249 * internal horizontal ones in transposed reading
3250 * order.
3251 *
3252 * We encode the number of non-lines between the
3253 * lines; _ means zero (two adjacent divisions), a
3254 * means 1, ..., y means 25, and z means 25 non-lines
3255 * _and no following line_ (so that za means 26, zb 27
3256 * etc).
3257 */
3258 for (i = 0; i <= 2*cr*(cr-1); i++) {
3259 int x, y, p0, p1, edge;
3260
3261 if (i == 2*cr*(cr-1)) {
3262 edge = TRUE; /* terminating virtual edge */
3263 } else {
3264 if (i < cr*(cr-1)) {
3265 y = i/(cr-1);
3266 x = i%(cr-1);
3267 p0 = y*cr+x;
3268 p1 = y*cr+x+1;
3269 } else {
3270 x = i/(cr-1) - cr;
3271 y = i%(cr-1);
3272 p0 = y*cr+x;
3273 p1 = (y+1)*cr+x;
3274 }
3275 edge = (blocks->whichblock[p0] != blocks->whichblock[p1]);
3276 }
3277
3278 if (edge) {
3279 while (currrun > 25)
3280 *p++ = 'z', currrun -= 25;
3281 if (currrun)
3282 *p++ = 'a'-1 + currrun;
3283 else
3284 *p++ = '_';
3285 currrun = 0;
3286 } else
3287 currrun++;
3288 }
3289 return p;
3290}
3291
3292static char *encode_grid(char *desc, digit *grid, int area)
3293{
3294 int run, i;
3295 char *p = desc;
3296
3297 run = 0;
3298 for (i = 0; i <= area; i++) {
3299 int n = (i < area ? grid[i] : -1);
3300
3301 if (!n)
3302 run++;
3303 else {
3304 if (run) {
3305 while (run > 0) {
3306 int c = 'a' - 1 + run;
3307 if (run > 26)
3308 c = 'z';
3309 *p++ = c;
3310 run -= c - ('a' - 1);
3311 }
3312 } else {
3313 /*
3314 * If there's a number in the very top left or
3315 * bottom right, there's no point putting an
3316 * unnecessary _ before or after it.
3317 */
3318 if (p > desc && n > 0)
3319 *p++ = '_';
3320 }
3321 if (n > 0)
3322 p += sprintf(p, "%d", n);
3323 run = 0;
3324 }
3325 }
3326 return p;
3327}
3328
3329/*
3330 * Conservatively stimate the number of characters required for
3331 * encoding a grid of a certain area.
3332 */
3333static int grid_encode_space (int area)
3334{
3335 int t, count;
3336 for (count = 1, t = area; t > 26; t -= 26)
3337 count++;
3338 return count * area;
3339}
3340
3341/*
3342 * Conservatively stimate the number of characters required for
3343 * encoding a given blocks structure.
3344 */
3345static int blocks_encode_space(struct block_structure *blocks)
3346{
3347 int cr = blocks->c * blocks->r, area = cr * cr;
3348 return grid_encode_space(area);
3349}
3350
3351static char *encode_puzzle_desc(const game_params *params, digit *grid,
3352 struct block_structure *blocks,
3353 digit *kgrid,
3354 struct block_structure *kblocks)
3355{
3356 int c = params->c, r = params->r, cr = c*r;
3357 int area = cr*cr;
3358 char *p, *desc;
3359 int space;
3360
3361 space = grid_encode_space(area) + 1;
3362 if (r == 1)
3363 space += blocks_encode_space(blocks) + 1;
3364 if (params->killer) {
3365 space += blocks_encode_space(kblocks) + 1;
3366 space += grid_encode_space(area) + 1;
3367 }
3368 desc = snewn(space, char);
3369 p = encode_grid(desc, grid, area);
3370
3371 if (r == 1) {
3372 *p++ = ',';
3373 p = encode_block_structure_desc(p, blocks);
3374 }
3375 if (params->killer) {
3376 *p++ = ',';
3377 p = encode_block_structure_desc(p, kblocks);
3378 *p++ = ',';
3379 p = encode_grid(p, kgrid, area);
3380 }
3381 assert(p - desc < space);
3382 *p++ = '\0';
3383 desc = sresize(desc, p - desc, char);
3384
3385 return desc;
3386}
3387
3388static void merge_blocks(struct block_structure *b, int n1, int n2)
3389{
3390 int i;
3391 /* Move data towards the lower block number. */
3392 if (n2 < n1) {
3393 int t = n2;
3394 n2 = n1;
3395 n1 = t;
3396 }
3397
3398 /* Merge n2 into n1, and move the last block into n2's position. */
3399 for (i = 0; i < b->nr_squares[n2]; i++)
3400 b->whichblock[b->blocks[n2][i]] = n1;
3401 memcpy(b->blocks[n1] + b->nr_squares[n1], b->blocks[n2],
3402 b->nr_squares[n2] * sizeof **b->blocks);
3403 b->nr_squares[n1] += b->nr_squares[n2];
3404
3405 n1 = b->nr_blocks - 1;
3406 if (n2 != n1) {
3407 memcpy(b->blocks[n2], b->blocks[n1],
3408 b->nr_squares[n1] * sizeof **b->blocks);
3409 for (i = 0; i < b->nr_squares[n1]; i++)
3410 b->whichblock[b->blocks[n1][i]] = n2;
3411 b->nr_squares[n2] = b->nr_squares[n1];
3412 }
3413 b->nr_blocks = n1;
3414}
3415
3416static int merge_some_cages(struct block_structure *b, int cr, int area,
3417 digit *grid, random_state *rs)
3418{
3419 /*
3420 * Make a list of all the pairs of adjacent blocks.
3421 */
3422 int i, j, k;
3423 struct pair {
3424 int b1, b2;
3425 } *pairs;
3426 int npairs;
3427
3428 pairs = snewn(b->nr_blocks * b->nr_blocks, struct pair);
3429 npairs = 0;
3430
3431 for (i = 0; i < b->nr_blocks; i++) {
3432 for (j = i+1; j < b->nr_blocks; j++) {
3433
3434 /*
3435 * Rule the merger out of consideration if it's
3436 * obviously not viable.
3437 */
3438 if (b->nr_squares[i] + b->nr_squares[j] > b->max_nr_squares)
3439 continue; /* we couldn't merge these anyway */
3440
3441 /*
3442 * See if these two blocks have a pair of squares
3443 * adjacent to each other.
3444 */
3445 for (k = 0; k < b->nr_squares[i]; k++) {
3446 int xy = b->blocks[i][k];
3447 int y = xy / cr, x = xy % cr;
3448 if ((y > 0 && b->whichblock[xy - cr] == j) ||
3449 (y+1 < cr && b->whichblock[xy + cr] == j) ||
3450 (x > 0 && b->whichblock[xy - 1] == j) ||
3451 (x+1 < cr && b->whichblock[xy + 1] == j)) {
3452 /*
3453 * Yes! Add this pair to our list.
3454 */
3455 pairs[npairs].b1 = i;
3456 pairs[npairs].b2 = j;
3457 break;
3458 }
3459 }
3460 }
3461 }
3462
3463 /*
3464 * Now go through that list in random order until we find a pair
3465 * of blocks we can merge.
3466 */
3467 while (npairs > 0) {
3468 int n1, n2;
3469 unsigned int digits_found;
3470
3471 /*
3472 * Pick a random pair, and remove it from the list.
3473 */
3474 i = random_upto(rs, npairs);
3475 n1 = pairs[i].b1;
3476 n2 = pairs[i].b2;
3477 if (i != npairs-1)
3478 pairs[i] = pairs[npairs-1];
3479 npairs--;
3480
3481 /* Guarantee that the merged cage would still be a region. */
3482 digits_found = 0;
3483 for (i = 0; i < b->nr_squares[n1]; i++)
3484 digits_found |= 1 << grid[b->blocks[n1][i]];
3485 for (i = 0; i < b->nr_squares[n2]; i++)
3486 if (digits_found & (1 << grid[b->blocks[n2][i]]))
3487 break;
3488 if (i != b->nr_squares[n2])
3489 continue;
3490
3491 /*
3492 * Got one! Do the merge.
3493 */
3494 merge_blocks(b, n1, n2);
3495 sfree(pairs);
3496 return TRUE;
3497 }
3498
3499 sfree(pairs);
3500 return FALSE;
3501}
3502
3503static void compute_kclues(struct block_structure *cages, digit *kclues,
3504 digit *grid, int area)
3505{
3506 int i;
3507 memset(kclues, 0, area * sizeof *kclues);
3508 for (i = 0; i < cages->nr_blocks; i++) {
3509 int j, sum = 0;
3510 for (j = 0; j < area; j++)
3511 if (cages->whichblock[j] == i)
3512 sum += grid[j];
3513 for (j = 0; j < area; j++)
3514 if (cages->whichblock[j] == i)
3515 break;
3516 assert (j != area);
3517 kclues[j] = sum;
3518 }
3519}
3520
3521static struct block_structure *gen_killer_cages(int cr, random_state *rs,
3522 int remove_singletons)
3523{
3524 int nr;
3525 int x, y, area = cr * cr;
3526 int n_singletons = 0;
3527 struct block_structure *b = alloc_block_structure (1, cr, area, cr, area);
3528
3529 for (x = 0; x < area; x++)
3530 b->whichblock[x] = -1;
3531 nr = 0;
3532 for (y = 0; y < cr; y++)
3533 for (x = 0; x < cr; x++) {
3534 int rnd;
3535 int xy = y*cr+x;
3536 if (b->whichblock[xy] != -1)
3537 continue;
3538 b->whichblock[xy] = nr;
3539
3540 rnd = random_bits(rs, 4);
3541 if (xy + 1 < area && (rnd >= 4 || (!remove_singletons && rnd >= 1))) {
3542 int xy2 = xy + 1;
3543 if (x + 1 == cr || b->whichblock[xy2] != -1 ||
3544 (xy + cr < area && random_bits(rs, 1) == 0))
3545 xy2 = xy + cr;
3546 if (xy2 >= area)
3547 n_singletons++;
3548 else
3549 b->whichblock[xy2] = nr;
3550 } else
3551 n_singletons++;
3552 nr++;
3553 }
3554
3555 b->nr_blocks = nr;
3556 make_blocks_from_whichblock(b);
3557
3558 for (x = y = 0; x < b->nr_blocks; x++)
3559 if (b->nr_squares[x] == 1)
3560 y++;
3561 assert(y == n_singletons);
3562
3563 if (n_singletons > 0 && remove_singletons) {
3564 int n;
3565 for (n = 0; n < b->nr_blocks;) {
3566 int xy, x, y, xy2, other;
3567 if (b->nr_squares[n] > 1) {
3568 n++;
3569 continue;
3570 }
3571 xy = b->blocks[n][0];
3572 x = xy % cr;
3573 y = xy / cr;
3574 if (xy + 1 == area)
3575 xy2 = xy - 1;
3576 else if (x + 1 < cr && (y + 1 == cr || random_bits(rs, 1) == 0))
3577 xy2 = xy + 1;
3578 else
3579 xy2 = xy + cr;
3580 other = b->whichblock[xy2];
3581
3582 if (b->nr_squares[other] == 1)
3583 n_singletons--;
3584 n_singletons--;
3585 merge_blocks(b, n, other);
3586 if (n < other)
3587 n++;
3588 }
3589 assert(n_singletons == 0);
3590 }
3591 return b;
3592}
3593
3594static char *new_game_desc(const game_params *params, random_state *rs,
3595 char **aux, int interactive)
3596{
3597 int c = params->c, r = params->r, cr = c*r;
3598 int area = cr*cr;
3599 struct block_structure *blocks, *kblocks;
3600 digit *grid, *grid2, *kgrid;
3601 struct xy { int x, y; } *locs;
3602 int nlocs;
3603 char *desc;
3604 int coords[16], ncoords;
3605 int x, y, i, j;
3606 struct difficulty dlev;
3607
3608 precompute_sum_bits();
3609
3610 /*
3611 * Adjust the maximum difficulty level to be consistent with
3612 * the puzzle size: all 2x2 puzzles appear to be Trivial
3613 * (DIFF_BLOCK) so we cannot hold out for even a Basic
3614 * (DIFF_SIMPLE) one.
3615 */
3616 dlev.maxdiff = params->diff;
3617 dlev.maxkdiff = params->kdiff;
3618 if (c == 2 && r == 2)
3619 dlev.maxdiff = DIFF_BLOCK;
3620
3621 grid = snewn(area, digit);
3622 locs = snewn(area, struct xy);
3623 grid2 = snewn(area, digit);
3624
3625 blocks = alloc_block_structure (c, r, area, cr, cr);
3626
3627 kblocks = NULL;
3628 kgrid = (params->killer) ? snewn(area, digit) : NULL;
3629
3630#ifdef STANDALONE_SOLVER
3631 assert(!"This should never happen, so we don't need to create blocknames");
3632#endif
3633
3634 /*
3635 * Loop until we get a grid of the required difficulty. This is
3636 * nasty, but it seems to be unpleasantly hard to generate
3637 * difficult grids otherwise.
3638 */
3639 while (1) {
3640 /*
3641 * Generate a random solved state, starting by
3642 * constructing the block structure.
3643 */
3644 if (r == 1) { /* jigsaw mode */
3645 int *dsf = divvy_rectangle(cr, cr, cr, rs);
3646
3647 dsf_to_blocks (dsf, blocks, cr, cr);
3648
3649 sfree(dsf);
3650 } else { /* basic Sudoku mode */
3651 for (y = 0; y < cr; y++)
3652 for (x = 0; x < cr; x++)
3653 blocks->whichblock[y*cr+x] = (y/c) * c + (x/r);
3654 }
3655 make_blocks_from_whichblock(blocks);
3656
3657 if (params->killer) {
3658 if (kblocks) free_block_structure(kblocks);
3659 kblocks = gen_killer_cages(cr, rs, params->kdiff > DIFF_KSINGLE);
3660 }
3661
3662 if (!gridgen(cr, blocks, kblocks, params->xtype, grid, rs, area*area))
3663 continue;
3664 assert(check_valid(cr, blocks, kblocks, NULL, params->xtype, grid));
3665
3666 /*
3667 * Save the solved grid in aux.
3668 */
3669 {
3670 /*
3671 * We might already have written *aux the last time we
3672 * went round this loop, in which case we should free
3673 * the old aux before overwriting it with the new one.
3674 */
3675 if (*aux) {
3676 sfree(*aux);
3677 }
3678
3679 *aux = encode_solve_move(cr, grid);
3680 }
3681
3682 /*
3683 * Now we have a solved grid. For normal puzzles, we start removing
3684 * things from it while preserving solubility. Killer puzzles are
3685 * different: we just pass the empty grid to the solver, and use
3686 * the puzzle if it comes back solved.
3687 */
3688
3689 if (params->killer) {
3690 struct block_structure *good_cages = NULL;
3691 struct block_structure *last_cages = NULL;
3692 int ntries = 0;
3693
3694 memcpy(grid2, grid, area);
3695
3696 for (;;) {
3697 compute_kclues(kblocks, kgrid, grid2, area);
3698
3699 memset(grid, 0, area * sizeof *grid);
3700 solver(cr, blocks, kblocks, params->xtype, grid, kgrid, &dlev);
3701 if (dlev.diff == dlev.maxdiff && dlev.kdiff == dlev.maxkdiff) {
3702 /*
3703 * We have one that matches our difficulty. Store it for
3704 * later, but keep going.
3705 */
3706 if (good_cages)
3707 free_block_structure(good_cages);
3708 ntries = 0;
3709 good_cages = dup_block_structure(kblocks);
3710 if (!merge_some_cages(kblocks, cr, area, grid2, rs))
3711 break;
3712 } else if (dlev.diff > dlev.maxdiff || dlev.kdiff > dlev.maxkdiff) {
3713 /*
3714 * Give up after too many tries and either use the good one we
3715 * found, or generate a new grid.
3716 */
3717 if (++ntries > 50)
3718 break;
3719 /*
3720 * The difficulty level got too high. If we have a good
3721 * one, use it, otherwise go back to the last one that
3722 * was at a lower difficulty and restart the process from
3723 * there.
3724 */
3725 if (good_cages != NULL) {
3726 free_block_structure(kblocks);
3727 kblocks = dup_block_structure(good_cages);
3728 if (!merge_some_cages(kblocks, cr, area, grid2, rs))
3729 break;
3730 } else {
3731 if (last_cages == NULL)
3732 break;
3733 free_block_structure(kblocks);
3734 kblocks = last_cages;
3735 last_cages = NULL;
3736 }
3737 } else {
3738 if (last_cages)
3739 free_block_structure(last_cages);
3740 last_cages = dup_block_structure(kblocks);
3741 if (!merge_some_cages(kblocks, cr, area, grid2, rs))
3742 break;
3743 }
3744 }
3745 if (last_cages)
3746 free_block_structure(last_cages);
3747 if (good_cages != NULL) {
3748 free_block_structure(kblocks);
3749 kblocks = good_cages;
3750 compute_kclues(kblocks, kgrid, grid2, area);
3751 memset(grid, 0, area * sizeof *grid);
3752 break;
3753 }
3754 continue;
3755 }
3756
3757 /*
3758 * Find the set of equivalence classes of squares permitted
3759 * by the selected symmetry. We do this by enumerating all
3760 * the grid squares which have no symmetric companion
3761 * sorting lower than themselves.
3762 */
3763 nlocs = 0;
3764 for (y = 0; y < cr; y++)
3765 for (x = 0; x < cr; x++) {
3766 int i = y*cr+x;
3767 int j;
3768
3769 ncoords = symmetries(params, x, y, coords, params->symm);
3770 for (j = 0; j < ncoords; j++)
3771 if (coords[2*j+1]*cr+coords[2*j] < i)
3772 break;
3773 if (j == ncoords) {
3774 locs[nlocs].x = x;
3775 locs[nlocs].y = y;
3776 nlocs++;
3777 }
3778 }
3779
3780 /*
3781 * Now shuffle that list.
3782 */
3783 shuffle(locs, nlocs, sizeof(*locs), rs);
3784
3785 /*
3786 * Now loop over the shuffled list and, for each element,
3787 * see whether removing that element (and its reflections)
3788 * from the grid will still leave the grid soluble.
3789 */
3790 for (i = 0; i < nlocs; i++) {
3791 x = locs[i].x;
3792 y = locs[i].y;
3793
3794 memcpy(grid2, grid, area);
3795 ncoords = symmetries(params, x, y, coords, params->symm);
3796 for (j = 0; j < ncoords; j++)
3797 grid2[coords[2*j+1]*cr+coords[2*j]] = 0;
3798
3799 solver(cr, blocks, kblocks, params->xtype, grid2, kgrid, &dlev);
3800 if (dlev.diff <= dlev.maxdiff &&
3801 (!params->killer || dlev.kdiff <= dlev.maxkdiff)) {
3802 for (j = 0; j < ncoords; j++)
3803 grid[coords[2*j+1]*cr+coords[2*j]] = 0;
3804 }
3805 }
3806
3807 memcpy(grid2, grid, area);
3808
3809 solver(cr, blocks, kblocks, params->xtype, grid2, kgrid, &dlev);
3810 if (dlev.diff == dlev.maxdiff &&
3811 (!params->killer || dlev.kdiff == dlev.maxkdiff))
3812 break; /* found one! */
3813 }
3814
3815 sfree(grid2);
3816 sfree(locs);
3817
3818 /*
3819 * Now we have the grid as it will be presented to the user.
3820 * Encode it in a game desc.
3821 */
3822 desc = encode_puzzle_desc(params, grid, blocks, kgrid, kblocks);
3823
3824 sfree(grid);
3825 free_block_structure(blocks);
3826 if (params->killer) {
3827 free_block_structure(kblocks);
3828 sfree(kgrid);
3829 }
3830
3831 return desc;
3832}
3833
3834static const char *spec_to_grid(const char *desc, digit *grid, int area)
3835{
3836 int i = 0;
3837 while (*desc && *desc != ',') {
3838 int n = *desc++;
3839 if (n >= 'a' && n <= 'z') {
3840 int run = n - 'a' + 1;
3841 assert(i + run <= area);
3842 while (run-- > 0)
3843 grid[i++] = 0;
3844 } else if (n == '_') {
3845 /* do nothing */;
3846 } else if (n > '0' && n <= '9') {
3847 assert(i < area);
3848 grid[i++] = atoi(desc-1);
3849 while (*desc >= '0' && *desc <= '9')
3850 desc++;
3851 } else {
3852 assert(!"We can't get here");
3853 }
3854 }
3855 assert(i == area);
3856 return desc;
3857}
3858
3859/*
3860 * Create a DSF from a spec found in *pdesc. Update this to point past the
3861 * end of the block spec, and return an error string or NULL if everything
3862 * is OK. The DSF is stored in *PDSF.
3863 */
3864static char *spec_to_dsf(const char **pdesc, int **pdsf, int cr, int area)
3865{
3866 const char *desc = *pdesc;
3867 int pos = 0;
3868 int *dsf;
3869
3870 *pdsf = dsf = snew_dsf(area);
3871
3872 while (*desc && *desc != ',') {
3873 int c, adv;
3874
3875 if (*desc == '_')
3876 c = 0;
3877 else if (*desc >= 'a' && *desc <= 'z')
3878 c = *desc - 'a' + 1;
3879 else {
3880 sfree(dsf);
3881 return "Invalid character in game description";
3882 }
3883 desc++;
3884
3885 adv = (c != 26); /* 'z' is a special case */
3886
3887 while (c-- > 0) {
3888 int p0, p1;
3889
3890 /*
3891 * Non-edge; merge the two dsf classes on either
3892 * side of it.
3893 */
3894 if (pos >= 2*cr*(cr-1)) {
3895 sfree(dsf);
3896 return "Too much data in block structure specification";
3897 }
3898
3899 if (pos < cr*(cr-1)) {
3900 int y = pos/(cr-1);
3901 int x = pos%(cr-1);
3902 p0 = y*cr+x;
3903 p1 = y*cr+x+1;
3904 } else {
3905 int x = pos/(cr-1) - cr;
3906 int y = pos%(cr-1);
3907 p0 = y*cr+x;
3908 p1 = (y+1)*cr+x;
3909 }
3910 dsf_merge(dsf, p0, p1);
3911
3912 pos++;
3913 }
3914 if (adv)
3915 pos++;
3916 }
3917 *pdesc = desc;
3918
3919 /*
3920 * When desc is exhausted, we expect to have gone exactly
3921 * one space _past_ the end of the grid, due to the dummy
3922 * edge at the end.
3923 */
3924 if (pos != 2*cr*(cr-1)+1) {
3925 sfree(dsf);
3926 return "Not enough data in block structure specification";
3927 }
3928
3929 return NULL;
3930}
3931
3932static char *validate_grid_desc(const char **pdesc, int range, int area)
3933{
3934 const char *desc = *pdesc;
3935 int squares = 0;
3936 while (*desc && *desc != ',') {
3937 int n = *desc++;
3938 if (n >= 'a' && n <= 'z') {
3939 squares += n - 'a' + 1;
3940 } else if (n == '_') {
3941 /* do nothing */;
3942 } else if (n > '0' && n <= '9') {
3943 int val = atoi(desc-1);
3944 if (val < 1 || val > range)
3945 return "Out-of-range number in game description";
3946 squares++;
3947 while (*desc >= '0' && *desc <= '9')
3948 desc++;
3949 } else
3950 return "Invalid character in game description";
3951 }
3952
3953 if (squares < area)
3954 return "Not enough data to fill grid";
3955
3956 if (squares > area)
3957 return "Too much data to fit in grid";
3958 *pdesc = desc;
3959 return NULL;
3960}
3961
3962static char *validate_block_desc(const char **pdesc, int cr, int area,
3963 int min_nr_blocks, int max_nr_blocks,
3964 int min_nr_squares, int max_nr_squares)
3965{
3966 char *err;
3967 int *dsf;
3968
3969 err = spec_to_dsf(pdesc, &dsf, cr, area);
3970 if (err) {
3971 return err;
3972 }
3973
3974 if (min_nr_squares == max_nr_squares) {
3975 assert(min_nr_blocks == max_nr_blocks);
3976 assert(min_nr_blocks * min_nr_squares == area);
3977 }
3978 /*
3979 * Now we've got our dsf. Verify that it matches
3980 * expectations.
3981 */
3982 {
3983 int *canons, *counts;
3984 int i, j, c, ncanons = 0;
3985
3986 canons = snewn(max_nr_blocks, int);
3987 counts = snewn(max_nr_blocks, int);
3988
3989 for (i = 0; i < area; i++) {
3990 j = dsf_canonify(dsf, i);
3991
3992 for (c = 0; c < ncanons; c++)
3993 if (canons[c] == j) {
3994 counts[c]++;
3995 if (counts[c] > max_nr_squares) {
3996 sfree(dsf);
3997 sfree(canons);
3998 sfree(counts);
3999 return "A jigsaw block is too big";
4000 }
4001 break;
4002 }
4003
4004 if (c == ncanons) {
4005 if (ncanons >= max_nr_blocks) {
4006 sfree(dsf);
4007 sfree(canons);
4008 sfree(counts);
4009 return "Too many distinct jigsaw blocks";
4010 }
4011 canons[ncanons] = j;
4012 counts[ncanons] = 1;
4013 ncanons++;
4014 }
4015 }
4016
4017 if (ncanons < min_nr_blocks) {
4018 sfree(dsf);
4019 sfree(canons);
4020 sfree(counts);
4021 return "Not enough distinct jigsaw blocks";
4022 }
4023 for (c = 0; c < ncanons; c++) {
4024 if (counts[c] < min_nr_squares) {
4025 sfree(dsf);
4026 sfree(canons);
4027 sfree(counts);
4028 return "A jigsaw block is too small";
4029 }
4030 }
4031 sfree(canons);
4032 sfree(counts);
4033 }
4034
4035 sfree(dsf);
4036 return NULL;
4037}
4038
4039static char *validate_desc(const game_params *params, const char *desc)
4040{
4041 int cr = params->c * params->r, area = cr*cr;
4042 char *err;
4043
4044 err = validate_grid_desc(&desc, cr, area);
4045 if (err)
4046 return err;
4047
4048 if (params->r == 1) {
4049 /*
4050 * Now we expect a suffix giving the jigsaw block
4051 * structure. Parse it and validate that it divides the
4052 * grid into the right number of regions which are the
4053 * right size.
4054 */
4055 if (*desc != ',')
4056 return "Expected jigsaw block structure in game description";
4057 desc++;
4058 err = validate_block_desc(&desc, cr, area, cr, cr, cr, cr);
4059 if (err)
4060 return err;
4061
4062 }
4063 if (params->killer) {
4064 if (*desc != ',')
4065 return "Expected killer block structure in game description";
4066 desc++;
4067 err = validate_block_desc(&desc, cr, area, cr, area, 2, cr);
4068 if (err)
4069 return err;
4070 if (*desc != ',')
4071 return "Expected killer clue grid in game description";
4072 desc++;
4073 err = validate_grid_desc(&desc, cr * area, area);
4074 if (err)
4075 return err;
4076 }
4077 if (*desc)
4078 return "Unexpected data at end of game description";
4079
4080 return NULL;
4081}
4082
4083static game_state *new_game(midend *me, const game_params *params,
4084 const char *desc)
4085{
4086 game_state *state = snew(game_state);
4087 int c = params->c, r = params->r, cr = c*r, area = cr * cr;
4088 int i;
4089
4090 precompute_sum_bits();
4091
4092 state->cr = cr;
4093 state->xtype = params->xtype;
4094 state->killer = params->killer;
4095
4096 state->grid = snewn(area, digit);
4097 state->pencil = snewn(area * cr, unsigned char);
4098 memset(state->pencil, 0, area * cr);
4099 state->immutable = snewn(area, unsigned char);
4100 memset(state->immutable, FALSE, area);
4101
4102 state->blocks = alloc_block_structure (c, r, area, cr, cr);
4103
4104 if (params->killer) {
4105 state->kblocks = alloc_block_structure (c, r, area, cr, area);
4106 state->kgrid = snewn(area, digit);
4107 } else {
4108 state->kblocks = NULL;
4109 state->kgrid = NULL;
4110 }
4111 state->completed = state->cheated = FALSE;
4112
4113 desc = spec_to_grid(desc, state->grid, area);
4114 for (i = 0; i < area; i++)
4115 if (state->grid[i] != 0)
4116 state->immutable[i] = TRUE;
4117
4118 if (r == 1) {
4119 char *err;
4120 int *dsf;
4121 assert(*desc == ',');
4122 desc++;
4123 err = spec_to_dsf(&desc, &dsf, cr, area);
4124 assert(err == NULL);
4125 dsf_to_blocks(dsf, state->blocks, cr, cr);
4126 sfree(dsf);
4127 } else {
4128 int x, y;
4129
4130 for (y = 0; y < cr; y++)
4131 for (x = 0; x < cr; x++)
4132 state->blocks->whichblock[y*cr+x] = (y/c) * c + (x/r);
4133 }
4134 make_blocks_from_whichblock(state->blocks);
4135
4136 if (params->killer) {
4137 char *err;
4138 int *dsf;
4139 assert(*desc == ',');
4140 desc++;
4141 err = spec_to_dsf(&desc, &dsf, cr, area);
4142 assert(err == NULL);
4143 dsf_to_blocks(dsf, state->kblocks, cr, area);
4144 sfree(dsf);
4145 make_blocks_from_whichblock(state->kblocks);
4146
4147 assert(*desc == ',');
4148 desc++;
4149 desc = spec_to_grid(desc, state->kgrid, area);
4150 }
4151 assert(!*desc);
4152
4153#ifdef STANDALONE_SOLVER
4154 /*
4155 * Set up the block names for solver diagnostic output.
4156 */
4157 {
4158 char *p = (char *)(state->blocks->blocknames + cr);
4159
4160 if (r == 1) {
4161 for (i = 0; i < area; i++) {
4162 int j = state->blocks->whichblock[i];
4163 if (!state->blocks->blocknames[j]) {
4164 state->blocks->blocknames[j] = p;
4165 p += 1 + sprintf(p, "starting at (%d,%d)",
4166 1 + i%cr, 1 + i/cr);
4167 }
4168 }
4169 } else {
4170 int bx, by;
4171 for (by = 0; by < r; by++)
4172 for (bx = 0; bx < c; bx++) {
4173 state->blocks->blocknames[by*c+bx] = p;
4174 p += 1 + sprintf(p, "(%d,%d)", bx+1, by+1);
4175 }
4176 }
4177 assert(p - (char *)state->blocks->blocknames < (int)(cr*(sizeof(char *)+80)));
4178 for (i = 0; i < cr; i++)
4179 assert(state->blocks->blocknames[i]);
4180 }
4181#endif
4182
4183 return state;
4184}
4185
4186static game_state *dup_game(const game_state *state)
4187{
4188 game_state *ret = snew(game_state);
4189 int cr = state->cr, area = cr * cr;
4190
4191 ret->cr = state->cr;
4192 ret->xtype = state->xtype;
4193 ret->killer = state->killer;
4194
4195 ret->blocks = state->blocks;
4196 ret->blocks->refcount++;
4197
4198 ret->kblocks = state->kblocks;
4199 if (ret->kblocks)
4200 ret->kblocks->refcount++;
4201
4202 ret->grid = snewn(area, digit);
4203 memcpy(ret->grid, state->grid, area);
4204
4205 if (state->killer) {
4206 ret->kgrid = snewn(area, digit);
4207 memcpy(ret->kgrid, state->kgrid, area);
4208 } else
4209 ret->kgrid = NULL;
4210
4211 ret->pencil = snewn(area * cr, unsigned char);
4212 memcpy(ret->pencil, state->pencil, area * cr);
4213
4214 ret->immutable = snewn(area, unsigned char);
4215 memcpy(ret->immutable, state->immutable, area);
4216
4217 ret->completed = state->completed;
4218 ret->cheated = state->cheated;
4219
4220 return ret;
4221}
4222
4223static void free_game(game_state *state)
4224{
4225 free_block_structure(state->blocks);
4226 if (state->kblocks)
4227 free_block_structure(state->kblocks);
4228
4229 sfree(state->immutable);
4230 sfree(state->pencil);
4231 sfree(state->grid);
4232 if (state->kgrid) sfree(state->kgrid);
4233 sfree(state);
4234}
4235
4236static char *solve_game(const game_state *state, const game_state *currstate,
4237 const char *ai, char **error)
4238{
4239 int cr = state->cr;
4240 char *ret;
4241 digit *grid;
4242 struct difficulty dlev;
4243
4244 /*
4245 * If we already have the solution in ai, save ourselves some
4246 * time.
4247 */
4248 if (ai)
4249 return dupstr(ai);
4250
4251 grid = snewn(cr*cr, digit);
4252 memcpy(grid, state->grid, cr*cr);
4253 dlev.maxdiff = DIFF_RECURSIVE;
4254 dlev.maxkdiff = DIFF_KINTERSECT;
4255 solver(cr, state->blocks, state->kblocks, state->xtype, grid,
4256 state->kgrid, &dlev);
4257
4258 *error = NULL;
4259
4260 if (dlev.diff == DIFF_IMPOSSIBLE)
4261 *error = "No solution exists for this puzzle";
4262 else if (dlev.diff == DIFF_AMBIGUOUS)
4263 *error = "Multiple solutions exist for this puzzle";
4264
4265 if (*error) {
4266 sfree(grid);
4267 return NULL;
4268 }
4269
4270 ret = encode_solve_move(cr, grid);
4271
4272 sfree(grid);
4273
4274 return ret;
4275}
4276
4277static char *grid_text_format(int cr, struct block_structure *blocks,
4278 int xtype, digit *grid)
4279{
4280 int vmod, hmod;
4281 int x, y;
4282 int totallen, linelen, nlines;
4283 char *ret, *p, ch;
4284
4285 /*
4286 * For non-jigsaw Sudoku, we format in the way we always have,
4287 * by having the digits unevenly spaced so that the dividing
4288 * lines can fit in:
4289 *
4290 * . . | . .
4291 * . . | . .
4292 * ----+----
4293 * . . | . .
4294 * . . | . .
4295 *
4296 * For jigsaw puzzles, however, we must leave space between
4297 * _all_ pairs of digits for an optional dividing line, so we
4298 * have to move to the rather ugly
4299 *
4300 * . . . .
4301 * ------+------
4302 * . . | . .
4303 * +---+
4304 * . . | . | .
4305 * ------+ |
4306 * . . . | .
4307 *
4308 * We deal with both cases using the same formatting code; we
4309 * simply invent a vmod value such that there's a vertical
4310 * dividing line before column i iff i is divisible by vmod
4311 * (so it's r in the first case and 1 in the second), and hmod
4312 * likewise for horizontal dividing lines.
4313 */
4314
4315 if (blocks->r != 1) {
4316 vmod = blocks->r;
4317 hmod = blocks->c;
4318 } else {
4319 vmod = hmod = 1;
4320 }
4321
4322 /*
4323 * Line length: we have cr digits, each with a space after it,
4324 * and (cr-1)/vmod dividing lines, each with a space after it.
4325 * The final space is replaced by a newline, but that doesn't
4326 * affect the length.
4327 */
4328 linelen = 2*(cr + (cr-1)/vmod);
4329
4330 /*
4331 * Number of lines: we have cr rows of digits, and (cr-1)/hmod
4332 * dividing rows.
4333 */
4334 nlines = cr + (cr-1)/hmod;
4335
4336 /*
4337 * Allocate the space.
4338 */
4339 totallen = linelen * nlines;
4340 ret = snewn(totallen+1, char); /* leave room for terminating NUL */
4341
4342 /*
4343 * Write the text.
4344 */
4345 p = ret;
4346 for (y = 0; y < cr; y++) {
4347 /*
4348 * Row of digits.
4349 */
4350 for (x = 0; x < cr; x++) {
4351 /*
4352 * Digit.
4353 */
4354 digit d = grid[y*cr+x];
4355
4356 if (d == 0) {
4357 /*
4358 * Empty space: we usually write a dot, but we'll
4359 * highlight spaces on the X-diagonals (in X mode)
4360 * by using underscores instead.
4361 */
4362 if (xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x)))
4363 ch = '_';
4364 else
4365 ch = '.';
4366 } else if (d <= 9) {
4367 ch = '0' + d;
4368 } else {
4369 ch = 'a' + d-10;
4370 }
4371
4372 *p++ = ch;
4373 if (x == cr-1) {
4374 *p++ = '\n';
4375 continue;
4376 }
4377 *p++ = ' ';
4378
4379 if ((x+1) % vmod)
4380 continue;
4381
4382 /*
4383 * Optional dividing line.
4384 */
4385 if (blocks->whichblock[y*cr+x] != blocks->whichblock[y*cr+x+1])
4386 ch = '|';
4387 else
4388 ch = ' ';
4389 *p++ = ch;
4390 *p++ = ' ';
4391 }
4392 if (y == cr-1 || (y+1) % hmod)
4393 continue;
4394
4395 /*
4396 * Dividing row.
4397 */
4398 for (x = 0; x < cr; x++) {
4399 int dwid;
4400 int tl, tr, bl, br;
4401
4402 /*
4403 * Division between two squares. This varies
4404 * complicatedly in length.
4405 */
4406 dwid = 2; /* digit and its following space */
4407 if (x == cr-1)
4408 dwid--; /* no following space at end of line */
4409 if (x > 0 && x % vmod == 0)
4410 dwid++; /* preceding space after a divider */
4411
4412 if (blocks->whichblock[y*cr+x] != blocks->whichblock[(y+1)*cr+x])
4413 ch = '-';
4414 else
4415 ch = ' ';
4416
4417 while (dwid-- > 0)
4418 *p++ = ch;
4419
4420 if (x == cr-1) {
4421 *p++ = '\n';
4422 break;
4423 }
4424
4425 if ((x+1) % vmod)
4426 continue;
4427
4428 /*
4429 * Corner square. This is:
4430 * - a space if all four surrounding squares are in
4431 * the same block
4432 * - a vertical line if the two left ones are in one
4433 * block and the two right in another
4434 * - a horizontal line if the two top ones are in one
4435 * block and the two bottom in another
4436 * - a plus sign in all other cases. (If we had a
4437 * richer character set available we could break
4438 * this case up further by doing fun things with
4439 * line-drawing T-pieces.)
4440 */
4441 tl = blocks->whichblock[y*cr+x];
4442 tr = blocks->whichblock[y*cr+x+1];
4443 bl = blocks->whichblock[(y+1)*cr+x];
4444 br = blocks->whichblock[(y+1)*cr+x+1];
4445
4446 if (tl == tr && tr == bl && bl == br)
4447 ch = ' ';
4448 else if (tl == bl && tr == br)
4449 ch = '|';
4450 else if (tl == tr && bl == br)
4451 ch = '-';
4452 else
4453 ch = '+';
4454
4455 *p++ = ch;
4456 }
4457 }
4458
4459 assert(p - ret == totallen);
4460 *p = '\0';
4461 return ret;
4462}
4463
4464static int game_can_format_as_text_now(const game_params *params)
4465{
4466 /*
4467 * Formatting Killer puzzles as text is currently unsupported. I
4468 * can't think of any sensible way of doing it which doesn't
4469 * involve expanding the puzzle to such a large scale as to make
4470 * it unusable.
4471 */
4472 if (params->killer)
4473 return FALSE;
4474 return TRUE;
4475}
4476
4477static char *game_text_format(const game_state *state)
4478{
4479 assert(!state->kblocks);
4480 return grid_text_format(state->cr, state->blocks, state->xtype,
4481 state->grid);
4482}
4483
4484struct game_ui {
4485 /*
4486 * These are the coordinates of the currently highlighted
4487 * square on the grid, if hshow = 1.
4488 */
4489 int hx, hy;
4490 /*
4491 * This indicates whether the current highlight is a
4492 * pencil-mark one or a real one.
4493 */
4494 int hpencil;
4495 /*
4496 * This indicates whether or not we're showing the highlight
4497 * (used to be hx = hy = -1); important so that when we're
4498 * using the cursor keys it doesn't keep coming back at a
4499 * fixed position. When hshow = 1, pressing a valid number
4500 * or letter key or Space will enter that number or letter in the grid.
4501 */
4502 int hshow;
4503 /*
4504 * This indicates whether we're using the highlight as a cursor;
4505 * it means that it doesn't vanish on a keypress, and that it is
4506 * allowed on immutable squares.
4507 */
4508 int hcursor;
4509};
4510
4511static game_ui *new_ui(const game_state *state)
4512{
4513 game_ui *ui = snew(game_ui);
4514
4515 ui->hx = ui->hy = 0;
4516 ui->hpencil = ui->hshow = ui->hcursor = 0;
4517
4518 return ui;
4519}
4520
4521static void free_ui(game_ui *ui)
4522{
4523 sfree(ui);
4524}
4525
4526static char *encode_ui(const game_ui *ui)
4527{
4528 return NULL;
4529}
4530
4531static void decode_ui(game_ui *ui, const char *encoding)
4532{
4533}
4534
4535static void game_changed_state(game_ui *ui, const game_state *oldstate,
4536 const game_state *newstate)
4537{
4538 int cr = newstate->cr;
4539 /*
4540 * We prevent pencil-mode highlighting of a filled square, unless
4541 * we're using the cursor keys. So if the user has just filled in
4542 * a square which we had a pencil-mode highlight in (by Undo, or
4543 * by Redo, or by Solve), then we cancel the highlight.
4544 */
4545 if (ui->hshow && ui->hpencil && !ui->hcursor &&
4546 newstate->grid[ui->hy * cr + ui->hx] != 0) {
4547 ui->hshow = 0;
4548 }
4549}
4550
4551struct game_drawstate {
4552 int started;
4553 int cr, xtype;
4554 int tilesize;
4555 digit *grid;
4556 unsigned char *pencil;
4557 unsigned char *hl;
4558 /* This is scratch space used within a single call to game_redraw. */
4559 int nregions, *entered_items;
4560};
4561
4562static char *interpret_move(const game_state *state, game_ui *ui,
4563 const game_drawstate *ds,
4564 int x, int y, int button)
4565{
4566 int cr = state->cr;
4567 int tx, ty;
4568 char buf[80];
4569
4570 button &= ~MOD_MASK;
4571
4572 tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
4573 ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
4574
4575 if (tx >= 0 && tx < cr && ty >= 0 && ty < cr) {
4576 if (button == LEFT_BUTTON) {
4577 if (state->immutable[ty*cr+tx]) {
4578 ui->hshow = 0;
4579 } else if (tx == ui->hx && ty == ui->hy &&
4580 ui->hshow && ui->hpencil == 0) {
4581 ui->hshow = 0;
4582 } else {
4583 ui->hx = tx;
4584 ui->hy = ty;
4585 ui->hshow = 1;
4586 ui->hpencil = 0;
4587 }
4588 ui->hcursor = 0;
4589 return ""; /* UI activity occurred */
4590 }
4591 if (button == RIGHT_BUTTON) {
4592 /*
4593 * Pencil-mode highlighting for non filled squares.
4594 */
4595 if (state->grid[ty*cr+tx] == 0) {
4596 if (tx == ui->hx && ty == ui->hy &&
4597 ui->hshow && ui->hpencil) {
4598 ui->hshow = 0;
4599 } else {
4600 ui->hpencil = 1;
4601 ui->hx = tx;
4602 ui->hy = ty;
4603 ui->hshow = 1;
4604 }
4605 } else {
4606 ui->hshow = 0;
4607 }
4608 ui->hcursor = 0;
4609 return ""; /* UI activity occurred */
4610 }
4611 }
4612 if (IS_CURSOR_MOVE(button)) {
4613 move_cursor(button, &ui->hx, &ui->hy, cr, cr, 0);
4614 ui->hshow = ui->hcursor = 1;
4615 return "";
4616 }
4617 if (ui->hshow &&
4618 (button == CURSOR_SELECT)) {
4619 ui->hpencil = 1 - ui->hpencil;
4620 ui->hcursor = 1;
4621 return "";
4622 }
4623
4624 if (ui->hshow &&
4625 ((button >= '0' && button <= '9' && button - '0' <= cr) ||
4626 (button >= 'a' && button <= 'z' && button - 'a' + 10 <= cr) ||
4627 (button >= 'A' && button <= 'Z' && button - 'A' + 10 <= cr) ||
4628 button == CURSOR_SELECT2 || button == '\b')) {
4629 int n = button - '0';
4630 if (button >= 'A' && button <= 'Z')
4631 n = button - 'A' + 10;
4632 if (button >= 'a' && button <= 'z')
4633 n = button - 'a' + 10;
4634 if (button == CURSOR_SELECT2 || button == '\b')
4635 n = 0;
4636
4637 /*
4638 * Can't overwrite this square. This can only happen here
4639 * if we're using the cursor keys.
4640 */
4641 if (state->immutable[ui->hy*cr+ui->hx])
4642 return NULL;
4643
4644 /*
4645 * Can't make pencil marks in a filled square. Again, this
4646 * can only become highlighted if we're using cursor keys.
4647 */
4648 if (ui->hpencil && state->grid[ui->hy*cr+ui->hx])
4649 return NULL;
4650
4651 sprintf(buf, "%c%d,%d,%d",
4652 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
4653
4654 if (!ui->hcursor) ui->hshow = 0;
4655
4656 return dupstr(buf);
4657 }
4658
4659 if (button == 'M' || button == 'm')
4660 return dupstr("M");
4661
4662 return NULL;
4663}
4664
4665static game_state *execute_move(const game_state *from, const char *move)
4666{
4667 int cr = from->cr;
4668 game_state *ret;
4669 int x, y, n;
4670
4671 if (move[0] == 'S') {
4672 const char *p;
4673
4674 ret = dup_game(from);
4675 ret->completed = ret->cheated = TRUE;
4676
4677 p = move+1;
4678 for (n = 0; n < cr*cr; n++) {
4679 ret->grid[n] = atoi(p);
4680
4681 if (!*p || ret->grid[n] < 1 || ret->grid[n] > cr) {
4682 free_game(ret);
4683 return NULL;
4684 }
4685
4686 while (*p && isdigit((unsigned char)*p)) p++;
4687 if (*p == ',') p++;
4688 }
4689
4690 return ret;
4691 } else if ((move[0] == 'P' || move[0] == 'R') &&
4692 sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
4693 x >= 0 && x < cr && y >= 0 && y < cr && n >= 0 && n <= cr) {
4694
4695 ret = dup_game(from);
4696 if (move[0] == 'P' && n > 0) {
4697 int index = (y*cr+x) * cr + (n-1);
4698 ret->pencil[index] = !ret->pencil[index];
4699 } else {
4700 ret->grid[y*cr+x] = n;
4701 memset(ret->pencil + (y*cr+x)*cr, 0, cr);
4702
4703 /*
4704 * We've made a real change to the grid. Check to see
4705 * if the game has been completed.
4706 */
4707 if (!ret->completed && check_valid(
4708 cr, ret->blocks, ret->kblocks, ret->kgrid,
4709 ret->xtype, ret->grid)) {
4710 ret->completed = TRUE;
4711 }
4712 }
4713 return ret;
4714 } else if (move[0] == 'M') {
4715 /*
4716 * Fill in absolutely all pencil marks in unfilled squares,
4717 * for those who like to play by the rigorous approach of
4718 * starting off in that state and eliminating things.
4719 */
4720 ret = dup_game(from);
4721 for (y = 0; y < cr; y++) {
4722 for (x = 0; x < cr; x++) {
4723 if (!ret->grid[y*cr+x]) {
4724 memset(ret->pencil + (y*cr+x)*cr, 1, cr);
4725 }
4726 }
4727 }
4728 return ret;
4729 } else
4730 return NULL; /* couldn't parse move string */
4731}
4732
4733/* ----------------------------------------------------------------------
4734 * Drawing routines.
4735 */
4736
4737#define SIZE(cr) ((cr) * TILE_SIZE + 2*BORDER + 1)
4738#define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) )
4739
4740static void game_compute_size(const game_params *params, int tilesize,
4741 int *x, int *y)
4742{
4743 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
4744 struct { int tilesize; } ads, *ds = &ads;
4745 ads.tilesize = tilesize;
4746
4747 *x = SIZE(params->c * params->r);
4748 *y = SIZE(params->c * params->r);
4749}
4750
4751static void game_set_size(drawing *dr, game_drawstate *ds,
4752 const game_params *params, int tilesize)
4753{
4754 ds->tilesize = tilesize;
4755}
4756
4757static float *game_colours(frontend *fe, int *ncolours)
4758{
4759 float *ret = snewn(3 * NCOLOURS, float);
4760
4761 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
4762
4763 ret[COL_XDIAGONALS * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0];
4764 ret[COL_XDIAGONALS * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1];
4765 ret[COL_XDIAGONALS * 3 + 2] = 0.9F * ret[COL_BACKGROUND * 3 + 2];
4766
4767 ret[COL_GRID * 3 + 0] = 0.0F;
4768 ret[COL_GRID * 3 + 1] = 0.0F;
4769 ret[COL_GRID * 3 + 2] = 0.0F;
4770
4771 ret[COL_CLUE * 3 + 0] = 0.0F;
4772 ret[COL_CLUE * 3 + 1] = 0.0F;
4773 ret[COL_CLUE * 3 + 2] = 0.0F;
4774
4775 ret[COL_USER * 3 + 0] = 0.0F;
4776 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
4777 ret[COL_USER * 3 + 2] = 0.0F;
4778
4779 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
4780 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
4781 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
4782
4783 ret[COL_ERROR * 3 + 0] = 1.0F;
4784 ret[COL_ERROR * 3 + 1] = 0.0F;
4785 ret[COL_ERROR * 3 + 2] = 0.0F;
4786
4787 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
4788 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
4789 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
4790
4791 ret[COL_KILLER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
4792 ret[COL_KILLER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
4793 ret[COL_KILLER * 3 + 2] = 0.1F * ret[COL_BACKGROUND * 3 + 2];
4794
4795 *ncolours = NCOLOURS;
4796 return ret;
4797}
4798
4799static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
4800{
4801 struct game_drawstate *ds = snew(struct game_drawstate);
4802 int cr = state->cr;
4803
4804 ds->started = FALSE;
4805 ds->cr = cr;
4806 ds->xtype = state->xtype;
4807 ds->grid = snewn(cr*cr, digit);
4808 memset(ds->grid, cr+2, cr*cr);
4809 ds->pencil = snewn(cr*cr*cr, digit);
4810 memset(ds->pencil, 0, cr*cr*cr);
4811 ds->hl = snewn(cr*cr, unsigned char);
4812 memset(ds->hl, 0, cr*cr);
4813 /*
4814 * ds->entered_items needs one row of cr entries per entity in
4815 * which digits may not be duplicated. That's one for each row,
4816 * each column, each block, each diagonal, and each Killer cage.
4817 */
4818 ds->nregions = cr*3 + 2;
4819 if (state->kblocks)
4820 ds->nregions += state->kblocks->nr_blocks;
4821 ds->entered_items = snewn(cr * ds->nregions, int);
4822 ds->tilesize = 0; /* not decided yet */
4823 return ds;
4824}
4825
4826static void game_free_drawstate(drawing *dr, game_drawstate *ds)
4827{
4828 sfree(ds->hl);
4829 sfree(ds->pencil);
4830 sfree(ds->grid);
4831 sfree(ds->entered_items);
4832 sfree(ds);
4833}
4834
4835static void draw_number(drawing *dr, game_drawstate *ds,
4836 const game_state *state, int x, int y, int hl)
4837{
4838 int cr = state->cr;
4839 int tx, ty, tw, th;
4840 int cx, cy, cw, ch;
4841 int col_killer = (hl & 32 ? COL_ERROR : COL_KILLER);
4842 char str[20];
4843
4844 if (ds->grid[y*cr+x] == state->grid[y*cr+x] &&
4845 ds->hl[y*cr+x] == hl &&
4846 !memcmp(ds->pencil+(y*cr+x)*cr, state->pencil+(y*cr+x)*cr, cr))
4847 return; /* no change required */
4848
4849 tx = BORDER + x * TILE_SIZE + 1 + GRIDEXTRA;
4850 ty = BORDER + y * TILE_SIZE + 1 + GRIDEXTRA;
4851
4852 cx = tx;
4853 cy = ty;
4854 cw = tw = TILE_SIZE-1-2*GRIDEXTRA;
4855 ch = th = TILE_SIZE-1-2*GRIDEXTRA;
4856
4857 if (x > 0 && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[y*cr+x-1])
4858 cx -= GRIDEXTRA, cw += GRIDEXTRA;
4859 if (x+1 < cr && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[y*cr+x+1])
4860 cw += GRIDEXTRA;
4861 if (y > 0 && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[(y-1)*cr+x])
4862 cy -= GRIDEXTRA, ch += GRIDEXTRA;
4863 if (y+1 < cr && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[(y+1)*cr+x])
4864 ch += GRIDEXTRA;
4865
4866 clip(dr, cx, cy, cw, ch);
4867
4868 /* background needs erasing */
4869 draw_rect(dr, cx, cy, cw, ch,
4870 ((hl & 15) == 1 ? COL_HIGHLIGHT :
4871 (ds->xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x))) ? COL_XDIAGONALS :
4872 COL_BACKGROUND));
4873
4874 /*
4875 * Draw the corners of thick lines in corner-adjacent squares,
4876 * which jut into this square by one pixel.
4877 */
4878 if (x > 0 && y > 0 && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y-1)*cr+x-1])
4879 draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
4880 if (x+1 < cr && y > 0 && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y-1)*cr+x+1])
4881 draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
4882 if (x > 0 && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x-1])
4883 draw_rect(dr, tx-GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
4884 if (x+1 < cr && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x+1])
4885 draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID);
4886
4887 /* pencil-mode highlight */
4888 if ((hl & 15) == 2) {
4889 int coords[6];
4890 coords[0] = cx;
4891 coords[1] = cy;
4892 coords[2] = cx+cw/2;
4893 coords[3] = cy;
4894 coords[4] = cx;
4895 coords[5] = cy+ch/2;
4896 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
4897 }
4898
4899 if (state->kblocks) {
4900 int t = GRIDEXTRA * 3;
4901 int kcx, kcy, kcw, kch;
4902 int kl, kt, kr, kb;
4903 int has_left = 0, has_right = 0, has_top = 0, has_bottom = 0;
4904
4905 /*
4906 * In non-jigsaw mode, the Killer cages are placed at a
4907 * fixed offset from the outer edge of the cell dividing
4908 * lines, so that they look right whether those lines are
4909 * thick or thin. In jigsaw mode, however, doing this will
4910 * sometimes cause the cage outlines in adjacent squares to
4911 * fail to match up with each other, so we must offset a
4912 * fixed amount from the _centre_ of the cell dividing
4913 * lines.
4914 */
4915 if (state->blocks->r == 1) {
4916 kcx = tx;
4917 kcy = ty;
4918 kcw = tw;
4919 kch = th;
4920 } else {
4921 kcx = cx;
4922 kcy = cy;
4923 kcw = cw;
4924 kch = ch;
4925 }
4926 kl = kcx - 1;
4927 kt = kcy - 1;
4928 kr = kcx + kcw;
4929 kb = kcy + kch;
4930
4931 /*
4932 * First, draw the lines dividing this area from neighbouring
4933 * different areas.
4934 */
4935 if (x == 0 || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[y*cr+x-1])
4936 has_left = 1, kl += t;
4937 if (x+1 >= cr || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[y*cr+x+1])
4938 has_right = 1, kr -= t;
4939 if (y == 0 || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y-1)*cr+x])
4940 has_top = 1, kt += t;
4941 if (y+1 >= cr || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x])
4942 has_bottom = 1, kb -= t;
4943 if (has_top)
4944 draw_line(dr, kl, kt, kr, kt, col_killer);
4945 if (has_bottom)
4946 draw_line(dr, kl, kb, kr, kb, col_killer);
4947 if (has_left)
4948 draw_line(dr, kl, kt, kl, kb, col_killer);
4949 if (has_right)
4950 draw_line(dr, kr, kt, kr, kb, col_killer);
4951 /*
4952 * Now, take care of the corners (just as for the normal borders).
4953 * We only need a corner if there wasn't a full edge.
4954 */
4955 if (x > 0 && y > 0 && !has_left && !has_top
4956 && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y-1)*cr+x-1])
4957 {
4958 draw_line(dr, kl, kt + t, kl + t, kt + t, col_killer);
4959 draw_line(dr, kl + t, kt, kl + t, kt + t, col_killer);
4960 }
4961 if (x+1 < cr && y > 0 && !has_right && !has_top
4962 && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y-1)*cr+x+1])
4963 {
4964 draw_line(dr, kcx + kcw - t, kt + t, kcx + kcw, kt + t, col_killer);
4965 draw_line(dr, kcx + kcw - t, kt, kcx + kcw - t, kt + t, col_killer);
4966 }
4967 if (x > 0 && y+1 < cr && !has_left && !has_bottom
4968 && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x-1])
4969 {
4970 draw_line(dr, kl, kcy + kch - t, kl + t, kcy + kch - t, col_killer);
4971 draw_line(dr, kl + t, kcy + kch - t, kl + t, kcy + kch, col_killer);
4972 }
4973 if (x+1 < cr && y+1 < cr && !has_right && !has_bottom
4974 && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x+1])
4975 {
4976 draw_line(dr, kcx + kcw - t, kcy + kch - t, kcx + kcw - t, kcy + kch, col_killer);
4977 draw_line(dr, kcx + kcw - t, kcy + kch - t, kcx + kcw, kcy + kch - t, col_killer);
4978 }
4979
4980 }
4981
4982 if (state->killer && state->kgrid[y*cr+x]) {
4983 sprintf (str, "%d", state->kgrid[y*cr+x]);
4984 draw_text(dr, tx + GRIDEXTRA * 4, ty + GRIDEXTRA * 4 + TILE_SIZE/4,
4985 FONT_VARIABLE, TILE_SIZE/4, ALIGN_VNORMAL | ALIGN_HLEFT,
4986 col_killer, str);
4987 }
4988
4989 /* new number needs drawing? */
4990 if (state->grid[y*cr+x]) {
4991 str[1] = '\0';
4992 str[0] = state->grid[y*cr+x] + '0';
4993 if (str[0] > '9')
4994 str[0] += 'a' - ('9'+1);
4995 draw_text(dr, tx + TILE_SIZE/2, ty + TILE_SIZE/2,
4996 FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
4997 state->immutable[y*cr+x] ? COL_CLUE : (hl & 16) ? COL_ERROR : COL_USER, str);
4998 } else {
4999 int i, j, npencil;
5000 int pl, pr, pt, pb;
5001 float bestsize;
5002 int pw, ph, minph, pbest, fontsize;
5003
5004 /* Count the pencil marks required. */
5005 for (i = npencil = 0; i < cr; i++)
5006 if (state->pencil[(y*cr+x)*cr+i])
5007 npencil++;
5008 if (npencil) {
5009
5010 minph = 2;
5011
5012 /*
5013 * Determine the bounding rectangle within which we're going
5014 * to put the pencil marks.
5015 */
5016 /* Start with the whole square */
5017 pl = tx + GRIDEXTRA;
5018 pr = pl + TILE_SIZE - GRIDEXTRA;
5019 pt = ty + GRIDEXTRA;
5020 pb = pt + TILE_SIZE - GRIDEXTRA;
5021 if (state->killer) {
5022 /*
5023 * Make space for the Killer cages. We do this
5024 * unconditionally, for uniformity between squares,
5025 * rather than making it depend on whether a Killer
5026 * cage edge is actually present on any given side.
5027 */
5028 pl += GRIDEXTRA * 3;
5029 pr -= GRIDEXTRA * 3;
5030 pt += GRIDEXTRA * 3;
5031 pb -= GRIDEXTRA * 3;
5032 if (state->kgrid[y*cr+x] != 0) {
5033 /* Make further space for the Killer number. */
5034 pt += TILE_SIZE/4;
5035 /* minph--; */
5036 }
5037 }
5038
5039 /*
5040 * We arrange our pencil marks in a grid layout, with
5041 * the number of rows and columns adjusted to allow the
5042 * maximum font size.
5043 *
5044 * So now we work out what the grid size ought to be.
5045 */
5046 bestsize = 0.0;
5047 pbest = 0;
5048 /* Minimum */
5049 for (pw = 3; pw < max(npencil,4); pw++) {
5050 float fw, fh, fs;
5051
5052 ph = (npencil + pw - 1) / pw;
5053 ph = max(ph, minph);
5054 fw = (pr - pl) / (float)pw;
5055 fh = (pb - pt) / (float)ph;
5056 fs = min(fw, fh);
5057 if (fs > bestsize) {
5058 bestsize = fs;
5059 pbest = pw;
5060 }
5061 }
5062 assert(pbest > 0);
5063 pw = pbest;
5064 ph = (npencil + pw - 1) / pw;
5065 ph = max(ph, minph);
5066
5067 /*
5068 * Now we've got our grid dimensions, work out the pixel
5069 * size of a grid element, and round it to the nearest
5070 * pixel. (We don't want rounding errors to make the
5071 * grid look uneven at low pixel sizes.)
5072 */
5073 fontsize = min((pr - pl) / pw, (pb - pt) / ph);
5074
5075 /*
5076 * Centre the resulting figure in the square.
5077 */
5078 pl = tx + (TILE_SIZE - fontsize * pw) / 2;
5079 pt = ty + (TILE_SIZE - fontsize * ph) / 2;
5080
5081 /*
5082 * And move it down a bit if it's collided with the
5083 * Killer cage number.
5084 */
5085 if (state->killer && state->kgrid[y*cr+x] != 0) {
5086 pt = max(pt, ty + GRIDEXTRA * 3 + TILE_SIZE/4);
5087 }
5088
5089 /*
5090 * Now actually draw the pencil marks.
5091 */
5092 for (i = j = 0; i < cr; i++)
5093 if (state->pencil[(y*cr+x)*cr+i]) {
5094 int dx = j % pw, dy = j / pw;
5095
5096 str[1] = '\0';
5097 str[0] = i + '1';
5098 if (str[0] > '9')
5099 str[0] += 'a' - ('9'+1);
5100 draw_text(dr, pl + fontsize * (2*dx+1) / 2,
5101 pt + fontsize * (2*dy+1) / 2,
5102 FONT_VARIABLE, fontsize,
5103 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
5104 j++;
5105 }
5106 }
5107 }
5108
5109 unclip(dr);
5110
5111 draw_update(dr, cx, cy, cw, ch);
5112
5113 ds->grid[y*cr+x] = state->grid[y*cr+x];
5114 memcpy(ds->pencil+(y*cr+x)*cr, state->pencil+(y*cr+x)*cr, cr);
5115 ds->hl[y*cr+x] = hl;
5116}
5117
5118static void game_redraw(drawing *dr, game_drawstate *ds,
5119 const game_state *oldstate, const game_state *state,
5120 int dir, const game_ui *ui,
5121 float animtime, float flashtime)
5122{
5123 int cr = state->cr;
5124 int x, y;
5125
5126 if (!ds->started) {
5127 /*
5128 * The initial contents of the window are not guaranteed
5129 * and can vary with front ends. To be on the safe side,
5130 * all games should start by drawing a big
5131 * background-colour rectangle covering the whole window.
5132 */
5133 draw_rect(dr, 0, 0, SIZE(cr), SIZE(cr), COL_BACKGROUND);
5134
5135 /*
5136 * Draw the grid. We draw it as a big thick rectangle of
5137 * COL_GRID initially; individual calls to draw_number()
5138 * will poke the right-shaped holes in it.
5139 */
5140 draw_rect(dr, BORDER-GRIDEXTRA, BORDER-GRIDEXTRA,
5141 cr*TILE_SIZE+1+2*GRIDEXTRA, cr*TILE_SIZE+1+2*GRIDEXTRA,
5142 COL_GRID);
5143 }
5144
5145 /*
5146 * This array is used to keep track of rows, columns and boxes
5147 * which contain a number more than once.
5148 */
5149 for (x = 0; x < cr * ds->nregions; x++)
5150 ds->entered_items[x] = 0;
5151 for (x = 0; x < cr; x++)
5152 for (y = 0; y < cr; y++) {
5153 digit d = state->grid[y*cr+x];
5154 if (d) {
5155 int box, kbox;
5156
5157 /* Rows */
5158 ds->entered_items[x*cr+d-1]++;
5159
5160 /* Columns */
5161 ds->entered_items[(y+cr)*cr+d-1]++;
5162
5163 /* Blocks */
5164 box = state->blocks->whichblock[y*cr+x];
5165 ds->entered_items[(box+2*cr)*cr+d-1]++;
5166
5167 /* Diagonals */
5168 if (ds->xtype) {
5169 if (ondiag0(y*cr+x))
5170 ds->entered_items[(3*cr)*cr+d-1]++;
5171 if (ondiag1(y*cr+x))
5172 ds->entered_items[(3*cr+1)*cr+d-1]++;
5173 }
5174
5175 /* Killer cages */
5176 if (state->kblocks) {
5177 kbox = state->kblocks->whichblock[y*cr+x];
5178 ds->entered_items[(kbox+3*cr+2)*cr+d-1]++;
5179 }
5180 }
5181 }
5182
5183 /*
5184 * Draw any numbers which need redrawing.
5185 */
5186 for (x = 0; x < cr; x++) {
5187 for (y = 0; y < cr; y++) {
5188 int highlight = 0;
5189 digit d = state->grid[y*cr+x];
5190
5191 if (flashtime > 0 &&
5192 (flashtime <= FLASH_TIME/3 ||
5193 flashtime >= FLASH_TIME*2/3))
5194 highlight = 1;
5195
5196 /* Highlight active input areas. */
5197 if (x == ui->hx && y == ui->hy && ui->hshow)
5198 highlight = ui->hpencil ? 2 : 1;
5199
5200 /* Mark obvious errors (ie, numbers which occur more than once
5201 * in a single row, column, or box). */
5202 if (d && (ds->entered_items[x*cr+d-1] > 1 ||
5203 ds->entered_items[(y+cr)*cr+d-1] > 1 ||
5204 ds->entered_items[(state->blocks->whichblock[y*cr+x]
5205 +2*cr)*cr+d-1] > 1 ||
5206 (ds->xtype && ((ondiag0(y*cr+x) &&
5207 ds->entered_items[(3*cr)*cr+d-1] > 1) ||
5208 (ondiag1(y*cr+x) &&
5209 ds->entered_items[(3*cr+1)*cr+d-1]>1)))||
5210 (state->kblocks &&
5211 ds->entered_items[(state->kblocks->whichblock[y*cr+x]
5212 +3*cr+2)*cr+d-1] > 1)))
5213 highlight |= 16;
5214
5215 if (d && state->kblocks) {
5216 if (check_killer_cage_sum(
5217 state->kblocks, state->kgrid, state->grid,
5218 state->kblocks->whichblock[y*cr+x]) == 0)
5219 highlight |= 32;
5220 }
5221
5222 draw_number(dr, ds, state, x, y, highlight);
5223 }
5224 }
5225
5226 /*
5227 * Update the _entire_ grid if necessary.
5228 */
5229 if (!ds->started) {
5230 draw_update(dr, 0, 0, SIZE(cr), SIZE(cr));
5231 ds->started = TRUE;
5232 }
5233}
5234
5235static float game_anim_length(const game_state *oldstate,
5236 const game_state *newstate, int dir, game_ui *ui)
5237{
5238 return 0.0F;
5239}
5240
5241static float game_flash_length(const game_state *oldstate,
5242 const game_state *newstate, int dir, game_ui *ui)
5243{
5244 if (!oldstate->completed && newstate->completed &&
5245 !oldstate->cheated && !newstate->cheated)
5246 return FLASH_TIME;
5247 return 0.0F;
5248}
5249
5250static int game_status(const game_state *state)
5251{
5252 return state->completed ? +1 : 0;
5253}
5254
5255static int game_timing_state(const game_state *state, game_ui *ui)
5256{
5257 if (state->completed)
5258 return FALSE;
5259 return TRUE;
5260}
5261
5262static void game_print_size(const game_params *params, float *x, float *y)
5263{
5264 int pw, ph;
5265
5266 /*
5267 * I'll use 9mm squares by default. They should be quite big
5268 * for this game, because players will want to jot down no end
5269 * of pencil marks in the squares.
5270 */
5271 game_compute_size(params, 900, &pw, &ph);
5272 *x = pw / 100.0F;
5273 *y = ph / 100.0F;
5274}
5275
5276/*
5277 * Subfunction to draw the thick lines between cells. In order to do
5278 * this using the line-drawing rather than rectangle-drawing API (so
5279 * as to get line thicknesses to scale correctly) and yet have
5280 * correctly mitred joins between lines, we must do this by tracing
5281 * the boundary of each sub-block and drawing it in one go as a
5282 * single polygon.
5283 *
5284 * This subfunction is also reused with thinner dotted lines to
5285 * outline the Killer cages, this time offsetting the outline toward
5286 * the interior of the affected squares.
5287 */
5288static void outline_block_structure(drawing *dr, game_drawstate *ds,
5289 const game_state *state,
5290 struct block_structure *blocks,
5291 int ink, int inset)
5292{
5293 int cr = state->cr;
5294 int *coords;
5295 int bi, i, n;
5296 int x, y, dx, dy, sx, sy, sdx, sdy;
5297
5298 /*
5299 * Maximum perimeter of a k-omino is 2k+2. (Proof: start
5300 * with k unconnected squares, with total perimeter 4k.
5301 * Now repeatedly join two disconnected components
5302 * together into a larger one; every time you do so you
5303 * remove at least two unit edges, and you require k-1 of
5304 * these operations to create a single connected piece, so
5305 * you must have at most 4k-2(k-1) = 2k+2 unit edges left
5306 * afterwards.)
5307 */
5308 coords = snewn(4*cr+4, int); /* 2k+2 points, 2 coords per point */
5309
5310 /*
5311 * Iterate over all the blocks.
5312 */
5313 for (bi = 0; bi < blocks->nr_blocks; bi++) {
5314 if (blocks->nr_squares[bi] == 0)
5315 continue;
5316
5317 /*
5318 * For each block, find a starting square within it
5319 * which has a boundary at the left.
5320 */
5321 for (i = 0; i < cr; i++) {
5322 int j = blocks->blocks[bi][i];
5323 if (j % cr == 0 || blocks->whichblock[j-1] != bi)
5324 break;
5325 }
5326 assert(i < cr); /* every block must have _some_ leftmost square */
5327 x = blocks->blocks[bi][i] % cr;
5328 y = blocks->blocks[bi][i] / cr;
5329 dx = -1;
5330 dy = 0;
5331
5332 /*
5333 * Now begin tracing round the perimeter. At all
5334 * times, (x,y) describes some square within the
5335 * block, and (x+dx,y+dy) is some adjacent square
5336 * outside it; so the edge between those two squares
5337 * is always an edge of the block.
5338 */
5339 sx = x, sy = y, sdx = dx, sdy = dy; /* save starting position */
5340 n = 0;
5341 do {
5342 int cx, cy, tx, ty, nin;
5343
5344 /*
5345 * Advance to the next edge, by looking at the two
5346 * squares beyond it. If they're both outside the block,
5347 * we turn right (by leaving x,y the same and rotating
5348 * dx,dy clockwise); if they're both inside, we turn
5349 * left (by rotating dx,dy anticlockwise and contriving
5350 * to leave x+dx,y+dy unchanged); if one of each, we go
5351 * straight on (and may enforce by assertion that
5352 * they're one of each the _right_ way round).
5353 */
5354 nin = 0;
5355 tx = x - dy + dx;
5356 ty = y + dx + dy;
5357 nin += (tx >= 0 && tx < cr && ty >= 0 && ty < cr &&
5358 blocks->whichblock[ty*cr+tx] == bi);
5359 tx = x - dy;
5360 ty = y + dx;
5361 nin += (tx >= 0 && tx < cr && ty >= 0 && ty < cr &&
5362 blocks->whichblock[ty*cr+tx] == bi);
5363 if (nin == 0) {
5364 /*
5365 * Turn right.
5366 */
5367 int tmp;
5368 tmp = dx;
5369 dx = -dy;
5370 dy = tmp;
5371 } else if (nin == 2) {
5372 /*
5373 * Turn left.
5374 */
5375 int tmp;
5376
5377 x += dx;
5378 y += dy;
5379
5380 tmp = dx;
5381 dx = dy;
5382 dy = -tmp;
5383
5384 x -= dx;
5385 y -= dy;
5386 } else {
5387 /*
5388 * Go straight on.
5389 */
5390 x -= dy;
5391 y += dx;
5392 }
5393
5394 /*
5395 * Now enforce by assertion that we ended up
5396 * somewhere sensible.
5397 */
5398 assert(x >= 0 && x < cr && y >= 0 && y < cr &&
5399 blocks->whichblock[y*cr+x] == bi);
5400 assert(x+dx < 0 || x+dx >= cr || y+dy < 0 || y+dy >= cr ||
5401 blocks->whichblock[(y+dy)*cr+(x+dx)] != bi);
5402
5403 /*
5404 * Record the point we just went past at one end of the
5405 * edge. To do this, we translate (x,y) down and right
5406 * by half a unit (so they're describing a point in the
5407 * _centre_ of the square) and then translate back again
5408 * in a manner rotated by dy and dx.
5409 */
5410 assert(n < 2*cr+2);
5411 cx = ((2*x+1) + dy + dx) / 2;
5412 cy = ((2*y+1) - dx + dy) / 2;
5413 coords[2*n+0] = BORDER + cx * TILE_SIZE;
5414 coords[2*n+1] = BORDER + cy * TILE_SIZE;
5415 coords[2*n+0] -= dx * inset;
5416 coords[2*n+1] -= dy * inset;
5417 if (nin == 0) {
5418 /*
5419 * We turned right, so inset this corner back along
5420 * the edge towards the centre of the square.
5421 */
5422 coords[2*n+0] -= dy * inset;
5423 coords[2*n+1] += dx * inset;
5424 } else if (nin == 2) {
5425 /*
5426 * We turned left, so inset this corner further
5427 * _out_ along the edge into the next square.
5428 */
5429 coords[2*n+0] += dy * inset;
5430 coords[2*n+1] -= dx * inset;
5431 }
5432 n++;
5433
5434 } while (x != sx || y != sy || dx != sdx || dy != sdy);
5435
5436 /*
5437 * That's our polygon; now draw it.
5438 */
5439 draw_polygon(dr, coords, n, -1, ink);
5440 }
5441
5442 sfree(coords);
5443}
5444
5445static void game_print(drawing *dr, const game_state *state, int tilesize)
5446{
5447 int cr = state->cr;
5448 int ink = print_mono_colour(dr, 0);
5449 int x, y;
5450
5451 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
5452 game_drawstate ads, *ds = &ads;
5453 game_set_size(dr, ds, NULL, tilesize);
5454
5455 /*
5456 * Border.
5457 */
5458 print_line_width(dr, 3 * TILE_SIZE / 40);
5459 draw_rect_outline(dr, BORDER, BORDER, cr*TILE_SIZE, cr*TILE_SIZE, ink);
5460
5461 /*
5462 * Highlight X-diagonal squares.
5463 */
5464 if (state->xtype) {
5465 int i;
5466 int xhighlight = print_grey_colour(dr, 0.90F);
5467
5468 for (i = 0; i < cr; i++)
5469 draw_rect(dr, BORDER + i*TILE_SIZE, BORDER + i*TILE_SIZE,
5470 TILE_SIZE, TILE_SIZE, xhighlight);
5471 for (i = 0; i < cr; i++)
5472 if (i*2 != cr-1) /* avoid redoing centre square, just for fun */
5473 draw_rect(dr, BORDER + i*TILE_SIZE,
5474 BORDER + (cr-1-i)*TILE_SIZE,
5475 TILE_SIZE, TILE_SIZE, xhighlight);
5476 }
5477
5478 /*
5479 * Main grid.
5480 */
5481 for (x = 1; x < cr; x++) {
5482 print_line_width(dr, TILE_SIZE / 40);
5483 draw_line(dr, BORDER+x*TILE_SIZE, BORDER,
5484 BORDER+x*TILE_SIZE, BORDER+cr*TILE_SIZE, ink);
5485 }
5486 for (y = 1; y < cr; y++) {
5487 print_line_width(dr, TILE_SIZE / 40);
5488 draw_line(dr, BORDER, BORDER+y*TILE_SIZE,
5489 BORDER+cr*TILE_SIZE, BORDER+y*TILE_SIZE, ink);
5490 }
5491
5492 /*
5493 * Thick lines between cells.
5494 */
5495 print_line_width(dr, 3 * TILE_SIZE / 40);
5496 outline_block_structure(dr, ds, state, state->blocks, ink, 0);
5497
5498 /*
5499 * Killer cages and their totals.
5500 */
5501 if (state->kblocks) {
5502 print_line_width(dr, TILE_SIZE / 40);
5503 print_line_dotted(dr, TRUE);
5504 outline_block_structure(dr, ds, state, state->kblocks, ink,
5505 5 * TILE_SIZE / 40);
5506 print_line_dotted(dr, FALSE);
5507 for (y = 0; y < cr; y++)
5508 for (x = 0; x < cr; x++)
5509 if (state->kgrid[y*cr+x]) {
5510 char str[20];
5511 sprintf(str, "%d", state->kgrid[y*cr+x]);
5512 draw_text(dr,
5513 BORDER+x*TILE_SIZE + 7*TILE_SIZE/40,
5514 BORDER+y*TILE_SIZE + 16*TILE_SIZE/40,
5515 FONT_VARIABLE, TILE_SIZE/4,
5516 ALIGN_VNORMAL | ALIGN_HLEFT,
5517 ink, str);
5518 }
5519 }
5520
5521 /*
5522 * Standard (non-Killer) clue numbers.
5523 */
5524 for (y = 0; y < cr; y++)
5525 for (x = 0; x < cr; x++)
5526 if (state->grid[y*cr+x]) {
5527 char str[2];
5528 str[1] = '\0';
5529 str[0] = state->grid[y*cr+x] + '0';
5530 if (str[0] > '9')
5531 str[0] += 'a' - ('9'+1);
5532 draw_text(dr, BORDER + x*TILE_SIZE + TILE_SIZE/2,
5533 BORDER + y*TILE_SIZE + TILE_SIZE/2,
5534 FONT_VARIABLE, TILE_SIZE/2,
5535 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
5536 }
5537}
5538
5539#ifdef COMBINED
5540#define thegame solo
5541#endif
5542
5543const struct game thegame = {
5544 "Solo", "games.solo", "solo",
5545 default_params,
5546 game_fetch_preset, NULL,
5547 decode_params,
5548 encode_params,
5549 free_params,
5550 dup_params,
5551 TRUE, game_configure, custom_params,
5552 validate_params,
5553 new_game_desc,
5554 validate_desc,
5555 new_game,
5556 dup_game,
5557 free_game,
5558 TRUE, solve_game,
5559 TRUE, game_can_format_as_text_now, game_text_format,
5560 new_ui,
5561 free_ui,
5562 encode_ui,
5563 decode_ui,
5564 game_changed_state,
5565 interpret_move,
5566 execute_move,
5567 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
5568 game_colours,
5569 game_new_drawstate,
5570 game_free_drawstate,
5571 game_redraw,
5572 game_anim_length,
5573 game_flash_length,
5574 game_status,
5575 TRUE, FALSE, game_print_size, game_print,
5576 FALSE, /* wants_statusbar */
5577 FALSE, game_timing_state,
5578 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
5579};
5580
5581#ifdef STANDALONE_SOLVER
5582
5583int main(int argc, char **argv)
5584{
5585 game_params *p;
5586 game_state *s;
5587 char *id = NULL, *desc, *err;
5588 int grade = FALSE;
5589 struct difficulty dlev;
5590
5591 while (--argc > 0) {
5592 char *p = *++argv;
5593 if (!strcmp(p, "-v")) {
5594 solver_show_working = TRUE;
5595 } else if (!strcmp(p, "-g")) {
5596 grade = TRUE;
5597 } else if (*p == '-') {
5598 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
5599 return 1;
5600 } else {
5601 id = p;
5602 }
5603 }
5604
5605 if (!id) {
5606 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
5607 return 1;
5608 }
5609
5610 desc = strchr(id, ':');
5611 if (!desc) {
5612 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
5613 return 1;
5614 }
5615 *desc++ = '\0';
5616
5617 p = default_params();
5618 decode_params(p, id);
5619 err = validate_desc(p, desc);
5620 if (err) {
5621 fprintf(stderr, "%s: %s\n", argv[0], err);
5622 return 1;
5623 }
5624 s = new_game(NULL, p, desc);
5625
5626 dlev.maxdiff = DIFF_RECURSIVE;
5627 dlev.maxkdiff = DIFF_KINTERSECT;
5628 solver(s->cr, s->blocks, s->kblocks, s->xtype, s->grid, s->kgrid, &dlev);
5629 if (grade) {
5630 printf("Difficulty rating: %s\n",
5631 dlev.diff==DIFF_BLOCK ? "Trivial (blockwise positional elimination only)":
5632 dlev.diff==DIFF_SIMPLE ? "Basic (row/column/number elimination required)":
5633 dlev.diff==DIFF_INTERSECT ? "Intermediate (intersectional analysis required)":
5634 dlev.diff==DIFF_SET ? "Advanced (set elimination required)":
5635 dlev.diff==DIFF_EXTREME ? "Extreme (complex non-recursive techniques required)":
5636 dlev.diff==DIFF_RECURSIVE ? "Unreasonable (guesswork and backtracking required)":
5637 dlev.diff==DIFF_AMBIGUOUS ? "Ambiguous (multiple solutions exist)":
5638 dlev.diff==DIFF_IMPOSSIBLE ? "Impossible (no solution exists)":
5639 "INTERNAL ERROR: unrecognised difficulty code");
5640 if (p->killer)
5641 printf("Killer difficulty: %s\n",
5642 dlev.kdiff==DIFF_KSINGLE ? "Trivial (single square cages only)":
5643 dlev.kdiff==DIFF_KMINMAX ? "Simple (maximum sum analysis required)":
5644 dlev.kdiff==DIFF_KSUMS ? "Intermediate (sum possibilities)":
5645 dlev.kdiff==DIFF_KINTERSECT ? "Advanced (sum region intersections)":
5646 "INTERNAL ERROR: unrecognised difficulty code");
5647 } else {
5648 printf("%s\n", grid_text_format(s->cr, s->blocks, s->xtype, s->grid));
5649 }
5650
5651 return 0;
5652}
5653
5654#endif
5655
5656/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/solo.html b/apps/plugins/puzzles/src/solo.html
new file mode 100644
index 0000000000..472a85b2d3
--- /dev/null
+++ b/apps/plugins/puzzles/src/solo.html
@@ -0,0 +1,99 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Solo</title>
7<link rel="previous" href="pattern.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="mines.html">
12</head>
13<body>
14<p><a href="pattern.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="mines.html">Next</a></p>
15<h1><a name="C11"></a>Chapter 11: <a name="i0"></a>Solo</h1>
16<p>
17You have a square grid, which is divided into as many equally sized sub-blocks as the grid has rows. Each square must be filled in with a digit from 1 to the size of the grid, in such a way that
18</p>
19<ul><li>
20every row contains only one occurrence of each digit
21</li>
22<li>
23every column contains only one occurrence of each digit
24</li>
25<li>
26every block contains only one occurrence of each digit.
27</li>
28<li>
29(optionally, by default off) each of the square's two main diagonals contains only one occurrence of each digit.
30</li>
31</ul>
32<p>
33You are given some of the numbers as clues; your aim is to place the rest of the numbers correctly.
34</p>
35<p>
36Under the default settings, the sub-blocks are square or rectangular. The default puzzle size is 3&#215;3 (a 9&#215;9 actual grid, divided into nine 3&#215;3 blocks). You can also select sizes with rectangular blocks instead of square ones, such as 2&#215;3 (a 6&#215;6 grid divided into six 3&#215;2 blocks). Alternatively, you can select &#8216;jigsaw&#8217; mode, in which the sub-blocks are arbitrary shapes which differ between individual puzzles.
37</p>
38<p>
39Another available mode is &#8216;killer&#8217;. In this mode, clues are not given in the form of filled-in squares; instead, the grid is divided into &#8216;cages&#8217; by coloured lines, and for each cage the game tells you what the sum of all the digits in that cage should be. Also, no digit may appear more than once within a cage, even if the cage crosses the boundaries of existing regions.
40</p>
41<p>
42If you select a puzzle size which requires more than 9 digits, the additional digits will be letters of the alphabet. For example, if you select 3&#215;4 then the digits which go in your grid will be 1 to 9, plus &#8216;<code>a</code>&#8217;, &#8216;<code>b</code>&#8217; and &#8216;<code>c</code>&#8217;. This cannot be selected for killer puzzles.
43</p>
44<p>
45I first saw this puzzle in <a name="i1"></a>Nikoli <a href="#p0">[5]</a>, although it's also been popularised by various newspapers under the name &#8216;Sudoku&#8217; or &#8216;Su Doku&#8217;. Howard Garns is considered the inventor of the modern form of the puzzle, and it was first published in <em>Dell Pencil Puzzles and Word Games</em>. A more elaborate treatment of the history of the puzzle can be found on Wikipedia <a href="#p1">[6]</a>.
46</p>
47<p><a name="p0"></a>
48[5] <a href="http://www.nikoli.co.jp/en/puzzles/sudoku.html"><code>http://www.nikoli.co.jp/en/puzzles/sudoku.html</code></a> (beware of Flash)
49</p>
50<p><a name="p1"></a>
51[6] <a href="http://en.wikipedia.org/wiki/Sudoku"><code>http://en.wikipedia.org/wiki/Sudoku</code></a>
52</p>
53<h2><a name="S11.1"></a>11.1 <a name="i2"></a>Solo controls</h2>
54<p>
55To play Solo, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).
56</p>
57<p>
58If you <em>right</em>-click in a square and then type a number, that number will be entered in the square as a &#8216;pencil mark&#8217;. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks.
59</p>
60<p>
61The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.
62</p>
63<p>
64To erase a single pencil mark, right-click in the square and type the same number again.
65</p>
66<p>
67All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.
68</p>
69<p>
70Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square.
71</p>
72<p>
73(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
74</p>
75<h2><a name="S11.2"></a>11.2 <a name="i3"></a>Solo parameters</h2>
76<p>
77Solo allows you to configure two separate dimensions of the puzzle grid on the &#8216;Type&#8217; menu: the number of columns, and the number of rows, into which the main grid is divided. (The size of a block is the inverse of this: for example, if you select 2 columns and 3 rows, each actual block will have 3 columns and 2 rows.)
78</p>
79<p>
80If you tick the &#8216;X&#8217; checkbox, Solo will apply the optional extra constraint that the two main diagonals of the grid also contain one of every digit. (This is sometimes known as &#8216;Sudoku-X&#8217; in newspapers.) In this mode, the squares on the two main diagonals will be shaded slightly so that you know it's enabled.
81</p>
82<p>
83If you tick the &#8216;Jigsaw&#8217; checkbox, Solo will generate randomly shaped sub-blocks. In this mode, the actual grid size will be taken to be the product of the numbers entered in the &#8216;Columns&#8217; and &#8216;Rows&#8217; boxes. There is no reason why you have to enter a number greater than 1 in both boxes; Jigsaw mode has no constraint on the grid size, and it can even be a prime number if you feel like it.
84</p>
85<p>
86If you tick the &#8216;Killer&#8217; checkbox, Solo will generate a set of of cages, which are randomly shaped and drawn in an outline of a different colour. Each of these regions contains a smaller clue which shows the digit sum of all the squares in this region.
87</p>
88<p>
89You can also configure the type of symmetry shown in the generated puzzles. More symmetry makes the puzzles look prettier but may also make them easier, since the symmetry constraints can force more clues than necessary to be present. Completely asymmetric puzzles have the freedom to contain as few clues as possible.
90</p>
91<p>
92Finally, you can configure the difficulty of the generated puzzles. Difficulty levels are judged by the complexity of the techniques of deduction required to solve the puzzle: each level requires a mode of reasoning which was not necessary in the previous one. In particular, on difficulty levels &#8216;Trivial&#8217; and &#8216;Basic&#8217; there will be a square you can fill in with a single number at all times, whereas at &#8216;Intermediate&#8217; level and beyond you will have to make partial deductions about the <em>set</em> of squares a number could be in (or the set of numbers that could be in a square). At &#8216;Unreasonable&#8217; level, even this is not enough, and you will eventually have to make a guess, and then backtrack if it turns out to be wrong.
93</p>
94<p>
95Generating difficult puzzles is itself difficult: if you select one of the higher difficulty levels, Solo may have to make many attempts at generating a puzzle before it finds one hard enough for you. Be prepared to wait, especially if you have also configured a large puzzle size.
96</p>
97
98<hr><address></address></body>
99</html>
diff --git a/apps/plugins/puzzles/src/tdq.c b/apps/plugins/puzzles/src/tdq.c
new file mode 100644
index 0000000000..d66f9f4a83
--- /dev/null
+++ b/apps/plugins/puzzles/src/tdq.c
@@ -0,0 +1,88 @@
1/*
2 * tdq.c: implement a 'to-do queue', a simple de-duplicating to-do
3 * list mechanism.
4 */
5
6#include <assert.h>
7
8#include "puzzles.h"
9
10/*
11 * Implementation: a tdq consists of a circular buffer of size n
12 * storing the integers currently in the queue, plus an array of n
13 * booleans indicating whether each integer is already there.
14 *
15 * Using a circular buffer of size n to store between 0 and n items
16 * inclusive has an obvious failure mode: if the input and output
17 * pointers are the same, how do you know whether that means the
18 * buffer is full or empty?
19 *
20 * In this application we have a simple way to tell: in the former
21 * case, the flags array is all 1s, and in the latter case it's all
22 * 0s. So we could spot that case and check, say, flags[0].
23 *
24 * However, it's even easier to simply determine whether the queue is
25 * non-empty by testing flags[buffer[op]] - that way we don't even
26 * _have_ to compare ip against op.
27 */
28
29struct tdq {
30 int n;
31 int *queue;
32 int ip, op; /* in pointer, out pointer */
33 char *flags;
34};
35
36tdq *tdq_new(int n)
37{
38 int i;
39 tdq *tdq = snew(struct tdq);
40 tdq->queue = snewn(n, int);
41 tdq->flags = snewn(n, char);
42 for (i = 0; i < n; i++) {
43 tdq->queue[i] = 0;
44 tdq->flags[i] = 0;
45 }
46 tdq->n = n;
47 tdq->ip = tdq->op = 0;
48 return tdq;
49}
50
51void tdq_free(tdq *tdq)
52{
53 sfree(tdq->queue);
54 sfree(tdq->flags);
55 sfree(tdq);
56}
57
58void tdq_add(tdq *tdq, int k)
59{
60 assert((unsigned)k < (unsigned)tdq->n);
61 if (!tdq->flags[k]) {
62 tdq->queue[tdq->ip] = k;
63 tdq->flags[k] = 1;
64 if (++tdq->ip == tdq->n)
65 tdq->ip = 0;
66 }
67}
68
69int tdq_remove(tdq *tdq)
70{
71 int ret = tdq->queue[tdq->op];
72
73 if (!tdq->flags[ret])
74 return -1;
75
76 tdq->flags[ret] = 0;
77 if (++tdq->op == tdq->n)
78 tdq->op = 0;
79
80 return ret;
81}
82
83void tdq_fill(tdq *tdq)
84{
85 int i;
86 for (i = 0; i < tdq->n; i++)
87 tdq_add(tdq, i);
88}
diff --git a/apps/plugins/puzzles/src/tents.R b/apps/plugins/puzzles/src/tents.R
new file mode 100644
index 0000000000..557f929840
--- /dev/null
+++ b/apps/plugins/puzzles/src/tents.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3TENTS_EXTRA = maxflow dsf
4
5tents : [X] GTK COMMON tents TENTS_EXTRA tents-icon|no-icon
6
7tents : [G] WINDOWS COMMON tents TENTS_EXTRA tents.res|noicon.res
8
9ALL += tents[COMBINED] TENTS_EXTRA
10
11tentssolver : [U] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE
12tentssolver : [C] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE
13
14!begin am gtk
15GAMES += tents
16!end
17
18!begin >list.c
19 A(tents) \
20!end
21
22!begin >gamedesc.txt
23tents:tents.exe:Tents:Tent-placing puzzle:Place a tent next to each tree.
24!end
diff --git a/apps/plugins/puzzles/src/tents.c b/apps/plugins/puzzles/src/tents.c
new file mode 100644
index 0000000000..4ffeb7be64
--- /dev/null
+++ b/apps/plugins/puzzles/src/tents.c
@@ -0,0 +1,2740 @@
1/*
2 * tents.c: Puzzle involving placing tents next to trees subject to
3 * some confusing conditions.
4 *
5 * TODO:
6 *
7 * - it might be nice to make setter-provided tent/nontent clues
8 * inviolable?
9 * * on the other hand, this would introduce considerable extra
10 * complexity and size into the game state; also inviolable
11 * clues would have to be marked as such somehow, in an
12 * intrusive and annoying manner. Since they're never
13 * generated by _my_ generator, I'm currently more inclined
14 * not to bother.
15 *
16 * - more difficult levels at the top end?
17 * * for example, sometimes we can deduce that two BLANKs in
18 * the same row are each adjacent to the same unattached tree
19 * and to nothing else, implying that they can't both be
20 * tents; this enables us to rule out some extra combinations
21 * in the row-based deduction loop, and hence deduce more
22 * from the number in that row than we could otherwise do.
23 * * that by itself doesn't seem worth implementing a new
24 * difficulty level for, but if I can find a few more things
25 * like that then it might become worthwhile.
26 * * I wonder if there's a sensible heuristic for where to
27 * guess which would make a recursive solver viable?
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <assert.h>
34#include <ctype.h>
35#include <math.h>
36
37#include "puzzles.h"
38#include "maxflow.h"
39
40/*
41 * Design discussion
42 * -----------------
43 *
44 * The rules of this puzzle as available on the WWW are poorly
45 * specified. The bits about tents having to be orthogonally
46 * adjacent to trees, tents not being even diagonally adjacent to
47 * one another, and the number of tents in each row and column
48 * being given are simple enough; the difficult bit is the
49 * tent-to-tree matching.
50 *
51 * Some sources use simplistic wordings such as `each tree is
52 * exactly connected to only one tent', which is extremely unclear:
53 * it's easy to read erroneously as `each tree is _orthogonally
54 * adjacent_ to exactly one tent', which is definitely incorrect.
55 * Even the most coherent sources I've found don't do a much better
56 * job of stating the rule.
57 *
58 * A more precise statement of the rule is that it must be possible
59 * to find a bijection f between tents and trees such that each
60 * tree T is orthogonally adjacent to the tent f(T), but that a
61 * tent is permitted to be adjacent to other trees in addition to
62 * its own. This slightly non-obvious criterion is what gives this
63 * puzzle most of its subtlety.
64 *
65 * However, there's a particularly subtle ambiguity left over. Is
66 * the bijection between tents and trees required to be _unique_?
67 * In other words, is that bijection conceptually something the
68 * player should be able to exhibit as part of the solution (even
69 * if they aren't actually required to do so)? Or is it sufficient
70 * to have a unique _placement_ of the tents which gives rise to at
71 * least one suitable bijection?
72 *
73 * The puzzle shown to the right of this .T. 2 *T* 2
74 * paragraph illustrates the problem. There T.T 0 -> T-T 0
75 * are two distinct bijections available. .T. 2 *T* 2
76 * The answer to the above question will
77 * determine whether it's a valid puzzle. 202 202
78 *
79 * This is an important question, because it affects both the
80 * player and the generator. Eventually I found all the instances
81 * of this puzzle I could Google up, solved them all by hand, and
82 * verified that in all cases the tree/tent matching was uniquely
83 * determined given the tree and tent positions. Therefore, the
84 * puzzle as implemented in this source file takes the following
85 * policy:
86 *
87 * - When checking a user-supplied solution for correctness, only
88 * verify that there exists _at least_ one matching.
89 * - When generating a puzzle, enforce that there must be
90 * _exactly_ one.
91 *
92 * Algorithmic implications
93 * ------------------------
94 *
95 * Another way of phrasing the tree/tent matching criterion is to
96 * say that the bipartite adjacency graph between trees and tents
97 * has a perfect matching. That is, if you construct a graph which
98 * has a vertex per tree and a vertex per tent, and an edge between
99 * any tree and tent which are orthogonally adjacent, it is
100 * possible to find a set of N edges of that graph (where N is the
101 * number of trees and also the number of tents) which between them
102 * connect every tree to every tent.
103 *
104 * The most efficient known algorithms for finding such a matching
105 * given a graph, as far as I'm aware, are the Munkres assignment
106 * algorithm (also known as the Hungarian algorithm) and the
107 * Ford-Fulkerson algorithm (for finding optimal flows in
108 * networks). Each of these takes O(N^3) running time; so we're
109 * talking O(N^3) time to verify any candidate solution to this
110 * puzzle. That's just about OK if you're doing it once per mouse
111 * click (and in fact not even that, since the sensible thing to do
112 * is check all the _other_ puzzle criteria and only wade into this
113 * quagmire if none are violated); but if the solver had to keep
114 * doing N^3 work internally, then it would probably end up with
115 * more like N^5 or N^6 running time, and grid generation would
116 * become very clunky.
117 *
118 * Fortunately, I've been able to prove a very useful property of
119 * _unique_ perfect matchings, by adapting the proof of Hall's
120 * Marriage Theorem. For those unaware of Hall's Theorem, I'll
121 * recap it and its proof: it states that a bipartite graph
122 * contains a perfect matching iff every set of vertices on the
123 * left side of the graph have a neighbourhood _at least_ as big on
124 * the right.
125 *
126 * This condition is obviously satisfied if a perfect matching does
127 * exist; each left-side node has a distinct right-side node which
128 * is the one assigned to it by the matching, and thus any set of n
129 * left vertices must have a combined neighbourhood containing at
130 * least the n corresponding right vertices, and possibly others
131 * too. Alternatively, imagine if you had (say) three left-side
132 * nodes all of which were connected to only two right-side nodes
133 * between them: any perfect matching would have to assign one of
134 * those two right nodes to each of the three left nodes, and still
135 * give the three left nodes a different right node each. This is
136 * of course impossible.
137 *
138 * To prove the converse (that if every subset of left vertices
139 * satisfies the Hall condition then a perfect matching exists),
140 * consider trying to find a proper subset of the left vertices
141 * which _exactly_ satisfies the Hall condition: that is, its right
142 * neighbourhood is precisely the same size as it. If we can find
143 * such a subset, then we can split the bipartite graph into two
144 * smaller ones: one consisting of the left subset and its right
145 * neighbourhood, the other consisting of everything else. Edges
146 * from the left side of the former graph to the right side of the
147 * latter do not exist, by construction; edges from the right side
148 * of the former to the left of the latter cannot be part of any
149 * perfect matching because otherwise the left subset would not be
150 * left with enough distinct right vertices to connect to (this is
151 * exactly the same deduction used in Solo's set analysis). You can
152 * then prove (left as an exercise) that both these smaller graphs
153 * still satisfy the Hall condition, and therefore the proof will
154 * follow by induction.
155 *
156 * There's one other possibility, which is the case where _no_
157 * proper subset of the left vertices has a right neighbourhood of
158 * exactly the same size. That is, every left subset has a strictly
159 * _larger_ right neighbourhood. In this situation, we can simply
160 * remove an _arbitrary_ edge from the graph. This cannot reduce
161 * the size of any left subset's right neighbourhood by more than
162 * one, so if all neighbourhoods were strictly bigger than they
163 * needed to be initially, they must now still be _at least as big_
164 * as they need to be. So we can keep throwing out arbitrary edges
165 * until we find a set which exactly satisfies the Hall condition,
166 * and then proceed as above. []
167 *
168 * That's Hall's theorem. I now build on this by examining the
169 * circumstances in which a bipartite graph can have a _unique_
170 * perfect matching. It is clear that in the second case, where no
171 * left subset exactly satisfies the Hall condition and so we can
172 * remove an arbitrary edge, there cannot be a unique perfect
173 * matching: given one perfect matching, we choose our arbitrary
174 * removed edge to be one of those contained in it, and then we can
175 * still find a perfect matching in the remaining graph, which will
176 * be a distinct perfect matching in the original.
177 *
178 * So it is a necessary condition for a unique perfect matching
179 * that there must be at least one proper left subset which
180 * _exactly_ satisfies the Hall condition. But now consider the
181 * smaller graph constructed by taking that left subset and its
182 * neighbourhood: if the graph as a whole had a unique perfect
183 * matching, then so must this smaller one, which means we can find
184 * a proper left subset _again_, and so on. Repeating this process
185 * must eventually reduce us to a graph with only one left-side
186 * vertex (so there are no proper subsets at all); this vertex must
187 * be connected to only one right-side vertex, and hence must be so
188 * in the original graph as well (by construction). So we can
189 * discard this vertex pair from the graph, and any other edges
190 * that involved it (which will by construction be from other left
191 * vertices only), and the resulting smaller graph still has a
192 * unique perfect matching which means we can do the same thing
193 * again.
194 *
195 * In other words, given any bipartite graph with a unique perfect
196 * matching, we can find that matching by the following extremely
197 * simple algorithm:
198 *
199 * - Find a left-side vertex which is only connected to one
200 * right-side vertex.
201 * - Assign those vertices to one another, and therefore discard
202 * any other edges connecting to that right vertex.
203 * - Repeat until all vertices have been matched.
204 *
205 * This algorithm can be run in O(V+E) time (where V is the number
206 * of vertices and E is the number of edges in the graph), and the
207 * only way it can fail is if there is not a unique perfect
208 * matching (either because there is no matching at all, or because
209 * it isn't unique; but it can't distinguish those cases).
210 *
211 * Thus, the internal solver in this source file can be confident
212 * that if the tree/tent matching is uniquely determined by the
213 * tree and tent positions, it can find it using only this kind of
214 * obvious and simple operation: assign a tree to a tent if it
215 * cannot possibly belong to any other tent, and vice versa. If the
216 * solver were _only_ trying to determine the matching, even that
217 * `vice versa' wouldn't be required; but it can come in handy when
218 * not all the tents have been placed yet. I can therefore be
219 * reasonably confident that as long as my solver doesn't need to
220 * cope with grids that have a non-unique matching, it will also
221 * not need to do anything complicated like set analysis between
222 * trees and tents.
223 */
224
225/*
226 * In standalone solver mode, `verbose' is a variable which can be
227 * set by command-line option; in debugging mode it's simply always
228 * true.
229 */
230#if defined STANDALONE_SOLVER
231#define SOLVER_DIAGNOSTICS
232int verbose = FALSE;
233#elif defined SOLVER_DIAGNOSTICS
234#define verbose TRUE
235#endif
236
237/*
238 * Difficulty levels. I do some macro ickery here to ensure that my
239 * enum and the various forms of my name list always match up.
240 */
241#define DIFFLIST(A) \
242 A(EASY,Easy,e) \
243 A(TRICKY,Tricky,t)
244#define ENUM(upper,title,lower) DIFF_ ## upper,
245#define TITLE(upper,title,lower) #title,
246#define ENCODE(upper,title,lower) #lower
247#define CONFIG(upper,title,lower) ":" #title
248enum { DIFFLIST(ENUM) DIFFCOUNT };
249static char const *const tents_diffnames[] = { DIFFLIST(TITLE) };
250static char const tents_diffchars[] = DIFFLIST(ENCODE);
251#define DIFFCONFIG DIFFLIST(CONFIG)
252
253enum {
254 COL_BACKGROUND,
255 COL_GRID,
256 COL_GRASS,
257 COL_TREETRUNK,
258 COL_TREELEAF,
259 COL_TENT,
260 COL_ERROR,
261 COL_ERRTEXT,
262 COL_ERRTRUNK,
263 NCOLOURS
264};
265
266enum { BLANK, TREE, TENT, NONTENT, MAGIC };
267
268struct game_params {
269 int w, h;
270 int diff;
271};
272
273struct numbers {
274 int refcount;
275 int *numbers;
276};
277
278struct game_state {
279 game_params p;
280 char *grid;
281 struct numbers *numbers;
282 int completed, used_solve;
283};
284
285static game_params *default_params(void)
286{
287 game_params *ret = snew(game_params);
288
289 ret->w = ret->h = 8;
290 ret->diff = DIFF_EASY;
291
292 return ret;
293}
294
295static const struct game_params tents_presets[] = {
296 {8, 8, DIFF_EASY},
297 {8, 8, DIFF_TRICKY},
298 {10, 10, DIFF_EASY},
299 {10, 10, DIFF_TRICKY},
300 {15, 15, DIFF_EASY},
301 {15, 15, DIFF_TRICKY},
302};
303
304static int game_fetch_preset(int i, char **name, game_params **params)
305{
306 game_params *ret;
307 char str[80];
308
309 if (i < 0 || i >= lenof(tents_presets))
310 return FALSE;
311
312 ret = snew(game_params);
313 *ret = tents_presets[i];
314
315 sprintf(str, "%dx%d %s", ret->w, ret->h, tents_diffnames[ret->diff]);
316
317 *name = dupstr(str);
318 *params = ret;
319 return TRUE;
320}
321
322static void free_params(game_params *params)
323{
324 sfree(params);
325}
326
327static game_params *dup_params(const game_params *params)
328{
329 game_params *ret = snew(game_params);
330 *ret = *params; /* structure copy */
331 return ret;
332}
333
334static void decode_params(game_params *params, char const *string)
335{
336 params->w = params->h = atoi(string);
337 while (*string && isdigit((unsigned char)*string)) string++;
338 if (*string == 'x') {
339 string++;
340 params->h = atoi(string);
341 while (*string && isdigit((unsigned char)*string)) string++;
342 }
343 if (*string == 'd') {
344 int i;
345 string++;
346 for (i = 0; i < DIFFCOUNT; i++)
347 if (*string == tents_diffchars[i])
348 params->diff = i;
349 if (*string) string++;
350 }
351}
352
353static char *encode_params(const game_params *params, int full)
354{
355 char buf[120];
356
357 sprintf(buf, "%dx%d", params->w, params->h);
358 if (full)
359 sprintf(buf + strlen(buf), "d%c",
360 tents_diffchars[params->diff]);
361 return dupstr(buf);
362}
363
364static config_item *game_configure(const game_params *params)
365{
366 config_item *ret;
367 char buf[80];
368
369 ret = snewn(4, config_item);
370
371 ret[0].name = "Width";
372 ret[0].type = C_STRING;
373 sprintf(buf, "%d", params->w);
374 ret[0].sval = dupstr(buf);
375 ret[0].ival = 0;
376
377 ret[1].name = "Height";
378 ret[1].type = C_STRING;
379 sprintf(buf, "%d", params->h);
380 ret[1].sval = dupstr(buf);
381 ret[1].ival = 0;
382
383 ret[2].name = "Difficulty";
384 ret[2].type = C_CHOICES;
385 ret[2].sval = DIFFCONFIG;
386 ret[2].ival = params->diff;
387
388 ret[3].name = NULL;
389 ret[3].type = C_END;
390 ret[3].sval = NULL;
391 ret[3].ival = 0;
392
393 return ret;
394}
395
396static game_params *custom_params(const config_item *cfg)
397{
398 game_params *ret = snew(game_params);
399
400 ret->w = atoi(cfg[0].sval);
401 ret->h = atoi(cfg[1].sval);
402 ret->diff = cfg[2].ival;
403
404 return ret;
405}
406
407static char *validate_params(const game_params *params, int full)
408{
409 /*
410 * Generating anything under 4x4 runs into trouble of one kind
411 * or another.
412 */
413 if (params->w < 4 || params->h < 4)
414 return "Width and height must both be at least four";
415 return NULL;
416}
417
418/*
419 * Scratch space for solver.
420 */
421enum { N, U, L, R, D, MAXDIR }; /* link directions */
422#define dx(d) ( ((d)==R) - ((d)==L) )
423#define dy(d) ( ((d)==D) - ((d)==U) )
424#define F(d) ( U + D - (d) )
425struct solver_scratch {
426 char *links; /* mapping between trees and tents */
427 int *locs;
428 char *place, *mrows, *trows;
429};
430
431static struct solver_scratch *new_scratch(int w, int h)
432{
433 struct solver_scratch *ret = snew(struct solver_scratch);
434
435 ret->links = snewn(w*h, char);
436 ret->locs = snewn(max(w, h), int);
437 ret->place = snewn(max(w, h), char);
438 ret->mrows = snewn(3 * max(w, h), char);
439 ret->trows = snewn(3 * max(w, h), char);
440
441 return ret;
442}
443
444static void free_scratch(struct solver_scratch *sc)
445{
446 sfree(sc->trows);
447 sfree(sc->mrows);
448 sfree(sc->place);
449 sfree(sc->locs);
450 sfree(sc->links);
451 sfree(sc);
452}
453
454/*
455 * Solver. Returns 0 for impossibility, 1 for success, 2 for
456 * ambiguity or failure to converge.
457 */
458static int tents_solve(int w, int h, const char *grid, int *numbers,
459 char *soln, struct solver_scratch *sc, int diff)
460{
461 int x, y, d, i, j;
462 char *mrow, *trow, *trow1, *trow2;
463
464 /*
465 * Set up solver data.
466 */
467 memset(sc->links, N, w*h);
468
469 /*
470 * Set up solution array.
471 */
472 memcpy(soln, grid, w*h);
473
474 /*
475 * Main solver loop.
476 */
477 while (1) {
478 int done_something = FALSE;
479
480 /*
481 * Any tent which has only one unattached tree adjacent to
482 * it can be tied to that tree.
483 */
484 for (y = 0; y < h; y++)
485 for (x = 0; x < w; x++)
486 if (soln[y*w+x] == TENT && !sc->links[y*w+x]) {
487 int linkd = 0;
488
489 for (d = 1; d < MAXDIR; d++) {
490 int x2 = x + dx(d), y2 = y + dy(d);
491 if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h &&
492 soln[y2*w+x2] == TREE &&
493 !sc->links[y2*w+x2]) {
494 if (linkd)
495 break; /* found more than one */
496 else
497 linkd = d;
498 }
499 }
500
501 if (d == MAXDIR && linkd == 0) {
502#ifdef SOLVER_DIAGNOSTICS
503 if (verbose)
504 printf("tent at %d,%d cannot link to anything\n",
505 x, y);
506#endif
507 return 0; /* no solution exists */
508 } else if (d == MAXDIR) {
509 int x2 = x + dx(linkd), y2 = y + dy(linkd);
510
511#ifdef SOLVER_DIAGNOSTICS
512 if (verbose)
513 printf("tent at %d,%d can only link to tree at"
514 " %d,%d\n", x, y, x2, y2);
515#endif
516
517 sc->links[y*w+x] = linkd;
518 sc->links[y2*w+x2] = F(linkd);
519 done_something = TRUE;
520 }
521 }
522
523 if (done_something)
524 continue;
525 if (diff < 0)
526 break; /* don't do anything else! */
527
528 /*
529 * Mark a blank square as NONTENT if it is not orthogonally
530 * adjacent to any unmatched tree.
531 */
532 for (y = 0; y < h; y++)
533 for (x = 0; x < w; x++)
534 if (soln[y*w+x] == BLANK) {
535 int can_be_tent = FALSE;
536
537 for (d = 1; d < MAXDIR; d++) {
538 int x2 = x + dx(d), y2 = y + dy(d);
539 if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h &&
540 soln[y2*w+x2] == TREE &&
541 !sc->links[y2*w+x2])
542 can_be_tent = TRUE;
543 }
544
545 if (!can_be_tent) {
546#ifdef SOLVER_DIAGNOSTICS
547 if (verbose)
548 printf("%d,%d cannot be a tent (no adjacent"
549 " unmatched tree)\n", x, y);
550#endif
551 soln[y*w+x] = NONTENT;
552 done_something = TRUE;
553 }
554 }
555
556 if (done_something)
557 continue;
558
559 /*
560 * Mark a blank square as NONTENT if it is (perhaps
561 * diagonally) adjacent to any other tent.
562 */
563 for (y = 0; y < h; y++)
564 for (x = 0; x < w; x++)
565 if (soln[y*w+x] == BLANK) {
566 int dx, dy, imposs = FALSE;
567
568 for (dy = -1; dy <= +1; dy++)
569 for (dx = -1; dx <= +1; dx++)
570 if (dy || dx) {
571 int x2 = x + dx, y2 = y + dy;
572 if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h &&
573 soln[y2*w+x2] == TENT)
574 imposs = TRUE;
575 }
576
577 if (imposs) {
578#ifdef SOLVER_DIAGNOSTICS
579 if (verbose)
580 printf("%d,%d cannot be a tent (adjacent tent)\n",
581 x, y);
582#endif
583 soln[y*w+x] = NONTENT;
584 done_something = TRUE;
585 }
586 }
587
588 if (done_something)
589 continue;
590
591 /*
592 * Any tree which has exactly one {unattached tent, BLANK}
593 * adjacent to it must have its tent in that square.
594 */
595 for (y = 0; y < h; y++)
596 for (x = 0; x < w; x++)
597 if (soln[y*w+x] == TREE && !sc->links[y*w+x]) {
598 int linkd = 0, linkd2 = 0, nd = 0;
599
600 for (d = 1; d < MAXDIR; d++) {
601 int x2 = x + dx(d), y2 = y + dy(d);
602 if (!(x2 >= 0 && x2 < w && y2 >= 0 && y2 < h))
603 continue;
604 if (soln[y2*w+x2] == BLANK ||
605 (soln[y2*w+x2] == TENT && !sc->links[y2*w+x2])) {
606 if (linkd)
607 linkd2 = d;
608 else
609 linkd = d;
610 nd++;
611 }
612 }
613
614 if (nd == 0) {
615#ifdef SOLVER_DIAGNOSTICS
616 if (verbose)
617 printf("tree at %d,%d cannot link to anything\n",
618 x, y);
619#endif
620 return 0; /* no solution exists */
621 } else if (nd == 1) {
622 int x2 = x + dx(linkd), y2 = y + dy(linkd);
623
624#ifdef SOLVER_DIAGNOSTICS
625 if (verbose)
626 printf("tree at %d,%d can only link to tent at"
627 " %d,%d\n", x, y, x2, y2);
628#endif
629 soln[y2*w+x2] = TENT;
630 sc->links[y*w+x] = linkd;
631 sc->links[y2*w+x2] = F(linkd);
632 done_something = TRUE;
633 } else if (nd == 2 && (!dx(linkd) != !dx(linkd2)) &&
634 diff >= DIFF_TRICKY) {
635 /*
636 * If there are two possible places where
637 * this tree's tent can go, and they are
638 * diagonally separated rather than being
639 * on opposite sides of the tree, then the
640 * square (other than the tree square)
641 * which is adjacent to both of them must
642 * be a non-tent.
643 */
644 int x2 = x + dx(linkd) + dx(linkd2);
645 int y2 = y + dy(linkd) + dy(linkd2);
646 assert(x2 >= 0 && x2 < w && y2 >= 0 && y2 < h);
647 if (soln[y2*w+x2] == BLANK) {
648#ifdef SOLVER_DIAGNOSTICS
649 if (verbose)
650 printf("possible tent locations for tree at"
651 " %d,%d rule out tent at %d,%d\n",
652 x, y, x2, y2);
653#endif
654 soln[y2*w+x2] = NONTENT;
655 done_something = TRUE;
656 }
657 }
658 }
659
660 if (done_something)
661 continue;
662
663 /*
664 * If localised deductions about the trees and tents
665 * themselves haven't helped us, it's time to resort to the
666 * numbers round the grid edge. For each row and column, we
667 * go through all possible combinations of locations for
668 * the unplaced tents, rule out any which have adjacent
669 * tents, and spot any square which is given the same state
670 * by all remaining combinations.
671 */
672 for (i = 0; i < w+h; i++) {
673 int start, step, len, start1, start2, n, k;
674
675 if (i < w) {
676 /*
677 * This is the number for a column.
678 */
679 start = i;
680 step = w;
681 len = h;
682 if (i > 0)
683 start1 = start - 1;
684 else
685 start1 = -1;
686 if (i+1 < w)
687 start2 = start + 1;
688 else
689 start2 = -1;
690 } else {
691 /*
692 * This is the number for a row.
693 */
694 start = (i-w)*w;
695 step = 1;
696 len = w;
697 if (i > w)
698 start1 = start - w;
699 else
700 start1 = -1;
701 if (i+1 < w+h)
702 start2 = start + w;
703 else
704 start2 = -1;
705 }
706
707 if (diff < DIFF_TRICKY) {
708 /*
709 * In Easy mode, we don't look at the effect of one
710 * row on the next (i.e. ruling out a square if all
711 * possibilities for an adjacent row place a tent
712 * next to it).
713 */
714 start1 = start2 = -1;
715 }
716
717 k = numbers[i];
718
719 /*
720 * Count and store the locations of the free squares,
721 * and also count the number of tents already placed.
722 */
723 n = 0;
724 for (j = 0; j < len; j++) {
725 if (soln[start+j*step] == TENT)
726 k--; /* one fewer tent to place */
727 else if (soln[start+j*step] == BLANK)
728 sc->locs[n++] = j;
729 }
730
731 if (n == 0)
732 continue; /* nothing left to do here */
733
734 /*
735 * Now we know we're placing k tents in n squares. Set
736 * up the first possibility.
737 */
738 for (j = 0; j < n; j++)
739 sc->place[j] = (j < k ? TENT : NONTENT);
740
741 /*
742 * We're aiming to find squares in this row which are
743 * invariant over all valid possibilities. Thus, we
744 * maintain the current state of that invariance. We
745 * start everything off at MAGIC to indicate that it
746 * hasn't been set up yet.
747 */
748 mrow = sc->mrows;
749 trow = sc->trows;
750 trow1 = sc->trows + len;
751 trow2 = sc->trows + 2*len;
752 memset(mrow, MAGIC, 3*len);
753
754 /*
755 * And iterate over all possibilities.
756 */
757 while (1) {
758 int p, valid;
759
760 /*
761 * See if this possibility is valid. The only way
762 * it can fail to be valid is if it contains two
763 * adjacent tents. (Other forms of invalidity, such
764 * as containing a tent adjacent to one already
765 * placed, will have been dealt with already by
766 * other parts of the solver.)
767 */
768 valid = TRUE;
769 for (j = 0; j+1 < n; j++)
770 if (sc->place[j] == TENT &&
771 sc->place[j+1] == TENT &&
772 sc->locs[j+1] == sc->locs[j]+1) {
773 valid = FALSE;
774 break;
775 }
776
777 if (valid) {
778 /*
779 * Merge this valid combination into mrow.
780 */
781 memset(trow, MAGIC, len);
782 memset(trow+len, BLANK, 2*len);
783 for (j = 0; j < n; j++) {
784 trow[sc->locs[j]] = sc->place[j];
785 if (sc->place[j] == TENT) {
786 int jj;
787 for (jj = sc->locs[j]-1; jj <= sc->locs[j]+1; jj++)
788 if (jj >= 0 && jj < len)
789 trow1[jj] = trow2[jj] = NONTENT;
790 }
791 }
792
793 for (j = 0; j < 3*len; j++) {
794 if (trow[j] == MAGIC)
795 continue;
796 if (mrow[j] == MAGIC || mrow[j] == trow[j]) {
797 /*
798 * Either this is the first valid
799 * placement we've found at all, or
800 * this square's contents are
801 * consistent with every previous valid
802 * combination.
803 */
804 mrow[j] = trow[j];
805 } else {
806 /*
807 * This square's contents fail to match
808 * what they were in a different
809 * combination, so we cannot deduce
810 * anything about this square.
811 */
812 mrow[j] = BLANK;
813 }
814 }
815 }
816
817 /*
818 * Find the next combination of k choices from n.
819 * We do this by finding the rightmost tent which
820 * can be moved one place right, doing so, and
821 * shunting all tents to the right of that as far
822 * left as they can go.
823 */
824 p = 0;
825 for (j = n-1; j > 0; j--) {
826 if (sc->place[j] == TENT)
827 p++;
828 if (sc->place[j] == NONTENT && sc->place[j-1] == TENT) {
829 sc->place[j-1] = NONTENT;
830 sc->place[j] = TENT;
831 while (p--)
832 sc->place[++j] = TENT;
833 while (++j < n)
834 sc->place[j] = NONTENT;
835 break;
836 }
837 }
838 if (j <= 0)
839 break; /* we've finished */
840 }
841
842 /*
843 * It's just possible that _no_ placement was valid, in
844 * which case we have an internally inconsistent
845 * puzzle.
846 */
847 if (mrow[sc->locs[0]] == MAGIC)
848 return 0; /* inconsistent */
849
850 /*
851 * Now go through mrow and see if there's anything
852 * we've deduced which wasn't already mentioned in soln.
853 */
854 for (j = 0; j < len; j++) {
855 int whichrow;
856
857 for (whichrow = 0; whichrow < 3; whichrow++) {
858 char *mthis = mrow + whichrow * len;
859 int tstart = (whichrow == 0 ? start :
860 whichrow == 1 ? start1 : start2);
861 if (tstart >= 0 &&
862 mthis[j] != MAGIC && mthis[j] != BLANK &&
863 soln[tstart+j*step] == BLANK) {
864 int pos = tstart+j*step;
865
866#ifdef SOLVER_DIAGNOSTICS
867 if (verbose)
868 printf("%s %d forces %s at %d,%d\n",
869 step==1 ? "row" : "column",
870 step==1 ? start/w : start,
871 mthis[j] == TENT ? "tent" : "non-tent",
872 pos % w, pos / w);
873#endif
874 soln[pos] = mthis[j];
875 done_something = TRUE;
876 }
877 }
878 }
879 }
880
881 if (done_something)
882 continue;
883
884 if (!done_something)
885 break;
886 }
887
888 /*
889 * The solver has nothing further it can do. Return 1 if both
890 * soln and sc->links are completely filled in, or 2 otherwise.
891 */
892 for (y = 0; y < h; y++)
893 for (x = 0; x < w; x++) {
894 if (soln[y*w+x] == BLANK)
895 return 2;
896 if (soln[y*w+x] != NONTENT && sc->links[y*w+x] == 0)
897 return 2;
898 }
899
900 return 1;
901}
902
903static char *new_game_desc(const game_params *params_in, random_state *rs,
904 char **aux, int interactive)
905{
906 game_params params_copy = *params_in; /* structure copy */
907 game_params *params = &params_copy;
908 int w = params->w, h = params->h;
909 int ntrees = w * h / 5;
910 char *grid = snewn(w*h, char);
911 char *puzzle = snewn(w*h, char);
912 int *numbers = snewn(w+h, int);
913 char *soln = snewn(w*h, char);
914 int *temp = snewn(2*w*h, int);
915 int maxedges = ntrees*4 + w*h;
916 int *edges = snewn(2*maxedges, int);
917 int *capacity = snewn(maxedges, int);
918 int *flow = snewn(maxedges, int);
919 struct solver_scratch *sc = new_scratch(w, h);
920 char *ret, *p;
921 int i, j, nedges;
922
923 /*
924 * Since this puzzle has many global deductions and doesn't
925 * permit limited clue sets, generating grids for this puzzle
926 * is hard enough that I see no better option than to simply
927 * generate a solution and see if it's unique and has the
928 * required difficulty. This turns out to be computationally
929 * plausible as well.
930 *
931 * We chose our tree count (hence also tent count) by dividing
932 * the total grid area by five above. Why five? Well, w*h/4 is
933 * the maximum number of tents you can _possibly_ fit into the
934 * grid without violating the separation criterion, and to
935 * achieve that you are constrained to a very small set of
936 * possible layouts (the obvious one with a tent at every
937 * (even,even) coordinate, and trivial variations thereon). So
938 * if we reduce the tent count a bit more, we enable more
939 * random-looking placement; 5 turns out to be a plausible
940 * figure which yields sensible puzzles. Increasing the tent
941 * count would give puzzles whose solutions were too regimented
942 * and could be solved by the use of that knowledge (and would
943 * also take longer to find a viable placement); decreasing it
944 * would make the grids emptier and more boring.
945 *
946 * Actually generating a grid is a matter of first placing the
947 * tents, and then placing the trees by the use of maxflow
948 * (finding a distinct square adjacent to every tent). We do it
949 * this way round because otherwise satisfying the tent
950 * separation condition would become onerous: most randomly
951 * chosen tent layouts do not satisfy this condition, so we'd
952 * have gone to a lot of work before finding that a candidate
953 * layout was unusable. Instead, we place the tents first and
954 * ensure they meet the separation criterion _before_ doing
955 * lots of computation; this works much better.
956 *
957 * The maxflow algorithm is not randomised, so employed naively
958 * it would give rise to grids with clear structure and
959 * directional bias. Hence, I assign the network nodes as seen
960 * by maxflow to be a _random_ permutation of the squares of
961 * the grid, so that any bias shown by maxflow towards
962 * low-numbered nodes is turned into a random bias.
963 *
964 * This generation strategy can fail at many points, including
965 * as early as tent placement (if you get a bad random order in
966 * which to greedily try the grid squares, you won't even
967 * manage to find enough mutually non-adjacent squares to put
968 * the tents in). Then it can fail if maxflow doesn't manage to
969 * find a good enough matching (i.e. the tent placements don't
970 * admit any adequate tree placements); and finally it can fail
971 * if the solver finds that the problem has the wrong
972 * difficulty (including being actually non-unique). All of
973 * these, however, are insufficiently frequent to cause
974 * trouble.
975 */
976
977 if (params->diff > DIFF_EASY && params->w <= 4 && params->h <= 4)
978 params->diff = DIFF_EASY; /* downgrade to prevent tight loop */
979
980 while (1) {
981 /*
982 * Arrange the grid squares into a random order.
983 */
984 for (i = 0; i < w*h; i++)
985 temp[i] = i;
986 shuffle(temp, w*h, sizeof(*temp), rs);
987
988 /*
989 * The first `ntrees' entries in temp which we can get
990 * without making two tents adjacent will be the tent
991 * locations.
992 */
993 memset(grid, BLANK, w*h);
994 j = ntrees;
995 for (i = 0; i < w*h && j > 0; i++) {
996 int x = temp[i] % w, y = temp[i] / w;
997 int dy, dx, ok = TRUE;
998
999 for (dy = -1; dy <= +1; dy++)
1000 for (dx = -1; dx <= +1; dx++)
1001 if (x+dx >= 0 && x+dx < w &&
1002 y+dy >= 0 && y+dy < h &&
1003 grid[(y+dy)*w+(x+dx)] == TENT)
1004 ok = FALSE;
1005
1006 if (ok) {
1007 grid[temp[i]] = TENT;
1008 j--;
1009 }
1010 }
1011 if (j > 0)
1012 continue; /* couldn't place all the tents */
1013
1014 /*
1015 * Now we build up the list of graph edges.
1016 */
1017 nedges = 0;
1018 for (i = 0; i < w*h; i++) {
1019 if (grid[temp[i]] == TENT) {
1020 for (j = 0; j < w*h; j++) {
1021 if (grid[temp[j]] != TENT) {
1022 int xi = temp[i] % w, yi = temp[i] / w;
1023 int xj = temp[j] % w, yj = temp[j] / w;
1024 if (abs(xi-xj) + abs(yi-yj) == 1) {
1025 edges[nedges*2] = i;
1026 edges[nedges*2+1] = j;
1027 capacity[nedges] = 1;
1028 nedges++;
1029 }
1030 }
1031 }
1032 } else {
1033 /*
1034 * Special node w*h is the sink node; any non-tent node
1035 * has an edge going to it.
1036 */
1037 edges[nedges*2] = i;
1038 edges[nedges*2+1] = w*h;
1039 capacity[nedges] = 1;
1040 nedges++;
1041 }
1042 }
1043
1044 /*
1045 * Special node w*h+1 is the source node, with an edge going to
1046 * every tent.
1047 */
1048 for (i = 0; i < w*h; i++) {
1049 if (grid[temp[i]] == TENT) {
1050 edges[nedges*2] = w*h+1;
1051 edges[nedges*2+1] = i;
1052 capacity[nedges] = 1;
1053 nedges++;
1054 }
1055 }
1056
1057 assert(nedges <= maxedges);
1058
1059 /*
1060 * Now we're ready to call the maxflow algorithm to place the
1061 * trees.
1062 */
1063 j = maxflow(w*h+2, w*h+1, w*h, nedges, edges, capacity, flow, NULL);
1064
1065 if (j < ntrees)
1066 continue; /* couldn't place all the trees */
1067
1068 /*
1069 * We've placed the trees. Now we need to work out _where_
1070 * we've placed them, which is a matter of reading back out
1071 * from the `flow' array.
1072 */
1073 for (i = 0; i < nedges; i++) {
1074 if (edges[2*i] < w*h && edges[2*i+1] < w*h && flow[i] > 0)
1075 grid[temp[edges[2*i+1]]] = TREE;
1076 }
1077
1078 /*
1079 * I think it looks ugly if there isn't at least one of
1080 * _something_ (tent or tree) in each row and each column
1081 * of the grid. This doesn't give any information away
1082 * since a completely empty row/column is instantly obvious
1083 * from the clues (it has no trees and a zero).
1084 */
1085 for (i = 0; i < w; i++) {
1086 for (j = 0; j < h; j++) {
1087 if (grid[j*w+i] != BLANK)
1088 break; /* found something in this column */
1089 }
1090 if (j == h)
1091 break; /* found empty column */
1092 }
1093 if (i < w)
1094 continue; /* a column was empty */
1095
1096 for (j = 0; j < h; j++) {
1097 for (i = 0; i < w; i++) {
1098 if (grid[j*w+i] != BLANK)
1099 break; /* found something in this row */
1100 }
1101 if (i == w)
1102 break; /* found empty row */
1103 }
1104 if (j < h)
1105 continue; /* a row was empty */
1106
1107 /*
1108 * Now set up the numbers round the edge.
1109 */
1110 for (i = 0; i < w; i++) {
1111 int n = 0;
1112 for (j = 0; j < h; j++)
1113 if (grid[j*w+i] == TENT)
1114 n++;
1115 numbers[i] = n;
1116 }
1117 for (i = 0; i < h; i++) {
1118 int n = 0;
1119 for (j = 0; j < w; j++)
1120 if (grid[i*w+j] == TENT)
1121 n++;
1122 numbers[w+i] = n;
1123 }
1124
1125 /*
1126 * And now actually solve the puzzle, to see whether it's
1127 * unique and has the required difficulty.
1128 */
1129 for (i = 0; i < w*h; i++)
1130 puzzle[i] = grid[i] == TREE ? TREE : BLANK;
1131 i = tents_solve(w, h, puzzle, numbers, soln, sc, params->diff-1);
1132 j = tents_solve(w, h, puzzle, numbers, soln, sc, params->diff);
1133
1134 /*
1135 * We expect solving with difficulty params->diff to have
1136 * succeeded (otherwise the problem is too hard), and
1137 * solving with diff-1 to have failed (otherwise it's too
1138 * easy).
1139 */
1140 if (i == 2 && j == 1)
1141 break;
1142 }
1143
1144 /*
1145 * That's it. Encode as a game ID.
1146 */
1147 ret = snewn((w+h)*40 + ntrees + (w*h)/26 + 1, char);
1148 p = ret;
1149 j = 0;
1150 for (i = 0; i <= w*h; i++) {
1151 int c = (i < w*h ? grid[i] == TREE : 1);
1152 if (c) {
1153 *p++ = (j == 0 ? '_' : j-1 + 'a');
1154 j = 0;
1155 } else {
1156 j++;
1157 while (j > 25) {
1158 *p++ = 'z';
1159 j -= 25;
1160 }
1161 }
1162 }
1163 for (i = 0; i < w+h; i++)
1164 p += sprintf(p, ",%d", numbers[i]);
1165 *p++ = '\0';
1166 ret = sresize(ret, p - ret, char);
1167
1168 /*
1169 * And encode the solution as an aux_info.
1170 */
1171 *aux = snewn(ntrees * 40, char);
1172 p = *aux;
1173 *p++ = 'S';
1174 for (i = 0; i < w*h; i++)
1175 if (grid[i] == TENT)
1176 p += sprintf(p, ";T%d,%d", i%w, i/w);
1177 *p++ = '\0';
1178 *aux = sresize(*aux, p - *aux, char);
1179
1180 free_scratch(sc);
1181 sfree(flow);
1182 sfree(capacity);
1183 sfree(edges);
1184 sfree(temp);
1185 sfree(soln);
1186 sfree(numbers);
1187 sfree(puzzle);
1188 sfree(grid);
1189
1190 return ret;
1191}
1192
1193static char *validate_desc(const game_params *params, const char *desc)
1194{
1195 int w = params->w, h = params->h;
1196 int area, i;
1197
1198 area = 0;
1199 while (*desc && *desc != ',') {
1200 if (*desc == '_')
1201 area++;
1202 else if (*desc >= 'a' && *desc < 'z')
1203 area += *desc - 'a' + 2;
1204 else if (*desc == 'z')
1205 area += 25;
1206 else if (*desc == '!' || *desc == '-')
1207 /* do nothing */;
1208 else
1209 return "Invalid character in grid specification";
1210
1211 desc++;
1212 }
1213 if (area < w * h + 1)
1214 return "Not enough data to fill grid";
1215 else if (area > w * h + 1)
1216 return "Too much data to fill grid";
1217
1218 for (i = 0; i < w+h; i++) {
1219 if (!*desc)
1220 return "Not enough numbers given after grid specification";
1221 else if (*desc != ',')
1222 return "Invalid character in number list";
1223 desc++;
1224 while (*desc && isdigit((unsigned char)*desc)) desc++;
1225 }
1226
1227 if (*desc)
1228 return "Unexpected additional data at end of game description";
1229 return NULL;
1230}
1231
1232static game_state *new_game(midend *me, const game_params *params,
1233 const char *desc)
1234{
1235 int w = params->w, h = params->h;
1236 game_state *state = snew(game_state);
1237 int i;
1238
1239 state->p = *params; /* structure copy */
1240 state->grid = snewn(w*h, char);
1241 state->numbers = snew(struct numbers);
1242 state->numbers->refcount = 1;
1243 state->numbers->numbers = snewn(w+h, int);
1244 state->completed = state->used_solve = FALSE;
1245
1246 i = 0;
1247 memset(state->grid, BLANK, w*h);
1248
1249 while (*desc) {
1250 int run, type;
1251
1252 type = TREE;
1253
1254 if (*desc == '_')
1255 run = 0;
1256 else if (*desc >= 'a' && *desc < 'z')
1257 run = *desc - ('a'-1);
1258 else if (*desc == 'z') {
1259 run = 25;
1260 type = BLANK;
1261 } else {
1262 assert(*desc == '!' || *desc == '-');
1263 run = -1;
1264 type = (*desc == '!' ? TENT : NONTENT);
1265 }
1266
1267 desc++;
1268
1269 i += run;
1270 assert(i >= 0 && i <= w*h);
1271 if (i == w*h) {
1272 assert(type == TREE);
1273 break;
1274 } else {
1275 if (type != BLANK)
1276 state->grid[i++] = type;
1277 }
1278 }
1279
1280 for (i = 0; i < w+h; i++) {
1281 assert(*desc == ',');
1282 desc++;
1283 state->numbers->numbers[i] = atoi(desc);
1284 while (*desc && isdigit((unsigned char)*desc)) desc++;
1285 }
1286
1287 assert(!*desc);
1288
1289 return state;
1290}
1291
1292static game_state *dup_game(const game_state *state)
1293{
1294 int w = state->p.w, h = state->p.h;
1295 game_state *ret = snew(game_state);
1296
1297 ret->p = state->p; /* structure copy */
1298 ret->grid = snewn(w*h, char);
1299 memcpy(ret->grid, state->grid, w*h);
1300 ret->numbers = state->numbers;
1301 state->numbers->refcount++;
1302 ret->completed = state->completed;
1303 ret->used_solve = state->used_solve;
1304
1305 return ret;
1306}
1307
1308static void free_game(game_state *state)
1309{
1310 if (--state->numbers->refcount <= 0) {
1311 sfree(state->numbers->numbers);
1312 sfree(state->numbers);
1313 }
1314 sfree(state->grid);
1315 sfree(state);
1316}
1317
1318static char *solve_game(const game_state *state, const game_state *currstate,
1319 const char *aux, char **error)
1320{
1321 int w = state->p.w, h = state->p.h;
1322
1323 if (aux) {
1324 /*
1325 * If we already have the solution, save ourselves some
1326 * time.
1327 */
1328 return dupstr(aux);
1329 } else {
1330 struct solver_scratch *sc = new_scratch(w, h);
1331 char *soln;
1332 int ret;
1333 char *move, *p;
1334 int i;
1335
1336 soln = snewn(w*h, char);
1337 ret = tents_solve(w, h, state->grid, state->numbers->numbers,
1338 soln, sc, DIFFCOUNT-1);
1339 free_scratch(sc);
1340 if (ret != 1) {
1341 sfree(soln);
1342 if (ret == 0)
1343 *error = "This puzzle is not self-consistent";
1344 else
1345 *error = "Unable to find a unique solution for this puzzle";
1346 return NULL;
1347 }
1348
1349 /*
1350 * Construct a move string which turns the current state
1351 * into the solved state.
1352 */
1353 move = snewn(w*h * 40, char);
1354 p = move;
1355 *p++ = 'S';
1356 for (i = 0; i < w*h; i++)
1357 if (soln[i] == TENT)
1358 p += sprintf(p, ";T%d,%d", i%w, i/w);
1359 *p++ = '\0';
1360 move = sresize(move, p - move, char);
1361
1362 sfree(soln);
1363
1364 return move;
1365 }
1366}
1367
1368static int game_can_format_as_text_now(const game_params *params)
1369{
1370 return params->w <= 1998 && params->h <= 1998; /* 999 tents */
1371}
1372
1373static char *game_text_format(const game_state *state)
1374{
1375 int w = state->p.w, h = state->p.h, r, c;
1376 int cw = 4, ch = 2, gw = (w+1)*cw + 2, gh = (h+1)*ch + 1, len = gw * gh;
1377 char *board = snewn(len + 1, char);
1378
1379 sprintf(board, "%*s\n", len - 2, "");
1380 for (r = 0; r <= h; ++r) {
1381 for (c = 0; c <= w; ++c) {
1382 int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2;
1383 int i = r*w + c, n = 1000;
1384
1385 if (r == h && c == w) /* NOP */;
1386 else if (c == w) n = state->numbers->numbers[w + r];
1387 else if (r == h) n = state->numbers->numbers[c];
1388 else switch (state->grid[i]) {
1389 case BLANK: board[center] = '.'; break;
1390 case TREE: board[center] = 'T'; break;
1391 case TENT: memcpy(board + center - 1, "//\\", 3); break;
1392 case NONTENT: break;
1393 default: memcpy(board + center - 1, "wtf", 3);
1394 }
1395
1396 if (n < 100) {
1397 board[center] = '0' + n % 10;
1398 if (n >= 10) board[center - 1] = '0' + n / 10;
1399 } else if (n < 1000) {
1400 board[center + 1] = '0' + n % 10;
1401 board[center] = '0' + n / 10 % 10;
1402 board[center - 1] = '0' + n / 100;
1403 }
1404
1405 board[cell] = '+';
1406 memset(board + cell + 1, '-', cw - 1);
1407 for (i = 1; i < ch; ++i) board[cell + i*gw] = '|';
1408 }
1409
1410 for (c = 0; c < ch; ++c) {
1411 board[(r*ch+c)*gw + gw - 2] =
1412 c == 0 ? '+' : r < h ? '|' : ' ';
1413 board[(r*ch+c)*gw + gw - 1] = '\n';
1414 }
1415 }
1416
1417 memset(board + len - gw, '-', gw - 2 - cw);
1418 for (c = 0; c <= w; ++c) board[len - gw + cw*c] = '+';
1419
1420 return board;
1421}
1422
1423struct game_ui {
1424 int dsx, dsy; /* coords of drag start */
1425 int dex, dey; /* coords of drag end */
1426 int drag_button; /* -1 for none, or a button code */
1427 int drag_ok; /* dragged off the window, to cancel */
1428
1429 int cx, cy, cdisp; /* cursor position, and ?display. */
1430};
1431
1432static game_ui *new_ui(const game_state *state)
1433{
1434 game_ui *ui = snew(game_ui);
1435 ui->dsx = ui->dsy = -1;
1436 ui->dex = ui->dey = -1;
1437 ui->drag_button = -1;
1438 ui->drag_ok = FALSE;
1439 ui->cx = ui->cy = ui->cdisp = 0;
1440 return ui;
1441}
1442
1443static void free_ui(game_ui *ui)
1444{
1445 sfree(ui);
1446}
1447
1448static char *encode_ui(const game_ui *ui)
1449{
1450 return NULL;
1451}
1452
1453static void decode_ui(game_ui *ui, const char *encoding)
1454{
1455}
1456
1457static void game_changed_state(game_ui *ui, const game_state *oldstate,
1458 const game_state *newstate)
1459{
1460}
1461
1462struct game_drawstate {
1463 int tilesize;
1464 int started;
1465 game_params p;
1466 int *drawn, *numbersdrawn;
1467 int cx, cy; /* last-drawn cursor pos, or (-1,-1) if absent. */
1468};
1469
1470#define PREFERRED_TILESIZE 32
1471#define TILESIZE (ds->tilesize)
1472#define TLBORDER (TILESIZE/2)
1473#define BRBORDER (TILESIZE*3/2)
1474#define COORD(x) ( (x) * TILESIZE + TLBORDER )
1475#define FROMCOORD(x) ( ((x) - TLBORDER + TILESIZE) / TILESIZE - 1 )
1476
1477#define FLASH_TIME 0.30F
1478
1479static int drag_xform(const game_ui *ui, int x, int y, int v)
1480{
1481 int xmin, ymin, xmax, ymax;
1482
1483 xmin = min(ui->dsx, ui->dex);
1484 xmax = max(ui->dsx, ui->dex);
1485 ymin = min(ui->dsy, ui->dey);
1486 ymax = max(ui->dsy, ui->dey);
1487
1488#ifndef STYLUS_BASED
1489 /*
1490 * Left-dragging has no effect, so we treat a left-drag as a
1491 * single click on dsx,dsy.
1492 */
1493 if (ui->drag_button == LEFT_BUTTON) {
1494 xmin = xmax = ui->dsx;
1495 ymin = ymax = ui->dsy;
1496 }
1497#endif
1498
1499 if (x < xmin || x > xmax || y < ymin || y > ymax)
1500 return v; /* no change outside drag area */
1501
1502 if (v == TREE)
1503 return v; /* trees are inviolate always */
1504
1505 if (xmin == xmax && ymin == ymax) {
1506 /*
1507 * Results of a simple click. Left button sets blanks to
1508 * tents; right button sets blanks to non-tents; either
1509 * button clears a non-blank square.
1510 * If stylus-based however, it loops instead.
1511 */
1512 if (ui->drag_button == LEFT_BUTTON)
1513#ifdef STYLUS_BASED
1514 v = (v == BLANK ? TENT : (v == TENT ? NONTENT : BLANK));
1515 else
1516 v = (v == BLANK ? NONTENT : (v == NONTENT ? TENT : BLANK));
1517#else
1518 v = (v == BLANK ? TENT : BLANK);
1519 else
1520 v = (v == BLANK ? NONTENT : BLANK);
1521#endif
1522 } else {
1523 /*
1524 * Results of a drag. Left-dragging has no effect.
1525 * Right-dragging sets all blank squares to non-tents and
1526 * has no effect on anything else.
1527 */
1528 if (ui->drag_button == RIGHT_BUTTON)
1529 v = (v == BLANK ? NONTENT : v);
1530 else
1531#ifdef STYLUS_BASED
1532 v = (v == BLANK ? NONTENT : v);
1533#else
1534 /* do nothing */;
1535#endif
1536 }
1537
1538 return v;
1539}
1540
1541static char *interpret_move(const game_state *state, game_ui *ui,
1542 const game_drawstate *ds,
1543 int x, int y, int button)
1544{
1545 int w = state->p.w, h = state->p.h;
1546 char tmpbuf[80];
1547 int shift = button & MOD_SHFT, control = button & MOD_CTRL;
1548
1549 button &= ~MOD_MASK;
1550
1551 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1552 x = FROMCOORD(x);
1553 y = FROMCOORD(y);
1554 if (x < 0 || y < 0 || x >= w || y >= h)
1555 return NULL;
1556
1557 ui->drag_button = button;
1558 ui->dsx = ui->dex = x;
1559 ui->dsy = ui->dey = y;
1560 ui->drag_ok = TRUE;
1561 ui->cdisp = 0;
1562 return ""; /* ui updated */
1563 }
1564
1565 if ((IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) &&
1566 ui->drag_button > 0) {
1567 int xmin, ymin, xmax, ymax;
1568 char *buf, *sep;
1569 int buflen, bufsize, tmplen;
1570
1571 x = FROMCOORD(x);
1572 y = FROMCOORD(y);
1573 if (x < 0 || y < 0 || x >= w || y >= h) {
1574 ui->drag_ok = FALSE;
1575 } else {
1576 /*
1577 * Drags are limited to one row or column. Hence, we
1578 * work out which coordinate is closer to the drag
1579 * start, and move it _to_ the drag start.
1580 */
1581 if (abs(x - ui->dsx) < abs(y - ui->dsy))
1582 x = ui->dsx;
1583 else
1584 y = ui->dsy;
1585
1586 ui->dex = x;
1587 ui->dey = y;
1588
1589 ui->drag_ok = TRUE;
1590 }
1591
1592 if (IS_MOUSE_DRAG(button))
1593 return ""; /* ui updated */
1594
1595 /*
1596 * The drag has been released. Enact it.
1597 */
1598 if (!ui->drag_ok) {
1599 ui->drag_button = -1;
1600 return ""; /* drag was just cancelled */
1601 }
1602
1603 xmin = min(ui->dsx, ui->dex);
1604 xmax = max(ui->dsx, ui->dex);
1605 ymin = min(ui->dsy, ui->dey);
1606 ymax = max(ui->dsy, ui->dey);
1607 assert(0 <= xmin && xmin <= xmax && xmax < w);
1608 assert(0 <= ymin && ymin <= ymax && ymax < h);
1609
1610 buflen = 0;
1611 bufsize = 256;
1612 buf = snewn(bufsize, char);
1613 sep = "";
1614 for (y = ymin; y <= ymax; y++)
1615 for (x = xmin; x <= xmax; x++) {
1616 int v = drag_xform(ui, x, y, state->grid[y*w+x]);
1617 if (state->grid[y*w+x] != v) {
1618 tmplen = sprintf(tmpbuf, "%s%c%d,%d", sep,
1619 (int)(v == BLANK ? 'B' :
1620 v == TENT ? 'T' : 'N'),
1621 x, y);
1622 sep = ";";
1623
1624 if (buflen + tmplen >= bufsize) {
1625 bufsize = buflen + tmplen + 256;
1626 buf = sresize(buf, bufsize, char);
1627 }
1628
1629 strcpy(buf+buflen, tmpbuf);
1630 buflen += tmplen;
1631 }
1632 }
1633
1634 ui->drag_button = -1; /* drag is terminated */
1635
1636 if (buflen == 0) {
1637 sfree(buf);
1638 return ""; /* ui updated (drag was terminated) */
1639 } else {
1640 buf[buflen] = '\0';
1641 return buf;
1642 }
1643 }
1644
1645 if (IS_CURSOR_MOVE(button)) {
1646 ui->cdisp = 1;
1647 if (shift || control) {
1648 int len = 0, i, indices[2];
1649 indices[0] = ui->cx + w * ui->cy;
1650 move_cursor(button, &ui->cx, &ui->cy, w, h, 0);
1651 indices[1] = ui->cx + w * ui->cy;
1652
1653 /* NONTENTify all unique traversed eligible squares */
1654 for (i = 0; i <= (indices[0] != indices[1]); ++i)
1655 if (state->grid[indices[i]] == BLANK ||
1656 (control && state->grid[indices[i]] == TENT)) {
1657 len += sprintf(tmpbuf + len, "%sN%d,%d", len ? ";" : "",
1658 indices[i] % w, indices[i] / w);
1659 assert(len < lenof(tmpbuf));
1660 }
1661
1662 tmpbuf[len] = '\0';
1663 if (len) return dupstr(tmpbuf);
1664 } else
1665 move_cursor(button, &ui->cx, &ui->cy, w, h, 0);
1666 return "";
1667 }
1668 if (ui->cdisp) {
1669 char rep = 0;
1670 int v = state->grid[ui->cy*w+ui->cx];
1671
1672 if (v != TREE) {
1673#ifdef SINGLE_CURSOR_SELECT
1674 if (button == CURSOR_SELECT)
1675 /* SELECT cycles T, N, B */
1676 rep = v == BLANK ? 'T' : v == TENT ? 'N' : 'B';
1677#else
1678 if (button == CURSOR_SELECT)
1679 rep = v == BLANK ? 'T' : 'B';
1680 else if (button == CURSOR_SELECT2)
1681 rep = v == BLANK ? 'N' : 'B';
1682 else if (button == 'T' || button == 'N' || button == 'B')
1683 rep = (char)button;
1684#endif
1685 }
1686
1687 if (rep) {
1688 sprintf(tmpbuf, "%c%d,%d", (int)rep, ui->cx, ui->cy);
1689 return dupstr(tmpbuf);
1690 }
1691 } else if (IS_CURSOR_SELECT(button)) {
1692 ui->cdisp = 1;
1693 return "";
1694 }
1695
1696 return NULL;
1697}
1698
1699static game_state *execute_move(const game_state *state, const char *move)
1700{
1701 int w = state->p.w, h = state->p.h;
1702 char c;
1703 int x, y, m, n, i, j;
1704 game_state *ret = dup_game(state);
1705
1706 while (*move) {
1707 c = *move;
1708 if (c == 'S') {
1709 int i;
1710 ret->used_solve = TRUE;
1711 /*
1712 * Set all non-tree squares to NONTENT. The rest of the
1713 * solve move will fill the tents in over the top.
1714 */
1715 for (i = 0; i < w*h; i++)
1716 if (ret->grid[i] != TREE)
1717 ret->grid[i] = NONTENT;
1718 move++;
1719 } else if (c == 'B' || c == 'T' || c == 'N') {
1720 move++;
1721 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
1722 x < 0 || y < 0 || x >= w || y >= h) {
1723 free_game(ret);
1724 return NULL;
1725 }
1726 if (ret->grid[y*w+x] == TREE) {
1727 free_game(ret);
1728 return NULL;
1729 }
1730 ret->grid[y*w+x] = (c == 'B' ? BLANK : c == 'T' ? TENT : NONTENT);
1731 move += n;
1732 } else {
1733 free_game(ret);
1734 return NULL;
1735 }
1736 if (*move == ';')
1737 move++;
1738 else if (*move) {
1739 free_game(ret);
1740 return NULL;
1741 }
1742 }
1743
1744 /*
1745 * Check for completion.
1746 */
1747 for (i = n = m = 0; i < w*h; i++) {
1748 if (ret->grid[i] == TENT)
1749 n++;
1750 else if (ret->grid[i] == TREE)
1751 m++;
1752 }
1753 if (n == m) {
1754 int nedges, maxedges, *edges, *capacity, *flow;
1755
1756 /*
1757 * We have the right number of tents, which is a
1758 * precondition for the game being complete. Now check that
1759 * the numbers add up.
1760 */
1761 for (i = 0; i < w; i++) {
1762 n = 0;
1763 for (j = 0; j < h; j++)
1764 if (ret->grid[j*w+i] == TENT)
1765 n++;
1766 if (ret->numbers->numbers[i] != n)
1767 goto completion_check_done;
1768 }
1769 for (i = 0; i < h; i++) {
1770 n = 0;
1771 for (j = 0; j < w; j++)
1772 if (ret->grid[i*w+j] == TENT)
1773 n++;
1774 if (ret->numbers->numbers[w+i] != n)
1775 goto completion_check_done;
1776 }
1777 /*
1778 * Also, check that no two tents are adjacent.
1779 */
1780 for (y = 0; y < h; y++)
1781 for (x = 0; x < w; x++) {
1782 if (x+1 < w &&
1783 ret->grid[y*w+x] == TENT && ret->grid[y*w+x+1] == TENT)
1784 goto completion_check_done;
1785 if (y+1 < h &&
1786 ret->grid[y*w+x] == TENT && ret->grid[(y+1)*w+x] == TENT)
1787 goto completion_check_done;
1788 if (x+1 < w && y+1 < h) {
1789 if (ret->grid[y*w+x] == TENT &&
1790 ret->grid[(y+1)*w+(x+1)] == TENT)
1791 goto completion_check_done;
1792 if (ret->grid[(y+1)*w+x] == TENT &&
1793 ret->grid[y*w+(x+1)] == TENT)
1794 goto completion_check_done;
1795 }
1796 }
1797
1798 /*
1799 * OK; we have the right number of tents, they match the
1800 * numeric clues, and they satisfy the non-adjacency
1801 * criterion. Finally, we need to verify that they can be
1802 * placed in a one-to-one matching with the trees such that
1803 * every tent is orthogonally adjacent to its tree.
1804 *
1805 * This bit is where the hard work comes in: we have to do
1806 * it by finding such a matching using maxflow.
1807 *
1808 * So we construct a network with one special source node,
1809 * one special sink node, one node per tent, and one node
1810 * per tree.
1811 */
1812 maxedges = 6 * m;
1813 edges = snewn(2 * maxedges, int);
1814 capacity = snewn(maxedges, int);
1815 flow = snewn(maxedges, int);
1816 nedges = 0;
1817 /*
1818 * Node numbering:
1819 *
1820 * 0..w*h trees/tents
1821 * w*h source
1822 * w*h+1 sink
1823 */
1824 for (y = 0; y < h; y++)
1825 for (x = 0; x < w; x++)
1826 if (ret->grid[y*w+x] == TREE) {
1827 int d;
1828
1829 /*
1830 * Here we use the direction enum declared for
1831 * the solver. We make use of the fact that the
1832 * directions are declared in the order
1833 * U,L,R,D, meaning that we go through the four
1834 * neighbours of any square in numerically
1835 * increasing order.
1836 */
1837 for (d = 1; d < MAXDIR; d++) {
1838 int x2 = x + dx(d), y2 = y + dy(d);
1839 if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h &&
1840 ret->grid[y2*w+x2] == TENT) {
1841 assert(nedges < maxedges);
1842 edges[nedges*2] = y*w+x;
1843 edges[nedges*2+1] = y2*w+x2;
1844 capacity[nedges] = 1;
1845 nedges++;
1846 }
1847 }
1848 } else if (ret->grid[y*w+x] == TENT) {
1849 assert(nedges < maxedges);
1850 edges[nedges*2] = y*w+x;
1851 edges[nedges*2+1] = w*h+1; /* edge going to sink */
1852 capacity[nedges] = 1;
1853 nedges++;
1854 }
1855 for (y = 0; y < h; y++)
1856 for (x = 0; x < w; x++)
1857 if (ret->grid[y*w+x] == TREE) {
1858 assert(nedges < maxedges);
1859 edges[nedges*2] = w*h; /* edge coming from source */
1860 edges[nedges*2+1] = y*w+x;
1861 capacity[nedges] = 1;
1862 nedges++;
1863 }
1864 n = maxflow(w*h+2, w*h, w*h+1, nedges, edges, capacity, flow, NULL);
1865
1866 sfree(flow);
1867 sfree(capacity);
1868 sfree(edges);
1869
1870 if (n != m)
1871 goto completion_check_done;
1872
1873 /*
1874 * We haven't managed to fault the grid on any count. Score!
1875 */
1876 ret->completed = TRUE;
1877 }
1878 completion_check_done:
1879
1880 return ret;
1881}
1882
1883/* ----------------------------------------------------------------------
1884 * Drawing routines.
1885 */
1886
1887static void game_compute_size(const game_params *params, int tilesize,
1888 int *x, int *y)
1889{
1890 /* fool the macros */
1891 struct dummy { int tilesize; } dummy, *ds = &dummy;
1892 dummy.tilesize = tilesize;
1893
1894 *x = TLBORDER + BRBORDER + TILESIZE * params->w;
1895 *y = TLBORDER + BRBORDER + TILESIZE * params->h;
1896}
1897
1898static void game_set_size(drawing *dr, game_drawstate *ds,
1899 const game_params *params, int tilesize)
1900{
1901 ds->tilesize = tilesize;
1902}
1903
1904static float *game_colours(frontend *fe, int *ncolours)
1905{
1906 float *ret = snewn(3 * NCOLOURS, float);
1907
1908 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1909
1910 ret[COL_GRID * 3 + 0] = 0.0F;
1911 ret[COL_GRID * 3 + 1] = 0.0F;
1912 ret[COL_GRID * 3 + 2] = 0.0F;
1913
1914 ret[COL_GRASS * 3 + 0] = 0.7F;
1915 ret[COL_GRASS * 3 + 1] = 1.0F;
1916 ret[COL_GRASS * 3 + 2] = 0.5F;
1917
1918 ret[COL_TREETRUNK * 3 + 0] = 0.6F;
1919 ret[COL_TREETRUNK * 3 + 1] = 0.4F;
1920 ret[COL_TREETRUNK * 3 + 2] = 0.0F;
1921
1922 ret[COL_TREELEAF * 3 + 0] = 0.0F;
1923 ret[COL_TREELEAF * 3 + 1] = 0.7F;
1924 ret[COL_TREELEAF * 3 + 2] = 0.0F;
1925
1926 ret[COL_TENT * 3 + 0] = 0.8F;
1927 ret[COL_TENT * 3 + 1] = 0.7F;
1928 ret[COL_TENT * 3 + 2] = 0.0F;
1929
1930 ret[COL_ERROR * 3 + 0] = 1.0F;
1931 ret[COL_ERROR * 3 + 1] = 0.0F;
1932 ret[COL_ERROR * 3 + 2] = 0.0F;
1933
1934 ret[COL_ERRTEXT * 3 + 0] = 1.0F;
1935 ret[COL_ERRTEXT * 3 + 1] = 1.0F;
1936 ret[COL_ERRTEXT * 3 + 2] = 1.0F;
1937
1938 ret[COL_ERRTRUNK * 3 + 0] = 0.6F;
1939 ret[COL_ERRTRUNK * 3 + 1] = 0.0F;
1940 ret[COL_ERRTRUNK * 3 + 2] = 0.0F;
1941
1942 *ncolours = NCOLOURS;
1943 return ret;
1944}
1945
1946static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1947{
1948 int w = state->p.w, h = state->p.h;
1949 struct game_drawstate *ds = snew(struct game_drawstate);
1950 int i;
1951
1952 ds->tilesize = 0;
1953 ds->started = FALSE;
1954 ds->p = state->p; /* structure copy */
1955 ds->drawn = snewn(w*h, int);
1956 for (i = 0; i < w*h; i++)
1957 ds->drawn[i] = MAGIC;
1958 ds->numbersdrawn = snewn(w+h, int);
1959 for (i = 0; i < w+h; i++)
1960 ds->numbersdrawn[i] = 2;
1961 ds->cx = ds->cy = -1;
1962
1963 return ds;
1964}
1965
1966static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1967{
1968 sfree(ds->drawn);
1969 sfree(ds->numbersdrawn);
1970 sfree(ds);
1971}
1972
1973enum {
1974 ERR_ADJ_TOPLEFT = 4,
1975 ERR_ADJ_TOP,
1976 ERR_ADJ_TOPRIGHT,
1977 ERR_ADJ_LEFT,
1978 ERR_ADJ_RIGHT,
1979 ERR_ADJ_BOTLEFT,
1980 ERR_ADJ_BOT,
1981 ERR_ADJ_BOTRIGHT,
1982 ERR_OVERCOMMITTED
1983};
1984
1985static int *find_errors(const game_state *state, char *grid)
1986{
1987 int w = state->p.w, h = state->p.h;
1988 int *ret = snewn(w*h + w + h, int);
1989 int *tmp = snewn(w*h*2, int), *dsf = tmp + w*h;
1990 int x, y;
1991
1992 /*
1993 * This function goes through a grid and works out where to
1994 * highlight play errors in red. The aim is that it should
1995 * produce at least one error highlight for any complete grid
1996 * (or complete piece of grid) violating a puzzle constraint, so
1997 * that a grid containing no BLANK squares is either a win or is
1998 * marked up in some way that indicates why not.
1999 *
2000 * So it's easy enough to highlight errors in the numeric clues
2001 * - just light up any row or column number which is not
2002 * fulfilled - and it's just as easy to highlight adjacent
2003 * tents. The difficult bit is highlighting failures in the
2004 * tent/tree matching criterion.
2005 *
2006 * A natural approach would seem to be to apply the maxflow
2007 * algorithm to find the tent/tree matching; if this fails, it
2008 * must necessarily terminate with a min-cut which can be
2009 * reinterpreted as some set of trees which have too few tents
2010 * between them (or vice versa). However, it's bad for
2011 * localising errors, because it's not easy to make the
2012 * algorithm narrow down to the _smallest_ such set of trees: if
2013 * trees A and B have only one tent between them, for instance,
2014 * it might perfectly well highlight not only A and B but also
2015 * trees C and D which are correctly matched on the far side of
2016 * the grid, on the grounds that those four trees between them
2017 * have only three tents.
2018 *
2019 * Also, that approach fares badly when you introduce the
2020 * additional requirement that incomplete grids should have
2021 * errors highlighted only when they can be proved to be errors
2022 * - so that trees should not be marked as having too few tents
2023 * if there are enough BLANK squares remaining around them that
2024 * could be turned into the missing tents (to do so would be
2025 * patronising, since the overwhelming likelihood is not that
2026 * the player has forgotten to put a tree there but that they
2027 * have merely not put one there _yet_). However, tents with too
2028 * few trees can be marked immediately, since those are
2029 * definitely player error.
2030 *
2031 * So I adopt an alternative approach, which is to consider the
2032 * bipartite adjacency graph between trees and tents
2033 * ('bipartite' in the sense that for these purposes I
2034 * deliberately ignore two adjacent trees or two adjacent
2035 * tents), divide that graph up into its connected components
2036 * using a dsf, and look for components which contain different
2037 * numbers of trees and tents. This allows me to highlight
2038 * groups of tents with too few trees between them immediately,
2039 * and then in order to find groups of trees with too few tents
2040 * I redo the same process but counting BLANKs as potential
2041 * tents (so that the only trees highlighted are those
2042 * surrounded by enough NONTENTs to make it impossible to give
2043 * them enough tents).
2044 *
2045 * However, this technique is incomplete: it is not a sufficient
2046 * condition for the existence of a perfect matching that every
2047 * connected component of the graph has the same number of tents
2048 * and trees. An example of a graph which satisfies the latter
2049 * condition but still has no perfect matching is
2050 *
2051 * A B C
2052 * | / ,/|
2053 * | / ,'/ |
2054 * | / ,' / |
2055 * |/,' / |
2056 * 1 2 3
2057 *
2058 * which can be realised in Tents as
2059 *
2060 * B
2061 * A 1 C 2
2062 * 3
2063 *
2064 * The matching-error highlighter described above will not mark
2065 * this construction as erroneous. However, something else will:
2066 * the three tents in the above diagram (let us suppose A,B,C
2067 * are the tents, though it doesn't matter which) contain two
2068 * diagonally adjacent pairs. So there will be _an_ error
2069 * highlighted for the above layout, even though not all types
2070 * of error will be highlighted.
2071 *
2072 * And in fact we can prove that this will always be the case:
2073 * that the shortcomings of the matching-error highlighter will
2074 * always be made up for by the easy tent adjacency highlighter.
2075 *
2076 * Lemma: Let G be a bipartite graph between n trees and n
2077 * tents, which is connected, and in which no tree has degree
2078 * more than two (but a tent may). Then G has a perfect matching.
2079 *
2080 * (Note: in the statement and proof of the Lemma I will
2081 * consistently use 'tree' to indicate a type of graph vertex as
2082 * opposed to a tent, and not to indicate a tree in the graph-
2083 * theoretic sense.)
2084 *
2085 * Proof:
2086 *
2087 * If we can find a tent of degree 1 joined to a tree of degree
2088 * 2, then any perfect matching must pair that tent with that
2089 * tree. Hence, we can remove both, leaving a smaller graph G'
2090 * which still satisfies all the conditions of the Lemma, and
2091 * which has a perfect matching iff G does.
2092 *
2093 * So, wlog, we may assume G contains no tent of degree 1 joined
2094 * to a tree of degree 2; if it does, we can reduce it as above.
2095 *
2096 * If G has no tent of degree 1 at all, then every tent has
2097 * degree at least two, so there are at least 2n edges in the
2098 * graph. But every tree has degree at most two, so there are at
2099 * most 2n edges. Hence there must be exactly 2n edges, so every
2100 * tree and every tent must have degree exactly two, which means
2101 * that the whole graph consists of a single loop (by
2102 * connectedness), and therefore certainly has a perfect
2103 * matching.
2104 *
2105 * Alternatively, if G does have a tent of degree 1 but it is
2106 * not connected to a tree of degree 2, then the tree it is
2107 * connected to must have degree 1 - and, by connectedness, that
2108 * must mean that that tent and that tree between them form the
2109 * entire graph. This trivial graph has a trivial perfect
2110 * matching. []
2111 *
2112 * That proves the lemma. Hence, in any case where the matching-
2113 * error highlighter fails to highlight an erroneous component
2114 * (because it has the same number of tents as trees, but they
2115 * cannot be matched up), the above lemma tells us that there
2116 * must be a tree with degree more than 2, i.e. a tree
2117 * orthogonally adjacent to at least three tents. But in that
2118 * case, there must be some pair of those three tents which are
2119 * diagonally adjacent to each other, so the tent-adjacency
2120 * highlighter will necessarily show an error. So any filled
2121 * layout in Tents which is not a correct solution to the puzzle
2122 * must have _some_ error highlighted by the subroutine below.
2123 *
2124 * (Of course it would be nicer if we could highlight all
2125 * errors: in the above example layout, we would like to
2126 * highlight tents A,B as having too few trees between them, and
2127 * trees 2,3 as having too few tents, in addition to marking the
2128 * adjacency problems. But I can't immediately think of any way
2129 * to find the smallest sets of such tents and trees without an
2130 * O(2^N) loop over all subsets of a given component.)
2131 */
2132
2133 /*
2134 * ret[0] through to ret[w*h-1] give error markers for the grid
2135 * squares. After that, ret[w*h] to ret[w*h+w-1] give error
2136 * markers for the column numbers, and ret[w*h+w] to
2137 * ret[w*h+w+h-1] for the row numbers.
2138 */
2139
2140 /*
2141 * Spot tent-adjacency violations.
2142 */
2143 for (x = 0; x < w*h; x++)
2144 ret[x] = 0;
2145 for (y = 0; y < h; y++) {
2146 for (x = 0; x < w; x++) {
2147 if (y+1 < h && x+1 < w &&
2148 ((grid[y*w+x] == TENT &&
2149 grid[(y+1)*w+(x+1)] == TENT) ||
2150 (grid[(y+1)*w+x] == TENT &&
2151 grid[y*w+(x+1)] == TENT))) {
2152 ret[y*w+x] |= 1 << ERR_ADJ_BOTRIGHT;
2153 ret[(y+1)*w+x] |= 1 << ERR_ADJ_TOPRIGHT;
2154 ret[y*w+(x+1)] |= 1 << ERR_ADJ_BOTLEFT;
2155 ret[(y+1)*w+(x+1)] |= 1 << ERR_ADJ_TOPLEFT;
2156 }
2157 if (y+1 < h &&
2158 grid[y*w+x] == TENT &&
2159 grid[(y+1)*w+x] == TENT) {
2160 ret[y*w+x] |= 1 << ERR_ADJ_BOT;
2161 ret[(y+1)*w+x] |= 1 << ERR_ADJ_TOP;
2162 }
2163 if (x+1 < w &&
2164 grid[y*w+x] == TENT &&
2165 grid[y*w+(x+1)] == TENT) {
2166 ret[y*w+x] |= 1 << ERR_ADJ_RIGHT;
2167 ret[y*w+(x+1)] |= 1 << ERR_ADJ_LEFT;
2168 }
2169 }
2170 }
2171
2172 /*
2173 * Spot numeric clue violations.
2174 */
2175 for (x = 0; x < w; x++) {
2176 int tents = 0, maybetents = 0;
2177 for (y = 0; y < h; y++) {
2178 if (grid[y*w+x] == TENT)
2179 tents++;
2180 else if (grid[y*w+x] == BLANK)
2181 maybetents++;
2182 }
2183 ret[w*h+x] = (tents > state->numbers->numbers[x] ||
2184 tents + maybetents < state->numbers->numbers[x]);
2185 }
2186 for (y = 0; y < h; y++) {
2187 int tents = 0, maybetents = 0;
2188 for (x = 0; x < w; x++) {
2189 if (grid[y*w+x] == TENT)
2190 tents++;
2191 else if (grid[y*w+x] == BLANK)
2192 maybetents++;
2193 }
2194 ret[w*h+w+y] = (tents > state->numbers->numbers[w+y] ||
2195 tents + maybetents < state->numbers->numbers[w+y]);
2196 }
2197
2198 /*
2199 * Identify groups of tents with too few trees between them,
2200 * which we do by constructing the connected components of the
2201 * bipartite adjacency graph between tents and trees
2202 * ('bipartite' in the sense that we deliberately ignore
2203 * adjacency between tents or between trees), and highlighting
2204 * all the tents in any component which has a smaller tree
2205 * count.
2206 */
2207 dsf_init(dsf, w*h);
2208 /* Construct the equivalence classes. */
2209 for (y = 0; y < h; y++) {
2210 for (x = 0; x < w-1; x++) {
2211 if ((grid[y*w+x] == TREE && grid[y*w+x+1] == TENT) ||
2212 (grid[y*w+x] == TENT && grid[y*w+x+1] == TREE))
2213 dsf_merge(dsf, y*w+x, y*w+x+1);
2214 }
2215 }
2216 for (y = 0; y < h-1; y++) {
2217 for (x = 0; x < w; x++) {
2218 if ((grid[y*w+x] == TREE && grid[(y+1)*w+x] == TENT) ||
2219 (grid[y*w+x] == TENT && grid[(y+1)*w+x] == TREE))
2220 dsf_merge(dsf, y*w+x, (y+1)*w+x);
2221 }
2222 }
2223 /* Count up the tent/tree difference in each one. */
2224 for (x = 0; x < w*h; x++)
2225 tmp[x] = 0;
2226 for (x = 0; x < w*h; x++) {
2227 y = dsf_canonify(dsf, x);
2228 if (grid[x] == TREE)
2229 tmp[y]++;
2230 else if (grid[x] == TENT)
2231 tmp[y]--;
2232 }
2233 /* And highlight any tent belonging to an equivalence class with
2234 * a score less than zero. */
2235 for (x = 0; x < w*h; x++) {
2236 y = dsf_canonify(dsf, x);
2237 if (grid[x] == TENT && tmp[y] < 0)
2238 ret[x] |= 1 << ERR_OVERCOMMITTED;
2239 }
2240
2241 /*
2242 * Identify groups of trees with too few tents between them.
2243 * This is done similarly, except that we now count BLANK as
2244 * equivalent to TENT, i.e. we only highlight such trees when
2245 * the user hasn't even left _room_ to provide tents for them
2246 * all. (Otherwise, we'd highlight all trees red right at the
2247 * start of the game, before the user had done anything wrong!)
2248 */
2249#define TENT(x) ((x)==TENT || (x)==BLANK)
2250 dsf_init(dsf, w*h);
2251 /* Construct the equivalence classes. */
2252 for (y = 0; y < h; y++) {
2253 for (x = 0; x < w-1; x++) {
2254 if ((grid[y*w+x] == TREE && TENT(grid[y*w+x+1])) ||
2255 (TENT(grid[y*w+x]) && grid[y*w+x+1] == TREE))
2256 dsf_merge(dsf, y*w+x, y*w+x+1);
2257 }
2258 }
2259 for (y = 0; y < h-1; y++) {
2260 for (x = 0; x < w; x++) {
2261 if ((grid[y*w+x] == TREE && TENT(grid[(y+1)*w+x])) ||
2262 (TENT(grid[y*w+x]) && grid[(y+1)*w+x] == TREE))
2263 dsf_merge(dsf, y*w+x, (y+1)*w+x);
2264 }
2265 }
2266 /* Count up the tent/tree difference in each one. */
2267 for (x = 0; x < w*h; x++)
2268 tmp[x] = 0;
2269 for (x = 0; x < w*h; x++) {
2270 y = dsf_canonify(dsf, x);
2271 if (grid[x] == TREE)
2272 tmp[y]++;
2273 else if (TENT(grid[x]))
2274 tmp[y]--;
2275 }
2276 /* And highlight any tree belonging to an equivalence class with
2277 * a score more than zero. */
2278 for (x = 0; x < w*h; x++) {
2279 y = dsf_canonify(dsf, x);
2280 if (grid[x] == TREE && tmp[y] > 0)
2281 ret[x] |= 1 << ERR_OVERCOMMITTED;
2282 }
2283#undef TENT
2284
2285 sfree(tmp);
2286 return ret;
2287}
2288
2289static void draw_err_adj(drawing *dr, game_drawstate *ds, int x, int y)
2290{
2291 int coords[8];
2292 int yext, xext;
2293
2294 /*
2295 * Draw a diamond.
2296 */
2297 coords[0] = x - TILESIZE*2/5;
2298 coords[1] = y;
2299 coords[2] = x;
2300 coords[3] = y - TILESIZE*2/5;
2301 coords[4] = x + TILESIZE*2/5;
2302 coords[5] = y;
2303 coords[6] = x;
2304 coords[7] = y + TILESIZE*2/5;
2305 draw_polygon(dr, coords, 4, COL_ERROR, COL_GRID);
2306
2307 /*
2308 * Draw an exclamation mark in the diamond. This turns out to
2309 * look unpleasantly off-centre if done via draw_text, so I do
2310 * it by hand on the basis that exclamation marks aren't that
2311 * difficult to draw...
2312 */
2313 xext = TILESIZE/16;
2314 yext = TILESIZE*2/5 - (xext*2+2);
2315 draw_rect(dr, x-xext, y-yext, xext*2+1, yext*2+1 - (xext*3),
2316 COL_ERRTEXT);
2317 draw_rect(dr, x-xext, y+yext-xext*2+1, xext*2+1, xext*2, COL_ERRTEXT);
2318}
2319
2320static void draw_tile(drawing *dr, game_drawstate *ds,
2321 int x, int y, int v, int cur, int printing)
2322{
2323 int err;
2324 int tx = COORD(x), ty = COORD(y);
2325 int cx = tx + TILESIZE/2, cy = ty + TILESIZE/2;
2326
2327 err = v & ~15;
2328 v &= 15;
2329
2330 clip(dr, tx, ty, TILESIZE, TILESIZE);
2331
2332 if (!printing) {
2333 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, COL_GRID);
2334 draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1,
2335 (v == BLANK ? COL_BACKGROUND : COL_GRASS));
2336 }
2337
2338 if (v == TREE) {
2339 int i;
2340
2341 (printing ? draw_rect_outline : draw_rect)
2342 (dr, cx-TILESIZE/15, ty+TILESIZE*3/10,
2343 2*(TILESIZE/15)+1, (TILESIZE*9/10 - TILESIZE*3/10),
2344 (err & (1<<ERR_OVERCOMMITTED) ? COL_ERRTRUNK : COL_TREETRUNK));
2345
2346 for (i = 0; i < (printing ? 2 : 1); i++) {
2347 int col = (i == 1 ? COL_BACKGROUND :
2348 (err & (1<<ERR_OVERCOMMITTED) ? COL_ERROR :
2349 COL_TREELEAF));
2350 int sub = i * (TILESIZE/32);
2351 draw_circle(dr, cx, ty+TILESIZE*4/10, TILESIZE/4 - sub,
2352 col, col);
2353 draw_circle(dr, cx+TILESIZE/5, ty+TILESIZE/4, TILESIZE/8 - sub,
2354 col, col);
2355 draw_circle(dr, cx-TILESIZE/5, ty+TILESIZE/4, TILESIZE/8 - sub,
2356 col, col);
2357 draw_circle(dr, cx+TILESIZE/4, ty+TILESIZE*6/13, TILESIZE/8 - sub,
2358 col, col);
2359 draw_circle(dr, cx-TILESIZE/4, ty+TILESIZE*6/13, TILESIZE/8 - sub,
2360 col, col);
2361 }
2362 } else if (v == TENT) {
2363 int coords[6];
2364 int col;
2365 coords[0] = cx - TILESIZE/3;
2366 coords[1] = cy + TILESIZE/3;
2367 coords[2] = cx + TILESIZE/3;
2368 coords[3] = cy + TILESIZE/3;
2369 coords[4] = cx;
2370 coords[5] = cy - TILESIZE/3;
2371 col = (err & (1<<ERR_OVERCOMMITTED) ? COL_ERROR : COL_TENT);
2372 draw_polygon(dr, coords, 3, (printing ? -1 : col), col);
2373 }
2374
2375 if (err & (1 << ERR_ADJ_TOPLEFT))
2376 draw_err_adj(dr, ds, tx, ty);
2377 if (err & (1 << ERR_ADJ_TOP))
2378 draw_err_adj(dr, ds, tx+TILESIZE/2, ty);
2379 if (err & (1 << ERR_ADJ_TOPRIGHT))
2380 draw_err_adj(dr, ds, tx+TILESIZE, ty);
2381 if (err & (1 << ERR_ADJ_LEFT))
2382 draw_err_adj(dr, ds, tx, ty+TILESIZE/2);
2383 if (err & (1 << ERR_ADJ_RIGHT))
2384 draw_err_adj(dr, ds, tx+TILESIZE, ty+TILESIZE/2);
2385 if (err & (1 << ERR_ADJ_BOTLEFT))
2386 draw_err_adj(dr, ds, tx, ty+TILESIZE);
2387 if (err & (1 << ERR_ADJ_BOT))
2388 draw_err_adj(dr, ds, tx+TILESIZE/2, ty+TILESIZE);
2389 if (err & (1 << ERR_ADJ_BOTRIGHT))
2390 draw_err_adj(dr, ds, tx+TILESIZE, ty+TILESIZE);
2391
2392 if (cur) {
2393 int coff = TILESIZE/8;
2394 draw_rect_outline(dr, tx + coff, ty + coff,
2395 TILESIZE - coff*2 + 1, TILESIZE - coff*2 + 1,
2396 COL_GRID);
2397 }
2398
2399 unclip(dr);
2400 draw_update(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1);
2401}
2402
2403/*
2404 * Internal redraw function, used for printing as well as drawing.
2405 */
2406static void int_redraw(drawing *dr, game_drawstate *ds,
2407 const game_state *oldstate, const game_state *state,
2408 int dir, const game_ui *ui,
2409 float animtime, float flashtime, int printing)
2410{
2411 int w = state->p.w, h = state->p.h;
2412 int x, y, flashing;
2413 int cx = -1, cy = -1;
2414 int cmoved = 0;
2415 char *tmpgrid;
2416 int *errors;
2417
2418 if (ui) {
2419 if (ui->cdisp) { cx = ui->cx; cy = ui->cy; }
2420 if (cx != ds->cx || cy != ds->cy) cmoved = 1;
2421 }
2422
2423 if (printing || !ds->started) {
2424 if (!printing) {
2425 int ww, wh;
2426 game_compute_size(&state->p, TILESIZE, &ww, &wh);
2427 draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND);
2428 draw_update(dr, 0, 0, ww, wh);
2429 ds->started = TRUE;
2430 }
2431
2432 if (printing)
2433 print_line_width(dr, TILESIZE/64);
2434
2435 /*
2436 * Draw the grid.
2437 */
2438 for (y = 0; y <= h; y++)
2439 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), COL_GRID);
2440 for (x = 0; x <= w; x++)
2441 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), COL_GRID);
2442 }
2443
2444 if (flashtime > 0)
2445 flashing = (int)(flashtime * 3 / FLASH_TIME) != 1;
2446 else
2447 flashing = FALSE;
2448
2449 /*
2450 * Find errors. For this we use _part_ of the information from a
2451 * currently active drag: we transform dsx,dsy but not anything
2452 * else. (This seems to strike a good compromise between having
2453 * the error highlights respond instantly to single clicks, but
2454 * not giving constant feedback during a right-drag.)
2455 */
2456 if (ui && ui->drag_button >= 0) {
2457 tmpgrid = snewn(w*h, char);
2458 memcpy(tmpgrid, state->grid, w*h);
2459 tmpgrid[ui->dsy * w + ui->dsx] =
2460 drag_xform(ui, ui->dsx, ui->dsy, tmpgrid[ui->dsy * w + ui->dsx]);
2461 errors = find_errors(state, tmpgrid);
2462 sfree(tmpgrid);
2463 } else {
2464 errors = find_errors(state, state->grid);
2465 }
2466
2467 /*
2468 * Draw the grid.
2469 */
2470 for (y = 0; y < h; y++) {
2471 for (x = 0; x < w; x++) {
2472 int v = state->grid[y*w+x];
2473 int credraw = 0;
2474
2475 /*
2476 * We deliberately do not take drag_ok into account
2477 * here, because user feedback suggests that it's
2478 * marginally nicer not to have the drag effects
2479 * flickering on and off disconcertingly.
2480 */
2481 if (ui && ui->drag_button >= 0)
2482 v = drag_xform(ui, x, y, v);
2483
2484 if (flashing && (v == TREE || v == TENT))
2485 v = NONTENT;
2486
2487 if (cmoved) {
2488 if ((x == cx && y == cy) ||
2489 (x == ds->cx && y == ds->cy)) credraw = 1;
2490 }
2491
2492 v |= errors[y*w+x];
2493
2494 if (printing || ds->drawn[y*w+x] != v || credraw) {
2495 draw_tile(dr, ds, x, y, v, (x == cx && y == cy), printing);
2496 if (!printing)
2497 ds->drawn[y*w+x] = v;
2498 }
2499 }
2500 }
2501
2502 /*
2503 * Draw (or redraw, if their error-highlighted state has
2504 * changed) the numbers.
2505 */
2506 for (x = 0; x < w; x++) {
2507 if (printing || ds->numbersdrawn[x] != errors[w*h+x]) {
2508 char buf[80];
2509 draw_rect(dr, COORD(x), COORD(h)+1, TILESIZE, BRBORDER-1,
2510 COL_BACKGROUND);
2511 sprintf(buf, "%d", state->numbers->numbers[x]);
2512 draw_text(dr, COORD(x) + TILESIZE/2, COORD(h+1),
2513 FONT_VARIABLE, TILESIZE/2, ALIGN_HCENTRE|ALIGN_VNORMAL,
2514 (errors[w*h+x] ? COL_ERROR : COL_GRID), buf);
2515 draw_update(dr, COORD(x), COORD(h)+1, TILESIZE, BRBORDER-1);
2516 if (!printing)
2517 ds->numbersdrawn[x] = errors[w*h+x];
2518 }
2519 }
2520 for (y = 0; y < h; y++) {
2521 if (printing || ds->numbersdrawn[w+y] != errors[w*h+w+y]) {
2522 char buf[80];
2523 draw_rect(dr, COORD(w)+1, COORD(y), BRBORDER-1, TILESIZE,
2524 COL_BACKGROUND);
2525 sprintf(buf, "%d", state->numbers->numbers[w+y]);
2526 draw_text(dr, COORD(w+1), COORD(y) + TILESIZE/2,
2527 FONT_VARIABLE, TILESIZE/2, ALIGN_HRIGHT|ALIGN_VCENTRE,
2528 (errors[w*h+w+y] ? COL_ERROR : COL_GRID), buf);
2529 draw_update(dr, COORD(w)+1, COORD(y), BRBORDER-1, TILESIZE);
2530 if (!printing)
2531 ds->numbersdrawn[w+y] = errors[w*h+w+y];
2532 }
2533 }
2534
2535 if (cmoved) {
2536 ds->cx = cx;
2537 ds->cy = cy;
2538 }
2539
2540 sfree(errors);
2541}
2542
2543static void game_redraw(drawing *dr, game_drawstate *ds,
2544 const game_state *oldstate, const game_state *state,
2545 int dir, const game_ui *ui,
2546 float animtime, float flashtime)
2547{
2548 int_redraw(dr, ds, oldstate, state, dir, ui, animtime, flashtime, FALSE);
2549}
2550
2551static float game_anim_length(const game_state *oldstate,
2552 const game_state *newstate, int dir, game_ui *ui)
2553{
2554 return 0.0F;
2555}
2556
2557static float game_flash_length(const game_state *oldstate,
2558 const game_state *newstate, int dir, game_ui *ui)
2559{
2560 if (!oldstate->completed && newstate->completed &&
2561 !oldstate->used_solve && !newstate->used_solve)
2562 return FLASH_TIME;
2563
2564 return 0.0F;
2565}
2566
2567static int game_status(const game_state *state)
2568{
2569 return state->completed ? +1 : 0;
2570}
2571
2572static int game_timing_state(const game_state *state, game_ui *ui)
2573{
2574 return TRUE;
2575}
2576
2577static void game_print_size(const game_params *params, float *x, float *y)
2578{
2579 int pw, ph;
2580
2581 /*
2582 * I'll use 6mm squares by default.
2583 */
2584 game_compute_size(params, 600, &pw, &ph);
2585 *x = pw / 100.0F;
2586 *y = ph / 100.0F;
2587}
2588
2589static void game_print(drawing *dr, const game_state *state, int tilesize)
2590{
2591 int c;
2592
2593 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2594 game_drawstate ads, *ds = &ads;
2595 game_set_size(dr, ds, NULL, tilesize);
2596
2597 c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
2598 c = print_mono_colour(dr, 0); assert(c == COL_GRID);
2599 c = print_mono_colour(dr, 1); assert(c == COL_GRASS);
2600 c = print_mono_colour(dr, 0); assert(c == COL_TREETRUNK);
2601 c = print_mono_colour(dr, 0); assert(c == COL_TREELEAF);
2602 c = print_mono_colour(dr, 0); assert(c == COL_TENT);
2603
2604 int_redraw(dr, ds, NULL, state, +1, NULL, 0.0F, 0.0F, TRUE);
2605}
2606
2607#ifdef COMBINED
2608#define thegame tents
2609#endif
2610
2611const struct game thegame = {
2612 "Tents", "games.tents", "tents",
2613 default_params,
2614 game_fetch_preset, NULL,
2615 decode_params,
2616 encode_params,
2617 free_params,
2618 dup_params,
2619 TRUE, game_configure, custom_params,
2620 validate_params,
2621 new_game_desc,
2622 validate_desc,
2623 new_game,
2624 dup_game,
2625 free_game,
2626 TRUE, solve_game,
2627 TRUE, game_can_format_as_text_now, game_text_format,
2628 new_ui,
2629 free_ui,
2630 encode_ui,
2631 decode_ui,
2632 game_changed_state,
2633 interpret_move,
2634 execute_move,
2635 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2636 game_colours,
2637 game_new_drawstate,
2638 game_free_drawstate,
2639 game_redraw,
2640 game_anim_length,
2641 game_flash_length,
2642 game_status,
2643 TRUE, FALSE, game_print_size, game_print,
2644 FALSE, /* wants_statusbar */
2645 FALSE, game_timing_state,
2646 REQUIRE_RBUTTON, /* flags */
2647};
2648
2649#ifdef STANDALONE_SOLVER
2650
2651#include <stdarg.h>
2652
2653int main(int argc, char **argv)
2654{
2655 game_params *p;
2656 game_state *s, *s2;
2657 char *id = NULL, *desc, *err;
2658 int grade = FALSE;
2659 int ret, diff, really_verbose = FALSE;
2660 struct solver_scratch *sc;
2661
2662 while (--argc > 0) {
2663 char *p = *++argv;
2664 if (!strcmp(p, "-v")) {
2665 really_verbose = TRUE;
2666 } else if (!strcmp(p, "-g")) {
2667 grade = TRUE;
2668 } else if (*p == '-') {
2669 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2670 return 1;
2671 } else {
2672 id = p;
2673 }
2674 }
2675
2676 if (!id) {
2677 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2678 return 1;
2679 }
2680
2681 desc = strchr(id, ':');
2682 if (!desc) {
2683 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2684 return 1;
2685 }
2686 *desc++ = '\0';
2687
2688 p = default_params();
2689 decode_params(p, id);
2690 err = validate_desc(p, desc);
2691 if (err) {
2692 fprintf(stderr, "%s: %s\n", argv[0], err);
2693 return 1;
2694 }
2695 s = new_game(NULL, p, desc);
2696 s2 = new_game(NULL, p, desc);
2697
2698 sc = new_scratch(p->w, p->h);
2699
2700 /*
2701 * When solving an Easy puzzle, we don't want to bother the
2702 * user with Hard-level deductions. For this reason, we grade
2703 * the puzzle internally before doing anything else.
2704 */
2705 ret = -1; /* placate optimiser */
2706 for (diff = 0; diff < DIFFCOUNT; diff++) {
2707 ret = tents_solve(p->w, p->h, s->grid, s->numbers->numbers,
2708 s2->grid, sc, diff);
2709 if (ret < 2)
2710 break;
2711 }
2712
2713 if (diff == DIFFCOUNT) {
2714 if (grade)
2715 printf("Difficulty rating: too hard to solve internally\n");
2716 else
2717 printf("Unable to find a unique solution\n");
2718 } else {
2719 if (grade) {
2720 if (ret == 0)
2721 printf("Difficulty rating: impossible (no solution exists)\n");
2722 else if (ret == 1)
2723 printf("Difficulty rating: %s\n", tents_diffnames[diff]);
2724 } else {
2725 verbose = really_verbose;
2726 ret = tents_solve(p->w, p->h, s->grid, s->numbers->numbers,
2727 s2->grid, sc, diff);
2728 if (ret == 0)
2729 printf("Puzzle is inconsistent\n");
2730 else
2731 fputs(game_text_format(s2), stdout);
2732 }
2733 }
2734
2735 return 0;
2736}
2737
2738#endif
2739
2740/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/tents.html b/apps/plugins/puzzles/src/tents.html
new file mode 100644
index 0000000000..c503f008d8
--- /dev/null
+++ b/apps/plugins/puzzles/src/tents.html
@@ -0,0 +1,67 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Tents</title>
7<link rel="previous" href="inertia.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="bridges.html">
12</head>
13<body>
14<p><a href="inertia.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="bridges.html">Next</a></p>
15<h1><a name="C25"></a>Chapter 25: <a name="i0"></a>Tents</h1>
16<p>
17You have a grid of squares, some of which contain trees. Your aim is to place tents in some of the remaining squares, in such a way that the following conditions are met:
18</p>
19<ul><li>
20There are exactly as many tents as trees.
21</li>
22<li>
23The tents and trees can be matched up in such a way that each tent is directly adjacent (horizontally or vertically, but not diagonally) to its own tree. However, a tent may be adjacent to other trees as well as its own.
24</li>
25<li>
26No two tents are adjacent horizontally, vertically <em>or diagonally</em>.
27</li>
28<li>
29The number of tents in each row, and in each column, matches the numbers given round the sides of the grid.
30</li>
31</ul>
32<p>
33This puzzle can be found in several places on the Internet, and was brought to my attention by e-mail. I don't know who I should credit for inventing it.
34</p>
35<h2><a name="S25.1"></a>25.1 <a name="i1"></a>Tents controls</h2>
36<p>
37Left-clicking in a blank square will place a tent in it. Right-clicking in a blank square will colour it green, indicating that you are sure it <em>isn't</em> a tent. Clicking either button in an occupied square will clear it.
38</p>
39<p>
40If you <em>drag</em> with the right button along a row or column, every blank square in the region you cover will be turned green, and no other squares will be affected. (This is useful for clearing the remainder of a row once you have placed all its tents.)
41</p>
42<p>
43You can also use the cursor keys to move around the grid. Pressing the return key over an empty square will place a tent, and pressing the space bar over an empty square will colour it green; either key will clear an occupied square. Holding Shift and pressing the cursor keys will colour empty squares green. Holding Control and pressing the cursor keys will colour green both empty squares and squares with tents.
44</p>
45<p>
46(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
47</p>
48<h2><a name="S25.2"></a>25.2 <a name="i2"></a>Tents parameters</h2>
49<p>
50These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
51</p>
52<dl><dt>
53<em>Width</em>, <em>Height</em>
54</dt>
55<dd>
56Size of grid in squares.
57</dd>
58<dt>
59<em>Difficulty</em>
60</dt>
61<dd>
62Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, but at present none of the available difficulty levels requires guesswork or backtracking.
63</dd>
64</dl>
65
66<hr><address></address></body>
67</html>
diff --git a/apps/plugins/puzzles/src/towers.R b/apps/plugins/puzzles/src/towers.R
new file mode 100644
index 0000000000..c060c697a7
--- /dev/null
+++ b/apps/plugins/puzzles/src/towers.R
@@ -0,0 +1,25 @@
1# -*- makefile -*-
2
3TOWERS_LATIN_EXTRA = tree234 maxflow
4TOWERS_EXTRA = latin TOWERS_LATIN_EXTRA
5
6towers : [X] GTK COMMON towers TOWERS_EXTRA towers-icon|no-icon
7
8towers : [G] WINDOWS COMMON towers TOWERS_EXTRA towers.res|noicon.res
9
10towerssolver : [U] towers[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] TOWERS_LATIN_EXTRA STANDALONE
11towerssolver : [C] towers[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] TOWERS_LATIN_EXTRA STANDALONE
12
13ALL += towers[COMBINED] TOWERS_EXTRA
14
15!begin am gtk
16GAMES += towers
17!end
18
19!begin >list.c
20 A(towers) \
21!end
22
23!begin >gamedesc.txt
24towers:towers.exe:Towers:Tower-placing Latin square puzzle:Complete the latin square of towers in accordance with the clues.
25!end
diff --git a/apps/plugins/puzzles/src/towers.c b/apps/plugins/puzzles/src/towers.c
new file mode 100644
index 0000000000..a3a7e55a45
--- /dev/null
+++ b/apps/plugins/puzzles/src/towers.c
@@ -0,0 +1,2104 @@
1/*
2 * towers.c: the puzzle also known as 'Skyscrapers'.
3 *
4 * Possible future work:
5 *
6 * - Relax the upper bound on grid size at 9?
7 * + I'd need TOCHAR and FROMCHAR macros a bit like group's, to
8 * be used wherever this code has +'0' or -'0'
9 * + the pencil marks in the drawstate would need a separate
10 * word to live in
11 * + the clues outside the grid would have to cope with being
12 * multi-digit, meaning in particular that the text formatting
13 * would become more unpleasant
14 * + most importantly, though, the solver just isn't fast
15 * enough. Even at size 9 it can't really do the solver_hard
16 * factorial-time enumeration at a sensible rate. Easy puzzles
17 * higher than that would be possible, but more latin-squarey
18 * than skyscrapery, as it were.
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <assert.h>
25#include <ctype.h>
26#include <math.h>
27
28#include "puzzles.h"
29#include "latin.h"
30
31/*
32 * Difficulty levels. I do some macro ickery here to ensure that my
33 * enum and the various forms of my name list always match up.
34 */
35#define DIFFLIST(A) \
36 A(EASY,Easy,solver_easy,e) \
37 A(HARD,Hard,solver_hard,h) \
38 A(EXTREME,Extreme,NULL,x) \
39 A(UNREASONABLE,Unreasonable,NULL,u)
40#define ENUM(upper,title,func,lower) DIFF_ ## upper,
41#define TITLE(upper,title,func,lower) #title,
42#define ENCODE(upper,title,func,lower) #lower
43#define CONFIG(upper,title,func,lower) ":" #title
44enum { DIFFLIST(ENUM) DIFFCOUNT };
45static char const *const towers_diffnames[] = { DIFFLIST(TITLE) };
46static char const towers_diffchars[] = DIFFLIST(ENCODE);
47#define DIFFCONFIG DIFFLIST(CONFIG)
48
49enum {
50 COL_BACKGROUND,
51 COL_GRID,
52 COL_USER,
53 COL_HIGHLIGHT,
54 COL_ERROR,
55 COL_PENCIL,
56 COL_DONE,
57 NCOLOURS
58};
59
60struct game_params {
61 int w, diff;
62};
63
64struct clues {
65 int refcount;
66 int w;
67 /*
68 * An array of 4w integers, of which:
69 * - the first w run across the top
70 * - the next w across the bottom
71 * - the third w down the left
72 * - the last w down the right.
73 */
74 int *clues;
75
76 /*
77 * An array of w*w digits.
78 */
79 digit *immutable;
80};
81
82/*
83 * Macros to compute clue indices and coordinates.
84 */
85#define STARTSTEP(start, step, index, w) do { \
86 if (index < w) \
87 start = index, step = w; \
88 else if (index < 2*w) \
89 start = (w-1)*w+(index-w), step = -w; \
90 else if (index < 3*w) \
91 start = w*(index-2*w), step = 1; \
92 else \
93 start = w*(index-3*w)+(w-1), step = -1; \
94} while (0)
95#define CSTARTSTEP(start, step, index, w) \
96 STARTSTEP(start, step, (((index)+2*w)%(4*w)), w)
97#define CLUEPOS(x, y, index, w) do { \
98 if (index < w) \
99 x = index, y = -1; \
100 else if (index < 2*w) \
101 x = index-w, y = w; \
102 else if (index < 3*w) \
103 x = -1, y = index-2*w; \
104 else \
105 x = w, y = index-3*w; \
106} while (0)
107
108#ifdef STANDALONE_SOLVER
109static const char *const cluepos[] = {
110 "above column", "below column", "left of row", "right of row"
111};
112#endif
113
114struct game_state {
115 game_params par;
116 struct clues *clues;
117 unsigned char *clues_done;
118 digit *grid;
119 int *pencil; /* bitmaps using bits 1<<1..1<<n */
120 int completed, cheated;
121};
122
123static game_params *default_params(void)
124{
125 game_params *ret = snew(game_params);
126
127 ret->w = 5;
128 ret->diff = DIFF_EASY;
129
130 return ret;
131}
132
133const static struct game_params towers_presets[] = {
134 { 4, DIFF_EASY },
135 { 5, DIFF_EASY },
136 { 5, DIFF_HARD },
137 { 6, DIFF_EASY },
138 { 6, DIFF_HARD },
139 { 6, DIFF_EXTREME },
140 { 6, DIFF_UNREASONABLE },
141};
142
143static int game_fetch_preset(int i, char **name, game_params **params)
144{
145 game_params *ret;
146 char buf[80];
147
148 if (i < 0 || i >= lenof(towers_presets))
149 return FALSE;
150
151 ret = snew(game_params);
152 *ret = towers_presets[i]; /* structure copy */
153
154 sprintf(buf, "%dx%d %s", ret->w, ret->w, towers_diffnames[ret->diff]);
155
156 *name = dupstr(buf);
157 *params = ret;
158 return TRUE;
159}
160
161static void free_params(game_params *params)
162{
163 sfree(params);
164}
165
166static game_params *dup_params(const game_params *params)
167{
168 game_params *ret = snew(game_params);
169 *ret = *params; /* structure copy */
170 return ret;
171}
172
173static void decode_params(game_params *params, char const *string)
174{
175 char const *p = string;
176
177 params->w = atoi(p);
178 while (*p && isdigit((unsigned char)*p)) p++;
179
180 if (*p == 'd') {
181 int i;
182 p++;
183 params->diff = DIFFCOUNT+1; /* ...which is invalid */
184 if (*p) {
185 for (i = 0; i < DIFFCOUNT; i++) {
186 if (*p == towers_diffchars[i])
187 params->diff = i;
188 }
189 p++;
190 }
191 }
192}
193
194static char *encode_params(const game_params *params, int full)
195{
196 char ret[80];
197
198 sprintf(ret, "%d", params->w);
199 if (full)
200 sprintf(ret + strlen(ret), "d%c", towers_diffchars[params->diff]);
201
202 return dupstr(ret);
203}
204
205static config_item *game_configure(const game_params *params)
206{
207 config_item *ret;
208 char buf[80];
209
210 ret = snewn(3, config_item);
211
212 ret[0].name = "Grid size";
213 ret[0].type = C_STRING;
214 sprintf(buf, "%d", params->w);
215 ret[0].sval = dupstr(buf);
216 ret[0].ival = 0;
217
218 ret[1].name = "Difficulty";
219 ret[1].type = C_CHOICES;
220 ret[1].sval = DIFFCONFIG;
221 ret[1].ival = params->diff;
222
223 ret[2].name = NULL;
224 ret[2].type = C_END;
225 ret[2].sval = NULL;
226 ret[2].ival = 0;
227
228 return ret;
229}
230
231static game_params *custom_params(const config_item *cfg)
232{
233 game_params *ret = snew(game_params);
234
235 ret->w = atoi(cfg[0].sval);
236 ret->diff = cfg[1].ival;
237
238 return ret;
239}
240
241static char *validate_params(const game_params *params, int full)
242{
243 if (params->w < 3 || params->w > 9)
244 return "Grid size must be between 3 and 9";
245 if (params->diff >= DIFFCOUNT)
246 return "Unknown difficulty rating";
247 return NULL;
248}
249
250/* ----------------------------------------------------------------------
251 * Solver.
252 */
253
254struct solver_ctx {
255 int w, diff;
256 int started;
257 int *clues;
258 long *iscratch;
259 int *dscratch;
260};
261
262static int solver_easy(struct latin_solver *solver, void *vctx)
263{
264 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
265 int w = ctx->w;
266 int c, i, j, n, m, furthest;
267 int start, step, cstart, cstep, clue, pos, cpos;
268 int ret = 0;
269#ifdef STANDALONE_SOLVER
270 char prefix[256];
271#endif
272
273 if (!ctx->started) {
274 ctx->started = TRUE;
275 /*
276 * One-off loop to help get started: when a pair of facing
277 * clues sum to w+1, it must mean that the row consists of
278 * two increasing sequences back to back, so we can
279 * immediately place the highest digit by knowing the
280 * lengths of those two sequences.
281 */
282 for (c = 0; c < 3*w; c = (c == w-1 ? 2*w : c+1)) {
283 int c2 = c + w;
284
285 if (ctx->clues[c] && ctx->clues[c2] &&
286 ctx->clues[c] + ctx->clues[c2] == w+1) {
287 STARTSTEP(start, step, c, w);
288 CSTARTSTEP(cstart, cstep, c, w);
289 pos = start + (ctx->clues[c]-1)*step;
290 cpos = cstart + (ctx->clues[c]-1)*cstep;
291 if (solver->cube[cpos*w+w-1]) {
292#ifdef STANDALONE_SOLVER
293 if (solver_show_working) {
294 printf("%*sfacing clues on %s %d are maximal:\n",
295 solver_recurse_depth*4, "",
296 c>=2*w ? "row" : "column", c % w + 1);
297 printf("%*s placing %d at (%d,%d)\n",
298 solver_recurse_depth*4, "",
299 w, pos%w+1, pos/w+1);
300 }
301#endif
302 latin_solver_place(solver, pos%w, pos/w, w);
303 ret = 1;
304 } else {
305 ret = -1;
306 }
307 }
308 }
309
310 if (ret)
311 return ret;
312 }
313
314 /*
315 * Go over every clue doing reasonably simple heuristic
316 * deductions.
317 */
318 for (c = 0; c < 4*w; c++) {
319 clue = ctx->clues[c];
320 if (!clue)
321 continue;
322 STARTSTEP(start, step, c, w);
323 CSTARTSTEP(cstart, cstep, c, w);
324
325 /* Find the location of each number in the row. */
326 for (i = 0; i < w; i++)
327 ctx->dscratch[i] = w;
328 for (i = 0; i < w; i++)
329 if (solver->grid[start+i*step])
330 ctx->dscratch[solver->grid[start+i*step]-1] = i;
331
332 n = m = 0;
333 furthest = w;
334 for (i = w; i >= 1; i--) {
335 if (ctx->dscratch[i-1] == w) {
336 break;
337 } else if (ctx->dscratch[i-1] < furthest) {
338 furthest = ctx->dscratch[i-1];
339 m = i;
340 n++;
341 }
342 }
343 if (clue == n+1 && furthest > 1) {
344#ifdef STANDALONE_SOLVER
345 if (solver_show_working)
346 sprintf(prefix, "%*sclue %s %d is nearly filled:\n",
347 solver_recurse_depth*4, "",
348 cluepos[c/w], c%w+1);
349 else
350 prefix[0] = '\0'; /* placate optimiser */
351#endif
352 /*
353 * We can already see an increasing sequence of the very
354 * highest numbers, of length one less than that
355 * specified in the clue. All of those numbers _must_ be
356 * part of the clue sequence, so the number right next
357 * to the clue must be the final one - i.e. it must be
358 * bigger than any of the numbers between it and m. This
359 * allows us to rule out small numbers in that square.
360 *
361 * (This is a generalisation of the obvious deduction
362 * that when you see a clue saying 1, it must be right
363 * next to the largest possible number; and similarly,
364 * when you see a clue saying 2 opposite that, it must
365 * be right next to the second-largest.)
366 */
367 j = furthest-1; /* number of small numbers we can rule out */
368 for (i = 1; i <= w && j > 0; i++) {
369 if (ctx->dscratch[i-1] < w && ctx->dscratch[i-1] >= furthest)
370 continue; /* skip this number, it's elsewhere */
371 j--;
372 if (solver->cube[cstart*w+i-1]) {
373#ifdef STANDALONE_SOLVER
374 if (solver_show_working) {
375 printf("%s%*s ruling out %d at (%d,%d)\n",
376 prefix, solver_recurse_depth*4, "",
377 i, start%w+1, start/w+1);
378 prefix[0] = '\0';
379 }
380#endif
381 solver->cube[cstart*w+i-1] = 0;
382 ret = 1;
383 }
384 }
385 }
386
387 if (ret)
388 return ret;
389
390#ifdef STANDALONE_SOLVER
391 if (solver_show_working)
392 sprintf(prefix, "%*slower bounds for clue %s %d:\n",
393 solver_recurse_depth*4, "",
394 cluepos[c/w], c%w+1);
395 else
396 prefix[0] = '\0'; /* placate optimiser */
397#endif
398
399 i = 0;
400 for (n = w; n > 0; n--) {
401 /*
402 * The largest number cannot occur in the first (clue-1)
403 * squares of the row, or else there wouldn't be space
404 * for a sufficiently long increasing sequence which it
405 * terminated. The second-largest number (not counting
406 * any that are known to be on the far side of a larger
407 * number and hence excluded from this sequence) cannot
408 * occur in the first (clue-2) squares, similarly, and
409 * so on.
410 */
411
412 if (ctx->dscratch[n-1] < w) {
413 for (m = n+1; m < w; m++)
414 if (ctx->dscratch[m] < ctx->dscratch[n-1])
415 break;
416 if (m < w)
417 continue; /* this number doesn't count */
418 }
419
420 for (j = 0; j < clue - i - 1; j++)
421 if (solver->cube[(cstart + j*cstep)*w+n-1]) {
422#ifdef STANDALONE_SOLVER
423 if (solver_show_working) {
424 int pos = start+j*step;
425 printf("%s%*s ruling out %d at (%d,%d)\n",
426 prefix, solver_recurse_depth*4, "",
427 n, pos%w+1, pos/w+1);
428 prefix[0] = '\0';
429 }
430#endif
431 solver->cube[(cstart + j*cstep)*w+n-1] = 0;
432 ret = 1;
433 }
434 i++;
435 }
436 }
437
438 if (ret)
439 return ret;
440
441 return 0;
442}
443
444static int solver_hard(struct latin_solver *solver, void *vctx)
445{
446 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
447 int w = ctx->w;
448 int c, i, j, n, best, clue, start, step, ret;
449 long bitmap;
450#ifdef STANDALONE_SOLVER
451 char prefix[256];
452#endif
453
454 /*
455 * Go over every clue analysing all possibilities.
456 */
457 for (c = 0; c < 4*w; c++) {
458 clue = ctx->clues[c];
459 if (!clue)
460 continue;
461 CSTARTSTEP(start, step, c, w);
462
463 for (i = 0; i < w; i++)
464 ctx->iscratch[i] = 0;
465
466 /*
467 * Instead of a tedious physical recursion, I iterate in the
468 * scratch array through all possibilities. At any given
469 * moment, i indexes the element of the box that will next
470 * be incremented.
471 */
472 i = 0;
473 ctx->dscratch[i] = 0;
474 best = n = 0;
475 bitmap = 0;
476
477 while (1) {
478 if (i < w) {
479 /*
480 * Find the next valid value for cell i.
481 */
482 int limit = (n == clue ? best : w);
483 int pos = start + step * i;
484 for (j = ctx->dscratch[i] + 1; j <= limit; j++) {
485 if (bitmap & (1L << j))
486 continue; /* used this one already */
487 if (!solver->cube[pos*w+j-1])
488 continue; /* ruled out already */
489
490 /* Found one. */
491 break;
492 }
493
494 if (j > limit) {
495 /* No valid values left; drop back. */
496 i--;
497 if (i < 0)
498 break; /* overall iteration is finished */
499 bitmap &= ~(1L << ctx->dscratch[i]);
500 if (ctx->dscratch[i] == best) {
501 n--;
502 best = 0;
503 for (j = 0; j < i; j++)
504 if (best < ctx->dscratch[j])
505 best = ctx->dscratch[j];
506 }
507 } else {
508 /* Got a valid value; store it and move on. */
509 bitmap |= 1L << j;
510 ctx->dscratch[i++] = j;
511 if (j > best) {
512 best = j;
513 n++;
514 }
515 ctx->dscratch[i] = 0;
516 }
517 } else {
518 if (n == clue) {
519 for (j = 0; j < w; j++)
520 ctx->iscratch[j] |= 1L << ctx->dscratch[j];
521 }
522 i--;
523 bitmap &= ~(1L << ctx->dscratch[i]);
524 if (ctx->dscratch[i] == best) {
525 n--;
526 best = 0;
527 for (j = 0; j < i; j++)
528 if (best < ctx->dscratch[j])
529 best = ctx->dscratch[j];
530 }
531 }
532 }
533
534#ifdef STANDALONE_SOLVER
535 if (solver_show_working)
536 sprintf(prefix, "%*sexhaustive analysis of clue %s %d:\n",
537 solver_recurse_depth*4, "",
538 cluepos[c/w], c%w+1);
539 else
540 prefix[0] = '\0'; /* placate optimiser */
541#endif
542
543 ret = 0;
544
545 for (i = 0; i < w; i++) {
546 int pos = start + step * i;
547 for (j = 1; j <= w; j++) {
548 if (solver->cube[pos*w+j-1] &&
549 !(ctx->iscratch[i] & (1L << j))) {
550#ifdef STANDALONE_SOLVER
551 if (solver_show_working) {
552 printf("%s%*s ruling out %d at (%d,%d)\n",
553 prefix, solver_recurse_depth*4, "",
554 j, pos/w+1, pos%w+1);
555 prefix[0] = '\0';
556 }
557#endif
558 solver->cube[pos*w+j-1] = 0;
559 ret = 1;
560 }
561 }
562
563 /*
564 * Once we find one clue we can do something with in
565 * this way, revert to trying easier deductions, so as
566 * not to generate solver diagnostics that make the
567 * problem look harder than it is.
568 */
569 if (ret)
570 return ret;
571 }
572 }
573
574 return 0;
575}
576
577#define SOLVER(upper,title,func,lower) func,
578static usersolver_t const towers_solvers[] = { DIFFLIST(SOLVER) };
579
580static int solver(int w, int *clues, digit *soln, int maxdiff)
581{
582 int ret;
583 struct solver_ctx ctx;
584
585 ctx.w = w;
586 ctx.diff = maxdiff;
587 ctx.clues = clues;
588 ctx.started = FALSE;
589 ctx.iscratch = snewn(w, long);
590 ctx.dscratch = snewn(w+1, int);
591
592 ret = latin_solver(soln, w, maxdiff,
593 DIFF_EASY, DIFF_HARD, DIFF_EXTREME,
594 DIFF_EXTREME, DIFF_UNREASONABLE,
595 towers_solvers, &ctx, NULL, NULL);
596
597 sfree(ctx.iscratch);
598 sfree(ctx.dscratch);
599
600 return ret;
601}
602
603/* ----------------------------------------------------------------------
604 * Grid generation.
605 */
606
607static char *new_game_desc(const game_params *params, random_state *rs,
608 char **aux, int interactive)
609{
610 int w = params->w, a = w*w;
611 digit *grid, *soln, *soln2;
612 int *clues, *order;
613 int i, ret;
614 int diff = params->diff;
615 char *desc, *p;
616
617 /*
618 * Difficulty exceptions: some combinations of size and
619 * difficulty cannot be satisfied, because all puzzles of at
620 * most that difficulty are actually even easier.
621 *
622 * Remember to re-test this whenever a change is made to the
623 * solver logic!
624 *
625 * I tested it using the following shell command:
626
627for d in e h x u; do
628 for i in {3..9}; do
629 echo -n "./towers --generate 1 ${i}d${d}: "
630 perl -e 'alarm 30; exec @ARGV' ./towers --generate 1 ${i}d${d} >/dev/null \
631 && echo ok
632 done
633done
634
635 * Of course, it's better to do that after taking the exceptions
636 * _out_, so as to detect exceptions that should be removed as
637 * well as those which should be added.
638 */
639 if (diff > DIFF_HARD && w <= 3)
640 diff = DIFF_HARD;
641
642 grid = NULL;
643 clues = snewn(4*w, int);
644 soln = snewn(a, digit);
645 soln2 = snewn(a, digit);
646 order = snewn(max(4*w,a), int);
647
648 while (1) {
649 /*
650 * Construct a latin square to be the solution.
651 */
652 sfree(grid);
653 grid = latin_generate(w, rs);
654
655 /*
656 * Fill in the clues.
657 */
658 for (i = 0; i < 4*w; i++) {
659 int start, step, j, k, best;
660 STARTSTEP(start, step, i, w);
661 k = best = 0;
662 for (j = 0; j < w; j++) {
663 if (grid[start+j*step] > best) {
664 best = grid[start+j*step];
665 k++;
666 }
667 }
668 clues[i] = k;
669 }
670
671 /*
672 * Remove the grid numbers and then the clues, one by one,
673 * for as long as the game remains soluble at the given
674 * difficulty.
675 */
676 memcpy(soln, grid, a);
677
678 if (diff == DIFF_EASY && w <= 5) {
679 /*
680 * Special case: for Easy-mode grids that are small
681 * enough, it's nice to be able to find completely empty
682 * grids.
683 */
684 memset(soln2, 0, a);
685 ret = solver(w, clues, soln2, diff);
686 if (ret > diff)
687 continue;
688 }
689
690 for (i = 0; i < a; i++)
691 order[i] = i;
692 shuffle(order, a, sizeof(*order), rs);
693 for (i = 0; i < a; i++) {
694 int j = order[i];
695
696 memcpy(soln2, grid, a);
697 soln2[j] = 0;
698 ret = solver(w, clues, soln2, diff);
699 if (ret <= diff)
700 grid[j] = 0;
701 }
702
703 if (diff > DIFF_EASY) { /* leave all clues on Easy mode */
704 for (i = 0; i < 4*w; i++)
705 order[i] = i;
706 shuffle(order, 4*w, sizeof(*order), rs);
707 for (i = 0; i < 4*w; i++) {
708 int j = order[i];
709 int clue = clues[j];
710
711 memcpy(soln2, grid, a);
712 clues[j] = 0;
713 ret = solver(w, clues, soln2, diff);
714 if (ret > diff)
715 clues[j] = clue;
716 }
717 }
718
719 /*
720 * See if the game can be solved at the specified difficulty
721 * level, but not at the one below.
722 */
723 memcpy(soln2, grid, a);
724 ret = solver(w, clues, soln2, diff);
725 if (ret != diff)
726 continue; /* go round again */
727
728 /*
729 * We've got a usable puzzle!
730 */
731 break;
732 }
733
734 /*
735 * Encode the puzzle description.
736 */
737 desc = snewn(40*a, char);
738 p = desc;
739 for (i = 0; i < 4*w; i++) {
740 if (i)
741 *p++ = '/';
742 if (clues[i])
743 p += sprintf(p, "%d", clues[i]);
744 }
745 for (i = 0; i < a; i++)
746 if (grid[i])
747 break;
748 if (i < a) {
749 int run = 0;
750
751 *p++ = ',';
752
753 for (i = 0; i <= a; i++) {
754 int n = (i < a ? grid[i] : -1);
755
756 if (!n)
757 run++;
758 else {
759 if (run) {
760 while (run > 0) {
761 int thisrun = min(run, 26);
762 *p++ = thisrun - 1 + 'a';
763 run -= thisrun;
764 }
765 } else {
766 /*
767 * If there's a number in the very top left or
768 * bottom right, there's no point putting an
769 * unnecessary _ before or after it.
770 */
771 if (i > 0 && n > 0)
772 *p++ = '_';
773 }
774 if (n > 0)
775 p += sprintf(p, "%d", n);
776 run = 0;
777 }
778 }
779 }
780 *p++ = '\0';
781 desc = sresize(desc, p - desc, char);
782
783 /*
784 * Encode the solution.
785 */
786 *aux = snewn(a+2, char);
787 (*aux)[0] = 'S';
788 for (i = 0; i < a; i++)
789 (*aux)[i+1] = '0' + soln[i];
790 (*aux)[a+1] = '\0';
791
792 sfree(grid);
793 sfree(clues);
794 sfree(soln);
795 sfree(soln2);
796 sfree(order);
797
798 return desc;
799}
800
801/* ----------------------------------------------------------------------
802 * Gameplay.
803 */
804
805static char *validate_desc(const game_params *params, const char *desc)
806{
807 int w = params->w, a = w*w;
808 const char *p = desc;
809 int i, clue;
810
811 /*
812 * Verify that the right number of clues are given, and that
813 * they're in range.
814 */
815 for (i = 0; i < 4*w; i++) {
816 if (!*p)
817 return "Too few clues for grid size";
818
819 if (i > 0) {
820 if (*p != '/')
821 return "Expected commas between clues";
822 p++;
823 }
824
825 if (isdigit((unsigned char)*p)) {
826 clue = atoi(p);
827 while (*p && isdigit((unsigned char)*p)) p++;
828
829 if (clue <= 0 || clue > w)
830 return "Clue number out of range";
831 }
832 }
833 if (*p == '/')
834 return "Too many clues for grid size";
835
836 if (*p == ',') {
837 /*
838 * Verify that the right amount of grid data is given, and
839 * that any grid elements provided are in range.
840 */
841 int squares = 0;
842
843 p++;
844 while (*p) {
845 int c = *p++;
846 if (c >= 'a' && c <= 'z') {
847 squares += c - 'a' + 1;
848 } else if (c == '_') {
849 /* do nothing */;
850 } else if (c > '0' && c <= '9') {
851 int val = atoi(p-1);
852 if (val < 1 || val > w)
853 return "Out-of-range number in grid description";
854 squares++;
855 while (*p && isdigit((unsigned char)*p)) p++;
856 } else
857 return "Invalid character in game description";
858 }
859
860 if (squares < a)
861 return "Not enough data to fill grid";
862
863 if (squares > a)
864 return "Too much data to fit in grid";
865 }
866
867 return NULL;
868}
869
870static game_state *new_game(midend *me, const game_params *params,
871 const char *desc)
872{
873 int w = params->w, a = w*w;
874 game_state *state = snew(game_state);
875 const char *p = desc;
876 int i;
877
878 state->par = *params; /* structure copy */
879 state->clues = snew(struct clues);
880 state->clues->refcount = 1;
881 state->clues->w = w;
882 state->clues->clues = snewn(4*w, int);
883 state->clues->immutable = snewn(a, digit);
884 state->grid = snewn(a, digit);
885 state->clues_done = snewn(4*w, unsigned char);
886 state->pencil = snewn(a, int);
887
888 for (i = 0; i < a; i++) {
889 state->grid[i] = 0;
890 state->pencil[i] = 0;
891 }
892
893 memset(state->clues->immutable, 0, a);
894 memset(state->clues_done, 0, 4*w*sizeof(unsigned char));
895
896 for (i = 0; i < 4*w; i++) {
897 if (i > 0) {
898 assert(*p == '/');
899 p++;
900 }
901 if (*p && isdigit((unsigned char)*p)) {
902 state->clues->clues[i] = atoi(p);
903 while (*p && isdigit((unsigned char)*p)) p++;
904 } else
905 state->clues->clues[i] = 0;
906 }
907
908 if (*p == ',') {
909 int pos = 0;
910 p++;
911 while (*p) {
912 int c = *p++;
913 if (c >= 'a' && c <= 'z') {
914 pos += c - 'a' + 1;
915 } else if (c == '_') {
916 /* do nothing */;
917 } else if (c > '0' && c <= '9') {
918 int val = atoi(p-1);
919 assert(val >= 1 && val <= w);
920 assert(pos < a);
921 state->grid[pos] = state->clues->immutable[pos] = val;
922 pos++;
923 while (*p && isdigit((unsigned char)*p)) p++;
924 } else
925 assert(!"Corrupt game description");
926 }
927 assert(pos == a);
928 }
929 assert(!*p);
930
931 state->completed = state->cheated = FALSE;
932
933 return state;
934}
935
936static game_state *dup_game(const game_state *state)
937{
938 int w = state->par.w, a = w*w;
939 game_state *ret = snew(game_state);
940
941 ret->par = state->par; /* structure copy */
942
943 ret->clues = state->clues;
944 ret->clues->refcount++;
945
946 ret->grid = snewn(a, digit);
947 ret->pencil = snewn(a, int);
948 ret->clues_done = snewn(4*w, unsigned char);
949 memcpy(ret->grid, state->grid, a*sizeof(digit));
950 memcpy(ret->pencil, state->pencil, a*sizeof(int));
951 memcpy(ret->clues_done, state->clues_done, 4*w*sizeof(unsigned char));
952
953 ret->completed = state->completed;
954 ret->cheated = state->cheated;
955
956 return ret;
957}
958
959static void free_game(game_state *state)
960{
961 sfree(state->grid);
962 sfree(state->pencil);
963 sfree(state->clues_done);
964 if (--state->clues->refcount <= 0) {
965 sfree(state->clues->immutable);
966 sfree(state->clues->clues);
967 sfree(state->clues);
968 }
969 sfree(state);
970}
971
972static char *solve_game(const game_state *state, const game_state *currstate,
973 const char *aux, char **error)
974{
975 int w = state->par.w, a = w*w;
976 int i, ret;
977 digit *soln;
978 char *out;
979
980 if (aux)
981 return dupstr(aux);
982
983 soln = snewn(a, digit);
984 memcpy(soln, state->clues->immutable, a);
985
986 ret = solver(w, state->clues->clues, soln, DIFFCOUNT-1);
987
988 if (ret == diff_impossible) {
989 *error = "No solution exists for this puzzle";
990 out = NULL;
991 } else if (ret == diff_ambiguous) {
992 *error = "Multiple solutions exist for this puzzle";
993 out = NULL;
994 } else {
995 out = snewn(a+2, char);
996 out[0] = 'S';
997 for (i = 0; i < a; i++)
998 out[i+1] = '0' + soln[i];
999 out[a+1] = '\0';
1000 }
1001
1002 sfree(soln);
1003 return out;
1004}
1005
1006static int game_can_format_as_text_now(const game_params *params)
1007{
1008 return TRUE;
1009}
1010
1011static char *game_text_format(const game_state *state)
1012{
1013 int w = state->par.w /* , a = w*w */;
1014 char *ret;
1015 char *p;
1016 int x, y;
1017 int total;
1018
1019 /*
1020 * We have:
1021 * - a top clue row, consisting of three spaces, then w clue
1022 * digits with spaces between (total 2*w+3 chars including
1023 * newline)
1024 * - a blank line (one newline)
1025 * - w main rows, consisting of a left clue digit, two spaces,
1026 * w grid digits with spaces between, two spaces and a right
1027 * clue digit (total 2*w+6 chars each including newline)
1028 * - a blank line (one newline)
1029 * - a bottom clue row (same as top clue row)
1030 * - terminating NUL.
1031 *
1032 * Total size is therefore 2*(2*w+3) + 2 + w*(2*w+6) + 1
1033 * = 2w^2+10w+9.
1034 */
1035 total = 2*w*w + 10*w + 9;
1036 ret = snewn(total, char);
1037 p = ret;
1038
1039 /* Top clue row. */
1040 *p++ = ' '; *p++ = ' ';
1041 for (x = 0; x < w; x++) {
1042 *p++ = ' ';
1043 *p++ = (state->clues->clues[x] ? '0' + state->clues->clues[x] : ' ');
1044 }
1045 *p++ = '\n';
1046
1047 /* Blank line. */
1048 *p++ = '\n';
1049
1050 /* Main grid. */
1051 for (y = 0; y < w; y++) {
1052 *p++ = (state->clues->clues[y+2*w] ? '0' + state->clues->clues[y+2*w] :
1053 ' ');
1054 *p++ = ' ';
1055 for (x = 0; x < w; x++) {
1056 *p++ = ' ';
1057 *p++ = (state->grid[y*w+x] ? '0' + state->grid[y*w+x] : ' ');
1058 }
1059 *p++ = ' '; *p++ = ' ';
1060 *p++ = (state->clues->clues[y+3*w] ? '0' + state->clues->clues[y+3*w] :
1061 ' ');
1062 *p++ = '\n';
1063 }
1064
1065 /* Blank line. */
1066 *p++ = '\n';
1067
1068 /* Bottom clue row. */
1069 *p++ = ' '; *p++ = ' ';
1070 for (x = 0; x < w; x++) {
1071 *p++ = ' ';
1072 *p++ = (state->clues->clues[x+w] ? '0' + state->clues->clues[x+w] :
1073 ' ');
1074 }
1075 *p++ = '\n';
1076
1077 *p++ = '\0';
1078 assert(p == ret + total);
1079
1080 return ret;
1081}
1082
1083struct game_ui {
1084 /*
1085 * These are the coordinates of the currently highlighted
1086 * square on the grid, if hshow = 1.
1087 */
1088 int hx, hy;
1089 /*
1090 * This indicates whether the current highlight is a
1091 * pencil-mark one or a real one.
1092 */
1093 int hpencil;
1094 /*
1095 * This indicates whether or not we're showing the highlight
1096 * (used to be hx = hy = -1); important so that when we're
1097 * using the cursor keys it doesn't keep coming back at a
1098 * fixed position. When hshow = 1, pressing a valid number
1099 * or letter key or Space will enter that number or letter in the grid.
1100 */
1101 int hshow;
1102 /*
1103 * This indicates whether we're using the highlight as a cursor;
1104 * it means that it doesn't vanish on a keypress, and that it is
1105 * allowed on immutable squares.
1106 */
1107 int hcursor;
1108};
1109
1110static game_ui *new_ui(const game_state *state)
1111{
1112 game_ui *ui = snew(game_ui);
1113
1114 ui->hx = ui->hy = 0;
1115 ui->hpencil = ui->hshow = ui->hcursor = 0;
1116
1117 return ui;
1118}
1119
1120static void free_ui(game_ui *ui)
1121{
1122 sfree(ui);
1123}
1124
1125static char *encode_ui(const game_ui *ui)
1126{
1127 return NULL;
1128}
1129
1130static void decode_ui(game_ui *ui, const char *encoding)
1131{
1132}
1133
1134static void game_changed_state(game_ui *ui, const game_state *oldstate,
1135 const game_state *newstate)
1136{
1137 int w = newstate->par.w;
1138 /*
1139 * We prevent pencil-mode highlighting of a filled square, unless
1140 * we're using the cursor keys. So if the user has just filled in
1141 * a square which we had a pencil-mode highlight in (by Undo, or
1142 * by Redo, or by Solve), then we cancel the highlight.
1143 */
1144 if (ui->hshow && ui->hpencil && !ui->hcursor &&
1145 newstate->grid[ui->hy * w + ui->hx] != 0) {
1146 ui->hshow = 0;
1147 }
1148}
1149
1150#define PREFERRED_TILESIZE 48
1151#define TILESIZE (ds->tilesize)
1152#define BORDER (TILESIZE * 9 / 8)
1153#define COORD(x) ((x)*TILESIZE + BORDER)
1154#define FROMCOORD(x) (((x)+(TILESIZE-BORDER)) / TILESIZE - 1)
1155
1156/* These always return positive values, though y offsets are actually -ve */
1157#define X_3D_DISP(height, w) ((height) * TILESIZE / (8 * (w)))
1158#define Y_3D_DISP(height, w) ((height) * TILESIZE / (4 * (w)))
1159
1160#define FLASH_TIME 0.4F
1161
1162#define DF_PENCIL_SHIFT 16
1163#define DF_CLUE_DONE 0x10000
1164#define DF_ERROR 0x8000
1165#define DF_HIGHLIGHT 0x4000
1166#define DF_HIGHLIGHT_PENCIL 0x2000
1167#define DF_IMMUTABLE 0x1000
1168#define DF_PLAYAREA 0x0800
1169#define DF_DIGIT_MASK 0x00FF
1170
1171struct game_drawstate {
1172 int tilesize;
1173 int three_d; /* default 3D graphics are user-disableable */
1174 int started;
1175 long *tiles; /* (w+2)*(w+2) temp space */
1176 long *drawn; /* (w+2)*(w+2)*4: current drawn data */
1177 int *errtmp;
1178};
1179
1180static int check_errors(const game_state *state, int *errors)
1181{
1182 int w = state->par.w /*, a = w*w */;
1183 int W = w+2, A = W*W; /* the errors array is (w+2) square */
1184 int *clues = state->clues->clues;
1185 digit *grid = state->grid;
1186 int i, x, y, errs = FALSE;
1187 int tmp[32];
1188
1189 assert(w < lenof(tmp));
1190
1191 if (errors)
1192 for (i = 0; i < A; i++)
1193 errors[i] = 0;
1194
1195 for (y = 0; y < w; y++) {
1196 unsigned long mask = 0, errmask = 0;
1197 for (x = 0; x < w; x++) {
1198 unsigned long bit = 1UL << grid[y*w+x];
1199 errmask |= (mask & bit);
1200 mask |= bit;
1201 }
1202
1203 if (mask != (1L << (w+1)) - (1L << 1)) {
1204 errs = TRUE;
1205 errmask &= ~1UL;
1206 if (errors) {
1207 for (x = 0; x < w; x++)
1208 if (errmask & (1UL << grid[y*w+x]))
1209 errors[(y+1)*W+(x+1)] = TRUE;
1210 }
1211 }
1212 }
1213
1214 for (x = 0; x < w; x++) {
1215 unsigned long mask = 0, errmask = 0;
1216 for (y = 0; y < w; y++) {
1217 unsigned long bit = 1UL << grid[y*w+x];
1218 errmask |= (mask & bit);
1219 mask |= bit;
1220 }
1221
1222 if (mask != (1 << (w+1)) - (1 << 1)) {
1223 errs = TRUE;
1224 errmask &= ~1UL;
1225 if (errors) {
1226 for (y = 0; y < w; y++)
1227 if (errmask & (1UL << grid[y*w+x]))
1228 errors[(y+1)*W+(x+1)] = TRUE;
1229 }
1230 }
1231 }
1232
1233 for (i = 0; i < 4*w; i++) {
1234 int start, step, j, n, best;
1235 STARTSTEP(start, step, i, w);
1236
1237 if (!clues[i])
1238 continue;
1239
1240 best = n = 0;
1241 for (j = 0; j < w; j++) {
1242 int number = grid[start+j*step];
1243 if (!number)
1244 break; /* can't tell what happens next */
1245 if (number > best) {
1246 best = number;
1247 n++;
1248 }
1249 }
1250
1251 if (n > clues[i] || (best == w && n < clues[i]) ||
1252 (best < w && n == clues[i])) {
1253 if (errors) {
1254 int x, y;
1255 CLUEPOS(x, y, i, w);
1256 errors[(y+1)*W+(x+1)] = TRUE;
1257 }
1258 errs = TRUE;
1259 }
1260 }
1261
1262 return errs;
1263}
1264
1265static int clue_index(const game_state *state, int x, int y)
1266{
1267 int w = state->par.w;
1268
1269 if (x == -1 || x == w)
1270 return w * (x == -1 ? 2 : 3) + y;
1271 else if (y == -1 || y == w)
1272 return (y == -1 ? 0 : w) + x;
1273
1274 return -1;
1275}
1276
1277static int is_clue(const game_state *state, int x, int y)
1278{
1279 int w = state->par.w;
1280
1281 if (((x == -1 || x == w) && y >= 0 && y < w) ||
1282 ((y == -1 || y == w) && x >= 0 && x < w))
1283 {
1284 if (state->clues->clues[clue_index(state, x, y)] & DF_DIGIT_MASK)
1285 return TRUE;
1286 }
1287
1288 return FALSE;
1289}
1290
1291static char *interpret_move(const game_state *state, game_ui *ui,
1292 const game_drawstate *ds,
1293 int x, int y, int button)
1294{
1295 int w = state->par.w;
1296 int shift_or_control = button & (MOD_SHFT | MOD_CTRL);
1297 int tx, ty;
1298 char buf[80];
1299
1300 button &= ~MOD_MASK;
1301
1302 tx = FROMCOORD(x);
1303 ty = FROMCOORD(y);
1304
1305 if (ds->three_d) {
1306 /*
1307 * In 3D mode, just locating the mouse click in the natural
1308 * square grid may not be sufficient to tell which tower the
1309 * user clicked on. Investigate the _tops_ of the nearby
1310 * towers to see if a click on one grid square was actually
1311 * a click on a tower protruding into that region from
1312 * another.
1313 */
1314 int dx, dy;
1315 for (dy = 0; dy <= 1; dy++)
1316 for (dx = 0; dx >= -1; dx--) {
1317 int cx = tx + dx, cy = ty + dy;
1318 if (cx >= 0 && cx < w && cy >= 0 && cy < w) {
1319 int height = state->grid[cy*w+cx];
1320 int bx = COORD(cx), by = COORD(cy);
1321 int ox = bx + X_3D_DISP(height, w);
1322 int oy = by - Y_3D_DISP(height, w);
1323 if (/* on top face? */
1324 (x - ox >= 0 && x - ox < TILESIZE &&
1325 y - oy >= 0 && y - oy < TILESIZE) ||
1326 /* in triangle between top-left corners? */
1327 (ox > bx && x >= bx && x <= ox && y <= by &&
1328 (by-y) * (ox-bx) <= (by-oy) * (x-bx)) ||
1329 /* in triangle between bottom-right corners? */
1330 (ox > bx && x >= bx+TILESIZE && x <= ox+TILESIZE &&
1331 y >= oy+TILESIZE &&
1332 (by-y+TILESIZE)*(ox-bx) >= (by-oy)*(x-bx-TILESIZE))) {
1333 tx = cx;
1334 ty = cy;
1335 }
1336 }
1337 }
1338 }
1339
1340 if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
1341 if (button == LEFT_BUTTON) {
1342 if (tx == ui->hx && ty == ui->hy &&
1343 ui->hshow && ui->hpencil == 0) {
1344 ui->hshow = 0;
1345 } else {
1346 ui->hx = tx;
1347 ui->hy = ty;
1348 ui->hshow = !state->clues->immutable[ty*w+tx];
1349 ui->hpencil = 0;
1350 }
1351 ui->hcursor = 0;
1352 return ""; /* UI activity occurred */
1353 }
1354 if (button == RIGHT_BUTTON) {
1355 /*
1356 * Pencil-mode highlighting for non filled squares.
1357 */
1358 if (state->grid[ty*w+tx] == 0) {
1359 if (tx == ui->hx && ty == ui->hy &&
1360 ui->hshow && ui->hpencil) {
1361 ui->hshow = 0;
1362 } else {
1363 ui->hpencil = 1;
1364 ui->hx = tx;
1365 ui->hy = ty;
1366 ui->hshow = 1;
1367 }
1368 } else {
1369 ui->hshow = 0;
1370 }
1371 ui->hcursor = 0;
1372 return ""; /* UI activity occurred */
1373 }
1374 } else if (button == LEFT_BUTTON) {
1375 if (is_clue(state, tx, ty)) {
1376 sprintf(buf, "%c%d,%d", 'D', tx, ty);
1377 return dupstr(buf);
1378 }
1379 }
1380 if (IS_CURSOR_MOVE(button)) {
1381 if (shift_or_control) {
1382 int x = ui->hx, y = ui->hy;
1383 switch (button) {
1384 case CURSOR_LEFT: x = -1; break;
1385 case CURSOR_RIGHT: x = w; break;
1386 case CURSOR_UP: y = -1; break;
1387 case CURSOR_DOWN: y = w; break;
1388 }
1389 if (is_clue(state, x, y)) {
1390 sprintf(buf, "%c%d,%d", 'D', x, y);
1391 return dupstr(buf);
1392 }
1393 return NULL;
1394 }
1395 move_cursor(button, &ui->hx, &ui->hy, w, w, 0);
1396 ui->hshow = ui->hcursor = 1;
1397 return "";
1398 }
1399 if (ui->hshow &&
1400 (button == CURSOR_SELECT)) {
1401 ui->hpencil = 1 - ui->hpencil;
1402 ui->hcursor = 1;
1403 return "";
1404 }
1405
1406 if (ui->hshow &&
1407 ((button >= '0' && button <= '9' && button - '0' <= w) ||
1408 button == CURSOR_SELECT2 || button == '\b')) {
1409 int n = button - '0';
1410 if (button == CURSOR_SELECT2 || button == '\b')
1411 n = 0;
1412
1413 /*
1414 * Can't make pencil marks in a filled square. This can only
1415 * become highlighted if we're using cursor keys.
1416 */
1417 if (ui->hpencil && state->grid[ui->hy*w+ui->hx])
1418 return NULL;
1419
1420 /*
1421 * Can't do anything to an immutable square.
1422 */
1423 if (state->clues->immutable[ui->hy*w+ui->hx])
1424 return NULL;
1425
1426 sprintf(buf, "%c%d,%d,%d",
1427 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1428
1429 if (!ui->hcursor) ui->hshow = 0;
1430
1431 return dupstr(buf);
1432 }
1433
1434 if (button == 'M' || button == 'm')
1435 return dupstr("M");
1436
1437 return NULL;
1438}
1439
1440static game_state *execute_move(const game_state *from, const char *move)
1441{
1442 int w = from->par.w, a = w*w;
1443 game_state *ret = dup_game(from);
1444 int x, y, i, n;
1445
1446 if (move[0] == 'S') {
1447 ret->completed = ret->cheated = TRUE;
1448
1449 for (i = 0; i < a; i++) {
1450 if (move[i+1] < '1' || move[i+1] > '0'+w)
1451 goto badmove;
1452 ret->grid[i] = move[i+1] - '0';
1453 ret->pencil[i] = 0;
1454 }
1455
1456 if (move[a+1] != '\0')
1457 goto badmove;
1458
1459 return ret;
1460 } else if ((move[0] == 'P' || move[0] == 'R') &&
1461 sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
1462 x >= 0 && x < w && y >= 0 && y < w && n >= 0 && n <= w) {
1463 if (from->clues->immutable[y*w+x])
1464 goto badmove;
1465
1466 if (move[0] == 'P' && n > 0) {
1467 ret->pencil[y*w+x] ^= 1L << n;
1468 } else {
1469 ret->grid[y*w+x] = n;
1470 ret->pencil[y*w+x] = 0;
1471
1472 if (!ret->completed && !check_errors(ret, NULL))
1473 ret->completed = TRUE;
1474 }
1475 return ret;
1476 } else if (move[0] == 'M') {
1477 /*
1478 * Fill in absolutely all pencil marks everywhere. (I
1479 * wouldn't use this for actual play, but it's a handy
1480 * starting point when following through a set of
1481 * diagnostics output by the standalone solver.)
1482 */
1483 for (i = 0; i < a; i++) {
1484 if (!ret->grid[i])
1485 ret->pencil[i] = (1L << (w+1)) - (1L << 1);
1486 }
1487 return ret;
1488 } else if (move[0] == 'D' && sscanf(move+1, "%d,%d", &x, &y) == 2 &&
1489 is_clue(from, x, y)) {
1490 int index = clue_index(from, x, y);
1491 ret->clues_done[index] = !ret->clues_done[index];
1492 return ret;
1493 }
1494
1495 badmove:
1496 /* couldn't parse move string */
1497 free_game(ret);
1498 return NULL;
1499}
1500
1501/* ----------------------------------------------------------------------
1502 * Drawing routines.
1503 */
1504
1505#define SIZE(w) ((w) * TILESIZE + 2*BORDER)
1506
1507static void game_compute_size(const game_params *params, int tilesize,
1508 int *x, int *y)
1509{
1510 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1511 struct { int tilesize; } ads, *ds = &ads;
1512 ads.tilesize = tilesize;
1513
1514 *x = *y = SIZE(params->w);
1515}
1516
1517static void game_set_size(drawing *dr, game_drawstate *ds,
1518 const game_params *params, int tilesize)
1519{
1520 ds->tilesize = tilesize;
1521}
1522
1523static float *game_colours(frontend *fe, int *ncolours)
1524{
1525 float *ret = snewn(3 * NCOLOURS, float);
1526
1527 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1528
1529 ret[COL_GRID * 3 + 0] = 0.0F;
1530 ret[COL_GRID * 3 + 1] = 0.0F;
1531 ret[COL_GRID * 3 + 2] = 0.0F;
1532
1533 ret[COL_USER * 3 + 0] = 0.0F;
1534 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1535 ret[COL_USER * 3 + 2] = 0.0F;
1536
1537 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
1538 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
1539 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
1540
1541 ret[COL_ERROR * 3 + 0] = 1.0F;
1542 ret[COL_ERROR * 3 + 1] = 0.0F;
1543 ret[COL_ERROR * 3 + 2] = 0.0F;
1544
1545 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1546 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1547 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1548
1549 ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F;
1550 ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F;
1551 ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F;
1552
1553 *ncolours = NCOLOURS;
1554 return ret;
1555}
1556
1557static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1558{
1559 int w = state->par.w /*, a = w*w */;
1560 struct game_drawstate *ds = snew(struct game_drawstate);
1561 int i;
1562
1563 ds->tilesize = 0;
1564 ds->three_d = !getenv("TOWERS_2D");
1565 ds->started = FALSE;
1566 ds->tiles = snewn((w+2)*(w+2), long);
1567 ds->drawn = snewn((w+2)*(w+2)*4, long);
1568 for (i = 0; i < (w+2)*(w+2)*4; i++)
1569 ds->drawn[i] = -1;
1570 ds->errtmp = snewn((w+2)*(w+2), int);
1571
1572 return ds;
1573}
1574
1575static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1576{
1577 sfree(ds->errtmp);
1578 sfree(ds->tiles);
1579 sfree(ds->drawn);
1580 sfree(ds);
1581}
1582
1583static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
1584 int x, int y, long tile)
1585{
1586 int w = clues->w /* , a = w*w */;
1587 int tx, ty, bg;
1588 char str[64];
1589
1590 tx = COORD(x);
1591 ty = COORD(y);
1592
1593 bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND;
1594
1595 /* draw tower */
1596 if (ds->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
1597 int coords[8];
1598 int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w);
1599 int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w);
1600
1601 /* left face of tower */
1602 coords[0] = tx;
1603 coords[1] = ty - 1;
1604 coords[2] = tx;
1605 coords[3] = ty + TILESIZE - 1;
1606 coords[4] = coords[2] + xoff;
1607 coords[5] = coords[3] - yoff;
1608 coords[6] = coords[0] + xoff;
1609 coords[7] = coords[1] - yoff;
1610 draw_polygon(dr, coords, 4, bg, COL_GRID);
1611
1612 /* bottom face of tower */
1613 coords[0] = tx + TILESIZE;
1614 coords[1] = ty + TILESIZE - 1;
1615 coords[2] = tx;
1616 coords[3] = ty + TILESIZE - 1;
1617 coords[4] = coords[2] + xoff;
1618 coords[5] = coords[3] - yoff;
1619 coords[6] = coords[0] + xoff;
1620 coords[7] = coords[1] - yoff;
1621 draw_polygon(dr, coords, 4, bg, COL_GRID);
1622
1623 /* now offset all subsequent drawing to the top of the tower */
1624 tx += xoff;
1625 ty -= yoff;
1626 }
1627
1628 /* erase background */
1629 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, bg);
1630
1631 /* pencil-mode highlight */
1632 if (tile & DF_HIGHLIGHT_PENCIL) {
1633 int coords[6];
1634 coords[0] = tx;
1635 coords[1] = ty;
1636 coords[2] = tx+TILESIZE/2;
1637 coords[3] = ty;
1638 coords[4] = tx;
1639 coords[5] = ty+TILESIZE/2;
1640 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1641 }
1642
1643 /* draw box outline */
1644 if (tile & DF_PLAYAREA) {
1645 int coords[8];
1646 coords[0] = tx;
1647 coords[1] = ty - 1;
1648 coords[2] = tx + TILESIZE;
1649 coords[3] = ty - 1;
1650 coords[4] = tx + TILESIZE;
1651 coords[5] = ty + TILESIZE - 1;
1652 coords[6] = tx;
1653 coords[7] = ty + TILESIZE - 1;
1654 draw_polygon(dr, coords, 4, -1, COL_GRID);
1655 }
1656
1657 /* new number needs drawing? */
1658 if (tile & DF_DIGIT_MASK) {
1659 int color;
1660
1661 str[1] = '\0';
1662 str[0] = (tile & DF_DIGIT_MASK) + '0';
1663
1664 if (tile & DF_ERROR)
1665 color = COL_ERROR;
1666 else if (tile & DF_CLUE_DONE)
1667 color = COL_DONE;
1668 else if (x < 0 || y < 0 || x >= w || y >= w)
1669 color = COL_GRID;
1670 else if (tile & DF_IMMUTABLE)
1671 color = COL_GRID;
1672 else
1673 color = COL_USER;
1674
1675 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, FONT_VARIABLE,
1676 (tile & DF_PLAYAREA ? TILESIZE/2 : TILESIZE*2/5),
1677 ALIGN_VCENTRE | ALIGN_HCENTRE, color, str);
1678 } else {
1679 int i, j, npencil;
1680 int pl, pr, pt, pb;
1681 float bestsize;
1682 int pw, ph, minph, pbest, fontsize;
1683
1684 /* Count the pencil marks required. */
1685 for (i = 1, npencil = 0; i <= w; i++)
1686 if (tile & (1L << (i + DF_PENCIL_SHIFT)))
1687 npencil++;
1688 if (npencil) {
1689
1690 minph = 2;
1691
1692 /*
1693 * Determine the bounding rectangle within which we're going
1694 * to put the pencil marks.
1695 */
1696 /* Start with the whole square, minus space for impinging towers */
1697 pl = tx + (ds->three_d ? X_3D_DISP(w,w) : 0);
1698 pr = tx + TILESIZE;
1699 pt = ty;
1700 pb = ty + TILESIZE - (ds->three_d ? Y_3D_DISP(w,w) : 0);
1701
1702 /*
1703 * We arrange our pencil marks in a grid layout, with
1704 * the number of rows and columns adjusted to allow the
1705 * maximum font size.
1706 *
1707 * So now we work out what the grid size ought to be.
1708 */
1709 bestsize = 0.0;
1710 pbest = 0;
1711 /* Minimum */
1712 for (pw = 3; pw < max(npencil,4); pw++) {
1713 float fw, fh, fs;
1714
1715 ph = (npencil + pw - 1) / pw;
1716 ph = max(ph, minph);
1717 fw = (pr - pl) / (float)pw;
1718 fh = (pb - pt) / (float)ph;
1719 fs = min(fw, fh);
1720 if (fs > bestsize) {
1721 bestsize = fs;
1722 pbest = pw;
1723 }
1724 }
1725 assert(pbest > 0);
1726 pw = pbest;
1727 ph = (npencil + pw - 1) / pw;
1728 ph = max(ph, minph);
1729
1730 /*
1731 * Now we've got our grid dimensions, work out the pixel
1732 * size of a grid element, and round it to the nearest
1733 * pixel. (We don't want rounding errors to make the
1734 * grid look uneven at low pixel sizes.)
1735 */
1736 fontsize = min((pr - pl) / pw, (pb - pt) / ph);
1737
1738 /*
1739 * Centre the resulting figure in the square.
1740 */
1741 pl = pl + (pr - pl - fontsize * pw) / 2;
1742 pt = pt + (pb - pt - fontsize * ph) / 2;
1743
1744 /*
1745 * Now actually draw the pencil marks.
1746 */
1747 for (i = 1, j = 0; i <= w; i++)
1748 if (tile & (1L << (i + DF_PENCIL_SHIFT))) {
1749 int dx = j % pw, dy = j / pw;
1750
1751 str[1] = '\0';
1752 str[0] = i + '0';
1753 draw_text(dr, pl + fontsize * (2*dx+1) / 2,
1754 pt + fontsize * (2*dy+1) / 2,
1755 FONT_VARIABLE, fontsize,
1756 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
1757 j++;
1758 }
1759 }
1760 }
1761}
1762
1763static void game_redraw(drawing *dr, game_drawstate *ds,
1764 const game_state *oldstate, const game_state *state,
1765 int dir, const game_ui *ui,
1766 float animtime, float flashtime)
1767{
1768 int w = state->par.w /*, a = w*w */;
1769 int i, x, y;
1770
1771 if (!ds->started) {
1772 /*
1773 * The initial contents of the window are not guaranteed and
1774 * can vary with front ends. To be on the safe side, all
1775 * games should start by drawing a big background-colour
1776 * rectangle covering the whole window.
1777 */
1778 draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
1779
1780 draw_update(dr, 0, 0, SIZE(w), SIZE(w));
1781
1782 ds->started = TRUE;
1783 }
1784
1785 check_errors(state, ds->errtmp);
1786
1787 /*
1788 * Work out what data each tile should contain.
1789 */
1790 for (i = 0; i < (w+2)*(w+2); i++)
1791 ds->tiles[i] = 0; /* completely blank square */
1792 /* The clue squares... */
1793 for (i = 0; i < 4*w; i++) {
1794 long tile = state->clues->clues[i];
1795
1796 CLUEPOS(x, y, i, w);
1797
1798 if (ds->errtmp[(y+1)*(w+2)+(x+1)])
1799 tile |= DF_ERROR;
1800 else if (state->clues_done[i])
1801 tile |= DF_CLUE_DONE;
1802
1803 ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
1804 }
1805 /* ... and the main grid. */
1806 for (y = 0; y < w; y++) {
1807 for (x = 0; x < w; x++) {
1808 long tile = DF_PLAYAREA;
1809
1810 if (state->grid[y*w+x])
1811 tile |= state->grid[y*w+x];
1812 else
1813 tile |= (long)state->pencil[y*w+x] << DF_PENCIL_SHIFT;
1814
1815 if (ui->hshow && ui->hx == x && ui->hy == y)
1816 tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
1817
1818 if (state->clues->immutable[y*w+x])
1819 tile |= DF_IMMUTABLE;
1820
1821 if (flashtime > 0 &&
1822 (flashtime <= FLASH_TIME/3 ||
1823 flashtime >= FLASH_TIME*2/3))
1824 tile |= DF_HIGHLIGHT; /* completion flash */
1825
1826 if (ds->errtmp[(y+1)*(w+2)+(x+1)])
1827 tile |= DF_ERROR;
1828
1829 ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
1830 }
1831 }
1832
1833 /*
1834 * Now actually draw anything that needs to be changed.
1835 */
1836 for (y = 0; y < w+2; y++) {
1837 for (x = 0; x < w+2; x++) {
1838 long tl, tr, bl, br;
1839 int i = y*(w+2)+x;
1840
1841 tr = ds->tiles[y*(w+2)+x];
1842 tl = (x == 0 ? 0 : ds->tiles[y*(w+2)+(x-1)]);
1843 br = (y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+x]);
1844 bl = (x == 0 || y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+(x-1)]);
1845
1846 if (ds->drawn[i*4] != tl || ds->drawn[i*4+1] != tr ||
1847 ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) {
1848 clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
1849
1850 draw_tile(dr, ds, state->clues, x-1, y-1, tr);
1851 if (x > 0)
1852 draw_tile(dr, ds, state->clues, x-2, y-1, tl);
1853 if (y <= w)
1854 draw_tile(dr, ds, state->clues, x-1, y, br);
1855 if (x > 0 && y <= w)
1856 draw_tile(dr, ds, state->clues, x-2, y, bl);
1857
1858 unclip(dr);
1859 draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
1860
1861 ds->drawn[i*4] = tl;
1862 ds->drawn[i*4+1] = tr;
1863 ds->drawn[i*4+2] = bl;
1864 ds->drawn[i*4+3] = br;
1865 }
1866 }
1867 }
1868}
1869
1870static float game_anim_length(const game_state *oldstate,
1871 const game_state *newstate, int dir, game_ui *ui)
1872{
1873 return 0.0F;
1874}
1875
1876static float game_flash_length(const game_state *oldstate,
1877 const game_state *newstate, int dir, game_ui *ui)
1878{
1879 if (!oldstate->completed && newstate->completed &&
1880 !oldstate->cheated && !newstate->cheated)
1881 return FLASH_TIME;
1882 return 0.0F;
1883}
1884
1885static int game_status(const game_state *state)
1886{
1887 return state->completed ? +1 : 0;
1888}
1889
1890static int game_timing_state(const game_state *state, game_ui *ui)
1891{
1892 if (state->completed)
1893 return FALSE;
1894 return TRUE;
1895}
1896
1897static void game_print_size(const game_params *params, float *x, float *y)
1898{
1899 int pw, ph;
1900
1901 /*
1902 * We use 9mm squares by default, like Solo.
1903 */
1904 game_compute_size(params, 900, &pw, &ph);
1905 *x = pw / 100.0F;
1906 *y = ph / 100.0F;
1907}
1908
1909static void game_print(drawing *dr, const game_state *state, int tilesize)
1910{
1911 int w = state->par.w;
1912 int ink = print_mono_colour(dr, 0);
1913 int i, x, y;
1914
1915 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1916 game_drawstate ads, *ds = &ads;
1917 game_set_size(dr, ds, NULL, tilesize);
1918
1919 /*
1920 * Border.
1921 */
1922 print_line_width(dr, 3 * TILESIZE / 40);
1923 draw_rect_outline(dr, BORDER, BORDER, w*TILESIZE, w*TILESIZE, ink);
1924
1925 /*
1926 * Main grid.
1927 */
1928 for (x = 1; x < w; x++) {
1929 print_line_width(dr, TILESIZE / 40);
1930 draw_line(dr, BORDER+x*TILESIZE, BORDER,
1931 BORDER+x*TILESIZE, BORDER+w*TILESIZE, ink);
1932 }
1933 for (y = 1; y < w; y++) {
1934 print_line_width(dr, TILESIZE / 40);
1935 draw_line(dr, BORDER, BORDER+y*TILESIZE,
1936 BORDER+w*TILESIZE, BORDER+y*TILESIZE, ink);
1937 }
1938
1939 /*
1940 * Clues.
1941 */
1942 for (i = 0; i < 4*w; i++) {
1943 char str[128];
1944
1945 if (!state->clues->clues[i])
1946 continue;
1947
1948 CLUEPOS(x, y, i, w);
1949
1950 sprintf (str, "%d", state->clues->clues[i]);
1951
1952 draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2,
1953 BORDER + y*TILESIZE + TILESIZE/2,
1954 FONT_VARIABLE, TILESIZE/2,
1955 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
1956 }
1957
1958 /*
1959 * Numbers for the solution, if any.
1960 */
1961 for (y = 0; y < w; y++)
1962 for (x = 0; x < w; x++)
1963 if (state->grid[y*w+x]) {
1964 char str[2];
1965 str[1] = '\0';
1966 str[0] = state->grid[y*w+x] + '0';
1967 draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2,
1968 BORDER + y*TILESIZE + TILESIZE/2,
1969 FONT_VARIABLE, TILESIZE/2,
1970 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
1971 }
1972}
1973
1974#ifdef COMBINED
1975#define thegame towers
1976#endif
1977
1978const struct game thegame = {
1979 "Towers", "games.towers", "towers",
1980 default_params,
1981 game_fetch_preset, NULL,
1982 decode_params,
1983 encode_params,
1984 free_params,
1985 dup_params,
1986 TRUE, game_configure, custom_params,
1987 validate_params,
1988 new_game_desc,
1989 validate_desc,
1990 new_game,
1991 dup_game,
1992 free_game,
1993 TRUE, solve_game,
1994 TRUE, game_can_format_as_text_now, game_text_format,
1995 new_ui,
1996 free_ui,
1997 encode_ui,
1998 decode_ui,
1999 game_changed_state,
2000 interpret_move,
2001 execute_move,
2002 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2003 game_colours,
2004 game_new_drawstate,
2005 game_free_drawstate,
2006 game_redraw,
2007 game_anim_length,
2008 game_flash_length,
2009 game_status,
2010 TRUE, FALSE, game_print_size, game_print,
2011 FALSE, /* wants_statusbar */
2012 FALSE, game_timing_state,
2013 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2014};
2015
2016#ifdef STANDALONE_SOLVER
2017
2018#include <stdarg.h>
2019
2020int main(int argc, char **argv)
2021{
2022 game_params *p;
2023 game_state *s;
2024 char *id = NULL, *desc, *err;
2025 int grade = FALSE;
2026 int ret, diff, really_show_working = FALSE;
2027
2028 while (--argc > 0) {
2029 char *p = *++argv;
2030 if (!strcmp(p, "-v")) {
2031 really_show_working = TRUE;
2032 } else if (!strcmp(p, "-g")) {
2033 grade = TRUE;
2034 } else if (*p == '-') {
2035 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2036 return 1;
2037 } else {
2038 id = p;
2039 }
2040 }
2041
2042 if (!id) {
2043 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2044 return 1;
2045 }
2046
2047 desc = strchr(id, ':');
2048 if (!desc) {
2049 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2050 return 1;
2051 }
2052 *desc++ = '\0';
2053
2054 p = default_params();
2055 decode_params(p, id);
2056 err = validate_desc(p, desc);
2057 if (err) {
2058 fprintf(stderr, "%s: %s\n", argv[0], err);
2059 return 1;
2060 }
2061 s = new_game(NULL, p, desc);
2062
2063 /*
2064 * When solving an Easy puzzle, we don't want to bother the
2065 * user with Hard-level deductions. For this reason, we grade
2066 * the puzzle internally before doing anything else.
2067 */
2068 ret = -1; /* placate optimiser */
2069 solver_show_working = FALSE;
2070 for (diff = 0; diff < DIFFCOUNT; diff++) {
2071 memcpy(s->grid, s->clues->immutable, p->w * p->w);
2072 ret = solver(p->w, s->clues->clues, s->grid, diff);
2073 if (ret <= diff)
2074 break;
2075 }
2076
2077 if (diff == DIFFCOUNT) {
2078 if (grade)
2079 printf("Difficulty rating: ambiguous\n");
2080 else
2081 printf("Unable to find a unique solution\n");
2082 } else {
2083 if (grade) {
2084 if (ret == diff_impossible)
2085 printf("Difficulty rating: impossible (no solution exists)\n");
2086 else
2087 printf("Difficulty rating: %s\n", towers_diffnames[ret]);
2088 } else {
2089 solver_show_working = really_show_working;
2090 memcpy(s->grid, s->clues->immutable, p->w * p->w);
2091 ret = solver(p->w, s->clues->clues, s->grid, diff);
2092 if (ret != diff)
2093 printf("Puzzle is inconsistent\n");
2094 else
2095 fputs(game_text_format(s), stdout);
2096 }
2097 }
2098
2099 return 0;
2100}
2101
2102#endif
2103
2104/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/towers.html b/apps/plugins/puzzles/src/towers.html
new file mode 100644
index 0000000000..415f30c0d8
--- /dev/null
+++ b/apps/plugins/puzzles/src/towers.html
@@ -0,0 +1,88 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Towers</title>
7<link rel="previous" href="keen.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="singles.html">
12</head>
13<body>
14<p><a href="keen.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="singles.html">Next</a></p>
15<h1><a name="C31"></a>Chapter 31: <a name="i0"></a>Towers</h1>
16<p>
17You have a square grid. On each square of the grid you can build a tower, with its height ranging from 1 to the size of the grid. Around the edge of the grid are some numeric clues.
18</p>
19<p>
20Your task is to build a tower on every square, in such a way that:
21</p>
22<ul><li>
23Each row contains every possible height of tower once
24</li>
25<li>
26Each column contains every possible height of tower once
27</li>
28<li>
29Each numeric clue describes the number of towers that can be seen if you look into the square from that direction, assuming that shorter towers are hidden behind taller ones. For example, in a 5&#215;5 grid, a clue marked &#8216;5&#8217; indicates that the five tower heights must appear in increasing order (otherwise you would not be able to see all five towers), whereas a clue marked &#8216;1&#8217; indicates that the tallest tower (the one marked 5) must come first.
30</li>
31</ul>
32<p>
33In harder or larger puzzles, some towers will be specified for you as well as the clues round the edge, and some edge clues may be missing.
34</p>
35<p>
36This puzzle appears on the web under various names, particularly &#8216;<a name="i1"></a>Skyscrapers&#8217;, but I don't know who first invented it.
37</p>
38<h2><a name="S31.1"></a>31.1 <a name="i2"></a>Towers controls</h2>
39<p>
40Towers shares much of its control system with Solo, Unequal and Keen.
41</p>
42<p>
43To play Towers, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square with a tower of the given height. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).
44</p>
45<p>
46If you <em>right</em>-click in a square and then type a number, that number will be entered in the square as a &#8216;pencil mark&#8217;. You can have pencil marks for multiple numbers in the same square. A square containing a tower cannot also contain pencil marks.
47</p>
48<p>
49The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.
50</p>
51<p>
52To erase a single pencil mark, right-click in the square and type the same number again.
53</p>
54<p>
55All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.
56</p>
57<p>
58As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks.
59</p>
60<p>
61Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it.
62</p>
63<p>
64Left-clicking a clue will mark it as done (grey it out), or unmark it if it is already marked. Holding Control or Shift and pressing an arrow key likewise marks any clue in the given direction.
65</p>
66<p>
67(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
68</p>
69<h2><a name="S31.2"></a>31.2 <a name="i3"></a>Towers parameters</h2>
70<p>
71These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
72</p>
73<dl><dt>
74<em>Grid size</em>
75</dt>
76<dd>
77Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with &#8216;digits&#8217; bigger than 9!).
78</dd>
79<dt>
80<em>Difficulty</em>
81</dt>
82<dd>
83Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack.
84</dd>
85</dl>
86
87<hr><address></address></body>
88</html>
diff --git a/apps/plugins/puzzles/src/tracks.R b/apps/plugins/puzzles/src/tracks.R
new file mode 100644
index 0000000000..f88dfb03eb
--- /dev/null
+++ b/apps/plugins/puzzles/src/tracks.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3TRACKS_EXTRA = dsf findloop
4
5tracks : [X] GTK COMMON tracks TRACKS_EXTRA tracks-icon|no-icon
6
7tracks : [G] WINDOWS COMMON tracks TRACKS_EXTRA tracks.res|noicon.res
8
9ALL += tracks[COMBINED] TRACKS_EXTRA
10
11!begin am gtk
12GAMES += tracks
13!end
14
15!begin >list.c
16 A(tracks) \
17!end
18
19!begin >gamedesc.txt
20tracks:tracks.exe:Tracks:Path-finding railway track puzzle:Fill in the railway track according to the clues.
21!end
diff --git a/apps/plugins/puzzles/src/tracks.c b/apps/plugins/puzzles/src/tracks.c
new file mode 100644
index 0000000000..43428a19e9
--- /dev/null
+++ b/apps/plugins/puzzles/src/tracks.c
@@ -0,0 +1,2660 @@
1/*
2 * Implementation of 'Train Tracks', a puzzle from the Times on Saturday.
3 *
4 * "Lay tracks to enable the train to travel from village A to village B.
5 * The numbers indicate how many sections of rail go in each row and
6 * column. There are only straight rails and curved rails. The track
7 * cannot cross itself."
8 *
9 * Puzzles:
10 * #9 8x8:d9s5c6zgAa,1,4,1,4,4,3,S3,5,2,2,4,S5,3,3,5,1
11 * #112 8x8:w6x5mAa,1,3,1,4,6,4,S4,3,3,4,5,2,4,2,S5,1
12 * #113 8x8:gCx5xAf,1,S4,2,5,4,6,2,3,4,2,5,2,S4,4,5,1
13 * #114 8x8:p5fAzkAb,1,6,3,3,3,S6,2,3,5,4,S3,3,5,1,5,1
14 * #115 8x8:zi9d5tAb,1,3,4,5,3,S4,2,4,2,6,2,3,6,S3,3,1
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <assert.h>
21#include <ctype.h>
22#include <math.h>
23
24#include "puzzles.h"
25
26/* --- Game parameters --- */
27
28/*
29 * Difficulty levels. I do some macro ickery here to ensure that my
30 * enum and the various forms of my name list always match up.
31 */
32#define DIFFLIST(A) \
33 A(EASY,Easy,e) \
34 A(TRICKY,Tricky,t)
35
36#define ENUM(upper,title,lower) DIFF_ ## upper,
37#define TITLE(upper,title,lower) #title,
38#define ENCODE(upper,title,lower) #lower
39#define CONFIG(upper,title,lower) ":" #title
40enum { DIFFLIST(ENUM) DIFFCOUNT };
41static char const *const tracks_diffnames[] = { DIFFLIST(TITLE) };
42static char const tracks_diffchars[] = DIFFLIST(ENCODE);
43#define DIFFCONFIG DIFFLIST(CONFIG)
44
45struct game_params {
46 int w, h, diff, single_ones;
47};
48
49static game_params *default_params(void)
50{
51 game_params *ret = snew(game_params);
52
53 ret->w = ret->h = 8;
54 ret->diff = DIFF_TRICKY;
55 ret->single_ones = TRUE;
56
57 return ret;
58}
59
60static const struct game_params tracks_presets[] = {
61 {8, 8, DIFF_EASY, 1},
62 {8, 8, DIFF_TRICKY, 1},
63 {10, 8, DIFF_EASY, 1},
64 {10, 8, DIFF_TRICKY, 1 },
65 {10, 10, DIFF_EASY, 1},
66 {10, 10, DIFF_TRICKY, 1},
67 {15, 10, DIFF_EASY, 1},
68 {15, 10, DIFF_TRICKY, 1},
69 {15, 15, DIFF_EASY, 1},
70 {15, 15, DIFF_TRICKY, 1},
71};
72
73static int game_fetch_preset(int i, char **name, game_params **params)
74{
75 game_params *ret;
76 char str[80];
77
78 if (i < 0 || i >= lenof(tracks_presets))
79 return FALSE;
80
81 ret = snew(game_params);
82 *ret = tracks_presets[i];
83
84 sprintf(str, "%dx%d %s", ret->w, ret->h, tracks_diffnames[ret->diff]);
85
86 *name = dupstr(str);
87 *params = ret;
88 return TRUE;
89}
90
91static void free_params(game_params *params)
92{
93 sfree(params);
94}
95
96static game_params *dup_params(const game_params *params)
97{
98 game_params *ret = snew(game_params);
99 *ret = *params; /* structure copy */
100 return ret;
101}
102
103static void decode_params(game_params *params, char const *string)
104{
105 params->w = params->h = atoi(string);
106 while (*string && isdigit((unsigned char)*string)) string++;
107 if (*string == 'x') {
108 string++;
109 params->h = atoi(string);
110 while (*string && isdigit((unsigned char)*string)) string++;
111 }
112 if (*string == 'd') {
113 int i;
114 string++;
115 params->diff = DIFF_TRICKY;
116 for (i = 0; i < DIFFCOUNT; i++)
117 if (*string == tracks_diffchars[i])
118 params->diff = i;
119 if (*string) string++;
120 }
121 params->single_ones = TRUE;
122 if (*string == 'o') {
123 params->single_ones = FALSE;
124 string++;
125 }
126
127}
128
129static char *encode_params(const game_params *params, int full)
130{
131 char buf[120];
132
133 sprintf(buf, "%dx%d", params->w, params->h);
134 if (full)
135 sprintf(buf + strlen(buf), "d%c%s",
136 tracks_diffchars[params->diff],
137 params->single_ones ? "" : "o");
138 return dupstr(buf);
139}
140
141static config_item *game_configure(const game_params *params)
142{
143 config_item *ret;
144 char buf[80];
145
146 ret = snewn(5, config_item);
147
148 ret[0].name = "Width";
149 ret[0].type = C_STRING;
150 sprintf(buf, "%d", params->w);
151 ret[0].sval = dupstr(buf);
152 ret[0].ival = 0;
153
154 ret[1].name = "Height";
155 ret[1].type = C_STRING;
156 sprintf(buf, "%d", params->h);
157 ret[1].sval = dupstr(buf);
158 ret[1].ival = 0;
159
160 ret[2].name = "Difficulty";
161 ret[2].type = C_CHOICES;
162 ret[2].sval = DIFFCONFIG;
163 ret[2].ival = params->diff;
164
165 ret[3].name = "Disallow consecutive 1 clues";
166 ret[3].type = C_BOOLEAN;
167 ret[3].ival = params->single_ones;
168
169 ret[4].name = NULL;
170 ret[4].type = C_END;
171 ret[4].sval = NULL;
172 ret[4].ival = 0;
173
174 return ret;
175}
176
177static game_params *custom_params(const config_item *cfg)
178{
179 game_params *ret = snew(game_params);
180
181 ret->w = atoi(cfg[0].sval);
182 ret->h = atoi(cfg[1].sval);
183 ret->diff = cfg[2].ival;
184 ret->single_ones = cfg[3].ival;
185
186 return ret;
187}
188
189static char *validate_params(const game_params *params, int full)
190{
191 /*
192 * Generating anything under 4x4 runs into trouble of one kind
193 * or another.
194 */
195 if (params->w < 4 || params->h < 4)
196 return "Width and height must both be at least four";
197 return NULL;
198}
199
200/* --- Game state --- */
201
202/* flag usage copied from pearl */
203
204#define R 1
205#define U 2
206#define L 4
207#define D 8
208
209#define MOVECHAR(m) ((m==R)?'R':(m==U)?'U':(m==L)?'L':(m==D)?'D':'?')
210
211#define DX(d) ( ((d)==R) - ((d)==L) )
212#define DY(d) ( ((d)==D) - ((d)==U) )
213
214#define F(d) (((d << 2) | (d >> 2)) & 0xF)
215#define C(d) (((d << 3) | (d >> 1)) & 0xF)
216#define A(d) (((d << 1) | (d >> 3)) & 0xF)
217
218#define LR (L | R)
219#define RL (R | L)
220#define UD (U | D)
221#define DU (D | U)
222#define LU (L | U)
223#define UL (U | L)
224#define LD (L | D)
225#define DL (D | L)
226#define RU (R | U)
227#define UR (U | R)
228#define RD (R | D)
229#define DR (D | R)
230#define ALLDIR 15
231#define BLANK 0
232#define UNKNOWN 15
233
234int nbits[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
235
236/* square grid flags */
237#define S_TRACK 1 /* a track passes through this square (--> 2 edges) */
238#define S_NOTRACK 2 /* no track passes through this square */
239#define S_ERROR 4
240#define S_CLUE 8
241#define S_MARK 16
242
243#define S_TRACK_SHIFT 16 /* U/D/L/R flags for edge track indicators */
244#define S_NOTRACK_SHIFT 20 /* U/D/L/R flags for edge no-track indicators */
245
246/* edge grid flags */
247#define E_TRACK 1 /* a track passes through this edge */
248#define E_NOTRACK 2 /* no track passes through this edge */
249
250struct numbers {
251 int refcount;
252 int *numbers; /* sz w+h */
253 int row_s, col_s; /* stations: TODO think about multiple lines
254 (for bigger grids)? */
255};
256
257#define INGRID(state, gx, gy) ((gx) >= 0 && (gx) < (state)->p.w && \
258 (gy) >= 0 && (gy) < (state)->p.h)
259
260struct game_state {
261 game_params p;
262 unsigned int *sflags; /* size w*h */
263 struct numbers *numbers;
264 int *num_errors; /* size w+h */
265 int completed, used_solve, impossible;
266};
267
268/* Return the four directions in which a particular edge flag is set, around a square. */
269int S_E_DIRS(const game_state *state, int sx, int sy, unsigned int eflag) {
270 return (state->sflags[sy*state->p.w+sx] >>
271 ((eflag == E_TRACK) ? S_TRACK_SHIFT : S_NOTRACK_SHIFT)) & ALLDIR;
272}
273
274/* Count the number of a particular edge flag around a grid square. */
275int S_E_COUNT(const game_state *state, int sx, int sy, unsigned int eflag) {
276 return nbits[S_E_DIRS(state, sx, sy, eflag)];
277}
278
279/* Return the two flags (E_TRACK and/or E_NOTRACK) set on a specific
280 * edge of a square. */
281unsigned S_E_FLAGS(const game_state *state, int sx, int sy, int d) {
282 unsigned f = state->sflags[sy*state->p.w+sx];
283 int t = (f & (d << S_TRACK_SHIFT)), nt = (f & (d << S_NOTRACK_SHIFT));
284 return (t ? E_TRACK : 0) | (nt ? E_NOTRACK : 0);
285}
286
287int S_E_ADJ(const game_state *state, int sx, int sy, int d, int *ax, int *ay, unsigned int *ad) {
288 if (d == L && sx > 0) { *ax = sx-1; *ay = sy; *ad = R; return 1; }
289 if (d == R && sx < state->p.w-1) { *ax = sx+1; *ay = sy; *ad = L; return 1; }
290 if (d == U && sy > 0) { *ax = sx; *ay = sy-1; *ad = D; return 1; }
291 if (d == D && sy < state->p.h-1) { *ax = sx; *ay = sy+1; *ad = U; return 1; }
292
293 return 0;
294}
295
296/* Sets flag (E_TRACK or E_NOTRACK) on a given edge of a square. */
297void S_E_SET(game_state *state, int sx, int sy, int d, unsigned int eflag) {
298 unsigned shift = (eflag == E_TRACK) ? S_TRACK_SHIFT : S_NOTRACK_SHIFT, ad;
299 int ax, ay;
300
301 state->sflags[sy*state->p.w+sx] |= (d << shift);
302
303 if (S_E_ADJ(state, sx, sy, d, &ax, &ay, &ad)) {
304 state->sflags[ay*state->p.w+ax] |= (ad << shift);
305 }
306}
307
308/* Clears flag (E_TRACK or E_NOTRACK) on a given edge of a square. */
309void S_E_CLEAR(game_state *state, int sx, int sy, int d, unsigned int eflag) {
310 unsigned shift = (eflag == E_TRACK) ? S_TRACK_SHIFT : S_NOTRACK_SHIFT, ad;
311 int ax, ay;
312
313 state->sflags[sy*state->p.w+sx] &= ~(d << shift);
314
315 if (S_E_ADJ(state, sx, sy, d, &ax, &ay, &ad)) {
316 state->sflags[ay*state->p.w+ax] &= ~(ad << shift);
317 }
318}
319
320static void clear_game(game_state *state)
321{
322 int w = state->p.w, h = state->p.h;
323
324 memset(state->sflags, 0, w*h * sizeof(unsigned int));
325
326 memset(state->numbers->numbers, 0, (w+h) * sizeof(int));
327 state->numbers->col_s = state->numbers->row_s = -1;
328
329 memset(state->num_errors, 0, (w+h) * sizeof(int));
330
331 state->completed = state->used_solve = state->impossible = FALSE;
332}
333
334static game_state *blank_game(const game_params *params)
335{
336 game_state *state = snew(game_state);
337 int w = params->w, h = params->h;
338
339 state->p = *params;
340
341 state->sflags = snewn(w*h, unsigned int);
342
343 state->numbers = snew(struct numbers);
344 state->numbers->refcount = 1;
345 state->numbers->numbers = snewn(w+h, int);
346
347 state->num_errors = snewn(w+h, int);
348
349 clear_game(state);
350
351 return state;
352}
353
354static void copy_game_flags(const game_state *src, game_state *dest)
355{
356 int w = src->p.w, h = src->p.h;
357
358 memcpy(dest->sflags, src->sflags, w*h*sizeof(unsigned int));
359}
360
361static game_state *dup_game(const game_state *state)
362{
363 int w = state->p.w, h = state->p.h;
364 game_state *ret = snew(game_state);
365
366 ret->p = state->p; /* structure copy */
367
368 ret->sflags = snewn(w*h, unsigned int);
369 copy_game_flags(state, ret);
370
371 ret->numbers = state->numbers;
372 state->numbers->refcount++;
373 ret->num_errors = snewn(w+h, int);
374 memcpy(ret->num_errors, state->num_errors, (w+h)*sizeof(int));
375
376 ret->completed = state->completed;
377 ret->used_solve = state->used_solve;
378 ret->impossible = state->impossible;
379
380 return ret;
381}
382
383static void free_game(game_state *state)
384{
385 if (--state->numbers->refcount <= 0) {
386 sfree(state->numbers->numbers);
387 sfree(state->numbers);
388 }
389 sfree(state->num_errors);
390 sfree(state->sflags);
391 sfree(state);
392}
393
394#define NDIRS 4
395const unsigned int dirs_const[] = { U, D, L, R };
396
397static unsigned int find_direction(game_state *state, random_state *rs,
398 int x, int y)
399{
400 int i, nx, ny, w=state->p.w, h=state->p.h;
401 unsigned int dirs[NDIRS];
402
403 memcpy(dirs, dirs_const, sizeof(dirs));
404 shuffle(dirs, NDIRS, sizeof(*dirs), rs);
405 for (i = 0; i < NDIRS; i++) {
406 nx = x + DX(dirs[i]);
407 ny = y + DY(dirs[i]);
408 if (nx >= 0 && nx < w && ny == h) {
409 /* off the bottom of the board: we've finished the path. */
410 return dirs[i];
411 } else if (!INGRID(state, nx, ny)) {
412 /* off the board: can't move here */
413 continue;
414 } else if (S_E_COUNT(state, nx, ny, E_TRACK) > 0) {
415 /* already tracks here: can't move */
416 continue;
417 }
418 return dirs[i];
419 }
420 return 0; /* no possible directions left. */
421}
422
423static int check_completion(game_state *state, int mark);
424
425static void lay_path(game_state *state, random_state *rs)
426{
427 int px, py, w=state->p.w, h=state->p.h;
428 unsigned int d;
429
430start:
431 clear_game(state);
432
433 /* pick a random entry point, lay its left edge */
434 state->numbers->row_s = py = random_upto(rs, h);
435 px = 0;
436 S_E_SET(state, px, py, L, E_TRACK);
437
438 while (INGRID(state, px, py)) {
439 d = find_direction(state, rs, px, py);
440 if (d == 0)
441 goto start; /* nowhere else to go, restart */
442
443 S_E_SET(state, px, py, d, E_TRACK);
444 px += DX(d);
445 py += DY(d);
446 }
447 /* double-check we got to the right place */
448 assert(px >= 0 && px < w && py == h);
449
450 state->numbers->col_s = px;
451}
452
453static int tracks_solve(game_state *state, int diff);
454static void debug_state(game_state *state, const char *what);
455
456/* Clue-setting algorithm:
457
458 - first lay clues randomly until it's soluble
459 - then remove clues randomly if removing them doesn't affect solubility
460
461 - We start with two clues, one at each path entrance.
462
463 More details:
464 - start with an array of all square i positions
465 - if the grid is already soluble by a level easier than we've requested,
466 go back and make a new grid
467 - if the grid is already soluble by our requested difficulty level, skip
468 the clue-laying step
469 - count the number of flags the solver managed to place, remember this.
470
471 - to lay clues:
472 - shuffle the i positions
473 - for each possible clue position:
474 - copy the solved board, strip it
475 - take the next position, add a clue there on the copy
476 - try and solve the copy
477 - if it's soluble by a level easier than we've requested, continue (on
478 to next clue position: putting a clue here makes it too easy)
479 - if it's soluble by our difficulty level, we're done:
480 - put the clue flag into the solved board
481 - go to strip-clues.
482 - if the solver didn't manage to place any more flags, continue (on to next
483 clue position: putting a clue here didn't help he solver)
484 - otherwise put the clue flag in the original board, and go on to the next
485 clue position
486 - if we get here and we've not solved it yet, we never will (did we really
487 fill _all_ the clues in?!). Go back and make a new grid.
488
489 - to strip clues:
490 - shuffle the i positions
491 - for each possible clue position:
492 - if the solved grid doesn't have a clue here, skip
493 - copy the solved board, remove this clue, strip it
494 - try and solve the copy
495 - assert that it is not soluble by a level easier than we've requested
496 - (because this should never happen)
497 - if this is (still) soluble by our difficulty level:
498 - remove this clue from the solved board, it's redundant (with the other
499 clues)
500
501 - that should be it.
502*/
503
504static game_state *copy_and_strip(const game_state *state, game_state *ret, int flipcluei)
505{
506 int i, j, w = state->p.w, h = state->p.h;
507
508 copy_game_flags(state, ret);
509
510 /* Add/remove a clue before stripping, if required */
511
512 if (flipcluei != -1)
513 ret->sflags[flipcluei] ^= S_CLUE;
514
515 /* All squares that are not clue squares have square track info erased, and some edge flags.. */
516
517 for (i = 0; i < w*h; i++) {
518 if (!(ret->sflags[i] & S_CLUE)) {
519 ret->sflags[i] &= ~(S_TRACK|S_NOTRACK|S_ERROR|S_MARK);
520 for (j = 0; j < 4; j++) {
521 unsigned f = 1<<j;
522 int xx = i%w + DX(f), yy = i/w + DY(f);
523 if (!INGRID(state, xx, yy) || !(ret->sflags[yy*w+xx] & S_CLUE)) {
524 /* only erase an edge flag if neither side of the edge is S_CLUE. */
525 S_E_CLEAR(ret, i%w, i/w, f, E_TRACK);
526 S_E_CLEAR(ret, i%w, i/w, f, E_NOTRACK);
527 }
528 }
529 }
530 }
531 return ret;
532}
533
534static int solve_progress(const game_state *state) {
535 int i, w = state->p.w, h = state->p.h, progress = 0;
536
537 /* Work out how many flags the solver managed to set (either TRACK
538 or NOTRACK) and return this as a progress measure, to check whether
539 a partially-solved board gets any further than a previous partially-
540 solved board. */
541
542 for (i = 0; i < w*h; i++) {
543 if (state->sflags[i] & S_TRACK) progress++;
544 if (state->sflags[i] & S_NOTRACK) progress++;
545 progress += S_E_COUNT(state, i%w, i/w, E_TRACK);
546 progress += S_E_COUNT(state, i%w, i/w, E_NOTRACK);
547 }
548 return progress;
549}
550
551static int check_phantom_moves(const game_state *state) {
552 int x, y, i;
553
554 /* Check that this state won't show 'phantom moves' at the start of the
555 * game: squares which have multiple edge flags set but no clue flag
556 * cause a piece of track to appear that isn't on a clue square. */
557
558 for (x = 0; x < state->p.w; x++) {
559 for (y = 0; y < state->p.h; y++) {
560 i = y*state->p.w+x;
561 if (state->sflags[i] & S_CLUE)
562 continue;
563 if (S_E_COUNT(state, x, y, E_TRACK) > 1)
564 return 1; /* found one! */
565 }
566 }
567 return 0;
568}
569
570static int add_clues(game_state *state, random_state *rs, int diff)
571{
572 int i, j, pi, w = state->p.w, h = state->p.h, progress, ret = 0, sr;
573 int *positions = snewn(w*h, int), npositions = 0;
574 int *nedges_previous_solve = snewn(w*h, int);
575 game_state *scratch = dup_game(state);
576
577 debug_state(state, "gen: Initial board");
578
579 debug(("gen: Adding clues..."));
580
581 /* set up the shuffly-position grid for later, used for adding clues:
582 * we only bother adding clues where any edges are set. */
583 for (i = 0; i < w*h; i++) {
584 if (S_E_DIRS(state, i%w, i/w, E_TRACK) != 0) {
585 positions[npositions++] = i;
586 }
587 nedges_previous_solve[i] = 0;
588 }
589
590 /* First, check whether the puzzle is already either too easy, or just right */
591 scratch = copy_and_strip(state, scratch, -1);
592 if (diff > 0) {
593 sr = tracks_solve(scratch, diff-1);
594 if (sr < 0)
595 assert(!"Generator should not have created impossible puzzle");
596 if (sr > 0) {
597 ret = -1; /* already too easy, even without adding clues. */
598 debug(("gen: ...already too easy, need new board."));
599 goto done;
600 }
601 }
602 sr = tracks_solve(scratch, diff);
603 if (sr < 0)
604 assert(!"Generator should not have created impossible puzzle");
605 if (sr > 0) {
606 ret = 1; /* already soluble without any extra clues. */
607 debug(("gen: ...soluble without clues, nothing to do."));
608 goto done;
609 }
610 debug_state(scratch, "gen: Initial part-solved state: ");
611 progress = solve_progress(scratch);
612 debug(("gen: Initial solve progress is %d", progress));
613
614 /* First, lay clues until we're soluble. */
615 shuffle(positions, npositions, sizeof(int), rs);
616 for (pi = 0; pi < npositions; pi++) {
617 i = positions[pi]; /* pick a random position */
618 if (state->sflags[i] & S_CLUE)
619 continue; /* already a clue here (entrance location?) */
620 if (nedges_previous_solve[i] == 2)
621 continue; /* no point putting a clue here, we could solve both edges
622 with the previous set of clues */
623
624 /* set a clue in that position (on a copy of the board) and test solubility */
625 scratch = copy_and_strip(state, scratch, i);
626
627 if (check_phantom_moves(scratch))
628 continue; /* adding a clue here would add phantom track */
629
630 if (diff > 0) {
631 if (tracks_solve(scratch, diff-1) > 0) {
632 continue; /* adding a clue here makes it too easy */
633 }
634 }
635 if (tracks_solve(scratch, diff) > 0) {
636 /* we're now soluble (and we weren't before): add this clue, and then
637 start stripping clues */
638 debug(("gen: ...adding clue at (%d,%d), now soluble", i%w, i/w));
639 state->sflags[i] |= S_CLUE;
640 goto strip_clues;
641 }
642 if (solve_progress(scratch) > progress) {
643 /* We've made more progress solving: add this clue, then. */
644 progress = solve_progress(scratch);
645 debug(("gen: ... adding clue at (%d,%d), new progress %d", i%w, i/w, progress));
646 state->sflags[i] |= S_CLUE;
647
648 for (j = 0; j < w*h; j++)
649 nedges_previous_solve[j] = S_E_COUNT(scratch, j%w, j/w, E_TRACK);
650 }
651 }
652 /* If we got here we didn't ever manage to make the puzzle soluble
653 (without making it too easily soluble, that is): give up. */
654
655 debug(("gen: Unable to make soluble with clues, need new board."));
656 ret = -1;
657 goto done;
658
659strip_clues:
660 debug(("gen: Stripping clues."));
661
662 /* Now, strip redundant clues (i.e. those without which the puzzle is still
663 soluble) */
664 shuffle(positions, npositions, sizeof(int), rs);
665 for (pi = 0; pi < npositions; pi++) {
666 i = positions[pi]; /* pick a random position */
667 if (!(state->sflags[i] & S_CLUE))
668 continue; /* no clue here to strip */
669 if ((i%w == 0 && i/w == state->numbers->row_s) ||
670 (i/w == (h-1) && i%w == state->numbers->col_s))
671 continue; /* don't strip clues at entrance/exit */
672
673 scratch = copy_and_strip(state, scratch, i);
674 if (check_phantom_moves(scratch))
675 continue; /* removing a clue here would add phantom track */
676
677 if (tracks_solve(scratch, diff) > 0) {
678 debug(("gen: ... removing clue at (%d,%d), still soluble without it", i%w, i/w));
679 state->sflags[i] &= ~S_CLUE; /* still soluble without this clue. */
680 }
681 }
682 debug(("gen: Finished stripping clues."));
683 ret = 1;
684
685done:
686 sfree(positions);
687 free_game(scratch);
688 return ret;
689}
690
691static char *new_game_desc(const game_params *params, random_state *rs,
692 char **aux, int interactive)
693{
694 int i, j, w = params->w, h = params->h, x, y, ret;
695 game_state *state;
696 char *desc, *p;
697 game_params adjusted_params;
698
699 /*
700 * 4x4 Tricky cannot be generated, so fall back to Easy.
701 */
702 if (w == 4 && h == 4 && params->diff > DIFF_EASY) {
703 adjusted_params = *params; /* structure copy */
704 adjusted_params.diff = DIFF_EASY;
705 params = &adjusted_params;
706 }
707
708 state = blank_game(params);
709
710 /* --- lay the random path */
711
712newpath:
713 lay_path(state, rs);
714 for (x = 0; x < w; x++) {
715 for (y = 0; y < h; y++) {
716 if (S_E_COUNT(state, x, y, E_TRACK) > 0) {
717 state->sflags[y*w + x] |= S_TRACK;
718 }
719 if ((x == 0 && y == state->numbers->row_s) ||
720 (y == (h-1) && x == state->numbers->col_s)) {
721 state->sflags[y*w + x] |= S_CLUE;
722 }
723 }
724 }
725
726 /* --- Update the clue numbers based on the tracks we have generated. */
727 for (x = 0; x < w; x++) {
728 for (y = 0; y < h; y++) {
729 if (state->sflags[y*w + x] & S_TRACK) {
730 state->numbers->numbers[x]++;
731 state->numbers->numbers[y+w]++;
732 }
733 }
734 }
735 for (i = 0; i < w+h; i++) {
736 if (state->numbers->numbers[i] == 0)
737 goto newpath; /* too boring */
738 }
739
740 if (params->single_ones) {
741 int last_was_one = 1, is_one; /* (disallow 1 clue at entry point) */
742 for (i = 0; i < w+h; i++) {
743 is_one = (state->numbers->numbers[i] == 1);
744 if (is_one && last_was_one)
745 goto newpath; /* disallow consecutive 1 clues. */
746 last_was_one = is_one;
747 }
748 if (state->numbers->numbers[w+h-1] == 1)
749 goto newpath; /* (disallow 1 clue at exit point) */
750 }
751
752 /* --- Add clues to make a soluble puzzle */
753 ret = add_clues(state, rs, params->diff);
754 if (ret != 1) goto newpath; /* couldn't make it soluble, or too easy */
755
756 /* --- Generate the game desc based on the generated grid. */
757 desc = snewn(w*h*3 + (w+h)*5, char);
758 for (i = j = 0; i < w*h; i++) {
759 if (!(state->sflags[i] & S_CLUE) && j > 0 &&
760 desc[j-1] >= 'a' && desc[j-1] < 'z')
761 desc[j-1]++;
762 else if (!(state->sflags[i] & S_CLUE))
763 desc[j++] = 'a';
764 else {
765 unsigned int f = S_E_DIRS(state, i%w, i/w, E_TRACK);
766 desc[j++] = (f < 10) ? ('0' + f) : ('A' + (f-10));
767 }
768 }
769
770 p = desc + j;
771 for (x = 0; x < w; x++) {
772 p += sprintf(p, ",%s%d", x == state->numbers->col_s ? "S" : "",
773 state->numbers->numbers[x]);
774 }
775 for (y = 0; y < h; y++) {
776 p += sprintf(p, ",%s%d", y == state->numbers->row_s ? "S" : "",
777 state->numbers->numbers[y+w]);
778 }
779 *p++ = '\0';
780
781 ret = tracks_solve(state, DIFFCOUNT);
782 assert(ret >= 0);
783 free_game(state);
784
785 debug(("new_game_desc: %s", desc));
786 return desc;
787}
788
789static char *validate_desc(const game_params *params, const char *desc)
790{
791 int i = 0, w = params->w, h = params->h, in = 0, out = 0;
792
793 while (*desc) {
794 unsigned int f = 0;
795 if (*desc >= '0' && *desc <= '9')
796 f = (*desc - '0');
797 else if (*desc >= 'A' && *desc <= 'F')
798 f = (*desc - 'A' + 10);
799 else if (*desc >= 'a' && *desc <= 'z')
800 i += *desc - 'a';
801 else
802 return "Game description contained unexpected characters";
803
804 if (f != 0) {
805 if (nbits[f] != 2)
806 return "Clue did not provide 2 direction flags";
807 }
808 i++;
809 desc++;
810 if (i == w*h) break;
811 }
812 for (i = 0; i < w+h; i++) {
813 if (!*desc)
814 return "Not enough numbers given after grid specification";
815 else if (*desc != ',')
816 return "Invalid character in number list";
817 desc++;
818 if (*desc == 'S') {
819 if (i < w)
820 out++;
821 else
822 in++;
823 desc++;
824 }
825 while (*desc && isdigit((unsigned char)*desc)) desc++;
826 }
827 if (in != 1 || out != 1)
828 return "Puzzle must have one entrance and one exit";
829 if (*desc)
830 return "Unexpected additional character at end of game description";
831 return NULL;
832}
833
834static game_state *new_game(midend *me, const game_params *params, const char *desc)
835{
836 game_state *state = blank_game(params);
837 int w = params->w, h = params->h, i = 0;
838
839 while (*desc) {
840 unsigned int f = 0;
841 if (*desc >= '0' && *desc <= '9')
842 f = (*desc - '0');
843 else if (*desc >= 'A' && *desc <= 'F')
844 f = (*desc - 'A' + 10);
845 else if (*desc >= 'a' && *desc <= 'z')
846 i += *desc - 'a';
847
848 if (f != 0) {
849 int x = i % w, y = i / w;
850 assert(f < 16);
851 assert(nbits[f] == 2);
852
853 state->sflags[i] |= (S_TRACK | S_CLUE);
854 if (f & U) S_E_SET(state, x, y, U, E_TRACK);
855 if (f & D) S_E_SET(state, x, y, D, E_TRACK);
856 if (f & L) S_E_SET(state, x, y, L, E_TRACK);
857 if (f & R) S_E_SET(state, x, y, R, E_TRACK);
858 }
859 i++;
860 desc++;
861 if (i == w*h) break;
862 }
863 for (i = 0; i < w+h; i++) {
864 assert(*desc == ',');
865 desc++;
866
867 if (*desc == 'S') {
868 if (i < w)
869 state->numbers->col_s = i;
870 else
871 state->numbers->row_s = i-w;
872 desc++;
873 }
874 state->numbers->numbers[i] = atoi(desc);
875 while (*desc && isdigit((unsigned char)*desc)) desc++;
876 }
877
878 assert(!*desc);
879
880 return state;
881}
882
883static int solve_set_sflag(game_state *state, int x, int y,
884 unsigned int f, const char *why)
885{
886 int w = state->p.w, i = y*w + x;
887
888 if (state->sflags[i] & f)
889 return 0;
890 debug(("solve: square (%d,%d) -> %s: %s",
891 x, y, (f == S_TRACK ? "TRACK" : "NOTRACK"), why));
892 if (state->sflags[i] & (f == S_TRACK ? S_NOTRACK : S_TRACK)) {
893 debug(("solve: opposite flag already set there, marking IMPOSSIBLE"));
894 state->impossible = TRUE;
895 }
896 state->sflags[i] |= f;
897 return 1;
898}
899
900static int solve_set_eflag(game_state *state, int x, int y, int d,
901 unsigned int f, const char *why)
902{
903 int sf = S_E_FLAGS(state, x, y, d);
904
905 if (sf & f)
906 return 0;
907 debug(("solve: edge (%d,%d)/%c -> %s: %s", x, y,
908 (d == U) ? 'U' : (d == D) ? 'D' : (d == L) ? 'L' : 'R',
909 (f == S_TRACK ? "TRACK" : "NOTRACK"), why));
910 if (sf & (f == E_TRACK ? E_NOTRACK : E_TRACK)) {
911 debug(("solve: opposite flag already set there, marking IMPOSSIBLE"));
912 state->impossible = TRUE;
913 }
914 S_E_SET(state, x, y, d, f);
915 return 1;
916}
917
918static int solve_update_flags(game_state *state)
919{
920 int x, y, i, w = state->p.w, h = state->p.h, did = 0;
921
922 for (x = 0; x < w; x++) {
923 for (y = 0; y < h; y++) {
924 /* If a square is NOTRACK, all four edges must be. */
925 if (state->sflags[y*w + x] & S_NOTRACK) {
926 for (i = 0; i < 4; i++) {
927 unsigned int d = 1<<i;
928 did += solve_set_eflag(state, x, y, d, E_NOTRACK, "edges around NOTRACK");
929 }
930 }
931
932 /* If 3 or more edges around a square are NOTRACK, the square is. */
933 if (S_E_COUNT(state, x, y, E_NOTRACK) >= 3) {
934 did += solve_set_sflag(state, x, y, S_NOTRACK, "square has >2 NOTRACK edges");
935 }
936
937 /* If any edge around a square is TRACK, the square is. */
938 if (S_E_COUNT(state, x, y, E_TRACK) > 0) {
939 did += solve_set_sflag(state, x, y, S_TRACK, "square has TRACK edge");
940 }
941
942 /* If a square is TRACK and 2 edges are NOTRACK,
943 the other two edges must be TRACK. */
944 if ((state->sflags[y*w + x] & S_TRACK) &&
945 (S_E_COUNT(state, x, y, E_NOTRACK) == 2) &&
946 (S_E_COUNT(state, x, y, E_TRACK) < 2)) {
947 for (i = 0; i < 4; i++) {
948 unsigned int d = 1<<i;
949 if (!(S_E_FLAGS(state, x, y, d) & (E_TRACK|E_NOTRACK))) {
950 did += solve_set_eflag(state, x, y, d, E_TRACK,
951 "TRACK square/2 NOTRACK edges");
952 }
953 }
954 }
955
956 /* If a square is TRACK and 2 edges are TRACK, the other two
957 must be NOTRACK. */
958 if ((state->sflags[y*w + x] & S_TRACK) &&
959 (S_E_COUNT(state, x, y, E_TRACK) == 2) &&
960 (S_E_COUNT(state, x, y, E_NOTRACK) < 2)) {
961 for (i = 0; i < 4; i++) {
962 unsigned int d = 1<<i;
963 if (!(S_E_FLAGS(state, x, y, d) & (E_TRACK|E_NOTRACK))) {
964 did += solve_set_eflag(state, x, y, d, E_NOTRACK,
965 "TRACK square/2 TRACK edges");
966 }
967 }
968 }
969 }
970 }
971 return did;
972}
973
974static int solve_count_col(game_state *state, int col, unsigned int f)
975{
976 int i, n, c = 0, h = state->p.h, w = state->p.w;
977 for (n = 0, i = col; n < h; n++, i += w) {
978 if (state->sflags[i] & f) c++;
979 }
980 return c;
981}
982
983static int solve_count_row(game_state *state, int row, unsigned int f)
984{
985 int i, n, c = 0, w = state->p.w;
986 for (n = 0, i = w*row; n < state->p.w; n++, i++) {
987 if (state->sflags[i] & f) c++;
988 }
989 return c;
990}
991
992static int solve_count_clues_sub(game_state *state, int si, int id, int n,
993 int target, const char *what)
994{
995 int ctrack = 0, cnotrack = 0, did = 0, j, i, w = state->p.w;
996
997 for (j = 0, i = si; j < n; j++, i += id) {
998 if (state->sflags[i] & S_TRACK)
999 ctrack++;
1000 if (state->sflags[i] & S_NOTRACK)
1001 cnotrack++;
1002 }
1003 if (ctrack == target) {
1004 /* everything that's not S_TRACK must be S_NOTRACK. */
1005 for (j = 0, i = si; j < n; j++, i += id) {
1006 if (!(state->sflags[i] & S_TRACK))
1007 did += solve_set_sflag(state, i%w, i/w, S_NOTRACK, what);
1008 }
1009 }
1010 if (cnotrack == (n-target)) {
1011 /* everything that's not S_NOTRACK must be S_TRACK. */
1012 for (j = 0, i = si; j < n; j++, i += id) {
1013 if (!(state->sflags[i] & S_NOTRACK))
1014 did += solve_set_sflag(state, i%w, i/w, S_TRACK, what);
1015 }
1016 }
1017 return did;
1018}
1019
1020static int solve_count_clues(game_state *state)
1021{
1022 int w = state->p.w, h = state->p.h, x, y, target, did = 0;
1023
1024 for (x = 0; x < w; x++) {
1025 target = state->numbers->numbers[x];
1026 did += solve_count_clues_sub(state, x, w, h, target, "col count");
1027 }
1028 for (y = 0; y < h; y++) {
1029 target = state->numbers->numbers[w+y];
1030 did += solve_count_clues_sub(state, y*w, 1, w, target, "row count");
1031 }
1032 return did;
1033}
1034
1035static int solve_check_single_sub(game_state *state, int si, int id, int n,
1036 int target, unsigned int perpf,
1037 const char *what)
1038{
1039 int ctrack = 0, nperp = 0, did = 0, j, i, w = state->p.w;
1040 int n1edge = 0, i1edge = 0, ox, oy, x, y;
1041 unsigned int impossible = 0;
1042
1043 /* For rows or columns which only have one more square to put a track in, we
1044 know the only way a new track section could be there would be to run
1045 perpendicular to the track (otherwise we'd need at least two free squares).
1046 So, if there is nowhere we can run perpendicular to the track (e.g. because
1047 we're on an edge) we know the extra track section much be on one end of an
1048 existing section. */
1049
1050 for (j = 0, i = si; j < n; j++, i += id) {
1051 if (state->sflags[i] & S_TRACK)
1052 ctrack++;
1053 impossible = S_E_DIRS(state, i%w, i/w, E_NOTRACK);
1054 if ((perpf & impossible) == 0)
1055 nperp++;
1056 if (S_E_COUNT(state, i%w, i/w, E_TRACK) <= 1) {
1057 n1edge++;
1058 i1edge = i;
1059 }
1060 }
1061 if (ctrack != (target-1)) return 0;
1062 if (nperp > 0 || n1edge != 1) return 0;
1063
1064 debug(("check_single from (%d,%d): 1 match from (%d,%d)",
1065 si%w, si/w, i1edge%w, i1edge/w));
1066
1067 /* We have a match: anything that's more than 1 away from this square
1068 cannot now contain a track. */
1069 ox = i1edge%w;
1070 oy = i1edge/w;
1071 for (j = 0, i = si; j < n; j++, i += id) {
1072 x = i%w;
1073 y = i/w;
1074 if (abs(ox-x) > 1 || abs(oy-y) > 1) {
1075 if (!(state->sflags[i] & S_TRACK))
1076 did += solve_set_sflag(state, x, y, S_NOTRACK, what);
1077 }
1078 }
1079
1080 return did;
1081}
1082
1083static int solve_check_single(game_state *state)
1084{
1085 int w = state->p.w, h = state->p.h, x, y, target, did = 0;
1086
1087 for (x = 0; x < w; x++) {
1088 target = state->numbers->numbers[x];
1089 did += solve_check_single_sub(state, x, w, h, target, R|L, "single on col");
1090 }
1091 for (y = 0; y < h; y++) {
1092 target = state->numbers->numbers[w+y];
1093 did += solve_check_single_sub(state, y*w, 1, w, target, U|D, "single on row");
1094 }
1095 return did;
1096}
1097
1098static int solve_check_loose_sub(game_state *state, int si, int id, int n,
1099 int target, unsigned int perpf,
1100 const char *what)
1101{
1102 int nperp = 0, nloose = 0, e2count = 0, did = 0, i, j, k;
1103 int w = state->p.w;
1104 unsigned int parf = ALLDIR & (~perpf);
1105
1106 for (j = 0, i = si; j < n; j++, i += id) {
1107 int fcount = S_E_COUNT(state, i%w, i/w, E_TRACK);
1108 if (fcount == 2)
1109 e2count++; /* this cell has 2 definite edges */
1110 state->sflags[i] &= ~S_MARK;
1111 if (fcount == 1 && (parf & S_E_DIRS(state, i%w, i/w, E_TRACK))) {
1112 nloose++; /* this cell has a loose end (single flag set parallel
1113 to the direction of this row/column) */
1114 state->sflags[i] |= S_MARK; /* mark loose ends */
1115 }
1116 if (fcount != 2 && !(perpf & S_E_DIRS(state, i%w, i/w, E_NOTRACK)))
1117 nperp++; /* we could lay perpendicular across this cell */
1118 }
1119
1120 if (nloose > (target - e2count)) {
1121 debug(("check %s from (%d,%d): more loose (%d) than empty (%d), IMPOSSIBLE",
1122 what, si%w, si/w, nloose, target-e2count));
1123 state->impossible = TRUE;
1124 }
1125 if (nloose > 0 && nloose == (target - e2count)) {
1126 debug(("check %s from (%d,%d): nloose = empty (%d), forcing loners out.",
1127 what, si%w, si/w, nloose));
1128 for (j = 0, i = si; j < n; j++, i += id) {
1129 if (!(state->sflags[i] & S_MARK))
1130 continue; /* skip non-loose ends */
1131 if (j > 0 && state->sflags[i-id] & S_MARK)
1132 continue; /* next to other loose end, could join up */
1133 if (j < (n-1) && state->sflags[i+id] & S_MARK)
1134 continue; /* ditto */
1135
1136 for (k = 0; k < 4; k++) {
1137 if ((parf & (1<<k)) &&
1138 !(S_E_DIRS(state, i%w, i/w, E_TRACK) & (1<<k))) {
1139 /* set as NOTRACK the edge parallel to the row/column that's
1140 not already set. */
1141 did += solve_set_eflag(state, i%w, i/w, 1<<k, E_NOTRACK, what);
1142 }
1143 }
1144 }
1145 }
1146 if (nloose == 1 && (target - e2count) == 2 && nperp == 0) {
1147 debug(("check %s from (%d,%d): 1 loose end, 2 empty squares, forcing parallel",
1148 what, si%w, si/w));
1149 for (j = 0, i = si; j < n; j++, i += id) {
1150 if (!(state->sflags[i] & S_MARK))
1151 continue; /* skip non-loose ends */
1152 for (k = 0; k < 4; k++) {
1153 if (parf & (1<<k))
1154 did += solve_set_eflag(state, i%w, i/w, 1<<k, E_TRACK, what);
1155 }
1156 }
1157 }
1158
1159 return did;
1160}
1161
1162static int solve_check_loose_ends(game_state *state)
1163{
1164 int w = state->p.w, h = state->p.h, x, y, target, did = 0;
1165
1166 for (x = 0; x < w; x++) {
1167 target = state->numbers->numbers[x];
1168 did += solve_check_loose_sub(state, x, w, h, target, R|L, "loose on col");
1169 }
1170 for (y = 0; y < h; y++) {
1171 target = state->numbers->numbers[w+y];
1172 did += solve_check_loose_sub(state, y*w, 1, w, target, U|D, "loose on row");
1173 }
1174 return did;
1175}
1176
1177static int solve_check_loop_sub(game_state *state, int x, int y, int dir,
1178 int *dsf, int startc, int endc)
1179{
1180 int w = state->p.w, h = state->p.h, i = y*w+x, j, k, satisfied = 1;
1181
1182 j = (y+DY(dir))*w + (x+DX(dir));
1183
1184 assert(i < w*h && j < w*h);
1185
1186 if ((state->sflags[i] & S_TRACK) &&
1187 (state->sflags[j] & S_TRACK) &&
1188 !(S_E_DIRS(state, x, y, E_TRACK) & dir) &&
1189 !(S_E_DIRS(state, x, y, E_NOTRACK) & dir)) {
1190 int ic = dsf_canonify(dsf, i), jc = dsf_canonify(dsf, j);
1191 if (ic == jc) {
1192 return solve_set_eflag(state, x, y, dir, E_NOTRACK, "would close loop");
1193 }
1194 if ((ic == startc && jc == endc) || (ic == endc && jc == startc)) {
1195 debug(("Adding link at (%d,%d) would join start to end", x, y));
1196 /* We mustn't join the start to the end if:
1197 - there are other bits of track that aren't attached to either end
1198 - the clues are not fully satisfied yet
1199 */
1200 for (k = 0; k < w*h; k++) {
1201 if (state->sflags[k] & S_TRACK &&
1202 dsf_canonify(dsf, k) != startc && dsf_canonify(dsf, k) != endc) {
1203 return solve_set_eflag(state, x, y, dir, E_NOTRACK,
1204 "joins start to end but misses tracks");
1205 }
1206 }
1207 for (k = 0; k < w; k++) {
1208 int target = state->numbers->numbers[k];
1209 int ntracks = solve_count_col(state, k, S_TRACK);
1210 if (ntracks < target) satisfied = 0;
1211 }
1212 for (k = 0; k < h; k++) {
1213 int target = state->numbers->numbers[w+k];
1214 int ntracks = solve_count_row(state, k, S_TRACK);
1215 if (ntracks < target) satisfied = 0;
1216 }
1217 if (!satisfied) {
1218 return solve_set_eflag(state, x, y, dir, E_NOTRACK,
1219 "joins start to end with incomplete clues");
1220 }
1221 }
1222 }
1223 return 0;
1224}
1225
1226static int solve_check_loop(game_state *state)
1227{
1228 int w = state->p.w, h = state->p.h, x, y, i, j, did = 0;
1229 int *dsf, startc, endc;
1230
1231 /* TODO eventually we should pull this out into a solver struct and keep it
1232 updated as we connect squares. For now we recreate it every time we try
1233 this particular solver step. */
1234 dsf = snewn(w*h, int);
1235 dsf_init(dsf, w*h);
1236
1237 /* Work out the connectedness of the current loop set. */
1238 for (x = 0; x < w; x++) {
1239 for (y = 0; y < h; y++) {
1240 i = y*w + x;
1241 if (x < (w-1) && S_E_DIRS(state, x, y, E_TRACK) & R) {
1242 /* connection to the right... */
1243 j = y*w + (x+1);
1244 assert(i < w*h && j < w*h);
1245 dsf_merge(dsf, i, j);
1246 }
1247 if (y < (h-1) && S_E_DIRS(state, x, y, E_TRACK) & D) {
1248 /* connection down... */
1249 j = (y+1)*w + x;
1250 assert(i < w*h && j < w*h);
1251 dsf_merge(dsf, i, j);
1252 }
1253 /* NB no need to check up and left because they'll have been checked
1254 by the other side. */
1255 }
1256 }
1257
1258 startc = dsf_canonify(dsf, state->numbers->row_s*w);
1259 endc = dsf_canonify(dsf, (h-1)*w+state->numbers->col_s);
1260
1261 /* Now look at all adjacent squares that are both S_TRACK: if connecting
1262 any of them would complete a loop (i.e. they're both the same dsf class
1263 already) then that edge must be NOTRACK. */
1264 for (x = 0; x < w; x++) {
1265 for (y = 0; y < h; y++) {
1266 if (x < (w-1))
1267 did += solve_check_loop_sub(state, x, y, R, dsf, startc, endc);
1268 if (y < (h-1))
1269 did += solve_check_loop_sub(state, x, y, D, dsf, startc, endc);
1270 }
1271 }
1272
1273 sfree(dsf);
1274
1275 return did;
1276}
1277
1278static void solve_discount_edge(game_state *state, int x, int y, int d)
1279{
1280 if (S_E_DIRS(state, x, y, E_TRACK) & d) {
1281 assert(state->sflags[y*state->p.w + x] & S_CLUE);
1282 return; /* (only) clue squares can have outer edges set. */
1283 }
1284 solve_set_eflag(state, x, y, d, E_NOTRACK, "outer edge");
1285}
1286
1287static int tracks_solve(game_state *state, int diff)
1288{
1289 int didsth, x, y, w = state->p.w, h = state->p.h;
1290
1291 debug(("solve..."));
1292 state->impossible = FALSE;
1293
1294 /* Set all the outer border edges as no-track. */
1295 for (x = 0; x < w; x++) {
1296 solve_discount_edge(state, x, 0, U);
1297 solve_discount_edge(state, x, h-1, D);
1298 }
1299 for (y = 0; y < h; y++) {
1300 solve_discount_edge(state, 0, y, L);
1301 solve_discount_edge(state, w-1, y, R);
1302 }
1303
1304 while (1) {
1305 didsth = 0;
1306
1307 didsth += solve_update_flags(state);
1308 didsth += solve_count_clues(state);
1309 didsth += solve_check_loop(state);
1310
1311 if (diff >= DIFF_TRICKY) {
1312 didsth += solve_check_single(state);
1313 didsth += solve_check_loose_ends(state);
1314 }
1315
1316 if (!didsth || state->impossible) break;
1317 }
1318
1319 return state->impossible ? -1 : check_completion(state, FALSE) ? 1 : 0;
1320}
1321
1322static char *move_string_diff(const game_state *before, const game_state *after, int issolve)
1323{
1324 int w = after->p.w, h = after->p.h, i, j;
1325 char *move = snewn(w*h*40, char), *p = move;
1326 const char *sep = "";
1327 unsigned int otf, ntf, onf, nnf;
1328
1329 if (issolve) {
1330 *p++ = 'S';
1331 sep = ";";
1332 }
1333 for (i = 0; i < w*h; i++) {
1334 otf = S_E_DIRS(before, i%w, i/w, E_TRACK);
1335 ntf = S_E_DIRS(after, i%w, i/w, E_TRACK);
1336 onf = S_E_DIRS(before, i%w, i/w, E_NOTRACK);
1337 nnf = S_E_DIRS(after, i%w, i/w, E_NOTRACK);
1338
1339 for (j = 0; j < 4; j++) {
1340 unsigned df = 1<<j;
1341 if ((otf & df) != (ntf & df)) {
1342 p += sprintf(p, "%s%c%c%d,%d", sep,
1343 (ntf & df) ? 'T' : 't', MOVECHAR(df), i%w, i/w);
1344 sep = ";";
1345 }
1346 if ((onf & df) != (nnf & df)) {
1347 p += sprintf(p, "%s%c%c%d,%d", sep,
1348 (nnf & df) ? 'N' : 'n', MOVECHAR(df), i%w, i/w);
1349 sep = ";";
1350 }
1351 }
1352
1353 if ((before->sflags[i] & S_NOTRACK) != (after->sflags[i] & S_NOTRACK)) {
1354 p += sprintf(p, "%s%cS%d,%d", sep,
1355 (after->sflags[i] & S_NOTRACK) ? 'N' : 'n', i%w, i/w);
1356 sep = ";";
1357 }
1358 if ((before->sflags[i] & S_TRACK) != (after->sflags[i] & S_TRACK)) {
1359 p += sprintf(p, "%s%cS%d,%d", sep,
1360 (after->sflags[i] & S_TRACK) ? 'T' : 't', i%w, i/w);
1361 sep = ";";
1362 }
1363 }
1364 *p++ = '\0';
1365 move = sresize(move, p - move, char);
1366
1367 return move;
1368}
1369
1370static char *solve_game(const game_state *state, const game_state *currstate,
1371 const char *aux, char **error)
1372{
1373 game_state *solved;
1374 int ret;
1375 char *move;
1376
1377 solved = dup_game(currstate);
1378 ret = tracks_solve(solved, DIFFCOUNT);
1379 if (ret < 1) {
1380 free_game(solved);
1381 solved = dup_game(state);
1382 ret = tracks_solve(solved, DIFFCOUNT);
1383 }
1384
1385 if (ret < 1) {
1386 *error = "Unable to find solution";
1387 move = NULL;
1388 } else {
1389 move = move_string_diff(currstate, solved, TRUE);
1390 }
1391
1392 free_game(solved);
1393 return move;
1394}
1395
1396static int game_can_format_as_text_now(const game_params *params)
1397{
1398 return TRUE;
1399}
1400
1401static char *game_text_format(const game_state *state)
1402{
1403 char *ret, *p;
1404 int x, y, len, w = state->p.w, h = state->p.h;
1405
1406 len = ((w*2) + 4) * ((h*2)+4) + 2;
1407 ret = snewn(len+1, char);
1408 p = ret;
1409
1410 /* top line: column clues */
1411 *p++ = ' ';
1412 *p++ = ' ';
1413 for (x = 0; x < w; x++) {
1414 *p++ = (state->numbers->numbers[x] < 10 ?
1415 '0' + state->numbers->numbers[x] :
1416 'A' + state->numbers->numbers[x] - 10);
1417 *p++ = ' ';
1418 }
1419 *p++ = '\n';
1420
1421 /* second line: top edge */
1422 *p++ = ' ';
1423 *p++ = '+';
1424 for (x = 0; x < w*2-1; x++)
1425 *p++ = '-';
1426 *p++ = '+';
1427 *p++ = '\n';
1428
1429 /* grid rows: one line of squares, one line of edges. */
1430 for (y = 0; y < h; y++) {
1431 /* grid square line */
1432 *p++ = (y == state->numbers->row_s) ? 'A' : ' ';
1433 *p++ = (y == state->numbers->row_s) ? '-' : '|';
1434
1435 for (x = 0; x < w; x++) {
1436 unsigned int f = S_E_DIRS(state, x, y, E_TRACK);
1437 if (state->sflags[y*w+x] & S_CLUE) *p++ = 'C';
1438 else if (f == LU || f == RD) *p++ = '/';
1439 else if (f == LD || f == RU) *p++ = '\\';
1440 else if (f == UD) *p++ = '|';
1441 else if (f == RL) *p++ = '-';
1442 else if (state->sflags[y*w+x] & S_NOTRACK) *p++ = 'x';
1443 else *p++ = ' ';
1444
1445 if (x < w-1) {
1446 *p++ = (f & R) ? '-' : ' ';
1447 } else
1448 *p++ = '|';
1449 }
1450 *p++ = (state->numbers->numbers[w+y] < 10 ?
1451 '0' + state->numbers->numbers[w+y] :
1452 'A' + state->numbers->numbers[w+y] - 10);
1453 *p++ = '\n';
1454
1455 if (y == h-1) continue;
1456
1457 /* edges line */
1458 *p++ = ' ';
1459 *p++ = '|';
1460 for (x = 0; x < w; x++) {
1461 unsigned int f = S_E_DIRS(state, x, y, E_TRACK);
1462 *p++ = (f & D) ? '|' : ' ';
1463 *p++ = (x < w-1) ? ' ' : '|';
1464 }
1465 *p++ = '\n';
1466 }
1467
1468 /* next line: bottom edge */
1469 *p++ = ' ';
1470 *p++ = '+';
1471 for (x = 0; x < w*2-1; x++)
1472 *p++ = (x == state->numbers->col_s*2) ? '|' : '-';
1473 *p++ = '+';
1474 *p++ = '\n';
1475
1476 /* final line: bottom clue */
1477 *p++ = ' ';
1478 *p++ = ' ';
1479 for (x = 0; x < w*2-1; x++)
1480 *p++ = (x == state->numbers->col_s*2) ? 'B' : ' ';
1481 *p++ = '\n';
1482
1483 *p = '\0';
1484 return ret;
1485}
1486
1487static void debug_state(game_state *state, const char *what) {
1488 char *sstring = game_text_format(state);
1489 debug(("%s: %s", what, sstring));
1490 sfree(sstring);
1491}
1492
1493static void dsf_update_completion(game_state *state, int ax, int ay,
1494 char dir, int *dsf)
1495{
1496 int w = state->p.w, ai = ay*w+ax, bx, by, bi;
1497
1498 if (!(S_E_DIRS(state, ax, ay, E_TRACK) & dir)) return;
1499 bx = ax + DX(dir);
1500 by = ay + DY(dir);
1501
1502 if (!INGRID(state, bx, by)) return;
1503 bi = by*w+bx;
1504
1505 dsf_merge(dsf, ai, bi);
1506}
1507
1508struct tracks_neighbour_ctx {
1509 game_state *state;
1510 int i, n, neighbours[4];
1511};
1512static int tracks_neighbour(int vertex, void *vctx)
1513{
1514 struct tracks_neighbour_ctx *ctx = (struct tracks_neighbour_ctx *)vctx;
1515 if (vertex >= 0) {
1516 game_state *state = ctx->state;
1517 int w = state->p.w, x = vertex % w, y = vertex / w;
1518 int dirs = S_E_DIRS(state, x, y, E_TRACK);
1519 int j;
1520
1521 ctx->i = ctx->n = 0;
1522
1523 for (j = 0; j < 4; j++) {
1524 int dir = 1<<j;
1525 if (dirs & dir) {
1526 int nx = x + DX(dir), ny = y + DY(dir);
1527 if (INGRID(state, nx, ny))
1528 ctx->neighbours[ctx->n++] = ny * w + nx;
1529 }
1530 }
1531 }
1532
1533 if (ctx->i < ctx->n)
1534 return ctx->neighbours[ctx->i++];
1535 else
1536 return -1;
1537}
1538
1539static int check_completion(game_state *state, int mark)
1540{
1541 int w = state->p.w, h = state->p.h, x, y, i, target, ret = TRUE;
1542 int ntrack, nnotrack, ntrackcomplete;
1543 int *dsf, pathclass;
1544 struct findloopstate *fls;
1545 struct tracks_neighbour_ctx ctx;
1546
1547 if (mark) {
1548 for (i = 0; i < w+h; i++) {
1549 state->num_errors[i] = 0;
1550 }
1551 for (i = 0; i < w*h; i++) {
1552 state->sflags[i] &= ~S_ERROR;
1553 if (S_E_COUNT(state, i%w, i/w, E_TRACK) > 0) {
1554 if (S_E_COUNT(state, i%w, i/w, E_TRACK) > 2) {
1555 ret = FALSE;
1556 state->sflags[i] |= S_ERROR;
1557 }
1558 }
1559 }
1560 }
1561
1562 /* A cell is 'complete', for the purposes of marking the game as
1563 * finished, if it has two edges marked as TRACK. But it only has
1564 * to have one edge marked as TRACK, or be filled in as trackful
1565 * without any specific edges known, to count towards checking
1566 * row/column clue errors. */
1567 for (x = 0; x < w; x++) {
1568 target = state->numbers->numbers[x];
1569 ntrack = nnotrack = ntrackcomplete = 0;
1570 for (y = 0; y < h; y++) {
1571 if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
1572 state->sflags[y*w+x] & S_TRACK)
1573 ntrack++;
1574 if (S_E_COUNT(state, x, y, E_TRACK) == 2)
1575 ntrackcomplete++;
1576 if (state->sflags[y*w+x] & S_NOTRACK)
1577 nnotrack++;
1578 }
1579 if (mark) {
1580 if (ntrack > target || nnotrack > (h-target)) {
1581 debug(("col %d error: target %d, track %d, notrack %d",
1582 x, target, ntrack, nnotrack));
1583 state->num_errors[x] = 1;
1584 ret = FALSE;
1585 }
1586 }
1587 if (ntrackcomplete != target)
1588 ret = FALSE;
1589 }
1590 for (y = 0; y < h; y++) {
1591 target = state->numbers->numbers[w+y];
1592 ntrack = nnotrack = ntrackcomplete = 0;
1593 for (x = 0; x < w; x++) {
1594 if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
1595 state->sflags[y*w+x] & S_TRACK)
1596 ntrack++;
1597 if (S_E_COUNT(state, x, y, E_TRACK) == 2)
1598 ntrackcomplete++;
1599 if (state->sflags[y*w+x] & S_NOTRACK)
1600 nnotrack++;
1601 }
1602 if (mark) {
1603 if (ntrack > target || nnotrack > (w-target)) {
1604 debug(("row %d error: target %d, track %d, notrack %d",
1605 y, target, ntrack, nnotrack));
1606 state->num_errors[w+y] = 1;
1607 ret = FALSE;
1608 }
1609 }
1610 if (ntrackcomplete != target)
1611 ret = FALSE;
1612 }
1613
1614 dsf = snewn(w*h, int);
1615 dsf_init(dsf, w*h);
1616
1617 for (x = 0; x < w; x++) {
1618 for (y = 0; y < h; y++) {
1619 dsf_update_completion(state, x, y, R, dsf);
1620 dsf_update_completion(state, x, y, D, dsf);
1621 }
1622 }
1623
1624 fls = findloop_new_state(w*h);
1625 ctx.state = state;
1626 if (findloop_run(fls, w*h, tracks_neighbour, &ctx)) {
1627 debug(("loop detected, not complete"));
1628 ret = FALSE; /* no loop allowed */
1629 if (mark) {
1630 for (x = 0; x < w; x++) {
1631 for (y = 0; y < h; y++) {
1632 int u, v;
1633
1634 u = y*w + x;
1635 for (v = tracks_neighbour(u, &ctx); v >= 0;
1636 v = tracks_neighbour(-1, &ctx))
1637 if (findloop_is_loop_edge(fls, u, v))
1638 state->sflags[y*w+x] |= S_ERROR;
1639 }
1640 }
1641 }
1642 }
1643 findloop_free_state(fls);
1644
1645 if (mark) {
1646 pathclass = dsf_canonify(dsf, state->numbers->row_s*w);
1647 if (pathclass == dsf_canonify(dsf, (h-1)*w + state->numbers->col_s)) {
1648 /* We have a continuous path between the entrance and the exit: any
1649 other path must be in error. */
1650 for (i = 0; i < w*h; i++) {
1651 if ((dsf_canonify(dsf, i) != pathclass) &&
1652 ((state->sflags[i] & S_TRACK) ||
1653 (S_E_COUNT(state, i%w, i/w, E_TRACK) > 0))) {
1654 ret = FALSE;
1655 state->sflags[i] |= S_ERROR;
1656 }
1657 }
1658 } else {
1659 /* If we _don't_ have such a path, then certainly the game
1660 * can't be in a winning state. So even if we're not
1661 * highlighting any _errors_, we certainly shouldn't
1662 * return true. */
1663 ret = FALSE;
1664 }
1665 }
1666
1667 if (mark)
1668 state->completed = ret;
1669 sfree(dsf);
1670 return ret;
1671}
1672
1673/* Code borrowed from Pearl. */
1674
1675struct game_ui {
1676 int dragging, clearing, notrack;
1677 int drag_sx, drag_sy, drag_ex, drag_ey; /* drag start and end grid coords */
1678 int clickx, clicky; /* pixel position of initial click */
1679
1680 int curx, cury; /* grid position of keyboard cursor; uses half-size grid */
1681 int cursor_active; /* TRUE iff cursor is shown */
1682};
1683
1684static game_ui *new_ui(const game_state *state)
1685{
1686 game_ui *ui = snew(game_ui);
1687
1688 ui->clearing = ui->notrack = ui->dragging = 0;
1689 ui->drag_sx = ui->drag_sy = ui->drag_ex = ui->drag_ey = -1;
1690 ui->cursor_active = FALSE;
1691 ui->curx = ui->cury = 1;
1692
1693 return ui;
1694}
1695
1696static void free_ui(game_ui *ui)
1697{
1698 sfree(ui);
1699}
1700
1701static char *encode_ui(const game_ui *ui)
1702{
1703 return NULL;
1704}
1705
1706static void decode_ui(game_ui *ui, const char *encoding)
1707{
1708}
1709
1710static void game_changed_state(game_ui *ui, const game_state *oldstate,
1711 const game_state *newstate)
1712{
1713}
1714
1715#define PREFERRED_TILE_SIZE 30
1716#define HALFSZ (ds->sz6*3)
1717#define THIRDSZ (ds->sz6*2)
1718#define TILE_SIZE (ds->sz6*6)
1719
1720#define BORDER (TILE_SIZE/8)
1721#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
1722
1723#define COORD(x) ( (x+1) * TILE_SIZE + BORDER )
1724#define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 )
1725#define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) - 1 )
1726
1727#define DS_DSHIFT 4 /* R/U/L/D shift, for drag-in-progress flags */
1728
1729#define DS_ERROR (1 << 8)
1730#define DS_CLUE (1 << 9)
1731#define DS_NOTRACK (1 << 10)
1732#define DS_FLASH (1 << 11)
1733#define DS_CURSOR (1 << 12) /* cursor in square (centre, or on edge) */
1734#define DS_TRACK (1 << 13)
1735#define DS_CLEARING (1 << 14)
1736
1737#define DS_NSHIFT 16 /* R/U/L/D shift, for no-track edge flags */
1738#define DS_CSHIFT 20 /* R/U/L/D shift, for cursor-on-edge */
1739
1740struct game_drawstate {
1741 int sz6;
1742 int started;
1743
1744 int w, h, sz;
1745 unsigned int *flags, *flags_drag;
1746 int *num_errors;
1747};
1748
1749static void update_ui_drag(const game_state *state, game_ui *ui, int gx, int gy)
1750{
1751 int w = state->p.w, h = state->p.h;
1752 int dx = abs(ui->drag_sx - gx), dy = abs(ui->drag_sy - gy);
1753
1754 if (dy == 0) {
1755 ui->drag_ex = gx < 0 ? 0 : gx >= w ? w-1 : gx;
1756 ui->drag_ey = ui->drag_sy;
1757 ui->dragging = TRUE;
1758 } else if (dx == 0) {
1759 ui->drag_ex = ui->drag_sx;
1760 ui->drag_ey = gy < 0 ? 0 : gy >= h ? h-1 : gy;
1761 ui->dragging = TRUE;
1762 } else {
1763 ui->drag_ex = ui->drag_sx;
1764 ui->drag_ey = ui->drag_sy;
1765 ui->dragging = FALSE;
1766 }
1767}
1768
1769static int ui_can_flip_edge(const game_state *state, int x, int y, int dir,
1770 int notrack)
1771{
1772 int w = state->p.w /*, h = state->shared->h, sz = state->shared->sz */;
1773 int x2 = x + DX(dir);
1774 int y2 = y + DY(dir);
1775 unsigned int sf1, sf2, ef;
1776
1777 if (!INGRID(state, x, y) || !INGRID(state, x2, y2))
1778 return FALSE;
1779
1780 sf1 = state->sflags[y*w + x];
1781 sf2 = state->sflags[y2*w + x2];
1782 if ( !notrack && ((sf1 & S_CLUE) || (sf2 & S_CLUE)) )
1783 return FALSE;
1784
1785 ef = S_E_FLAGS(state, x, y, dir);
1786 if (notrack) {
1787 /* if we're going to _set_ NOTRACK (i.e. the flag is currently unset),
1788 make sure the edge is not already set to TRACK. The adjacent squares
1789 could be set to TRACK, because we don't know which edges the general
1790 square setting refers to. */
1791 if (!(ef & E_NOTRACK) && (ef & E_TRACK))
1792 return FALSE;
1793 } else {
1794 if (!(ef & E_TRACK)) {
1795 /* if we're going to _set_ TRACK, make sure neither adjacent square nor
1796 the edge itself is already set to NOTRACK. */
1797 if ((sf1 & S_NOTRACK) || (sf2 & S_NOTRACK) || (ef & E_NOTRACK))
1798 return FALSE;
1799 /* if we're going to _set_ TRACK, make sure neither adjacent square has
1800 2 track flags already. */
1801 if ((S_E_COUNT(state, x, y, E_TRACK) >= 2) ||
1802 (S_E_COUNT(state, x2, y2, E_TRACK) >= 2))
1803 return FALSE;
1804 }
1805 }
1806 return TRUE;
1807}
1808
1809static int ui_can_flip_square(const game_state *state, int x, int y, int notrack)
1810{
1811 int w = state->p.w, trackc;
1812 unsigned sf;
1813
1814 if (!INGRID(state, x, y)) return FALSE;
1815 sf = state->sflags[y*w+x];
1816 trackc = S_E_COUNT(state, x, y, E_TRACK);
1817
1818 if (sf & S_CLUE) return FALSE;
1819
1820 if (notrack) {
1821 /* If we're setting S_NOTRACK, we cannot have either S_TRACK or any E_TRACK. */
1822 if (!(sf & S_NOTRACK) && ((sf & S_TRACK) || (trackc > 0)))
1823 return FALSE;
1824 } else {
1825 /* If we're setting S_TRACK, we cannot have any S_NOTRACK (we could have
1826 E_NOTRACK, though, because one or two wouldn't rule out a track) */
1827 if (!(sf & S_TRACK) && (sf & S_NOTRACK))
1828 return FALSE;
1829 }
1830 return TRUE;
1831}
1832
1833static char *edge_flip_str(const game_state *state, int x, int y, int dir, int notrack, char *buf) {
1834 unsigned ef = S_E_FLAGS(state, x, y, dir);
1835 char c;
1836
1837 if (notrack)
1838 c = (ef & E_NOTRACK) ? 'n' : 'N';
1839 else
1840 c = (ef & E_TRACK) ? 't' : 'T';
1841
1842 sprintf(buf, "%c%c%d,%d", c, MOVECHAR(dir), x, y);
1843 return dupstr(buf);
1844}
1845
1846static char *square_flip_str(const game_state *state, int x, int y, int notrack, char *buf) {
1847 unsigned f = state->sflags[y*state->p.w+x];
1848 char c;
1849
1850 if (notrack)
1851 c = (f & E_NOTRACK) ? 'n' : 'N';
1852 else
1853 c = (f & E_TRACK) ? 't' : 'T';
1854
1855 sprintf(buf, "%cS%d,%d", c, x, y);
1856 return dupstr(buf);
1857}
1858
1859#define SIGN(x) ((x<0) ? -1 : (x>0))
1860
1861static game_state *copy_and_apply_drag(const game_state *state, const game_ui *ui)
1862{
1863 game_state *after = dup_game(state);
1864 int x1, y1, x2, y2, x, y, w = state->p.w;
1865 unsigned f = ui->notrack ? S_NOTRACK : S_TRACK, ff;
1866
1867 x1 = min(ui->drag_sx, ui->drag_ex); x2 = max(ui->drag_sx, ui->drag_ex);
1868 y1 = min(ui->drag_sy, ui->drag_ey); y2 = max(ui->drag_sy, ui->drag_ey);
1869
1870 /* actually either x1 == x2, or y1 == y2, but it's easier just to code
1871 the nested loop. */
1872 for (x = x1; x <= x2; x++) {
1873 for (y = y1; y <= y2; y++) {
1874 ff = state->sflags[y*w+x];
1875 if (ui->clearing && !(ff & f))
1876 continue; /* nothing to do, clearing and already clear */
1877 else if (!ui->clearing && (ff & f))
1878 continue; /* nothing to do, setting and already set */
1879 else if (ui_can_flip_square(state, x, y, ui->notrack))
1880 after->sflags[y*w+x] ^= f;
1881 }
1882 }
1883 return after;
1884}
1885
1886#define KEY_DIRECTION(btn) (\
1887 (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\
1888 (btn) == CURSOR_LEFT ? L : R)
1889
1890static char *interpret_move(const game_state *state, game_ui *ui,
1891 const game_drawstate *ds,
1892 int x, int y, int button)
1893{
1894 int w = state->p.w, h = state->p.h, direction;
1895 int gx = FROMCOORD(x), gy = FROMCOORD(y);
1896 char tmpbuf[80];
1897
1898 /* --- mouse operations --- */
1899
1900 if (IS_MOUSE_DOWN(button)) {
1901 ui->cursor_active = FALSE;
1902 ui->dragging = FALSE;
1903
1904 if (!INGRID(state, gx, gy)) {
1905 /* can't drag from off grid */
1906 return NULL;
1907 }
1908
1909 if (button == RIGHT_BUTTON) {
1910 ui->notrack = TRUE;
1911 ui->clearing = state->sflags[gy*w+gx] & S_NOTRACK;
1912 } else {
1913 ui->notrack = FALSE;
1914 ui->clearing = state->sflags[gy*w+gx] & S_TRACK;
1915 }
1916
1917 ui->clickx = x;
1918 ui->clicky = y;
1919 ui->drag_sx = ui->drag_ex = gx;
1920 ui->drag_sy = ui->drag_ey = gy;
1921
1922 return "";
1923 }
1924
1925 if (IS_MOUSE_DRAG(button)) {
1926 ui->cursor_active = FALSE;
1927 update_ui_drag(state, ui, gx, gy);
1928 return "";
1929 }
1930
1931 if (IS_MOUSE_RELEASE(button)) {
1932 ui->cursor_active = FALSE;
1933 if (ui->dragging &&
1934 (ui->drag_sx != ui->drag_ex || ui->drag_sy != ui->drag_ey)) {
1935 game_state *dragged = copy_and_apply_drag(state, ui);
1936 char *ret = move_string_diff(state, dragged, FALSE);
1937
1938 ui->dragging = 0;
1939 free_game(dragged);
1940
1941 return ret;
1942 } else {
1943 int cx, cy;
1944
1945 /* We might still have been dragging (and just done a one-
1946 * square drag): cancel drag, so undo doesn't make it like
1947 * a drag-in-progress. */
1948 ui->dragging = 0;
1949
1950 /* Click (or tiny drag). Work out which edge we were
1951 * closest to. */
1952
1953 /*
1954 * We process clicks based on the mouse-down location,
1955 * because that's more natural for a user to carefully
1956 * control than the mouse-up.
1957 */
1958 x = ui->clickx;
1959 y = ui->clicky;
1960
1961 cx = CENTERED_COORD(gx);
1962 cy = CENTERED_COORD(gy);
1963
1964 if (!INGRID(state, gx, gy) || FROMCOORD(x) != gx || FROMCOORD(y) != gy)
1965 return "";
1966
1967 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
1968 if (ui_can_flip_square(state, gx, gy, button == RIGHT_RELEASE))
1969 return square_flip_str(state, gx, gy, button == RIGHT_RELEASE, tmpbuf);
1970 return "";
1971 } else {
1972 if (abs(x-cx) < abs(y-cy)) {
1973 /* Closest to top/bottom edge. */
1974 direction = (y < cy) ? U : D;
1975 } else {
1976 /* Closest to left/right edge. */
1977 direction = (x < cx) ? L : R;
1978 }
1979 if (ui_can_flip_edge(state, gx, gy, direction,
1980 button == RIGHT_RELEASE))
1981 return edge_flip_str(state, gx, gy, direction,
1982 button == RIGHT_RELEASE, tmpbuf);
1983 else
1984 return "";
1985 }
1986 }
1987 }
1988
1989 /* --- cursor/keyboard operations --- */
1990
1991 if (IS_CURSOR_MOVE(button)) {
1992 int dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0);
1993 int dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0);
1994
1995 if (!ui->cursor_active) {
1996 ui->cursor_active = TRUE;
1997 return "";
1998 }
1999
2000 ui->curx = ui->curx + dx;
2001 ui->cury = ui->cury + dy;
2002 if ((ui->curx % 2 == 0) && (ui->cury % 2 == 0)) {
2003 /* disallow cursor on square corners: centres and edges only */
2004 ui->curx += dx; ui->cury += dy;
2005 }
2006 ui->curx = min(max(ui->curx, 1), 2*w-1);
2007 ui->cury = min(max(ui->cury, 1), 2*h-1);
2008 return "";
2009 }
2010
2011 if (IS_CURSOR_SELECT(button)) {
2012 if (!ui->cursor_active) {
2013 ui->cursor_active = TRUE;
2014 return "";
2015 }
2016 /* click on square corner does nothing (shouldn't get here) */
2017 if ((ui->curx % 2) == 0 && (ui->cury % 2 == 0))
2018 return "";
2019
2020 gx = ui->curx / 2;
2021 gy = ui->cury / 2;
2022 direction = ((ui->curx % 2) == 0) ? L : ((ui->cury % 2) == 0) ? U : 0;
2023
2024 if (direction &&
2025 ui_can_flip_edge(state, gx, gy, direction, button == CURSOR_SELECT2))
2026 return edge_flip_str(state, gx, gy, direction, button == CURSOR_SELECT2, tmpbuf);
2027 else if (!direction &&
2028 ui_can_flip_square(state, gx, gy, button == CURSOR_SELECT2))
2029 return square_flip_str(state, gx, gy, button == CURSOR_SELECT2, tmpbuf);
2030 return "";
2031 }
2032
2033#if 0
2034 /* helps to debug the solver */
2035 if (button == 'H' || button == 'h')
2036 return dupstr("H");
2037#endif
2038
2039 return NULL;
2040}
2041
2042static game_state *execute_move(const game_state *state, const char *move)
2043{
2044 int w = state->p.w, x, y, n, i;
2045 char c, d;
2046 unsigned f;
2047 game_state *ret = dup_game(state);
2048
2049 /* this is breaking the bank on GTK, which vsprintf's into a fixed-size buffer
2050 * which is 4096 bytes long. vsnprintf needs a feature-test macro to use, faff. */
2051 /*debug(("move: %s\n", move));*/
2052
2053 while (*move) {
2054 c = *move;
2055 if (c == 'S') {
2056 ret->used_solve = TRUE;
2057 move++;
2058 } else if (c == 'T' || c == 't' || c == 'N' || c == 'n') {
2059 /* set track, clear track; set notrack, clear notrack */
2060 move++;
2061 if (sscanf(move, "%c%d,%d%n", &d, &x, &y, &n) != 3)
2062 goto badmove;
2063 if (!INGRID(state, x, y)) goto badmove;
2064
2065 f = (c == 'T' || c == 't') ? S_TRACK : S_NOTRACK;
2066
2067 if (d == 'S') {
2068 if (c == 'T' || c == 'N')
2069 ret->sflags[y*w+x] |= f;
2070 else
2071 ret->sflags[y*w+x] &= ~f;
2072 } else if (d == 'U' || d == 'D' || d == 'L' || d == 'R') {
2073 for (i = 0; i < 4; i++) {
2074 unsigned df = 1<<i;
2075
2076 if (MOVECHAR(df) == d) {
2077 if (c == 'T' || c == 'N')
2078 S_E_SET(ret, x, y, df, f);
2079 else
2080 S_E_CLEAR(ret, x, y, df, f);
2081 }
2082 }
2083 } else
2084 goto badmove;
2085 move += n;
2086 } else if (c == 'H') {
2087 tracks_solve(ret, DIFFCOUNT);
2088 move++;
2089 } else {
2090 goto badmove;
2091 }
2092 if (*move == ';')
2093 move++;
2094 else if (*move)
2095 goto badmove;
2096 }
2097
2098 check_completion(ret, TRUE);
2099
2100 return ret;
2101
2102 badmove:
2103 free_game(ret);
2104 return NULL;
2105}
2106
2107/* ----------------------------------------------------------------------
2108 * Drawing routines.
2109 */
2110
2111#define FLASH_TIME 0.5F
2112
2113static void game_compute_size(const game_params *params, int tilesize,
2114 int *x, int *y)
2115{
2116 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2117 struct {
2118 int sz6;
2119 } ads, *ds = &ads;
2120 ads.sz6 = tilesize/6;
2121
2122 *x = (params->w+2) * TILE_SIZE + 2 * BORDER;
2123 *y = (params->h+2) * TILE_SIZE + 2 * BORDER;
2124}
2125
2126static void game_set_size(drawing *dr, game_drawstate *ds,
2127 const game_params *params, int tilesize)
2128{
2129 ds->sz6 = tilesize/6;
2130}
2131
2132enum {
2133 COL_BACKGROUND, COL_LOWLIGHT, COL_HIGHLIGHT,
2134 COL_TRACK_BACKGROUND = COL_LOWLIGHT,
2135 COL_GRID, COL_CLUE, COL_CURSOR,
2136 COL_TRACK, COL_TRACK_CLUE, COL_SLEEPER,
2137 COL_DRAGON, COL_DRAGOFF,
2138 COL_ERROR, COL_FLASH,
2139 NCOLOURS
2140};
2141
2142static float *game_colours(frontend *fe, int *ncolours)
2143{
2144 float *ret = snewn(3 * NCOLOURS, float);
2145 int i;
2146
2147 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
2148
2149 for (i = 0; i < 3; i++) {
2150 ret[COL_TRACK_CLUE * 3 + i] = 0.0F;
2151 ret[COL_TRACK * 3 + i] = 0.5F;
2152 ret[COL_CLUE * 3 + i] = 0.0F;
2153 ret[COL_GRID * 3 + i] = 0.75F;
2154 ret[COL_CURSOR * 3 + i] = 0.6F;
2155 }
2156
2157 ret[COL_SLEEPER * 3 + 0] = 0.5F;
2158 ret[COL_SLEEPER * 3 + 1] = 0.4F;
2159 ret[COL_SLEEPER * 3 + 2] = 0.1F;
2160
2161 ret[COL_ERROR * 3 + 0] = 1.0F;
2162 ret[COL_ERROR * 3 + 1] = 0.0F;
2163 ret[COL_ERROR * 3 + 2] = 0.0F;
2164
2165 ret[COL_DRAGON * 3 + 0] = 0.0F;
2166 ret[COL_DRAGON * 3 + 1] = 0.0F;
2167 ret[COL_DRAGON * 3 + 2] = 1.0F;
2168
2169 ret[COL_DRAGOFF * 3 + 0] = 0.8F;
2170 ret[COL_DRAGOFF * 3 + 1] = 0.8F;
2171 ret[COL_DRAGOFF * 3 + 2] = 1.0F;
2172
2173 ret[COL_FLASH * 3 + 0] = 1.0F;
2174 ret[COL_FLASH * 3 + 1] = 1.0F;
2175 ret[COL_FLASH * 3 + 2] = 1.0F;
2176
2177 *ncolours = NCOLOURS;
2178 return ret;
2179}
2180
2181static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2182{
2183 struct game_drawstate *ds = snew(struct game_drawstate);
2184 int i;
2185
2186 ds->sz6 = 0;
2187 ds->started = FALSE;
2188
2189 ds->w = state->p.w;
2190 ds->h = state->p.h;
2191 ds->sz = ds->w*ds->h;
2192 ds->flags = snewn(ds->sz, unsigned int);
2193 ds->flags_drag = snewn(ds->sz, unsigned int);
2194 for (i = 0; i < ds->sz; i++)
2195 ds->flags[i] = ds->flags_drag[i] = 0;
2196
2197 ds->num_errors = snewn(ds->w+ds->h, int);
2198 for (i = 0; i < ds->w+ds->h; i++)
2199 ds->num_errors[i] = 0;
2200
2201 return ds;
2202}
2203
2204static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2205{
2206 sfree(ds->flags);
2207 sfree(ds->flags_drag);
2208 sfree(ds->num_errors);
2209 sfree(ds);
2210}
2211
2212static void draw_circle_sleepers(drawing *dr, game_drawstate *ds,
2213 float cx, float cy, float r2, float thickness, int c)
2214{
2215 float qr6 = (float)PI/12, qr3 = (float)PI/6, th, x1, y1, x2, y2;
2216 float t6 = THIRDSZ/2.0F, r1 = t6;
2217 int i;
2218
2219 for (i = 0; i < 12; i++) {
2220 th = qr6 + (i*qr3);
2221 x1 = r1*(float)cos(th);
2222 x2 = r2*(float)cos(th);
2223 y1 = r1*(float)sin(th);
2224 y2 = r2*(float)sin(th);
2225 draw_thick_line(dr, thickness, cx+x1, cy+y1, cx+x2, cy+y2, c);
2226 }
2227}
2228
2229static void draw_thick_circle_outline(drawing *dr, float thickness,
2230 float cx, float cy, float r,
2231 int colour)
2232{
2233 float circ4 = 0.5F * (float)PI * r, ang, x1, y1, x2, y2;
2234 int i, nseg;
2235
2236 nseg = (int)(circ4 / 4.0F)*4; /* ensure a quarter-circle has a whole #segs */
2237 ang = 2.0F*(float)PI / nseg;
2238
2239 for (i = 0; i < nseg; i++) {
2240 float th = ang * i, th2 = ang * (i+1);
2241 x1 = cx + r*(float)cos(th);
2242 x2 = cx + r*(float)cos(th2);
2243 y1 = cy + r*(float)sin(th);
2244 y2 = cy + r*(float)sin(th2);
2245 debug(("circ outline: x=%.2f -> %.2f, thick=%.2f", x1, x2, thickness));
2246 draw_thick_line(dr, thickness, x1, y1, x2, y2, colour);
2247 }
2248}
2249
2250static void draw_tracks_specific(drawing *dr, game_drawstate *ds,
2251 int x, int y, unsigned int flags,
2252 int ctrack, int csleeper)
2253{
2254 float ox = (float)COORD(x), oy = (float)COORD(y), cx, cy;
2255 float t1 = (float)TILE_SIZE, t3 = TILE_SIZE/3.0F, t6 = TILE_SIZE/6.0F;
2256 int d, i;
2257 float thick_track = TILE_SIZE/8.0F, thick_sleeper = TILE_SIZE/12.0F;
2258
2259 if (flags == LR) {
2260 for (i = 1; i <= 7; i+=2) {
2261 cx = ox + TILE_SIZE/8.0F*i;
2262 draw_thick_line(dr, thick_sleeper,
2263 cx, oy+t6, cx, oy+t6+2*t3, csleeper);
2264 }
2265 draw_thick_line(dr, thick_track, ox, oy + t3, ox + TILE_SIZE, oy + t3, ctrack);
2266 draw_thick_line(dr, thick_track, ox, oy + 2*t3, ox + TILE_SIZE, oy + 2*t3, ctrack);
2267 return;
2268 }
2269 if (flags == UD) {
2270 for (i = 1; i <= 7; i+=2) {
2271 cy = oy + TILE_SIZE/8.0F*i;
2272 draw_thick_line(dr, thick_sleeper,
2273 ox+t6, cy, ox+t6+2*t3, cy, csleeper);
2274 }
2275 debug(("vert line: x=%.2f, thick=%.2f", ox + t3, thick_track));
2276 draw_thick_line(dr, thick_track, ox + t3, oy, ox + t3, oy + TILE_SIZE, ctrack);
2277 draw_thick_line(dr, thick_track, ox + 2*t3, oy, ox + 2*t3, oy + TILE_SIZE, ctrack);
2278 return;
2279 }
2280 if (flags == UL || flags == DL || flags == UR || flags == DR) {
2281 cx = (flags & L) ? ox : ox + TILE_SIZE;
2282 cy = (flags & U) ? oy : oy + TILE_SIZE;
2283
2284 draw_circle_sleepers(dr, ds, cx, cy, (float)(5*t6), thick_sleeper, csleeper);
2285
2286 draw_thick_circle_outline(dr, thick_track, (float)cx, (float)cy,
2287 2*t3, ctrack);
2288 draw_thick_circle_outline(dr, thick_track, (float)cx, (float)cy,
2289 t3, ctrack);
2290
2291 return;
2292 }
2293
2294 for (d = 1; d < 16; d *= 2) {
2295 float ox1 = 0, ox2 = 0, oy1 = 0, oy2 = 0;
2296
2297 if (!(flags & d)) continue;
2298
2299 for (i = 1; i <= 2; i++) {
2300 if (d == L) {
2301 ox1 = 0;
2302 ox2 = thick_track;
2303 oy1 = oy2 = i*t3;
2304 } else if (d == R) {
2305 ox1 = t1;
2306 ox2 = t1 - thick_track;
2307 oy1 = oy2 = i*t3;
2308 } else if (d == U) {
2309 ox1 = ox2 = i*t3;
2310 oy1 = 0;
2311 oy2 = thick_track;
2312 } else if (d == D) {
2313 ox1 = ox2 = i*t3;
2314 oy1 = t1;
2315 oy2 = t1 - thick_track;
2316 }
2317 draw_thick_line(dr, thick_track, ox+ox1, oy+oy1, ox+ox2, oy+oy2, ctrack);
2318 }
2319 }
2320}
2321
2322static unsigned int best_bits(unsigned int flags, unsigned int flags_drag, int *col)
2323{
2324 int nb_orig = nbits[flags & ALLDIR], nb_drag = nbits[flags_drag & ALLDIR];
2325
2326 if (nb_orig > nb_drag) {
2327 *col = COL_DRAGOFF;
2328 return flags & ALLDIR;
2329 } else if (nb_orig < nb_drag) {
2330 *col = COL_DRAGON;
2331 return flags_drag & ALLDIR;
2332 }
2333 return flags & ALLDIR; /* same number of bits: no special colour. */
2334}
2335
2336static void draw_square(drawing *dr, game_drawstate *ds,
2337 int x, int y, unsigned int flags, unsigned int flags_drag)
2338{
2339 int t2 = HALFSZ, t16 = HALFSZ/4, off;
2340 int ox = COORD(x), oy = COORD(y), cx = ox + t2, cy = oy + t2, d, c;
2341 int bg = (flags & DS_TRACK) ? COL_TRACK_BACKGROUND : COL_BACKGROUND;
2342 unsigned int flags_best;
2343
2344 assert(dr);
2345
2346 /* Clip to the grid square. */
2347 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2348
2349 /* Clear the square. */
2350 best_bits((flags & DS_TRACK) == DS_TRACK,
2351 (flags_drag & DS_TRACK) == DS_TRACK, &bg);
2352 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, bg);
2353
2354 /* Draw outline of grid square */
2355 draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID);
2356 draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID);
2357
2358 /* More outlines for clue squares. */
2359 if (flags & DS_CURSOR) {
2360 int curx, cury, curw, curh;
2361
2362 off = t16;
2363 curx = ox + off; cury = oy + off;
2364 curw = curh = TILE_SIZE - (2*off) + 1;
2365
2366 if (flags & (U << DS_CSHIFT)) {
2367 cury = oy - off; curh = 2*off + 1;
2368 } else if (flags & (D << DS_CSHIFT)) {
2369 cury = oy + TILE_SIZE - off; curh = 2*off + 1;
2370 } else if (flags & (L << DS_CSHIFT)) {
2371 curx = ox - off; curw = 2*off + 1;
2372 } else if (flags & (R << DS_CSHIFT)) {
2373 curx = ox + TILE_SIZE - off; curw = 2*off + 1;
2374 }
2375
2376 draw_rect_outline(dr, curx, cury, curw, curh, COL_GRID);
2377 }
2378
2379 /* Draw tracks themselves */
2380 c = (flags & DS_ERROR) ? COL_ERROR :
2381 (flags & DS_FLASH) ? COL_FLASH :
2382 (flags & DS_CLUE) ? COL_TRACK_CLUE : COL_TRACK;
2383 flags_best = best_bits(flags, flags_drag, &c);
2384 draw_tracks_specific(dr, ds, x, y, flags_best, c, COL_SLEEPER);
2385
2386 /* Draw no-track marks, if present, in square and on edges. */
2387 c = COL_TRACK;
2388 flags_best = best_bits((flags & DS_NOTRACK) == DS_NOTRACK,
2389 (flags_drag & DS_NOTRACK) == DS_NOTRACK, &c);
2390 if (flags_best) {
2391 off = HALFSZ/2;
2392 draw_line(dr, cx - off, cy - off, cx + off, cy + off, c);
2393 draw_line(dr, cx - off, cy + off, cx + off, cy - off, c);
2394 }
2395
2396 c = COL_TRACK;
2397 flags_best = best_bits(flags >> DS_NSHIFT, flags_drag >> DS_NSHIFT, &c);
2398 for (d = 1; d < 16; d *= 2) {
2399 off = t16;
2400 cx = ox + t2;
2401 cy = oy + t2;
2402
2403 if (flags_best & d) {
2404 cx += (d == R) ? t2 : (d == L) ? -t2 : 0;
2405 cy += (d == D) ? t2 : (d == U) ? -t2 : 0;
2406
2407 draw_line(dr, cx - off, cy - off, cx + off, cy + off, c);
2408 draw_line(dr, cx - off, cy + off, cx + off, cy - off, c);
2409 }
2410 }
2411
2412 unclip(dr);
2413 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2414}
2415
2416static void draw_clue(drawing *dr, game_drawstate *ds, int w, int clue, int i, int col)
2417{
2418 int cx, cy, tsz = TILE_SIZE/2;
2419 char buf[20];
2420
2421 if (i < w) {
2422 cx = CENTERED_COORD(i);
2423 cy = CENTERED_COORD(-1);
2424 } else {
2425 cx = CENTERED_COORD(w);
2426 cy = CENTERED_COORD(i-w);
2427 }
2428
2429 draw_rect(dr, cx - tsz + BORDER, cy - tsz + BORDER,
2430 TILE_SIZE - BORDER, TILE_SIZE - BORDER, COL_BACKGROUND);
2431 sprintf(buf, "%d", clue);
2432 draw_text(dr, cx, cy, FONT_VARIABLE, tsz, ALIGN_VCENTRE|ALIGN_HCENTRE,
2433 col, buf);
2434 draw_update(dr, cx - tsz, cy - tsz, TILE_SIZE, TILE_SIZE);
2435}
2436
2437static void draw_loop_ends(drawing *dr, game_drawstate *ds,
2438 const game_state *state, int c)
2439{
2440 int tsz = TILE_SIZE/2;
2441
2442 draw_text(dr, CENTERED_COORD(-1), CENTERED_COORD(state->numbers->row_s),
2443 FONT_VARIABLE, tsz, ALIGN_VCENTRE|ALIGN_HCENTRE,
2444 c, "A");
2445
2446 draw_text(dr, CENTERED_COORD(state->numbers->col_s), CENTERED_COORD(state->p.h),
2447 FONT_VARIABLE, tsz, ALIGN_VCENTRE|ALIGN_HCENTRE,
2448 c, "B");
2449}
2450
2451static unsigned int s2d_flags(const game_state *state, int x, int y, const game_ui *ui)
2452{
2453 unsigned int f;
2454 int w = state->p.w;
2455
2456 f = S_E_DIRS(state, x, y, E_TRACK);
2457 f |= (S_E_DIRS(state, x, y, E_NOTRACK) << DS_NSHIFT);
2458
2459 if (state->sflags[y*w+x] & S_ERROR)
2460 f |= DS_ERROR;
2461 if (state->sflags[y*w+x] & S_CLUE)
2462 f |= DS_CLUE;
2463 if (state->sflags[y*w+x] & S_NOTRACK)
2464 f |= DS_NOTRACK;
2465 if ((state->sflags[y*w+x] & S_TRACK) || (S_E_COUNT(state, x, y, E_TRACK) > 0))
2466 f |= DS_TRACK;
2467
2468 if (ui->cursor_active) {
2469 if (ui->curx >= x*2 && ui->curx <= (x+1)*2 &&
2470 ui->cury >= y*2 && ui->cury <= (y+1)*2) {
2471 f |= DS_CURSOR;
2472 if (ui->curx == x*2) f |= (L << DS_CSHIFT);
2473 if (ui->curx == (x+1)*2) f |= (R << DS_CSHIFT);
2474 if (ui->cury == y*2) f |= (U << DS_CSHIFT);
2475 if (ui->cury == (y+1)*2) f |= (D << DS_CSHIFT);
2476 }
2477 }
2478
2479 return f;
2480}
2481
2482static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldstate,
2483 const game_state *state, int dir, const game_ui *ui,
2484 float animtime, float flashtime)
2485{
2486 int i, x, y, force = 0, flashing = 0, w = ds->w, h = ds->h;
2487 game_state *drag_state = NULL;
2488
2489 if (!ds->started) {
2490 /*
2491 * The initial contents of the window are not guaranteed and
2492 * can vary with front ends. To be on the safe side, all games
2493 * should start by drawing a big background-colour rectangle
2494 * covering the whole window.
2495 */
2496 draw_rect(dr, 0, 0, (w+2)*TILE_SIZE + 2*BORDER, (h+2)*TILE_SIZE + 2*BORDER,
2497 COL_BACKGROUND);
2498
2499 draw_loop_ends(dr, ds, state, COL_CLUE);
2500
2501 draw_line(dr, COORD(ds->w), COORD(0), COORD(ds->w), COORD(ds->h), COL_GRID);
2502 draw_line(dr, COORD(0), COORD(ds->h), COORD(ds->w), COORD(ds->h), COL_GRID);
2503
2504 draw_update(dr, 0, 0, (w+2)*TILE_SIZE + 2*BORDER, (h+2)*TILE_SIZE + 2*BORDER);
2505
2506 ds->started = TRUE;
2507 force = 1;
2508 }
2509
2510 for (i = 0; i < w+h; i++) {
2511 if (force || (state->num_errors[i] != ds->num_errors[i])) {
2512 ds->num_errors[i] = state->num_errors[i];
2513 draw_clue(dr, ds, w, state->numbers->numbers[i], i,
2514 ds->num_errors[i] ? COL_ERROR : COL_CLUE);
2515 }
2516 }
2517
2518 if (flashtime > 0 &&
2519 (flashtime <= FLASH_TIME/3 ||
2520 flashtime >= FLASH_TIME*2/3))
2521 flashing = DS_FLASH;
2522
2523 if (ui->dragging)
2524 drag_state = copy_and_apply_drag(state, ui);
2525
2526 for (x = 0; x < w; x++) {
2527 for (y = 0; y < h; y++) {
2528 unsigned int f, f_d;
2529
2530 f = s2d_flags(state, x, y, ui) | flashing;
2531 f_d = drag_state ? s2d_flags(drag_state, x, y, ui) : f;
2532
2533 if (f != ds->flags[y*w+x] || f_d != ds->flags_drag[y*w+x] || force) {
2534 ds->flags[y*w+x] = f;
2535 ds->flags_drag[y*w+x] = f_d;
2536 draw_square(dr, ds, x, y, f, f_d);
2537 }
2538 }
2539 }
2540
2541 if (drag_state) free_game(drag_state);
2542}
2543
2544static float game_anim_length(const game_state *oldstate, const game_state *newstate,
2545 int dir, game_ui *ui)
2546{
2547 return 0.0F;
2548}
2549
2550static float game_flash_length(const game_state *oldstate, const game_state *newstate,
2551 int dir, game_ui *ui)
2552{
2553 if (!oldstate->completed &&
2554 newstate->completed && !newstate->used_solve)
2555 return FLASH_TIME;
2556 else
2557 return 0.0F;
2558}
2559
2560static int game_status(const game_state *state)
2561{
2562 return state->completed ? +1 : 0;
2563}
2564
2565static int game_timing_state(const game_state *state, game_ui *ui)
2566{
2567 return TRUE;
2568}
2569
2570static void game_print_size(const game_params *params, float *x, float *y)
2571{
2572 int pw, ph;
2573
2574 /* The Times uses 7mm squares */
2575 game_compute_size(params, 700, &pw, &ph);
2576 *x = pw / 100.0F;
2577 *y = ph / 100.0F;
2578}
2579
2580static void game_print(drawing *dr, const game_state *state, int tilesize)
2581{
2582 int w = state->p.w, h = state->p.h;
2583 int black = print_mono_colour(dr, 0), grey = print_grey_colour(dr, 0.5F);
2584 int x, y, i;
2585
2586 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2587 game_drawstate ads, *ds = &ads;
2588 game_set_size(dr, ds, NULL, tilesize);
2589
2590 /* Grid, then border (second so it is on top) */
2591 print_line_width(dr, TILE_SIZE / 24);
2592 for (x = 1; x < w; x++)
2593 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), grey);
2594 for (y = 1; y < h; y++)
2595 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), grey);
2596
2597 print_line_width(dr, TILE_SIZE / 16);
2598 draw_rect_outline(dr, COORD(0), COORD(0), w*TILE_SIZE, h*TILE_SIZE, black);
2599
2600 print_line_width(dr, TILE_SIZE / 24);
2601
2602 /* clue numbers, and loop ends */
2603 for (i = 0; i < w+h; i++)
2604 draw_clue(dr, ds, w, state->numbers->numbers[i], i, black);
2605 draw_loop_ends(dr, ds, state, black);
2606
2607 /* clue tracks / solution */
2608 for (x = 0; x < w; x++) {
2609 for (y = 0; y < h; y++) {
2610 clip(dr, COORD(x), COORD(y), TILE_SIZE, TILE_SIZE);
2611 draw_tracks_specific(dr, ds, x, y, S_E_DIRS(state, x, y, E_TRACK),
2612 black, grey);
2613 unclip(dr);
2614 }
2615 }
2616}
2617
2618#ifdef COMBINED
2619#define thegame tracks
2620#endif
2621
2622const struct game thegame = {
2623 "Train Tracks", "games.tracks", "tracks",
2624 default_params,
2625 game_fetch_preset, NULL,
2626 decode_params,
2627 encode_params,
2628 free_params,
2629 dup_params,
2630 TRUE, game_configure, custom_params,
2631 validate_params,
2632 new_game_desc,
2633 validate_desc,
2634 new_game,
2635 dup_game,
2636 free_game,
2637 TRUE, solve_game,
2638 TRUE, game_can_format_as_text_now, game_text_format,
2639 new_ui,
2640 free_ui,
2641 encode_ui,
2642 decode_ui,
2643 game_changed_state,
2644 interpret_move,
2645 execute_move,
2646 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2647 game_colours,
2648 game_new_drawstate,
2649 game_free_drawstate,
2650 game_redraw,
2651 game_anim_length,
2652 game_flash_length,
2653 game_status,
2654 TRUE, FALSE, game_print_size, game_print,
2655 FALSE, /* wants_statusbar */
2656 FALSE, game_timing_state,
2657 0, /* flags */
2658};
2659
2660/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/tracks.html b/apps/plugins/puzzles/src/tracks.html
new file mode 100644
index 0000000000..dbff14394f
--- /dev/null
+++ b/apps/plugins/puzzles/src/tracks.html
@@ -0,0 +1,63 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Tracks</title>
7<link rel="previous" href="flood.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="palisade.html">
12</head>
13<body>
14<p><a href="flood.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="palisade.html">Next</a></p>
15<h1><a name="C40"></a>Chapter 40: <a name="i0"></a>Tracks</h1>
16<p>
17You are given a grid of squares, some of which are filled with train tracks. You need to complete the track from A to B so that the rows and columns contain the same number of track segments as are indicated in the clues to the top and right of the grid.
18</p>
19<p>
20There are only straight and 90 degree curved rails, and the track may not cross itself.
21</p>
22<p>
23Tracks was contributed to this collection by James Harvey.
24</p>
25<h2><a name="S40.1"></a>40.1 <a name="i1"></a>Tracks controls</h2>
26<p>
27Left-clicking on an edge between two squares adds a track segment between the two squares. Right-clicking on an edge adds a cross on the edge, indicating no track is possible there.
28</p>
29<p>
30Left-clicking in a square adds a colour indicator showing that you know the square must contain a track, even if you don't know which edges it crosses yet. Right-clicking in a square adds a cross indicating it contains no track segment.
31</p>
32<p>
33Left- or right-dragging between squares allows you to lay a straight line of is-track or is-not-track indicators, useful for filling in rows or columns to match the clue.
34</p>
35<p>
36(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
37</p>
38<h2><a name="S40.2"></a>40.2 <a name="i2"></a>Tracks parameters</h2>
39<p>
40These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
41</p>
42<dl><dt>
43<em>Width</em>, <em>Height</em>
44</dt>
45<dd>
46Size of the grid, in squares.
47</dd>
48<dt>
49<em>Difficulty</em>
50</dt>
51<dd>
52Controls the difficulty of the generated puzzle: at Tricky level, you are required to make more deductions regarding disregarding moves that would lead to impossible crossings later.
53</dd>
54<dt>
55<em>Disallow consecutive 1 clues</em>
56</dt>
57<dd>
58Controls whether the Tracks game generation permits two adjacent rows or columns to have a 1 clue, or permits the row or column of the track's endpoint to have a 1 clue. By default this is not permitted, to avoid long straight boring segments of track and make the games more twiddly and interesting. If you want to restore the possibility, turn this option off.
59</dd>
60</dl>
61
62<hr><address></address></body>
63</html>
diff --git a/apps/plugins/puzzles/src/tree234.c b/apps/plugins/puzzles/src/tree234.c
new file mode 100644
index 0000000000..4b3151ee2f
--- /dev/null
+++ b/apps/plugins/puzzles/src/tree234.c
@@ -0,0 +1,2200 @@
1/*
2 * tree234.c: reasonably generic counted 2-3-4 tree routines.
3 *
4 * This file is copyright 1999-2001 Simon Tatham.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following
13 * conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <assert.h>
31
32#include "tree234.h"
33
34#include "puzzles.h" /* for smalloc/sfree */
35
36#ifdef TEST
37#define LOG(x) (printf x)
38#define smalloc malloc
39#define srealloc realloc
40#define sfree free
41#else
42#define LOG(x)
43#endif
44
45typedef struct node234_Tag node234;
46
47struct tree234_Tag {
48 node234 *root;
49 cmpfn234 cmp;
50};
51
52struct node234_Tag {
53 node234 *parent;
54 node234 *kids[4];
55 int counts[4];
56 void *elems[3];
57};
58
59/*
60 * Create a 2-3-4 tree.
61 */
62tree234 *newtree234(cmpfn234 cmp) {
63 tree234 *ret = snew(tree234);
64 LOG(("created tree %p\n", ret));
65 ret->root = NULL;
66 ret->cmp = cmp;
67 return ret;
68}
69
70/*
71 * Free a 2-3-4 tree (not including freeing the elements).
72 */
73static void freenode234(node234 *n) {
74 if (!n)
75 return;
76 freenode234(n->kids[0]);
77 freenode234(n->kids[1]);
78 freenode234(n->kids[2]);
79 freenode234(n->kids[3]);
80 sfree(n);
81}
82void freetree234(tree234 *t) {
83 freenode234(t->root);
84 sfree(t);
85}
86
87/*
88 * Internal function to count a node.
89 */
90static int countnode234(node234 *n) {
91 int count = 0;
92 int i;
93 if (!n)
94 return 0;
95 for (i = 0; i < 4; i++)
96 count += n->counts[i];
97 for (i = 0; i < 3; i++)
98 if (n->elems[i])
99 count++;
100 return count;
101}
102
103/*
104 * Count the elements in a tree.
105 */
106int count234(tree234 *t) {
107 if (t->root)
108 return countnode234(t->root);
109 else
110 return 0;
111}
112
113/*
114 * Propagate a node overflow up a tree until it stops. Returns 0 or
115 * 1, depending on whether the root had to be split or not.
116 */
117static int add234_insert(node234 *left, void *e, node234 *right,
118 node234 **root, node234 *n, int ki) {
119 int lcount, rcount;
120 /*
121 * We need to insert the new left/element/right set in n at
122 * child position ki.
123 */
124 lcount = countnode234(left);
125 rcount = countnode234(right);
126 while (n) {
127 LOG((" at %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
128 n,
129 n->kids[0], n->counts[0], n->elems[0],
130 n->kids[1], n->counts[1], n->elems[1],
131 n->kids[2], n->counts[2], n->elems[2],
132 n->kids[3], n->counts[3]));
133 LOG((" need to insert %p/%d \"%s\" %p/%d at position %d\n",
134 left, lcount, e, right, rcount, ki));
135 if (n->elems[1] == NULL) {
136 /*
137 * Insert in a 2-node; simple.
138 */
139 if (ki == 0) {
140 LOG((" inserting on left of 2-node\n"));
141 n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1];
142 n->elems[1] = n->elems[0];
143 n->kids[1] = right; n->counts[1] = rcount;
144 n->elems[0] = e;
145 n->kids[0] = left; n->counts[0] = lcount;
146 } else { /* ki == 1 */
147 LOG((" inserting on right of 2-node\n"));
148 n->kids[2] = right; n->counts[2] = rcount;
149 n->elems[1] = e;
150 n->kids[1] = left; n->counts[1] = lcount;
151 }
152 if (n->kids[0]) n->kids[0]->parent = n;
153 if (n->kids[1]) n->kids[1]->parent = n;
154 if (n->kids[2]) n->kids[2]->parent = n;
155 LOG((" done\n"));
156 break;
157 } else if (n->elems[2] == NULL) {
158 /*
159 * Insert in a 3-node; simple.
160 */
161 if (ki == 0) {
162 LOG((" inserting on left of 3-node\n"));
163 n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2];
164 n->elems[2] = n->elems[1];
165 n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1];
166 n->elems[1] = n->elems[0];
167 n->kids[1] = right; n->counts[1] = rcount;
168 n->elems[0] = e;
169 n->kids[0] = left; n->counts[0] = lcount;
170 } else if (ki == 1) {
171 LOG((" inserting in middle of 3-node\n"));
172 n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2];
173 n->elems[2] = n->elems[1];
174 n->kids[2] = right; n->counts[2] = rcount;
175 n->elems[1] = e;
176 n->kids[1] = left; n->counts[1] = lcount;
177 } else { /* ki == 2 */
178 LOG((" inserting on right of 3-node\n"));
179 n->kids[3] = right; n->counts[3] = rcount;
180 n->elems[2] = e;
181 n->kids[2] = left; n->counts[2] = lcount;
182 }
183 if (n->kids[0]) n->kids[0]->parent = n;
184 if (n->kids[1]) n->kids[1]->parent = n;
185 if (n->kids[2]) n->kids[2]->parent = n;
186 if (n->kids[3]) n->kids[3]->parent = n;
187 LOG((" done\n"));
188 break;
189 } else {
190 node234 *m = snew(node234);
191 m->parent = n->parent;
192 LOG((" splitting a 4-node; created new node %p\n", m));
193 /*
194 * Insert in a 4-node; split into a 2-node and a
195 * 3-node, and move focus up a level.
196 *
197 * I don't think it matters which way round we put the
198 * 2 and the 3. For simplicity, we'll put the 3 first
199 * always.
200 */
201 if (ki == 0) {
202 m->kids[0] = left; m->counts[0] = lcount;
203 m->elems[0] = e;
204 m->kids[1] = right; m->counts[1] = rcount;
205 m->elems[1] = n->elems[0];
206 m->kids[2] = n->kids[1]; m->counts[2] = n->counts[1];
207 e = n->elems[1];
208 n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2];
209 n->elems[0] = n->elems[2];
210 n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3];
211 } else if (ki == 1) {
212 m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0];
213 m->elems[0] = n->elems[0];
214 m->kids[1] = left; m->counts[1] = lcount;
215 m->elems[1] = e;
216 m->kids[2] = right; m->counts[2] = rcount;
217 e = n->elems[1];
218 n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2];
219 n->elems[0] = n->elems[2];
220 n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3];
221 } else if (ki == 2) {
222 m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0];
223 m->elems[0] = n->elems[0];
224 m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1];
225 m->elems[1] = n->elems[1];
226 m->kids[2] = left; m->counts[2] = lcount;
227 /* e = e; */
228 n->kids[0] = right; n->counts[0] = rcount;
229 n->elems[0] = n->elems[2];
230 n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3];
231 } else { /* ki == 3 */
232 m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0];
233 m->elems[0] = n->elems[0];
234 m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1];
235 m->elems[1] = n->elems[1];
236 m->kids[2] = n->kids[2]; m->counts[2] = n->counts[2];
237 n->kids[0] = left; n->counts[0] = lcount;
238 n->elems[0] = e;
239 n->kids[1] = right; n->counts[1] = rcount;
240 e = n->elems[2];
241 }
242 m->kids[3] = n->kids[3] = n->kids[2] = NULL;
243 m->counts[3] = n->counts[3] = n->counts[2] = 0;
244 m->elems[2] = n->elems[2] = n->elems[1] = NULL;
245 if (m->kids[0]) m->kids[0]->parent = m;
246 if (m->kids[1]) m->kids[1]->parent = m;
247 if (m->kids[2]) m->kids[2]->parent = m;
248 if (n->kids[0]) n->kids[0]->parent = n;
249 if (n->kids[1]) n->kids[1]->parent = n;
250 LOG((" left (%p): %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", m,
251 m->kids[0], m->counts[0], m->elems[0],
252 m->kids[1], m->counts[1], m->elems[1],
253 m->kids[2], m->counts[2]));
254 LOG((" right (%p): %p/%d \"%s\" %p/%d\n", n,
255 n->kids[0], n->counts[0], n->elems[0],
256 n->kids[1], n->counts[1]));
257 left = m; lcount = countnode234(left);
258 right = n; rcount = countnode234(right);
259 }
260 if (n->parent)
261 ki = (n->parent->kids[0] == n ? 0 :
262 n->parent->kids[1] == n ? 1 :
263 n->parent->kids[2] == n ? 2 : 3);
264 n = n->parent;
265 }
266
267 /*
268 * If we've come out of here by `break', n will still be
269 * non-NULL and all we need to do is go back up the tree
270 * updating counts. If we've come here because n is NULL, we
271 * need to create a new root for the tree because the old one
272 * has just split into two. */
273 if (n) {
274 while (n->parent) {
275 int count = countnode234(n);
276 int childnum;
277 childnum = (n->parent->kids[0] == n ? 0 :
278 n->parent->kids[1] == n ? 1 :
279 n->parent->kids[2] == n ? 2 : 3);
280 n->parent->counts[childnum] = count;
281 n = n->parent;
282 }
283 return 0; /* root unchanged */
284 } else {
285 LOG((" root is overloaded, split into two\n"));
286 (*root) = snew(node234);
287 (*root)->kids[0] = left; (*root)->counts[0] = lcount;
288 (*root)->elems[0] = e;
289 (*root)->kids[1] = right; (*root)->counts[1] = rcount;
290 (*root)->elems[1] = NULL;
291 (*root)->kids[2] = NULL; (*root)->counts[2] = 0;
292 (*root)->elems[2] = NULL;
293 (*root)->kids[3] = NULL; (*root)->counts[3] = 0;
294 (*root)->parent = NULL;
295 if ((*root)->kids[0]) (*root)->kids[0]->parent = (*root);
296 if ((*root)->kids[1]) (*root)->kids[1]->parent = (*root);
297 LOG((" new root is %p/%d \"%s\" %p/%d\n",
298 (*root)->kids[0], (*root)->counts[0],
299 (*root)->elems[0],
300 (*root)->kids[1], (*root)->counts[1]));
301 return 1; /* root moved */
302 }
303}
304
305/*
306 * Add an element e to a 2-3-4 tree t. Returns e on success, or if
307 * an existing element compares equal, returns that.
308 */
309static void *add234_internal(tree234 *t, void *e, int index) {
310 node234 *n;
311 int ki;
312 void *orig_e = e;
313 int c;
314
315 LOG(("adding element \"%s\" to tree %p\n", e, t));
316 if (t->root == NULL) {
317 t->root = snew(node234);
318 t->root->elems[1] = t->root->elems[2] = NULL;
319 t->root->kids[0] = t->root->kids[1] = NULL;
320 t->root->kids[2] = t->root->kids[3] = NULL;
321 t->root->counts[0] = t->root->counts[1] = 0;
322 t->root->counts[2] = t->root->counts[3] = 0;
323 t->root->parent = NULL;
324 t->root->elems[0] = e;
325 LOG((" created root %p\n", t->root));
326 return orig_e;
327 }
328
329 n = t->root;
330 while (n) {
331 LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
332 n,
333 n->kids[0], n->counts[0], n->elems[0],
334 n->kids[1], n->counts[1], n->elems[1],
335 n->kids[2], n->counts[2], n->elems[2],
336 n->kids[3], n->counts[3]));
337 if (index >= 0) {
338 if (!n->kids[0]) {
339 /*
340 * Leaf node. We want to insert at kid position
341 * equal to the index:
342 *
343 * 0 A 1 B 2 C 3
344 */
345 ki = index;
346 } else {
347 /*
348 * Internal node. We always descend through it (add
349 * always starts at the bottom, never in the
350 * middle).
351 */
352 if (index <= n->counts[0]) {
353 ki = 0;
354 } else if (index -= n->counts[0] + 1, index <= n->counts[1]) {
355 ki = 1;
356 } else if (index -= n->counts[1] + 1, index <= n->counts[2]) {
357 ki = 2;
358 } else if (index -= n->counts[2] + 1, index <= n->counts[3]) {
359 ki = 3;
360 } else
361 return NULL; /* error: index out of range */
362 }
363 } else {
364 if ((c = t->cmp(e, n->elems[0])) < 0)
365 ki = 0;
366 else if (c == 0)
367 return n->elems[0]; /* already exists */
368 else if (n->elems[1] == NULL || (c = t->cmp(e, n->elems[1])) < 0)
369 ki = 1;
370 else if (c == 0)
371 return n->elems[1]; /* already exists */
372 else if (n->elems[2] == NULL || (c = t->cmp(e, n->elems[2])) < 0)
373 ki = 2;
374 else if (c == 0)
375 return n->elems[2]; /* already exists */
376 else
377 ki = 3;
378 }
379 LOG((" moving to child %d (%p)\n", ki, n->kids[ki]));
380 if (!n->kids[ki])
381 break;
382 n = n->kids[ki];
383 }
384
385 add234_insert(NULL, e, NULL, &t->root, n, ki);
386
387 return orig_e;
388}
389
390void *add234(tree234 *t, void *e) {
391 if (!t->cmp) /* tree is unsorted */
392 return NULL;
393
394 return add234_internal(t, e, -1);
395}
396void *addpos234(tree234 *t, void *e, int index) {
397 if (index < 0 || /* index out of range */
398 t->cmp) /* tree is sorted */
399 return NULL; /* return failure */
400
401 return add234_internal(t, e, index); /* this checks the upper bound */
402}
403
404/*
405 * Look up the element at a given numeric index in a 2-3-4 tree.
406 * Returns NULL if the index is out of range.
407 */
408void *index234(tree234 *t, int index) {
409 node234 *n;
410
411 if (!t->root)
412 return NULL; /* tree is empty */
413
414 if (index < 0 || index >= countnode234(t->root))
415 return NULL; /* out of range */
416
417 n = t->root;
418
419 while (n) {
420 if (index < n->counts[0])
421 n = n->kids[0];
422 else if (index -= n->counts[0] + 1, index < 0)
423 return n->elems[0];
424 else if (index < n->counts[1])
425 n = n->kids[1];
426 else if (index -= n->counts[1] + 1, index < 0)
427 return n->elems[1];
428 else if (index < n->counts[2])
429 n = n->kids[2];
430 else if (index -= n->counts[2] + 1, index < 0)
431 return n->elems[2];
432 else
433 n = n->kids[3];
434 }
435
436 /* We shouldn't ever get here. I wonder how we did. */
437 return NULL;
438}
439
440/*
441 * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not
442 * found. e is always passed as the first argument to cmp, so cmp
443 * can be an asymmetric function if desired. cmp can also be passed
444 * as NULL, in which case the compare function from the tree proper
445 * will be used.
446 */
447void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp,
448 int relation, int *index) {
449 node234 *n;
450 void *ret;
451 int c;
452 int idx, ecount, kcount, cmpret;
453
454 if (t->root == NULL)
455 return NULL;
456
457 if (cmp == NULL)
458 cmp = t->cmp;
459
460 n = t->root;
461 /*
462 * Attempt to find the element itself.
463 */
464 idx = 0;
465 ecount = -1;
466 /*
467 * Prepare a fake `cmp' result if e is NULL.
468 */
469 cmpret = 0;
470 if (e == NULL) {
471 assert(relation == REL234_LT || relation == REL234_GT);
472 if (relation == REL234_LT)
473 cmpret = +1; /* e is a max: always greater */
474 else if (relation == REL234_GT)
475 cmpret = -1; /* e is a min: always smaller */
476 }
477 while (1) {
478 for (kcount = 0; kcount < 4; kcount++) {
479 if (kcount >= 3 || n->elems[kcount] == NULL ||
480 (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) {
481 break;
482 }
483 if (n->kids[kcount]) idx += n->counts[kcount];
484 if (c == 0) {
485 ecount = kcount;
486 break;
487 }
488 idx++;
489 }
490 if (ecount >= 0)
491 break;
492 if (n->kids[kcount])
493 n = n->kids[kcount];
494 else
495 break;
496 }
497
498 if (ecount >= 0) {
499 /*
500 * We have found the element we're looking for. It's
501 * n->elems[ecount], at tree index idx. If our search
502 * relation is EQ, LE or GE we can now go home.
503 */
504 if (relation != REL234_LT && relation != REL234_GT) {
505 if (index) *index = idx;
506 return n->elems[ecount];
507 }
508
509 /*
510 * Otherwise, we'll do an indexed lookup for the previous
511 * or next element. (It would be perfectly possible to
512 * implement these search types in a non-counted tree by
513 * going back up from where we are, but far more fiddly.)
514 */
515 if (relation == REL234_LT)
516 idx--;
517 else
518 idx++;
519 } else {
520 /*
521 * We've found our way to the bottom of the tree and we
522 * know where we would insert this node if we wanted to:
523 * we'd put it in in place of the (empty) subtree
524 * n->kids[kcount], and it would have index idx
525 *
526 * But the actual element isn't there. So if our search
527 * relation is EQ, we're doomed.
528 */
529 if (relation == REL234_EQ)
530 return NULL;
531
532 /*
533 * Otherwise, we must do an index lookup for index idx-1
534 * (if we're going left - LE or LT) or index idx (if we're
535 * going right - GE or GT).
536 */
537 if (relation == REL234_LT || relation == REL234_LE) {
538 idx--;
539 }
540 }
541
542 /*
543 * We know the index of the element we want; just call index234
544 * to do the rest. This will return NULL if the index is out of
545 * bounds, which is exactly what we want.
546 */
547 ret = index234(t, idx);
548 if (ret && index) *index = idx;
549 return ret;
550}
551void *find234(tree234 *t, void *e, cmpfn234 cmp) {
552 return findrelpos234(t, e, cmp, REL234_EQ, NULL);
553}
554void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation) {
555 return findrelpos234(t, e, cmp, relation, NULL);
556}
557void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index) {
558 return findrelpos234(t, e, cmp, REL234_EQ, index);
559}
560
561/*
562 * Tree transformation used in delete and split: move a subtree
563 * right, from child ki of a node to the next child. Update k and
564 * index so that they still point to the same place in the
565 * transformed tree. Assumes the destination child is not full, and
566 * that the source child does have a subtree to spare. Can cope if
567 * the destination child is undersized.
568 *
569 * . C . . B .
570 * / \ -> / \
571 * [more] a A b B c d D e [more] a A b c C d D e
572 *
573 * . C . . B .
574 * / \ -> / \
575 * [more] a A b B c d [more] a A b c C d
576 */
577static void trans234_subtree_right(node234 *n, int ki, int *k, int *index) {
578 node234 *src, *dest;
579 int i, srclen, adjust;
580
581 src = n->kids[ki];
582 dest = n->kids[ki+1];
583
584 LOG((" trans234_subtree_right(%p, %d):\n", n, ki));
585 LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
586 n,
587 n->kids[0], n->counts[0], n->elems[0],
588 n->kids[1], n->counts[1], n->elems[1],
589 n->kids[2], n->counts[2], n->elems[2],
590 n->kids[3], n->counts[3]));
591 LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
592 src,
593 src->kids[0], src->counts[0], src->elems[0],
594 src->kids[1], src->counts[1], src->elems[1],
595 src->kids[2], src->counts[2], src->elems[2],
596 src->kids[3], src->counts[3]));
597 LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
598 dest,
599 dest->kids[0], dest->counts[0], dest->elems[0],
600 dest->kids[1], dest->counts[1], dest->elems[1],
601 dest->kids[2], dest->counts[2], dest->elems[2],
602 dest->kids[3], dest->counts[3]));
603 /*
604 * Move over the rest of the destination node to make space.
605 */
606 dest->kids[3] = dest->kids[2]; dest->counts[3] = dest->counts[2];
607 dest->elems[2] = dest->elems[1];
608 dest->kids[2] = dest->kids[1]; dest->counts[2] = dest->counts[1];
609 dest->elems[1] = dest->elems[0];
610 dest->kids[1] = dest->kids[0]; dest->counts[1] = dest->counts[0];
611
612 /* which element to move over */
613 i = (src->elems[2] ? 2 : src->elems[1] ? 1 : 0);
614
615 dest->elems[0] = n->elems[ki];
616 n->elems[ki] = src->elems[i];
617 src->elems[i] = NULL;
618
619 dest->kids[0] = src->kids[i+1]; dest->counts[0] = src->counts[i+1];
620 src->kids[i+1] = NULL; src->counts[i+1] = 0;
621
622 if (dest->kids[0]) dest->kids[0]->parent = dest;
623
624 adjust = dest->counts[0] + 1;
625
626 n->counts[ki] -= adjust;
627 n->counts[ki+1] += adjust;
628
629 srclen = n->counts[ki];
630
631 if (k) {
632 LOG((" before: k,index = %d,%d\n", (*k), (*index)));
633 if ((*k) == ki && (*index) > srclen) {
634 (*index) -= srclen + 1;
635 (*k)++;
636 } else if ((*k) == ki+1) {
637 (*index) += adjust;
638 }
639 LOG((" after: k,index = %d,%d\n", (*k), (*index)));
640 }
641
642 LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
643 n,
644 n->kids[0], n->counts[0], n->elems[0],
645 n->kids[1], n->counts[1], n->elems[1],
646 n->kids[2], n->counts[2], n->elems[2],
647 n->kids[3], n->counts[3]));
648 LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
649 src,
650 src->kids[0], src->counts[0], src->elems[0],
651 src->kids[1], src->counts[1], src->elems[1],
652 src->kids[2], src->counts[2], src->elems[2],
653 src->kids[3], src->counts[3]));
654 LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
655 dest,
656 dest->kids[0], dest->counts[0], dest->elems[0],
657 dest->kids[1], dest->counts[1], dest->elems[1],
658 dest->kids[2], dest->counts[2], dest->elems[2],
659 dest->kids[3], dest->counts[3]));
660}
661
662/*
663 * Tree transformation used in delete and split: move a subtree
664 * left, from child ki of a node to the previous child. Update k
665 * and index so that they still point to the same place in the
666 * transformed tree. Assumes the destination child is not full, and
667 * that the source child does have a subtree to spare. Can cope if
668 * the destination child is undersized.
669 *
670 * . B . . C .
671 * / \ -> / \
672 * a A b c C d D e [more] a A b B c d D e [more]
673 *
674 * . A . . B .
675 * / \ -> / \
676 * a b B c C d [more] a A b c C d [more]
677 */
678static void trans234_subtree_left(node234 *n, int ki, int *k, int *index) {
679 node234 *src, *dest;
680 int i, adjust;
681
682 src = n->kids[ki];
683 dest = n->kids[ki-1];
684
685 LOG((" trans234_subtree_left(%p, %d):\n", n, ki));
686 LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
687 n,
688 n->kids[0], n->counts[0], n->elems[0],
689 n->kids[1], n->counts[1], n->elems[1],
690 n->kids[2], n->counts[2], n->elems[2],
691 n->kids[3], n->counts[3]));
692 LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
693 dest,
694 dest->kids[0], dest->counts[0], dest->elems[0],
695 dest->kids[1], dest->counts[1], dest->elems[1],
696 dest->kids[2], dest->counts[2], dest->elems[2],
697 dest->kids[3], dest->counts[3]));
698 LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
699 src,
700 src->kids[0], src->counts[0], src->elems[0],
701 src->kids[1], src->counts[1], src->elems[1],
702 src->kids[2], src->counts[2], src->elems[2],
703 src->kids[3], src->counts[3]));
704
705 /* where in dest to put it */
706 i = (dest->elems[1] ? 2 : dest->elems[0] ? 1 : 0);
707 dest->elems[i] = n->elems[ki-1];
708 n->elems[ki-1] = src->elems[0];
709
710 dest->kids[i+1] = src->kids[0]; dest->counts[i+1] = src->counts[0];
711
712 if (dest->kids[i+1]) dest->kids[i+1]->parent = dest;
713
714 /*
715 * Move over the rest of the source node.
716 */
717 src->kids[0] = src->kids[1]; src->counts[0] = src->counts[1];
718 src->elems[0] = src->elems[1];
719 src->kids[1] = src->kids[2]; src->counts[1] = src->counts[2];
720 src->elems[1] = src->elems[2];
721 src->kids[2] = src->kids[3]; src->counts[2] = src->counts[3];
722 src->elems[2] = NULL;
723 src->kids[3] = NULL; src->counts[3] = 0;
724
725 adjust = dest->counts[i+1] + 1;
726
727 n->counts[ki] -= adjust;
728 n->counts[ki-1] += adjust;
729
730 if (k) {
731 LOG((" before: k,index = %d,%d\n", (*k), (*index)));
732 if ((*k) == ki) {
733 (*index) -= adjust;
734 if ((*index) < 0) {
735 (*index) += n->counts[ki-1] + 1;
736 (*k)--;
737 }
738 }
739 LOG((" after: k,index = %d,%d\n", (*k), (*index)));
740 }
741
742 LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
743 n,
744 n->kids[0], n->counts[0], n->elems[0],
745 n->kids[1], n->counts[1], n->elems[1],
746 n->kids[2], n->counts[2], n->elems[2],
747 n->kids[3], n->counts[3]));
748 LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
749 dest,
750 dest->kids[0], dest->counts[0], dest->elems[0],
751 dest->kids[1], dest->counts[1], dest->elems[1],
752 dest->kids[2], dest->counts[2], dest->elems[2],
753 dest->kids[3], dest->counts[3]));
754 LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
755 src,
756 src->kids[0], src->counts[0], src->elems[0],
757 src->kids[1], src->counts[1], src->elems[1],
758 src->kids[2], src->counts[2], src->elems[2],
759 src->kids[3], src->counts[3]));
760}
761
762/*
763 * Tree transformation used in delete and split: merge child nodes
764 * ki and ki+1 of a node. Update k and index so that they still
765 * point to the same place in the transformed tree. Assumes both
766 * children _are_ sufficiently small.
767 *
768 * . B . .
769 * / \ -> |
770 * a A b c C d a A b B c C d
771 *
772 * This routine can also cope with either child being undersized:
773 *
774 * . A . .
775 * / \ -> |
776 * a b B c a A b B c
777 *
778 * . A . .
779 * / \ -> |
780 * a b B c C d a A b B c C d
781 */
782static void trans234_subtree_merge(node234 *n, int ki, int *k, int *index) {
783 node234 *left, *right;
784 int i, leftlen, rightlen, lsize, rsize;
785
786 left = n->kids[ki]; leftlen = n->counts[ki];
787 right = n->kids[ki+1]; rightlen = n->counts[ki+1];
788
789 LOG((" trans234_subtree_merge(%p, %d):\n", n, ki));
790 LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
791 n,
792 n->kids[0], n->counts[0], n->elems[0],
793 n->kids[1], n->counts[1], n->elems[1],
794 n->kids[2], n->counts[2], n->elems[2],
795 n->kids[3], n->counts[3]));
796 LOG((" left %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
797 left,
798 left->kids[0], left->counts[0], left->elems[0],
799 left->kids[1], left->counts[1], left->elems[1],
800 left->kids[2], left->counts[2], left->elems[2],
801 left->kids[3], left->counts[3]));
802 LOG((" right %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
803 right,
804 right->kids[0], right->counts[0], right->elems[0],
805 right->kids[1], right->counts[1], right->elems[1],
806 right->kids[2], right->counts[2], right->elems[2],
807 right->kids[3], right->counts[3]));
808
809 assert(!left->elems[2] && !right->elems[2]); /* neither is large! */
810 lsize = (left->elems[1] ? 2 : left->elems[0] ? 1 : 0);
811 rsize = (right->elems[1] ? 2 : right->elems[0] ? 1 : 0);
812
813 left->elems[lsize] = n->elems[ki];
814
815 for (i = 0; i < rsize+1; i++) {
816 left->kids[lsize+1+i] = right->kids[i];
817 left->counts[lsize+1+i] = right->counts[i];
818 if (left->kids[lsize+1+i])
819 left->kids[lsize+1+i]->parent = left;
820 if (i < rsize)
821 left->elems[lsize+1+i] = right->elems[i];
822 }
823
824 n->counts[ki] += rightlen + 1;
825
826 sfree(right);
827
828 /*
829 * Move the rest of n up by one.
830 */
831 for (i = ki+1; i < 3; i++) {
832 n->kids[i] = n->kids[i+1];
833 n->counts[i] = n->counts[i+1];
834 }
835 for (i = ki; i < 2; i++) {
836 n->elems[i] = n->elems[i+1];
837 }
838 n->kids[3] = NULL;
839 n->counts[3] = 0;
840 n->elems[2] = NULL;
841
842 if (k) {
843 LOG((" before: k,index = %d,%d\n", (*k), (*index)));
844 if ((*k) == ki+1) {
845 (*k)--;
846 (*index) += leftlen + 1;
847 } else if ((*k) > ki+1) {
848 (*k)--;
849 }
850 LOG((" after: k,index = %d,%d\n", (*k), (*index)));
851 }
852
853 LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
854 n,
855 n->kids[0], n->counts[0], n->elems[0],
856 n->kids[1], n->counts[1], n->elems[1],
857 n->kids[2], n->counts[2], n->elems[2],
858 n->kids[3], n->counts[3]));
859 LOG((" merged %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
860 left,
861 left->kids[0], left->counts[0], left->elems[0],
862 left->kids[1], left->counts[1], left->elems[1],
863 left->kids[2], left->counts[2], left->elems[2],
864 left->kids[3], left->counts[3]));
865
866}
867
868/*
869 * Delete an element e in a 2-3-4 tree. Does not free the element,
870 * merely removes all links to it from the tree nodes.
871 */
872static void *delpos234_internal(tree234 *t, int index) {
873 node234 *n;
874 void *retval;
875 int ki, i;
876
877 retval = NULL;
878
879 n = t->root; /* by assumption this is non-NULL */
880 LOG(("deleting item %d from tree %p\n", index, t));
881 while (1) {
882 node234 *sub;
883
884 LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d index=%d\n",
885 n,
886 n->kids[0], n->counts[0], n->elems[0],
887 n->kids[1], n->counts[1], n->elems[1],
888 n->kids[2], n->counts[2], n->elems[2],
889 n->kids[3], n->counts[3],
890 index));
891 if (index <= n->counts[0]) {
892 ki = 0;
893 } else if (index -= n->counts[0]+1, index <= n->counts[1]) {
894 ki = 1;
895 } else if (index -= n->counts[1]+1, index <= n->counts[2]) {
896 ki = 2;
897 } else if (index -= n->counts[2]+1, index <= n->counts[3]) {
898 ki = 3;
899 } else {
900 assert(0); /* can't happen */
901 }
902
903 if (!n->kids[0])
904 break; /* n is a leaf node; we're here! */
905
906 /*
907 * Check to see if we've found our target element. If so,
908 * we must choose a new target (we'll use the old target's
909 * successor, which will be in a leaf), move it into the
910 * place of the old one, continue down to the leaf and
911 * delete the old copy of the new target.
912 */
913 if (index == n->counts[ki]) {
914 node234 *m;
915 LOG((" found element in internal node, index %d\n", ki));
916 assert(n->elems[ki]); /* must be a kid _before_ an element */
917 ki++; index = 0;
918 for (m = n->kids[ki]; m->kids[0]; m = m->kids[0])
919 continue;
920 LOG((" replacing with element \"%s\" from leaf node %p\n",
921 m->elems[0], m));
922 retval = n->elems[ki-1];
923 n->elems[ki-1] = m->elems[0];
924 }
925
926 /*
927 * Recurse down to subtree ki. If it has only one element,
928 * we have to do some transformation to start with.
929 */
930 LOG((" moving to subtree %d\n", ki));
931 sub = n->kids[ki];
932 if (!sub->elems[1]) {
933 LOG((" subtree has only one element!\n"));
934 if (ki > 0 && n->kids[ki-1]->elems[1]) {
935 /*
936 * Child ki has only one element, but child
937 * ki-1 has two or more. So we need to move a
938 * subtree from ki-1 to ki.
939 */
940 trans234_subtree_right(n, ki-1, &ki, &index);
941 } else if (ki < 3 && n->kids[ki+1] &&
942 n->kids[ki+1]->elems[1]) {
943 /*
944 * Child ki has only one element, but ki+1 has
945 * two or more. Move a subtree from ki+1 to ki.
946 */
947 trans234_subtree_left(n, ki+1, &ki, &index);
948 } else {
949 /*
950 * ki is small with only small neighbours. Pick a
951 * neighbour and merge with it.
952 */
953 trans234_subtree_merge(n, ki>0 ? ki-1 : ki, &ki, &index);
954 sub = n->kids[ki];
955
956 if (!n->elems[0]) {
957 /*
958 * The root is empty and needs to be
959 * removed.
960 */
961 LOG((" shifting root!\n"));
962 t->root = sub;
963 sub->parent = NULL;
964 sfree(n);
965 n = NULL;
966 }
967 }
968 }
969
970 if (n)
971 n->counts[ki]--;
972 n = sub;
973 }
974
975 /*
976 * Now n is a leaf node, and ki marks the element number we
977 * want to delete. We've already arranged for the leaf to be
978 * bigger than minimum size, so let's just go to it.
979 */
980 assert(!n->kids[0]);
981 if (!retval)
982 retval = n->elems[ki];
983
984 for (i = ki; i < 2 && n->elems[i+1]; i++)
985 n->elems[i] = n->elems[i+1];
986 n->elems[i] = NULL;
987
988 /*
989 * It's just possible that we have reduced the leaf to zero
990 * size. This can only happen if it was the root - so destroy
991 * it and make the tree empty.
992 */
993 if (!n->elems[0]) {
994 LOG((" removed last element in tree, destroying empty root\n"));
995 assert(n == t->root);
996 sfree(n);
997 t->root = NULL;
998 }
999
1000 return retval; /* finished! */
1001}
1002void *delpos234(tree234 *t, int index) {
1003 if (index < 0 || index >= countnode234(t->root))
1004 return NULL;
1005 return delpos234_internal(t, index);
1006}
1007void *del234(tree234 *t, void *e) {
1008 int index;
1009 if (!findrelpos234(t, e, NULL, REL234_EQ, &index))
1010 return NULL; /* it wasn't in there anyway */
1011 return delpos234_internal(t, index); /* it's there; delete it. */
1012}
1013
1014/*
1015 * Join two subtrees together with a separator element between
1016 * them, given their relative height.
1017 *
1018 * (Height<0 means the left tree is shorter, >0 means the right
1019 * tree is shorter, =0 means (duh) they're equal.)
1020 *
1021 * It is assumed that any checks needed on the ordering criterion
1022 * have _already_ been done.
1023 *
1024 * The value returned in `height' is 0 or 1 depending on whether the
1025 * resulting tree is the same height as the original larger one, or
1026 * one higher.
1027 */
1028static node234 *join234_internal(node234 *left, void *sep,
1029 node234 *right, int *height) {
1030 node234 *root, *node;
1031 int relht = *height;
1032 int ki;
1033
1034 LOG((" join: joining %p \"%s\" %p, relative height is %d\n",
1035 left, sep, right, relht));
1036 if (relht == 0) {
1037 /*
1038 * The trees are the same height. Create a new one-element
1039 * root containing the separator and pointers to the two
1040 * nodes.
1041 */
1042 node234 *newroot;
1043 newroot = snew(node234);
1044 newroot->kids[0] = left; newroot->counts[0] = countnode234(left);
1045 newroot->elems[0] = sep;
1046 newroot->kids[1] = right; newroot->counts[1] = countnode234(right);
1047 newroot->elems[1] = NULL;
1048 newroot->kids[2] = NULL; newroot->counts[2] = 0;
1049 newroot->elems[2] = NULL;
1050 newroot->kids[3] = NULL; newroot->counts[3] = 0;
1051 newroot->parent = NULL;
1052 if (left) left->parent = newroot;
1053 if (right) right->parent = newroot;
1054 *height = 1;
1055 LOG((" join: same height, brand new root\n"));
1056 return newroot;
1057 }
1058
1059 /*
1060 * This now works like the addition algorithm on the larger
1061 * tree. We're replacing a single kid pointer with two kid
1062 * pointers separated by an element; if that causes the node to
1063 * overload, we split it in two, move a separator element up to
1064 * the next node, and repeat.
1065 */
1066 if (relht < 0) {
1067 /*
1068 * Left tree is shorter. Search down the right tree to find
1069 * the pointer we're inserting at.
1070 */
1071 node = root = right;
1072 while (++relht < 0) {
1073 node = node->kids[0];
1074 }
1075 ki = 0;
1076 right = node->kids[ki];
1077 } else {
1078 /*
1079 * Right tree is shorter; search down the left to find the
1080 * pointer we're inserting at.
1081 */
1082 node = root = left;
1083 while (--relht > 0) {
1084 if (node->elems[2])
1085 node = node->kids[3];
1086 else if (node->elems[1])
1087 node = node->kids[2];
1088 else
1089 node = node->kids[1];
1090 }
1091 if (node->elems[2])
1092 ki = 3;
1093 else if (node->elems[1])
1094 ki = 2;
1095 else
1096 ki = 1;
1097 left = node->kids[ki];
1098 }
1099
1100 /*
1101 * Now proceed as for addition.
1102 */
1103 *height = add234_insert(left, sep, right, &root, node, ki);
1104
1105 return root;
1106}
1107static int height234(tree234 *t) {
1108 int level = 0;
1109 node234 *n = t->root;
1110 while (n) {
1111 level++;
1112 n = n->kids[0];
1113 }
1114 return level;
1115}
1116tree234 *join234(tree234 *t1, tree234 *t2) {
1117 int size2 = countnode234(t2->root);
1118 if (size2 > 0) {
1119 void *element;
1120 int relht;
1121
1122 if (t1->cmp) {
1123 element = index234(t2, 0);
1124 element = findrelpos234(t1, element, NULL, REL234_GE, NULL);
1125 if (element)
1126 return NULL;
1127 }
1128
1129 element = delpos234(t2, 0);
1130 relht = height234(t1) - height234(t2);
1131 t1->root = join234_internal(t1->root, element, t2->root, &relht);
1132 t2->root = NULL;
1133 }
1134 return t1;
1135}
1136tree234 *join234r(tree234 *t1, tree234 *t2) {
1137 int size1 = countnode234(t1->root);
1138 if (size1 > 0) {
1139 void *element;
1140 int relht;
1141
1142 if (t2->cmp) {
1143 element = index234(t1, size1-1);
1144 element = findrelpos234(t2, element, NULL, REL234_LE, NULL);
1145 if (element)
1146 return NULL;
1147 }
1148
1149 element = delpos234(t1, size1-1);
1150 relht = height234(t1) - height234(t2);
1151 t2->root = join234_internal(t1->root, element, t2->root, &relht);
1152 t1->root = NULL;
1153 }
1154 return t2;
1155}
1156
1157/*
1158 * Split out the first <index> elements in a tree and return a
1159 * pointer to the root node. Leave the root node of the remainder
1160 * in t.
1161 */
1162static node234 *split234_internal(tree234 *t, int index) {
1163 node234 *halves[2] = { NULL, NULL }, *n, *sib, *sub;
1164 node234 *lparent, *rparent;
1165 int ki, pki, i, half, lcount, rcount;
1166
1167 n = t->root;
1168 LOG(("splitting tree %p at point %d\n", t, index));
1169
1170 /*
1171 * Easy special cases. After this we have also dealt completely
1172 * with the empty-tree case and we can assume the root exists.
1173 */
1174 if (index == 0) /* return nothing */
1175 return NULL;
1176 if (index == countnode234(t->root)) { /* return the whole tree */
1177 node234 *ret = t->root;
1178 t->root = NULL;
1179 return ret;
1180 }
1181
1182 /*
1183 * Search down the tree to find the split point.
1184 */
1185 halves[0] = halves[1] = NULL;
1186 lparent = rparent = NULL;
1187 pki = -1;
1188 while (n) {
1189 LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d index=%d\n",
1190 n,
1191 n->kids[0], n->counts[0], n->elems[0],
1192 n->kids[1], n->counts[1], n->elems[1],
1193 n->kids[2], n->counts[2], n->elems[2],
1194 n->kids[3], n->counts[3],
1195 index));
1196 lcount = index;
1197 rcount = countnode234(n) - lcount;
1198 if (index <= n->counts[0]) {
1199 ki = 0;
1200 } else if (index -= n->counts[0]+1, index <= n->counts[1]) {
1201 ki = 1;
1202 } else if (index -= n->counts[1]+1, index <= n->counts[2]) {
1203 ki = 2;
1204 } else {
1205 index -= n->counts[2]+1;
1206 ki = 3;
1207 }
1208
1209 LOG((" splitting at subtree %d\n", ki));
1210 sub = n->kids[ki];
1211
1212 LOG((" splitting at child index %d\n", ki));
1213
1214 /*
1215 * Split the node, put halves[0] on the right of the left
1216 * one and halves[1] on the left of the right one, put the
1217 * new node pointers in halves[0] and halves[1], and go up
1218 * a level.
1219 */
1220 sib = snew(node234);
1221 for (i = 0; i < 3; i++) {
1222 if (i+ki < 3 && n->elems[i+ki]) {
1223 sib->elems[i] = n->elems[i+ki];
1224 sib->kids[i+1] = n->kids[i+ki+1];
1225 if (sib->kids[i+1]) sib->kids[i+1]->parent = sib;
1226 sib->counts[i+1] = n->counts[i+ki+1];
1227 n->elems[i+ki] = NULL;
1228 n->kids[i+ki+1] = NULL;
1229 n->counts[i+ki+1] = 0;
1230 } else {
1231 sib->elems[i] = NULL;
1232 sib->kids[i+1] = NULL;
1233 sib->counts[i+1] = 0;
1234 }
1235 }
1236 if (lparent) {
1237 lparent->kids[pki] = n;
1238 lparent->counts[pki] = lcount;
1239 n->parent = lparent;
1240 rparent->kids[0] = sib;
1241 rparent->counts[0] = rcount;
1242 sib->parent = rparent;
1243 } else {
1244 halves[0] = n;
1245 n->parent = NULL;
1246 halves[1] = sib;
1247 sib->parent = NULL;
1248 }
1249 lparent = n;
1250 rparent = sib;
1251 pki = ki;
1252 LOG((" left node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
1253 n,
1254 n->kids[0], n->counts[0], n->elems[0],
1255 n->kids[1], n->counts[1], n->elems[1],
1256 n->kids[2], n->counts[2], n->elems[2],
1257 n->kids[3], n->counts[3]));
1258 LOG((" right node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
1259 sib,
1260 sib->kids[0], sib->counts[0], sib->elems[0],
1261 sib->kids[1], sib->counts[1], sib->elems[1],
1262 sib->kids[2], sib->counts[2], sib->elems[2],
1263 sib->kids[3], sib->counts[3]));
1264
1265 n = sub;
1266 }
1267
1268 /*
1269 * We've come off the bottom here, so we've successfully split
1270 * the tree into two equally high subtrees. The only problem is
1271 * that some of the nodes down the fault line will be smaller
1272 * than the minimum permitted size. (Since this is a 2-3-4
1273 * tree, that means they'll be zero-element one-child nodes.)
1274 */
1275 LOG((" fell off bottom, lroot is %p, rroot is %p\n",
1276 halves[0], halves[1]));
1277 assert(halves[0] != NULL);
1278 assert(halves[1] != NULL);
1279 lparent->counts[pki] = rparent->counts[0] = 0;
1280 lparent->kids[pki] = rparent->kids[0] = NULL;
1281
1282 /*
1283 * So now we go back down the tree from each of the two roots,
1284 * fixing up undersize nodes.
1285 */
1286 for (half = 0; half < 2; half++) {
1287 /*
1288 * Remove the root if it's undersize (it will contain only
1289 * one child pointer, so just throw it away and replace it
1290 * with its child). This might happen several times.
1291 */
1292 while (halves[half] && !halves[half]->elems[0]) {
1293 LOG((" root %p is undersize, throwing away\n", halves[half]));
1294 halves[half] = halves[half]->kids[0];
1295 sfree(halves[half]->parent);
1296 halves[half]->parent = NULL;
1297 LOG((" new root is %p\n", halves[half]));
1298 }
1299
1300 n = halves[half];
1301 while (n) {
1302 void (*toward)(node234 *n, int ki, int *k, int *index);
1303 int ni, merge;
1304
1305 /*
1306 * Now we have a potentially undersize node on the
1307 * right (if half==0) or left (if half==1). Sort it
1308 * out, by merging with a neighbour or by transferring
1309 * subtrees over. At this time we must also ensure that
1310 * nodes are bigger than minimum, in case we need an
1311 * element to merge two nodes below.
1312 */
1313 LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n",
1314 n,
1315 n->kids[0], n->counts[0], n->elems[0],
1316 n->kids[1], n->counts[1], n->elems[1],
1317 n->kids[2], n->counts[2], n->elems[2],
1318 n->kids[3], n->counts[3]));
1319 if (half == 1) {
1320 ki = 0; /* the kid we're interested in */
1321 ni = 1; /* the neighbour */
1322 merge = 0; /* for merge: leftmost of the two */
1323 toward = trans234_subtree_left;
1324 } else {
1325 ki = (n->kids[3] ? 3 : n->kids[2] ? 2 : 1);
1326 ni = ki-1;
1327 merge = ni;
1328 toward = trans234_subtree_right;
1329 }
1330
1331 sub = n->kids[ki];
1332 if (sub && !sub->elems[1]) {
1333 /*
1334 * This node is undersized or minimum-size. If we
1335 * can merge it with its neighbour, we do so;
1336 * otherwise we must be able to transfer subtrees
1337 * over to it until it is greater than minimum
1338 * size.
1339 */
1340 int undersized = (!sub->elems[0]);
1341 LOG((" child %d is %ssize\n", ki,
1342 undersized ? "under" : "minimum-"));
1343 LOG((" neighbour is %s\n",
1344 n->kids[ni]->elems[2] ? "large" :
1345 n->kids[ni]->elems[1] ? "medium" : "small"));
1346 if (!n->kids[ni]->elems[1] ||
1347 (undersized && !n->kids[ni]->elems[2])) {
1348 /*
1349 * Neighbour is small, or possibly neighbour is
1350 * medium and we are undersize.
1351 */
1352 trans234_subtree_merge(n, merge, NULL, NULL);
1353 sub = n->kids[merge];
1354 if (!n->elems[0]) {
1355 /*
1356 * n is empty, and hence must have been the
1357 * root and needs to be removed.
1358 */
1359 assert(!n->parent);
1360 LOG((" shifting root!\n"));
1361 halves[half] = sub;
1362 halves[half]->parent = NULL;
1363 sfree(n);
1364 }
1365 } else {
1366 /* Neighbour is big enough to move trees over. */
1367 toward(n, ni, NULL, NULL);
1368 if (undersized)
1369 toward(n, ni, NULL, NULL);
1370 }
1371 }
1372 n = sub;
1373 }
1374 }
1375
1376 t->root = halves[1];
1377 return halves[0];
1378}
1379tree234 *splitpos234(tree234 *t, int index, int before) {
1380 tree234 *ret;
1381 node234 *n;
1382 int count;
1383
1384 count = countnode234(t->root);
1385 if (index < 0 || index > count)
1386 return NULL; /* error */
1387 ret = newtree234(t->cmp);
1388 n = split234_internal(t, index);
1389 if (before) {
1390 /* We want to return the ones before the index. */
1391 ret->root = n;
1392 } else {
1393 /*
1394 * We want to keep the ones before the index and return the
1395 * ones after.
1396 */
1397 ret->root = t->root;
1398 t->root = n;
1399 }
1400 return ret;
1401}
1402tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel) {
1403 int before;
1404 int index;
1405
1406 assert(rel != REL234_EQ);
1407
1408 if (rel == REL234_GT || rel == REL234_GE) {
1409 before = 1;
1410 rel = (rel == REL234_GT ? REL234_LE : REL234_LT);
1411 } else {
1412 before = 0;
1413 }
1414 if (!findrelpos234(t, e, cmp, rel, &index))
1415 index = 0;
1416
1417 return splitpos234(t, index+1, before);
1418}
1419
1420static node234 *copynode234(node234 *n, copyfn234 copyfn, void *copyfnstate) {
1421 int i;
1422 node234 *n2 = snew(node234);
1423
1424 for (i = 0; i < 3; i++) {
1425 if (n->elems[i] && copyfn)
1426 n2->elems[i] = copyfn(copyfnstate, n->elems[i]);
1427 else
1428 n2->elems[i] = n->elems[i];
1429 }
1430
1431 for (i = 0; i < 4; i++) {
1432 if (n->kids[i]) {
1433 n2->kids[i] = copynode234(n->kids[i], copyfn, copyfnstate);
1434 n2->kids[i]->parent = n2;
1435 } else {
1436 n2->kids[i] = NULL;
1437 }
1438 n2->counts[i] = n->counts[i];
1439 }
1440
1441 return n2;
1442}
1443tree234 *copytree234(tree234 *t, copyfn234 copyfn, void *copyfnstate) {
1444 tree234 *t2;
1445
1446 t2 = newtree234(t->cmp);
1447 if (t->root) {
1448 t2->root = copynode234(t->root, copyfn, copyfnstate);
1449 t2->root->parent = NULL;
1450 } else
1451 t2->root = NULL;
1452
1453 return t2;
1454}
1455
1456#ifdef TEST
1457
1458/*
1459 * Test code for the 2-3-4 tree. This code maintains an alternative
1460 * representation of the data in the tree, in an array (using the
1461 * obvious and slow insert and delete functions). After each tree
1462 * operation, the verify() function is called, which ensures all
1463 * the tree properties are preserved:
1464 * - node->child->parent always equals node
1465 * - tree->root->parent always equals NULL
1466 * - number of kids == 0 or number of elements + 1;
1467 * - tree has the same depth everywhere
1468 * - every node has at least one element
1469 * - subtree element counts are accurate
1470 * - any NULL kid pointer is accompanied by a zero count
1471 * - in a sorted tree: ordering property between elements of a
1472 * node and elements of its children is preserved
1473 * and also ensures the list represented by the tree is the same
1474 * list it should be. (This last check also doubly verifies the
1475 * ordering properties, because the `same list it should be' is by
1476 * definition correctly ordered. It also ensures all nodes are
1477 * distinct, because the enum functions would get caught in a loop
1478 * if not.)
1479 */
1480
1481#include <string.h>
1482#include <stdarg.h>
1483
1484#define srealloc realloc
1485
1486/*
1487 * Error reporting function.
1488 */
1489void error(char *fmt, ...) {
1490 va_list ap;
1491 printf("ERROR: ");
1492 va_start(ap, fmt);
1493 vfprintf(stdout, fmt, ap);
1494 va_end(ap);
1495 printf("\n");
1496}
1497
1498/* The array representation of the data. */
1499void **array;
1500int arraylen, arraysize;
1501cmpfn234 cmp;
1502
1503/* The tree representation of the same data. */
1504tree234 *tree;
1505
1506/*
1507 * Routines to provide a diagnostic printout of a tree. Currently
1508 * relies on every element in the tree being a one-character string
1509 * :-)
1510 */
1511typedef struct {
1512 char **levels;
1513} dispctx;
1514
1515int dispnode(node234 *n, int level, dispctx *ctx) {
1516 if (level == 0) {
1517 int xpos = strlen(ctx->levels[0]);
1518 int len;
1519
1520 if (n->elems[2])
1521 len = sprintf(ctx->levels[0]+xpos, " %s%s%s",
1522 n->elems[0], n->elems[1], n->elems[2]);
1523 else if (n->elems[1])
1524 len = sprintf(ctx->levels[0]+xpos, " %s%s",
1525 n->elems[0], n->elems[1]);
1526 else
1527 len = sprintf(ctx->levels[0]+xpos, " %s",
1528 n->elems[0]);
1529 return xpos + 1 + (len-1) / 2;
1530 } else {
1531 int xpos[4], nkids;
1532 int nodelen, mypos, myleft, x, i;
1533
1534 xpos[0] = dispnode(n->kids[0], level-3, ctx);
1535 xpos[1] = dispnode(n->kids[1], level-3, ctx);
1536 nkids = 2;
1537 if (n->kids[2]) {
1538 xpos[2] = dispnode(n->kids[2], level-3, ctx);
1539 nkids = 3;
1540 }
1541 if (n->kids[3]) {
1542 xpos[3] = dispnode(n->kids[3], level-3, ctx);
1543 nkids = 4;
1544 }
1545
1546 if (nkids == 4)
1547 mypos = (xpos[1] + xpos[2]) / 2;
1548 else if (nkids == 3)
1549 mypos = xpos[1];
1550 else
1551 mypos = (xpos[0] + xpos[1]) / 2;
1552 nodelen = nkids * 2 - 1;
1553 myleft = mypos - ((nodelen-1)/2);
1554 assert(myleft >= xpos[0]);
1555 assert(myleft + nodelen-1 <= xpos[nkids-1]);
1556
1557 x = strlen(ctx->levels[level]);
1558 while (x <= xpos[0] && x < myleft)
1559 ctx->levels[level][x++] = ' ';
1560 while (x < myleft)
1561 ctx->levels[level][x++] = '_';
1562 if (nkids==4)
1563 x += sprintf(ctx->levels[level]+x, ".%s.%s.%s.",
1564 n->elems[0], n->elems[1], n->elems[2]);
1565 else if (nkids==3)
1566 x += sprintf(ctx->levels[level]+x, ".%s.%s.",
1567 n->elems[0], n->elems[1]);
1568 else
1569 x += sprintf(ctx->levels[level]+x, ".%s.",
1570 n->elems[0]);
1571 while (x < xpos[nkids-1])
1572 ctx->levels[level][x++] = '_';
1573 ctx->levels[level][x] = '\0';
1574
1575 x = strlen(ctx->levels[level-1]);
1576 for (i = 0; i < nkids; i++) {
1577 int rpos, pos;
1578 rpos = xpos[i];
1579 if (i > 0 && i < nkids-1)
1580 pos = myleft + 2*i;
1581 else
1582 pos = rpos;
1583 if (rpos < pos)
1584 rpos++;
1585 while (x < pos && x < rpos)
1586 ctx->levels[level-1][x++] = ' ';
1587 if (x == pos)
1588 ctx->levels[level-1][x++] = '|';
1589 while (x < pos || x < rpos)
1590 ctx->levels[level-1][x++] = '_';
1591 if (x == pos)
1592 ctx->levels[level-1][x++] = '|';
1593 }
1594 ctx->levels[level-1][x] = '\0';
1595
1596 x = strlen(ctx->levels[level-2]);
1597 for (i = 0; i < nkids; i++) {
1598 int rpos = xpos[i];
1599
1600 while (x < rpos)
1601 ctx->levels[level-2][x++] = ' ';
1602 ctx->levels[level-2][x++] = '|';
1603 }
1604 ctx->levels[level-2][x] = '\0';
1605
1606 return mypos;
1607 }
1608}
1609
1610void disptree(tree234 *t) {
1611 dispctx ctx;
1612 char *leveldata;
1613 int width = count234(t);
1614 int ht = height234(t) * 3 - 2;
1615 int i;
1616
1617 if (!t->root) {
1618 printf("[empty tree]\n");
1619 }
1620
1621 leveldata = smalloc(ht * (width+2));
1622 ctx.levels = smalloc(ht * sizeof(char *));
1623 for (i = 0; i < ht; i++) {
1624 ctx.levels[i] = leveldata + i * (width+2);
1625 ctx.levels[i][0] = '\0';
1626 }
1627
1628 (void) dispnode(t->root, ht-1, &ctx);
1629
1630 for (i = ht; i-- ;)
1631 printf("%s\n", ctx.levels[i]);
1632
1633 sfree(ctx.levels);
1634 sfree(leveldata);
1635}
1636
1637typedef struct {
1638 int treedepth;
1639 int elemcount;
1640} chkctx;
1641
1642int chknode(chkctx *ctx, int level, node234 *node,
1643 void *lowbound, void *highbound) {
1644 int nkids, nelems;
1645 int i;
1646 int count;
1647
1648 /* Count the non-NULL kids. */
1649 for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++);
1650 /* Ensure no kids beyond the first NULL are non-NULL. */
1651 for (i = nkids; i < 4; i++)
1652 if (node->kids[i]) {
1653 error("node %p: nkids=%d but kids[%d] non-NULL",
1654 node, nkids, i);
1655 } else if (node->counts[i]) {
1656 error("node %p: kids[%d] NULL but count[%d]=%d nonzero",
1657 node, i, i, node->counts[i]);
1658 }
1659
1660 /* Count the non-NULL elements. */
1661 for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++);
1662 /* Ensure no elements beyond the first NULL are non-NULL. */
1663 for (i = nelems; i < 3; i++)
1664 if (node->elems[i]) {
1665 error("node %p: nelems=%d but elems[%d] non-NULL",
1666 node, nelems, i);
1667 }
1668
1669 if (nkids == 0) {
1670 /*
1671 * If nkids==0, this is a leaf node; verify that the tree
1672 * depth is the same everywhere.
1673 */
1674 if (ctx->treedepth < 0)
1675 ctx->treedepth = level; /* we didn't know the depth yet */
1676 else if (ctx->treedepth != level)
1677 error("node %p: leaf at depth %d, previously seen depth %d",
1678 node, level, ctx->treedepth);
1679 } else {
1680 /*
1681 * If nkids != 0, then it should be nelems+1, unless nelems
1682 * is 0 in which case nkids should also be 0 (and so we
1683 * shouldn't be in this condition at all).
1684 */
1685 int shouldkids = (nelems ? nelems+1 : 0);
1686 if (nkids != shouldkids) {
1687 error("node %p: %d elems should mean %d kids but has %d",
1688 node, nelems, shouldkids, nkids);
1689 }
1690 }
1691
1692 /*
1693 * nelems should be at least 1.
1694 */
1695 if (nelems == 0) {
1696 error("node %p: no elems", node, nkids);
1697 }
1698
1699 /*
1700 * Add nelems to the running element count of the whole tree.
1701 */
1702 ctx->elemcount += nelems;
1703
1704 /*
1705 * Check ordering property: all elements should be strictly >
1706 * lowbound, strictly < highbound, and strictly < each other in
1707 * sequence. (lowbound and highbound are NULL at edges of tree
1708 * - both NULL at root node - and NULL is considered to be <
1709 * everything and > everything. IYSWIM.)
1710 */
1711 if (cmp) {
1712 for (i = -1; i < nelems; i++) {
1713 void *lower = (i == -1 ? lowbound : node->elems[i]);
1714 void *higher = (i+1 == nelems ? highbound : node->elems[i+1]);
1715 if (lower && higher && cmp(lower, higher) >= 0) {
1716 error("node %p: kid comparison [%d=%s,%d=%s] failed",
1717 node, i, lower, i+1, higher);
1718 }
1719 }
1720 }
1721
1722 /*
1723 * Check parent pointers: all non-NULL kids should have a
1724 * parent pointer coming back to this node.
1725 */
1726 for (i = 0; i < nkids; i++)
1727 if (node->kids[i]->parent != node) {
1728 error("node %p kid %d: parent ptr is %p not %p",
1729 node, i, node->kids[i]->parent, node);
1730 }
1731
1732
1733 /*
1734 * Now (finally!) recurse into subtrees.
1735 */
1736 count = nelems;
1737
1738 for (i = 0; i < nkids; i++) {
1739 void *lower = (i == 0 ? lowbound : node->elems[i-1]);
1740 void *higher = (i >= nelems ? highbound : node->elems[i]);
1741 int subcount = chknode(ctx, level+1, node->kids[i], lower, higher);
1742 if (node->counts[i] != subcount) {
1743 error("node %p kid %d: count says %d, subtree really has %d",
1744 node, i, node->counts[i], subcount);
1745 }
1746 count += subcount;
1747 }
1748
1749 return count;
1750}
1751
1752void verifytree(tree234 *tree, void **array, int arraylen) {
1753 chkctx ctx;
1754 int i;
1755 void *p;
1756
1757 ctx.treedepth = -1; /* depth unknown yet */
1758 ctx.elemcount = 0; /* no elements seen yet */
1759 /*
1760 * Verify validity of tree properties.
1761 */
1762 if (tree->root) {
1763 if (tree->root->parent != NULL)
1764 error("root->parent is %p should be null", tree->root->parent);
1765 chknode(&ctx, 0, tree->root, NULL, NULL);
1766 }
1767 printf("tree depth: %d\n", ctx.treedepth);
1768 /*
1769 * Enumerate the tree and ensure it matches up to the array.
1770 */
1771 for (i = 0; NULL != (p = index234(tree, i)); i++) {
1772 if (i >= arraylen)
1773 error("tree contains more than %d elements", arraylen);
1774 if (array[i] != p)
1775 error("enum at position %d: array says %s, tree says %s",
1776 i, array[i], p);
1777 }
1778 if (ctx.elemcount != i) {
1779 error("tree really contains %d elements, enum gave %d",
1780 ctx.elemcount, i);
1781 }
1782 if (i < arraylen) {
1783 error("enum gave only %d elements, array has %d", i, arraylen);
1784 }
1785 i = count234(tree);
1786 if (ctx.elemcount != i) {
1787 error("tree really contains %d elements, count234 gave %d",
1788 ctx.elemcount, i);
1789 }
1790}
1791void verify(void) { verifytree(tree, array, arraylen); }
1792
1793void internal_addtest(void *elem, int index, void *realret) {
1794 int i, j;
1795 void *retval;
1796
1797 if (arraysize < arraylen+1) {
1798 arraysize = arraylen+1+256;
1799 array = (array == NULL ? smalloc(arraysize*sizeof(*array)) :
1800 srealloc(array, arraysize*sizeof(*array)));
1801 }
1802
1803 i = index;
1804 /* now i points to the first element >= elem */
1805 retval = elem; /* expect elem returned (success) */
1806 for (j = arraylen; j > i; j--)
1807 array[j] = array[j-1];
1808 array[i] = elem; /* add elem to array */
1809 arraylen++;
1810
1811 if (realret != retval) {
1812 error("add: retval was %p expected %p", realret, retval);
1813 }
1814
1815 verify();
1816}
1817
1818void addtest(void *elem) {
1819 int i;
1820 void *realret;
1821
1822 realret = add234(tree, elem);
1823
1824 i = 0;
1825 while (i < arraylen && cmp(elem, array[i]) > 0)
1826 i++;
1827 if (i < arraylen && !cmp(elem, array[i])) {
1828 void *retval = array[i]; /* expect that returned not elem */
1829 if (realret != retval) {
1830 error("add: retval was %p expected %p", realret, retval);
1831 }
1832 } else
1833 internal_addtest(elem, i, realret);
1834}
1835
1836void addpostest(void *elem, int i) {
1837 void *realret;
1838
1839 realret = addpos234(tree, elem, i);
1840
1841 internal_addtest(elem, i, realret);
1842}
1843
1844void delpostest(int i) {
1845 int index = i;
1846 void *elem = array[i], *ret;
1847
1848 /* i points to the right element */
1849 while (i < arraylen-1) {
1850 array[i] = array[i+1];
1851 i++;
1852 }
1853 arraylen--; /* delete elem from array */
1854
1855 if (tree->cmp)
1856 ret = del234(tree, elem);
1857 else
1858 ret = delpos234(tree, index);
1859
1860 if (ret != elem) {
1861 error("del returned %p, expected %p", ret, elem);
1862 }
1863
1864 verify();
1865}
1866
1867void deltest(void *elem) {
1868 int i;
1869
1870 i = 0;
1871 while (i < arraylen && cmp(elem, array[i]) > 0)
1872 i++;
1873 if (i >= arraylen || cmp(elem, array[i]) != 0)
1874 return; /* don't do it! */
1875 delpostest(i);
1876}
1877
1878/* A sample data set and test utility. Designed for pseudo-randomness,
1879 * and yet repeatability. */
1880
1881/*
1882 * This random number generator uses the `portable implementation'
1883 * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits;
1884 * change it if not.
1885 */
1886int randomnumber(unsigned *seed) {
1887 *seed *= 1103515245;
1888 *seed += 12345;
1889 return ((*seed) / 65536) % 32768;
1890}
1891
1892int mycmp(void *av, void *bv) {
1893 char const *a = (char const *)av;
1894 char const *b = (char const *)bv;
1895 return strcmp(a, b);
1896}
1897
1898char *strings[] = {
1899 "0", "2", "3", "I", "K", "d", "H", "J", "Q", "N", "n", "q", "j", "i",
1900 "7", "G", "F", "D", "b", "x", "g", "B", "e", "v", "V", "T", "f", "E",
1901 "S", "8", "A", "k", "X", "p", "C", "R", "a", "o", "r", "O", "Z", "u",
1902 "6", "1", "w", "L", "P", "M", "c", "U", "h", "9", "t", "5", "W", "Y",
1903 "m", "s", "l", "4",
1904#if 0
1905 "a", "ab", "absque", "coram", "de",
1906 "palam", "clam", "cum", "ex", "e",
1907 "sine", "tenus", "pro", "prae",
1908 "banana", "carrot", "cabbage", "broccoli", "onion", "zebra",
1909 "penguin", "blancmange", "pangolin", "whale", "hedgehog",
1910 "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux",
1911 "murfl", "spoo", "breen", "flarn", "octothorpe",
1912 "snail", "tiger", "elephant", "octopus", "warthog", "armadillo",
1913 "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin",
1914 "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper",
1915 "wand", "ring", "amulet"
1916#endif
1917};
1918
1919#define NSTR lenof(strings)
1920
1921void findtest(void) {
1922 static const int rels[] = {
1923 REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT
1924 };
1925 static const char *const relnames[] = {
1926 "EQ", "GE", "LE", "LT", "GT"
1927 };
1928 int i, j, rel, index;
1929 char *p, *ret, *realret, *realret2;
1930 int lo, hi, mid, c;
1931
1932 for (i = 0; i < (int)NSTR; i++) {
1933 p = strings[i];
1934 for (j = 0; j < (int)(sizeof(rels)/sizeof(*rels)); j++) {
1935 rel = rels[j];
1936
1937 lo = 0; hi = arraylen-1;
1938 while (lo <= hi) {
1939 mid = (lo + hi) / 2;
1940 c = strcmp(p, array[mid]);
1941 if (c < 0)
1942 hi = mid-1;
1943 else if (c > 0)
1944 lo = mid+1;
1945 else
1946 break;
1947 }
1948
1949 if (c == 0) {
1950 if (rel == REL234_LT)
1951 ret = (mid > 0 ? array[--mid] : NULL);
1952 else if (rel == REL234_GT)
1953 ret = (mid < arraylen-1 ? array[++mid] : NULL);
1954 else
1955 ret = array[mid];
1956 } else {
1957 assert(lo == hi+1);
1958 if (rel == REL234_LT || rel == REL234_LE) {
1959 mid = hi;
1960 ret = (hi >= 0 ? array[hi] : NULL);
1961 } else if (rel == REL234_GT || rel == REL234_GE) {
1962 mid = lo;
1963 ret = (lo < arraylen ? array[lo] : NULL);
1964 } else
1965 ret = NULL;
1966 }
1967
1968 realret = findrelpos234(tree, p, NULL, rel, &index);
1969 if (realret != ret) {
1970 error("find(\"%s\",%s) gave %s should be %s",
1971 p, relnames[j], realret, ret);
1972 }
1973 if (realret && index != mid) {
1974 error("find(\"%s\",%s) gave %d should be %d",
1975 p, relnames[j], index, mid);
1976 }
1977 if (realret && rel == REL234_EQ) {
1978 realret2 = index234(tree, index);
1979 if (realret2 != realret) {
1980 error("find(\"%s\",%s) gave %s(%d) but %d -> %s",
1981 p, relnames[j], realret, index, index, realret2);
1982 }
1983 }
1984#if 0
1985 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j],
1986 realret, index);
1987#endif
1988 }
1989 }
1990
1991 realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index);
1992 if (arraylen && (realret != array[0] || index != 0)) {
1993 error("find(NULL,GT) gave %s(%d) should be %s(0)",
1994 realret, index, array[0]);
1995 } else if (!arraylen && (realret != NULL)) {
1996 error("find(NULL,GT) gave %s(%d) should be NULL",
1997 realret, index);
1998 }
1999
2000 realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index);
2001 if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) {
2002 error("find(NULL,LT) gave %s(%d) should be %s(0)",
2003 realret, index, array[arraylen-1]);
2004 } else if (!arraylen && (realret != NULL)) {
2005 error("find(NULL,LT) gave %s(%d) should be NULL",
2006 realret, index);
2007 }
2008}
2009
2010void splittest(tree234 *tree, void **array, int arraylen) {
2011 int i;
2012 tree234 *tree3, *tree4;
2013 for (i = 0; i <= arraylen; i++) {
2014 tree3 = copytree234(tree, NULL, NULL);
2015 tree4 = splitpos234(tree3, i, 0);
2016 verifytree(tree3, array, i);
2017 verifytree(tree4, array+i, arraylen-i);
2018 join234(tree3, tree4);
2019 freetree234(tree4); /* left empty by join */
2020 verifytree(tree3, array, arraylen);
2021 freetree234(tree3);
2022 }
2023}
2024
2025int main(void) {
2026 int in[NSTR];
2027 int i, j, k;
2028 int tworoot, tmplen;
2029 unsigned seed = 0;
2030 tree234 *tree2, *tree3, *tree4;
2031 int c;
2032
2033 setvbuf(stdout, NULL, _IOLBF, 0);
2034
2035 for (i = 0; i < (int)NSTR; i++) in[i] = 0;
2036 array = NULL;
2037 arraylen = arraysize = 0;
2038 tree = newtree234(mycmp);
2039 cmp = mycmp;
2040
2041 verify();
2042 for (i = 0; i < 10000; i++) {
2043 j = randomnumber(&seed);
2044 j %= NSTR;
2045 printf("trial: %d\n", i);
2046 if (in[j]) {
2047 printf("deleting %s (%d)\n", strings[j], j);
2048 deltest(strings[j]);
2049 in[j] = 0;
2050 } else {
2051 printf("adding %s (%d)\n", strings[j], j);
2052 addtest(strings[j]);
2053 in[j] = 1;
2054 }
2055 disptree(tree);
2056 findtest();
2057 }
2058
2059 while (arraylen > 0) {
2060 j = randomnumber(&seed);
2061 j %= arraylen;
2062 deltest(array[j]);
2063 }
2064
2065 freetree234(tree);
2066
2067 /*
2068 * Now try an unsorted tree. We don't really need to test
2069 * delpos234 because we know del234 is based on it, so it's
2070 * already been tested in the above sorted-tree code; but for
2071 * completeness we'll use it to tear down our unsorted tree
2072 * once we've built it.
2073 */
2074 tree = newtree234(NULL);
2075 cmp = NULL;
2076 verify();
2077 for (i = 0; i < 1000; i++) {
2078 printf("trial: %d\n", i);
2079 j = randomnumber(&seed);
2080 j %= NSTR;
2081 k = randomnumber(&seed);
2082 k %= count234(tree)+1;
2083 printf("adding string %s at index %d\n", strings[j], k);
2084 addpostest(strings[j], k);
2085 }
2086
2087 /*
2088 * While we have this tree in its full form, we'll take a copy
2089 * of it to use in split and join testing.
2090 */
2091 tree2 = copytree234(tree, NULL, NULL);
2092 verifytree(tree2, array, arraylen);/* check the copy is accurate */
2093 /*
2094 * Split tests. Split the tree at every possible point and
2095 * check the resulting subtrees.
2096 */
2097 tworoot = (!tree2->root->elems[1]);/* see if it has a 2-root */
2098 splittest(tree2, array, arraylen);
2099 /*
2100 * Now do the split test again, but on a tree that has a 2-root
2101 * (if the previous one didn't) or doesn't (if the previous one
2102 * did).
2103 */
2104 tmplen = arraylen;
2105 while ((!tree2->root->elems[1]) == tworoot) {
2106 delpos234(tree2, --tmplen);
2107 }
2108 printf("now trying splits on second tree\n");
2109 splittest(tree2, array, tmplen);
2110 freetree234(tree2);
2111
2112 /*
2113 * Back to the main testing of uncounted trees.
2114 */
2115 while (count234(tree) > 0) {
2116 printf("cleanup: tree size %d\n", count234(tree));
2117 j = randomnumber(&seed);
2118 j %= count234(tree);
2119 printf("deleting string %s from index %d\n", (char *)array[j], j);
2120 delpostest(j);
2121 }
2122 freetree234(tree);
2123
2124 /*
2125 * Finally, do some testing on split/join on _sorted_ trees. At
2126 * the same time, we'll be testing split on very small trees.
2127 */
2128 tree = newtree234(mycmp);
2129 cmp = mycmp;
2130 arraylen = 0;
2131 for (i = 0; i < 17; i++) {
2132 tree2 = copytree234(tree, NULL, NULL);
2133 splittest(tree2, array, arraylen);
2134 freetree234(tree2);
2135 if (i < 16)
2136 addtest(strings[i]);
2137 }
2138 freetree234(tree);
2139
2140 /*
2141 * Test silly cases of join: join(emptytree, emptytree), and
2142 * also ensure join correctly spots when sorted trees fail the
2143 * ordering constraint.
2144 */
2145 tree = newtree234(mycmp);
2146 tree2 = newtree234(mycmp);
2147 tree3 = newtree234(mycmp);
2148 tree4 = newtree234(mycmp);
2149 assert(mycmp(strings[0], strings[1]) < 0); /* just in case :-) */
2150 add234(tree2, strings[1]);
2151 add234(tree4, strings[0]);
2152 array[0] = strings[0];
2153 array[1] = strings[1];
2154 verifytree(tree, array, 0);
2155 verifytree(tree2, array+1, 1);
2156 verifytree(tree3, array, 0);
2157 verifytree(tree4, array, 1);
2158
2159 /*
2160 * So:
2161 * - join(tree,tree3) should leave both tree and tree3 unchanged.
2162 * - joinr(tree,tree2) should leave both tree and tree2 unchanged.
2163 * - join(tree4,tree3) should leave both tree3 and tree4 unchanged.
2164 * - join(tree, tree2) should move the element from tree2 to tree.
2165 * - joinr(tree4, tree3) should move the element from tree4 to tree3.
2166 * - join(tree,tree3) should return NULL and leave both unchanged.
2167 * - join(tree3,tree) should work and create a bigger tree in tree3.
2168 */
2169 assert(tree == join234(tree, tree3));
2170 verifytree(tree, array, 0);
2171 verifytree(tree3, array, 0);
2172 assert(tree2 == join234r(tree, tree2));
2173 verifytree(tree, array, 0);
2174 verifytree(tree2, array+1, 1);
2175 assert(tree4 == join234(tree4, tree3));
2176 verifytree(tree3, array, 0);
2177 verifytree(tree4, array, 1);
2178 assert(tree == join234(tree, tree2));
2179 verifytree(tree, array+1, 1);
2180 verifytree(tree2, array, 0);
2181 assert(tree3 == join234r(tree4, tree3));
2182 verifytree(tree3, array, 1);
2183 verifytree(tree4, array, 0);
2184 assert(NULL == join234(tree, tree3));
2185 verifytree(tree, array+1, 1);
2186 verifytree(tree3, array, 1);
2187 assert(tree3 == join234(tree3, tree));
2188 verifytree(tree3, array, 2);
2189 verifytree(tree, array, 0);
2190
2191 return 0;
2192}
2193
2194#endif
2195
2196#if 0 /* sorted list of strings might be useful */
2197{
2198 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
2199}
2200#endif
diff --git a/apps/plugins/puzzles/src/tree234.h b/apps/plugins/puzzles/src/tree234.h
new file mode 100644
index 0000000000..f75c8f7fb3
--- /dev/null
+++ b/apps/plugins/puzzles/src/tree234.h
@@ -0,0 +1,202 @@
1/*
2 * tree234.h: header defining functions in tree234.c.
3 *
4 * This file is copyright 1999-2001 Simon Tatham.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following
13 * conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28#ifndef TREE234_H
29#define TREE234_H
30
31/*
32 * This typedef is opaque outside tree234.c itself.
33 */
34typedef struct tree234_Tag tree234;
35
36typedef int (*cmpfn234)(void *, void *);
37
38typedef void *(*copyfn234)(void *state, void *element);
39
40/*
41 * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and
42 * lookups by key will fail: you can only look things up by numeric
43 * index, and you have to use addpos234() and delpos234().
44 */
45tree234 *newtree234(cmpfn234 cmp);
46
47/*
48 * Free a 2-3-4 tree (not including freeing the elements).
49 */
50void freetree234(tree234 *t);
51
52/*
53 * Add an element e to a sorted 2-3-4 tree t. Returns e on success,
54 * or if an existing element compares equal, returns that.
55 */
56void *add234(tree234 *t, void *e);
57
58/*
59 * Add an element e to an unsorted 2-3-4 tree t. Returns e on
60 * success, NULL on failure. (Failure should only occur if the
61 * index is out of range or the tree is sorted.)
62 *
63 * Index range can be from 0 to the tree's current element count,
64 * inclusive.
65 */
66void *addpos234(tree234 *t, void *e, int index);
67
68/*
69 * Look up the element at a given numeric index in a 2-3-4 tree.
70 * Returns NULL if the index is out of range.
71 *
72 * One obvious use for this function is in iterating over the whole
73 * of a tree (sorted or unsorted):
74 *
75 * for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p);
76 *
77 * or
78 *
79 * int maxcount = count234(tree);
80 * for (i = 0; i < maxcount; i++) {
81 * p = index234(tree, i);
82 * assert(p != NULL);
83 * consume(p);
84 * }
85 */
86void *index234(tree234 *t, int index);
87
88/*
89 * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not
90 * found. e is always passed as the first argument to cmp, so cmp
91 * can be an asymmetric function if desired. cmp can also be passed
92 * as NULL, in which case the compare function from the tree proper
93 * will be used.
94 *
95 * Three of these functions are special cases of findrelpos234. The
96 * non-`pos' variants lack the `index' parameter: if the parameter
97 * is present and non-NULL, it must point to an integer variable
98 * which will be filled with the numeric index of the returned
99 * element.
100 *
101 * The non-`rel' variants lack the `relation' parameter. This
102 * parameter allows you to specify what relation the element you
103 * provide has to the element you're looking for. This parameter
104 * can be:
105 *
106 * REL234_EQ - find only an element that compares equal to e
107 * REL234_LT - find the greatest element that compares < e
108 * REL234_LE - find the greatest element that compares <= e
109 * REL234_GT - find the smallest element that compares > e
110 * REL234_GE - find the smallest element that compares >= e
111 *
112 * Non-`rel' variants assume REL234_EQ.
113 *
114 * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be
115 * NULL. In this case, REL234_GT will return the smallest element
116 * in the tree, and REL234_LT will return the greatest. This gives
117 * an alternative means of iterating over a sorted tree, instead of
118 * using index234:
119 *
120 * // to loop forwards
121 * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;)
122 * consume(p);
123 *
124 * // to loop backwards
125 * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;)
126 * consume(p);
127 */
128enum {
129 REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE
130};
131void *find234(tree234 *t, void *e, cmpfn234 cmp);
132void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation);
133void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index);
134void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation,
135 int *index);
136
137/*
138 * Delete an element e in a 2-3-4 tree. Does not free the element,
139 * merely removes all links to it from the tree nodes.
140 *
141 * delpos234 deletes the element at a particular tree index: it
142 * works on both sorted and unsorted trees.
143 *
144 * del234 deletes the element passed to it, so it only works on
145 * sorted trees. (It's equivalent to using findpos234 to determine
146 * the index of an element, and then passing that index to
147 * delpos234.)
148 *
149 * Both functions return a pointer to the element they delete, for
150 * the user to free or pass on elsewhere or whatever. If the index
151 * is out of range (delpos234) or the element is already not in the
152 * tree (del234) then they return NULL.
153 */
154void *del234(tree234 *t, void *e);
155void *delpos234(tree234 *t, int index);
156
157/*
158 * Return the total element count of a tree234.
159 */
160int count234(tree234 *t);
161
162/*
163 * Split a tree234 into two valid tree234s.
164 *
165 * splitpos234 splits at a given index. If `before' is TRUE, the
166 * items at and after that index are left in t and the ones before
167 * are returned; if `before' is FALSE, the items before that index
168 * are left in t and the rest are returned.
169 *
170 * split234 splits at a given key. You can pass any of the
171 * relations used with findrel234, except for REL234_EQ. The items
172 * in the tree that satisfy the relation are returned; the
173 * remainder are left.
174 */
175tree234 *splitpos234(tree234 *t, int index, int before);
176tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel);
177
178/*
179 * Join two tree234s together into a single one.
180 *
181 * All the elements in t1 are placed to the left of all the
182 * elements in t2. If the trees are sorted, there will be a test to
183 * ensure that this satisfies the ordering criterion, and NULL will
184 * be returned otherwise. If the trees are unsorted, there is no
185 * restriction on the use of join234.
186 *
187 * The tree returned is t1 (join234) or t2 (join234r), if the
188 * operation is successful.
189 */
190tree234 *join234(tree234 *t1, tree234 *t2);
191tree234 *join234r(tree234 *t1, tree234 *t2);
192
193/*
194 * Make a complete copy of a tree234. Element pointers will be
195 * reused unless copyfn is non-NULL, in which case it will be used
196 * to copy each element. (copyfn takes two `void *' parameters; the
197 * first is private state and the second is the element. A simple
198 * copy routine probably won't need private state.)
199 */
200tree234 *copytree234(tree234 *t, copyfn234 copyfn, void *copyfnstate);
201
202#endif /* TREE234_H */
diff --git a/apps/plugins/puzzles/src/twiddle.R b/apps/plugins/puzzles/src/twiddle.R
new file mode 100644
index 0000000000..1495c33181
--- /dev/null
+++ b/apps/plugins/puzzles/src/twiddle.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3twiddle : [X] GTK COMMON twiddle twiddle-icon|no-icon
4
5twiddle : [G] WINDOWS COMMON twiddle twiddle.res|noicon.res
6
7ALL += twiddle[COMBINED]
8
9!begin am gtk
10GAMES += twiddle
11!end
12
13!begin >list.c
14 A(twiddle) \
15!end
16
17!begin >gamedesc.txt
18twiddle:twiddle.exe:Twiddle:Rotational sliding block puzzle:Rotate the tiles around themselves to arrange them into order.
19!end
diff --git a/apps/plugins/puzzles/src/twiddle.c b/apps/plugins/puzzles/src/twiddle.c
new file mode 100644
index 0000000000..6e05f4ddec
--- /dev/null
+++ b/apps/plugins/puzzles/src/twiddle.c
@@ -0,0 +1,1319 @@
1/*
2 * twiddle.c: Puzzle involving rearranging a grid of squares by
3 * rotating subsquares. Adapted and generalised from a
4 * door-unlocking puzzle in Metroid Prime 2 (the one in the Main
5 * Gyro Chamber).
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <assert.h>
12#include <ctype.h>
13#include <math.h>
14
15#include "puzzles.h"
16
17#define PREFERRED_TILE_SIZE 48
18#define TILE_SIZE (ds->tilesize)
19#define BORDER (TILE_SIZE / 2)
20#define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
21#define COORD(x) ( (x) * TILE_SIZE + BORDER )
22#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
23
24#define ANIM_PER_BLKSIZE_UNIT 0.13F
25#define FLASH_FRAME 0.13F
26
27enum {
28 COL_BACKGROUND,
29 COL_TEXT,
30 COL_HIGHLIGHT,
31 COL_HIGHLIGHT_GENTLE,
32 COL_LOWLIGHT,
33 COL_LOWLIGHT_GENTLE,
34 COL_HIGHCURSOR, COL_LOWCURSOR,
35 NCOLOURS
36};
37
38struct game_params {
39 int w, h, n;
40 int rowsonly;
41 int orientable;
42 int movetarget;
43};
44
45struct game_state {
46 int w, h, n;
47 int orientable;
48 int *grid;
49 int completed;
50 int used_solve; /* used to suppress completion flash */
51 int movecount, movetarget;
52 int lastx, lasty, lastr; /* coordinates of last rotation */
53};
54
55static game_params *default_params(void)
56{
57 game_params *ret = snew(game_params);
58
59 ret->w = ret->h = 3;
60 ret->n = 2;
61 ret->rowsonly = ret->orientable = FALSE;
62 ret->movetarget = 0;
63
64 return ret;
65}
66
67
68static void free_params(game_params *params)
69{
70 sfree(params);
71}
72
73static game_params *dup_params(const game_params *params)
74{
75 game_params *ret = snew(game_params);
76 *ret = *params; /* structure copy */
77 return ret;
78}
79
80static int game_fetch_preset(int i, char **name, game_params **params)
81{
82 static struct {
83 char *title;
84 game_params params;
85 } presets[] = {
86 { "3x3 rows only", { 3, 3, 2, TRUE, FALSE } },
87 { "3x3 normal", { 3, 3, 2, FALSE, FALSE } },
88 { "3x3 orientable", { 3, 3, 2, FALSE, TRUE } },
89 { "4x4 normal", { 4, 4, 2, FALSE } },
90 { "4x4 orientable", { 4, 4, 2, FALSE, TRUE } },
91 { "4x4, rotating 3x3 blocks", { 4, 4, 3, FALSE } },
92 { "5x5, rotating 3x3 blocks", { 5, 5, 3, FALSE } },
93 { "6x6, rotating 4x4 blocks", { 6, 6, 4, FALSE } },
94 };
95
96 if (i < 0 || i >= lenof(presets))
97 return FALSE;
98
99 *name = dupstr(presets[i].title);
100 *params = dup_params(&presets[i].params);
101
102 return TRUE;
103}
104
105static void decode_params(game_params *ret, char const *string)
106{
107 ret->w = ret->h = atoi(string);
108 ret->n = 2;
109 ret->rowsonly = ret->orientable = FALSE;
110 ret->movetarget = 0;
111 while (*string && isdigit((unsigned char)*string)) string++;
112 if (*string == 'x') {
113 string++;
114 ret->h = atoi(string);
115 while (*string && isdigit((unsigned char)*string)) string++;
116 }
117 if (*string == 'n') {
118 string++;
119 ret->n = atoi(string);
120 while (*string && isdigit((unsigned char)*string)) string++;
121 }
122 while (*string) {
123 if (*string == 'r') {
124 ret->rowsonly = TRUE;
125 } else if (*string == 'o') {
126 ret->orientable = TRUE;
127 } else if (*string == 'm') {
128 string++;
129 ret->movetarget = atoi(string);
130 while (string[1] && isdigit((unsigned char)string[1])) string++;
131 }
132 string++;
133 }
134}
135
136static char *encode_params(const game_params *params, int full)
137{
138 char buf[256];
139 sprintf(buf, "%dx%dn%d%s%s", params->w, params->h, params->n,
140 params->rowsonly ? "r" : "",
141 params->orientable ? "o" : "");
142 /* Shuffle limit is part of the limited parameters, because we have to
143 * supply the target move count. */
144 if (params->movetarget)
145 sprintf(buf + strlen(buf), "m%d", params->movetarget);
146 return dupstr(buf);
147}
148
149static config_item *game_configure(const game_params *params)
150{
151 config_item *ret;
152 char buf[80];
153
154 ret = snewn(7, config_item);
155
156 ret[0].name = "Width";
157 ret[0].type = C_STRING;
158 sprintf(buf, "%d", params->w);
159 ret[0].sval = dupstr(buf);
160 ret[0].ival = 0;
161
162 ret[1].name = "Height";
163 ret[1].type = C_STRING;
164 sprintf(buf, "%d", params->h);
165 ret[1].sval = dupstr(buf);
166 ret[1].ival = 0;
167
168 ret[2].name = "Rotating block size";
169 ret[2].type = C_STRING;
170 sprintf(buf, "%d", params->n);
171 ret[2].sval = dupstr(buf);
172 ret[2].ival = 0;
173
174 ret[3].name = "One number per row";
175 ret[3].type = C_BOOLEAN;
176 ret[3].sval = NULL;
177 ret[3].ival = params->rowsonly;
178
179 ret[4].name = "Orientation matters";
180 ret[4].type = C_BOOLEAN;
181 ret[4].sval = NULL;
182 ret[4].ival = params->orientable;
183
184 ret[5].name = "Number of shuffling moves";
185 ret[5].type = C_STRING;
186 sprintf(buf, "%d", params->movetarget);
187 ret[5].sval = dupstr(buf);
188 ret[5].ival = 0;
189
190 ret[6].name = NULL;
191 ret[6].type = C_END;
192 ret[6].sval = NULL;
193 ret[6].ival = 0;
194
195 return ret;
196}
197
198static game_params *custom_params(const config_item *cfg)
199{
200 game_params *ret = snew(game_params);
201
202 ret->w = atoi(cfg[0].sval);
203 ret->h = atoi(cfg[1].sval);
204 ret->n = atoi(cfg[2].sval);
205 ret->rowsonly = cfg[3].ival;
206 ret->orientable = cfg[4].ival;
207 ret->movetarget = atoi(cfg[5].sval);
208
209 return ret;
210}
211
212static char *validate_params(const game_params *params, int full)
213{
214 if (params->n < 2)
215 return "Rotating block size must be at least two";
216 if (params->w < params->n)
217 return "Width must be at least the rotating block size";
218 if (params->h < params->n)
219 return "Height must be at least the rotating block size";
220 return NULL;
221}
222
223/*
224 * This function actually performs a rotation on a grid. The `x'
225 * and `y' coordinates passed in are the coordinates of the _top
226 * left corner_ of the rotated region. (Using the centre would have
227 * involved half-integers and been annoyingly fiddly. Clicking in
228 * the centre is good for a user interface, but too inconvenient to
229 * use internally.)
230 */
231static void do_rotate(int *grid, int w, int h, int n, int orientable,
232 int x, int y, int dir)
233{
234 int i, j;
235
236 assert(x >= 0 && x+n <= w);
237 assert(y >= 0 && y+n <= h);
238 dir &= 3;
239 if (dir == 0)
240 return; /* nothing to do */
241
242 grid += y*w+x; /* translate region to top corner */
243
244 /*
245 * If we were leaving the result of the rotation in a separate
246 * grid, the simple thing to do would be to loop over each
247 * square within the rotated region and assign it from its
248 * source square. However, to do it in place without taking
249 * O(n^2) memory, we need to be marginally more clever. What
250 * I'm going to do is loop over about one _quarter_ of the
251 * rotated region and permute each element within that quarter
252 * with its rotational coset.
253 *
254 * The size of the region I need to loop over is (n+1)/2 by
255 * n/2, which is an obvious exact quarter for even n and is a
256 * rectangle for odd n. (For odd n, this technique leaves out
257 * one element of the square, which is of course the central
258 * one that never moves anyway.)
259 */
260 for (i = 0; i < (n+1)/2; i++) {
261 for (j = 0; j < n/2; j++) {
262 int k;
263 int g[4];
264 int p[4];
265
266 p[0] = j*w+i;
267 p[1] = i*w+(n-j-1);
268 p[2] = (n-j-1)*w+(n-i-1);
269 p[3] = (n-i-1)*w+j;
270
271 for (k = 0; k < 4; k++)
272 g[k] = grid[p[k]];
273
274 for (k = 0; k < 4; k++) {
275 int v = g[(k+dir) & 3];
276 if (orientable)
277 v ^= ((v+dir) ^ v) & 3; /* alter orientation */
278 grid[p[k]] = v;
279 }
280 }
281 }
282
283 /*
284 * Don't forget the orientation on the centre square, if n is
285 * odd.
286 */
287 if (orientable && (n & 1)) {
288 int v = grid[n/2*(w+1)];
289 v ^= ((v+dir) ^ v) & 3; /* alter orientation */
290 grid[n/2*(w+1)] = v;
291 }
292}
293
294static int grid_complete(int *grid, int wh, int orientable)
295{
296 int ok = TRUE;
297 int i;
298 for (i = 1; i < wh; i++)
299 if (grid[i] < grid[i-1])
300 ok = FALSE;
301 if (orientable) {
302 for (i = 0; i < wh; i++)
303 if (grid[i] & 3)
304 ok = FALSE;
305 }
306 return ok;
307}
308
309static char *new_game_desc(const game_params *params, random_state *rs,
310 char **aux, int interactive)
311{
312 int *grid;
313 int w = params->w, h = params->h, n = params->n, wh = w*h;
314 int i;
315 char *ret;
316 int retlen;
317 int total_moves;
318
319 /*
320 * Set up a solved grid.
321 */
322 grid = snewn(wh, int);
323 for (i = 0; i < wh; i++)
324 grid[i] = ((params->rowsonly ? i/w : i) + 1) * 4;
325
326 /*
327 * Shuffle it. This game is complex enough that I don't feel up
328 * to analysing its full symmetry properties (particularly at
329 * n=4 and above!), so I'm going to do it the pedestrian way
330 * and simply shuffle the grid by making a long sequence of
331 * randomly chosen moves.
332 */
333 total_moves = params->movetarget;
334 if (!total_moves)
335 /* Add a random move to avoid parity issues. */
336 total_moves = w*h*n*n*2 + random_upto(rs, 2);
337
338 do {
339 int *prevmoves;
340 int rw, rh; /* w/h of rotation centre space */
341
342 rw = w - n + 1;
343 rh = h - n + 1;
344 prevmoves = snewn(rw * rh, int);
345 for (i = 0; i < rw * rh; i++)
346 prevmoves[i] = 0;
347
348 for (i = 0; i < total_moves; i++) {
349 int x, y, r, oldtotal, newtotal, dx, dy;
350
351 do {
352 x = random_upto(rs, w - n + 1);
353 y = random_upto(rs, h - n + 1);
354 r = 2 * random_upto(rs, 2) - 1;
355
356 /*
357 * See if any previous rotations has happened at
358 * this point which nothing has overlapped since.
359 * If so, ensure we haven't either undone a
360 * previous move or repeated one so many times that
361 * it turns into fewer moves in the inverse
362 * direction (i.e. three identical rotations).
363 */
364 oldtotal = prevmoves[y*rw+x];
365 newtotal = oldtotal + r;
366
367 /*
368 * Special case here for w==h==n, in which case
369 * there is actually no way to _avoid_ all moves
370 * repeating or undoing previous ones.
371 */
372 } while ((w != n || h != n) &&
373 (abs(newtotal) < abs(oldtotal) || abs(newtotal) > 2));
374
375 do_rotate(grid, w, h, n, params->orientable, x, y, r);
376
377 /*
378 * Log the rotation we've just performed at this point,
379 * for inversion detection in the next move.
380 *
381 * Also zero a section of the prevmoves array, because
382 * any rotation area which _overlaps_ this one is now
383 * entirely safe to perform further moves in.
384 *
385 * Two rotation areas overlap if their top left
386 * coordinates differ by strictly less than n in both
387 * directions
388 */
389 prevmoves[y*rw+x] += r;
390 for (dy = -n+1; dy <= n-1; dy++) {
391 if (y + dy < 0 || y + dy >= rh)
392 continue;
393 for (dx = -n+1; dx <= n-1; dx++) {
394 if (x + dx < 0 || x + dx >= rw)
395 continue;
396 if (dx == 0 && dy == 0)
397 continue;
398 prevmoves[(y+dy)*rw+(x+dx)] = 0;
399 }
400 }
401 }
402
403 sfree(prevmoves);
404
405 } while (grid_complete(grid, wh, params->orientable));
406
407 /*
408 * Now construct the game description, by describing the grid
409 * as a simple sequence of integers. They're comma-separated,
410 * unless the puzzle is orientable in which case they're
411 * separated by orientation letters `u', `d', `l' and `r'.
412 */
413 ret = NULL;
414 retlen = 0;
415 for (i = 0; i < wh; i++) {
416 char buf[80];
417 int k;
418
419 k = sprintf(buf, "%d%c", grid[i] / 4,
420 (char)(params->orientable ? "uldr"[grid[i] & 3] : ','));
421
422 ret = sresize(ret, retlen + k + 1, char);
423 strcpy(ret + retlen, buf);
424 retlen += k;
425 }
426 if (!params->orientable)
427 ret[retlen-1] = '\0'; /* delete last comma */
428
429 sfree(grid);
430 return ret;
431}
432
433static char *validate_desc(const game_params *params, const char *desc)
434{
435 const char *p;
436 int w = params->w, h = params->h, wh = w*h;
437 int i;
438
439 p = desc;
440
441 for (i = 0; i < wh; i++) {
442 if (*p < '0' || *p > '9')
443 return "Not enough numbers in string";
444 while (*p >= '0' && *p <= '9')
445 p++;
446 if (!params->orientable && i < wh-1) {
447 if (*p != ',')
448 return "Expected comma after number";
449 } else if (params->orientable && i < wh) {
450 if (*p != 'l' && *p != 'r' && *p != 'u' && *p != 'd')
451 return "Expected orientation letter after number";
452 } else if (i == wh-1 && *p) {
453 return "Excess junk at end of string";
454 }
455
456 if (*p) p++; /* eat comma */
457 }
458
459 return NULL;
460}
461
462static game_state *new_game(midend *me, const game_params *params,
463 const char *desc)
464{
465 game_state *state = snew(game_state);
466 int w = params->w, h = params->h, n = params->n, wh = w*h;
467 int i;
468 const char *p;
469
470 state->w = w;
471 state->h = h;
472 state->n = n;
473 state->orientable = params->orientable;
474 state->completed = 0;
475 state->used_solve = FALSE;
476 state->movecount = 0;
477 state->movetarget = params->movetarget;
478 state->lastx = state->lasty = state->lastr = -1;
479
480 state->grid = snewn(wh, int);
481
482 p = desc;
483
484 for (i = 0; i < wh; i++) {
485 state->grid[i] = 4 * atoi(p);
486 while (*p >= '0' && *p <= '9')
487 p++;
488 if (*p) {
489 if (params->orientable) {
490 switch (*p) {
491 case 'l': state->grid[i] |= 1; break;
492 case 'd': state->grid[i] |= 2; break;
493 case 'r': state->grid[i] |= 3; break;
494 }
495 }
496 p++;
497 }
498 }
499
500 return state;
501}
502
503static game_state *dup_game(const game_state *state)
504{
505 game_state *ret = snew(game_state);
506
507 ret->w = state->w;
508 ret->h = state->h;
509 ret->n = state->n;
510 ret->orientable = state->orientable;
511 ret->completed = state->completed;
512 ret->movecount = state->movecount;
513 ret->movetarget = state->movetarget;
514 ret->lastx = state->lastx;
515 ret->lasty = state->lasty;
516 ret->lastr = state->lastr;
517 ret->used_solve = state->used_solve;
518
519 ret->grid = snewn(ret->w * ret->h, int);
520 memcpy(ret->grid, state->grid, ret->w * ret->h * sizeof(int));
521
522 return ret;
523}
524
525static void free_game(game_state *state)
526{
527 sfree(state->grid);
528 sfree(state);
529}
530
531static int compare_int(const void *av, const void *bv)
532{
533 const int *a = (const int *)av;
534 const int *b = (const int *)bv;
535 if (*a < *b)
536 return -1;
537 else if (*a > *b)
538 return +1;
539 else
540 return 0;
541}
542
543static char *solve_game(const game_state *state, const game_state *currstate,
544 const char *aux, char **error)
545{
546 return dupstr("S");
547}
548
549static int game_can_format_as_text_now(const game_params *params)
550{
551 return TRUE;
552}
553
554static char *game_text_format(const game_state *state)
555{
556 char *ret, *p, buf[80];
557 int i, x, y, col, o, maxlen;
558
559 /*
560 * First work out how many characters we need to display each
561 * number. We're pretty flexible on grid contents here, so we
562 * have to scan the entire grid.
563 */
564 col = 0;
565 for (i = 0; i < state->w * state->h; i++) {
566 x = sprintf(buf, "%d", state->grid[i] / 4);
567 if (col < x) col = x;
568 }
569 o = (state->orientable ? 1 : 0);
570
571 /*
572 * Now we know the exact total size of the grid we're going to
573 * produce: it's got h rows, each containing w lots of col+o,
574 * w-1 spaces and a trailing newline.
575 */
576 maxlen = state->h * state->w * (col+o+1);
577
578 ret = snewn(maxlen+1, char);
579 p = ret;
580
581 for (y = 0; y < state->h; y++) {
582 for (x = 0; x < state->w; x++) {
583 int v = state->grid[state->w*y+x];
584 sprintf(buf, "%*d", col, v/4);
585 memcpy(p, buf, col);
586 p += col;
587 if (o)
588 *p++ = "^<v>"[v & 3];
589 if (x+1 == state->w)
590 *p++ = '\n';
591 else
592 *p++ = ' ';
593 }
594 }
595
596 assert(p - ret == maxlen);
597 *p = '\0';
598 return ret;
599}
600
601struct game_ui {
602 int cur_x, cur_y;
603 int cur_visible;
604};
605
606static game_ui *new_ui(const game_state *state)
607{
608 game_ui *ui = snew(game_ui);
609
610 ui->cur_x = 0;
611 ui->cur_y = 0;
612 ui->cur_visible = FALSE;
613
614 return ui;
615}
616
617static void free_ui(game_ui *ui)
618{
619 sfree(ui);
620}
621
622static char *encode_ui(const game_ui *ui)
623{
624 return NULL;
625}
626
627static void decode_ui(game_ui *ui, const char *encoding)
628{
629}
630
631static void game_changed_state(game_ui *ui, const game_state *oldstate,
632 const game_state *newstate)
633{
634}
635
636struct game_drawstate {
637 int started;
638 int w, h, bgcolour;
639 int *grid;
640 int tilesize;
641 int cur_x, cur_y;
642};
643
644static char *interpret_move(const game_state *state, game_ui *ui,
645 const game_drawstate *ds,
646 int x, int y, int button)
647{
648 int w = state->w, h = state->h, n = state->n /* , wh = w*h */;
649 char buf[80];
650 int dir;
651
652 button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
653
654 if (IS_CURSOR_MOVE(button)) {
655 if (button == CURSOR_LEFT && ui->cur_x > 0)
656 ui->cur_x--;
657 if (button == CURSOR_RIGHT && (ui->cur_x+n) < (w))
658 ui->cur_x++;
659 if (button == CURSOR_UP && ui->cur_y > 0)
660 ui->cur_y--;
661 if (button == CURSOR_DOWN && (ui->cur_y+n) < (h))
662 ui->cur_y++;
663 ui->cur_visible = 1;
664 return "";
665 }
666
667 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
668 /*
669 * Determine the coordinates of the click. We offset by n-1
670 * half-blocks so that the user must click at the centre of
671 * a rotation region rather than at the corner.
672 */
673 x -= (n-1) * TILE_SIZE / 2;
674 y -= (n-1) * TILE_SIZE / 2;
675 x = FROMCOORD(x);
676 y = FROMCOORD(y);
677 dir = (button == LEFT_BUTTON ? 1 : -1);
678 if (x < 0 || x > w-n || y < 0 || y > h-n)
679 return NULL;
680 ui->cur_visible = 0;
681 } else if (IS_CURSOR_SELECT(button)) {
682 if (ui->cur_visible) {
683 x = ui->cur_x;
684 y = ui->cur_y;
685 dir = (button == CURSOR_SELECT2) ? -1 : +1;
686 } else {
687 ui->cur_visible = 1;
688 return "";
689 }
690 } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') {
691 x = y = 0;
692 dir = (button == 'A' ? -1 : +1);
693 } else if (button == 'b' || button == 'B' || button==MOD_NUM_KEYPAD+'9') {
694 x = w-n;
695 y = 0;
696 dir = (button == 'B' ? -1 : +1);
697 } else if (button == 'c' || button == 'C' || button==MOD_NUM_KEYPAD+'1') {
698 x = 0;
699 y = h-n;
700 dir = (button == 'C' ? -1 : +1);
701 } else if (button == 'd' || button == 'D' || button==MOD_NUM_KEYPAD+'3') {
702 x = w-n;
703 y = h-n;
704 dir = (button == 'D' ? -1 : +1);
705 } else if (button==MOD_NUM_KEYPAD+'8' && (w-n) % 2 == 0) {
706 x = (w-n) / 2;
707 y = 0;
708 dir = +1;
709 } else if (button==MOD_NUM_KEYPAD+'2' && (w-n) % 2 == 0) {
710 x = (w-n) / 2;
711 y = h-n;
712 dir = +1;
713 } else if (button==MOD_NUM_KEYPAD+'4' && (h-n) % 2 == 0) {
714 x = 0;
715 y = (h-n) / 2;
716 dir = +1;
717 } else if (button==MOD_NUM_KEYPAD+'6' && (h-n) % 2 == 0) {
718 x = w-n;
719 y = (h-n) / 2;
720 dir = +1;
721 } else if (button==MOD_NUM_KEYPAD+'5' && (w-n) % 2 == 0 && (h-n) % 2 == 0){
722 x = (w-n) / 2;
723 y = (h-n) / 2;
724 dir = +1;
725 } else {
726 return NULL; /* no move to be made */
727 }
728
729 /*
730 * If we reach here, we have a valid move.
731 */
732 sprintf(buf, "M%d,%d,%d", x, y, dir);
733 return dupstr(buf);
734}
735
736static game_state *execute_move(const game_state *from, const char *move)
737{
738 game_state *ret;
739 int w = from->w, h = from->h, n = from->n, wh = w*h;
740 int x, y, dir;
741
742 if (!strcmp(move, "S")) {
743 int i;
744 ret = dup_game(from);
745
746 /*
747 * Simply replace the grid with a solved one. For this game,
748 * this isn't a useful operation for actually telling the user
749 * what they should have done, but it is useful for
750 * conveniently being able to get hold of a clean state from
751 * which to practise manoeuvres.
752 */
753 qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_int);
754 for (i = 0; i < ret->w*ret->h; i++)
755 ret->grid[i] &= ~3;
756 ret->used_solve = TRUE;
757 ret->completed = ret->movecount = 1;
758
759 return ret;
760 }
761
762 if (move[0] != 'M' ||
763 sscanf(move+1, "%d,%d,%d", &x, &y, &dir) != 3 ||
764 x < 0 || y < 0 || x > from->w - n || y > from->h - n)
765 return NULL; /* can't parse this move string */
766
767 ret = dup_game(from);
768 ret->movecount++;
769 do_rotate(ret->grid, w, h, n, ret->orientable, x, y, dir);
770 ret->lastx = x;
771 ret->lasty = y;
772 ret->lastr = dir;
773
774 /*
775 * See if the game has been completed. To do this we simply
776 * test that the grid contents are in increasing order.
777 */
778 if (!ret->completed && grid_complete(ret->grid, wh, ret->orientable))
779 ret->completed = ret->movecount;
780 return ret;
781}
782
783/* ----------------------------------------------------------------------
784 * Drawing routines.
785 */
786
787static void game_compute_size(const game_params *params, int tilesize,
788 int *x, int *y)
789{
790 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
791 struct { int tilesize; } ads, *ds = &ads;
792 ads.tilesize = tilesize;
793
794 *x = TILE_SIZE * params->w + 2 * BORDER;
795 *y = TILE_SIZE * params->h + 2 * BORDER;
796}
797
798static void game_set_size(drawing *dr, game_drawstate *ds,
799 const game_params *params, int tilesize)
800{
801 ds->tilesize = tilesize;
802}
803
804static float *game_colours(frontend *fe, int *ncolours)
805{
806 float *ret = snewn(3 * NCOLOURS, float);
807 int i;
808
809 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
810
811 /* cursor is light-background with a red tinge. */
812 ret[COL_HIGHCURSOR * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 1.0F;
813 ret[COL_HIGHCURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.5F;
814 ret[COL_HIGHCURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.5F;
815
816 for (i = 0; i < 3; i++) {
817 ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F;
818 ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F;
819 ret[COL_TEXT * 3 + i] = 0.0;
820 ret[COL_LOWCURSOR * 3 + i] = ret[COL_HIGHCURSOR * 3 + i] * 0.6F;
821 }
822
823 *ncolours = NCOLOURS;
824 return ret;
825}
826
827static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
828{
829 struct game_drawstate *ds = snew(struct game_drawstate);
830 int i;
831
832 ds->started = FALSE;
833 ds->w = state->w;
834 ds->h = state->h;
835 ds->bgcolour = COL_BACKGROUND;
836 ds->grid = snewn(ds->w*ds->h, int);
837 ds->tilesize = 0; /* haven't decided yet */
838 for (i = 0; i < ds->w*ds->h; i++)
839 ds->grid[i] = -1;
840 ds->cur_x = ds->cur_y = -state->n;
841
842 return ds;
843}
844
845static void game_free_drawstate(drawing *dr, game_drawstate *ds)
846{
847 sfree(ds->grid);
848 sfree(ds);
849}
850
851struct rotation {
852 int cx, cy, cw, ch; /* clip region */
853 int ox, oy; /* rotation origin */
854 float c, s; /* cos and sin of rotation angle */
855 int lc, rc, tc, bc; /* colours of tile edges */
856};
857
858static void rotate(int *xy, struct rotation *rot)
859{
860 if (rot) {
861 float xf = (float)xy[0] - rot->ox, yf = (float)xy[1] - rot->oy;
862 float xf2, yf2;
863
864 xf2 = rot->c * xf + rot->s * yf;
865 yf2 = - rot->s * xf + rot->c * yf;
866
867 xy[0] = (int)(xf2 + rot->ox + 0.5); /* round to nearest */
868 xy[1] = (int)(yf2 + rot->oy + 0.5); /* round to nearest */
869 }
870}
871
872#define CUR_TOP 1
873#define CUR_RIGHT 2
874#define CUR_BOTTOM 4
875#define CUR_LEFT 8
876
877static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
878 int x, int y, int tile, int flash_colour,
879 struct rotation *rot, unsigned cedges)
880{
881 int coords[8];
882 char str[40];
883
884 /*
885 * If we've been passed a rotation region but we're drawing a
886 * tile which is outside it, we must draw it normally. This can
887 * occur if we're cleaning up after a completion flash while a
888 * new move is also being made.
889 */
890 if (rot && (x < rot->cx || y < rot->cy ||
891 x >= rot->cx+rot->cw || y >= rot->cy+rot->ch))
892 rot = NULL;
893
894 if (rot)
895 clip(dr, rot->cx, rot->cy, rot->cw, rot->ch);
896
897 /*
898 * We must draw each side of the tile's highlight separately,
899 * because in some cases (during rotation) they will all need
900 * to be different colours.
901 */
902
903 /* The centre point is common to all sides. */
904 coords[4] = x + TILE_SIZE / 2;
905 coords[5] = y + TILE_SIZE / 2;
906 rotate(coords+4, rot);
907
908 /* Right side. */
909 coords[0] = x + TILE_SIZE - 1;
910 coords[1] = y + TILE_SIZE - 1;
911 rotate(coords+0, rot);
912 coords[2] = x + TILE_SIZE - 1;
913 coords[3] = y;
914 rotate(coords+2, rot);
915 draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT,
916 rot ? rot->rc : (cedges & CUR_RIGHT) ? COL_LOWCURSOR : COL_LOWLIGHT);
917
918 /* Bottom side. */
919 coords[2] = x;
920 coords[3] = y + TILE_SIZE - 1;
921 rotate(coords+2, rot);
922 draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT,
923 rot ? rot->bc : (cedges & CUR_BOTTOM) ? COL_LOWCURSOR : COL_LOWLIGHT);
924
925 /* Left side. */
926 coords[0] = x;
927 coords[1] = y;
928 rotate(coords+0, rot);
929 draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT,
930 rot ? rot->lc : (cedges & CUR_LEFT) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
931
932 /* Top side. */
933 coords[2] = x + TILE_SIZE - 1;
934 coords[3] = y;
935 rotate(coords+2, rot);
936 draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT,
937 rot ? rot->tc : (cedges & CUR_TOP) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
938
939 /*
940 * Now the main blank area in the centre of the tile.
941 */
942 if (rot) {
943 coords[0] = x + HIGHLIGHT_WIDTH;
944 coords[1] = y + HIGHLIGHT_WIDTH;
945 rotate(coords+0, rot);
946 coords[2] = x + HIGHLIGHT_WIDTH;
947 coords[3] = y + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
948 rotate(coords+2, rot);
949 coords[4] = x + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
950 coords[5] = y + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
951 rotate(coords+4, rot);
952 coords[6] = x + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
953 coords[7] = y + HIGHLIGHT_WIDTH;
954 rotate(coords+6, rot);
955 draw_polygon(dr, coords, 4, flash_colour, flash_colour);
956 } else {
957 draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
958 TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
959 flash_colour);
960 }
961
962 /*
963 * Next, the triangles for orientation.
964 */
965 if (state->orientable) {
966 int xdx, xdy, ydx, ydy;
967 int cx, cy, displ, displ2;
968 switch (tile & 3) {
969 case 0:
970 xdx = 1, xdy = 0;
971 ydx = 0, ydy = 1;
972 break;
973 case 1:
974 xdx = 0, xdy = -1;
975 ydx = 1, ydy = 0;
976 break;
977 case 2:
978 xdx = -1, xdy = 0;
979 ydx = 0, ydy = -1;
980 break;
981 default /* case 3 */:
982 xdx = 0, xdy = 1;
983 ydx = -1, ydy = 0;
984 break;
985 }
986
987 cx = x + TILE_SIZE / 2;
988 cy = y + TILE_SIZE / 2;
989 displ = TILE_SIZE / 2 - HIGHLIGHT_WIDTH - 2;
990 displ2 = TILE_SIZE / 3 - HIGHLIGHT_WIDTH;
991
992 coords[0] = cx - displ * xdx + displ2 * ydx;
993 coords[1] = cy - displ * xdy + displ2 * ydy;
994 rotate(coords+0, rot);
995 coords[2] = cx + displ * xdx + displ2 * ydx;
996 coords[3] = cy + displ * xdy + displ2 * ydy;
997 rotate(coords+2, rot);
998 coords[4] = cx - displ * ydx;
999 coords[5] = cy - displ * ydy;
1000 rotate(coords+4, rot);
1001 draw_polygon(dr, coords, 3, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE);
1002 }
1003
1004 coords[0] = x + TILE_SIZE/2;
1005 coords[1] = y + TILE_SIZE/2;
1006 rotate(coords+0, rot);
1007 sprintf(str, "%d", tile / 4);
1008 draw_text(dr, coords[0], coords[1],
1009 FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE,
1010 COL_TEXT, str);
1011
1012 if (rot)
1013 unclip(dr);
1014
1015 draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
1016}
1017
1018static int highlight_colour(float angle)
1019{
1020 int colours[32] = {
1021 COL_LOWLIGHT,
1022 COL_LOWLIGHT_GENTLE,
1023 COL_LOWLIGHT_GENTLE,
1024 COL_LOWLIGHT_GENTLE,
1025 COL_HIGHLIGHT_GENTLE,
1026 COL_HIGHLIGHT_GENTLE,
1027 COL_HIGHLIGHT_GENTLE,
1028 COL_HIGHLIGHT,
1029 COL_HIGHLIGHT,
1030 COL_HIGHLIGHT,
1031 COL_HIGHLIGHT,
1032 COL_HIGHLIGHT,
1033 COL_HIGHLIGHT,
1034 COL_HIGHLIGHT,
1035 COL_HIGHLIGHT,
1036 COL_HIGHLIGHT,
1037 COL_HIGHLIGHT,
1038 COL_HIGHLIGHT_GENTLE,
1039 COL_HIGHLIGHT_GENTLE,
1040 COL_HIGHLIGHT_GENTLE,
1041 COL_LOWLIGHT_GENTLE,
1042 COL_LOWLIGHT_GENTLE,
1043 COL_LOWLIGHT_GENTLE,
1044 COL_LOWLIGHT,
1045 COL_LOWLIGHT,
1046 COL_LOWLIGHT,
1047 COL_LOWLIGHT,
1048 COL_LOWLIGHT,
1049 COL_LOWLIGHT,
1050 COL_LOWLIGHT,
1051 COL_LOWLIGHT,
1052 COL_LOWLIGHT,
1053 };
1054
1055 return colours[(int)((angle + 2*PI) / (PI/16)) & 31];
1056}
1057
1058static float game_anim_length_real(const game_state *oldstate,
1059 const game_state *newstate, int dir,
1060 const game_ui *ui)
1061{
1062 /*
1063 * Our game_anim_length doesn't need to modify its game_ui, so
1064 * this is the real function which declares ui as const. We must
1065 * wrap this for the backend structure with a version that has ui
1066 * non-const, but we still need this version to call from within
1067 * game_redraw which only has a const ui available.
1068 */
1069 return (float)(ANIM_PER_BLKSIZE_UNIT * sqrt(newstate->n-1));
1070}
1071
1072static float game_anim_length(const game_state *oldstate,
1073 const game_state *newstate, int dir, game_ui *ui)
1074{
1075 return game_anim_length_real(oldstate, newstate, dir, ui);
1076
1077}
1078
1079static float game_flash_length(const game_state *oldstate,
1080 const game_state *newstate, int dir, game_ui *ui)
1081{
1082 if (!oldstate->completed && newstate->completed &&
1083 !oldstate->used_solve && !newstate->used_solve)
1084 return 2 * FLASH_FRAME;
1085 else
1086 return 0.0F;
1087}
1088
1089static int game_status(const game_state *state)
1090{
1091 return state->completed ? +1 : 0;
1092}
1093
1094static void game_redraw(drawing *dr, game_drawstate *ds,
1095 const game_state *oldstate, const game_state *state,
1096 int dir, const game_ui *ui,
1097 float animtime, float flashtime)
1098{
1099 int i, bgcolour;
1100 struct rotation srot, *rot;
1101 int lastx = -1, lasty = -1, lastr = -1;
1102 int cx, cy, cmoved = 0, n = state->n;
1103
1104 cx = ui->cur_visible ? ui->cur_x : -state->n;
1105 cy = ui->cur_visible ? ui->cur_y : -state->n;
1106 if (cx != ds->cur_x || cy != ds->cur_y)
1107 cmoved = 1;
1108
1109 if (flashtime > 0) {
1110 int frame = (int)(flashtime / FLASH_FRAME);
1111 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
1112 } else
1113 bgcolour = COL_BACKGROUND;
1114
1115 if (!ds->started) {
1116 int coords[10];
1117
1118 draw_rect(dr, 0, 0,
1119 TILE_SIZE * state->w + 2 * BORDER,
1120 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
1121 draw_update(dr, 0, 0,
1122 TILE_SIZE * state->w + 2 * BORDER,
1123 TILE_SIZE * state->h + 2 * BORDER);
1124
1125 /*
1126 * Recessed area containing the whole puzzle.
1127 */
1128 coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
1129 coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
1130 coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
1131 coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
1132 coords[4] = coords[2] - TILE_SIZE;
1133 coords[5] = coords[3] + TILE_SIZE;
1134 coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
1135 coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
1136 coords[6] = coords[8] + TILE_SIZE;
1137 coords[7] = coords[9] - TILE_SIZE;
1138 draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
1139
1140 coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
1141 coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
1142 draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
1143
1144 ds->started = TRUE;
1145 }
1146
1147 /*
1148 * If we're drawing any rotated tiles, sort out the rotation
1149 * parameters, and also zap the rotation region to the
1150 * background colour before doing anything else.
1151 */
1152 if (oldstate) {
1153 float angle;
1154 float anim_max = game_anim_length_real(oldstate, state, dir, ui);
1155
1156 if (dir > 0) {
1157 lastx = state->lastx;
1158 lasty = state->lasty;
1159 lastr = state->lastr;
1160 } else {
1161 lastx = oldstate->lastx;
1162 lasty = oldstate->lasty;
1163 lastr = -oldstate->lastr;
1164 }
1165
1166 rot = &srot;
1167 rot->cx = COORD(lastx);
1168 rot->cy = COORD(lasty);
1169 rot->cw = rot->ch = TILE_SIZE * state->n;
1170 rot->ox = rot->cx + rot->cw/2;
1171 rot->oy = rot->cy + rot->ch/2;
1172 angle = (float)((-PI/2 * lastr) * (1.0 - animtime / anim_max));
1173 rot->c = (float)cos(angle);
1174 rot->s = (float)sin(angle);
1175
1176 /*
1177 * Sort out the colours of the various sides of the tile.
1178 */
1179 rot->lc = highlight_colour((float)PI + angle);
1180 rot->rc = highlight_colour(angle);
1181 rot->tc = highlight_colour((float)(PI/2.0) + angle);
1182 rot->bc = highlight_colour((float)(-PI/2.0) + angle);
1183
1184 draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour);
1185 } else
1186 rot = NULL;
1187
1188 /*
1189 * Now draw each tile.
1190 */
1191 for (i = 0; i < state->w * state->h; i++) {
1192 int t, cc = 0;
1193 int tx = i % state->w, ty = i / state->w;
1194
1195 /*
1196 * Figure out what should be displayed at this location.
1197 * Usually it will be state->grid[i], unless we're in the
1198 * middle of animating an actual rotation and this cell is
1199 * within the rotation region, in which case we set -1
1200 * (always display).
1201 */
1202 if (oldstate && lastx >= 0 && lasty >= 0 &&
1203 tx >= lastx && tx < lastx + state->n &&
1204 ty >= lasty && ty < lasty + state->n)
1205 t = -1;
1206 else
1207 t = state->grid[i];
1208
1209 if (cmoved) {
1210 /* cursor has moved (or changed visibility)... */
1211 if (tx == cx || tx == cx+n-1 || ty == cy || ty == cy+n-1)
1212 cc = 1; /* ...we're on new cursor, redraw */
1213 if (tx == ds->cur_x || tx == ds->cur_x+n-1 ||
1214 ty == ds->cur_y || ty == ds->cur_y+n-1)
1215 cc = 1; /* ...we were on old cursor, redraw */
1216 }
1217
1218 if (ds->bgcolour != bgcolour || /* always redraw when flashing */
1219 ds->grid[i] != t || ds->grid[i] == -1 || t == -1 || cc) {
1220 int x = COORD(tx), y = COORD(ty);
1221 unsigned cedges = 0;
1222
1223 if (tx == cx && ty >= cy && ty <= cy+n-1) cedges |= CUR_LEFT;
1224 if (ty == cy && tx >= cx && tx <= cx+n-1) cedges |= CUR_TOP;
1225 if (tx == cx+n-1 && ty >= cy && ty <= cy+n-1) cedges |= CUR_RIGHT;
1226 if (ty == cy+n-1 && tx >= cx && tx <= cx+n-1) cedges |= CUR_BOTTOM;
1227
1228 draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot, cedges);
1229 ds->grid[i] = t;
1230 }
1231 }
1232 ds->bgcolour = bgcolour;
1233 ds->cur_x = cx; ds->cur_y = cy;
1234
1235 /*
1236 * Update the status bar.
1237 */
1238 {
1239 char statusbuf[256];
1240
1241 /*
1242 * Don't show the new status until we're also showing the
1243 * new _state_ - after the game animation is complete.
1244 */
1245 if (oldstate)
1246 state = oldstate;
1247
1248 if (state->used_solve)
1249 sprintf(statusbuf, "Moves since auto-solve: %d",
1250 state->movecount - state->completed);
1251 else {
1252 sprintf(statusbuf, "%sMoves: %d",
1253 (state->completed ? "COMPLETED! " : ""),
1254 (state->completed ? state->completed : state->movecount));
1255 if (state->movetarget)
1256 sprintf(statusbuf+strlen(statusbuf), " (target %d)",
1257 state->movetarget);
1258 }
1259
1260 status_bar(dr, statusbuf);
1261 }
1262}
1263
1264static int game_timing_state(const game_state *state, game_ui *ui)
1265{
1266 return TRUE;
1267}
1268
1269static void game_print_size(const game_params *params, float *x, float *y)
1270{
1271}
1272
1273static void game_print(drawing *dr, const game_state *state, int tilesize)
1274{
1275}
1276
1277#ifdef COMBINED
1278#define thegame twiddle
1279#endif
1280
1281const struct game thegame = {
1282 "Twiddle", "games.twiddle", "twiddle",
1283 default_params,
1284 game_fetch_preset, NULL,
1285 decode_params,
1286 encode_params,
1287 free_params,
1288 dup_params,
1289 TRUE, game_configure, custom_params,
1290 validate_params,
1291 new_game_desc,
1292 validate_desc,
1293 new_game,
1294 dup_game,
1295 free_game,
1296 TRUE, solve_game,
1297 TRUE, game_can_format_as_text_now, game_text_format,
1298 new_ui,
1299 free_ui,
1300 encode_ui,
1301 decode_ui,
1302 game_changed_state,
1303 interpret_move,
1304 execute_move,
1305 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1306 game_colours,
1307 game_new_drawstate,
1308 game_free_drawstate,
1309 game_redraw,
1310 game_anim_length,
1311 game_flash_length,
1312 game_status,
1313 FALSE, FALSE, game_print_size, game_print,
1314 TRUE, /* wants_statusbar */
1315 FALSE, game_timing_state,
1316 0, /* flags */
1317};
1318
1319/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/twiddle.html b/apps/plugins/puzzles/src/twiddle.html
new file mode 100644
index 0000000000..244ce45144
--- /dev/null
+++ b/apps/plugins/puzzles/src/twiddle.html
@@ -0,0 +1,63 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Twiddle</title>
7<link rel="previous" href="sixteen.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="rect.html">
12</head>
13<body>
14<p><a href="sixteen.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="rect.html">Next</a></p>
15<h1><a name="C7"></a>Chapter 7: <a name="i0"></a>Twiddle</h1>
16<p>
17Twiddle is a tile-rearrangement puzzle, visually similar to Sixteen (see <a href="sixteen.html#C6">chapter 6</a>): you are given a grid of square tiles, each containing a number, and your aim is to arrange the numbers into ascending order.
18</p>
19<p>
20In basic Twiddle, your move is to rotate a square group of four tiles about their common centre. (Orientation is not significant in the basic puzzle, although you can select it.) On more advanced settings, you can rotate a larger square group of tiles.
21</p>
22<p>
23I first saw this type of puzzle in the GameCube game &#8216;Metroid Prime 2&#8217;. In the Main Gyro Chamber in that game, there is a puzzle you solve to unlock a door, which is a special case of Twiddle. I developed this game as a generalisation of that puzzle.
24</p>
25<h2><a name="S7.1"></a>7.1 <a name="i1"></a>Twiddle controls</h2>
26<p>
27To play Twiddle, click the mouse in the centre of the square group you wish to rotate. In the basic mode, you rotate a 2&#215;2 square, which means you have to click at a corner point where four tiles meet.
28</p>
29<p>
30In more advanced modes you might be rotating 3&#215;3 or even more at a time; if the size of the square is odd then you simply click in the centre tile of the square you want to rotate.
31</p>
32<p>
33Clicking with the left mouse button rotates the group anticlockwise. Clicking with the right button rotates it clockwise.
34</p>
35<p>
36You can also move an outline square around the grid with the cursor keys; the square is the size above (2&#215;2 by default, or larger). Pressing the return key or space bar will rotate the current square anticlockwise or clockwise respectively.
37</p>
38<p>
39(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
40</p>
41<h2><a name="S7.2"></a>7.2 <a name="i2"></a>Twiddle parameters</h2>
42<p>
43Twiddle provides several configuration options via the &#8216;Custom&#8217; option on the &#8216;Type&#8217; menu:
44</p>
45<ul><li>
46You can configure the width and height of the puzzle grid.
47</li>
48<li>
49You can configure the size of square block that rotates at a time.
50</li>
51<li>
52You can ask for every square in the grid to be distinguishable (the default), or you can ask for a simplified puzzle in which there are groups of identical numbers. In the simplified puzzle your aim is just to arrange all the 1s into the first row, all the 2s into the second row, and so on.
53</li>
54<li>
55You can configure whether the orientation of tiles matters. If you ask for an orientable puzzle, each tile will have a triangle drawn in it. All the triangles must be pointing upwards to complete the puzzle.
56</li>
57<li>
58You can ask for a limited shuffling operation to be performed on the grid. By default, Twiddle will shuffle the grid so much that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible.
59</li>
60</ul>
61
62<hr><address></address></body>
63</html>
diff --git a/apps/plugins/puzzles/src/undead.R b/apps/plugins/puzzles/src/undead.R
new file mode 100644
index 0000000000..5907ed6b74
--- /dev/null
+++ b/apps/plugins/puzzles/src/undead.R
@@ -0,0 +1,18 @@
1# -*- makefile -*-
2
3undead : [X] GTK COMMON undead undead-icon|no-icon
4undead : [G] WINDOWS COMMON undead undead.res|noicon.res
5
6ALL += undead[COMBINED]
7
8!begin am gtk
9GAMES += undead
10!end
11
12!begin >list.c
13 A(undead) \
14!end
15
16!begin >gamedesc.txt
17undead:undead.exe:Undead:Monster-placing puzzle:Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors.
18!end
diff --git a/apps/plugins/puzzles/src/undead.c b/apps/plugins/puzzles/src/undead.c
new file mode 100644
index 0000000000..b1f536e8d0
--- /dev/null
+++ b/apps/plugins/puzzles/src/undead.c
@@ -0,0 +1,2738 @@
1/*
2 * undead: Implementation of Haunted Mirror Mazes
3 *
4 * http://www.janko.at/Raetsel/Spukschloss/index.htm
5 *
6 * Puzzle definition is the total number of each monster type, the
7 * grid definition, and the list of sightings (clockwise, starting
8 * from top left corner)
9 *
10 * Example: (Janko puzzle No. 1,
11 * http://www.janko.at/Raetsel/Spukschloss/001.a.htm )
12 *
13 * Ghosts: 0 Vampires: 2 Zombies: 6
14 *
15 * 2 1 1 1
16 * 1 \ \ . / 2
17 * 0 \ . / . 2
18 * 0 / . / . 2
19 * 3 . . . \ 2
20 * 3 3 2 2
21 *
22 * would be encoded into:
23 * 4x4:0,2,6,LLaRLaRaRaRdL,2,1,1,1,2,2,2,2,2,2,3,3,3,0,0,1
24 *
25 * Additionally, the game description can contain monsters fixed at a
26 * certain grid position. The internal generator does not (yet) use
27 * this feature, but this is needed to enter puzzles like Janko No.
28 * 14, which is encoded as:
29 * 8x5:12,12,0,LaRbLaRaLaRLbRaVaVaGRaRaRaLbLaRbRLb,0,2,0,2,2,1,2,1,3,1,0,1,8,4,3,0,0,2,3,2,7,2,1,6,2,1
30 *
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <assert.h>
37#include <ctype.h>
38#include <math.h>
39
40#include "puzzles.h"
41
42enum {
43 COL_BACKGROUND,
44 COL_GRID,
45 COL_TEXT,
46 COL_ERROR,
47 COL_HIGHLIGHT,
48 COL_FLASH,
49 COL_GHOST,
50 COL_ZOMBIE,
51 COL_VAMPIRE,
52 COL_DONE,
53 NCOLOURS
54};
55
56#define DIFFLIST(A) \
57 A(EASY,Easy,e) \
58 A(NORMAL,Normal,n) \
59 A(TRICKY,Tricky,t)
60#define ENUM(upper,title,lower) DIFF_ ## upper,
61#define TITLE(upper,title,lower) #title,
62#define ENCODE(upper,title,lower) #lower
63#define CONFIG(upper,title,lower) ":" #title
64enum { DIFFLIST(ENUM) DIFFCOUNT };
65static char const *const undead_diffnames[] = { DIFFLIST(TITLE) "(count)" };
66static char const undead_diffchars[] = DIFFLIST(ENCODE);
67#define DIFFCONFIG DIFFLIST(CONFIG)
68
69struct game_params {
70 int w; /* Grid width */
71 int h; /* Grid height */
72 int diff; /* Puzzle difficulty */
73};
74
75static const struct game_params undead_presets[] = {
76 { 4, 4, DIFF_EASY },
77 { 4, 4, DIFF_NORMAL },
78 { 4, 4, DIFF_TRICKY },
79 { 5, 5, DIFF_EASY },
80 { 5, 5, DIFF_NORMAL },
81 { 5, 5, DIFF_TRICKY },
82 { 7, 7, DIFF_EASY },
83 { 7, 7, DIFF_NORMAL }
84};
85
86#define DEFAULT_PRESET 1
87
88static game_params *default_params(void) {
89 game_params *ret = snew(game_params);
90
91 *ret = undead_presets[DEFAULT_PRESET];
92 return ret;
93}
94
95static int game_fetch_preset(int i, char **name, game_params **params) {
96 game_params *ret;
97 char buf[64];
98
99 if (i < 0 || i >= lenof(undead_presets)) return FALSE;
100
101 ret = default_params();
102 *ret = undead_presets[i]; /* struct copy */
103 *params = ret;
104
105 sprintf(buf, "%dx%d %s",
106 undead_presets[i].w, undead_presets[i].h,
107 undead_diffnames[undead_presets[i].diff]);
108 *name = dupstr(buf);
109
110 return TRUE;
111}
112
113static void free_params(game_params *params) {
114 sfree(params);
115}
116
117static game_params *dup_params(const game_params *params)
118{
119 game_params *ret = snew(game_params);
120 *ret = *params; /* structure copy */
121 return ret;
122}
123
124static void decode_params(game_params *params, char const *string) {
125 params->w = params->h = atoi(string);
126
127 while (*string && isdigit((unsigned char) *string)) ++string;
128 if (*string == 'x') {
129 string++;
130 params->h = atoi(string);
131 while (*string && isdigit((unsigned char)*string)) string++;
132 }
133
134 params->diff = DIFF_NORMAL;
135 if (*string == 'd') {
136 int i;
137 string++;
138 for (i = 0; i < DIFFCOUNT; i++)
139 if (*string == undead_diffchars[i])
140 params->diff = i;
141 if (*string) string++;
142 }
143
144 return;
145}
146
147static char *encode_params(const game_params *params, int full)
148{
149 char buf[256];
150 sprintf(buf, "%dx%d", params->w, params->h);
151 if (full)
152 sprintf(buf + strlen(buf), "d%c", undead_diffchars[params->diff]);
153 return dupstr(buf);
154}
155
156static config_item *game_configure(const game_params *params)
157{
158 config_item *ret;
159 char buf[64];
160
161 ret = snewn(4, config_item);
162
163 ret[0].name = "Width";
164 ret[0].type = C_STRING;
165 sprintf(buf, "%d", params->w);
166 ret[0].sval = dupstr(buf);
167 ret[0].ival = 0;
168
169 ret[1].name = "Height";
170 ret[1].type = C_STRING;
171 sprintf(buf, "%d", params->h);
172 ret[1].sval = dupstr(buf);
173 ret[1].ival = 0;
174
175 ret[2].name = "Difficulty";
176 ret[2].type = C_CHOICES;
177 ret[2].sval = DIFFCONFIG;
178 ret[2].ival = params->diff;
179
180 ret[3].name = NULL;
181 ret[3].type = C_END;
182 ret[3].sval = NULL;
183 ret[3].ival = 0;
184
185 return ret;
186}
187
188static game_params *custom_params(const config_item *cfg)
189{
190 game_params *ret = snew(game_params);
191
192 ret->w = atoi(cfg[0].sval);
193 ret->h = atoi(cfg[1].sval);
194 ret->diff = cfg[2].ival;
195 return ret;
196}
197
198static char *validate_params(const game_params *params, int full)
199{
200 if ((params->w * params->h ) > 54) return "Grid is too big";
201 if (params->w < 3) return "Width must be at least 3";
202 if (params->h < 3) return "Height must be at least 3";
203 if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating";
204 return NULL;
205}
206
207/* --------------------------------------------------------------- */
208/* Game state allocation, deallocation. */
209
210struct path {
211 int length;
212 int *p;
213 int grid_start;
214 int grid_end;
215 int num_monsters;
216 int *mapping;
217 int sightings_start;
218 int sightings_end;
219 int *xy;
220};
221
222struct game_common {
223 int refcount;
224 struct game_params params;
225 int wh;
226 int num_ghosts,num_vampires,num_zombies,num_total;
227 int num_paths;
228 struct path *paths;
229 int *grid;
230 int *xinfo;
231 int *fixed;
232 int solved;
233};
234
235struct game_state {
236 struct game_common *common;
237 int *guess;
238 unsigned char *pencils;
239 unsigned char *cell_errors;
240 unsigned char *hint_errors;
241 unsigned char *hints_done;
242 unsigned char count_errors[3];
243 int solved;
244 int cheated;
245};
246
247static game_state *new_state(const game_params *params) {
248 int i;
249 game_state *state = snew(game_state);
250 state->common = snew(struct game_common);
251
252 state->common->refcount = 1;
253 state->common->params.w = params->w;
254 state->common->params.h = params->h;
255 state->common->params.diff = params->diff;
256
257 state->common->wh = (state->common->params.w +2) * (state->common->params.h +2);
258
259 state->common->num_ghosts = 0;
260 state->common->num_vampires = 0;
261 state->common->num_zombies = 0;
262 state->common->num_total = 0;
263
264 state->common->grid = snewn(state->common->wh, int);
265 state->common->xinfo = snewn(state->common->wh, int);
266 state->common->fixed = NULL;
267 state->common->solved = FALSE;
268
269 state->common->num_paths =
270 state->common->params.w + state->common->params.h;
271 state->common->paths = snewn(state->common->num_paths, struct path);
272
273 for (i=0;i<state->common->num_paths;i++) {
274 state->common->paths[i].length = 0;
275 state->common->paths[i].grid_start = -1;
276 state->common->paths[i].grid_end = -1;
277 state->common->paths[i].num_monsters = 0;
278 state->common->paths[i].sightings_start = 0;
279 state->common->paths[i].sightings_end = 0;
280 state->common->paths[i].p = snewn(state->common->wh,int);
281 state->common->paths[i].xy = snewn(state->common->wh,int);
282 state->common->paths[i].mapping = snewn(state->common->wh,int);
283 }
284
285 state->guess = NULL;
286 state->pencils = NULL;
287
288 state->cell_errors = snewn(state->common->wh, unsigned char);
289 for (i=0;i<state->common->wh;i++)
290 state->cell_errors[i] = FALSE;
291 state->hint_errors = snewn(2*state->common->num_paths, unsigned char);
292 for (i=0;i<2*state->common->num_paths;i++)
293 state->hint_errors[i] = FALSE;
294 state->hints_done = snewn(2 * state->common->num_paths, unsigned char);
295 memset(state->hints_done, 0,
296 2 * state->common->num_paths * sizeof(unsigned char));
297 for (i=0;i<3;i++)
298 state->count_errors[i] = FALSE;
299
300 state->solved = FALSE;
301 state->cheated = FALSE;
302
303 return state;
304}
305
306static game_state *dup_game(const game_state *state)
307{
308 game_state *ret = snew(game_state);
309
310 ret->common = state->common;
311 ret->common->refcount++;
312
313 if (state->guess != NULL) {
314 ret->guess = snewn(ret->common->num_total,int);
315 memcpy(ret->guess, state->guess, ret->common->num_total*sizeof(int));
316 }
317 else ret->guess = NULL;
318
319 if (state->pencils != NULL) {
320 ret->pencils = snewn(ret->common->num_total,unsigned char);
321 memcpy(ret->pencils, state->pencils,
322 ret->common->num_total*sizeof(unsigned char));
323 }
324 else ret->pencils = NULL;
325
326 if (state->cell_errors != NULL) {
327 ret->cell_errors = snewn(ret->common->wh,unsigned char);
328 memcpy(ret->cell_errors, state->cell_errors,
329 ret->common->wh*sizeof(unsigned char));
330 }
331 else ret->cell_errors = NULL;
332
333 if (state->hint_errors != NULL) {
334 ret->hint_errors = snewn(2*ret->common->num_paths,unsigned char);
335 memcpy(ret->hint_errors, state->hint_errors,
336 2*ret->common->num_paths*sizeof(unsigned char));
337 }
338 else ret->hint_errors = NULL;
339
340 if (state->hints_done != NULL) {
341 ret->hints_done = snewn(2 * state->common->num_paths, unsigned char);
342 memcpy(ret->hints_done, state->hints_done,
343 2 * state->common->num_paths * sizeof(unsigned char));
344 }
345 else ret->hints_done = NULL;
346
347 ret->count_errors[0] = state->count_errors[0];
348 ret->count_errors[1] = state->count_errors[1];
349 ret->count_errors[2] = state->count_errors[2];
350
351 ret->solved = state->solved;
352 ret->cheated = state->cheated;
353
354 return ret;
355}
356
357static void free_game(game_state *state) {
358 int i;
359
360 state->common->refcount--;
361 if (state->common->refcount == 0) {
362 for (i=0;i<state->common->num_paths;i++) {
363 sfree(state->common->paths[i].mapping);
364 sfree(state->common->paths[i].xy);
365 sfree(state->common->paths[i].p);
366 }
367 sfree(state->common->paths);
368 sfree(state->common->xinfo);
369 sfree(state->common->grid);
370 if (state->common->fixed != NULL) sfree(state->common->fixed);
371 sfree(state->common);
372 }
373 if (state->hints_done != NULL) sfree(state->hints_done);
374 if (state->hint_errors != NULL) sfree(state->hint_errors);
375 if (state->cell_errors != NULL) sfree(state->cell_errors);
376 if (state->pencils != NULL) sfree(state->pencils);
377 if (state->guess != NULL) sfree(state->guess);
378 sfree(state);
379
380 return;
381}
382
383/* --------------------------------------------------------------- */
384/* Puzzle generator */
385
386/* cell states */
387enum {
388 CELL_EMPTY,
389 CELL_MIRROR_L,
390 CELL_MIRROR_R,
391 CELL_GHOST,
392 CELL_VAMPIRE,
393 CELL_ZOMBIE,
394 CELL_UNDEF
395};
396
397/* grid walk directions */
398enum {
399 DIRECTION_NONE,
400 DIRECTION_UP,
401 DIRECTION_RIGHT,
402 DIRECTION_LEFT,
403 DIRECTION_DOWN
404};
405
406int range2grid(int rangeno, int width, int height, int *x, int *y) {
407
408 if (rangeno < 0) {
409 *x = 0; *y = 0; return DIRECTION_NONE;
410 }
411 if (rangeno < width) {
412 *x = rangeno+1; *y = 0; return DIRECTION_DOWN;
413 }
414 rangeno = rangeno - width;
415 if (rangeno < height) {
416 *x = width+1; *y = rangeno+1; return DIRECTION_LEFT;
417 }
418 rangeno = rangeno - height;
419 if (rangeno < width) {
420 *x = width-rangeno; *y = height+1; return DIRECTION_UP;
421 }
422 rangeno = rangeno - width;
423 if (rangeno < height) {
424 *x = 0; *y = height-rangeno; return DIRECTION_RIGHT;
425 }
426 *x = 0; *y = 0;
427 return DIRECTION_NONE;
428}
429
430int grid2range(int x, int y, int w, int h) {
431 if (x>0 && x<w+1 && y>0 && y<h+1) return -1;
432 if (x<0 || x>w+1 || y<0 || y>h+1) return -1;
433 if ((x == 0 || x==w+1) && (y==0 || y==h+1)) return -1;
434 if (y==0) return x-1;
435 if (x==(w+1)) return y-1+w;
436 if (y==(h+1)) return 2*w + h - x;
437 return 2*(w+h) - y;
438}
439
440void make_paths(game_state *state) {
441 int i;
442 int count = 0;
443
444 for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) {
445 int x,y,dir;
446 int j,k,num_monsters;
447 int found;
448 int c,p;
449 found = FALSE;
450 /* Check whether inverse path is already in list */
451 for (j=0;j<count;j++) {
452 if (i == state->common->paths[j].grid_end) {
453 found = TRUE;
454 break;
455 }
456 }
457 if (found) continue;
458
459 /* We found a new path through the mirror maze */
460 state->common->paths[count].grid_start = i;
461 dir = range2grid(i, state->common->params.w,
462 state->common->params.h,&x,&y);
463 state->common->paths[count].sightings_start =
464 state->common->grid[x+y*(state->common->params.w +2)];
465 while (TRUE) {
466 int c,r;
467
468 if (dir == DIRECTION_DOWN) y++;
469 else if (dir == DIRECTION_LEFT) x--;
470 else if (dir == DIRECTION_UP) y--;
471 else if (dir == DIRECTION_RIGHT) x++;
472
473 r = grid2range(x, y, state->common->params.w,
474 state->common->params.h);
475 if (r != -1) {
476 state->common->paths[count].grid_end = r;
477 state->common->paths[count].sightings_end =
478 state->common->grid[x+y*(state->common->params.w +2)];
479 break;
480 }
481
482 c = state->common->grid[x+y*(state->common->params.w+2)];
483 state->common->paths[count].xy[state->common->paths[count].length] =
484 x+y*(state->common->params.w+2);
485 if (c == CELL_MIRROR_L) {
486 state->common->paths[count].p[state->common->paths[count].length] = -1;
487 if (dir == DIRECTION_DOWN) dir = DIRECTION_RIGHT;
488 else if (dir == DIRECTION_LEFT) dir = DIRECTION_UP;
489 else if (dir == DIRECTION_UP) dir = DIRECTION_LEFT;
490 else if (dir == DIRECTION_RIGHT) dir = DIRECTION_DOWN;
491 }
492 else if (c == CELL_MIRROR_R) {
493 state->common->paths[count].p[state->common->paths[count].length] = -1;
494 if (dir == DIRECTION_DOWN) dir = DIRECTION_LEFT;
495 else if (dir == DIRECTION_LEFT) dir = DIRECTION_DOWN;
496 else if (dir == DIRECTION_UP) dir = DIRECTION_RIGHT;
497 else if (dir == DIRECTION_RIGHT) dir = DIRECTION_UP;
498 }
499 else {
500 state->common->paths[count].p[state->common->paths[count].length] =
501 state->common->xinfo[x+y*(state->common->params.w+2)];
502 }
503 state->common->paths[count].length++;
504 }
505 /* Count unique monster entries in each path */
506 state->common->paths[count].num_monsters = 0;
507 for (j=0;j<state->common->num_total;j++) {
508 num_monsters = 0;
509 for (k=0;k<state->common->paths[count].length;k++)
510 if (state->common->paths[count].p[k] == j)
511 num_monsters++;
512 if (num_monsters > 0)
513 state->common->paths[count].num_monsters++;
514 }
515
516 /* Generate mapping vector */
517 c = 0;
518 for (p=0;p<state->common->paths[count].length;p++) {
519 int m;
520 m = state->common->paths[count].p[p];
521 if (m == -1) continue;
522 found = FALSE;
523 for (j=0; j<c; j++)
524 if (state->common->paths[count].mapping[j] == m) found = TRUE;
525 if (!found) state->common->paths[count].mapping[c++] = m;
526 }
527 count++;
528 }
529 return;
530}
531
532struct guess {
533 int length;
534 int *guess;
535 int *possible;
536};
537
538int next_list(struct guess *g, int pos) {
539
540 if (pos == 0) {
541 if ((g->guess[pos] == 1 && g->possible[pos] == 1) ||
542 (g->guess[pos] == 2 && (g->possible[pos] == 3 ||
543 g->possible[pos] == 2)) ||
544 g->guess[pos] == 4)
545 return FALSE;
546 if (g->guess[pos] == 1 && (g->possible[pos] == 3 ||
547 g->possible[pos] == 7)) {
548 g->guess[pos] = 2; return TRUE;
549 }
550 if (g->guess[pos] == 1 && g->possible[pos] == 5) {
551 g->guess[pos] = 4; return TRUE;
552 }
553 if (g->guess[pos] == 2 && (g->possible[pos] == 6 || g->possible[pos] == 7)) {
554 g->guess[pos] = 4; return TRUE;
555 }
556 }
557
558 if (g->guess[pos] == 1) {
559 if (g->possible[pos] == 1) {
560 return next_list(g,pos-1);
561 }
562 if (g->possible[pos] == 3 || g->possible[pos] == 7) {
563 g->guess[pos] = 2; return TRUE;
564 }
565 if (g->possible[pos] == 5) {
566 g->guess[pos] = 4; return TRUE;
567 }
568 }
569
570 if (g->guess[pos] == 2) {
571 if (g->possible[pos] == 2) {
572 return next_list(g,pos-1);
573 }
574 if (g->possible[pos] == 3) {
575 g->guess[pos] = 1; return next_list(g,pos-1);
576 }
577 if (g->possible[pos] == 6 || g->possible[pos] == 7) {
578 g->guess[pos] = 4; return TRUE;
579 }
580 }
581
582 if (g->guess[pos] == 4) {
583 if (g->possible[pos] == 5 || g->possible[pos] == 7) {
584 g->guess[pos] = 1; return next_list(g,pos-1);
585 }
586 if (g->possible[pos] == 6) {
587 g->guess[pos] = 2; return next_list(g,pos-1);
588 }
589 if (g->possible[pos] == 4) {
590 return next_list(g,pos-1);
591 }
592 }
593 return FALSE;
594}
595
596void get_unique(game_state *state, int counter, random_state *rs) {
597
598 int p,i,c,pathlimit,count_uniques;
599 struct guess path_guess;
600 int *view_count;
601
602 struct entry {
603 struct entry *link;
604 int *guess;
605 int start_view;
606 int end_view;
607 };
608
609 struct {
610 struct entry *head;
611 struct entry *node;
612 } views, single_views, test_views;
613
614 struct entry test_entry;
615
616 path_guess.length = state->common->paths[counter].num_monsters;
617 path_guess.guess = snewn(path_guess.length,int);
618 path_guess.possible = snewn(path_guess.length,int);
619 for (i=0;i<path_guess.length;i++)
620 path_guess.guess[i] = path_guess.possible[i] = 0;
621
622 for (p=0;p<path_guess.length;p++) {
623 path_guess.possible[p] =
624 state->guess[state->common->paths[counter].mapping[p]];
625 switch (path_guess.possible[p]) {
626 case 1: path_guess.guess[p] = 1; break;
627 case 2: path_guess.guess[p] = 2; break;
628 case 3: path_guess.guess[p] = 1; break;
629 case 4: path_guess.guess[p] = 4; break;
630 case 5: path_guess.guess[p] = 1; break;
631 case 6: path_guess.guess[p] = 2; break;
632 case 7: path_guess.guess[p] = 1; break;
633 }
634 }
635
636 views.head = NULL;
637 views.node = NULL;
638
639 pathlimit = state->common->paths[counter].length + 1;
640 view_count = snewn(pathlimit*pathlimit, int);
641 for (i = 0; i < pathlimit*pathlimit; i++)
642 view_count[i] = 0;
643
644 do {
645 int mirror, start_view, end_view;
646
647 mirror = FALSE;
648 start_view = 0;
649 for (p=0;p<state->common->paths[counter].length;p++) {
650 if (state->common->paths[counter].p[p] == -1) mirror = TRUE;
651 else {
652 for (i=0;i<path_guess.length;i++) {
653 if (state->common->paths[counter].p[p] ==
654 state->common->paths[counter].mapping[i]) {
655 if (path_guess.guess[i] == 1 && mirror == TRUE)
656 start_view++;
657 if (path_guess.guess[i] == 2 && mirror == FALSE)
658 start_view++;
659 if (path_guess.guess[i] == 4)
660 start_view++;
661 break;
662 }
663 }
664 }
665 }
666 mirror = FALSE;
667 end_view = 0;
668 for (p=state->common->paths[counter].length-1;p>=0;p--) {
669 if (state->common->paths[counter].p[p] == -1) mirror = TRUE;
670 else {
671 for (i=0;i<path_guess.length;i++) {
672 if (state->common->paths[counter].p[p] ==
673 state->common->paths[counter].mapping[i]) {
674 if (path_guess.guess[i] == 1 && mirror == TRUE)
675 end_view++;
676 if (path_guess.guess[i] == 2 && mirror == FALSE)
677 end_view++;
678 if (path_guess.guess[i] == 4)
679 end_view++;
680 break;
681 }
682 }
683 }
684 }
685
686 assert(start_view >= 0 && start_view < pathlimit);
687 assert(end_view >= 0 && end_view < pathlimit);
688 i = start_view * pathlimit + end_view;
689 view_count[i]++;
690 if (view_count[i] == 1) {
691 views.node = snewn(1,struct entry);
692 views.node->link = views.head;
693 views.node->guess = snewn(path_guess.length,int);
694 views.head = views.node;
695 views.node->start_view = start_view;
696 views.node->end_view = end_view;
697 memcpy(views.node->guess, path_guess.guess,
698 path_guess.length*sizeof(int));
699 }
700 } while (next_list(&path_guess, path_guess.length-1));
701
702 /* extract single entries from view list */
703
704 test_views.head = views.head;
705 test_views.node = views.node;
706
707 test_entry.guess = snewn(path_guess.length,int);
708
709 single_views.head = NULL;
710 single_views.node = NULL;
711
712 count_uniques = 0;
713 while (test_views.head != NULL) {
714 test_views.node = test_views.head;
715 test_views.head = test_views.head->link;
716 i = test_views.node->start_view * pathlimit + test_views.node->end_view;
717 if (view_count[i] == 1) {
718 single_views.node = snewn(1,struct entry);
719 single_views.node->link = single_views.head;
720 single_views.node->guess = snewn(path_guess.length,int);
721 single_views.head = single_views.node;
722 single_views.node->start_view = test_views.node->start_view;
723 single_views.node->end_view = test_views.node->end_view;
724 memcpy(single_views.node->guess, test_views.node->guess,
725 path_guess.length*sizeof(int));
726 count_uniques++;
727 }
728 }
729
730 sfree(view_count);
731
732 if (count_uniques > 0) {
733 test_entry.start_view = 0;
734 test_entry.end_view = 0;
735 /* Choose one unique guess per random */
736 /* While we are busy with looping through single_views, we
737 * conveniently free the linked list single_view */
738 c = random_upto(rs,count_uniques);
739 while(single_views.head != NULL) {
740 single_views.node = single_views.head;
741 single_views.head = single_views.head->link;
742 if (c-- == 0) {
743 memcpy(test_entry.guess, single_views.node->guess,
744 path_guess.length*sizeof(int));
745 test_entry.start_view = single_views.node->start_view;
746 test_entry.end_view = single_views.node->end_view;
747 }
748 sfree(single_views.node->guess);
749 sfree(single_views.node);
750 }
751
752 /* Modify state_guess according to path_guess.mapping */
753 for (i=0;i<path_guess.length;i++)
754 state->guess[state->common->paths[counter].mapping[i]] =
755 test_entry.guess[i];
756 }
757
758 sfree(test_entry.guess);
759
760 while (views.head != NULL) {
761 views.node = views.head;
762 views.head = views.head->link;
763 sfree(views.node->guess);
764 sfree(views.node);
765 }
766
767 sfree(path_guess.possible);
768 sfree(path_guess.guess);
769
770 return;
771}
772
773int count_monsters(game_state *state,
774 int *cGhost, int *cVampire, int *cZombie) {
775 int cNone;
776 int i;
777
778 *cGhost = *cVampire = *cZombie = cNone = 0;
779
780 for (i=0;i<state->common->num_total;i++) {
781 if (state->guess[i] == 1) (*cGhost)++;
782 else if (state->guess[i] == 2) (*cVampire)++;
783 else if (state->guess[i] == 4) (*cZombie)++;
784 else cNone++;
785 }
786
787 return cNone;
788}
789
790int check_numbers(game_state *state, int *guess) {
791 int valid;
792 int i;
793 int count_ghosts, count_vampires, count_zombies;
794
795 count_ghosts = count_vampires = count_zombies = 0;
796 for (i=0;i<state->common->num_total;i++) {
797 if (guess[i] == 1) count_ghosts++;
798 if (guess[i] == 2) count_vampires++;
799 if (guess[i] == 4) count_zombies++;
800 }
801
802 valid = TRUE;
803
804 if (count_ghosts > state->common->num_ghosts) valid = FALSE;
805 if (count_vampires > state->common->num_vampires) valid = FALSE;
806 if (count_zombies > state->common->num_zombies) valid = FALSE;
807
808 return valid;
809}
810
811int check_solution(int *g, struct path path) {
812 int i;
813 int mirror;
814 int count;
815
816 count = 0;
817 mirror = FALSE;
818 for (i=0;i<path.length;i++) {
819 if (path.p[i] == -1) mirror = TRUE;
820 else {
821 if (g[path.p[i]] == 1 && mirror) count++;
822 else if (g[path.p[i]] == 2 && !mirror) count++;
823 else if (g[path.p[i]] == 4) count++;
824 }
825 }
826 if (count != path.sightings_start) return FALSE;
827
828 count = 0;
829 mirror = FALSE;
830 for (i=path.length-1;i>=0;i--) {
831 if (path.p[i] == -1) mirror = TRUE;
832 else {
833 if (g[path.p[i]] == 1 && mirror) count++;
834 else if (g[path.p[i]] == 2 && !mirror) count++;
835 else if (g[path.p[i]] == 4) count++;
836 }
837 }
838 if (count != path.sightings_end) return FALSE;
839
840 return TRUE;
841}
842
843int solve_iterative(game_state *state, struct path *paths) {
844 int solved;
845 int p,i,j,count;
846
847 int *guess;
848 int *possible;
849
850 struct guess loop;
851
852 solved = TRUE;
853 loop.length = state->common->num_total;
854 guess = snewn(state->common->num_total,int);
855 possible = snewn(state->common->num_total,int);
856
857 for (i=0;i<state->common->num_total;i++) {
858 guess[i] = state->guess[i];
859 possible[i] = 0;
860 }
861
862 for (p=0;p<state->common->num_paths;p++) {
863 if (paths[p].num_monsters > 0) {
864 loop.length = paths[p].num_monsters;
865 loop.guess = snewn(paths[p].num_monsters,int);
866 loop.possible = snewn(paths[p].num_monsters,int);
867
868 for (i=0;i<paths[p].num_monsters;i++) {
869 switch (state->guess[paths[p].mapping[i]]) {
870 case 1: loop.guess[i] = 1; break;
871 case 2: loop.guess[i] = 2; break;
872 case 3: loop.guess[i] = 1; break;
873 case 4: loop.guess[i] = 4; break;
874 case 5: loop.guess[i] = 1; break;
875 case 6: loop.guess[i] = 2; break;
876 case 7: loop.guess[i] = 1; break;
877 }
878 loop.possible[i] = state->guess[paths[p].mapping[i]];
879 possible[paths[p].mapping[i]] = 0;
880 }
881
882 while(TRUE) {
883 for (i=0;i<state->common->num_total;i++) {
884 guess[i] = state->guess[i];
885 }
886 count = 0;
887 for (i=0;i<paths[p].num_monsters;i++)
888 guess[paths[p].mapping[i]] = loop.guess[count++];
889 if (check_numbers(state,guess) &&
890 check_solution(guess,paths[p]))
891 for (j=0;j<paths[p].num_monsters;j++)
892 possible[paths[p].mapping[j]] |= loop.guess[j];
893 if (!next_list(&loop,loop.length-1)) break;
894 }
895 for (i=0;i<paths[p].num_monsters;i++)
896 state->guess[paths[p].mapping[i]] &=
897 possible[paths[p].mapping[i]];
898 sfree(loop.possible);
899 sfree(loop.guess);
900 }
901 }
902
903 for (i=0;i<state->common->num_total;i++) {
904 if (state->guess[i] == 3 || state->guess[i] == 5 ||
905 state->guess[i] == 6 || state->guess[i] == 7) {
906 solved = FALSE; break;
907 }
908 }
909
910 sfree(possible);
911 sfree(guess);
912
913 return solved;
914}
915
916int solve_bruteforce(game_state *state, struct path *paths) {
917 int solved, correct;
918 int number_solutions;
919 int p,i;
920
921 struct guess loop;
922
923 loop.guess = snewn(state->common->num_total,int);
924 loop.possible = snewn(state->common->num_total,int);
925
926 for (i=0;i<state->common->num_total;i++) {
927 loop.possible[i] = state->guess[i];
928 switch (state->guess[i]) {
929 case 1: loop.guess[i] = 1; break;
930 case 2: loop.guess[i] = 2; break;
931 case 3: loop.guess[i] = 1; break;
932 case 4: loop.guess[i] = 4; break;
933 case 5: loop.guess[i] = 1; break;
934 case 6: loop.guess[i] = 2; break;
935 case 7: loop.guess[i] = 1; break;
936 }
937 }
938
939 solved = FALSE;
940 number_solutions = 0;
941
942 while (TRUE) {
943
944 correct = TRUE;
945 if (!check_numbers(state,loop.guess)) correct = FALSE;
946 else
947 for (p=0;p<state->common->num_paths;p++)
948 if (!check_solution(loop.guess,paths[p])) {
949 correct = FALSE; break;
950 }
951 if (correct) {
952 number_solutions++;
953 solved = TRUE;
954 if(number_solutions > 1) {
955 solved = FALSE;
956 break;
957 }
958 for (i=0;i<state->common->num_total;i++)
959 state->guess[i] = loop.guess[i];
960 }
961 if (!next_list(&loop,state->common->num_total -1)) {
962 break;
963 }
964 }
965
966 sfree(loop.possible);
967 sfree(loop.guess);
968
969 return solved;
970}
971
972int path_cmp(const void *a, const void *b) {
973 const struct path *pa = (const struct path *)a;
974 const struct path *pb = (const struct path *)b;
975 return pa->num_monsters - pb->num_monsters;
976}
977
978static char *new_game_desc(const game_params *params, random_state *rs,
979 char **aux, int interactive) {
980 int i,count,c,w,h,r,p,g;
981 game_state *new;
982
983 /* Variables for puzzle generation algorithm */
984 int filling;
985 int max_length;
986 int count_ghosts, count_vampires, count_zombies;
987 int abort;
988 float ratio;
989
990 /* Variables for solver algorithm */
991 int solved_iterative, solved_bruteforce, contains_inconsistency,
992 count_ambiguous;
993 int iterative_depth;
994 int *old_guess;
995
996 /* Variables for game description generation */
997 int x,y;
998 char *e;
999 char *desc;
1000
1001 i = 0;
1002 while (TRUE) {
1003 new = new_state(params);
1004 abort = FALSE;
1005
1006 /* Fill grid with random mirrors and (later to be populated)
1007 * empty monster cells */
1008 count = 0;
1009 for (h=1;h<new->common->params.h+1;h++)
1010 for (w=1;w<new->common->params.w+1;w++) {
1011 c = random_upto(rs,5);
1012 if (c >= 2) {
1013 new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY;
1014 new->common->xinfo[w+h*(new->common->params.w+2)] = count++;
1015 }
1016 else if (c == 0) {
1017 new->common->grid[w+h*(new->common->params.w+2)] =
1018 CELL_MIRROR_L;
1019 new->common->xinfo[w+h*(new->common->params.w+2)] = -1;
1020 }
1021 else {
1022 new->common->grid[w+h*(new->common->params.w+2)] =
1023 CELL_MIRROR_R;
1024 new->common->xinfo[w+h*(new->common->params.w+2)] = -1;
1025 }
1026 }
1027 new->common->num_total = count; /* Total number of monsters in maze */
1028
1029 /* Puzzle is boring if it has too few monster cells. Discard
1030 * grid, make new grid */
1031 if (new->common->num_total <= 4) {
1032 free_game(new);
1033 continue;
1034 }
1035
1036 /* Monsters / Mirrors ratio should be balanced */
1037 ratio = (float)new->common->num_total /
1038 (float)(new->common->params.w * new->common->params.h);
1039 if (ratio < 0.48 || ratio > 0.78) {
1040 free_game(new);
1041 continue;
1042 }
1043
1044 /* Assign clue identifiers */
1045 for (r=0;r<2*(new->common->params.w+new->common->params.h);r++) {
1046 int x,y,gridno;
1047 gridno = range2grid(r,new->common->params.w,new->common->params.h,
1048 &x,&y);
1049 new->common->grid[x+y*(new->common->params.w +2)] = gridno;
1050 new->common->xinfo[x+y*(new->common->params.w +2)] = 0;
1051 }
1052 /* The four corners don't matter at all for the game. Set them
1053 * all to zero, just to have a nice data structure */
1054 new->common->grid[0] = 0;
1055 new->common->xinfo[0] = 0;
1056 new->common->grid[new->common->params.w+1] = 0;
1057 new->common->xinfo[new->common->params.w+1] = 0;
1058 new->common->grid[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0;
1059 new->common->xinfo[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0;
1060 new->common->grid[(new->common->params.h+1)*(new->common->params.w+2)] = 0;
1061 new->common->xinfo[(new->common->params.h+1)*(new->common->params.w+2)] = 0;
1062
1063 /* Initialize solution vector */
1064 new->guess = snewn(new->common->num_total,int);
1065 for (g=0;g<new->common->num_total;g++) new->guess[g] = 7;
1066
1067 /* Initialize fixed flag from common. Not needed for the
1068 * puzzle generator; initialize it for having clean code */
1069 new->common->fixed = snewn(new->common->num_total,int);
1070 for (g=0;g<new->common->num_total;g++)
1071 new->common->fixed[g] = FALSE;
1072
1073 /* paths generation */
1074 make_paths(new);
1075
1076 /* Grid is invalid if max. path length > threshold. Discard
1077 * grid, make new one */
1078 switch (new->common->params.diff) {
1079 case DIFF_EASY: max_length = min(new->common->params.w,new->common->params.h) + 1; break;
1080 case DIFF_NORMAL: max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break;
1081 case DIFF_TRICKY: max_length = 9; break;
1082 default: max_length = 9; break;
1083 }
1084
1085 for (p=0;p<new->common->num_paths;p++) {
1086 if (new->common->paths[p].num_monsters > max_length) {
1087 abort = TRUE;
1088 }
1089 }
1090 if (abort) {
1091 free_game(new);
1092 continue;
1093 }
1094
1095 qsort(new->common->paths, new->common->num_paths,
1096 sizeof(struct path), path_cmp);
1097
1098 /* Grid monster initialization */
1099 /* For easy puzzles, we try to fill nearly the whole grid
1100 with unique solution paths (up to 2) For more difficult
1101 puzzles, we fill only roughly half the grid, and choose
1102 random monsters for the rest For hard puzzles, we fill
1103 even less paths with unique solutions */
1104
1105 switch (new->common->params.diff) {
1106 case DIFF_EASY: filling = 2; break;
1107 case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
1108 case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
1109 default: filling = 0; break;
1110 }
1111
1112 count = 0;
1113 while ( (count_monsters(new, &count_ghosts, &count_vampires,
1114 &count_zombies)) > filling) {
1115 if ((count) >= new->common->num_paths) break;
1116 if (new->common->paths[count].num_monsters == 0) {
1117 count++;
1118 continue;
1119 }
1120 get_unique(new,count,rs);
1121 count++;
1122 }
1123
1124 /* Fill any remaining ambiguous entries with random monsters */
1125 for(g=0;g<new->common->num_total;g++) {
1126 if (new->guess[g] == 7) {
1127 r = random_upto(rs,3);
1128 new->guess[g] = (r == 0) ? 1 : ( (r == 1) ? 2 : 4 );
1129 }
1130 }
1131
1132 /* Determine all hints */
1133 count_monsters(new, &new->common->num_ghosts,
1134 &new->common->num_vampires, &new->common->num_zombies);
1135
1136 /* Puzzle is trivial if it has only one type of monster. Discard. */
1137 if ((new->common->num_ghosts == 0 && new->common->num_vampires == 0) ||
1138 (new->common->num_ghosts == 0 && new->common->num_zombies == 0) ||
1139 (new->common->num_vampires == 0 && new->common->num_zombies == 0)) {
1140 free_game(new);
1141 continue;
1142 }
1143
1144 /* Discard puzzle if difficulty Tricky, and it has only 1
1145 * member of any monster type */
1146 if (new->common->params.diff == DIFF_TRICKY &&
1147 (new->common->num_ghosts <= 1 ||
1148 new->common->num_vampires <= 1 || new->common->num_zombies <= 1)) {
1149 free_game(new);
1150 continue;
1151 }
1152
1153 for (w=1;w<new->common->params.w+1;w++)
1154 for (h=1;h<new->common->params.h+1;h++) {
1155 c = new->common->xinfo[w+h*(new->common->params.w+2)];
1156 if (c >= 0) {
1157 if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST;
1158 if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE;
1159 if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE;
1160 }
1161 }
1162
1163 /* Prepare path information needed by the solver (containing all hints) */
1164 for (p=0;p<new->common->num_paths;p++) {
1165 int mirror,x,y;
1166
1167 new->common->paths[p].sightings_start = 0;
1168 new->common->paths[p].sightings_end = 0;
1169
1170 mirror = FALSE;
1171 for (g=0;g<new->common->paths[p].length;g++) {
1172
1173 if (new->common->paths[p].p[g] == -1) mirror = TRUE;
1174 else {
1175 if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_start)++;
1176 else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_start)++;
1177 else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_start)++;
1178 }
1179 }
1180
1181 mirror = FALSE;
1182 for (g=new->common->paths[p].length-1;g>=0;g--) {
1183 if (new->common->paths[p].p[g] == -1) mirror = TRUE;
1184 else {
1185 if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_end)++;
1186 else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_end)++;
1187 else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_end)++;
1188 }
1189 }
1190
1191 range2grid(new->common->paths[p].grid_start,
1192 new->common->params.w,new->common->params.h,&x,&y);
1193 new->common->grid[x+y*(new->common->params.w +2)] =
1194 new->common->paths[p].sightings_start;
1195 range2grid(new->common->paths[p].grid_end,
1196 new->common->params.w,new->common->params.h,&x,&y);
1197 new->common->grid[x+y*(new->common->params.w +2)] =
1198 new->common->paths[p].sightings_end;
1199 }
1200
1201 /* Try to solve the puzzle with the iterative solver */
1202 old_guess = snewn(new->common->num_total,int);
1203 for (p=0;p<new->common->num_total;p++) {
1204 new->guess[p] = 7;
1205 old_guess[p] = 7;
1206 }
1207 iterative_depth = 0;
1208 solved_iterative = FALSE;
1209 contains_inconsistency = FALSE;
1210 count_ambiguous = 0;
1211
1212 while (TRUE) {
1213 int no_change;
1214 no_change = TRUE;
1215 solved_iterative = solve_iterative(new,new->common->paths);
1216 iterative_depth++;
1217 for (p=0;p<new->common->num_total;p++) {
1218 if (new->guess[p] != old_guess[p]) no_change = FALSE;
1219 old_guess[p] = new->guess[p];
1220 if (new->guess[p] == 0) contains_inconsistency = TRUE;
1221 }
1222 if (solved_iterative || no_change) break;
1223 }
1224
1225 /* If necessary, try to solve the puzzle with the brute-force solver */
1226 solved_bruteforce = FALSE;
1227 if (new->common->params.diff != DIFF_EASY &&
1228 !solved_iterative && !contains_inconsistency) {
1229 for (p=0;p<new->common->num_total;p++)
1230 if (new->guess[p] != 1 && new->guess[p] != 2 &&
1231 new->guess[p] != 4) count_ambiguous++;
1232
1233 solved_bruteforce = solve_bruteforce(new, new->common->paths);
1234 }
1235
1236 /* Determine puzzle difficulty level */
1237 if (new->common->params.diff == DIFF_EASY && solved_iterative &&
1238 iterative_depth <= 3 && !contains_inconsistency) {
1239/* printf("Puzzle level: EASY Level %d Ratio %f Ambiguous %d (Found after %i tries)\n",iterative_depth, ratio, count_ambiguous, i); */
1240 break;
1241 }
1242
1243 if (new->common->params.diff == DIFF_NORMAL &&
1244 ((solved_iterative && iterative_depth > 3) ||
1245 (solved_bruteforce && count_ambiguous < 4)) &&
1246 !contains_inconsistency) {
1247/* printf("Puzzle level: NORMAL Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */
1248 break;
1249 }
1250 if (new->common->params.diff == DIFF_TRICKY &&
1251 solved_bruteforce && iterative_depth > 0 &&
1252 count_ambiguous >= 4 && !contains_inconsistency) {
1253/* printf("Puzzle level: TRICKY Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */
1254 break;
1255 }
1256
1257 /* If puzzle is not solvable or does not satisfy the desired
1258 * difficulty level, free memory and start from scratch */
1259 sfree(old_guess);
1260 free_game(new);
1261 i++;
1262 }
1263
1264 /* We have a valid puzzle! */
1265
1266 desc = snewn(10 + new->common->wh +
1267 6*(new->common->params.w + new->common->params.h), char);
1268 e = desc;
1269
1270 /* Encode monster counts */
1271 e += sprintf(e, "%d,", new->common->num_ghosts);
1272 e += sprintf(e, "%d,", new->common->num_vampires);
1273 e += sprintf(e, "%d,", new->common->num_zombies);
1274
1275 /* Encode grid */
1276 count = 0;
1277 for (y=1;y<new->common->params.h+1;y++)
1278 for (x=1;x<new->common->params.w+1;x++) {
1279 c = new->common->grid[x+y*(new->common->params.w+2)];
1280 if (count > 25) {
1281 *e++ = 'z';
1282 count -= 26;
1283 }
1284 if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) {
1285 count++;
1286 }
1287 else if (c == CELL_MIRROR_L) {
1288 if (count > 0) *e++ = (count-1 + 'a');
1289 *e++ = 'L';
1290 count = 0;
1291 }
1292 else {
1293 if (count > 0) *e++ = (count-1 + 'a');
1294 *e++ = 'R';
1295 count = 0;
1296 }
1297 }
1298 if (count > 0) *e++ = (count-1 + 'a');
1299
1300 /* Encode hints */
1301 for (p=0;p<2*(new->common->params.w + new->common->params.h);p++) {
1302 range2grid(p,new->common->params.w,new->common->params.h,&x,&y);
1303 e += sprintf(e, ",%d", new->common->grid[x+y*(new->common->params.w+2)]);
1304 }
1305
1306 *e++ = '\0';
1307 desc = sresize(desc, e - desc, char);
1308
1309 sfree(old_guess);
1310 free_game(new);
1311
1312 return desc;
1313}
1314
1315void num2grid(int num, int width, int height, int *x, int *y) {
1316 *x = 1+(num%width);
1317 *y = 1+(num/width);
1318 return;
1319}
1320
1321static game_state *new_game(midend *me, const game_params *params,
1322 const char *desc)
1323{
1324 int i;
1325 int n;
1326 int count;
1327
1328 game_state *state = new_state(params);
1329
1330 state->common->num_ghosts = atoi(desc);
1331 while (*desc && isdigit((unsigned char)*desc)) desc++;
1332 desc++;
1333 state->common->num_vampires = atoi(desc);
1334 while (*desc && isdigit((unsigned char)*desc)) desc++;
1335 desc++;
1336 state->common->num_zombies = atoi(desc);
1337 while (*desc && isdigit((unsigned char)*desc)) desc++;
1338 desc++;
1339
1340 state->common->num_total = state->common->num_ghosts + state->common->num_vampires + state->common->num_zombies;
1341
1342 state->guess = snewn(state->common->num_total,int);
1343 state->pencils = snewn(state->common->num_total,unsigned char);
1344 state->common->fixed = snewn(state->common->num_total,int);
1345 for (i=0;i<state->common->num_total;i++) {
1346 state->guess[i] = 7;
1347 state->pencils[i] = 0;
1348 state->common->fixed[i] = FALSE;
1349 }
1350 for (i=0;i<state->common->wh;i++)
1351 state->cell_errors[i] = FALSE;
1352 for (i=0;i<2*state->common->num_paths;i++)
1353 state->hint_errors[i] = FALSE;
1354 for (i=0;i<3;i++)
1355 state->count_errors[i] = FALSE;
1356
1357 count = 0;
1358 n = 0;
1359 while (*desc != ',') {
1360 int c;
1361 int x,y;
1362
1363 if (*desc == 'L') {
1364 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1365 state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_L;
1366 state->common->xinfo[x+y*(state->common->params.w+2)] = -1;
1367 n++;
1368 }
1369 else if (*desc == 'R') {
1370 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1371 state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_R;
1372 state->common->xinfo[x+y*(state->common->params.w+2)] = -1;
1373 n++;
1374 }
1375 else if (*desc == 'G') {
1376 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1377 state->common->grid[x+y*(state->common->params.w +2)] = CELL_GHOST;
1378 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1379 state->guess[count] = 1;
1380 state->common->fixed[count++] = TRUE;
1381 n++;
1382 }
1383 else if (*desc == 'V') {
1384 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1385 state->common->grid[x+y*(state->common->params.w +2)] = CELL_VAMPIRE;
1386 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1387 state->guess[count] = 2;
1388 state->common->fixed[count++] = TRUE;
1389 n++;
1390 }
1391 else if (*desc == 'Z') {
1392 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1393 state->common->grid[x+y*(state->common->params.w +2)] = CELL_ZOMBIE;
1394 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1395 state->guess[count] = 4;
1396 state->common->fixed[count++] = TRUE;
1397 n++;
1398 }
1399 else {
1400 c = *desc - ('a' -1);
1401 while (c-- > 0) {
1402 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1403 state->common->grid[x+y*(state->common->params.w +2)] = CELL_EMPTY;
1404 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1405 state->guess[count] = 7;
1406 state->common->fixed[count++] = FALSE;
1407 n++;
1408 }
1409 }
1410 desc++;
1411 }
1412 desc++;
1413
1414 for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) {
1415 int x,y;
1416 int sights;
1417
1418 sights = atoi(desc);
1419 while (*desc && isdigit((unsigned char)*desc)) desc++;
1420 desc++;
1421
1422
1423 range2grid(i,state->common->params.w,state->common->params.h,&x,&y);
1424 state->common->grid[x+y*(state->common->params.w +2)] = sights;
1425 state->common->xinfo[x+y*(state->common->params.w +2)] = -2;
1426 }
1427
1428 state->common->grid[0] = 0;
1429 state->common->xinfo[0] = -2;
1430 state->common->grid[state->common->params.w+1] = 0;
1431 state->common->xinfo[state->common->params.w+1] = -2;
1432 state->common->grid[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = 0;
1433 state->common->xinfo[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = -2;
1434 state->common->grid[(state->common->params.h+1)*(state->common->params.w+2)] = 0;
1435 state->common->xinfo[(state->common->params.h+1)*(state->common->params.w+2)] = -2;
1436
1437 make_paths(state);
1438 qsort(state->common->paths, state->common->num_paths, sizeof(struct path), path_cmp);
1439
1440 return state;
1441}
1442
1443static char *validate_desc(const game_params *params, const char *desc)
1444{
1445 int i;
1446 int w = params->w, h = params->h;
1447 int wh = w*h;
1448 int area;
1449 int monsters;
1450 int monster_count;
1451 const char *desc_s = desc;
1452
1453 for (i=0;i<3;i++) {
1454 if (!*desc) return "Faulty game description";
1455 while (*desc && isdigit((unsigned char)*desc)) { desc++; }
1456 if (*desc != ',') return "Invalid character in number list";
1457 desc++;
1458 }
1459 desc = desc_s;
1460
1461 area = monsters = monster_count = 0;
1462 for (i=0;i<3;i++) {
1463 monster_count += atoi(desc);
1464 while (*desc && isdigit((unsigned char)*desc)) desc++;
1465 desc++;
1466 }
1467 while (*desc && *desc != ',') {
1468 if (*desc >= 'a' && *desc <= 'z') {
1469 area += *desc - 'a' +1; monsters += *desc - 'a' +1;
1470 } else if (*desc == 'G' || *desc == 'V' || *desc == 'Z') {
1471 area++; monsters++;
1472 } else if (*desc == 'L' || *desc == 'R') {
1473 area++;
1474 } else
1475 return "Invalid character in grid specification";
1476 desc++;
1477 }
1478 if (area < wh) return "Not enough data to fill grid";
1479 else if (area > wh) return "Too much data to fill grid";
1480 if (monsters != monster_count)
1481 return "Monster numbers do not match grid spaces";
1482
1483 for (i = 0; i < 2*(w+h); i++) {
1484 if (!*desc) return "Not enough numbers given after grid specification";
1485 else if (*desc != ',') return "Invalid character in number list";
1486 desc++;
1487 while (*desc && isdigit((unsigned char)*desc)) { desc++; }
1488 }
1489
1490 if (*desc) return "Unexpected additional data at end of game description";
1491
1492 return NULL;
1493}
1494
1495static char *solve_game(const game_state *state_start, const game_state *currstate,
1496 const char *aux, char **error)
1497{
1498 int p;
1499 int *old_guess;
1500 int iterative_depth;
1501 int solved_iterative, solved_bruteforce, contains_inconsistency,
1502 count_ambiguous;
1503
1504 int i;
1505 char *move, *c;
1506
1507 game_state *solve_state = dup_game(currstate);
1508
1509 old_guess = snewn(solve_state->common->num_total,int);
1510 for (p=0;p<solve_state->common->num_total;p++) {
1511 if (solve_state->common->fixed[p]) {
1512 old_guess[p] = solve_state->guess[p] = state_start->guess[p];
1513 }
1514 else {
1515 old_guess[p] = solve_state->guess[p] = 7;
1516 }
1517 }
1518 iterative_depth = 0;
1519 solved_iterative = FALSE;
1520 contains_inconsistency = FALSE;
1521 count_ambiguous = 0;
1522
1523 /* Try to solve the puzzle with the iterative solver */
1524 while (TRUE) {
1525 int no_change;
1526 no_change = TRUE;
1527 solved_iterative =
1528 solve_iterative(solve_state,solve_state->common->paths);
1529 iterative_depth++;
1530 for (p=0;p<solve_state->common->num_total;p++) {
1531 if (solve_state->guess[p] != old_guess[p]) no_change = FALSE;
1532 old_guess[p] = solve_state->guess[p];
1533 if (solve_state->guess[p] == 0) contains_inconsistency = TRUE;
1534 }
1535 if (solved_iterative || no_change || contains_inconsistency) break;
1536 }
1537
1538 if (contains_inconsistency) {
1539 *error = "Puzzle is inconsistent";
1540 sfree(old_guess);
1541 free_game(solve_state);
1542 return NULL;
1543 }
1544
1545 /* If necessary, try to solve the puzzle with the brute-force solver */
1546 solved_bruteforce = FALSE;
1547 if (!solved_iterative) {
1548 for (p=0;p<solve_state->common->num_total;p++)
1549 if (solve_state->guess[p] != 1 && solve_state->guess[p] != 2 &&
1550 solve_state->guess[p] != 4) count_ambiguous++;
1551 solved_bruteforce =
1552 solve_bruteforce(solve_state, solve_state->common->paths);
1553 }
1554
1555 if (!solved_iterative && !solved_bruteforce) {
1556 *error = "Puzzle is unsolvable";
1557 sfree(old_guess);
1558 free_game(solve_state);
1559 return NULL;
1560 }
1561
1562/* printf("Puzzle solved at level %s, iterations %d, ambiguous %d\n", (solved_bruteforce ? "TRICKY" : "NORMAL"), iterative_depth, count_ambiguous); */
1563
1564 move = snewn(solve_state->common->num_total * 4 +2, char);
1565 c = move;
1566 *c++='S';
1567 for (i = 0; i < solve_state->common->num_total; i++) {
1568 if (solve_state->guess[i] == 1) c += sprintf(c, ";G%d", i);
1569 if (solve_state->guess[i] == 2) c += sprintf(c, ";V%d", i);
1570 if (solve_state->guess[i] == 4) c += sprintf(c, ";Z%d", i);
1571 }
1572 *c++ = '\0';
1573 move = sresize(move, c - move, char);
1574
1575 sfree(old_guess);
1576 free_game(solve_state);
1577 return move;
1578}
1579
1580static int game_can_format_as_text_now(const game_params *params)
1581{
1582 return TRUE;
1583}
1584
1585static char *game_text_format(const game_state *state)
1586{
1587 int w,h,c,r,xi,g;
1588 char *ret;
1589 char buf[120];
1590
1591 ret = snewn(50 + 6*(state->common->params.w +2) +
1592 6*(state->common->params.h+2) +
1593 3*(state->common->params.w * state->common->params.h), char);
1594
1595 sprintf(ret,"G: %d V: %d Z: %d\n\n",state->common->num_ghosts,
1596 state->common->num_vampires, state->common->num_zombies);
1597
1598 for (h=0;h<state->common->params.h+2;h++) {
1599 for (w=0;w<state->common->params.w+2;w++) {
1600 c = state->common->grid[w+h*(state->common->params.w+2)];
1601 xi = state->common->xinfo[w+h*(state->common->params.w+2)];
1602 r = grid2range(w,h,state->common->params.w,state->common->params.h);
1603 if (r != -1) {
1604 sprintf(buf,"%2d", c); strcat(ret,buf);
1605 } else if (c == CELL_MIRROR_L) {
1606 sprintf(buf," \\"); strcat(ret,buf);
1607 } else if (c == CELL_MIRROR_R) {
1608 sprintf(buf," /"); strcat(ret,buf);
1609 } else if (xi >= 0) {
1610 g = state->guess[xi];
1611 if (g == 1) { sprintf(buf," G"); strcat(ret,buf); }
1612 else if (g == 2) { sprintf(buf," V"); strcat(ret,buf); }
1613 else if (g == 4) { sprintf(buf," Z"); strcat(ret,buf); }
1614 else { sprintf(buf," ."); strcat(ret,buf); }
1615 } else {
1616 sprintf(buf," "); strcat(ret,buf);
1617 }
1618 }
1619 sprintf(buf,"\n"); strcat(ret,buf);
1620 }
1621
1622 return ret;
1623}
1624
1625struct game_ui {
1626 int hx, hy; /* as for solo.c, highlight pos */
1627 int hshow, hpencil, hcursor; /* show state, type, and ?cursor. */
1628 int ascii;
1629};
1630
1631static game_ui *new_ui(const game_state *state)
1632{
1633 game_ui *ui = snew(game_ui);
1634 ui->hx = ui->hy = 0;
1635 ui->hpencil = ui->hshow = ui->hcursor = 0;
1636 ui->ascii = FALSE;
1637 return ui;
1638}
1639
1640static void free_ui(game_ui *ui) {
1641 sfree(ui);
1642 return;
1643}
1644
1645static char *encode_ui(const game_ui *ui)
1646{
1647 return NULL;
1648}
1649
1650static void decode_ui(game_ui *ui, const char *encoding)
1651{
1652 return;
1653}
1654
1655static void game_changed_state(game_ui *ui, const game_state *oldstate,
1656 const game_state *newstate)
1657{
1658 /* See solo.c; if we were pencil-mode highlighting and
1659 * somehow a square has just been properly filled, cancel
1660 * pencil mode. */
1661 if (ui->hshow && ui->hpencil && !ui->hcursor) {
1662 int g = newstate->guess[newstate->common->xinfo[ui->hx + ui->hy*(newstate->common->params.w+2)]];
1663 if (g == 1 || g == 2 || g == 4)
1664 ui->hshow = 0;
1665 }
1666}
1667
1668struct game_drawstate {
1669 int tilesize, started, solved;
1670 int w, h;
1671
1672 int *monsters;
1673 unsigned char *pencils;
1674
1675 unsigned char count_errors[3];
1676 unsigned char *cell_errors;
1677 unsigned char *hint_errors;
1678 unsigned char *hints_done;
1679
1680 int hx, hy, hshow, hpencil; /* as for game_ui. */
1681 int hflash;
1682 int ascii;
1683};
1684
1685static int is_clue(const game_state *state, int x, int y)
1686{
1687 int h = state->common->params.h, w = state->common->params.w;
1688
1689 if (((x == 0 || x == w + 1) && y > 0 && y <= h) ||
1690 ((y == 0 || y == h + 1) && x > 0 && x <= w))
1691 return TRUE;
1692
1693 return FALSE;
1694}
1695
1696static int clue_index(const game_state *state, int x, int y)
1697{
1698 int h = state->common->params.h, w = state->common->params.w;
1699
1700 if (y == 0)
1701 return x - 1;
1702 else if (x == w + 1)
1703 return w + y - 1;
1704 else if (y == h + 1)
1705 return 2 * w + h - x;
1706 else if (x == 0)
1707 return 2 * (w + h) - y;
1708
1709 return -1;
1710}
1711
1712#define TILESIZE (ds->tilesize)
1713#define BORDER (TILESIZE/4)
1714
1715static char *interpret_move(const game_state *state, game_ui *ui,
1716 const game_drawstate *ds,
1717 int x, int y, int button)
1718{
1719 int gx,gy;
1720 int g,xi;
1721 char buf[80];
1722
1723 gx = ((x-BORDER-1) / TILESIZE );
1724 gy = ((y-BORDER-2) / TILESIZE ) - 1;
1725
1726 if (button == 'a' || button == 'A') {
1727 ui->ascii = !ui->ascii;
1728 return "";
1729 }
1730
1731 if (button == 'm' || button == 'M') {
1732 return dupstr("M");
1733 }
1734
1735 if (ui->hshow == 1 && ui->hpencil == 0) {
1736 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
1737 if (xi >= 0 && !state->common->fixed[xi]) {
1738 if (button == 'g' || button == 'G' || button == '1') {
1739 if (!ui->hcursor) ui->hshow = 0;
1740 sprintf(buf,"G%d",xi);
1741 return dupstr(buf);
1742 }
1743 if (button == 'v' || button == 'V' || button == '2') {
1744 if (!ui->hcursor) ui->hshow = 0;
1745 sprintf(buf,"V%d",xi);
1746 return dupstr(buf);
1747 }
1748 if (button == 'z' || button == 'Z' || button == '3') {
1749 if (!ui->hcursor) ui->hshow = 0;
1750 sprintf(buf,"Z%d",xi);
1751 return dupstr(buf);
1752 }
1753 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
1754 button == '0' || button == '\b' ) {
1755 if (!ui->hcursor) ui->hshow = 0;
1756 sprintf(buf,"E%d",xi);
1757 return dupstr(buf);
1758 }
1759 }
1760 }
1761
1762 if (IS_CURSOR_MOVE(button)) {
1763 if (ui->hx == 0 && ui->hy == 0) {
1764 ui->hx = 1;
1765 ui->hy = 1;
1766 }
1767 else switch (button) {
1768 case CURSOR_UP: ui->hy -= (ui->hy > 1) ? 1 : 0; break;
1769 case CURSOR_DOWN: ui->hy += (ui->hy < ds->h) ? 1 : 0; break;
1770 case CURSOR_RIGHT: ui->hx += (ui->hx < ds->w) ? 1 : 0; break;
1771 case CURSOR_LEFT: ui->hx -= (ui->hx > 1) ? 1 : 0; break;
1772 }
1773 ui->hshow = ui->hcursor = 1;
1774 return "";
1775 }
1776 if (ui->hshow && button == CURSOR_SELECT) {
1777 ui->hpencil = 1 - ui->hpencil;
1778 ui->hcursor = 1;
1779 return "";
1780 }
1781
1782 if (ui->hshow == 1 && ui->hpencil == 1) {
1783 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
1784 if (xi >= 0 && !state->common->fixed[xi]) {
1785 if (button == 'g' || button == 'G' || button == '1') {
1786 sprintf(buf,"g%d",xi);
1787 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1788 return dupstr(buf);
1789 }
1790 if (button == 'v' || button == 'V' || button == '2') {
1791 sprintf(buf,"v%d",xi);
1792 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1793 return dupstr(buf);
1794 }
1795 if (button == 'z' || button == 'Z' || button == '3') {
1796 sprintf(buf,"z%d",xi);
1797 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1798 return dupstr(buf);
1799 }
1800 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
1801 button == '0' || button == '\b') {
1802 sprintf(buf,"E%d",xi);
1803 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1804 return dupstr(buf);
1805 }
1806 }
1807 }
1808
1809 if (gx > 0 && gx < ds->w+1 && gy > 0 && gy < ds->h+1) {
1810 xi = state->common->xinfo[gx+gy*(state->common->params.w+2)];
1811 if (xi >= 0 && !state->common->fixed[xi]) {
1812 g = state->guess[xi];
1813 if (ui->hshow == 0) {
1814 if (button == LEFT_BUTTON) {
1815 ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0;
1816 ui->hx = gx; ui->hy = gy;
1817 return "";
1818 }
1819 else if (button == RIGHT_BUTTON && g == 7) {
1820 ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0;
1821 ui->hx = gx; ui->hy = gy;
1822 return "";
1823 }
1824 }
1825 else if (ui->hshow == 1) {
1826 if (button == LEFT_BUTTON) {
1827 if (ui->hpencil == 0) {
1828 if (gx == ui->hx && gy == ui->hy) {
1829 ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0;
1830 ui->hx = 0; ui->hy = 0;
1831 return "";
1832 }
1833 else {
1834 ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0;
1835 ui->hx = gx; ui->hy = gy;
1836 return "";
1837 }
1838 }
1839 else {
1840 ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0;
1841 ui->hx = gx; ui->hy = gy;
1842 return "";
1843 }
1844 }
1845 else if (button == RIGHT_BUTTON) {
1846 if (ui->hpencil == 0 && g == 7) {
1847 ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0;
1848 ui->hx = gx; ui->hy = gy;
1849 return "";
1850 }
1851 else {
1852 if (gx == ui->hx && gy == ui->hy) {
1853 ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0;
1854 ui->hx = 0; ui->hy = 0;
1855 return "";
1856 }
1857 else if (g == 7) {
1858 ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0;
1859 ui->hx = gx; ui->hy = gy;
1860 return "";
1861 }
1862 }
1863 }
1864 }
1865 }
1866 } else if (button == LEFT_BUTTON) {
1867 if (is_clue(state, gx, gy)) {
1868 sprintf(buf, "D%d,%d", gx, gy);
1869 return dupstr(buf);
1870 }
1871 }
1872
1873 return NULL;
1874}
1875
1876int check_numbers_draw(game_state *state, int *guess) {
1877 int valid, filled;
1878 int i,x,y,xy;
1879 int count_ghosts, count_vampires, count_zombies;
1880
1881 count_ghosts = count_vampires = count_zombies = 0;
1882 for (i=0;i<state->common->num_total;i++) {
1883 if (guess[i] == 1) count_ghosts++;
1884 if (guess[i] == 2) count_vampires++;
1885 if (guess[i] == 4) count_zombies++;
1886 }
1887
1888 valid = TRUE;
1889 filled = (count_ghosts + count_vampires + count_zombies >=
1890 state->common->num_total);
1891
1892 if (count_ghosts > state->common->num_ghosts ||
1893 (filled && count_ghosts != state->common->num_ghosts) ) {
1894 valid = FALSE;
1895 state->count_errors[0] = TRUE;
1896 for (x=1;x<state->common->params.w+1;x++)
1897 for (y=1;y<state->common->params.h+1;y++) {
1898 xy = x+y*(state->common->params.w+2);
1899 if (state->common->xinfo[xy] >= 0 &&
1900 guess[state->common->xinfo[xy]] == 1)
1901 state->cell_errors[xy] = TRUE;
1902 }
1903 }
1904 if (count_vampires > state->common->num_vampires ||
1905 (filled && count_vampires != state->common->num_vampires) ) {
1906 valid = FALSE;
1907 state->count_errors[1] = TRUE;
1908 for (x=1;x<state->common->params.w+1;x++)
1909 for (y=1;y<state->common->params.h+1;y++) {
1910 xy = x+y*(state->common->params.w+2);
1911 if (state->common->xinfo[xy] >= 0 &&
1912 guess[state->common->xinfo[xy]] == 2)
1913 state->cell_errors[xy] = TRUE;
1914 }
1915 }
1916 if (count_zombies > state->common->num_zombies ||
1917 (filled && count_zombies != state->common->num_zombies) ) {
1918 valid = FALSE;
1919 state->count_errors[2] = TRUE;
1920 for (x=1;x<state->common->params.w+1;x++)
1921 for (y=1;y<state->common->params.h+1;y++) {
1922 xy = x+y*(state->common->params.w+2);
1923 if (state->common->xinfo[xy] >= 0 &&
1924 guess[state->common->xinfo[xy]] == 4)
1925 state->cell_errors[xy] = TRUE;
1926 }
1927 }
1928
1929 return valid;
1930}
1931
1932int check_path_solution(game_state *state, int p) {
1933 int i;
1934 int mirror;
1935 int count;
1936 int correct;
1937 int unfilled;
1938
1939 count = 0;
1940 mirror = FALSE;
1941 correct = TRUE;
1942
1943 unfilled = 0;
1944 for (i=0;i<state->common->paths[p].length;i++) {
1945 if (state->common->paths[p].p[i] == -1) mirror = TRUE;
1946 else {
1947 if (state->guess[state->common->paths[p].p[i]] == 1 && mirror)
1948 count++;
1949 else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror)
1950 count++;
1951 else if (state->guess[state->common->paths[p].p[i]] == 4)
1952 count++;
1953 else if (state->guess[state->common->paths[p].p[i]] == 7)
1954 unfilled++;
1955 }
1956 }
1957
1958 if (count > state->common->paths[p].sightings_start ||
1959 count + unfilled < state->common->paths[p].sightings_start)
1960 {
1961 correct = FALSE;
1962 state->hint_errors[state->common->paths[p].grid_start] = TRUE;
1963 }
1964
1965 count = 0;
1966 mirror = FALSE;
1967 unfilled = 0;
1968 for (i=state->common->paths[p].length-1;i>=0;i--) {
1969 if (state->common->paths[p].p[i] == -1) mirror = TRUE;
1970 else {
1971 if (state->guess[state->common->paths[p].p[i]] == 1 && mirror)
1972 count++;
1973 else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror)
1974 count++;
1975 else if (state->guess[state->common->paths[p].p[i]] == 4)
1976 count++;
1977 else if (state->guess[state->common->paths[p].p[i]] == 7)
1978 unfilled++;
1979 }
1980 }
1981
1982 if (count > state->common->paths[p].sightings_end ||
1983 count + unfilled < state->common->paths[p].sightings_end)
1984 {
1985 correct = FALSE;
1986 state->hint_errors[state->common->paths[p].grid_end] = TRUE;
1987 }
1988
1989 if (!correct) {
1990 for (i=0;i<state->common->paths[p].length;i++)
1991 state->cell_errors[state->common->paths[p].xy[i]] = TRUE;
1992 }
1993
1994 return correct;
1995}
1996
1997static game_state *execute_move(const game_state *state, const char *move)
1998{
1999 int x,y,n,p,i;
2000 char c;
2001 int correct;
2002 int solver;
2003
2004 game_state *ret = dup_game(state);
2005 solver = FALSE;
2006
2007 while (*move) {
2008 c = *move;
2009 if (c == 'S') {
2010 move++;
2011 solver = TRUE;
2012 }
2013 if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' ||
2014 c == 'g' || c == 'v' || c == 'z') {
2015 move++;
2016 sscanf(move, "%d%n", &x, &n);
2017 if (c == 'G') ret->guess[x] = 1;
2018 if (c == 'V') ret->guess[x] = 2;
2019 if (c == 'Z') ret->guess[x] = 4;
2020 if (c == 'E') { ret->guess[x] = 7; ret->pencils[x] = 0; }
2021 if (c == 'g') ret->pencils[x] ^= 1;
2022 if (c == 'v') ret->pencils[x] ^= 2;
2023 if (c == 'z') ret->pencils[x] ^= 4;
2024 move += n;
2025 }
2026 if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 &&
2027 is_clue(ret, x, y)) {
2028 ret->hints_done[clue_index(ret, x, y)] ^= 1;
2029 move += n + 1;
2030 }
2031 if (c == 'M') {
2032 /*
2033 * Fill in absolutely all pencil marks in unfilled
2034 * squares, for those who like to play by the rigorous
2035 * approach of starting off in that state and eliminating
2036 * things.
2037 */
2038 for (i = 0; i < ret->common->wh; i++)
2039 if (ret->guess[i] == 7)
2040 ret->pencils[i] = 7;
2041 move++;
2042 }
2043 if (*move == ';') move++;
2044 }
2045
2046 correct = TRUE;
2047
2048 for (i=0;i<ret->common->wh;i++) ret->cell_errors[i] = FALSE;
2049 for (i=0;i<2*ret->common->num_paths;i++) ret->hint_errors[i] = FALSE;
2050 for (i=0;i<3;i++) ret->count_errors[i] = FALSE;
2051
2052 if (!check_numbers_draw(ret,ret->guess)) correct = FALSE;
2053
2054 for (p=0;p<state->common->num_paths;p++)
2055 if (!check_path_solution(ret,p)) correct = FALSE;
2056
2057 for (i=0;i<state->common->num_total;i++)
2058 if (!(ret->guess[i] == 1 || ret->guess[i] == 2 ||
2059 ret->guess[i] == 4)) correct = FALSE;
2060
2061 if (correct && !solver) ret->solved = TRUE;
2062 if (solver) ret->cheated = TRUE;
2063
2064 return ret;
2065}
2066
2067/* ----------------------------------------------------------------------
2068 * Drawing routines.
2069 */
2070
2071#define PREFERRED_TILE_SIZE 64
2072
2073static void game_compute_size(const game_params *params, int tilesize,
2074 int *x, int *y)
2075{
2076 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2077 struct { int tilesize; } ads, *ds = &ads;
2078 ads.tilesize = tilesize;
2079
2080 *x = 2*BORDER+(params->w+2)*TILESIZE;
2081 *y = 2*BORDER+(params->h+3)*TILESIZE;
2082 return;
2083}
2084
2085static void game_set_size(drawing *dr, game_drawstate *ds,
2086 const game_params *params, int tilesize)
2087{
2088 ds->tilesize = tilesize;
2089 return;
2090}
2091
2092#define COLOUR(ret, i, r, g, b) ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b)))
2093
2094static float *game_colours(frontend *fe, int *ncolours)
2095{
2096 float *ret = snewn(3 * NCOLOURS, float);
2097
2098 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2099
2100 ret[COL_GRID * 3 + 0] = 0.0F;
2101 ret[COL_GRID * 3 + 1] = 0.0F;
2102 ret[COL_GRID * 3 + 2] = 0.0F;
2103
2104 ret[COL_TEXT * 3 + 0] = 0.0F;
2105 ret[COL_TEXT * 3 + 1] = 0.0F;
2106 ret[COL_TEXT * 3 + 2] = 0.0F;
2107
2108 ret[COL_ERROR * 3 + 0] = 1.0F;
2109 ret[COL_ERROR * 3 + 1] = 0.0F;
2110 ret[COL_ERROR * 3 + 2] = 0.0F;
2111
2112 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
2113 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
2114 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
2115
2116 ret[COL_FLASH * 3 + 0] = 1.0F;
2117 ret[COL_FLASH * 3 + 1] = 1.0F;
2118 ret[COL_FLASH * 3 + 2] = 1.0F;
2119
2120 ret[COL_GHOST * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F;
2121 ret[COL_GHOST * 3 + 1] = ret[COL_BACKGROUND * 3 + 0];
2122 ret[COL_GHOST * 3 + 2] = ret[COL_BACKGROUND * 3 + 0];
2123
2124 ret[COL_ZOMBIE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F;
2125 ret[COL_ZOMBIE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0];
2126 ret[COL_ZOMBIE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.5F;
2127
2128 ret[COL_VAMPIRE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
2129 ret[COL_VAMPIRE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
2130 ret[COL_VAMPIRE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
2131
2132 ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F;
2133 ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F;
2134 ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F;
2135
2136 *ncolours = NCOLOURS;
2137 return ret;
2138}
2139
2140static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2141{
2142 int i;
2143 struct game_drawstate *ds = snew(struct game_drawstate);
2144
2145 ds->tilesize = 0;
2146 ds->started = ds->solved = FALSE;
2147 ds->w = state->common->params.w;
2148 ds->h = state->common->params.h;
2149 ds->ascii = FALSE;
2150
2151 ds->count_errors[0] = FALSE;
2152 ds->count_errors[1] = FALSE;
2153 ds->count_errors[2] = FALSE;
2154
2155 ds->monsters = snewn(state->common->num_total,int);
2156 for (i=0;i<(state->common->num_total);i++)
2157 ds->monsters[i] = 7;
2158 ds->pencils = snewn(state->common->num_total,unsigned char);
2159 for (i=0;i<state->common->num_total;i++)
2160 ds->pencils[i] = 0;
2161
2162 ds->cell_errors = snewn(state->common->wh,unsigned char);
2163 for (i=0;i<state->common->wh;i++)
2164 ds->cell_errors[i] = FALSE;
2165 ds->hint_errors = snewn(2*state->common->num_paths,unsigned char);
2166 for (i=0;i<2*state->common->num_paths;i++)
2167 ds->hint_errors[i] = FALSE;
2168 ds->hints_done = snewn(2 * state->common->num_paths, unsigned char);
2169 memset(ds->hints_done, 0,
2170 2 * state->common->num_paths * sizeof(unsigned char));
2171
2172 ds->hshow = ds->hpencil = ds->hflash = 0;
2173 ds->hx = ds->hy = 0;
2174 return ds;
2175}
2176
2177static void game_free_drawstate(drawing *dr, game_drawstate *ds) {
2178 sfree(ds->hints_done);
2179 sfree(ds->hint_errors);
2180 sfree(ds->cell_errors);
2181 sfree(ds->pencils);
2182 sfree(ds->monsters);
2183 sfree(ds);
2184 return;
2185}
2186
2187static void draw_cell_background(drawing *dr, game_drawstate *ds,
2188 const game_state *state, const game_ui *ui,
2189 int x, int y) {
2190
2191 int hon;
2192 int dx,dy;
2193 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
2194 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
2195
2196 hon = (ui->hshow && x == ui->hx && y == ui->hy);
2197 draw_rect(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1,(hon && !ui->hpencil) ? COL_HIGHLIGHT : COL_BACKGROUND);
2198
2199 if (hon && ui->hpencil) {
2200 int coords[6];
2201 coords[0] = dx-(TILESIZE/2)+1;
2202 coords[1] = dy-(TILESIZE/2)+1;
2203 coords[2] = coords[0] + TILESIZE/2;
2204 coords[3] = coords[1];
2205 coords[4] = coords[0];
2206 coords[5] = coords[1] + TILESIZE/2;
2207 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
2208 }
2209
2210 draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1);
2211
2212 return;
2213}
2214
2215static void draw_circle_or_point(drawing *dr, int cx, int cy, int radius,
2216 int colour)
2217{
2218 if (radius > 0)
2219 draw_circle(dr, cx, cy, radius, colour, colour);
2220 else
2221 draw_rect(dr, cx, cy, 1, 1, colour);
2222}
2223
2224static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y,
2225 int tilesize, int hflash, int monster)
2226{
2227 int black = (hflash ? COL_FLASH : COL_TEXT);
2228
2229 if (monster == 1) { /* ghost */
2230 int poly[80], i, j;
2231
2232 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2+1);
2233 draw_circle(dr,x,y,2*tilesize/5, COL_GHOST,black);
2234 unclip(dr);
2235
2236 i = 0;
2237 poly[i++] = x - 2*tilesize/5;
2238 poly[i++] = y-2;
2239 poly[i++] = x - 2*tilesize/5;
2240 poly[i++] = y + 2*tilesize/5;
2241
2242 for (j = 0; j < 3; j++) {
2243 int total = (2*tilesize/5) * 2;
2244 int before = total * j / 3;
2245 int after = total * (j+1) / 3;
2246 int mid = (before + after) / 2;
2247 poly[i++] = x - 2*tilesize/5 + mid;
2248 poly[i++] = y + 2*tilesize/5 - (total / 6);
2249 poly[i++] = x - 2*tilesize/5 + after;
2250 poly[i++] = y + 2*tilesize/5;
2251 }
2252
2253 poly[i++] = x + 2*tilesize/5;
2254 poly[i++] = y-2;
2255
2256 clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize-(tilesize/2)-1);
2257 draw_polygon(dr, poly, i/2, COL_GHOST, black);
2258 unclip(dr);
2259
2260 draw_circle(dr,x-tilesize/6,y-tilesize/12,tilesize/10,
2261 COL_BACKGROUND,black);
2262 draw_circle(dr,x+tilesize/6,y-tilesize/12,tilesize/10,
2263 COL_BACKGROUND,black);
2264
2265 draw_circle_or_point(dr,x-tilesize/6+1+tilesize/48,y-tilesize/12,
2266 tilesize/48,black);
2267 draw_circle_or_point(dr,x+tilesize/6+1+tilesize/48,y-tilesize/12,
2268 tilesize/48,black);
2269
2270 } else if (monster == 2) { /* vampire */
2271 int poly[80], i;
2272
2273 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2);
2274 draw_circle(dr,x,y,2*tilesize/5,black,black);
2275 unclip(dr);
2276
2277 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize/2+1,tilesize/2);
2278 draw_circle(dr,x-tilesize/7,y,2*tilesize/5-tilesize/7,
2279 COL_VAMPIRE,black);
2280 unclip(dr);
2281 clip(dr,x,y-(tilesize/2)+2,tilesize/2+1,tilesize/2);
2282 draw_circle(dr,x+tilesize/7,y,2*tilesize/5-tilesize/7,
2283 COL_VAMPIRE,black);
2284 unclip(dr);
2285
2286 clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize/2);
2287 draw_circle(dr,x,y,2*tilesize/5, COL_VAMPIRE,black);
2288 unclip(dr);
2289
2290 draw_circle(dr, x-tilesize/7, y-tilesize/16, tilesize/16,
2291 COL_BACKGROUND, black);
2292 draw_circle(dr, x+tilesize/7, y-tilesize/16, tilesize/16,
2293 COL_BACKGROUND, black);
2294 draw_circle_or_point(dr, x-tilesize/7, y-tilesize/16, tilesize/48,
2295 black);
2296 draw_circle_or_point(dr, x+tilesize/7, y-tilesize/16, tilesize/48,
2297 black);
2298
2299 clip(dr, x-(tilesize/2)+2, y+tilesize/8, tilesize-3, tilesize/4);
2300
2301 i = 0;
2302 poly[i++] = x-3*tilesize/16;
2303 poly[i++] = y+1*tilesize/8;
2304 poly[i++] = x-2*tilesize/16;
2305 poly[i++] = y+7*tilesize/24;
2306 poly[i++] = x-1*tilesize/16;
2307 poly[i++] = y+1*tilesize/8;
2308 draw_polygon(dr, poly, i/2, COL_BACKGROUND, black);
2309 i = 0;
2310 poly[i++] = x+3*tilesize/16;
2311 poly[i++] = y+1*tilesize/8;
2312 poly[i++] = x+2*tilesize/16;
2313 poly[i++] = y+7*tilesize/24;
2314 poly[i++] = x+1*tilesize/16;
2315 poly[i++] = y+1*tilesize/8;
2316 draw_polygon(dr, poly, i/2, COL_BACKGROUND, black);
2317
2318 draw_circle(dr, x, y-tilesize/5, 2*tilesize/5, COL_VAMPIRE, black);
2319 unclip(dr);
2320
2321 } else if (monster == 4) { /* zombie */
2322 draw_circle(dr,x,y,2*tilesize/5, COL_ZOMBIE,black);
2323
2324 draw_line(dr,
2325 x-tilesize/7-tilesize/16, y-tilesize/12-tilesize/16,
2326 x-tilesize/7+tilesize/16, y-tilesize/12+tilesize/16,
2327 black);
2328 draw_line(dr,
2329 x-tilesize/7+tilesize/16, y-tilesize/12-tilesize/16,
2330 x-tilesize/7-tilesize/16, y-tilesize/12+tilesize/16,
2331 black);
2332 draw_line(dr,
2333 x+tilesize/7-tilesize/16, y-tilesize/12-tilesize/16,
2334 x+tilesize/7+tilesize/16, y-tilesize/12+tilesize/16,
2335 black);
2336 draw_line(dr,
2337 x+tilesize/7+tilesize/16, y-tilesize/12-tilesize/16,
2338 x+tilesize/7-tilesize/16, y-tilesize/12+tilesize/16,
2339 black);
2340
2341 clip(dr, x-tilesize/5, y+tilesize/6, 2*tilesize/5+1, tilesize/2);
2342 draw_circle(dr, x-tilesize/15, y+tilesize/6, tilesize/12,
2343 COL_BACKGROUND, black);
2344 unclip(dr);
2345
2346 draw_line(dr, x-tilesize/5, y+tilesize/6, x+tilesize/5, y+tilesize/6,
2347 black);
2348 }
2349
2350 draw_update(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize-3);
2351}
2352
2353static void draw_monster_count(drawing *dr, game_drawstate *ds,
2354 const game_state *state, int c, int hflash) {
2355 int dx,dy;
2356 char buf[8];
2357 char bufm[8];
2358
2359 dy = TILESIZE/4;
2360 dx = BORDER+(ds->w+2)*TILESIZE/2+TILESIZE/4;
2361 switch (c) {
2362 case 0:
2363 sprintf(buf,"%d",state->common->num_ghosts);
2364 sprintf(bufm,"G");
2365 dx -= 3*TILESIZE/2;
2366 break;
2367 case 1:
2368 sprintf(buf,"%d",state->common->num_vampires);
2369 sprintf(bufm,"V");
2370 break;
2371 case 2:
2372 sprintf(buf,"%d",state->common->num_zombies);
2373 sprintf(bufm,"Z");
2374 dx += 3*TILESIZE/2;
2375 break;
2376 }
2377
2378 draw_rect(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE,
2379 COL_BACKGROUND);
2380 if (!ds->ascii) {
2381 draw_monster(dr, ds, dx-TILESIZE/3, dy+TILESIZE/2,
2382 2*TILESIZE/3, hflash, 1<<c);
2383 } else {
2384 draw_text(dr, dx-TILESIZE/3,dy+TILESIZE/2,FONT_VARIABLE,TILESIZE/2,
2385 ALIGN_HCENTRE|ALIGN_VCENTRE,
2386 hflash ? COL_FLASH : COL_TEXT, bufm);
2387 }
2388 draw_text(dr, dx, dy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2,
2389 ALIGN_HLEFT|ALIGN_VCENTRE,
2390 (state->count_errors[c] ? COL_ERROR :
2391 hflash ? COL_FLASH : COL_TEXT), buf);
2392 draw_update(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE);
2393
2394 return;
2395}
2396
2397static void draw_path_hint(drawing *dr, game_drawstate *ds,
2398 const struct game_params *params,
2399 int hint_index, int hflash, int hint) {
2400 int x, y, color, dx, dy, text_dx, text_dy, text_size;
2401 char buf[4];
2402
2403 if (ds->hint_errors[hint_index])
2404 color = COL_ERROR;
2405 else if (hflash)
2406 color = COL_FLASH;
2407 else if (ds->hints_done[hint_index])
2408 color = COL_DONE;
2409 else
2410 color = COL_TEXT;
2411
2412 range2grid(hint_index, params->w, params->h, &x, &y);
2413 /* Upper-left corner of the "tile" */
2414 dx = BORDER + x * TILESIZE;
2415 dy = BORDER + y * TILESIZE + TILESIZE;
2416 /* Center of the "tile" */
2417 text_dx = dx + TILESIZE / 2;
2418 text_dy = dy + TILESIZE / 2;
2419 /* Avoid wiping out the borders of the puzzle */
2420 dx += 2;
2421 dy += 2;
2422 text_size = TILESIZE - 3;
2423
2424 sprintf(buf,"%d", hint);
2425 draw_rect(dr, dx, dy, text_size, text_size, COL_BACKGROUND);
2426 draw_text(dr, text_dx, text_dy, FONT_FIXED, TILESIZE / 2,
2427 ALIGN_HCENTRE | ALIGN_VCENTRE, color, buf);
2428 draw_update(dr, dx, dy, text_size, text_size);
2429
2430 return;
2431}
2432
2433static void draw_mirror(drawing *dr, game_drawstate *ds,
2434 const game_state *state, int x, int y,
2435 int hflash, int mirror) {
2436 int dx,dy,mx1,my1,mx2,my2;
2437 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
2438 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
2439
2440 if (mirror == CELL_MIRROR_L) {
2441 mx1 = dx-(TILESIZE/4);
2442 my1 = dy-(TILESIZE/4);
2443 mx2 = dx+(TILESIZE/4);
2444 my2 = dy+(TILESIZE/4);
2445 }
2446 else {
2447 mx1 = dx-(TILESIZE/4);
2448 my1 = dy+(TILESIZE/4);
2449 mx2 = dx+(TILESIZE/4);
2450 my2 = dy-(TILESIZE/4);
2451 }
2452 draw_thick_line(dr,(float)(TILESIZE/16),mx1,my1,mx2,my2,
2453 hflash ? COL_FLASH : COL_TEXT);
2454 draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1);
2455
2456 return;
2457}
2458
2459static void draw_big_monster(drawing *dr, game_drawstate *ds,
2460 const game_state *state, int x, int y,
2461 int hflash, int monster)
2462{
2463 int dx,dy;
2464 char buf[10];
2465 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
2466 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
2467 if (ds->ascii) {
2468 if (monster == 1) sprintf(buf,"G");
2469 else if (monster == 2) sprintf(buf,"V");
2470 else if (monster == 4) sprintf(buf,"Z");
2471 else sprintf(buf," ");
2472 draw_text(dr,dx,dy,FONT_FIXED,TILESIZE/2,ALIGN_HCENTRE|ALIGN_VCENTRE,
2473 hflash ? COL_FLASH : COL_TEXT,buf);
2474 draw_update(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3,
2475 TILESIZE-3);
2476 }
2477 else {
2478 draw_monster(dr, ds, dx, dy, 3*TILESIZE/4, hflash, monster);
2479 }
2480 return;
2481}
2482
2483static void draw_pencils(drawing *dr, game_drawstate *ds,
2484 const game_state *state, int x, int y, int pencil)
2485{
2486 int dx, dy;
2487 int monsters[4];
2488 int i, j, px, py;
2489 char buf[10];
2490 dx = BORDER+(x* ds->tilesize)+(TILESIZE/4);
2491 dy = BORDER+(y* ds->tilesize)+(TILESIZE/4)+TILESIZE;
2492
2493 for (i = 0, j = 1; j < 8; j *= 2)
2494 if (pencil & j)
2495 monsters[i++] = j;
2496 while (i < 4)
2497 monsters[i++] = 0;
2498
2499 for (py = 0; py < 2; py++)
2500 for (px = 0; px < 2; px++)
2501 if (monsters[py*2+px]) {
2502 if (!ds->ascii) {
2503 draw_monster(dr, ds,
2504 dx + TILESIZE/2 * px, dy + TILESIZE/2 * py,
2505 TILESIZE/2, 0, monsters[py*2+px]);
2506 }
2507 else {
2508 switch (monsters[py*2+px]) {
2509 case 1: sprintf(buf,"G"); break;
2510 case 2: sprintf(buf,"V"); break;
2511 case 4: sprintf(buf,"Z"); break;
2512 }
2513 draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py,
2514 FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE,
2515 COL_TEXT,buf);
2516 }
2517 }
2518 draw_update(dr,dx-(TILESIZE/4)+2,dy-(TILESIZE/4)+2,
2519 (TILESIZE/2)-3,(TILESIZE/2)-3);
2520
2521 return;
2522}
2523
2524#define FLASH_TIME 0.7F
2525
2526static int is_hint_stale(const game_drawstate *ds, int hflash,
2527 const game_state *state, int index)
2528{
2529 int ret = FALSE;
2530 if (!ds->started) ret = TRUE;
2531 if (ds->hflash != hflash) ret = TRUE;
2532
2533 if (ds->hint_errors[index] != state->hint_errors[index]) {
2534 ds->hint_errors[index] = state->hint_errors[index];
2535 ret = TRUE;
2536 }
2537
2538 if (ds->hints_done[index] != state->hints_done[index]) {
2539 ds->hints_done[index] = state->hints_done[index];
2540 ret = TRUE;
2541 }
2542
2543 return ret;
2544}
2545
2546static void game_redraw(drawing *dr, game_drawstate *ds,
2547 const game_state *oldstate, const game_state *state,
2548 int dir, const game_ui *ui,
2549 float animtime, float flashtime)
2550{
2551 int i,j,x,y,xy;
2552 int stale, xi, c, hflash, hchanged, changed_ascii;
2553
2554 hflash = (int)(flashtime * 5 / FLASH_TIME) % 2;
2555
2556 /* Draw static grid components at startup */
2557 if (!ds->started) {
2558 draw_rect(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE,
2559 2*BORDER+(ds->h+3)*TILESIZE, COL_BACKGROUND);
2560 draw_rect(dr, BORDER+TILESIZE-1, BORDER+2*TILESIZE-1,
2561 (ds->w)*TILESIZE +3, (ds->h)*TILESIZE +3, COL_GRID);
2562 for (i=0;i<ds->w;i++)
2563 for (j=0;j<ds->h;j++)
2564 draw_rect(dr, BORDER+(ds->tilesize*(i+1))+1,
2565 BORDER+(ds->tilesize*(j+2))+1, ds->tilesize-1,
2566 ds->tilesize-1, COL_BACKGROUND);
2567 draw_update(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE,
2568 2*BORDER+(ds->h+3)*TILESIZE);
2569 }
2570
2571 hchanged = FALSE;
2572 if (ds->hx != ui->hx || ds->hy != ui->hy ||
2573 ds->hshow != ui->hshow || ds->hpencil != ui->hpencil)
2574 hchanged = TRUE;
2575
2576 if (ds->ascii != ui->ascii) {
2577 ds->ascii = ui->ascii;
2578 changed_ascii = TRUE;
2579 } else
2580 changed_ascii = FALSE;
2581
2582 /* Draw monster count hints */
2583
2584 for (i=0;i<3;i++) {
2585 stale = FALSE;
2586 if (!ds->started) stale = TRUE;
2587 if (ds->hflash != hflash) stale = TRUE;
2588 if (changed_ascii) stale = TRUE;
2589 if (ds->count_errors[i] != state->count_errors[i]) {
2590 stale = TRUE;
2591 ds->count_errors[i] = state->count_errors[i];
2592 }
2593
2594 if (stale) {
2595 draw_monster_count(dr, ds, state, i, hflash);
2596 }
2597 }
2598
2599 /* Draw path count hints */
2600 for (i=0;i<state->common->num_paths;i++) {
2601 struct path *path = &state->common->paths[i];
2602
2603 if (is_hint_stale(ds, hflash, state, path->grid_start)) {
2604 draw_path_hint(dr, ds, &state->common->params, path->grid_start,
2605 hflash, path->sightings_start);
2606 }
2607
2608 if (is_hint_stale(ds, hflash, state, path->grid_end)) {
2609 draw_path_hint(dr, ds, &state->common->params, path->grid_end,
2610 hflash, path->sightings_end);
2611 }
2612 }
2613
2614 /* Draw puzzle grid contents */
2615 for (x = 1; x < ds->w+1; x++)
2616 for (y = 1; y < ds->h+1; y++) {
2617 stale = FALSE;
2618 xy = x+y*(state->common->params.w+2);
2619 xi = state->common->xinfo[xy];
2620 c = state->common->grid[xy];
2621
2622 if (!ds->started) stale = TRUE;
2623 if (ds->hflash != hflash) stale = TRUE;
2624 if (changed_ascii) stale = TRUE;
2625
2626 if (hchanged) {
2627 if ((x == ui->hx && y == ui->hy) ||
2628 (x == ds->hx && y == ds->hy))
2629 stale = TRUE;
2630 }
2631
2632 if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) {
2633 stale = TRUE;
2634 ds->monsters[xi] = state->guess[xi];
2635 }
2636
2637 if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) {
2638 stale = TRUE;
2639 ds->pencils[xi] = state->pencils[xi];
2640 }
2641
2642 if (state->cell_errors[xy] != ds->cell_errors[xy]) {
2643 stale = TRUE;
2644 ds->cell_errors[xy] = state->cell_errors[xy];
2645 }
2646
2647 if (stale) {
2648 draw_cell_background(dr, ds, state, ui, x, y);
2649 if (xi < 0)
2650 draw_mirror(dr, ds, state, x, y, hflash, c);
2651 else if (state->guess[xi] == 1 || state->guess[xi] == 2 ||
2652 state->guess[xi] == 4)
2653 draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]);
2654 else
2655 draw_pencils(dr, ds, state, x, y, state->pencils[xi]);
2656 }
2657 }
2658
2659 ds->hx = ui->hx; ds->hy = ui->hy;
2660 ds->hshow = ui->hshow;
2661 ds->hpencil = ui->hpencil;
2662 ds->hflash = hflash;
2663 ds->started = TRUE;
2664 return;
2665}
2666
2667static float game_anim_length(const game_state *oldstate,
2668 const game_state *newstate, int dir, game_ui *ui)
2669{
2670 return 0.0F;
2671}
2672
2673static float game_flash_length(const game_state *oldstate,
2674 const game_state *newstate, int dir, game_ui *ui)
2675{
2676 return (!oldstate->solved && newstate->solved && !oldstate->cheated &&
2677 !newstate->cheated) ? FLASH_TIME : 0.0F;
2678}
2679
2680static int game_status(const game_state *state)
2681{
2682 return state->solved;
2683}
2684
2685static int game_timing_state(const game_state *state, game_ui *ui)
2686{
2687 return TRUE;
2688}
2689
2690static void game_print_size(const game_params *params, float *x, float *y)
2691{
2692}
2693
2694static void game_print(drawing *dr, const game_state *state, int tilesize)
2695{
2696}
2697
2698#ifdef COMBINED
2699#define thegame undead
2700#endif
2701
2702const struct game thegame = {
2703 "Undead", "games.undead", "undead",
2704 default_params,
2705 game_fetch_preset, NULL,
2706 decode_params,
2707 encode_params,
2708 free_params,
2709 dup_params,
2710 TRUE, game_configure, custom_params,
2711 validate_params,
2712 new_game_desc,
2713 validate_desc,
2714 new_game,
2715 dup_game,
2716 free_game,
2717 TRUE, solve_game,
2718 TRUE, game_can_format_as_text_now, game_text_format,
2719 new_ui,
2720 free_ui,
2721 encode_ui,
2722 decode_ui,
2723 game_changed_state,
2724 interpret_move,
2725 execute_move,
2726 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2727 game_colours,
2728 game_new_drawstate,
2729 game_free_drawstate,
2730 game_redraw,
2731 game_anim_length,
2732 game_flash_length,
2733 game_status,
2734 FALSE, FALSE, game_print_size, game_print,
2735 FALSE, /* wants_statusbar */
2736 FALSE, game_timing_state,
2737 0, /* flags */
2738};
diff --git a/apps/plugins/puzzles/src/undead.html b/apps/plugins/puzzles/src/undead.html
new file mode 100644
index 0000000000..f7a66c8a52
--- /dev/null
+++ b/apps/plugins/puzzles/src/undead.html
@@ -0,0 +1,84 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Undead</title>
7<link rel="previous" href="pearl.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="unruly.html">
12</head>
13<body>
14<p><a href="pearl.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="unruly.html">Next</a></p>
15<h1><a name="C37"></a>Chapter 37: <a name="i0"></a>Undead</h1>
16<p>
17You are given a grid of squares, some of which contain diagonal mirrors. Every square which is not a mirror must be filled with one of three types of undead monster: a ghost, a vampire, or a zombie.
18</p>
19<p>
20Vampires can be seen directly, but are invisible when reflected in mirrors. Ghosts are the opposite way round: they can be seen in mirrors, but are invisible when looked at directly. Zombies are visible by any means.
21</p>
22<p>
23You are also told the total number of each type of monster in the grid. Also around the edge of the grid are written numbers, which indicate how many monsters can be seen if you look into the grid along a row or column starting from that position. (The diagonal mirrors are reflective on both sides. If your reflected line of sight crosses the same monster more than once, the number will count it each time it is visible, not just once.)
24</p>
25<p>
26This puzzle type was invented by David Millar, under the name &#8216;Haunted Mirror Maze&#8217;. See <a href="#p0">[20]</a> for more details.
27</p>
28<p>
29Undead was contributed to this collection by Steffen Bauer.
30</p>
31<p><a name="p0"></a>
32[20] <a href="http://www.janko.at/Raetsel/Spukschloss/index.htm"><code>http://www.janko.at/Raetsel/Spukschloss/index.htm</code></a>
33</p>
34<h2><a name="S37.1"></a>37.1 <a name="i1"></a>Undead controls</h2>
35<p>
36Undead has a similar control system to Solo, Unequal and Keen.
37</p>
38<p>
39To play Undead, click the mouse in any empty square and then type a letter on the keyboard indicating the type of monster: &#8216;G&#8217; for a ghost, &#8216;V&#8217; for a vampire, or &#8216;Z&#8217; for a zombie. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).
40</p>
41<p>
42If you <em>right</em>-click in a square and then type a letter, the corresponding monster will be shown in reduced size in that square, as a &#8216;pencil mark&#8217;. You can have pencil marks for multiple monsters in the same square. A square containing a full-size monster cannot also contain pencil marks.
43</p>
44<p>
45The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular monster, or you can use them as lists of the possible monster in a given square, or anything else you feel like.
46</p>
47<p>
48To erase a single pencil mark, right-click in the square and type the same letter again.
49</p>
50<p>
51All pencil marks in a square are erased when you left-click and type a monster letter, or when you left-click and press Space. Right-clicking and pressing space will also erase pencil marks.
52</p>
53<p>
54As for Solo, the cursor keys can be used in conjunction with the letter keys to place monsters or pencil marks. Use the cursor keys to move a highlight around the grid, and type a monster letter to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks.
55</p>
56<p>
57If you prefer plain letters of the alphabet to cute monster pictures, you can press &#8216;A&#8217; to toggle between showing the monsters as monsters or showing them as letters.
58</p>
59<p>
60Left-clicking a clue will mark it as done (grey it out), or unmark it if it is already marked.
61</p>
62<p>
63(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
64</p>
65<h2><a name="S37.2"></a>37.2 <a name="i2"></a>Undead parameters</h2>
66<p>
67These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
68</p>
69<dl><dt>
70<em>Width</em>, <em>Height</em>
71</dt>
72<dd>
73Size of grid in squares.
74</dd>
75<dt>
76<em>Difficulty</em>
77</dt>
78<dd>
79Controls the difficulty of the generated puzzle.
80</dd>
81</dl>
82
83<hr><address></address></body>
84</html>
diff --git a/apps/plugins/puzzles/src/unequal.R b/apps/plugins/puzzles/src/unequal.R
new file mode 100644
index 0000000000..a061582768
--- /dev/null
+++ b/apps/plugins/puzzles/src/unequal.R
@@ -0,0 +1,27 @@
1# -*- makefile -*-
2
3UNEQUAL_EXTRA = latin tree234 maxflow
4
5unequal : [X] GTK COMMON unequal UNEQUAL_EXTRA unequal-icon|no-icon
6
7unequal : [G] WINDOWS COMMON unequal UNEQUAL_EXTRA unequal.res|noicon.res
8
9unequalsolver : [U] unequal[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] tree234 maxflow STANDALONE
10unequalsolver : [C] unequal[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] tree234 maxflow STANDALONE
11
12latincheck : [U] latin[STANDALONE_LATIN_TEST] tree234 maxflow STANDALONE
13latincheck : [C] latin[STANDALONE_LATIN_TEST] tree234 maxflow STANDALONE
14
15ALL += unequal[COMBINED] UNEQUAL_EXTRA
16
17!begin am gtk
18GAMES += unequal
19!end
20
21!begin >list.c
22 A(unequal) \
23!end
24
25!begin >gamedesc.txt
26unequal:unequal.exe:Unequal:Latin square puzzle:Complete the latin square in accordance with the > signs.
27!end
diff --git a/apps/plugins/puzzles/src/unequal.c b/apps/plugins/puzzles/src/unequal.c
new file mode 100644
index 0000000000..a63b7d8ed0
--- /dev/null
+++ b/apps/plugins/puzzles/src/unequal.c
@@ -0,0 +1,2267 @@
1/*
2 * unequal.c
3 *
4 * Implementation of 'Futoshiki', a puzzle featured in the Guardian.
5 *
6 * TTD:
7 * add multiple-links-on-same-col/row solver nous
8 * Optimise set solver to use bit operations instead
9 *
10 * Guardian puzzles of note:
11 * #1: 5:0,0L,0L,0,0,0R,0,0L,0D,0L,0R,0,2,0D,0,0,0,0,0,0,0U,0,0,0,0U,
12 * #2: 5:0,0,0,4L,0L,0,2LU,0L,0U,0,0,0U,0,0,0,0,0D,0,3LRUD,0,0R,3,0L,0,0,
13 * #3: (reprint of #2)
14 * #4:
15 * #5: 5:0,0,0,0,0,0,2,0U,3U,0U,0,0,3,0,0,0,3,0D,4,0,0,0L,0R,0,0,
16 * #6: 5:0D,0L,0,0R,0,0,0D,0,3,0D,0,0R,0,0R,0D,0U,0L,0,1,2,0,0,0U,0,0L,
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <assert.h>
23#include <ctype.h>
24#include <math.h>
25
26#include "puzzles.h"
27#include "latin.h" /* contains typedef for digit */
28
29/* ----------------------------------------------------------
30 * Constant and structure definitions
31 */
32
33#define FLASH_TIME 0.4F
34
35#define PREFERRED_TILE_SIZE 32
36
37#define TILE_SIZE (ds->tilesize)
38#define GAP_SIZE (TILE_SIZE/2)
39#define SQUARE_SIZE (TILE_SIZE + GAP_SIZE)
40
41#define BORDER (TILE_SIZE / 2)
42
43#define COORD(x) ( (x) * SQUARE_SIZE + BORDER )
44#define FROMCOORD(x) ( ((x) - BORDER + SQUARE_SIZE) / SQUARE_SIZE - 1 )
45
46#define GRID(p,w,x,y) ((p)->w[((y)*(p)->order)+(x)])
47#define GRID3(p,w,x,y,z) ((p)->w[ (((x)*(p)->order+(y))*(p)->order+(z)) ])
48#define HINT(p,x,y,n) GRID3(p, hints, x, y, n)
49
50enum {
51 COL_BACKGROUND,
52 COL_GRID,
53 COL_TEXT, COL_GUESS, COL_ERROR, COL_PENCIL,
54 COL_HIGHLIGHT, COL_LOWLIGHT, COL_SPENT = COL_LOWLIGHT,
55 NCOLOURS
56};
57
58struct game_params {
59 int order; /* Size of latin square */
60 int diff; /* Difficulty */
61 int adjacent; /* Puzzle indicators are 'adjacent number'
62 not 'greater-than'. */
63};
64
65#define F_IMMUTABLE 1 /* passed in as game description */
66#define F_ADJ_UP 2
67#define F_ADJ_RIGHT 4
68#define F_ADJ_DOWN 8
69#define F_ADJ_LEFT 16
70#define F_ERROR 32
71#define F_ERROR_UP 64
72#define F_ERROR_RIGHT 128
73#define F_ERROR_DOWN 256
74#define F_ERROR_LEFT 512
75#define F_SPENT_UP 1024
76#define F_SPENT_RIGHT 2048
77#define F_SPENT_DOWN 4096
78#define F_SPENT_LEFT 8192
79
80#define ADJ_TO_SPENT(x) ((x) << 9)
81
82#define F_ERROR_MASK (F_ERROR|F_ERROR_UP|F_ERROR_RIGHT|F_ERROR_DOWN|F_ERROR_LEFT)
83
84struct game_state {
85 int order, completed, cheated, adjacent;
86 digit *nums; /* actual numbers (size order^2) */
87 unsigned char *hints; /* remaining possiblities (size order^3) */
88 unsigned int *flags; /* flags (size order^2) */
89};
90
91/* ----------------------------------------------------------
92 * Game parameters and presets
93 */
94
95/* Steal the method from map.c for difficulty levels. */
96#define DIFFLIST(A) \
97 A(LATIN,Trivial,NULL,t) \
98 A(EASY,Easy,solver_easy, e) \
99 A(SET,Tricky,solver_set, k) \
100 A(EXTREME,Extreme,NULL,x) \
101 A(RECURSIVE,Recursive,NULL,r)
102
103#define ENUM(upper,title,func,lower) DIFF_ ## upper,
104#define TITLE(upper,title,func,lower) #title,
105#define ENCODE(upper,title,func,lower) #lower
106#define CONFIG(upper,title,func,lower) ":" #title
107enum { DIFFLIST(ENUM) DIFFCOUNT, DIFF_IMPOSSIBLE = diff_impossible, DIFF_AMBIGUOUS = diff_ambiguous, DIFF_UNFINISHED = diff_unfinished };
108static char const *const unequal_diffnames[] = { DIFFLIST(TITLE) };
109static char const unequal_diffchars[] = DIFFLIST(ENCODE);
110#define DIFFCONFIG DIFFLIST(CONFIG)
111
112#define DEFAULT_PRESET 0
113
114const static struct game_params unequal_presets[] = {
115 { 4, DIFF_EASY, 0 },
116 { 5, DIFF_EASY, 0 },
117 { 5, DIFF_SET, 0 },
118 { 5, DIFF_SET, 1 },
119 { 5, DIFF_EXTREME, 0 },
120 { 6, DIFF_EASY, 0 },
121 { 6, DIFF_SET, 0 },
122 { 6, DIFF_SET, 1 },
123 { 6, DIFF_EXTREME, 0 },
124 { 7, DIFF_SET, 0 },
125 { 7, DIFF_SET, 1 },
126 { 7, DIFF_EXTREME, 0 }
127};
128
129static int game_fetch_preset(int i, char **name, game_params **params)
130{
131 game_params *ret;
132 char buf[80];
133
134 if (i < 0 || i >= lenof(unequal_presets))
135 return FALSE;
136
137 ret = snew(game_params);
138 *ret = unequal_presets[i]; /* structure copy */
139
140 sprintf(buf, "%s: %dx%d %s",
141 ret->adjacent ? "Adjacent" : "Unequal",
142 ret->order, ret->order,
143 unequal_diffnames[ret->diff]);
144
145 *name = dupstr(buf);
146 *params = ret;
147 return TRUE;
148}
149
150static game_params *default_params(void)
151{
152 game_params *ret;
153 char *name;
154
155 if (!game_fetch_preset(DEFAULT_PRESET, &name, &ret)) return NULL;
156 sfree(name);
157 return ret;
158}
159
160static void free_params(game_params *params)
161{
162 sfree(params);
163}
164
165static game_params *dup_params(const game_params *params)
166{
167 game_params *ret = snew(game_params);
168 *ret = *params; /* structure copy */
169 return ret;
170}
171
172static void decode_params(game_params *ret, char const *string)
173{
174 char const *p = string;
175
176 ret->order = atoi(p);
177 while (*p && isdigit((unsigned char)*p)) p++;
178
179 if (*p == 'a') {
180 p++;
181 ret->adjacent = 1;
182 } else
183 ret->adjacent = 0;
184
185 if (*p == 'd') {
186 int i;
187 p++;
188 ret->diff = DIFFCOUNT+1; /* ...which is invalid */
189 if (*p) {
190 for (i = 0; i < DIFFCOUNT; i++) {
191 if (*p == unequal_diffchars[i])
192 ret->diff = i;
193 }
194 p++;
195 }
196 }
197}
198
199static char *encode_params(const game_params *params, int full)
200{
201 char ret[80];
202
203 sprintf(ret, "%d", params->order);
204 if (params->adjacent)
205 sprintf(ret + strlen(ret), "a");
206 if (full)
207 sprintf(ret + strlen(ret), "d%c", unequal_diffchars[params->diff]);
208
209 return dupstr(ret);
210}
211
212static config_item *game_configure(const game_params *params)
213{
214 config_item *ret;
215 char buf[80];
216
217 ret = snewn(4, config_item);
218
219 ret[0].name = "Mode";
220 ret[0].type = C_CHOICES;
221 ret[0].sval = ":Unequal:Adjacent";
222 ret[0].ival = params->adjacent;
223
224 ret[1].name = "Size (s*s)";
225 ret[1].type = C_STRING;
226 sprintf(buf, "%d", params->order);
227 ret[1].sval = dupstr(buf);
228 ret[1].ival = 0;
229
230 ret[2].name = "Difficulty";
231 ret[2].type = C_CHOICES;
232 ret[2].sval = DIFFCONFIG;
233 ret[2].ival = params->diff;
234
235 ret[3].name = NULL;
236 ret[3].type = C_END;
237 ret[3].sval = NULL;
238 ret[3].ival = 0;
239
240 return ret;
241}
242
243static game_params *custom_params(const config_item *cfg)
244{
245 game_params *ret = snew(game_params);
246
247 ret->adjacent = cfg[0].ival;
248 ret->order = atoi(cfg[1].sval);
249 ret->diff = cfg[2].ival;
250
251 return ret;
252}
253
254static char *validate_params(const game_params *params, int full)
255{
256 if (params->order < 3 || params->order > 32)
257 return "Order must be between 3 and 32";
258 if (params->diff >= DIFFCOUNT)
259 return "Unknown difficulty rating";
260 if (params->order < 5 && params->adjacent &&
261 params->diff >= DIFF_SET)
262 return "Order must be at least 5 for Adjacent puzzles of this difficulty.";
263 return NULL;
264}
265
266/* ----------------------------------------------------------
267 * Various utility functions
268 */
269
270static const struct { unsigned int f, fo, fe; int dx, dy; char c, ac; } adjthan[] = {
271 { F_ADJ_UP, F_ADJ_DOWN, F_ERROR_UP, 0, -1, '^', '-' },
272 { F_ADJ_RIGHT, F_ADJ_LEFT, F_ERROR_RIGHT, 1, 0, '>', '|' },
273 { F_ADJ_DOWN, F_ADJ_UP, F_ERROR_DOWN, 0, 1, 'v', '-' },
274 { F_ADJ_LEFT, F_ADJ_RIGHT, F_ERROR_LEFT, -1, 0, '<', '|' }
275};
276
277static game_state *blank_game(int order, int adjacent)
278{
279 game_state *state = snew(game_state);
280 int o2 = order*order, o3 = o2*order;
281
282 state->order = order;
283 state->adjacent = adjacent;
284 state->completed = state->cheated = 0;
285
286 state->nums = snewn(o2, digit);
287 state->hints = snewn(o3, unsigned char);
288 state->flags = snewn(o2, unsigned int);
289
290 memset(state->nums, 0, o2 * sizeof(digit));
291 memset(state->hints, 0, o3);
292 memset(state->flags, 0, o2 * sizeof(unsigned int));
293
294 return state;
295}
296
297static game_state *dup_game(const game_state *state)
298{
299 game_state *ret = blank_game(state->order, state->adjacent);
300 int o2 = state->order*state->order, o3 = o2*state->order;
301
302 memcpy(ret->nums, state->nums, o2 * sizeof(digit));
303 memcpy(ret->hints, state->hints, o3);
304 memcpy(ret->flags, state->flags, o2 * sizeof(unsigned int));
305
306 return ret;
307}
308
309static void free_game(game_state *state)
310{
311 sfree(state->nums);
312 sfree(state->hints);
313 sfree(state->flags);
314 sfree(state);
315}
316
317#define CHECKG(x,y) grid[(y)*o+(x)]
318
319/* Returns 0 if it finds an error, 1 otherwise. */
320static int check_num_adj(digit *grid, game_state *state,
321 int x, int y, int me)
322{
323 unsigned int f = GRID(state, flags, x, y);
324 int ret = 1, i, o = state->order;
325
326 for (i = 0; i < 4; i++) {
327 int dx = adjthan[i].dx, dy = adjthan[i].dy, n, dn;
328
329 if (x+dx < 0 || x+dx >= o || y+dy < 0 || y+dy >= o)
330 continue;
331
332 n = CHECKG(x, y);
333 dn = CHECKG(x+dx, y+dy);
334
335 assert (n != 0);
336 if (dn == 0) continue;
337
338 if (state->adjacent) {
339 int gd = abs(n-dn);
340
341 if ((f & adjthan[i].f) && (gd != 1)) {
342 debug(("check_adj error (%d,%d):%d should be | (%d,%d):%d",
343 x, y, n, x+dx, y+dy, dn));
344 if (me) GRID(state, flags, x, y) |= adjthan[i].fe;
345 ret = 0;
346 }
347 if (!(f & adjthan[i].f) && (gd == 1)) {
348 debug(("check_adj error (%d,%d):%d should not be | (%d,%d):%d",
349 x, y, n, x+dx, y+dy, dn));
350 if (me) GRID(state, flags, x, y) |= adjthan[i].fe;
351 ret = 0;
352 }
353
354 } else {
355 if ((f & adjthan[i].f) && (n <= dn)) {
356 debug(("check_adj error (%d,%d):%d not > (%d,%d):%d",
357 x, y, n, x+dx, y+dy, dn));
358 if (me) GRID(state, flags, x, y) |= adjthan[i].fe;
359 ret = 0;
360 }
361 }
362 }
363 return ret;
364}
365
366/* Returns 0 if it finds an error, 1 otherwise. */
367static int check_num_error(digit *grid, game_state *state,
368 int x, int y, int mark_errors)
369{
370 int o = state->order;
371 int xx, yy, val = CHECKG(x,y), ret = 1;
372
373 assert(val != 0);
374
375 /* check for dups in same column. */
376 for (yy = 0; yy < state->order; yy++) {
377 if (yy == y) continue;
378 if (CHECKG(x,yy) == val) ret = 0;
379 }
380
381 /* check for dups in same row. */
382 for (xx = 0; xx < state->order; xx++) {
383 if (xx == x) continue;
384 if (CHECKG(xx,y) == val) ret = 0;
385 }
386
387 if (!ret) {
388 debug(("check_num_error (%d,%d) duplicate %d", x, y, val));
389 if (mark_errors) GRID(state, flags, x, y) |= F_ERROR;
390 }
391 return ret;
392}
393
394/* Returns: -1 for 'wrong'
395 * 0 for 'incomplete'
396 * 1 for 'complete and correct'
397 */
398static int check_complete(digit *grid, game_state *state, int mark_errors)
399{
400 int x, y, ret = 1, o = state->order;
401
402 if (mark_errors)
403 assert(grid == state->nums);
404
405 for (x = 0; x < state->order; x++) {
406 for (y = 0; y < state->order; y++) {
407 if (mark_errors)
408 GRID(state, flags, x, y) &= ~F_ERROR_MASK;
409 if (grid[y*o+x] == 0) {
410 ret = 0;
411 } else {
412 if (!check_num_error(grid, state, x, y, mark_errors)) ret = -1;
413 if (!check_num_adj(grid, state, x, y, mark_errors)) ret = -1;
414 }
415 }
416 }
417 if (ret == 1 && latin_check(grid, o))
418 ret = -1;
419 return ret;
420}
421
422static char n2c(digit n, int order) {
423 if (n == 0) return ' ';
424 if (order < 10) {
425 if (n < 10) return '0' + n;
426 } else {
427 if (n < 11) return '0' + n-1;
428 n -= 11;
429 if (n <= 26) return 'A' + n;
430 }
431 return '?';
432}
433
434/* should be 'digit', but includes -1 for 'not a digit'.
435 * Includes keypresses (0 especially) for interpret_move. */
436static int c2n(int c, int order) {
437 if (c < 0 || c > 0xff)
438 return -1;
439 if (c == ' ' || c == '\b')
440 return 0;
441 if (order < 10) {
442 if (c >= '0' && c <= '9')
443 return (int)(c - '0');
444 } else {
445 if (c >= '0' && c <= '9')
446 return (int)(c - '0' + 1);
447 if (c >= 'A' && c <= 'Z')
448 return (int)(c - 'A' + 11);
449 if (c >= 'a' && c <= 'z')
450 return (int)(c - 'a' + 11);
451 }
452 return -1;
453}
454
455static int game_can_format_as_text_now(const game_params *params)
456{
457 return TRUE;
458}
459
460static char *game_text_format(const game_state *state)
461{
462 int x, y, len, n;
463 char *ret, *p;
464
465 len = (state->order*2) * (state->order*2-1) + 1;
466 ret = snewn(len, char);
467 p = ret;
468
469 for (y = 0; y < state->order; y++) {
470 for (x = 0; x < state->order; x++) {
471 n = GRID(state, nums, x, y);
472 *p++ = n > 0 ? n2c(n, state->order) : '.';
473
474 if (x < (state->order-1)) {
475 if (state->adjacent) {
476 *p++ = (GRID(state, flags, x, y) & F_ADJ_RIGHT) ? '|' : ' ';
477 } else {
478 if (GRID(state, flags, x, y) & F_ADJ_RIGHT)
479 *p++ = '>';
480 else if (GRID(state, flags, x+1, y) & F_ADJ_LEFT)
481 *p++ = '<';
482 else
483 *p++ = ' ';
484 }
485 }
486 }
487 *p++ = '\n';
488
489 if (y < (state->order-1)) {
490 for (x = 0; x < state->order; x++) {
491 if (state->adjacent) {
492 *p++ = (GRID(state, flags, x, y) & F_ADJ_DOWN) ? '-' : ' ';
493 } else {
494 if (GRID(state, flags, x, y) & F_ADJ_DOWN)
495 *p++ = 'v';
496 else if (GRID(state, flags, x, y+1) & F_ADJ_UP)
497 *p++ = '^';
498 else
499 *p++ = ' ';
500 }
501
502 if (x < state->order-1)
503 *p++ = ' ';
504 }
505 *p++ = '\n';
506 }
507 }
508 *p++ = '\0';
509
510 assert(p - ret == len);
511 return ret;
512}
513
514#ifdef STANDALONE_SOLVER
515static void game_debug(game_state *state)
516{
517 char *dbg = game_text_format(state);
518 printf("%s", dbg);
519 sfree(dbg);
520}
521#endif
522
523/* ----------------------------------------------------------
524 * Solver.
525 */
526
527struct solver_link {
528 int len, gx, gy, lx, ly;
529};
530
531struct solver_ctx {
532 game_state *state;
533
534 int nlinks, alinks;
535 struct solver_link *links;
536};
537
538static void solver_add_link(struct solver_ctx *ctx,
539 int gx, int gy, int lx, int ly, int len)
540{
541 if (ctx->alinks < ctx->nlinks+1) {
542 ctx->alinks = ctx->alinks*2 + 1;
543 /*debug(("resizing ctx->links, new size %d", ctx->alinks));*/
544 ctx->links = sresize(ctx->links, ctx->alinks, struct solver_link);
545 }
546 ctx->links[ctx->nlinks].gx = gx;
547 ctx->links[ctx->nlinks].gy = gy;
548 ctx->links[ctx->nlinks].lx = lx;
549 ctx->links[ctx->nlinks].ly = ly;
550 ctx->links[ctx->nlinks].len = len;
551 ctx->nlinks++;
552 /*debug(("Adding new link: len %d (%d,%d) < (%d,%d), nlinks now %d",
553 len, lx, ly, gx, gy, ctx->nlinks));*/
554}
555
556static struct solver_ctx *new_ctx(game_state *state)
557{
558 struct solver_ctx *ctx = snew(struct solver_ctx);
559 int o = state->order;
560 int i, x, y;
561 unsigned int f;
562
563 ctx->nlinks = ctx->alinks = 0;
564 ctx->links = NULL;
565 ctx->state = state;
566
567 if (state->adjacent) return ctx; /* adjacent mode doesn't use links. */
568
569 for (x = 0; x < o; x++) {
570 for (y = 0; y < o; y++) {
571 f = GRID(state, flags, x, y);
572 for (i = 0; i < 4; i++) {
573 if (f & adjthan[i].f)
574 solver_add_link(ctx, x, y, x+adjthan[i].dx, y+adjthan[i].dy, 1);
575 }
576 }
577 }
578
579 return ctx;
580}
581
582static void *clone_ctx(void *vctx)
583{
584 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
585 return new_ctx(ctx->state);
586}
587
588static void free_ctx(void *vctx)
589{
590 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
591 if (ctx->links) sfree(ctx->links);
592 sfree(ctx);
593}
594
595static void solver_nminmax(struct latin_solver *solver,
596 int x, int y, int *min_r, int *max_r,
597 unsigned char **ns_r)
598{
599 int o = solver->o, min = o, max = 0, n;
600 unsigned char *ns;
601
602 assert(x >= 0 && y >= 0 && x < o && y < o);
603
604 ns = solver->cube + cubepos(x,y,1);
605
606 if (grid(x,y) > 0) {
607 min = max = grid(x,y)-1;
608 } else {
609 for (n = 0; n < o; n++) {
610 if (ns[n]) {
611 if (n > max) max = n;
612 if (n < min) min = n;
613 }
614 }
615 }
616 if (min_r) *min_r = min;
617 if (max_r) *max_r = max;
618 if (ns_r) *ns_r = ns;
619}
620
621static int solver_links(struct latin_solver *solver, void *vctx)
622{
623 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
624 int i, j, lmin, gmax, nchanged = 0;
625 unsigned char *gns, *lns;
626 struct solver_link *link;
627
628 for (i = 0; i < ctx->nlinks; i++) {
629 link = &ctx->links[i];
630 solver_nminmax(solver, link->gx, link->gy, NULL, &gmax, &gns);
631 solver_nminmax(solver, link->lx, link->ly, &lmin, NULL, &lns);
632
633 for (j = 0; j < solver->o; j++) {
634 /* For the 'greater' end of the link, discount all numbers
635 * too small to satisfy the inequality. */
636 if (gns[j]) {
637 if (j < (lmin+link->len)) {
638#ifdef STANDALONE_SOLVER
639 if (solver_show_working) {
640 printf("%*slink elimination, (%d,%d) > (%d,%d):\n",
641 solver_recurse_depth*4, "",
642 link->gx+1, link->gy+1, link->lx+1, link->ly+1);
643 printf("%*s ruling out %d at (%d,%d)\n",
644 solver_recurse_depth*4, "",
645 j+1, link->gx+1, link->gy+1);
646 }
647#endif
648 cube(link->gx, link->gy, j+1) = FALSE;
649 nchanged++;
650 }
651 }
652 /* For the 'lesser' end of the link, discount all numbers
653 * too large to satisfy inequality. */
654 if (lns[j]) {
655 if (j > (gmax-link->len)) {
656#ifdef STANDALONE_SOLVER
657 if (solver_show_working) {
658 printf("%*slink elimination, (%d,%d) > (%d,%d):\n",
659 solver_recurse_depth*4, "",
660 link->gx+1, link->gy+1, link->lx+1, link->ly+1);
661 printf("%*s ruling out %d at (%d,%d)\n",
662 solver_recurse_depth*4, "",
663 j+1, link->lx+1, link->ly+1);
664 }
665#endif
666 cube(link->lx, link->ly, j+1) = FALSE;
667 nchanged++;
668 }
669 }
670 }
671 }
672 return nchanged;
673}
674
675static int solver_adjacent(struct latin_solver *solver, void *vctx)
676{
677 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
678 int nchanged = 0, x, y, i, n, o = solver->o, nx, ny, gd;
679
680 /* Update possible values based on known values and adjacency clues. */
681
682 for (x = 0; x < o; x++) {
683 for (y = 0; y < o; y++) {
684 if (grid(x, y) == 0) continue;
685
686 /* We have a definite number here. Make sure that any
687 * adjacent possibles reflect the adjacent/non-adjacent clue. */
688
689 for (i = 0; i < 4; i++) {
690 int isadjacent = (GRID(ctx->state, flags, x, y) & adjthan[i].f);
691
692 nx = x + adjthan[i].dx, ny = y + adjthan[i].dy;
693 if (nx < 0 || ny < 0 || nx >= o || ny >= o)
694 continue;
695
696 for (n = 0; n < o; n++) {
697 /* Continue past numbers the adjacent square _could_ be,
698 * given the clue we have. */
699 gd = abs((n+1) - grid(x, y));
700 if (isadjacent && (gd == 1)) continue;
701 if (!isadjacent && (gd != 1)) continue;
702
703 if (cube(nx, ny, n+1) == FALSE)
704 continue; /* already discounted this possibility. */
705
706#ifdef STANDALONE_SOLVER
707 if (solver_show_working) {
708 printf("%*sadjacent elimination, (%d,%d):%d %s (%d,%d):\n",
709 solver_recurse_depth*4, "",
710 x+1, y+1, grid(x, y), isadjacent ? "|" : "!|", nx+1, ny+1);
711 printf("%*s ruling out %d at (%d,%d)\n",
712 solver_recurse_depth*4, "", n+1, nx+1, ny+1);
713 }
714#endif
715 cube(nx, ny, n+1) = FALSE;
716 nchanged++;
717 }
718 }
719 }
720 }
721
722 return nchanged;
723}
724
725static int solver_adjacent_set(struct latin_solver *solver, void *vctx)
726{
727 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
728 int x, y, i, n, nn, o = solver->o, nx, ny, gd;
729 int nchanged = 0, *scratch = snewn(o, int);
730
731 /* Update possible values based on other possible values
732 * of adjacent squares, and adjacency clues. */
733
734 for (x = 0; x < o; x++) {
735 for (y = 0; y < o; y++) {
736 for (i = 0; i < 4; i++) {
737 int isadjacent = (GRID(ctx->state, flags, x, y) & adjthan[i].f);
738
739 nx = x + adjthan[i].dx, ny = y + adjthan[i].dy;
740 if (nx < 0 || ny < 0 || nx >= o || ny >= o)
741 continue;
742
743 /* We know the current possibles for the square (x,y)
744 * and also the adjacency clue from (x,y) to (nx,ny).
745 * Construct a maximum set of possibles for (nx,ny)
746 * in scratch, based on these constraints... */
747
748 memset(scratch, 0, o*sizeof(int));
749
750 for (n = 0; n < o; n++) {
751 if (cube(x, y, n+1) == FALSE) continue;
752
753 for (nn = 0; nn < o; nn++) {
754 if (n == nn) continue;
755
756 gd = abs(nn - n);
757 if (isadjacent && (gd != 1)) continue;
758 if (!isadjacent && (gd == 1)) continue;
759
760 scratch[nn] = 1;
761 }
762 }
763
764 /* ...and remove any possibilities for (nx,ny) that are
765 * currently set but are not indicated in scratch. */
766 for (n = 0; n < o; n++) {
767 if (scratch[n] == 1) continue;
768 if (cube(nx, ny, n+1) == FALSE) continue;
769
770#ifdef STANDALONE_SOLVER
771 if (solver_show_working) {
772 printf("%*sadjacent possible elimination, (%d,%d) %s (%d,%d):\n",
773 solver_recurse_depth*4, "",
774 x+1, y+1, isadjacent ? "|" : "!|", nx+1, ny+1);
775 printf("%*s ruling out %d at (%d,%d)\n",
776 solver_recurse_depth*4, "", n+1, nx+1, ny+1);
777 }
778#endif
779 cube(nx, ny, n+1) = FALSE;
780 nchanged++;
781 }
782 }
783 }
784 }
785
786 return nchanged;
787}
788
789static int solver_easy(struct latin_solver *solver, void *vctx)
790{
791 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
792 if (ctx->state->adjacent)
793 return solver_adjacent(solver, vctx);
794 else
795 return solver_links(solver, vctx);
796}
797
798static int solver_set(struct latin_solver *solver, void *vctx)
799{
800 struct solver_ctx *ctx = (struct solver_ctx *)vctx;
801 if (ctx->state->adjacent)
802 return solver_adjacent_set(solver, vctx);
803 else
804 return 0;
805}
806
807#define SOLVER(upper,title,func,lower) func,
808static usersolver_t const unequal_solvers[] = { DIFFLIST(SOLVER) };
809
810static int solver_state(game_state *state, int maxdiff)
811{
812 struct solver_ctx *ctx = new_ctx(state);
813 struct latin_solver solver;
814 int diff;
815
816 latin_solver_alloc(&solver, state->nums, state->order);
817
818 diff = latin_solver_main(&solver, maxdiff,
819 DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
820 DIFF_EXTREME, DIFF_RECURSIVE,
821 unequal_solvers, ctx, clone_ctx, free_ctx);
822
823 memcpy(state->hints, solver.cube, state->order*state->order*state->order);
824
825 free_ctx(ctx);
826
827 latin_solver_free(&solver);
828
829 if (diff == DIFF_IMPOSSIBLE)
830 return -1;
831 if (diff == DIFF_UNFINISHED)
832 return 0;
833 if (diff == DIFF_AMBIGUOUS)
834 return 2;
835 return 1;
836}
837
838static game_state *solver_hint(const game_state *state, int *diff_r,
839 int mindiff, int maxdiff)
840{
841 game_state *ret = dup_game(state);
842 int diff, r = 0;
843
844 for (diff = mindiff; diff <= maxdiff; diff++) {
845 r = solver_state(ret, diff);
846 debug(("solver_state after %s %d", unequal_diffnames[diff], r));
847 if (r != 0) goto done;
848 }
849
850done:
851 if (diff_r) *diff_r = (r > 0) ? diff : -1;
852 return ret;
853}
854
855/* ----------------------------------------------------------
856 * Game generation.
857 */
858
859static char *latin_desc(digit *sq, size_t order)
860{
861 int o2 = order*order, i;
862 char *soln = snewn(o2+2, char);
863
864 soln[0] = 'S';
865 for (i = 0; i < o2; i++)
866 soln[i+1] = n2c(sq[i], order);
867 soln[o2+1] = '\0';
868
869 return soln;
870}
871
872/* returns non-zero if it placed (or could have placed) clue. */
873static int gg_place_clue(game_state *state, int ccode, digit *latin, int checkonly)
874{
875 int loc = ccode / 5, which = ccode % 5;
876 int x = loc % state->order, y = loc / state->order;
877
878 assert(loc < state->order*state->order);
879
880 if (which == 4) { /* add number */
881 if (state->nums[loc] != 0) {
882#ifdef STANDALONE_SOLVER
883 if (state->nums[loc] != latin[loc]) {
884 printf("inconsistency for (%d,%d): state %d latin %d\n",
885 x+1, y+1, state->nums[loc], latin[loc]);
886 }
887#endif
888 assert(state->nums[loc] == latin[loc]);
889 return 0;
890 }
891 if (!checkonly) {
892 state->nums[loc] = latin[loc];
893 }
894 } else { /* add flag */
895 int lx, ly, lloc;
896
897 if (state->adjacent)
898 return 0; /* never add flag clues in adjacent mode (they're always
899 all present) */
900
901 if (state->flags[loc] & adjthan[which].f)
902 return 0; /* already has flag. */
903
904 lx = x + adjthan[which].dx;
905 ly = y + adjthan[which].dy;
906 if (lx < 0 || ly < 0 || lx >= state->order || ly >= state->order)
907 return 0; /* flag compares to off grid */
908
909 lloc = loc + adjthan[which].dx + adjthan[which].dy*state->order;
910 if (latin[loc] <= latin[lloc])
911 return 0; /* flag would be incorrect */
912
913 if (!checkonly) {
914 state->flags[loc] |= adjthan[which].f;
915 }
916 }
917 return 1;
918}
919
920/* returns non-zero if it removed (or could have removed) the clue. */
921static int gg_remove_clue(game_state *state, int ccode, int checkonly)
922{
923 int loc = ccode / 5, which = ccode % 5;
924#ifdef STANDALONE_SOLVER
925 int x = loc % state->order, y = loc / state->order;
926#endif
927
928 assert(loc < state->order*state->order);
929
930 if (which == 4) { /* remove number. */
931 if (state->nums[loc] == 0) return 0;
932 if (!checkonly) {
933#ifdef STANDALONE_SOLVER
934 if (solver_show_working)
935 printf("gg_remove_clue: removing %d at (%d,%d)",
936 state->nums[loc], x+1, y+1);
937#endif
938 state->nums[loc] = 0;
939 }
940 } else { /* remove flag */
941 if (state->adjacent)
942 return 0; /* never remove clues in adjacent mode. */
943
944 if (!(state->flags[loc] & adjthan[which].f)) return 0;
945 if (!checkonly) {
946#ifdef STANDALONE_SOLVER
947 if (solver_show_working)
948 printf("gg_remove_clue: removing %c at (%d,%d)",
949 adjthan[which].c, x+1, y+1);
950#endif
951 state->flags[loc] &= ~adjthan[which].f;
952 }
953 }
954 return 1;
955}
956
957static int gg_best_clue(game_state *state, int *scratch, digit *latin)
958{
959 int ls = state->order * state->order * 5;
960 int maxposs = 0, minclues = 5, best = -1, i, j;
961 int nposs, nclues, loc;
962
963#ifdef STANDALONE_SOLVER
964 if (solver_show_working) {
965 game_debug(state);
966 latin_solver_debug(state->hints, state->order);
967 }
968#endif
969
970 for (i = ls; i-- > 0 ;) {
971 if (!gg_place_clue(state, scratch[i], latin, 1)) continue;
972
973 loc = scratch[i] / 5;
974 for (j = nposs = 0; j < state->order; j++) {
975 if (state->hints[loc*state->order + j]) nposs++;
976 }
977 for (j = nclues = 0; j < 4; j++) {
978 if (state->flags[loc] & adjthan[j].f) nclues++;
979 }
980 if ((nposs > maxposs) ||
981 (nposs == maxposs && nclues < minclues)) {
982 best = i; maxposs = nposs; minclues = nclues;
983#ifdef STANDALONE_SOLVER
984 if (solver_show_working) {
985 int x = loc % state->order, y = loc / state->order;
986 printf("gg_best_clue: b%d (%d,%d) new best [%d poss, %d clues].\n",
987 best, x+1, y+1, nposs, nclues);
988 }
989#endif
990 }
991 }
992 /* if we didn't solve, we must have 1 clue to place! */
993 assert(best != -1);
994 return best;
995}
996
997#ifdef STANDALONE_SOLVER
998int maxtries;
999#define MAXTRIES maxtries
1000#else
1001#define MAXTRIES 50
1002#endif
1003int gg_solved;
1004
1005static int game_assemble(game_state *new, int *scratch, digit *latin,
1006 int difficulty)
1007{
1008 game_state *copy = dup_game(new);
1009 int best;
1010
1011 if (difficulty >= DIFF_RECURSIVE) {
1012 /* We mustn't use any solver that might guess answers;
1013 * if it guesses wrongly but solves, gg_place_clue will
1014 * get mighty confused. We will always trim clues down
1015 * (making it more difficult) in game_strip, which doesn't
1016 * have this problem. */
1017 difficulty = DIFF_RECURSIVE-1;
1018 }
1019
1020#ifdef STANDALONE_SOLVER
1021 if (solver_show_working) {
1022 game_debug(new);
1023 latin_solver_debug(new->hints, new->order);
1024 }
1025#endif
1026
1027 while(1) {
1028 gg_solved++;
1029 if (solver_state(copy, difficulty) == 1) break;
1030
1031 best = gg_best_clue(copy, scratch, latin);
1032 gg_place_clue(new, scratch[best], latin, 0);
1033 gg_place_clue(copy, scratch[best], latin, 0);
1034 }
1035 free_game(copy);
1036#ifdef STANDALONE_SOLVER
1037 if (solver_show_working) {
1038 char *dbg = game_text_format(new);
1039 printf("game_assemble: done, %d solver iterations:\n%s\n", gg_solved, dbg);
1040 sfree(dbg);
1041 }
1042#endif
1043 return 0;
1044}
1045
1046static void game_strip(game_state *new, int *scratch, digit *latin,
1047 int difficulty)
1048{
1049 int o = new->order, o2 = o*o, lscratch = o2*5, i;
1050 game_state *copy = blank_game(new->order, new->adjacent);
1051
1052 /* For each symbol (if it exists in new), try and remove it and
1053 * solve again; if we couldn't solve without it put it back. */
1054 for (i = 0; i < lscratch; i++) {
1055 if (!gg_remove_clue(new, scratch[i], 0)) continue;
1056
1057 memcpy(copy->nums, new->nums, o2 * sizeof(digit));
1058 memcpy(copy->flags, new->flags, o2 * sizeof(unsigned int));
1059 gg_solved++;
1060 if (solver_state(copy, difficulty) != 1) {
1061 /* put clue back, we can't solve without it. */
1062 int ret = gg_place_clue(new, scratch[i], latin, 0);
1063 assert(ret == 1);
1064 } else {
1065#ifdef STANDALONE_SOLVER
1066 if (solver_show_working)
1067 printf("game_strip: clue was redundant.");
1068#endif
1069 }
1070 }
1071 free_game(copy);
1072#ifdef STANDALONE_SOLVER
1073 if (solver_show_working) {
1074 char *dbg = game_text_format(new);
1075 debug(("game_strip: done, %d solver iterations.", gg_solved));
1076 debug(("%s", dbg));
1077 sfree(dbg);
1078 }
1079#endif
1080}
1081
1082static void add_adjacent_flags(game_state *state, digit *latin)
1083{
1084 int x, y, o = state->order;
1085
1086 /* All clues in adjacent mode are always present (the only variables are
1087 * the numbers). This adds all the flags to state based on the supplied
1088 * latin square. */
1089
1090 for (y = 0; y < o; y++) {
1091 for (x = 0; x < o; x++) {
1092 if (x < (o-1) && (abs(latin[y*o+x] - latin[y*o+x+1]) == 1)) {
1093 GRID(state, flags, x, y) |= F_ADJ_RIGHT;
1094 GRID(state, flags, x+1, y) |= F_ADJ_LEFT;
1095 }
1096 if (y < (o-1) && (abs(latin[y*o+x] - latin[(y+1)*o+x]) == 1)) {
1097 GRID(state, flags, x, y) |= F_ADJ_DOWN;
1098 GRID(state, flags, x, y+1) |= F_ADJ_UP;
1099 }
1100 }
1101 }
1102}
1103
1104static char *new_game_desc(const game_params *params_in, random_state *rs,
1105 char **aux, int interactive)
1106{
1107 game_params params_copy = *params_in; /* structure copy */
1108 game_params *params = &params_copy;
1109 digit *sq = NULL;
1110 int i, x, y, retlen, k, nsol;
1111 int o2 = params->order * params->order, ntries = 1;
1112 int *scratch, lscratch = o2*5;
1113 char *ret, buf[80];
1114 game_state *state = blank_game(params->order, params->adjacent);
1115
1116 /* Generate a list of 'things to strip' (randomised later) */
1117 scratch = snewn(lscratch, int);
1118 /* Put the numbers (4 mod 5) before the inequalities (0-3 mod 5) */
1119 for (i = 0; i < lscratch; i++) scratch[i] = (i%o2)*5 + 4 - (i/o2);
1120
1121generate:
1122#ifdef STANDALONE_SOLVER
1123 if (solver_show_working)
1124 printf("new_game_desc: generating %s puzzle, ntries so far %d\n",
1125 unequal_diffnames[params->diff], ntries);
1126#endif
1127 if (sq) sfree(sq);
1128 sq = latin_generate(params->order, rs);
1129 latin_debug(sq, params->order);
1130 /* Separately shuffle the numeric and inequality clues */
1131 shuffle(scratch, lscratch/5, sizeof(int), rs);
1132 shuffle(scratch+lscratch/5, 4*lscratch/5, sizeof(int), rs);
1133
1134 memset(state->nums, 0, o2 * sizeof(digit));
1135 memset(state->flags, 0, o2 * sizeof(unsigned int));
1136
1137 if (state->adjacent) {
1138 /* All adjacency flags are always present. */
1139 add_adjacent_flags(state, sq);
1140 }
1141
1142 gg_solved = 0;
1143 if (game_assemble(state, scratch, sq, params->diff) < 0)
1144 goto generate;
1145 game_strip(state, scratch, sq, params->diff);
1146
1147 if (params->diff > 0) {
1148 game_state *copy = dup_game(state);
1149 nsol = solver_state(copy, params->diff-1);
1150 free_game(copy);
1151 if (nsol > 0) {
1152#ifdef STANDALONE_SOLVER
1153 if (solver_show_working)
1154 printf("game_assemble: puzzle as generated is too easy.\n");
1155#endif
1156 if (ntries < MAXTRIES) {
1157 ntries++;
1158 goto generate;
1159 }
1160#ifdef STANDALONE_SOLVER
1161 if (solver_show_working)
1162 printf("Unable to generate %s %dx%d after %d attempts.\n",
1163 unequal_diffnames[params->diff],
1164 params->order, params->order, MAXTRIES);
1165#endif
1166 params->diff--;
1167 }
1168 }
1169#ifdef STANDALONE_SOLVER
1170 if (solver_show_working)
1171 printf("new_game_desc: generated %s puzzle; %d attempts (%d solver).\n",
1172 unequal_diffnames[params->diff], ntries, gg_solved);
1173#endif
1174
1175 ret = NULL; retlen = 0;
1176 for (y = 0; y < params->order; y++) {
1177 for (x = 0; x < params->order; x++) {
1178 unsigned int f = GRID(state, flags, x, y);
1179 k = sprintf(buf, "%d%s%s%s%s,",
1180 GRID(state, nums, x, y),
1181 (f & F_ADJ_UP) ? "U" : "",
1182 (f & F_ADJ_RIGHT) ? "R" : "",
1183 (f & F_ADJ_DOWN) ? "D" : "",
1184 (f & F_ADJ_LEFT) ? "L" : "");
1185
1186 ret = sresize(ret, retlen + k + 1, char);
1187 strcpy(ret + retlen, buf);
1188 retlen += k;
1189 }
1190 }
1191 *aux = latin_desc(sq, params->order);
1192
1193 free_game(state);
1194 sfree(sq);
1195 sfree(scratch);
1196
1197 return ret;
1198}
1199
1200static game_state *load_game(const game_params *params, const char *desc,
1201 char **why_r)
1202{
1203 game_state *state = blank_game(params->order, params->adjacent);
1204 const char *p = desc;
1205 int i = 0, n, o = params->order, x, y;
1206 char *why = NULL;
1207
1208 while (*p) {
1209 while (*p >= 'a' && *p <= 'z') {
1210 i += *p - 'a' + 1;
1211 p++;
1212 }
1213 if (i >= o*o) {
1214 why = "Too much data to fill grid"; goto fail;
1215 }
1216
1217 if (*p < '0' || *p > '9') {
1218 why = "Expecting number in game description"; goto fail;
1219 }
1220 n = atoi(p);
1221 if (n < 0 || n > o) {
1222 why = "Out-of-range number in game description"; goto fail;
1223 }
1224 state->nums[i] = (digit)n;
1225 while (*p >= '0' && *p <= '9') p++; /* skip number */
1226
1227 if (state->nums[i] != 0)
1228 state->flags[i] |= F_IMMUTABLE; /* === number set by game description */
1229
1230 while (*p == 'U' || *p == 'R' || *p == 'D' || *p == 'L') {
1231 switch (*p) {
1232 case 'U': state->flags[i] |= F_ADJ_UP; break;
1233 case 'R': state->flags[i] |= F_ADJ_RIGHT; break;
1234 case 'D': state->flags[i] |= F_ADJ_DOWN; break;
1235 case 'L': state->flags[i] |= F_ADJ_LEFT; break;
1236 default: why = "Expecting flag URDL in game description"; goto fail;
1237 }
1238 p++;
1239 }
1240 i++;
1241 if (i < o*o && *p != ',') {
1242 why = "Missing separator"; goto fail;
1243 }
1244 if (*p == ',') p++;
1245 }
1246 if (i < o*o) {
1247 why = "Not enough data to fill grid"; goto fail;
1248 }
1249 i = 0;
1250 for (y = 0; y < o; y++) {
1251 for (x = 0; x < o; x++) {
1252 for (n = 0; n < 4; n++) {
1253 if (GRID(state, flags, x, y) & adjthan[n].f) {
1254 int nx = x + adjthan[n].dx;
1255 int ny = y + adjthan[n].dy;
1256 /* a flag must not point us off the grid. */
1257 if (nx < 0 || ny < 0 || nx >= o || ny >= o) {
1258 why = "Flags go off grid"; goto fail;
1259 }
1260 if (params->adjacent) {
1261 /* if one cell is adjacent to another, the other must
1262 * also be adjacent to the first. */
1263 if (!(GRID(state, flags, nx, ny) & adjthan[n].fo)) {
1264 why = "Flags contradicting each other"; goto fail;
1265 }
1266 } else {
1267 /* if one cell is GT another, the other must _not_ also
1268 * be GT the first. */
1269 if (GRID(state, flags, nx, ny) & adjthan[n].fo) {
1270 why = "Flags contradicting each other"; goto fail;
1271 }
1272 }
1273 }
1274 }
1275 }
1276 }
1277
1278 return state;
1279
1280fail:
1281 free_game(state);
1282 if (why_r) *why_r = why;
1283 return NULL;
1284}
1285
1286static game_state *new_game(midend *me, const game_params *params,
1287 const char *desc)
1288{
1289 game_state *state = load_game(params, desc, NULL);
1290 if (!state) {
1291 assert("Unable to load ?validated game.");
1292 return NULL;
1293 }
1294 return state;
1295}
1296
1297static char *validate_desc(const game_params *params, const char *desc)
1298{
1299 char *why = NULL;
1300 game_state *dummy = load_game(params, desc, &why);
1301 if (dummy) {
1302 free_game(dummy);
1303 assert(!why);
1304 } else
1305 assert(why);
1306 return why;
1307}
1308
1309static char *solve_game(const game_state *state, const game_state *currstate,
1310 const char *aux, char **error)
1311{
1312 game_state *solved;
1313 int r;
1314 char *ret = NULL;
1315
1316 if (aux) return dupstr(aux);
1317
1318 solved = dup_game(state);
1319 for (r = 0; r < state->order*state->order; r++) {
1320 if (!(solved->flags[r] & F_IMMUTABLE))
1321 solved->nums[r] = 0;
1322 }
1323 r = solver_state(solved, DIFFCOUNT-1); /* always use full solver */
1324 if (r > 0) ret = latin_desc(solved->nums, solved->order);
1325 free_game(solved);
1326 return ret;
1327}
1328
1329/* ----------------------------------------------------------
1330 * Game UI input processing.
1331 */
1332
1333struct game_ui {
1334 int hx, hy; /* as for solo.c, highlight pos */
1335 int hshow, hpencil, hcursor; /* show state, type, and ?cursor. */
1336};
1337
1338static game_ui *new_ui(const game_state *state)
1339{
1340 game_ui *ui = snew(game_ui);
1341
1342 ui->hx = ui->hy = 0;
1343 ui->hpencil = ui->hshow = ui->hcursor = 0;
1344
1345 return ui;
1346}
1347
1348static void free_ui(game_ui *ui)
1349{
1350 sfree(ui);
1351}
1352
1353static char *encode_ui(const game_ui *ui)
1354{
1355 return NULL;
1356}
1357
1358static void decode_ui(game_ui *ui, const char *encoding)
1359{
1360}
1361
1362static void game_changed_state(game_ui *ui, const game_state *oldstate,
1363 const game_state *newstate)
1364{
1365 /* See solo.c; if we were pencil-mode highlighting and
1366 * somehow a square has just been properly filled, cancel
1367 * pencil mode. */
1368 if (ui->hshow && ui->hpencil && !ui->hcursor &&
1369 GRID(newstate, nums, ui->hx, ui->hy) != 0) {
1370 ui->hshow = 0;
1371 }
1372}
1373
1374struct game_drawstate {
1375 int tilesize, order, started, adjacent;
1376 digit *nums; /* copy of nums, o^2 */
1377 unsigned char *hints; /* copy of hints, o^3 */
1378 unsigned int *flags; /* o^2 */
1379
1380 int hx, hy, hshow, hpencil; /* as for game_ui. */
1381 int hflash;
1382};
1383
1384static char *interpret_move(const game_state *state, game_ui *ui,
1385 const game_drawstate *ds,
1386 int ox, int oy, int button)
1387{
1388 int x = FROMCOORD(ox), y = FROMCOORD(oy), n;
1389 char buf[80];
1390 int shift_or_control = button & (MOD_SHFT | MOD_CTRL);
1391
1392 button &= ~MOD_MASK;
1393
1394 if (x >= 0 && x < ds->order && y >= 0 && y < ds->order && IS_MOUSE_DOWN(button)) {
1395 if (oy - COORD(y) > TILE_SIZE && ox - COORD(x) > TILE_SIZE)
1396 return NULL;
1397
1398 if (oy - COORD(y) > TILE_SIZE) {
1399 if (GRID(state, flags, x, y) & F_ADJ_DOWN)
1400 sprintf(buf, "F%d,%d,%d", x, y, F_SPENT_DOWN);
1401 else if (y + 1 < ds->order && GRID(state, flags, x, y + 1) & F_ADJ_UP)
1402 sprintf(buf, "F%d,%d,%d", x, y + 1, F_SPENT_UP);
1403 else return NULL;
1404 return dupstr(buf);
1405 }
1406
1407 if (ox - COORD(x) > TILE_SIZE) {
1408 if (GRID(state, flags, x, y) & F_ADJ_RIGHT)
1409 sprintf(buf, "F%d,%d,%d", x, y, F_SPENT_RIGHT);
1410 else if (x + 1 < ds->order && GRID(state, flags, x + 1, y) & F_ADJ_LEFT)
1411 sprintf(buf, "F%d,%d,%d", x + 1, y, F_SPENT_LEFT);
1412 else return NULL;
1413 return dupstr(buf);
1414 }
1415
1416 if (button == LEFT_BUTTON) {
1417 /* normal highlighting for non-immutable squares */
1418 if (GRID(state, flags, x, y) & F_IMMUTABLE)
1419 ui->hshow = 0;
1420 else if (x == ui->hx && y == ui->hy &&
1421 ui->hshow && ui->hpencil == 0)
1422 ui->hshow = 0;
1423 else {
1424 ui->hx = x; ui->hy = y; ui->hpencil = 0;
1425 ui->hshow = 1;
1426 }
1427 ui->hcursor = 0;
1428 return "";
1429 }
1430 if (button == RIGHT_BUTTON) {
1431 /* pencil highlighting for non-filled squares */
1432 if (GRID(state, nums, x, y) != 0)
1433 ui->hshow = 0;
1434 else if (x == ui->hx && y == ui->hy &&
1435 ui->hshow && ui->hpencil)
1436 ui->hshow = 0;
1437 else {
1438 ui->hx = x; ui->hy = y; ui->hpencil = 1;
1439 ui->hshow = 1;
1440 }
1441 ui->hcursor = 0;
1442 return "";
1443 }
1444 }
1445
1446 if (IS_CURSOR_MOVE(button)) {
1447 if (shift_or_control) {
1448 int nx = ui->hx, ny = ui->hy, i, self;
1449 move_cursor(button, &nx, &ny, ds->order, ds->order, FALSE);
1450 ui->hshow = ui->hcursor = 1;
1451
1452 for (i = 0; i < 4 && (nx != ui->hx + adjthan[i].dx ||
1453 ny != ui->hy + adjthan[i].dy); ++i);
1454
1455 if (i == 4)
1456 return ""; /* invalid direction, i.e. out of the board */
1457
1458 if (!(GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f ||
1459 GRID(state, flags, nx, ny ) & adjthan[i].fo))
1460 return ""; /* no clue to toggle */
1461
1462 if (state->adjacent)
1463 self = (adjthan[i].dx >= 0 && adjthan[i].dy >= 0);
1464 else
1465 self = (GRID(state, flags, ui->hx, ui->hy) & adjthan[i].f);
1466
1467 if (self)
1468 sprintf(buf, "F%d,%d,%d", ui->hx, ui->hy,
1469 ADJ_TO_SPENT(adjthan[i].f));
1470 else
1471 sprintf(buf, "F%d,%d,%d", nx, ny,
1472 ADJ_TO_SPENT(adjthan[i].fo));
1473
1474 return dupstr(buf);
1475 } else {
1476 move_cursor(button, &ui->hx, &ui->hy, ds->order, ds->order, FALSE);
1477 ui->hshow = ui->hcursor = 1;
1478 return "";
1479 }
1480 }
1481 if (ui->hshow && IS_CURSOR_SELECT(button)) {
1482 ui->hpencil = 1 - ui->hpencil;
1483 ui->hcursor = 1;
1484 return "";
1485 }
1486
1487 n = c2n(button, state->order);
1488 if (ui->hshow && n >= 0 && n <= ds->order) {
1489 debug(("button %d, cbutton %d", button, (int)((char)button)));
1490
1491 debug(("n %d, h (%d,%d) p %d flags 0x%x nums %d",
1492 n, ui->hx, ui->hy, ui->hpencil,
1493 GRID(state, flags, ui->hx, ui->hy),
1494 GRID(state, nums, ui->hx, ui->hy)));
1495
1496 if (GRID(state, flags, ui->hx, ui->hy) & F_IMMUTABLE)
1497 return NULL; /* can't edit immutable square (!) */
1498 if (ui->hpencil && GRID(state, nums, ui->hx, ui->hy) > 0)
1499 return NULL; /* can't change hints on filled square (!) */
1500
1501
1502 sprintf(buf, "%c%d,%d,%d",
1503 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
1504
1505 if (!ui->hcursor) ui->hshow = 0;
1506
1507 return dupstr(buf);
1508 }
1509
1510 if (button == 'H' || button == 'h')
1511 return dupstr("H");
1512 if (button == 'M' || button == 'm')
1513 return dupstr("M");
1514
1515 return NULL;
1516}
1517
1518static game_state *execute_move(const game_state *state, const char *move)
1519{
1520 game_state *ret = NULL;
1521 int x, y, n, i, rc;
1522
1523 debug(("execute_move: %s", move));
1524
1525 if ((move[0] == 'P' || move[0] == 'R') &&
1526 sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
1527 x >= 0 && x < state->order && y >= 0 && y < state->order &&
1528 n >= 0 && n <= state->order) {
1529 ret = dup_game(state);
1530 if (move[0] == 'P' && n > 0)
1531 HINT(ret, x, y, n-1) = !HINT(ret, x, y, n-1);
1532 else {
1533 GRID(ret, nums, x, y) = n;
1534 for (i = 0; i < state->order; i++)
1535 HINT(ret, x, y, i) = 0;
1536
1537 /* real change to grid; check for completion */
1538 if (!ret->completed && check_complete(ret->nums, ret, 1) > 0)
1539 ret->completed = TRUE;
1540 }
1541 return ret;
1542 } else if (move[0] == 'S') {
1543 const char *p;
1544
1545 ret = dup_game(state);
1546 ret->completed = ret->cheated = TRUE;
1547
1548 p = move+1;
1549 for (i = 0; i < state->order*state->order; i++) {
1550 n = c2n((int)*p, state->order);
1551 if (!*p || n <= 0 || n > state->order)
1552 goto badmove;
1553 ret->nums[i] = n;
1554 p++;
1555 }
1556 if (*p) goto badmove;
1557 rc = check_complete(ret->nums, ret, 1);
1558 assert(rc > 0);
1559 return ret;
1560 } else if (move[0] == 'M') {
1561 ret = dup_game(state);
1562 for (x = 0; x < state->order; x++) {
1563 for (y = 0; y < state->order; y++) {
1564 for (n = 0; n < state->order; n++) {
1565 HINT(ret, x, y, n) = 1;
1566 }
1567 }
1568 }
1569 return ret;
1570 } else if (move[0] == 'H') {
1571 return solver_hint(state, NULL, DIFF_EASY, DIFF_EASY);
1572 } else if (move[0] == 'F' && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 &&
1573 x >= 0 && x < state->order && y >= 0 && y < state->order) {
1574 ret = dup_game(state);
1575 GRID(ret, flags, x, y) ^= n;
1576 return ret;
1577 }
1578
1579badmove:
1580 if (ret) free_game(ret);
1581 return NULL;
1582}
1583
1584/* ----------------------------------------------------------------------
1585 * Drawing/printing routines.
1586 */
1587
1588#define DRAW_SIZE (TILE_SIZE*ds->order + GAP_SIZE*(ds->order-1) + BORDER*2)
1589
1590static void game_compute_size(const game_params *params, int tilesize,
1591 int *x, int *y)
1592{
1593 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1594 struct { int tilesize, order; } ads, *ds = &ads;
1595 ads.tilesize = tilesize;
1596 ads.order = params->order;
1597
1598 *x = *y = DRAW_SIZE;
1599}
1600
1601static void game_set_size(drawing *dr, game_drawstate *ds,
1602 const game_params *params, int tilesize)
1603{
1604 ds->tilesize = tilesize;
1605}
1606
1607static float *game_colours(frontend *fe, int *ncolours)
1608{
1609 float *ret = snewn(3 * NCOLOURS, float);
1610 int i;
1611
1612 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1613
1614 for (i = 0; i < 3; i++) {
1615 ret[COL_TEXT * 3 + i] = 0.0F;
1616 ret[COL_GRID * 3 + i] = 0.5F;
1617 }
1618
1619 /* Lots of these were taken from solo.c. */
1620 ret[COL_GUESS * 3 + 0] = 0.0F;
1621 ret[COL_GUESS * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1622 ret[COL_GUESS * 3 + 2] = 0.0F;
1623
1624 ret[COL_ERROR * 3 + 0] = 1.0F;
1625 ret[COL_ERROR * 3 + 1] = 0.0F;
1626 ret[COL_ERROR * 3 + 2] = 0.0F;
1627
1628 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1629 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1630 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1631
1632 *ncolours = NCOLOURS;
1633 return ret;
1634}
1635
1636static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1637{
1638 struct game_drawstate *ds = snew(struct game_drawstate);
1639 int o2 = state->order*state->order, o3 = o2*state->order;
1640
1641 ds->tilesize = 0;
1642 ds->order = state->order;
1643 ds->adjacent = state->adjacent;
1644
1645 ds->nums = snewn(o2, digit);
1646 ds->hints = snewn(o3, unsigned char);
1647 ds->flags = snewn(o2, unsigned int);
1648 memset(ds->nums, 0, o2*sizeof(digit));
1649 memset(ds->hints, 0, o3);
1650 memset(ds->flags, 0, o2*sizeof(unsigned int));
1651
1652 ds->hx = ds->hy = 0;
1653 ds->started = ds->hshow = ds->hpencil = ds->hflash = 0;
1654
1655 return ds;
1656}
1657
1658static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1659{
1660 sfree(ds->nums);
1661 sfree(ds->hints);
1662 sfree(ds->flags);
1663 sfree(ds);
1664}
1665
1666static void draw_gt(drawing *dr, int ox, int oy,
1667 int dx1, int dy1, int dx2, int dy2, int col)
1668{
1669 int coords[12];
1670 int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0);
1671 coords[0] = ox + xdx;
1672 coords[1] = oy + xdy;
1673 coords[2] = ox + xdx + dx1;
1674 coords[3] = oy + xdy + dy1;
1675 coords[4] = ox + xdx + dx1 + dx2;
1676 coords[5] = oy + xdy + dy1 + dy2;
1677 coords[6] = ox - xdx + dx1 + dx2;
1678 coords[7] = oy - xdy + dy1 + dy2;
1679 coords[8] = ox - xdx + dx1;
1680 coords[9] = oy - xdy + dy1;
1681 coords[10] = ox - xdx;
1682 coords[11] = oy - xdy;
1683 draw_polygon(dr, coords, 6, col, col);
1684}
1685
1686#define COLOUR(direction) (f & (F_ERROR_##direction) ? COL_ERROR : \
1687 f & (F_SPENT_##direction) ? COL_SPENT : fg)
1688
1689static void draw_gts(drawing *dr, game_drawstate *ds, int ox, int oy,
1690 unsigned int f, int bg, int fg)
1691{
1692 int g = GAP_SIZE, g2 = (g+1)/2, g4 = (g+1)/4;
1693
1694 /* Draw all the greater-than signs emanating from this tile. */
1695
1696 if (f & F_ADJ_UP) {
1697 if (bg >= 0) draw_rect(dr, ox, oy - g, TILE_SIZE, g, bg);
1698 draw_gt(dr, ox+g2, oy-g4, g2, -g2, g2, g2, COLOUR(UP));
1699 draw_update(dr, ox, oy-g, TILE_SIZE, g);
1700 }
1701 if (f & F_ADJ_RIGHT) {
1702 if (bg >= 0) draw_rect(dr, ox + TILE_SIZE, oy, g, TILE_SIZE, bg);
1703 draw_gt(dr, ox+TILE_SIZE+g4, oy+g2, g2, g2, -g2, g2, COLOUR(RIGHT));
1704 draw_update(dr, ox+TILE_SIZE, oy, g, TILE_SIZE);
1705 }
1706 if (f & F_ADJ_DOWN) {
1707 if (bg >= 0) draw_rect(dr, ox, oy + TILE_SIZE, TILE_SIZE, g, bg);
1708 draw_gt(dr, ox+g2, oy+TILE_SIZE+g4, g2, g2, g2, -g2, COLOUR(DOWN));
1709 draw_update(dr, ox, oy+TILE_SIZE, TILE_SIZE, g);
1710 }
1711 if (f & F_ADJ_LEFT) {
1712 if (bg >= 0) draw_rect(dr, ox - g, oy, g, TILE_SIZE, bg);
1713 draw_gt(dr, ox-g4, oy+g2, -g2, g2, g2, g2, COLOUR(LEFT));
1714 draw_update(dr, ox-g, oy, g, TILE_SIZE);
1715 }
1716}
1717
1718static void draw_adjs(drawing *dr, game_drawstate *ds, int ox, int oy,
1719 unsigned int f, int bg, int fg)
1720{
1721 int g = GAP_SIZE, g38 = 3*(g+1)/8, g4 = (g+1)/4;
1722
1723 /* Draw all the adjacency bars relevant to this tile; we only have
1724 * to worry about F_ADJ_RIGHT and F_ADJ_DOWN.
1725 *
1726 * If we _only_ have the error flag set (i.e. it's not supposed to be
1727 * adjacent, but adjacent numbers were entered) draw an outline red bar.
1728 */
1729
1730 if (f & (F_ADJ_RIGHT|F_ERROR_RIGHT)) {
1731 if (f & F_ADJ_RIGHT) {
1732 draw_rect(dr, ox+TILE_SIZE+g38, oy, g4, TILE_SIZE, COLOUR(RIGHT));
1733 } else {
1734 draw_rect_outline(dr, ox+TILE_SIZE+g38, oy, g4, TILE_SIZE, COL_ERROR);
1735 }
1736 } else if (bg >= 0) {
1737 draw_rect(dr, ox+TILE_SIZE+g38, oy, g4, TILE_SIZE, bg);
1738 }
1739 draw_update(dr, ox+TILE_SIZE, oy, g, TILE_SIZE);
1740
1741 if (f & (F_ADJ_DOWN|F_ERROR_DOWN)) {
1742 if (f & F_ADJ_DOWN) {
1743 draw_rect(dr, ox, oy+TILE_SIZE+g38, TILE_SIZE, g4, COLOUR(DOWN));
1744 } else {
1745 draw_rect_outline(dr, ox, oy+TILE_SIZE+g38, TILE_SIZE, g4, COL_ERROR);
1746 }
1747 } else if (bg >= 0) {
1748 draw_rect(dr, ox, oy+TILE_SIZE+g38, TILE_SIZE, g4, bg);
1749 }
1750 draw_update(dr, ox, oy+TILE_SIZE, TILE_SIZE, g);
1751}
1752
1753static void draw_furniture(drawing *dr, game_drawstate *ds,
1754 const game_state *state, const game_ui *ui,
1755 int x, int y, int hflash)
1756{
1757 int ox = COORD(x), oy = COORD(y), bg, hon;
1758 unsigned int f = GRID(state, flags, x, y);
1759
1760 bg = hflash ? COL_HIGHLIGHT : COL_BACKGROUND;
1761
1762 hon = (ui->hshow && x == ui->hx && y == ui->hy);
1763
1764 /* Clear square. */
1765 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE,
1766 (hon && !ui->hpencil) ? COL_HIGHLIGHT : bg);
1767
1768 /* Draw the highlight (pencil or full), if we're the highlight */
1769 if (hon && ui->hpencil) {
1770 int coords[6];
1771 coords[0] = ox;
1772 coords[1] = oy;
1773 coords[2] = ox + TILE_SIZE/2;
1774 coords[3] = oy;
1775 coords[4] = ox;
1776 coords[5] = oy + TILE_SIZE/2;
1777 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1778 }
1779
1780 /* Draw the square outline (which is the cursor, if we're the cursor). */
1781 draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_GRID);
1782
1783 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
1784
1785 /* Draw the adjacent clue signs. */
1786 if (ds->adjacent)
1787 draw_adjs(dr, ds, ox, oy, f, COL_BACKGROUND, COL_GRID);
1788 else
1789 draw_gts(dr, ds, ox, oy, f, COL_BACKGROUND, COL_TEXT);
1790}
1791
1792static void draw_num(drawing *dr, game_drawstate *ds, int x, int y)
1793{
1794 int ox = COORD(x), oy = COORD(y);
1795 unsigned int f = GRID(ds,flags,x,y);
1796 char str[2];
1797
1798 /* (can assume square has just been cleared) */
1799
1800 /* Draw number, choosing appropriate colour */
1801 str[0] = n2c(GRID(ds, nums, x, y), ds->order);
1802 str[1] = '\0';
1803 draw_text(dr, ox + TILE_SIZE/2, oy + TILE_SIZE/2,
1804 FONT_VARIABLE, 3*TILE_SIZE/4, ALIGN_VCENTRE | ALIGN_HCENTRE,
1805 (f & F_IMMUTABLE) ? COL_TEXT : (f & F_ERROR) ? COL_ERROR : COL_GUESS, str);
1806}
1807
1808static void draw_hints(drawing *dr, game_drawstate *ds, int x, int y)
1809{
1810 int ox = COORD(x), oy = COORD(y);
1811 int nhints, i, j, hw, hh, hmax, fontsz;
1812 char str[2];
1813
1814 /* (can assume square has just been cleared) */
1815
1816 /* Draw hints; steal ingenious algorithm (basically)
1817 * from solo.c:draw_number() */
1818 for (i = nhints = 0; i < ds->order; i++) {
1819 if (HINT(ds, x, y, i)) nhints++;
1820 }
1821
1822 for (hw = 1; hw * hw < nhints; hw++);
1823 if (hw < 3) hw = 3;
1824 hh = (nhints + hw - 1) / hw;
1825 if (hh < 2) hh = 2;
1826 hmax = max(hw, hh);
1827 fontsz = TILE_SIZE/(hmax*(11-hmax)/8);
1828
1829 for (i = j = 0; i < ds->order; i++) {
1830 if (HINT(ds,x,y,i)) {
1831 int hx = j % hw, hy = j / hw;
1832
1833 str[0] = n2c(i+1, ds->order);
1834 str[1] = '\0';
1835 draw_text(dr,
1836 ox + (4*hx+3) * TILE_SIZE / (4*hw+2),
1837 oy + (4*hy+3) * TILE_SIZE / (4*hh+2),
1838 FONT_VARIABLE, fontsz,
1839 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
1840 j++;
1841 }
1842 }
1843}
1844
1845static void game_redraw(drawing *dr, game_drawstate *ds,
1846 const game_state *oldstate, const game_state *state,
1847 int dir, const game_ui *ui,
1848 float animtime, float flashtime)
1849{
1850 int x, y, i, hchanged = 0, stale, hflash = 0;
1851
1852 debug(("highlight old (%d,%d), new (%d,%d)", ds->hx, ds->hy, ui->hx, ui->hy));
1853
1854 if (flashtime > 0 &&
1855 (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3))
1856 hflash = 1;
1857
1858 if (!ds->started) {
1859 draw_rect(dr, 0, 0, DRAW_SIZE, DRAW_SIZE, COL_BACKGROUND);
1860 draw_update(dr, 0, 0, DRAW_SIZE, DRAW_SIZE);
1861 }
1862 if (ds->hx != ui->hx || ds->hy != ui->hy ||
1863 ds->hshow != ui->hshow || ds->hpencil != ui->hpencil)
1864 hchanged = 1;
1865
1866 for (x = 0; x < ds->order; x++) {
1867 for (y = 0; y < ds->order; y++) {
1868 if (!ds->started)
1869 stale = 1;
1870 else if (hflash != ds->hflash)
1871 stale = 1;
1872 else
1873 stale = 0;
1874
1875 if (hchanged) {
1876 if ((x == ui->hx && y == ui->hy) ||
1877 (x == ds->hx && y == ds->hy))
1878 stale = 1;
1879 }
1880
1881 if (GRID(state, nums, x, y) != GRID(ds, nums, x, y)) {
1882 GRID(ds, nums, x, y) = GRID(state, nums, x, y);
1883 stale = 1;
1884 }
1885 if (GRID(state, flags, x, y) != GRID(ds, flags, x, y)) {
1886 GRID(ds, flags, x, y) = GRID(state, flags, x, y);
1887 stale = 1;
1888 }
1889 if (GRID(ds, nums, x, y) == 0) {
1890 /* We're not a number square (therefore we might
1891 * display hints); do we need to update? */
1892 for (i = 0; i < ds->order; i++) {
1893 if (HINT(state, x, y, i) != HINT(ds, x, y, i)) {
1894 HINT(ds, x, y, i) = HINT(state, x, y, i);
1895 stale = 1;
1896 }
1897 }
1898 }
1899 if (stale) {
1900 draw_furniture(dr, ds, state, ui, x, y, hflash);
1901 if (GRID(ds, nums, x, y) > 0)
1902 draw_num(dr, ds, x, y);
1903 else
1904 draw_hints(dr, ds, x, y);
1905 }
1906 }
1907 }
1908 ds->hx = ui->hx; ds->hy = ui->hy;
1909 ds->hshow = ui->hshow;
1910 ds->hpencil = ui->hpencil;
1911
1912 ds->started = 1;
1913 ds->hflash = hflash;
1914}
1915
1916static float game_anim_length(const game_state *oldstate,
1917 const game_state *newstate, int dir, game_ui *ui)
1918{
1919 return 0.0F;
1920}
1921
1922static float game_flash_length(const game_state *oldstate,
1923 const game_state *newstate, int dir, game_ui *ui)
1924{
1925 if (!oldstate->completed && newstate->completed &&
1926 !oldstate->cheated && !newstate->cheated)
1927 return FLASH_TIME;
1928 return 0.0F;
1929}
1930
1931static int game_status(const game_state *state)
1932{
1933 return state->completed ? +1 : 0;
1934}
1935
1936static int game_timing_state(const game_state *state, game_ui *ui)
1937{
1938 return TRUE;
1939}
1940
1941static void game_print_size(const game_params *params, float *x, float *y)
1942{
1943 int pw, ph;
1944
1945 /* 10mm squares by default, roughly the same as Grauniad. */
1946 game_compute_size(params, 1000, &pw, &ph);
1947 *x = pw / 100.0F;
1948 *y = ph / 100.0F;
1949}
1950
1951static void game_print(drawing *dr, const game_state *state, int tilesize)
1952{
1953 int ink = print_mono_colour(dr, 0);
1954 int x, y, o = state->order, ox, oy, n;
1955 char str[2];
1956
1957 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1958 game_drawstate ads, *ds = &ads;
1959 game_set_size(dr, ds, NULL, tilesize);
1960
1961 print_line_width(dr, 2 * TILE_SIZE / 40);
1962
1963 /* Squares, numbers, gt signs */
1964 for (y = 0; y < o; y++) {
1965 for (x = 0; x < o; x++) {
1966 ox = COORD(x); oy = COORD(y);
1967 n = GRID(state, nums, x, y);
1968
1969 draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink);
1970
1971 str[0] = n ? n2c(n, state->order) : ' ';
1972 str[1] = '\0';
1973 draw_text(dr, ox + TILE_SIZE/2, oy + TILE_SIZE/2,
1974 FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1975 ink, str);
1976
1977 if (state->adjacent)
1978 draw_adjs(dr, ds, ox, oy, GRID(state, flags, x, y), -1, ink);
1979 else
1980 draw_gts(dr, ds, ox, oy, GRID(state, flags, x, y), -1, ink);
1981 }
1982 }
1983}
1984
1985/* ----------------------------------------------------------------------
1986 * Housekeeping.
1987 */
1988
1989#ifdef COMBINED
1990#define thegame unequal
1991#endif
1992
1993const struct game thegame = {
1994 "Unequal", "games.unequal", "unequal",
1995 default_params,
1996 game_fetch_preset, NULL,
1997 decode_params,
1998 encode_params,
1999 free_params,
2000 dup_params,
2001 TRUE, game_configure, custom_params,
2002 validate_params,
2003 new_game_desc,
2004 validate_desc,
2005 new_game,
2006 dup_game,
2007 free_game,
2008 TRUE, solve_game,
2009 TRUE, game_can_format_as_text_now, game_text_format,
2010 new_ui,
2011 free_ui,
2012 encode_ui,
2013 decode_ui,
2014 game_changed_state,
2015 interpret_move,
2016 execute_move,
2017 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2018 game_colours,
2019 game_new_drawstate,
2020 game_free_drawstate,
2021 game_redraw,
2022 game_anim_length,
2023 game_flash_length,
2024 game_status,
2025 TRUE, FALSE, game_print_size, game_print,
2026 FALSE, /* wants_statusbar */
2027 FALSE, game_timing_state,
2028 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2029};
2030
2031/* ----------------------------------------------------------------------
2032 * Standalone solver.
2033 */
2034
2035#ifdef STANDALONE_SOLVER
2036
2037#include <time.h>
2038#include <stdarg.h>
2039
2040const char *quis = NULL;
2041
2042#if 0 /* currently unused */
2043
2044static void debug_printf(char *fmt, ...)
2045{
2046 char buf[4096];
2047 va_list ap;
2048
2049 va_start(ap, fmt);
2050 vsprintf(buf, fmt, ap);
2051 puts(buf);
2052 va_end(ap);
2053}
2054
2055static void game_printf(game_state *state)
2056{
2057 char *dbg = game_text_format(state);
2058 printf("%s", dbg);
2059 sfree(dbg);
2060}
2061
2062static void game_printf_wide(game_state *state)
2063{
2064 int x, y, i, n;
2065
2066 for (y = 0; y < state->order; y++) {
2067 for (x = 0; x < state->order; x++) {
2068 n = GRID(state, nums, x, y);
2069 for (i = 0; i < state->order; i++) {
2070 if (n > 0)
2071 printf("%c", n2c(n, state->order));
2072 else if (HINT(state, x, y, i))
2073 printf("%c", n2c(i+1, state->order));
2074 else
2075 printf(".");
2076 }
2077 printf(" ");
2078 }
2079 printf("\n");
2080 }
2081 printf("\n");
2082}
2083
2084#endif
2085
2086static void pdiff(int diff)
2087{
2088 if (diff == DIFF_IMPOSSIBLE)
2089 printf("Game is impossible.\n");
2090 else if (diff == DIFF_UNFINISHED)
2091 printf("Game has incomplete.\n");
2092 else if (diff == DIFF_AMBIGUOUS)
2093 printf("Game has multiple solutions.\n");
2094 else
2095 printf("Game has difficulty %s.\n", unequal_diffnames[diff]);
2096}
2097
2098static int solve(game_params *p, char *desc, int debug)
2099{
2100 game_state *state = new_game(NULL, p, desc);
2101 struct solver_ctx *ctx = new_ctx(state);
2102 struct latin_solver solver;
2103 int diff;
2104
2105 solver_show_working = debug;
2106 game_debug(state);
2107
2108 latin_solver_alloc(&solver, state->nums, state->order);
2109
2110 diff = latin_solver_main(&solver, DIFF_RECURSIVE,
2111 DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
2112 DIFF_EXTREME, DIFF_RECURSIVE,
2113 unequal_solvers, ctx, clone_ctx, free_ctx);
2114
2115 free_ctx(ctx);
2116
2117 latin_solver_free(&solver);
2118
2119 if (debug) pdiff(diff);
2120
2121 game_debug(state);
2122 free_game(state);
2123 return diff;
2124}
2125
2126static void check(game_params *p)
2127{
2128 char *msg = validate_params(p, 1);
2129 if (msg) {
2130 fprintf(stderr, "%s: %s", quis, msg);
2131 exit(1);
2132 }
2133}
2134
2135static int gen(game_params *p, random_state *rs, int debug)
2136{
2137 char *desc, *aux;
2138 int diff;
2139
2140 check(p);
2141
2142 solver_show_working = debug;
2143 desc = new_game_desc(p, rs, &aux, 0);
2144 diff = solve(p, desc, debug);
2145 sfree(aux);
2146 sfree(desc);
2147
2148 return diff;
2149}
2150
2151static void soak(game_params *p, random_state *rs)
2152{
2153 time_t tt_start, tt_now, tt_last;
2154 char *aux, *desc;
2155 game_state *st;
2156 int n = 0, neasy = 0, realdiff = p->diff;
2157
2158 check(p);
2159
2160 solver_show_working = 0;
2161 maxtries = 1;
2162
2163 tt_start = tt_now = time(NULL);
2164
2165 printf("Soak-generating an %s %dx%d grid, difficulty %s.\n",
2166 p->adjacent ? "adjacent" : "unequal",
2167 p->order, p->order, unequal_diffnames[p->diff]);
2168
2169 while (1) {
2170 p->diff = realdiff;
2171 desc = new_game_desc(p, rs, &aux, 0);
2172 st = new_game(NULL, p, desc);
2173 solver_state(st, DIFF_RECURSIVE);
2174 free_game(st);
2175 sfree(aux);
2176 sfree(desc);
2177
2178 n++;
2179 if (realdiff != p->diff) neasy++;
2180
2181 tt_last = time(NULL);
2182 if (tt_last > tt_now) {
2183 tt_now = tt_last;
2184 printf("%d total, %3.1f/s; %d/%2.1f%% easy, %3.1f/s good.\n",
2185 n, (double)n / ((double)tt_now - tt_start),
2186 neasy, (double)neasy*100.0/(double)n,
2187 (double)(n - neasy) / ((double)tt_now - tt_start));
2188 }
2189 }
2190}
2191
2192static void usage_exit(const char *msg)
2193{
2194 if (msg)
2195 fprintf(stderr, "%s: %s\n", quis, msg);
2196 fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
2197 exit(1);
2198}
2199
2200int main(int argc, const char *argv[])
2201{
2202 random_state *rs;
2203 time_t seed = time(NULL);
2204 int do_soak = 0, diff;
2205
2206 game_params *p;
2207
2208 maxtries = 50;
2209
2210 quis = argv[0];
2211 while (--argc > 0) {
2212 const char *p = *++argv;
2213 if (!strcmp(p, "--soak"))
2214 do_soak = 1;
2215 else if (!strcmp(p, "--seed")) {
2216 if (argc == 0)
2217 usage_exit("--seed needs an argument");
2218 seed = (time_t)atoi(*++argv);
2219 argc--;
2220 } else if (*p == '-')
2221 usage_exit("unrecognised option");
2222 else
2223 break;
2224 }
2225 rs = random_new((void*)&seed, sizeof(time_t));
2226
2227 if (do_soak == 1) {
2228 if (argc != 1) usage_exit("only one argument for --soak");
2229 p = default_params();
2230 decode_params(p, *argv);
2231 soak(p, rs);
2232 } else if (argc > 0) {
2233 int i;
2234 for (i = 0; i < argc; i++) {
2235 const char *id = *argv++;
2236 char *desc = strchr(id, ':'), *err;
2237 p = default_params();
2238 if (desc) {
2239 *desc++ = '\0';
2240 decode_params(p, id);
2241 err = validate_desc(p, desc);
2242 if (err) {
2243 fprintf(stderr, "%s: %s\n", quis, err);
2244 exit(1);
2245 }
2246 solve(p, desc, 1);
2247 } else {
2248 decode_params(p, id);
2249 diff = gen(p, rs, 1);
2250 }
2251 }
2252 } else {
2253 while(1) {
2254 p = default_params();
2255 p->order = random_upto(rs, 7) + 3;
2256 p->diff = random_upto(rs, 4);
2257 diff = gen(p, rs, 0);
2258 pdiff(diff);
2259 }
2260 }
2261
2262 return 0;
2263}
2264
2265#endif
2266
2267/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/unequal.html b/apps/plugins/puzzles/src/unequal.html
new file mode 100644
index 0000000000..8337695c17
--- /dev/null
+++ b/apps/plugins/puzzles/src/unequal.html
@@ -0,0 +1,103 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Unequal</title>
7<link rel="previous" href="bridges.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="galaxies.html">
12</head>
13<body>
14<p><a href="bridges.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="galaxies.html">Next</a></p>
15<h1><a name="C27"></a>Chapter 27: <a name="i0"></a>Unequal</h1>
16<p>
17You have a square grid; each square may contain a digit from 1 to the size of the grid, and some squares have clue signs between them. Your aim is to fully populate the grid with numbers such that:
18</p>
19<ul><li>
20Each row contains only one occurrence of each digit
21</li>
22<li>
23Each column contains only one occurrence of each digit
24</li>
25<li>
26All the clue signs are satisfied.
27</li>
28</ul>
29<p>
30There are two modes for this game, &#8216;Unequal&#8217; and &#8216;Adjacent&#8217;.
31</p>
32<p>
33In &#8216;Unequal&#8217; mode, the clue signs are greater-than symbols indicating one square's value is greater than its neighbour's. In this mode not all clues may be visible, particularly at higher difficulty levels.
34</p>
35<p>
36In &#8216;Adjacent&#8217; mode, the clue signs are bars indicating one square's value is numerically adjacent (i.e. one higher or one lower) than its neighbour. In this mode all clues are always visible: absence of a bar thus means that a square's value is definitely not numerically adjacent to that neighbour's.
37</p>
38<p>
39In &#8216;Trivial&#8217; difficulty level (available via the &#8216;Custom&#8217; game type selector), there are no greater-than signs in &#8216;Unequal&#8217; mode; the puzzle is to solve the <a name="i1"></a>Latin square only.
40</p>
41<p>
42At the time of writing, the &#8216;Unequal&#8217; mode of this puzzle is appearing in the Guardian weekly under the name &#8216;<a name="i2"></a>Futoshiki&#8217;.
43</p>
44<p>
45Unequal was contributed to this collection by James Harvey.
46</p>
47<h2><a name="S27.1"></a>27.1 <a name="i3"></a>Unequal controls</h2>
48<p>
49Unequal shares much of its control system with Solo.
50</p>
51<p>
52To play Unequal, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).
53</p>
54<p>
55If you <em>right</em>-click in a square and then type a number, that number will be entered in the square as a &#8216;pencil mark&#8217;. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks.
56</p>
57<p>
58The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.
59</p>
60<p>
61To erase a single pencil mark, right-click in the square and type the same number again.
62</p>
63<p>
64All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.
65</p>
66<p>
67As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. You can also use the &#8216;M&#8217; key to auto-fill every numeric hint, ready for removal as required, or the &#8216;H&#8217; key to do the same but also to remove all obvious hints.
68</p>
69<p>
70Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square.
71</p>
72<p>
73Left-clicking a clue will mark it as done (grey it out), or unmark it if it is already marked. Holding Control or Shift and pressing an arrow key likewise marks any clue adjacent to the cursor in the given direction.
74</p>
75<p>
76(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
77</p>
78<h2><a name="S27.2"></a>27.2 <a name="i4"></a>Unequal parameters</h2>
79<p>
80These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
81</p>
82<dl><dt>
83<em>Mode</em>
84</dt>
85<dd>
86Mode of the puzzle (&#8216;Unequal&#8217; or &#8216;Adjacent&#8217;)
87</dd>
88<dt>
89<em>Size (s*s)</em>
90</dt>
91<dd>
92Size of grid.
93</dd>
94<dt>
95<em>Difficulty</em>
96</dt>
97<dd>
98Controls the difficulty of the generated puzzle. At Trivial level, there are no greater-than signs; the puzzle is to solve the Latin square only. At Recursive level (only available via the &#8216;Custom&#8217; game type selector) backtracking will be required, but the solution should still be unique. The levels in between require increasingly complex reasoning to avoid having to backtrack.
99</dd>
100</dl>
101
102<hr><address></address></body>
103</html>
diff --git a/apps/plugins/puzzles/src/unfinished/README b/apps/plugins/puzzles/src/unfinished/README
new file mode 100644
index 0000000000..0f8bb41d7c
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/README
@@ -0,0 +1,9 @@
1This subdirectory contains puzzle implementations which are
2half-written, fundamentally flawed, or in other ways unready to be
3shipped as part of the polished Puzzles collection.
4
5Those puzzles which have .R files can be built as part of the
6Puzzles collection by symlinking their source files into the parent
7directory and re-running mkfiles.pl. Anything without a .R file
8isn't even finished enough to do that, and you should read the
9source file itself to find out the status.
diff --git a/apps/plugins/puzzles/src/unfinished/group.R b/apps/plugins/puzzles/src/unfinished/group.R
new file mode 100644
index 0000000000..a11d22e9b9
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.R
@@ -0,0 +1,25 @@
1# -*- makefile -*-
2
3GROUP_LATIN_EXTRA = tree234 maxflow
4GROUP_EXTRA = latin GROUP_LATIN_EXTRA
5
6group : [X] GTK COMMON group GROUP_EXTRA group-icon|no-icon
7
8group : [G] WINDOWS COMMON group GROUP_EXTRA group.res|noicon.res
9
10groupsolver : [U] group[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] GROUP_LATIN_EXTRA STANDALONE
11groupsolver : [C] group[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] GROUP_LATIN_EXTRA STANDALONE
12
13ALL += group[COMBINED] GROUP_EXTRA
14
15!begin am gtk
16GAMES += group
17!end
18
19!begin >list.c
20 A(group) \
21!end
22
23!begin >gamedesc.txt
24group:group.exe:Group:Group theory puzzle:Complete the unfinished Cayley table of a group.
25!end
diff --git a/apps/plugins/puzzles/src/unfinished/group.c b/apps/plugins/puzzles/src/unfinished/group.c
new file mode 100644
index 0000000000..4a4ad6ce53
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.c
@@ -0,0 +1,2198 @@
1/*
2 * group.c: a Latin-square puzzle, but played with groups' Cayley
3 * tables. That is, you are given a Cayley table of a group with
4 * most elements blank and a few clues, and you must fill it in
5 * so as to preserve the group axioms.
6 *
7 * This is a perfectly playable and fully working puzzle, but I'm
8 * leaving it for the moment in the 'unfinished' directory because
9 * it's just too esoteric (not to mention _hard_) for me to be
10 * comfortable presenting it to the general public as something they
11 * might (implicitly) actually want to play.
12 *
13 * TODO:
14 *
15 * - more solver techniques?
16 * * Inverses: once we know that gh = e, we can immediately
17 * deduce hg = e as well; then for any gx=y we can deduce
18 * hy=x, and for any xg=y we have yh=x.
19 * * Hard-mode associativity: we currently deduce based on
20 * definite numbers in the grid, but we could also winnow
21 * based on _possible_ numbers.
22 * * My overambitious original thoughts included wondering if we
23 * could infer that there must be elements of certain orders
24 * (e.g. a group of order divisible by 5 must contain an
25 * element of order 5), but I think in fact this is probably
26 * silly.
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <assert.h>
33#include <ctype.h>
34#include <math.h>
35
36#include "puzzles.h"
37#include "latin.h"
38
39/*
40 * Difficulty levels. I do some macro ickery here to ensure that my
41 * enum and the various forms of my name list always match up.
42 */
43#define DIFFLIST(A) \
44 A(TRIVIAL,Trivial,NULL,t) \
45 A(NORMAL,Normal,solver_normal,n) \
46 A(HARD,Hard,NULL,h) \
47 A(EXTREME,Extreme,NULL,x) \
48 A(UNREASONABLE,Unreasonable,NULL,u)
49#define ENUM(upper,title,func,lower) DIFF_ ## upper,
50#define TITLE(upper,title,func,lower) #title,
51#define ENCODE(upper,title,func,lower) #lower
52#define CONFIG(upper,title,func,lower) ":" #title
53enum { DIFFLIST(ENUM) DIFFCOUNT };
54static char const *const group_diffnames[] = { DIFFLIST(TITLE) };
55static char const group_diffchars[] = DIFFLIST(ENCODE);
56#define DIFFCONFIG DIFFLIST(CONFIG)
57
58enum {
59 COL_BACKGROUND,
60 COL_GRID,
61 COL_USER,
62 COL_HIGHLIGHT,
63 COL_ERROR,
64 COL_PENCIL,
65 COL_DIAGONAL,
66 NCOLOURS
67};
68
69/*
70 * In identity mode, we number the elements e,a,b,c,d,f,g,h,...
71 * Otherwise, they're a,b,c,d,e,f,g,h,... in the obvious way.
72 */
73#define E_TO_FRONT(c,id) ( (id) && (c)<=5 ? (c) % 5 + 1 : (c) )
74#define E_FROM_FRONT(c,id) ( (id) && (c)<=5 ? ((c) + 3) % 5 + 1 : (c) )
75
76#define FROMCHAR(c,id) E_TO_FRONT((((c)-('A'-1)) & ~0x20), id)
77#define ISCHAR(c) (((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z'))
78#define TOCHAR(c,id) (E_FROM_FRONT(c,id) + ('a'-1))
79
80struct game_params {
81 int w, diff, id;
82};
83
84struct game_state {
85 game_params par;
86 digit *grid;
87 unsigned char *immutable;
88 int *pencil; /* bitmaps using bits 1<<1..1<<n */
89 int completed, cheated;
90 digit *sequence; /* sequence of group elements shown */
91
92 /*
93 * This array indicates thick lines separating rows and columns
94 * placed and unplaced manually by the user as a visual aid, e.g.
95 * to delineate a subgroup and its cosets.
96 *
97 * When a line is placed, it's deemed to be between the two
98 * particular group elements that are on either side of it at the
99 * time; dragging those two away from each other automatically
100 * gets rid of the line. Hence, for a given element i, dividers[i]
101 * is either -1 (indicating no divider to the right of i), or some
102 * other element (indicating a divider to the right of i iff that
103 * element is the one right of it). These are eagerly cleared
104 * during drags.
105 */
106 int *dividers; /* thick lines between rows/cols */
107};
108
109static game_params *default_params(void)
110{
111 game_params *ret = snew(game_params);
112
113 ret->w = 6;
114 ret->diff = DIFF_NORMAL;
115 ret->id = TRUE;
116
117 return ret;
118}
119
120const static struct game_params group_presets[] = {
121 { 6, DIFF_NORMAL, TRUE },
122 { 6, DIFF_NORMAL, FALSE },
123 { 8, DIFF_NORMAL, TRUE },
124 { 8, DIFF_NORMAL, FALSE },
125 { 8, DIFF_HARD, TRUE },
126 { 8, DIFF_HARD, FALSE },
127 { 12, DIFF_NORMAL, TRUE },
128};
129
130static int game_fetch_preset(int i, char **name, game_params **params)
131{
132 game_params *ret;
133 char buf[80];
134
135 if (i < 0 || i >= lenof(group_presets))
136 return FALSE;
137
138 ret = snew(game_params);
139 *ret = group_presets[i]; /* structure copy */
140
141 sprintf(buf, "%dx%d %s%s", ret->w, ret->w, group_diffnames[ret->diff],
142 ret->id ? "" : ", identity hidden");
143
144 *name = dupstr(buf);
145 *params = ret;
146 return TRUE;
147}
148
149static void free_params(game_params *params)
150{
151 sfree(params);
152}
153
154static game_params *dup_params(const game_params *params)
155{
156 game_params *ret = snew(game_params);
157 *ret = *params; /* structure copy */
158 return ret;
159}
160
161static void decode_params(game_params *params, char const *string)
162{
163 char const *p = string;
164
165 params->w = atoi(p);
166 while (*p && isdigit((unsigned char)*p)) p++;
167 params->diff = DIFF_NORMAL;
168 params->id = TRUE;
169
170 while (*p) {
171 if (*p == 'd') {
172 int i;
173 p++;
174 params->diff = DIFFCOUNT+1; /* ...which is invalid */
175 if (*p) {
176 for (i = 0; i < DIFFCOUNT; i++) {
177 if (*p == group_diffchars[i])
178 params->diff = i;
179 }
180 p++;
181 }
182 } else if (*p == 'i') {
183 params->id = FALSE;
184 p++;
185 } else {
186 /* unrecognised character */
187 p++;
188 }
189 }
190}
191
192static char *encode_params(const game_params *params, int full)
193{
194 char ret[80];
195
196 sprintf(ret, "%d", params->w);
197 if (full)
198 sprintf(ret + strlen(ret), "d%c", group_diffchars[params->diff]);
199 if (!params->id)
200 sprintf(ret + strlen(ret), "i");
201
202 return dupstr(ret);
203}
204
205static config_item *game_configure(const game_params *params)
206{
207 config_item *ret;
208 char buf[80];
209
210 ret = snewn(4, config_item);
211
212 ret[0].name = "Grid size";
213 ret[0].type = C_STRING;
214 sprintf(buf, "%d", params->w);
215 ret[0].sval = dupstr(buf);
216 ret[0].ival = 0;
217
218 ret[1].name = "Difficulty";
219 ret[1].type = C_CHOICES;
220 ret[1].sval = DIFFCONFIG;
221 ret[1].ival = params->diff;
222
223 ret[2].name = "Show identity";
224 ret[2].type = C_BOOLEAN;
225 ret[2].sval = NULL;
226 ret[2].ival = params->id;
227
228 ret[3].name = NULL;
229 ret[3].type = C_END;
230 ret[3].sval = NULL;
231 ret[3].ival = 0;
232
233 return ret;
234}
235
236static game_params *custom_params(const config_item *cfg)
237{
238 game_params *ret = snew(game_params);
239
240 ret->w = atoi(cfg[0].sval);
241 ret->diff = cfg[1].ival;
242 ret->id = cfg[2].ival;
243
244 return ret;
245}
246
247static char *validate_params(const game_params *params, int full)
248{
249 if (params->w < 3 || params->w > 26)
250 return "Grid size must be between 3 and 26";
251 if (params->diff >= DIFFCOUNT)
252 return "Unknown difficulty rating";
253 if (!params->id && params->diff == DIFF_TRIVIAL) {
254 /*
255 * We can't have a Trivial-difficulty puzzle (i.e. latin
256 * square deductions only) without a clear identity, because
257 * identityless puzzles always have two rows and two columns
258 * entirely blank, and no latin-square deduction permits the
259 * distinguishing of two such rows.
260 */
261 return "Trivial puzzles must have an identity";
262 }
263 if (!params->id && params->w == 3) {
264 /*
265 * We can't have a 3x3 puzzle without an identity either,
266 * because 3x3 puzzles can't ever be harder than Trivial
267 * (there are no 3x3 latin squares which aren't also valid
268 * group tables, so enabling group-based deductions doesn't
269 * rule out any possible solutions) and - as above - Trivial
270 * puzzles can't not have an identity.
271 */
272 return "3x3 puzzles must have an identity";
273 }
274 return NULL;
275}
276
277/* ----------------------------------------------------------------------
278 * Solver.
279 */
280
281static int solver_normal(struct latin_solver *solver, void *vctx)
282{
283 int w = solver->o;
284#ifdef STANDALONE_SOLVER
285 char **names = solver->names;
286#endif
287 digit *grid = solver->grid;
288 int i, j, k;
289
290 /*
291 * Deduce using associativity: (ab)c = a(bc).
292 *
293 * So we pick any a,b,c we like; then if we know ab, bc, and
294 * (ab)c we can fill in a(bc).
295 */
296 for (i = 1; i < w; i++)
297 for (j = 1; j < w; j++)
298 for (k = 1; k < w; k++) {
299 if (!grid[i*w+j] || !grid[j*w+k])
300 continue;
301 if (grid[(grid[i*w+j]-1)*w+k] &&
302 !grid[i*w+(grid[j*w+k]-1)]) {
303 int x = grid[j*w+k]-1, y = i;
304 int n = grid[(grid[i*w+j]-1)*w+k];
305#ifdef STANDALONE_SOLVER
306 if (solver_show_working) {
307 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
308 solver_recurse_depth*4, "",
309 names[i], names[j], names[k],
310 names[grid[i*w+j]-1], names[k],
311 names[i], names[grid[j*w+k]-1]);
312 printf("%*s placing %s at (%d,%d)\n",
313 solver_recurse_depth*4, "",
314 names[n-1], x+1, y+1);
315 }
316#endif
317 if (solver->cube[(x*w+y)*w+n-1]) {
318 latin_solver_place(solver, x, y, n);
319 return 1;
320 } else {
321#ifdef STANDALONE_SOLVER
322 if (solver_show_working)
323 printf("%*s contradiction!\n",
324 solver_recurse_depth*4, "");
325 return -1;
326#endif
327 }
328 }
329 if (!grid[(grid[i*w+j]-1)*w+k] &&
330 grid[i*w+(grid[j*w+k]-1)]) {
331 int x = k, y = grid[i*w+j]-1;
332 int n = grid[i*w+(grid[j*w+k]-1)];
333#ifdef STANDALONE_SOLVER
334 if (solver_show_working) {
335 printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n",
336 solver_recurse_depth*4, "",
337 names[i], names[j], names[k],
338 names[grid[i*w+j]-1], names[k],
339 names[i], names[grid[j*w+k]-1]);
340 printf("%*s placing %s at (%d,%d)\n",
341 solver_recurse_depth*4, "",
342 names[n-1], x+1, y+1);
343 }
344#endif
345 if (solver->cube[(x*w+y)*w+n-1]) {
346 latin_solver_place(solver, x, y, n);
347 return 1;
348 } else {
349#ifdef STANDALONE_SOLVER
350 if (solver_show_working)
351 printf("%*s contradiction!\n",
352 solver_recurse_depth*4, "");
353 return -1;
354#endif
355 }
356 }
357 }
358
359 return 0;
360}
361
362#define SOLVER(upper,title,func,lower) func,
363static usersolver_t const group_solvers[] = { DIFFLIST(SOLVER) };
364
365static int solver(const game_params *params, digit *grid, int maxdiff)
366{
367 int w = params->w;
368 int ret;
369 struct latin_solver solver;
370#ifdef STANDALONE_SOLVER
371 char *p, text[100], *names[50];
372 int i;
373#endif
374
375 latin_solver_alloc(&solver, grid, w);
376#ifdef STANDALONE_SOLVER
377 for (i = 0, p = text; i < w; i++) {
378 names[i] = p;
379 *p++ = TOCHAR(i+1, params->id);
380 *p++ = '\0';
381 }
382 solver.names = names;
383#endif
384
385 ret = latin_solver_main(&solver, maxdiff,
386 DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME,
387 DIFF_EXTREME, DIFF_UNREASONABLE,
388 group_solvers, NULL, NULL, NULL);
389
390 latin_solver_free(&solver);
391
392 return ret;
393}
394
395/* ----------------------------------------------------------------------
396 * Grid generation.
397 */
398
399static char *encode_grid(char *desc, digit *grid, int area)
400{
401 int run, i;
402 char *p = desc;
403
404 run = 0;
405 for (i = 0; i <= area; i++) {
406 int n = (i < area ? grid[i] : -1);
407
408 if (!n)
409 run++;
410 else {
411 if (run) {
412 while (run > 0) {
413 int c = 'a' - 1 + run;
414 if (run > 26)
415 c = 'z';
416 *p++ = c;
417 run -= c - ('a' - 1);
418 }
419 } else {
420 /*
421 * If there's a number in the very top left or
422 * bottom right, there's no point putting an
423 * unnecessary _ before or after it.
424 */
425 if (p > desc && n > 0)
426 *p++ = '_';
427 }
428 if (n > 0)
429 p += sprintf(p, "%d", n);
430 run = 0;
431 }
432 }
433 return p;
434}
435
436/* ----- data generated by group.gap begins ----- */
437
438struct group {
439 unsigned long autosize;
440 int order, ngens;
441 const char *gens;
442};
443struct groups {
444 int ngroups;
445 const struct group *groups;
446};
447
448static const struct group groupdata[] = {
449 /* order 2 */
450 {1L, 2, 1, "BA"},
451 /* order 3 */
452 {2L, 3, 1, "BCA"},
453 /* order 4 */
454 {2L, 4, 1, "BCDA"},
455 {6L, 4, 2, "BADC" "CDAB"},
456 /* order 5 */
457 {4L, 5, 1, "BCDEA"},
458 /* order 6 */
459 {6L, 6, 2, "CFEBAD" "BADCFE"},
460 {2L, 6, 1, "DCFEBA"},
461 /* order 7 */
462 {6L, 7, 1, "BCDEFGA"},
463 /* order 8 */
464 {4L, 8, 1, "BCEFDGHA"},
465 {8L, 8, 2, "BDEFGAHC" "EGBHDCFA"},
466 {8L, 8, 2, "EGBHDCFA" "BAEFCDHG"},
467 {24L, 8, 2, "BDEFGAHC" "CHDGBEAF"},
468 {168L, 8, 3, "BAEFCDHG" "CEAGBHDF" "DFGAHBCE"},
469 /* order 9 */
470 {6L, 9, 1, "BDECGHFIA"},
471 {48L, 9, 2, "BDEAGHCIF" "CEFGHAIBD"},
472 /* order 10 */
473 {20L, 10, 2, "CJEBGDIFAH" "BADCFEHGJI"},
474 {4L, 10, 1, "DCFEHGJIBA"},
475 /* order 11 */
476 {10L, 11, 1, "BCDEFGHIJKA"},
477 /* order 12 */
478 {12L, 12, 2, "GLDKJEHCBIAF" "BCEFAGIJDKLH"},
479 {4L, 12, 1, "EHIJKCBLDGFA"},
480 {24L, 12, 2, "BEFGAIJKCDLH" "FJBKHLEGDCIA"},
481 {12L, 12, 2, "GLDKJEHCBIAF" "BAEFCDIJGHLK"},
482 {12L, 12, 2, "FDIJGHLBKAEC" "GIDKFLHCJEAB"},
483 /* order 13 */
484 {12L, 13, 1, "BCDEFGHIJKLMA"},
485 /* order 14 */
486 {42L, 14, 2, "ELGNIBKDMFAHCJ" "BADCFEHGJILKNM"},
487 {6L, 14, 1, "FEHGJILKNMBADC"},
488 /* order 15 */
489 {8L, 15, 1, "EGHCJKFMNIOBLDA"},
490 /* order 16 */
491 {8L, 16, 1, "MKNPFOADBGLCIEHJ"},
492 {96L, 16, 2, "ILKCONFPEDJHGMAB" "BDFGHIAKLMNCOEPJ"},
493 {32L, 16, 2, "MIHPFDCONBLAKJGE" "BEFGHJKALMNOCDPI"},
494 {32L, 16, 2, "IFACOGLMDEJBNPKH" "BEFGHJKALMNOCDPI"},
495 {16L, 16, 2, "MOHPFKCINBLADJGE" "BDFGHIEKLMNJOAPC"},
496 {16L, 16, 2, "MIHPFDJONBLEKCGA" "BDFGHIEKLMNJOAPC"},
497 {32L, 16, 2, "MOHPFDCINBLEKJGA" "BAFGHCDELMNIJKPO"},
498 {16L, 16, 2, "MIHPFKJONBLADCGE" "GDPHNOEKFLBCIAMJ"},
499 {32L, 16, 2, "MIBPFDJOGHLEKCNA" "CLEIJGMPKAOHNFDB"},
500 {192L, 16, 3,
501 "MCHPFAIJNBLDEOGK" "BEFGHJKALMNOCDPI" "GKLBNOEDFPHJIAMC"},
502 {64L, 16, 3, "MCHPFAIJNBLDEOGK" "LOGFPKJIBNMEDCHA" "CMAIJHPFDEONBLKG"},
503 {192L, 16, 3,
504 "IPKCOGMLEDJBNFAH" "BEFGHJKALMNOCDPI" "CMEIJBPFKAOGHLDN"},
505 {48L, 16, 3, "IPDJONFLEKCBGMAH" "FJBLMEOCGHPKAIND" "DGIEKLHNJOAMPBCF"},
506 {20160L, 16, 4,
507 "EHJKAMNBOCDPFGIL" "BAFGHCDELMNIJKPO" "CFAIJBLMDEOGHPKN"
508 "DGIAKLBNCOEFPHJM"},
509 /* order 17 */
510 {16L, 17, 1, "EFGHIJKLMNOPQABCD"},
511 /* order 18 */
512 {54L, 18, 2, "MKIQOPNAGLRECDBJHF" "BAEFCDJKLGHIOPMNRQ"},
513 {6L, 18, 1, "ECJKGHFOPDMNLRIQBA"},
514 {12L, 18, 2, "ECJKGHBOPAMNFRDQLI" "KNOPQCFREIGHLJAMBD"},
515 {432L, 18, 3,
516 "IFNAKLQCDOPBGHREMJ" "NOQCFRIGHKLJAMPBDE" "BAEFCDJKLGHIOPMNRQ"},
517 {48L, 18, 2, "ECJKGHBOPAMNFRDQLI" "FDKLHIOPBMNAREQCJG"},
518 /* order 19 */
519 {18L, 19, 1, "EFGHIJKLMNOPQRSABCD"},
520 /* order 20 */
521 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "EABICDFMGHJQKLNTOPRS"},
522 {8L, 20, 1, "EHIJLCMNPGQRSKBTDOFA"},
523 {20L, 20, 2, "DJSHQNCLTRGPEBKAIFOM" "EABICDFMGHJQKLNTOPRS"},
524 {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "ECBIAGFMDKJQHONTLSRP"},
525 {24L, 20, 2, "IGFMDKJQHONTLSREPCBA" "FDIJGHMNKLQROPTBSAEC"},
526 /* order 21 */
527 {42L, 21, 2, "ITLSBOUERDHAGKCJNFMQP" "EJHLMKOPNRSQAUTCDBFGI"},
528 {12L, 21, 1, "EGHCJKFMNIPQLSTOUBRDA"},
529 /* order 22 */
530 {110L, 22, 2, "ETGVIBKDMFOHQJSLUNAPCR" "BADCFEHGJILKNMPORQTSVU"},
531 {10L, 22, 1, "FEHGJILKNMPORQTSVUBADC"},
532 /* order 23 */
533 {22L, 23, 1, "EFGHIJKLMNOPQRSTUVWABCD"},
534 /* order 24 */
535 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "HRNOPSWCTUVBLDIJXFGAKQME"},
536 {8L, 24, 1, "MQBTUDRWFGHXJELINOPKSAVC"},
537 {24L, 24, 2, "IOQRBEUVFWGHKLAXMNPSCDTJ" "NJXOVGDKSMTFIPQELCURBWAH"},
538 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "HSNOPWLDTUVBRIAKXFGCQEMJ"},
539 {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "TWHNXLRIOPUMSACQVBFDEJGK"},
540 {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "BAFGHCDEMNOPIJKLTUVQRSXW"},
541 {48L, 24, 3,
542 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
543 "HSNOPWLDTUVBRIAKXFGCQEMJ"},
544 {24L, 24, 3,
545 "QUKJWPXFESRIVBMNLDCGHTAO" "JXEQRVUMKLWCPGFTSAIBONDH"
546 "TRONXLWCHVUMSAIJPGFDEQBK"},
547 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "VKXHOQASNTPBCWDEUFGIJLMR"},
548 {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "RMLWIGTUSDJQOPFXEKCBVNAH"},
549 {48L, 24, 2, "IULQRGXMSDCWOPNTEKJBVFAH" "GLMOPRSDTUBVWIEKFXHJQANC"},
550 {24L, 24, 2, "UJPXMRCSNHGTLWIKFVBEDQOA" "NRUFVLWIPXMOJEDQHGTCSABK"},
551 {24L, 24, 2, "MIBTUAQRFGHXCDEWNOPJKLVS" "OKXVFWSCGUTNDRQJBPMALIHE"},
552 {144L, 24, 3,
553 "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO"
554 "BAFGHCDEMNOPIJKLTUVQRSXW"},
555 {336L, 24, 3,
556 "QTKJWONXESRIHVUMLDCPGFAB" "JNEQRHTUKLWCOPXFSAIVBMDG"
557 "HENOPJKLTUVBQRSAXFGWCDMI"},
558 /* order 25 */
559 {20L, 25, 1, "EHILMNPQRSFTUVBJWXDOYGAKC"},
560 {480L, 25, 2, "EHILMNPQRSCTUVBFWXDJYGOKA" "BDEGHIKLMNAPQRSCTUVFWXJYO"},
561 /* order 26 */
562 {156L, 26, 2,
563 "EXGZIBKDMFOHQJSLUNWPYRATCV" "BADCFEHGJILKNMPORQTSVUXWZY"},
564 {12L, 26, 1, "FEHGJILKNMPORQTSVUXWZYBADC"},
565};
566
567static const struct groups groups[] = {
568 {0, NULL}, /* trivial case: 0 */
569 {0, NULL}, /* trivial case: 1 */
570 {1, groupdata + 0}, /* 2 */
571 {1, groupdata + 1}, /* 3 */
572 {2, groupdata + 2}, /* 4 */
573 {1, groupdata + 4}, /* 5 */
574 {2, groupdata + 5}, /* 6 */
575 {1, groupdata + 7}, /* 7 */
576 {5, groupdata + 8}, /* 8 */
577 {2, groupdata + 13}, /* 9 */
578 {2, groupdata + 15}, /* 10 */
579 {1, groupdata + 17}, /* 11 */
580 {5, groupdata + 18}, /* 12 */
581 {1, groupdata + 23}, /* 13 */
582 {2, groupdata + 24}, /* 14 */
583 {1, groupdata + 26}, /* 15 */
584 {14, groupdata + 27}, /* 16 */
585 {1, groupdata + 41}, /* 17 */
586 {5, groupdata + 42}, /* 18 */
587 {1, groupdata + 47}, /* 19 */
588 {5, groupdata + 48}, /* 20 */
589 {2, groupdata + 53}, /* 21 */
590 {2, groupdata + 55}, /* 22 */
591 {1, groupdata + 57}, /* 23 */
592 {15, groupdata + 58}, /* 24 */
593 {2, groupdata + 73}, /* 25 */
594 {2, groupdata + 75}, /* 26 */
595};
596
597/* ----- data generated by group.gap ends ----- */
598
599static char *new_game_desc(const game_params *params, random_state *rs,
600 char **aux, int interactive)
601{
602 int w = params->w, a = w*w;
603 digit *grid, *soln, *soln2;
604 int *indices;
605 int i, j, k, qh, qt;
606 int diff = params->diff;
607 const struct group *group;
608 char *desc, *p;
609
610 /*
611 * Difficulty exceptions: some combinations of size and
612 * difficulty cannot be satisfied, because all puzzles of at
613 * most that difficulty are actually even easier.
614 *
615 * Remember to re-test this whenever a change is made to the
616 * solver logic!
617 *
618 * I tested it using the following shell command:
619
620for d in t n h x u; do
621 for id in '' i; do
622 for i in {3..9}; do
623 echo -n "./group --generate 1 ${i}d${d}${id}: "
624 perl -e 'alarm 30; exec @ARGV' \
625 ./group --generate 1 ${i}d${d}${id} >/dev/null && echo ok
626 done
627 done
628done
629
630 * Of course, it's better to do that after taking the exceptions
631 * _out_, so as to detect exceptions that should be removed as
632 * well as those which should be added.
633 */
634 if (w < 5 && diff == DIFF_UNREASONABLE)
635 diff--;
636 if ((w < 5 || ((w == 6 || w == 8) && params->id)) && diff == DIFF_EXTREME)
637 diff--;
638 if ((w < 6 || (w == 6 && params->id)) && diff == DIFF_HARD)
639 diff--;
640 if ((w < 4 || (w == 4 && params->id)) && diff == DIFF_NORMAL)
641 diff--;
642
643 grid = snewn(a, digit);
644 soln = snewn(a, digit);
645 soln2 = snewn(a, digit);
646 indices = snewn(a, int);
647
648 while (1) {
649 /*
650 * Construct a valid group table, by picking a group from
651 * the above data table, decompressing it into a full
652 * representation by BFS, and then randomly permuting its
653 * non-identity elements.
654 *
655 * We build the canonical table in 'soln' (and use 'grid' as
656 * our BFS queue), then transfer the table into 'grid'
657 * having shuffled the rows.
658 */
659 assert(w >= 2);
660 assert(w < lenof(groups));
661 group = groups[w].groups + random_upto(rs, groups[w].ngroups);
662 assert(group->order == w);
663 memset(soln, 0, a);
664 for (i = 0; i < w; i++)
665 soln[i] = i+1;
666 qh = qt = 0;
667 grid[qt++] = 1;
668 while (qh < qt) {
669 digit *row, *newrow;
670
671 i = grid[qh++];
672 row = soln + (i-1)*w;
673
674 for (j = 0; j < group->ngens; j++) {
675 int nri;
676 const char *gen = group->gens + j*w;
677
678 /*
679 * Apply each group generator to row, constructing a
680 * new row.
681 */
682 nri = gen[row[0]-1] - 'A' + 1; /* which row is it? */
683 newrow = soln + (nri-1)*w;
684 if (!newrow[0]) { /* not done yet */
685 for (k = 0; k < w; k++)
686 newrow[k] = gen[row[k]-1] - 'A' + 1;
687 grid[qt++] = nri;
688 }
689 }
690 }
691 /* That's got the canonical table. Now shuffle it. */
692 for (i = 0; i < w; i++)
693 soln2[i] = i;
694 if (params->id) /* do we shuffle in the identity? */
695 shuffle(soln2+1, w-1, sizeof(*soln2), rs);
696 else
697 shuffle(soln2, w, sizeof(*soln2), rs);
698 for (i = 0; i < w; i++)
699 for (j = 0; j < w; j++)
700 grid[(soln2[i])*w+(soln2[j])] = soln2[soln[i*w+j]-1]+1;
701
702 /*
703 * Remove entries one by one while the puzzle is still
704 * soluble at the appropriate difficulty level.
705 */
706 memcpy(soln, grid, a);
707 if (!params->id) {
708 /*
709 * Start by blanking the entire identity row and column,
710 * and also another row and column so that the player
711 * can't trivially determine which element is the
712 * identity.
713 */
714
715 j = 1 + random_upto(rs, w-1); /* pick a second row/col to blank */
716 for (i = 0; i < w; i++) {
717 grid[(soln2[0])*w+i] = grid[i*w+(soln2[0])] = 0;
718 grid[(soln2[j])*w+i] = grid[i*w+(soln2[j])] = 0;
719 }
720
721 memcpy(soln2, grid, a);
722 if (solver(params, soln2, diff) > diff)
723 continue; /* go round again if that didn't work */
724 }
725
726 k = 0;
727 for (i = (params->id ? 1 : 0); i < w; i++)
728 for (j = (params->id ? 1 : 0); j < w; j++)
729 if (grid[i*w+j])
730 indices[k++] = i*w+j;
731 shuffle(indices, k, sizeof(*indices), rs);
732
733 for (i = 0; i < k; i++) {
734 memcpy(soln2, grid, a);
735 soln2[indices[i]] = 0;
736 if (solver(params, soln2, diff) <= diff)
737 grid[indices[i]] = 0;
738 }
739
740 /*
741 * Make sure the puzzle isn't too easy.
742 */
743 if (diff > 0) {
744 memcpy(soln2, grid, a);
745 if (solver(params, soln2, diff-1) < diff)
746 continue; /* go round and try again */
747 }
748
749 /*
750 * Done.
751 */
752 break;
753 }
754
755 /*
756 * Encode the puzzle description.
757 */
758 desc = snewn(a*20, char);
759 p = encode_grid(desc, grid, a);
760 *p++ = '\0';
761 desc = sresize(desc, p - desc, char);
762
763 /*
764 * Encode the solution.
765 */
766 *aux = snewn(a+2, char);
767 (*aux)[0] = 'S';
768 for (i = 0; i < a; i++)
769 (*aux)[i+1] = TOCHAR(soln[i], params->id);
770 (*aux)[a+1] = '\0';
771
772 sfree(grid);
773 sfree(soln);
774 sfree(soln2);
775 sfree(indices);
776
777 return desc;
778}
779
780/* ----------------------------------------------------------------------
781 * Gameplay.
782 */
783
784static char *validate_grid_desc(const char **pdesc, int range, int area)
785{
786 const char *desc = *pdesc;
787 int squares = 0;
788 while (*desc && *desc != ',') {
789 int n = *desc++;
790 if (n >= 'a' && n <= 'z') {
791 squares += n - 'a' + 1;
792 } else if (n == '_') {
793 /* do nothing */;
794 } else if (n > '0' && n <= '9') {
795 int val = atoi(desc-1);
796 if (val < 1 || val > range)
797 return "Out-of-range number in game description";
798 squares++;
799 while (*desc >= '0' && *desc <= '9')
800 desc++;
801 } else
802 return "Invalid character in game description";
803 }
804
805 if (squares < area)
806 return "Not enough data to fill grid";
807
808 if (squares > area)
809 return "Too much data to fit in grid";
810 *pdesc = desc;
811 return NULL;
812}
813
814static char *validate_desc(const game_params *params, const char *desc)
815{
816 int w = params->w, a = w*w;
817 const char *p = desc;
818
819 return validate_grid_desc(&p, w, a);
820}
821
822static const char *spec_to_grid(const char *desc, digit *grid, int area)
823{
824 int i = 0;
825 while (*desc && *desc != ',') {
826 int n = *desc++;
827 if (n >= 'a' && n <= 'z') {
828 int run = n - 'a' + 1;
829 assert(i + run <= area);
830 while (run-- > 0)
831 grid[i++] = 0;
832 } else if (n == '_') {
833 /* do nothing */;
834 } else if (n > '0' && n <= '9') {
835 assert(i < area);
836 grid[i++] = atoi(desc-1);
837 while (*desc >= '0' && *desc <= '9')
838 desc++;
839 } else {
840 assert(!"We can't get here");
841 }
842 }
843 assert(i == area);
844 return desc;
845}
846
847static game_state *new_game(midend *me, const game_params *params,
848 const char *desc)
849{
850 int w = params->w, a = w*w;
851 game_state *state = snew(game_state);
852 int i;
853
854 state->par = *params; /* structure copy */
855 state->grid = snewn(a, digit);
856 state->immutable = snewn(a, unsigned char);
857 state->pencil = snewn(a, int);
858 for (i = 0; i < a; i++) {
859 state->grid[i] = 0;
860 state->immutable[i] = 0;
861 state->pencil[i] = 0;
862 }
863 state->sequence = snewn(w, digit);
864 state->dividers = snewn(w, int);
865 for (i = 0; i < w; i++) {
866 state->sequence[i] = i;
867 state->dividers[i] = -1;
868 }
869
870 desc = spec_to_grid(desc, state->grid, a);
871 for (i = 0; i < a; i++)
872 if (state->grid[i] != 0)
873 state->immutable[i] = TRUE;
874
875 state->completed = state->cheated = FALSE;
876
877 return state;
878}
879
880static game_state *dup_game(const game_state *state)
881{
882 int w = state->par.w, a = w*w;
883 game_state *ret = snew(game_state);
884
885 ret->par = state->par; /* structure copy */
886
887 ret->grid = snewn(a, digit);
888 ret->immutable = snewn(a, unsigned char);
889 ret->pencil = snewn(a, int);
890 ret->sequence = snewn(w, digit);
891 ret->dividers = snewn(w, int);
892 memcpy(ret->grid, state->grid, a*sizeof(digit));
893 memcpy(ret->immutable, state->immutable, a*sizeof(unsigned char));
894 memcpy(ret->pencil, state->pencil, a*sizeof(int));
895 memcpy(ret->sequence, state->sequence, w*sizeof(digit));
896 memcpy(ret->dividers, state->dividers, w*sizeof(int));
897
898 ret->completed = state->completed;
899 ret->cheated = state->cheated;
900
901 return ret;
902}
903
904static void free_game(game_state *state)
905{
906 sfree(state->grid);
907 sfree(state->immutable);
908 sfree(state->pencil);
909 sfree(state->sequence);
910 sfree(state);
911}
912
913static char *solve_game(const game_state *state, const game_state *currstate,
914 const char *aux, char **error)
915{
916 int w = state->par.w, a = w*w;
917 int i, ret;
918 digit *soln;
919 char *out;
920
921 if (aux)
922 return dupstr(aux);
923
924 soln = snewn(a, digit);
925 memcpy(soln, state->grid, a*sizeof(digit));
926
927 ret = solver(&state->par, soln, DIFFCOUNT-1);
928
929 if (ret == diff_impossible) {
930 *error = "No solution exists for this puzzle";
931 out = NULL;
932 } else if (ret == diff_ambiguous) {
933 *error = "Multiple solutions exist for this puzzle";
934 out = NULL;
935 } else {
936 out = snewn(a+2, char);
937 out[0] = 'S';
938 for (i = 0; i < a; i++)
939 out[i+1] = TOCHAR(soln[i], state->par.id);
940 out[a+1] = '\0';
941 }
942
943 sfree(soln);
944 return out;
945}
946
947static int game_can_format_as_text_now(const game_params *params)
948{
949 return TRUE;
950}
951
952static char *game_text_format(const game_state *state)
953{
954 int w = state->par.w;
955 int x, y;
956 char *ret, *p, ch;
957
958 ret = snewn(2*w*w+1, char); /* leave room for terminating NUL */
959
960 p = ret;
961 for (y = 0; y < w; y++) {
962 for (x = 0; x < w; x++) {
963 digit d = state->grid[y*w+x];
964
965 if (d == 0) {
966 ch = '.';
967 } else {
968 ch = TOCHAR(d, state->par.id);
969 }
970
971 *p++ = ch;
972 if (x == w-1) {
973 *p++ = '\n';
974 } else {
975 *p++ = ' ';
976 }
977 }
978 }
979
980 assert(p - ret == 2*w*w);
981 *p = '\0';
982 return ret;
983}
984
985struct game_ui {
986 /*
987 * These are the coordinates of the primary highlighted square on
988 * the grid, if hshow = 1.
989 */
990 int hx, hy;
991 /*
992 * These are the coordinates hx,hy _before_ they go through
993 * state->sequence.
994 */
995 int ohx, ohy;
996 /*
997 * These variables give the length and displacement of a diagonal
998 * sequence of highlighted squares starting at ohx,ohy (still if
999 * hshow = 1). To find the squares' real coordinates, for 0<=i<dn,
1000 * compute ohx+i*odx and ohy+i*ody and then map through
1001 * state->sequence.
1002 */
1003 int odx, ody, odn;
1004 /*
1005 * This indicates whether the current highlight is a
1006 * pencil-mark one or a real one.
1007 */
1008 int hpencil;
1009 /*
1010 * This indicates whether or not we're showing the highlight
1011 * (used to be hx = hy = -1); important so that when we're
1012 * using the cursor keys it doesn't keep coming back at a
1013 * fixed position. When hshow = 1, pressing a valid number
1014 * or letter key or Space will enter that number or letter in the grid.
1015 */
1016 int hshow;
1017 /*
1018 * This indicates whether we're using the highlight as a cursor;
1019 * it means that it doesn't vanish on a keypress, and that it is
1020 * allowed on immutable squares.
1021 */
1022 int hcursor;
1023 /*
1024 * This indicates whether we're dragging a table header to
1025 * reposition an entire row or column.
1026 */
1027 int drag; /* 0=none 1=row 2=col */
1028 int dragnum; /* element being dragged */
1029 int dragpos; /* its current position */
1030 int edgepos;
1031};
1032
1033static game_ui *new_ui(const game_state *state)
1034{
1035 game_ui *ui = snew(game_ui);
1036
1037 ui->hx = ui->hy = 0;
1038 ui->hpencil = ui->hshow = ui->hcursor = 0;
1039 ui->drag = 0;
1040
1041 return ui;
1042}
1043
1044static void free_ui(game_ui *ui)
1045{
1046 sfree(ui);
1047}
1048
1049static char *encode_ui(const game_ui *ui)
1050{
1051 return NULL;
1052}
1053
1054static void decode_ui(game_ui *ui, const char *encoding)
1055{
1056}
1057
1058static void game_changed_state(game_ui *ui, const game_state *oldstate,
1059 const game_state *newstate)
1060{
1061 int w = newstate->par.w;
1062 /*
1063 * We prevent pencil-mode highlighting of a filled square, unless
1064 * we're using the cursor keys. So if the user has just filled in
1065 * a square which we had a pencil-mode highlight in (by Undo, or
1066 * by Redo, or by Solve), then we cancel the highlight.
1067 */
1068 if (ui->hshow && ui->hpencil && !ui->hcursor &&
1069 newstate->grid[ui->hy * w + ui->hx] != 0) {
1070 ui->hshow = 0;
1071 }
1072 if (ui->hshow && ui->odn > 1) {
1073 /*
1074 * Reordering of rows or columns within the range of a
1075 * multifill selection cancels the multifill and deselects
1076 * everything.
1077 */
1078 int i;
1079 for (i = 0; i < ui->odn; i++) {
1080 if (oldstate->sequence[ui->ohx + i*ui->odx] !=
1081 newstate->sequence[ui->ohx + i*ui->odx]) {
1082 ui->hshow = 0;
1083 break;
1084 }
1085 if (oldstate->sequence[ui->ohy + i*ui->ody] !=
1086 newstate->sequence[ui->ohy + i*ui->ody]) {
1087 ui->hshow = 0;
1088 break;
1089 }
1090 }
1091 } else if (ui->hshow &&
1092 (newstate->sequence[ui->ohx] != ui->hx ||
1093 newstate->sequence[ui->ohy] != ui->hy)) {
1094 /*
1095 * Otherwise, reordering of the row or column containing the
1096 * selection causes the selection to move with it.
1097 */
1098 int i;
1099 for (i = 0; i < w; i++) {
1100 if (newstate->sequence[i] == ui->hx)
1101 ui->ohx = i;
1102 if (newstate->sequence[i] == ui->hy)
1103 ui->ohy = i;
1104 }
1105 }
1106}
1107
1108#define PREFERRED_TILESIZE 48
1109#define TILESIZE (ds->tilesize)
1110#define BORDER (TILESIZE / 2)
1111#define LEGEND (TILESIZE)
1112#define GRIDEXTRA max((TILESIZE / 32),1)
1113#define COORD(x) ((x)*TILESIZE + BORDER + LEGEND)
1114#define FROMCOORD(x) (((x)+(TILESIZE-BORDER-LEGEND)) / TILESIZE - 1)
1115
1116#define FLASH_TIME 0.4F
1117
1118#define DF_DIVIDER_TOP 0x1000
1119#define DF_DIVIDER_BOT 0x2000
1120#define DF_DIVIDER_LEFT 0x4000
1121#define DF_DIVIDER_RIGHT 0x8000
1122#define DF_HIGHLIGHT 0x0400
1123#define DF_HIGHLIGHT_PENCIL 0x0200
1124#define DF_IMMUTABLE 0x0100
1125#define DF_LEGEND 0x0080
1126#define DF_DIGIT_MASK 0x001F
1127
1128#define EF_DIGIT_SHIFT 5
1129#define EF_DIGIT_MASK ((1 << EF_DIGIT_SHIFT) - 1)
1130#define EF_LEFT_SHIFT 0
1131#define EF_RIGHT_SHIFT (3*EF_DIGIT_SHIFT)
1132#define EF_LEFT_MASK ((1UL << (3*EF_DIGIT_SHIFT)) - 1UL)
1133#define EF_RIGHT_MASK (EF_LEFT_MASK << EF_RIGHT_SHIFT)
1134#define EF_LATIN (1UL << (6*EF_DIGIT_SHIFT))
1135
1136struct game_drawstate {
1137 game_params par;
1138 int w, tilesize;
1139 int started;
1140 long *tiles, *legend, *pencil, *errors;
1141 long *errtmp;
1142 digit *sequence;
1143};
1144
1145static int check_errors(const game_state *state, long *errors)
1146{
1147 int w = state->par.w, a = w*w;
1148 digit *grid = state->grid;
1149 int i, j, k, x, y, errs = FALSE;
1150
1151 /*
1152 * To verify that we have a valid group table, it suffices to
1153 * test latin-square-hood and associativity only. All the other
1154 * group axioms follow from those two.
1155 *
1156 * Proof:
1157 *
1158 * Associativity is given; closure is obvious from latin-
1159 * square-hood. We need to show that an identity exists and that
1160 * every element has an inverse.
1161 *
1162 * Identity: take any element a. There will be some element e
1163 * such that ea=a (in a latin square, every element occurs in
1164 * every row and column, so a must occur somewhere in the a
1165 * column, say on row e). For any other element b, there must
1166 * exist x such that ax=b (same argument from latin-square-hood
1167 * again), and then associativity gives us eb = e(ax) = (ea)x =
1168 * ax = b. Hence eb=b for all b, i.e. e is a left-identity. A
1169 * similar argument tells us that there must be some f which is
1170 * a right-identity, and then we show they are the same element
1171 * by observing that ef must simultaneously equal e and equal f.
1172 *
1173 * Inverses: given any a, by the latin-square argument again,
1174 * there must exist p and q such that pa=e and aq=e (i.e. left-
1175 * and right-inverses). We can show these are equal by
1176 * associativity: p = pe = p(aq) = (pa)q = eq = q. []
1177 */
1178
1179 if (errors)
1180 for (i = 0; i < a; i++)
1181 errors[i] = 0;
1182
1183 for (y = 0; y < w; y++) {
1184 unsigned long mask = 0, errmask = 0;
1185 for (x = 0; x < w; x++) {
1186 unsigned long bit = 1UL << grid[y*w+x];
1187 errmask |= (mask & bit);
1188 mask |= bit;
1189 }
1190
1191 if (mask != (1 << (w+1)) - (1 << 1)) {
1192 errs = TRUE;
1193 errmask &= ~1UL;
1194 if (errors) {
1195 for (x = 0; x < w; x++)
1196 if (errmask & (1UL << grid[y*w+x]))
1197 errors[y*w+x] |= EF_LATIN;
1198 }
1199 }
1200 }
1201
1202 for (x = 0; x < w; x++) {
1203 unsigned long mask = 0, errmask = 0;
1204 for (y = 0; y < w; y++) {
1205 unsigned long bit = 1UL << grid[y*w+x];
1206 errmask |= (mask & bit);
1207 mask |= bit;
1208 }
1209
1210 if (mask != (1 << (w+1)) - (1 << 1)) {
1211 errs = TRUE;
1212 errmask &= ~1UL;
1213 if (errors) {
1214 for (y = 0; y < w; y++)
1215 if (errmask & (1UL << grid[y*w+x]))
1216 errors[y*w+x] |= EF_LATIN;
1217 }
1218 }
1219 }
1220
1221 for (i = 1; i < w; i++)
1222 for (j = 1; j < w; j++)
1223 for (k = 1; k < w; k++)
1224 if (grid[i*w+j] && grid[j*w+k] &&
1225 grid[(grid[i*w+j]-1)*w+k] &&
1226 grid[i*w+(grid[j*w+k]-1)] &&
1227 grid[(grid[i*w+j]-1)*w+k] != grid[i*w+(grid[j*w+k]-1)]) {
1228 if (errors) {
1229 int a = i+1, b = j+1, c = k+1;
1230 int ab = grid[i*w+j], bc = grid[j*w+k];
1231 int left = (ab-1)*w+(c-1), right = (a-1)*w+(bc-1);
1232 /*
1233 * If the appropriate error slot is already
1234 * used for one of the squares, we don't
1235 * fill either of them.
1236 */
1237 if (!(errors[left] & EF_LEFT_MASK) &&
1238 !(errors[right] & EF_RIGHT_MASK)) {
1239 long err;
1240 err = a;
1241 err = (err << EF_DIGIT_SHIFT) | b;
1242 err = (err << EF_DIGIT_SHIFT) | c;
1243 errors[left] |= err << EF_LEFT_SHIFT;
1244 errors[right] |= err << EF_RIGHT_SHIFT;
1245 }
1246 }
1247 errs = TRUE;
1248 }
1249
1250 return errs;
1251}
1252
1253static int find_in_sequence(digit *seq, int len, digit n)
1254{
1255 int i;
1256
1257 for (i = 0; i < len; i++)
1258 if (seq[i] == n)
1259 return i;
1260
1261 assert(!"Should never get here");
1262 return -1;
1263}
1264
1265static char *interpret_move(const game_state *state, game_ui *ui,
1266 const game_drawstate *ds,
1267 int x, int y, int button)
1268{
1269 int w = state->par.w;
1270 int tx, ty;
1271 char buf[80];
1272
1273 button &= ~MOD_MASK;
1274
1275 tx = FROMCOORD(x);
1276 ty = FROMCOORD(y);
1277
1278 if (ui->drag) {
1279 if (IS_MOUSE_DRAG(button)) {
1280 int tcoord = ((ui->drag &~ 4) == 1 ? ty : tx);
1281 ui->drag |= 4; /* some movement has happened */
1282 if (tcoord >= 0 && tcoord < w) {
1283 ui->dragpos = tcoord;
1284 return "";
1285 }
1286 } else if (IS_MOUSE_RELEASE(button)) {
1287 if (ui->drag & 4) {
1288 ui->drag = 0; /* end drag */
1289 if (state->sequence[ui->dragpos] == ui->dragnum)
1290 return ""; /* drag was a no-op overall */
1291 sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos);
1292 return dupstr(buf);
1293 } else {
1294 ui->drag = 0; /* end 'drag' */
1295 if (ui->edgepos > 0 && ui->edgepos < w) {
1296 sprintf(buf, "V%d,%d",
1297 state->sequence[ui->edgepos-1],
1298 state->sequence[ui->edgepos]);
1299 return dupstr(buf);
1300 } else
1301 return ""; /* no-op */
1302 }
1303 }
1304 } else if (IS_MOUSE_DOWN(button)) {
1305 if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
1306 int otx = tx, oty = ty;
1307 tx = state->sequence[tx];
1308 ty = state->sequence[ty];
1309 if (button == LEFT_BUTTON) {
1310 if (tx == ui->hx && ty == ui->hy &&
1311 ui->hshow && ui->hpencil == 0) {
1312 ui->hshow = 0;
1313 } else {
1314 ui->hx = tx;
1315 ui->hy = ty;
1316 ui->ohx = otx;
1317 ui->ohy = oty;
1318 ui->odx = ui->ody = 0;
1319 ui->odn = 1;
1320 ui->hshow = !state->immutable[ty*w+tx];
1321 ui->hpencil = 0;
1322 }
1323 ui->hcursor = 0;
1324 return ""; /* UI activity occurred */
1325 }
1326 if (button == RIGHT_BUTTON) {
1327 /*
1328 * Pencil-mode highlighting for non filled squares.
1329 */
1330 if (state->grid[ty*w+tx] == 0) {
1331 if (tx == ui->hx && ty == ui->hy &&
1332 ui->hshow && ui->hpencil) {
1333 ui->hshow = 0;
1334 } else {
1335 ui->hpencil = 1;
1336 ui->hx = tx;
1337 ui->hy = ty;
1338 ui->ohx = otx;
1339 ui->ohy = oty;
1340 ui->odx = ui->ody = 0;
1341 ui->odn = 1;
1342 ui->hshow = 1;
1343 }
1344 } else {
1345 ui->hshow = 0;
1346 }
1347 ui->hcursor = 0;
1348 return ""; /* UI activity occurred */
1349 }
1350 } else if (tx >= 0 && tx < w && ty == -1) {
1351 ui->drag = 2;
1352 ui->dragnum = state->sequence[tx];
1353 ui->dragpos = tx;
1354 ui->edgepos = FROMCOORD(x + TILESIZE/2);
1355 return "";
1356 } else if (ty >= 0 && ty < w && tx == -1) {
1357 ui->drag = 1;
1358 ui->dragnum = state->sequence[ty];
1359 ui->dragpos = ty;
1360 ui->edgepos = FROMCOORD(y + TILESIZE/2);
1361 return "";
1362 }
1363 } else if (IS_MOUSE_DRAG(button)) {
1364 if (!ui->hpencil &&
1365 tx >= 0 && tx < w && ty >= 0 && ty < w &&
1366 abs(tx - ui->ohx) == abs(ty - ui->ohy)) {
1367 ui->odn = abs(tx - ui->ohx) + 1;
1368 ui->odx = (tx < ui->ohx ? -1 : +1);
1369 ui->ody = (ty < ui->ohy ? -1 : +1);
1370 } else {
1371 ui->odx = ui->ody = 0;
1372 ui->odn = 1;
1373 }
1374 return "";
1375 }
1376
1377 if (IS_CURSOR_MOVE(button)) {
1378 int cx = find_in_sequence(state->sequence, w, ui->hx);
1379 int cy = find_in_sequence(state->sequence, w, ui->hy);
1380 move_cursor(button, &cx, &cy, w, w, 0);
1381 ui->hx = state->sequence[cx];
1382 ui->hy = state->sequence[cy];
1383 ui->hshow = ui->hcursor = 1;
1384 return "";
1385 }
1386 if (ui->hshow &&
1387 (button == CURSOR_SELECT)) {
1388 ui->hpencil = 1 - ui->hpencil;
1389 ui->hcursor = 1;
1390 return "";
1391 }
1392
1393 if (ui->hshow &&
1394 ((ISCHAR(button) && FROMCHAR(button, state->par.id) <= w) ||
1395 button == CURSOR_SELECT2 || button == '\b')) {
1396 int n = FROMCHAR(button, state->par.id);
1397 int i, buflen;
1398 char *movebuf;
1399
1400 if (button == CURSOR_SELECT2 || button == '\b')
1401 n = 0;
1402
1403 for (i = 0; i < ui->odn; i++) {
1404 int x = state->sequence[ui->ohx + i*ui->odx];
1405 int y = state->sequence[ui->ohy + i*ui->ody];
1406 int index = y*w+x;
1407
1408 /*
1409 * Can't make pencil marks in a filled square. This can only
1410 * become highlighted if we're using cursor keys.
1411 */
1412 if (ui->hpencil && state->grid[index])
1413 return NULL;
1414
1415 /*
1416 * Can't do anything to an immutable square. Exception:
1417 * trying to set it to what it already was is OK (so that
1418 * multifilling can set a whole diagonal to a without
1419 * having to detour round the one immutable square in the
1420 * middle that already said a).
1421 */
1422 if (!ui->hpencil && state->grid[index] == n)
1423 /* OK even if it is immutable */;
1424 else if (state->immutable[index])
1425 return NULL;
1426 }
1427
1428 movebuf = snewn(80 * ui->odn, char);
1429 buflen = sprintf(movebuf, "%c%d,%d,%d",
1430 (char)(ui->hpencil && n > 0 ? 'P' : 'R'),
1431 ui->hx, ui->hy, n);
1432 for (i = 1; i < ui->odn; i++) {
1433 assert(buflen < i*80);
1434 buflen += sprintf(movebuf + buflen, "+%d,%d",
1435 state->sequence[ui->ohx + i*ui->odx],
1436 state->sequence[ui->ohy + i*ui->ody]);
1437 }
1438 movebuf = sresize(movebuf, buflen+1, char);
1439
1440 if (!ui->hcursor) ui->hshow = 0;
1441
1442 return movebuf;
1443 }
1444
1445 if (button == 'M' || button == 'm')
1446 return dupstr("M");
1447
1448 return NULL;
1449}
1450
1451static game_state *execute_move(const game_state *from, const char *move)
1452{
1453 int w = from->par.w, a = w*w;
1454 game_state *ret;
1455 int x, y, i, j, n, pos;
1456
1457 if (move[0] == 'S') {
1458 ret = dup_game(from);
1459 ret->completed = ret->cheated = TRUE;
1460
1461 for (i = 0; i < a; i++) {
1462 if (!ISCHAR(move[i+1]) || FROMCHAR(move[i+1], from->par.id) > w) {
1463 free_game(ret);
1464 return NULL;
1465 }
1466 ret->grid[i] = FROMCHAR(move[i+1], from->par.id);
1467 ret->pencil[i] = 0;
1468 }
1469
1470 if (move[a+1] != '\0') {
1471 free_game(ret);
1472 return NULL;
1473 }
1474
1475 return ret;
1476 } else if ((move[0] == 'P' || move[0] == 'R') &&
1477 sscanf(move+1, "%d,%d,%d%n", &x, &y, &n, &pos) == 3 &&
1478 n >= 0 && n <= w) {
1479 const char *mp = move + 1 + pos;
1480 int pencil = (move[0] == 'P');
1481 ret = dup_game(from);
1482
1483 while (1) {
1484 if (x < 0 || x >= w || y < 0 || y >= w) {
1485 free_game(ret);
1486 return NULL;
1487 }
1488 if (from->immutable[y*w+x] && !(!pencil && from->grid[y*w+x] == n))
1489 return NULL;
1490
1491 if (move[0] == 'P' && n > 0) {
1492 ret->pencil[y*w+x] ^= 1 << n;
1493 } else {
1494 ret->grid[y*w+x] = n;
1495 ret->pencil[y*w+x] = 0;
1496 }
1497
1498 if (!*mp)
1499 break;
1500
1501 if (*mp != '+')
1502 return NULL;
1503 if (sscanf(mp, "+%d,%d%n", &x, &y, &pos) < 2)
1504 return NULL;
1505 mp += pos;
1506 }
1507
1508 if (!ret->completed && !check_errors(ret, NULL))
1509 ret->completed = TRUE;
1510
1511 return ret;
1512 } else if (move[0] == 'M') {
1513 /*
1514 * Fill in absolutely all pencil marks everywhere. (I
1515 * wouldn't use this for actual play, but it's a handy
1516 * starting point when following through a set of
1517 * diagnostics output by the standalone solver.)
1518 */
1519 ret = dup_game(from);
1520 for (i = 0; i < a; i++) {
1521 if (!ret->grid[i])
1522 ret->pencil[i] = (1 << (w+1)) - (1 << 1);
1523 }
1524 return ret;
1525 } else if (move[0] == 'D' &&
1526 sscanf(move+1, "%d,%d", &x, &y) == 2) {
1527 /*
1528 * Reorder the rows and columns so that digit x is in position
1529 * y.
1530 */
1531 ret = dup_game(from);
1532 for (i = j = 0; i < w; i++) {
1533 if (i == y) {
1534 ret->sequence[i] = x;
1535 } else {
1536 if (from->sequence[j] == x)
1537 j++;
1538 ret->sequence[i] = from->sequence[j++];
1539 }
1540 }
1541 /*
1542 * Eliminate any obsoleted dividers.
1543 */
1544 for (x = 0; x < w; x++) {
1545 int i = ret->sequence[x];
1546 int j = (x+1 < w ? ret->sequence[x+1] : -1);
1547 if (ret->dividers[i] != j)
1548 ret->dividers[i] = -1;
1549 }
1550 return ret;
1551 } else if (move[0] == 'V' &&
1552 sscanf(move+1, "%d,%d", &i, &j) == 2) {
1553 ret = dup_game(from);
1554 if (ret->dividers[i] == j)
1555 ret->dividers[i] = -1;
1556 else
1557 ret->dividers[i] = j;
1558 return ret;
1559 } else
1560 return NULL; /* couldn't parse move string */
1561}
1562
1563/* ----------------------------------------------------------------------
1564 * Drawing routines.
1565 */
1566
1567#define SIZE(w) ((w) * TILESIZE + 2*BORDER + LEGEND)
1568
1569static void game_compute_size(const game_params *params, int tilesize,
1570 int *x, int *y)
1571{
1572 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1573 struct { int tilesize; } ads, *ds = &ads;
1574 ads.tilesize = tilesize;
1575
1576 *x = *y = SIZE(params->w);
1577}
1578
1579static void game_set_size(drawing *dr, game_drawstate *ds,
1580 const game_params *params, int tilesize)
1581{
1582 ds->tilesize = tilesize;
1583}
1584
1585static float *game_colours(frontend *fe, int *ncolours)
1586{
1587 float *ret = snewn(3 * NCOLOURS, float);
1588
1589 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1590
1591 ret[COL_GRID * 3 + 0] = 0.0F;
1592 ret[COL_GRID * 3 + 1] = 0.0F;
1593 ret[COL_GRID * 3 + 2] = 0.0F;
1594
1595 ret[COL_USER * 3 + 0] = 0.0F;
1596 ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1];
1597 ret[COL_USER * 3 + 2] = 0.0F;
1598
1599 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
1600 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
1601 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
1602
1603 ret[COL_ERROR * 3 + 0] = 1.0F;
1604 ret[COL_ERROR * 3 + 1] = 0.0F;
1605 ret[COL_ERROR * 3 + 2] = 0.0F;
1606
1607 ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1608 ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1609 ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1610
1611 ret[COL_DIAGONAL * 3 + 0] = 0.95F * ret[COL_BACKGROUND * 3 + 0];
1612 ret[COL_DIAGONAL * 3 + 1] = 0.95F * ret[COL_BACKGROUND * 3 + 1];
1613 ret[COL_DIAGONAL * 3 + 2] = 0.95F * ret[COL_BACKGROUND * 3 + 2];
1614
1615 *ncolours = NCOLOURS;
1616 return ret;
1617}
1618
1619static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1620{
1621 int w = state->par.w, a = w*w;
1622 struct game_drawstate *ds = snew(struct game_drawstate);
1623 int i;
1624
1625 ds->w = w;
1626 ds->par = state->par; /* structure copy */
1627 ds->tilesize = 0;
1628 ds->started = FALSE;
1629 ds->tiles = snewn(a, long);
1630 ds->legend = snewn(w, long);
1631 ds->pencil = snewn(a, long);
1632 ds->errors = snewn(a, long);
1633 ds->sequence = snewn(a, digit);
1634 for (i = 0; i < a; i++)
1635 ds->tiles[i] = ds->pencil[i] = -1;
1636 for (i = 0; i < w; i++)
1637 ds->legend[i] = -1;
1638 ds->errtmp = snewn(a, long);
1639
1640 return ds;
1641}
1642
1643static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1644{
1645 sfree(ds->tiles);
1646 sfree(ds->pencil);
1647 sfree(ds->errors);
1648 sfree(ds->errtmp);
1649 sfree(ds->sequence);
1650 sfree(ds);
1651}
1652
1653static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile,
1654 long pencil, long error)
1655{
1656 int w = ds->w /* , a = w*w */;
1657 int tx, ty, tw, th;
1658 int cx, cy, cw, ch;
1659 char str[64];
1660
1661 tx = BORDER + LEGEND + x * TILESIZE + 1;
1662 ty = BORDER + LEGEND + y * TILESIZE + 1;
1663
1664 cx = tx;
1665 cy = ty;
1666 cw = tw = TILESIZE-1;
1667 ch = th = TILESIZE-1;
1668
1669 if (tile & DF_LEGEND) {
1670 cx += TILESIZE/10;
1671 cy += TILESIZE/10;
1672 cw -= TILESIZE/5;
1673 ch -= TILESIZE/5;
1674 tile |= DF_IMMUTABLE;
1675 }
1676
1677 clip(dr, cx, cy, cw, ch);
1678
1679 /* background needs erasing */
1680 draw_rect(dr, cx, cy, cw, ch,
1681 (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT :
1682 (x == y) ? COL_DIAGONAL : COL_BACKGROUND);
1683
1684 /* dividers */
1685 if (tile & DF_DIVIDER_TOP)
1686 draw_rect(dr, cx, cy, cw, 1, COL_GRID);
1687 if (tile & DF_DIVIDER_BOT)
1688 draw_rect(dr, cx, cy+ch-1, cw, 1, COL_GRID);
1689 if (tile & DF_DIVIDER_LEFT)
1690 draw_rect(dr, cx, cy, 1, ch, COL_GRID);
1691 if (tile & DF_DIVIDER_RIGHT)
1692 draw_rect(dr, cx+cw-1, cy, 1, ch, COL_GRID);
1693
1694 /* pencil-mode highlight */
1695 if (tile & DF_HIGHLIGHT_PENCIL) {
1696 int coords[6];
1697 coords[0] = cx;
1698 coords[1] = cy;
1699 coords[2] = cx+cw/2;
1700 coords[3] = cy;
1701 coords[4] = cx;
1702 coords[5] = cy+ch/2;
1703 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1704 }
1705
1706 /* new number needs drawing? */
1707 if (tile & DF_DIGIT_MASK) {
1708 str[1] = '\0';
1709 str[0] = TOCHAR(tile & DF_DIGIT_MASK, ds->par.id);
1710 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1711 FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE,
1712 (error & EF_LATIN) ? COL_ERROR :
1713 (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str);
1714
1715 if (error & EF_LEFT_MASK) {
1716 int a = (error >> (EF_LEFT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1717 int b = (error >> (EF_LEFT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1718 int c = (error >> (EF_LEFT_SHIFT ))&EF_DIGIT_MASK;
1719 char buf[10];
1720 sprintf(buf, "(%c%c)%c", TOCHAR(a, ds->par.id),
1721 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
1722 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/6,
1723 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
1724 COL_ERROR, buf);
1725 }
1726 if (error & EF_RIGHT_MASK) {
1727 int a = (error >> (EF_RIGHT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1728 int b = (error >> (EF_RIGHT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK;
1729 int c = (error >> (EF_RIGHT_SHIFT ))&EF_DIGIT_MASK;
1730 char buf[10];
1731 sprintf(buf, "%c(%c%c)", TOCHAR(a, ds->par.id),
1732 TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id));
1733 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE - TILESIZE/6,
1734 FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE,
1735 COL_ERROR, buf);
1736 }
1737 } else {
1738 int i, j, npencil;
1739 int pl, pr, pt, pb;
1740 float bestsize;
1741 int pw, ph, minph, pbest, fontsize;
1742
1743 /* Count the pencil marks required. */
1744 for (i = 1, npencil = 0; i <= w; i++)
1745 if (pencil & (1 << i))
1746 npencil++;
1747 if (npencil) {
1748
1749 minph = 2;
1750
1751 /*
1752 * Determine the bounding rectangle within which we're going
1753 * to put the pencil marks.
1754 */
1755 /* Start with the whole square */
1756 pl = tx + GRIDEXTRA;
1757 pr = pl + TILESIZE - GRIDEXTRA;
1758 pt = ty + GRIDEXTRA;
1759 pb = pt + TILESIZE - GRIDEXTRA;
1760
1761 /*
1762 * We arrange our pencil marks in a grid layout, with
1763 * the number of rows and columns adjusted to allow the
1764 * maximum font size.
1765 *
1766 * So now we work out what the grid size ought to be.
1767 */
1768 bestsize = 0.0;
1769 pbest = 0;
1770 /* Minimum */
1771 for (pw = 3; pw < max(npencil,4); pw++) {
1772 float fw, fh, fs;
1773
1774 ph = (npencil + pw - 1) / pw;
1775 ph = max(ph, minph);
1776 fw = (pr - pl) / (float)pw;
1777 fh = (pb - pt) / (float)ph;
1778 fs = min(fw, fh);
1779 if (fs > bestsize) {
1780 bestsize = fs;
1781 pbest = pw;
1782 }
1783 }
1784 assert(pbest > 0);
1785 pw = pbest;
1786 ph = (npencil + pw - 1) / pw;
1787 ph = max(ph, minph);
1788
1789 /*
1790 * Now we've got our grid dimensions, work out the pixel
1791 * size of a grid element, and round it to the nearest
1792 * pixel. (We don't want rounding errors to make the
1793 * grid look uneven at low pixel sizes.)
1794 */
1795 fontsize = min((pr - pl) / pw, (pb - pt) / ph);
1796
1797 /*
1798 * Centre the resulting figure in the square.
1799 */
1800 pl = tx + (TILESIZE - fontsize * pw) / 2;
1801 pt = ty + (TILESIZE - fontsize * ph) / 2;
1802
1803 /*
1804 * Now actually draw the pencil marks.
1805 */
1806 for (i = 1, j = 0; i <= w; i++)
1807 if (pencil & (1 << i)) {
1808 int dx = j % pw, dy = j / pw;
1809
1810 str[1] = '\0';
1811 str[0] = TOCHAR(i, ds->par.id);
1812 draw_text(dr, pl + fontsize * (2*dx+1) / 2,
1813 pt + fontsize * (2*dy+1) / 2,
1814 FONT_VARIABLE, fontsize,
1815 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str);
1816 j++;
1817 }
1818 }
1819 }
1820
1821 unclip(dr);
1822
1823 draw_update(dr, cx, cy, cw, ch);
1824}
1825
1826static void game_redraw(drawing *dr, game_drawstate *ds,
1827 const game_state *oldstate, const game_state *state,
1828 int dir, const game_ui *ui,
1829 float animtime, float flashtime)
1830{
1831 int w = state->par.w /*, a = w*w */;
1832 int x, y, i, j;
1833
1834 if (!ds->started) {
1835 /*
1836 * The initial contents of the window are not guaranteed and
1837 * can vary with front ends. To be on the safe side, all
1838 * games should start by drawing a big background-colour
1839 * rectangle covering the whole window.
1840 */
1841 draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
1842
1843 /*
1844 * Big containing rectangle.
1845 */
1846 draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA,
1847 w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2,
1848 COL_GRID);
1849
1850 draw_update(dr, 0, 0, SIZE(w), SIZE(w));
1851
1852 ds->started = TRUE;
1853 }
1854
1855 check_errors(state, ds->errtmp);
1856
1857 /*
1858 * Construct a modified version of state->sequence which takes
1859 * into account an unfinished drag operation.
1860 */
1861 if (ui->drag) {
1862 x = ui->dragnum;
1863 y = ui->dragpos;
1864 } else {
1865 x = y = -1;
1866 }
1867 for (i = j = 0; i < w; i++) {
1868 if (i == y) {
1869 ds->sequence[i] = x;
1870 } else {
1871 if (state->sequence[j] == x)
1872 j++;
1873 ds->sequence[i] = state->sequence[j++];
1874 }
1875 }
1876
1877 /*
1878 * Draw the table legend.
1879 */
1880 for (x = 0; x < w; x++) {
1881 int sx = ds->sequence[x];
1882 long tile = (sx+1) | DF_LEGEND;
1883 if (ds->legend[x] != tile) {
1884 ds->legend[x] = tile;
1885 draw_tile(dr, ds, -1, x, tile, 0, 0);
1886 draw_tile(dr, ds, x, -1, tile, 0, 0);
1887 }
1888 }
1889
1890 for (y = 0; y < w; y++) {
1891 int sy = ds->sequence[y];
1892 for (x = 0; x < w; x++) {
1893 long tile = 0L, pencil = 0L, error;
1894 int sx = ds->sequence[x];
1895
1896 if (state->grid[sy*w+sx])
1897 tile = state->grid[sy*w+sx];
1898 else
1899 pencil = (long)state->pencil[sy*w+sx];
1900
1901 if (state->immutable[sy*w+sx])
1902 tile |= DF_IMMUTABLE;
1903
1904 if ((ui->drag == 5 && ui->dragnum == sy) ||
1905 (ui->drag == 6 && ui->dragnum == sx)) {
1906 tile |= DF_HIGHLIGHT;
1907 } else if (ui->hshow) {
1908 int i = abs(x - ui->ohx);
1909 int highlight = 0;
1910 if (ui->odn > 1) {
1911 /*
1912 * When a diagonal multifill selection is shown,
1913 * we show it in its original grid position
1914 * regardless of in-progress row/col drags. Moving
1915 * every square about would be horrible.
1916 */
1917 if (i >= 0 && i < ui->odn &&
1918 x == ui->ohx + i*ui->odx &&
1919 y == ui->ohy + i*ui->ody)
1920 highlight = 1;
1921 } else {
1922 /*
1923 * For a single square, we move its highlight
1924 * around with the drag.
1925 */
1926 highlight = (ui->hx == sx && ui->hy == sy);
1927 }
1928 if (highlight)
1929 tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
1930 }
1931
1932 if (flashtime > 0 &&
1933 (flashtime <= FLASH_TIME/3 ||
1934 flashtime >= FLASH_TIME*2/3))
1935 tile |= DF_HIGHLIGHT; /* completion flash */
1936
1937 if (y <= 0 || state->dividers[ds->sequence[y-1]] == sy)
1938 tile |= DF_DIVIDER_TOP;
1939 if (y+1 >= w || state->dividers[sy] == ds->sequence[y+1])
1940 tile |= DF_DIVIDER_BOT;
1941 if (x <= 0 || state->dividers[ds->sequence[x-1]] == sx)
1942 tile |= DF_DIVIDER_LEFT;
1943 if (x+1 >= w || state->dividers[sx] == ds->sequence[x+1])
1944 tile |= DF_DIVIDER_RIGHT;
1945
1946 error = ds->errtmp[sy*w+sx];
1947
1948 if (ds->tiles[y*w+x] != tile ||
1949 ds->pencil[y*w+x] != pencil ||
1950 ds->errors[y*w+x] != error) {
1951 ds->tiles[y*w+x] = tile;
1952 ds->pencil[y*w+x] = pencil;
1953 ds->errors[y*w+x] = error;
1954 draw_tile(dr, ds, x, y, tile, pencil, error);
1955 }
1956 }
1957 }
1958}
1959
1960static float game_anim_length(const game_state *oldstate,
1961 const game_state *newstate, int dir, game_ui *ui)
1962{
1963 return 0.0F;
1964}
1965
1966static float game_flash_length(const game_state *oldstate,
1967 const game_state *newstate, int dir, game_ui *ui)
1968{
1969 if (!oldstate->completed && newstate->completed &&
1970 !oldstate->cheated && !newstate->cheated)
1971 return FLASH_TIME;
1972 return 0.0F;
1973}
1974
1975static int game_status(const game_state *state)
1976{
1977 return state->completed ? +1 : 0;
1978}
1979
1980static int game_timing_state(const game_state *state, game_ui *ui)
1981{
1982 if (state->completed)
1983 return FALSE;
1984 return TRUE;
1985}
1986
1987static void game_print_size(const game_params *params, float *x, float *y)
1988{
1989 int pw, ph;
1990
1991 /*
1992 * We use 9mm squares by default, like Solo.
1993 */
1994 game_compute_size(params, 900, &pw, &ph);
1995 *x = pw / 100.0F;
1996 *y = ph / 100.0F;
1997}
1998
1999static void game_print(drawing *dr, const game_state *state, int tilesize)
2000{
2001 int w = state->par.w;
2002 int ink = print_mono_colour(dr, 0);
2003 int x, y;
2004
2005 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2006 game_drawstate ads, *ds = &ads;
2007 game_set_size(dr, ds, NULL, tilesize);
2008
2009 /*
2010 * Border.
2011 */
2012 print_line_width(dr, 3 * TILESIZE / 40);
2013 draw_rect_outline(dr, BORDER + LEGEND, BORDER + LEGEND,
2014 w*TILESIZE, w*TILESIZE, ink);
2015
2016 /*
2017 * Legend on table.
2018 */
2019 for (x = 0; x < w; x++) {
2020 char str[2];
2021 str[1] = '\0';
2022 str[0] = TOCHAR(x+1, state->par.id);
2023 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2024 BORDER + TILESIZE/2,
2025 FONT_VARIABLE, TILESIZE/2,
2026 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2027 draw_text(dr, BORDER + TILESIZE/2,
2028 BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2029 FONT_VARIABLE, TILESIZE/2,
2030 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2031 }
2032
2033 /*
2034 * Main grid.
2035 */
2036 for (x = 1; x < w; x++) {
2037 print_line_width(dr, TILESIZE / 40);
2038 draw_line(dr, BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND,
2039 BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND+w*TILESIZE, ink);
2040 }
2041 for (y = 1; y < w; y++) {
2042 print_line_width(dr, TILESIZE / 40);
2043 draw_line(dr, BORDER+LEGEND, BORDER+LEGEND+y*TILESIZE,
2044 BORDER+LEGEND+w*TILESIZE, BORDER+LEGEND+y*TILESIZE, ink);
2045 }
2046
2047 /*
2048 * Numbers.
2049 */
2050 for (y = 0; y < w; y++)
2051 for (x = 0; x < w; x++)
2052 if (state->grid[y*w+x]) {
2053 char str[2];
2054 str[1] = '\0';
2055 str[0] = TOCHAR(state->grid[y*w+x], state->par.id);
2056 draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2,
2057 BORDER+LEGEND + y*TILESIZE + TILESIZE/2,
2058 FONT_VARIABLE, TILESIZE/2,
2059 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
2060 }
2061}
2062
2063#ifdef COMBINED
2064#define thegame group
2065#endif
2066
2067const struct game thegame = {
2068 "Group", NULL, NULL,
2069 default_params,
2070 game_fetch_preset, NULL,
2071 decode_params,
2072 encode_params,
2073 free_params,
2074 dup_params,
2075 TRUE, game_configure, custom_params,
2076 validate_params,
2077 new_game_desc,
2078 validate_desc,
2079 new_game,
2080 dup_game,
2081 free_game,
2082 TRUE, solve_game,
2083 TRUE, game_can_format_as_text_now, game_text_format,
2084 new_ui,
2085 free_ui,
2086 encode_ui,
2087 decode_ui,
2088 game_changed_state,
2089 interpret_move,
2090 execute_move,
2091 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2092 game_colours,
2093 game_new_drawstate,
2094 game_free_drawstate,
2095 game_redraw,
2096 game_anim_length,
2097 game_flash_length,
2098 game_status,
2099 TRUE, FALSE, game_print_size, game_print,
2100 FALSE, /* wants_statusbar */
2101 FALSE, game_timing_state,
2102 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2103};
2104
2105#ifdef STANDALONE_SOLVER
2106
2107#include <stdarg.h>
2108
2109int main(int argc, char **argv)
2110{
2111 game_params *p;
2112 game_state *s;
2113 char *id = NULL, *desc, *err;
2114 digit *grid;
2115 int grade = FALSE;
2116 int ret, diff, really_show_working = FALSE;
2117
2118 while (--argc > 0) {
2119 char *p = *++argv;
2120 if (!strcmp(p, "-v")) {
2121 really_show_working = TRUE;
2122 } else if (!strcmp(p, "-g")) {
2123 grade = TRUE;
2124 } else if (*p == '-') {
2125 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2126 return 1;
2127 } else {
2128 id = p;
2129 }
2130 }
2131
2132 if (!id) {
2133 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
2134 return 1;
2135 }
2136
2137 desc = strchr(id, ':');
2138 if (!desc) {
2139 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2140 return 1;
2141 }
2142 *desc++ = '\0';
2143
2144 p = default_params();
2145 decode_params(p, id);
2146 err = validate_desc(p, desc);
2147 if (err) {
2148 fprintf(stderr, "%s: %s\n", argv[0], err);
2149 return 1;
2150 }
2151 s = new_game(NULL, p, desc);
2152
2153 grid = snewn(p->w * p->w, digit);
2154
2155 /*
2156 * When solving a Normal puzzle, we don't want to bother the
2157 * user with Hard-level deductions. For this reason, we grade
2158 * the puzzle internally before doing anything else.
2159 */
2160 ret = -1; /* placate optimiser */
2161 solver_show_working = FALSE;
2162 for (diff = 0; diff < DIFFCOUNT; diff++) {
2163 memcpy(grid, s->grid, p->w * p->w);
2164 ret = solver(&s->par, grid, diff);
2165 if (ret <= diff)
2166 break;
2167 }
2168
2169 if (diff == DIFFCOUNT) {
2170 if (grade)
2171 printf("Difficulty rating: ambiguous\n");
2172 else
2173 printf("Unable to find a unique solution\n");
2174 } else {
2175 if (grade) {
2176 if (ret == diff_impossible)
2177 printf("Difficulty rating: impossible (no solution exists)\n");
2178 else
2179 printf("Difficulty rating: %s\n", group_diffnames[ret]);
2180 } else {
2181 solver_show_working = really_show_working;
2182 memcpy(grid, s->grid, p->w * p->w);
2183 ret = solver(&s->par, grid, diff);
2184 if (ret != diff)
2185 printf("Puzzle is inconsistent\n");
2186 else {
2187 memcpy(s->grid, grid, p->w * p->w);
2188 fputs(game_text_format(s), stdout);
2189 }
2190 }
2191 }
2192
2193 return 0;
2194}
2195
2196#endif
2197
2198/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/unfinished/group.gap b/apps/plugins/puzzles/src/unfinished/group.gap
new file mode 100644
index 0000000000..280adf4664
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/group.gap
@@ -0,0 +1,97 @@
1# run this file with
2# gap -b -q < /dev/null group.gap | perl -pe 's/\\\n//s' | indent -kr
3
4Print("/* ----- data generated by group.gap begins ----- */\n\n");
5Print("struct group {\n unsigned long autosize;\n");
6Print(" int order, ngens;\n const char *gens;\n};\n");
7Print("struct groups {\n int ngroups;\n");
8Print(" const struct group *groups;\n};\n\n");
9Print("static const struct group groupdata[] = {\n");
10offsets := [0];
11offset := 0;
12for n in [2..26] do
13 Print(" /* order ", n, " */\n");
14 for G in AllSmallGroups(n) do
15
16 # Construct a representation of the group G as a subgroup
17 # of a permutation group, and find its generators in that
18 # group.
19
20 # GAP has the 'IsomorphismPermGroup' function, but I don't want
21 # to use it because it doesn't guarantee that the permutation
22 # representation of the group forms a Cayley table. For example,
23 # C_4 could be represented as a subgroup of S_4 in many ways,
24 # and not all of them work: the group generated by (12) and (34)
25 # is clearly isomorphic to C_4 but its four elements do not form
26 # a Cayley table. The group generated by (12)(34) and (13)(24)
27 # is OK, though.
28 #
29 # Hence I construct the permutation representation _as_ the
30 # Cayley table, and then pick generators of that. This
31 # guarantees that when we rebuild the full group by BFS in
32 # group.c, we will end up with the right thing.
33
34 ge := Elements(G);
35 gi := [];
36 for g in ge do
37 gr := [];
38 for h in ge do
39 k := g*h;
40 for i in [1..n] do
41 if k = ge[i] then
42 Add(gr, i);
43 fi;
44 od;
45 od;
46 Add(gi, PermList(gr));
47 od;
48
49 # GAP has the 'GeneratorsOfGroup' function, but we don't want to
50 # use it because it's bad at picking generators - it thinks the
51 # generators of C_4 are [ (1,2)(3,4), (1,3,2,4) ] and that those
52 # of C_6 are [ (1,2,3)(4,5,6), (1,4)(2,5)(3,6) ] !
53
54 gl := ShallowCopy(Elements(gi));
55 Sort(gl, function(v,w) return Order(v) > Order(w); end);
56
57 gens := [];
58 for x in gl do
59 if gens = [] or not (x in gp) then
60 Add(gens, x);
61 gp := GroupWithGenerators(gens);
62 fi;
63 od;
64
65 # Construct the C representation of the group generators.
66 s := [];
67 for x in gens do
68 if Size(s) > 0 then
69 Add(s, '"');
70 Add(s, ' ');
71 Add(s, '"');
72 fi;
73 sep := "\\0";
74 for i in ListPerm(x) do
75 chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
76 Add(s, chars[i]);
77 od;
78 od;
79 s := JoinStringsWithSeparator([" {", String(Size(AutomorphismGroup(G))),
80 "L, ", String(Size(G)),
81 ", ", String(Size(gens)),
82 ", \"", s, "\"},\n"],"");
83 Print(s);
84 offset := offset + 1;
85 od;
86 Add(offsets, offset);
87od;
88Print("};\n\nstatic const struct groups groups[] = {\n");
89Print(" {0, NULL}, /* trivial case: 0 */\n");
90Print(" {0, NULL}, /* trivial case: 1 */\n");
91n := 2;
92for i in [1..Size(offsets)-1] do
93 Print(" {", offsets[i+1] - offsets[i], ", groupdata+",
94 offsets[i], "}, /* ", i+1, " */\n");
95od;
96Print("};\n\n/* ----- data generated by group.gap ends ----- */\n");
97quit;
diff --git a/apps/plugins/puzzles/src/unfinished/numgame.c b/apps/plugins/puzzles/src/unfinished/numgame.c
new file mode 100644
index 0000000000..aed5c17347
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/numgame.c
@@ -0,0 +1,1290 @@
1/*
2 * This program implements a breadth-first search which
3 * exhaustively solves the Countdown numbers game, and related
4 * games with slightly different rule sets such as `Flippo'.
5 *
6 * Currently it is simply a standalone command-line utility to
7 * which you provide a set of numbers and it tells you everything
8 * it can make together with how many different ways it can be
9 * made. I would like ultimately to turn it into the generator for
10 * a Puzzles puzzle, but I haven't even started on writing a
11 * Puzzles user interface yet.
12 */
13
14/*
15 * TODO:
16 *
17 * - start thinking about difficulty ratings
18 * + anything involving associative operations will be flagged
19 * as many-paths because of the associative options (e.g.
20 * 2*3*4 can be (2*3)*4 or 2*(3*4), or indeed (2*4)*3). This
21 * is probably a _good_ thing, since those are unusually
22 * easy.
23 * + tree-structured calculations ((a*b)/(c+d)) have multiple
24 * paths because the independent branches of the tree can be
25 * evaluated in either order, whereas straight-line
26 * calculations with no branches will be considered easier.
27 * Can we do anything about this? It's certainly not clear to
28 * me that tree-structure calculations are _easier_, although
29 * I'm also not convinced they're harder.
30 * + I think for a realistic difficulty assessment we must also
31 * consider the `obviousness' of the arithmetic operations in
32 * some heuristic sense, and also (in Countdown) how many
33 * numbers ended up being used.
34 * - actually try some generations
35 * - at this point we're probably ready to start on the Puzzles
36 * integration.
37 */
38
39#include <stdio.h>
40#include <string.h>
41#include <limits.h>
42#include <assert.h>
43#include <math.h>
44
45#include "puzzles.h"
46#include "tree234.h"
47
48/*
49 * To search for numbers we can make, we employ a breadth-first
50 * search across the space of sets of input numbers. That is, for
51 * example, we start with the set (3,6,25,50,75,100); we apply
52 * moves which involve combining two numbers (e.g. adding the 50
53 * and the 75 takes us to the set (3,6,25,100,125); and then we see
54 * if we ever end up with a set containing (say) 952.
55 *
56 * If the rules are changed so that all the numbers must be used,
57 * this is easy to adjust to: we simply see if we end up with a set
58 * containing _only_ (say) 952.
59 *
60 * Obviously, we can vary the rules about permitted arithmetic
61 * operations simply by altering the set of valid moves in the bfs.
62 * However, there's one common rule in this sort of puzzle which
63 * takes a little more thought, and that's _concatenation_. For
64 * example, if you are given (say) four 4s and required to make 10,
65 * you are permitted to combine two of the 4s into a 44 to begin
66 * with, making (44-4)/4 = 10. However, you are generally not
67 * allowed to concatenate two numbers that _weren't_ both in the
68 * original input set (you couldn't multiply two 4s to get 16 and
69 * then concatenate a 4 on to it to make 164), so concatenation is
70 * not an operation which is valid in all situations.
71 *
72 * We could enforce this restriction by storing a flag alongside
73 * each number indicating whether or not it's an original number;
74 * the rules being that concatenation of two numbers is only valid
75 * if they both have the original flag, and that its output _also_
76 * has the original flag (so that you can concatenate three 4s into
77 * a 444), but that applying any other arithmetic operation clears
78 * the original flag on the output. However, we can get marginally
79 * simpler than that by observing that since concatenation has to
80 * happen to a number before any other operation, we can simply
81 * place all the concatenations at the start of the search. In
82 * other words, we have a global flag on an entire number _set_
83 * which indicates whether we are still permitted to perform
84 * concatenations; if so, we can concatenate any of the numbers in
85 * that set. Performing any other operation clears the flag.
86 */
87
88#define SETFLAG_CONCAT 1 /* we can do concatenation */
89
90struct sets;
91
92struct ancestor {
93 struct set *prev; /* index of ancestor set in set list */
94 unsigned char pa, pb, po, pr; /* operation that got here from prev */
95};
96
97struct set {
98 int *numbers; /* rationals stored as n,d pairs */
99 short nnumbers; /* # of rationals, so half # of ints */
100 short flags; /* SETFLAG_CONCAT only, at present */
101 int npaths; /* number of ways to reach this set */
102 struct ancestor a; /* primary ancestor */
103 struct ancestor *as; /* further ancestors, if we care */
104 int nas, assize;
105};
106
107struct output {
108 int number;
109 struct set *set;
110 int index; /* which number in the set is it? */
111 int npaths; /* number of ways to reach this */
112};
113
114#define SETLISTLEN 1024
115#define NUMBERLISTLEN 32768
116#define OUTPUTLISTLEN 1024
117struct operation;
118struct sets {
119 struct set **setlists;
120 int nsets, nsetlists, setlistsize;
121 tree234 *settree;
122 int **numberlists;
123 int nnumbers, nnumberlists, numberlistsize;
124 struct output **outputlists;
125 int noutputs, noutputlists, outputlistsize;
126 tree234 *outputtree;
127 const struct operation *const *ops;
128};
129
130#define OPFLAG_NEEDS_CONCAT 1
131#define OPFLAG_KEEPS_CONCAT 2
132#define OPFLAG_UNARY 4
133#define OPFLAG_UNARYPREFIX 8
134#define OPFLAG_FN 16
135
136struct operation {
137 /*
138 * Most operations should be shown in the output working, but
139 * concatenation should not; we just take the result of the
140 * concatenation and assume that it's obvious how it was
141 * derived.
142 */
143 int display;
144
145 /*
146 * Text display of the operator, in expressions and for
147 * debugging respectively.
148 */
149 char *text, *dbgtext;
150
151 /*
152 * Flags dictating when the operator can be applied.
153 */
154 int flags;
155
156 /*
157 * Priority of the operator (for avoiding unnecessary
158 * parentheses when formatting it into a string).
159 */
160 int priority;
161
162 /*
163 * Associativity of the operator. Bit 0 means we need parens
164 * when the left operand of one of these operators is another
165 * instance of it, e.g. (2^3)^4. Bit 1 means we need parens
166 * when the right operand is another instance of the same
167 * operator, e.g. 2-(3-4). Thus:
168 *
169 * - this field is 0 for a fully associative operator, since
170 * we never need parens.
171 * - it's 1 for a right-associative operator.
172 * - it's 2 for a left-associative operator.
173 * - it's 3 for a _non_-associative operator (which always
174 * uses parens just to be sure).
175 */
176 int assoc;
177
178 /*
179 * Whether the operator is commutative. Saves time in the
180 * search if we don't have to try it both ways round.
181 */
182 int commutes;
183
184 /*
185 * Function which implements the operator. Returns TRUE on
186 * success, FALSE on failure. Takes two rationals and writes
187 * out a third.
188 */
189 int (*perform)(int *a, int *b, int *output);
190};
191
192struct rules {
193 const struct operation *const *ops;
194 int use_all;
195};
196
197#define MUL(r, a, b) do { \
198 (r) = (a) * (b); \
199 if ((b) && (a) && (r) / (b) != (a)) return FALSE; \
200} while (0)
201
202#define ADD(r, a, b) do { \
203 (r) = (a) + (b); \
204 if ((a) > 0 && (b) > 0 && (r) < 0) return FALSE; \
205 if ((a) < 0 && (b) < 0 && (r) > 0) return FALSE; \
206} while (0)
207
208#define OUT(output, n, d) do { \
209 int g = gcd((n),(d)); \
210 if (g < 0) g = -g; \
211 if ((d) < 0) g = -g; \
212 if (g == -1 && (n) < -INT_MAX) return FALSE; \
213 if (g == -1 && (d) < -INT_MAX) return FALSE; \
214 (output)[0] = (n)/g; \
215 (output)[1] = (d)/g; \
216 assert((output)[1] > 0); \
217} while (0)
218
219static int gcd(int x, int y)
220{
221 while (x != 0 && y != 0) {
222 int t = x;
223 x = y;
224 y = t % y;
225 }
226
227 return abs(x + y); /* i.e. whichever one isn't zero */
228}
229
230static int perform_add(int *a, int *b, int *output)
231{
232 int at, bt, tn, bn;
233 /*
234 * a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1)
235 */
236 MUL(at, a[0], b[1]);
237 MUL(bt, b[0], a[1]);
238 ADD(tn, at, bt);
239 MUL(bn, a[1], b[1]);
240 OUT(output, tn, bn);
241 return TRUE;
242}
243
244static int perform_sub(int *a, int *b, int *output)
245{
246 int at, bt, tn, bn;
247 /*
248 * a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1)
249 */
250 MUL(at, a[0], b[1]);
251 MUL(bt, b[0], a[1]);
252 ADD(tn, at, -bt);
253 MUL(bn, a[1], b[1]);
254 OUT(output, tn, bn);
255 return TRUE;
256}
257
258static int perform_mul(int *a, int *b, int *output)
259{
260 int tn, bn;
261 /*
262 * a0/a1 * b0/b1 = (a0*b0) / (a1*b1)
263 */
264 MUL(tn, a[0], b[0]);
265 MUL(bn, a[1], b[1]);
266 OUT(output, tn, bn);
267 return TRUE;
268}
269
270static int perform_div(int *a, int *b, int *output)
271{
272 int tn, bn;
273
274 /*
275 * Division by zero is outlawed.
276 */
277 if (b[0] == 0)
278 return FALSE;
279
280 /*
281 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
282 */
283 MUL(tn, a[0], b[1]);
284 MUL(bn, a[1], b[0]);
285 OUT(output, tn, bn);
286 return TRUE;
287}
288
289static int perform_exact_div(int *a, int *b, int *output)
290{
291 int tn, bn;
292
293 /*
294 * Division by zero is outlawed.
295 */
296 if (b[0] == 0)
297 return FALSE;
298
299 /*
300 * a0/a1 / b0/b1 = (a0*b1) / (a1*b0)
301 */
302 MUL(tn, a[0], b[1]);
303 MUL(bn, a[1], b[0]);
304 OUT(output, tn, bn);
305
306 /*
307 * Exact division means we require the result to be an integer.
308 */
309 return (output[1] == 1);
310}
311
312static int max_p10(int n, int *p10_r)
313{
314 /*
315 * Find the smallest power of ten strictly greater than n.
316 *
317 * Special case: we must return at least 10, even if n is
318 * zero. (This is because this function is used for finding
319 * the power of ten by which to multiply a number being
320 * concatenated to the front of n, and concatenating 1 to 0
321 * should yield 10 and not 1.)
322 */
323 int p10 = 10;
324 while (p10 <= (INT_MAX/10) && p10 <= n)
325 p10 *= 10;
326 if (p10 > INT_MAX/10)
327 return FALSE; /* integer overflow */
328 *p10_r = p10;
329 return TRUE;
330}
331
332static int perform_concat(int *a, int *b, int *output)
333{
334 int t1, t2, p10;
335
336 /*
337 * We can't concatenate anything which isn't a non-negative
338 * integer.
339 */
340 if (a[1] != 1 || b[1] != 1 || a[0] < 0 || b[0] < 0)
341 return FALSE;
342
343 /*
344 * For concatenation, we can safely assume leading zeroes
345 * aren't an issue. It isn't clear whether they `should' be
346 * allowed, but it turns out not to matter: concatenating a
347 * leading zero on to a number in order to harmlessly get rid
348 * of the zero is never necessary because unwanted zeroes can
349 * be disposed of by adding them to something instead. So we
350 * disallow them always.
351 *
352 * The only other possibility is that you might want to
353 * concatenate a leading zero on to something and then
354 * concatenate another non-zero digit on to _that_ (to make,
355 * for example, 106); but that's also unnecessary, because you
356 * can make 106 just as easily by concatenating the 0 on to the
357 * _end_ of the 1 first.
358 */
359 if (a[0] == 0)
360 return FALSE;
361
362 if (!max_p10(b[0], &p10)) return FALSE;
363
364 MUL(t1, p10, a[0]);
365 ADD(t2, t1, b[0]);
366 OUT(output, t2, 1);
367 return TRUE;
368}
369
370#define IPOW(ret, x, y) do { \
371 int ipow_limit = (y); \
372 if ((x) == 1 || (x) == 0) ipow_limit = 1; \
373 else if ((x) == -1) ipow_limit &= 1; \
374 (ret) = 1; \
375 while (ipow_limit-- > 0) { \
376 int tmp; \
377 MUL(tmp, ret, x); \
378 ret = tmp; \
379 } \
380} while (0)
381
382static int perform_exp(int *a, int *b, int *output)
383{
384 int an, ad, xn, xd;
385
386 /*
387 * Exponentiation is permitted if the result is rational. This
388 * means that:
389 *
390 * - first we see whether we can take the (denominator-of-b)th
391 * root of a and get a rational; if not, we give up.
392 *
393 * - then we do take that root of a
394 *
395 * - then we multiply by itself (numerator-of-b) times.
396 */
397 if (b[1] > 1) {
398 an = (int)(0.5 + pow(a[0], 1.0/b[1]));
399 ad = (int)(0.5 + pow(a[1], 1.0/b[1]));
400 IPOW(xn, an, b[1]);
401 IPOW(xd, ad, b[1]);
402 if (xn != a[0] || xd != a[1])
403 return FALSE;
404 } else {
405 an = a[0];
406 ad = a[1];
407 }
408 if (b[0] >= 0) {
409 IPOW(xn, an, b[0]);
410 IPOW(xd, ad, b[0]);
411 } else {
412 IPOW(xd, an, -b[0]);
413 IPOW(xn, ad, -b[0]);
414 }
415 if (xd == 0)
416 return FALSE;
417
418 OUT(output, xn, xd);
419 return TRUE;
420}
421
422static int perform_factorial(int *a, int *b, int *output)
423{
424 int ret, t, i;
425
426 /*
427 * Factorials of non-negative integers are permitted.
428 */
429 if (a[1] != 1 || a[0] < 0)
430 return FALSE;
431
432 /*
433 * However, a special case: we don't take a factorial of
434 * anything which would thereby remain the same.
435 */
436 if (a[0] == 1 || a[0] == 2)
437 return FALSE;
438
439 ret = 1;
440 for (i = 1; i <= a[0]; i++) {
441 MUL(t, ret, i);
442 ret = t;
443 }
444
445 OUT(output, ret, 1);
446 return TRUE;
447}
448
449static int perform_decimal(int *a, int *b, int *output)
450{
451 int p10;
452
453 /*
454 * Add a decimal digit to the front of a number;
455 * fail if it's not an integer.
456 * So, 1 --> 0.1, 15 --> 0.15,
457 * or, rather, 1 --> 1/10, 15 --> 15/100,
458 * x --> x / (smallest power of 10 > than x)
459 *
460 */
461 if (a[1] != 1) return FALSE;
462
463 if (!max_p10(a[0], &p10)) return FALSE;
464
465 OUT(output, a[0], p10);
466 return TRUE;
467}
468
469static int perform_recur(int *a, int *b, int *output)
470{
471 int p10, tn, bn;
472
473 /*
474 * This converts a number like .4 to .44444..., or .45 to .45454...
475 * The input number must be -1 < a < 1.
476 *
477 * Calculate the smallest power of 10 that divides the denominator exactly,
478 * returning if no such power of 10 exists. Then multiply the numerator
479 * up accordingly, and the new denominator becomes that power of 10 - 1.
480 */
481 if (abs(a[0]) >= abs(a[1])) return FALSE; /* -1 < a < 1 */
482
483 p10 = 10;
484 while (p10 <= (INT_MAX/10)) {
485 if ((a[1] <= p10) && (p10 % a[1]) == 0) goto found;
486 p10 *= 10;
487 }
488 return FALSE;
489found:
490 tn = a[0] * (p10 / a[1]);
491 bn = p10 - 1;
492
493 OUT(output, tn, bn);
494 return TRUE;
495}
496
497static int perform_root(int *a, int *b, int *output)
498{
499 /*
500 * A root B is: 1 iff a == 0
501 * B ^ (1/A) otherwise
502 */
503 int ainv[2], res;
504
505 if (a[0] == 0) {
506 OUT(output, 1, 1);
507 return TRUE;
508 }
509
510 OUT(ainv, a[1], a[0]);
511 res = perform_exp(b, ainv, output);
512 return res;
513}
514
515static int perform_perc(int *a, int *b, int *output)
516{
517 if (a[0] == 0) return FALSE; /* 0% = 0, uninteresting. */
518 if (a[1] > (INT_MAX/100)) return FALSE;
519
520 OUT(output, a[0], a[1]*100);
521 return TRUE;
522}
523
524static int perform_gamma(int *a, int *b, int *output)
525{
526 int asub1[2];
527
528 /*
529 * gamma(a) = (a-1)!
530 *
531 * special case not caught by perform_fact: gamma(1) is 1 so
532 * don't bother.
533 */
534 if (a[0] == 1 && a[1] == 1) return FALSE;
535
536 OUT(asub1, a[0]-a[1], a[1]);
537 return perform_factorial(asub1, b, output);
538}
539
540static int perform_sqrt(int *a, int *b, int *output)
541{
542 int half[2] = { 1, 2 };
543
544 /*
545 * sqrt(0) == 0, sqrt(1) == 1: don't perform unary noops.
546 */
547 if (a[0] == 0 || (a[0] == 1 && a[1] == 1)) return FALSE;
548
549 return perform_exp(a, half, output);
550}
551
552const static struct operation op_add = {
553 TRUE, "+", "+", 0, 10, 0, TRUE, perform_add
554};
555const static struct operation op_sub = {
556 TRUE, "-", "-", 0, 10, 2, FALSE, perform_sub
557};
558const static struct operation op_mul = {
559 TRUE, "*", "*", 0, 20, 0, TRUE, perform_mul
560};
561const static struct operation op_div = {
562 TRUE, "/", "/", 0, 20, 2, FALSE, perform_div
563};
564const static struct operation op_xdiv = {
565 TRUE, "/", "/", 0, 20, 2, FALSE, perform_exact_div
566};
567const static struct operation op_concat = {
568 FALSE, "", "concat", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT,
569 1000, 0, FALSE, perform_concat
570};
571const static struct operation op_exp = {
572 TRUE, "^", "^", 0, 30, 1, FALSE, perform_exp
573};
574const static struct operation op_factorial = {
575 TRUE, "!", "!", OPFLAG_UNARY, 40, 0, FALSE, perform_factorial
576};
577const static struct operation op_decimal = {
578 TRUE, ".", ".", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 50, 0, FALSE, perform_decimal
579};
580const static struct operation op_recur = {
581 TRUE, "...", "recur", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 2, FALSE, perform_recur
582};
583const static struct operation op_root = {
584 TRUE, "v~", "root", 0, 30, 1, FALSE, perform_root
585};
586const static struct operation op_perc = {
587 TRUE, "%", "%", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 1, FALSE, perform_perc
588};
589const static struct operation op_gamma = {
590 TRUE, "gamma", "gamma", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_FN, 1, 3, FALSE, perform_gamma
591};
592const static struct operation op_sqrt = {
593 TRUE, "v~", "sqrt", OPFLAG_UNARY | OPFLAG_UNARYPREFIX, 30, 1, FALSE, perform_sqrt
594};
595
596/*
597 * In Countdown, divisions resulting in fractions are disallowed.
598 * http://www.askoxford.com/wordgames/countdown/rules/
599 */
600const static struct operation *const ops_countdown[] = {
601 &op_add, &op_mul, &op_sub, &op_xdiv, NULL
602};
603const static struct rules rules_countdown = {
604 ops_countdown, FALSE
605};
606
607/*
608 * A slightly different rule set which handles the reasonably well
609 * known puzzle of making 24 using two 3s and two 8s. For this we
610 * need rational rather than integer division.
611 */
612const static struct operation *const ops_3388[] = {
613 &op_add, &op_mul, &op_sub, &op_div, NULL
614};
615const static struct rules rules_3388 = {
616 ops_3388, TRUE
617};
618
619/*
620 * A still more permissive rule set usable for the four-4s problem
621 * and similar things. Permits concatenation.
622 */
623const static struct operation *const ops_four4s[] = {
624 &op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL
625};
626const static struct rules rules_four4s = {
627 ops_four4s, TRUE
628};
629
630/*
631 * The most permissive ruleset I can think of. Permits
632 * exponentiation, and also silly unary operators like factorials.
633 */
634const static struct operation *const ops_anythinggoes[] = {
635 &op_add, &op_mul, &op_sub, &op_div, &op_concat, &op_exp, &op_factorial,
636 &op_decimal, &op_recur, &op_root, &op_perc, &op_gamma, &op_sqrt, NULL
637};
638const static struct rules rules_anythinggoes = {
639 ops_anythinggoes, TRUE
640};
641
642#define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \
643 (long long)(b)[0] * (a)[1] )
644
645static int addtoset(struct set *set, int newnumber[2])
646{
647 int i, j;
648
649 /* Find where we want to insert the new number */
650 for (i = 0; i < set->nnumbers &&
651 ratcmp(set->numbers+2*i, <, newnumber); i++);
652
653 /* Move everything else up */
654 for (j = set->nnumbers; j > i; j--) {
655 set->numbers[2*j] = set->numbers[2*j-2];
656 set->numbers[2*j+1] = set->numbers[2*j-1];
657 }
658
659 /* Insert the new number */
660 set->numbers[2*i] = newnumber[0];
661 set->numbers[2*i+1] = newnumber[1];
662
663 set->nnumbers++;
664
665 return i;
666}
667
668#define ensure(array, size, newlen, type) do { \
669 if ((newlen) > (size)) { \
670 (size) = (newlen) + 512; \
671 (array) = sresize((array), (size), type); \
672 } \
673} while (0)
674
675static int setcmp(void *av, void *bv)
676{
677 struct set *a = (struct set *)av;
678 struct set *b = (struct set *)bv;
679 int i;
680
681 if (a->nnumbers < b->nnumbers)
682 return -1;
683 else if (a->nnumbers > b->nnumbers)
684 return +1;
685
686 if (a->flags < b->flags)
687 return -1;
688 else if (a->flags > b->flags)
689 return +1;
690
691 for (i = 0; i < a->nnumbers; i++) {
692 if (ratcmp(a->numbers+2*i, <, b->numbers+2*i))
693 return -1;
694 else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i))
695 return +1;
696 }
697
698 return 0;
699}
700
701static int outputcmp(void *av, void *bv)
702{
703 struct output *a = (struct output *)av;
704 struct output *b = (struct output *)bv;
705
706 if (a->number < b->number)
707 return -1;
708 else if (a->number > b->number)
709 return +1;
710
711 return 0;
712}
713
714static int outputfindcmp(void *av, void *bv)
715{
716 int *a = (int *)av;
717 struct output *b = (struct output *)bv;
718
719 if (*a < b->number)
720 return -1;
721 else if (*a > b->number)
722 return +1;
723
724 return 0;
725}
726
727static void addset(struct sets *s, struct set *set, int multiple,
728 struct set *prev, int pa, int po, int pb, int pr)
729{
730 struct set *s2;
731 int npaths = (prev ? prev->npaths : 1);
732
733 assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN);
734 s2 = add234(s->settree, set);
735 if (s2 == set) {
736 /*
737 * New set added to the tree.
738 */
739 set->a.prev = prev;
740 set->a.pa = pa;
741 set->a.po = po;
742 set->a.pb = pb;
743 set->a.pr = pr;
744 set->npaths = npaths;
745 s->nsets++;
746 s->nnumbers += 2 * set->nnumbers;
747 set->as = NULL;
748 set->nas = set->assize = 0;
749 } else {
750 /*
751 * Rediscovered an existing set. Update its npaths.
752 */
753 s2->npaths += npaths;
754 /*
755 * And optionally enter it as an additional ancestor.
756 */
757 if (multiple) {
758 if (s2->nas >= s2->assize) {
759 s2->assize = s2->nas * 3 / 2 + 4;
760 s2->as = sresize(s2->as, s2->assize, struct ancestor);
761 }
762 s2->as[s2->nas].prev = prev;
763 s2->as[s2->nas].pa = pa;
764 s2->as[s2->nas].po = po;
765 s2->as[s2->nas].pb = pb;
766 s2->as[s2->nas].pr = pr;
767 s2->nas++;
768 }
769 }
770}
771
772static struct set *newset(struct sets *s, int nnumbers, int flags)
773{
774 struct set *sn;
775
776 ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *);
777 while (s->nsetlists <= s->nsets / SETLISTLEN)
778 s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set);
779 sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN;
780
781 if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN)
782 s->nnumbers = s->nnumberlists * NUMBERLISTLEN;
783 ensure(s->numberlists, s->numberlistsize,
784 s->nnumbers/NUMBERLISTLEN+1, int *);
785 while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN)
786 s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int);
787 sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] +
788 s->nnumbers % NUMBERLISTLEN;
789
790 /*
791 * Start the set off empty.
792 */
793 sn->nnumbers = 0;
794
795 sn->flags = flags;
796
797 return sn;
798}
799
800static int addoutput(struct sets *s, struct set *ss, int index, int *n)
801{
802 struct output *o, *o2;
803
804 /*
805 * Target numbers are always integers.
806 */
807 if (ss->numbers[2*index+1] != 1)
808 return FALSE;
809
810 ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1,
811 struct output *);
812 while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN)
813 s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN,
814 struct output);
815 o = s->outputlists[s->noutputs / OUTPUTLISTLEN] +
816 s->noutputs % OUTPUTLISTLEN;
817
818 o->number = ss->numbers[2*index];
819 o->set = ss;
820 o->index = index;
821 o->npaths = ss->npaths;
822 o2 = add234(s->outputtree, o);
823 if (o2 != o) {
824 o2->npaths += o->npaths;
825 } else {
826 s->noutputs++;
827 }
828 *n = o->number;
829 return TRUE;
830}
831
832static struct sets *do_search(int ninputs, int *inputs,
833 const struct rules *rules, int *target,
834 int debug, int multiple)
835{
836 struct sets *s;
837 struct set *sn;
838 int qpos, i;
839 const struct operation *const *ops = rules->ops;
840
841 s = snew(struct sets);
842 s->setlists = NULL;
843 s->nsets = s->nsetlists = s->setlistsize = 0;
844 s->numberlists = NULL;
845 s->nnumbers = s->nnumberlists = s->numberlistsize = 0;
846 s->outputlists = NULL;
847 s->noutputs = s->noutputlists = s->outputlistsize = 0;
848 s->settree = newtree234(setcmp);
849 s->outputtree = newtree234(outputcmp);
850 s->ops = ops;
851
852 /*
853 * Start with the input set.
854 */
855 sn = newset(s, ninputs, SETFLAG_CONCAT);
856 for (i = 0; i < ninputs; i++) {
857 int newnumber[2];
858 newnumber[0] = inputs[i];
859 newnumber[1] = 1;
860 addtoset(sn, newnumber);
861 }
862 addset(s, sn, multiple, NULL, 0, 0, 0, 0);
863
864 /*
865 * Now perform the breadth-first search: keep looping over sets
866 * until we run out of steam.
867 */
868 qpos = 0;
869 while (qpos < s->nsets) {
870 struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN;
871 struct set *sn;
872 int i, j, k, m;
873
874 if (debug) {
875 int i;
876 printf("processing set:");
877 for (i = 0; i < ss->nnumbers; i++) {
878 printf(" %d", ss->numbers[2*i]);
879 if (ss->numbers[2*i+1] != 1)
880 printf("/%d", ss->numbers[2*i+1]);
881 }
882 printf("\n");
883 }
884
885 /*
886 * Record all the valid output numbers in this state. We
887 * can always do this if there's only one number in the
888 * state; otherwise, we can only do it if we aren't
889 * required to use all the numbers in coming to our answer.
890 */
891 if (ss->nnumbers == 1 || !rules->use_all) {
892 for (i = 0; i < ss->nnumbers; i++) {
893 int n;
894
895 if (addoutput(s, ss, i, &n) && target && n == *target)
896 return s;
897 }
898 }
899
900 /*
901 * Try every possible operation from this state.
902 */
903 for (k = 0; ops[k] && ops[k]->perform; k++) {
904 if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) &&
905 !(ss->flags & SETFLAG_CONCAT))
906 continue; /* can't use this operation here */
907 for (i = 0; i < ss->nnumbers; i++) {
908 int jlimit = (ops[k]->flags & OPFLAG_UNARY ? 1 : ss->nnumbers);
909 for (j = 0; j < jlimit; j++) {
910 int n[2], newnn = ss->nnumbers;
911 int pa, po, pb, pr;
912
913 if (!(ops[k]->flags & OPFLAG_UNARY)) {
914 if (i == j)
915 continue; /* can't combine a number with itself */
916 if (i > j && ops[k]->commutes)
917 continue; /* no need to do this both ways round */
918 newnn--;
919 }
920 if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n))
921 continue; /* operation failed */
922
923 sn = newset(s, newnn, ss->flags);
924
925 if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT))
926 sn->flags &= ~SETFLAG_CONCAT;
927
928 for (m = 0; m < ss->nnumbers; m++) {
929 if (m == i || (!(ops[k]->flags & OPFLAG_UNARY) &&
930 m == j))
931 continue;
932 sn->numbers[2*sn->nnumbers] = ss->numbers[2*m];
933 sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1];
934 sn->nnumbers++;
935 }
936 pa = i;
937 if (ops[k]->flags & OPFLAG_UNARY)
938 pb = sn->nnumbers+10;
939 else
940 pb = j;
941 po = k;
942 pr = addtoset(sn, n);
943 addset(s, sn, multiple, ss, pa, po, pb, pr);
944 if (debug) {
945 int i;
946 if (ops[k]->flags & OPFLAG_UNARYPREFIX)
947 printf(" %s %d ->", ops[po]->dbgtext, pa);
948 else if (ops[k]->flags & OPFLAG_UNARY)
949 printf(" %d %s ->", pa, ops[po]->dbgtext);
950 else
951 printf(" %d %s %d ->", pa, ops[po]->dbgtext, pb);
952 for (i = 0; i < sn->nnumbers; i++) {
953 printf(" %d", sn->numbers[2*i]);
954 if (sn->numbers[2*i+1] != 1)
955 printf("/%d", sn->numbers[2*i+1]);
956 }
957 printf("\n");
958 }
959 }
960 }
961 }
962
963 qpos++;
964 }
965
966 return s;
967}
968
969static void free_sets(struct sets *s)
970{
971 int i;
972
973 freetree234(s->settree);
974 freetree234(s->outputtree);
975 for (i = 0; i < s->nsetlists; i++)
976 sfree(s->setlists[i]);
977 sfree(s->setlists);
978 for (i = 0; i < s->nnumberlists; i++)
979 sfree(s->numberlists[i]);
980 sfree(s->numberlists);
981 for (i = 0; i < s->noutputlists; i++)
982 sfree(s->outputlists[i]);
983 sfree(s->outputlists);
984 sfree(s);
985}
986
987/*
988 * Print a text formula for producing a given output.
989 */
990void print_recurse(struct sets *s, struct set *ss, int pathindex, int index,
991 int priority, int assoc, int child);
992void print_recurse_inner(struct sets *s, struct set *ss,
993 struct ancestor *a, int pathindex, int index,
994 int priority, int assoc, int child)
995{
996 if (a->prev && index != a->pr) {
997 int pi;
998
999 /*
1000 * This number was passed straight down from this set's
1001 * predecessor. Find its index in the previous set and
1002 * recurse to there.
1003 */
1004 pi = index;
1005 assert(pi != a->pr);
1006 if (pi > a->pr)
1007 pi--;
1008 if (pi >= min(a->pa, a->pb)) {
1009 pi++;
1010 if (pi >= max(a->pa, a->pb))
1011 pi++;
1012 }
1013 print_recurse(s, a->prev, pathindex, pi, priority, assoc, child);
1014 } else if (a->prev && index == a->pr &&
1015 s->ops[a->po]->display) {
1016 /*
1017 * This number was created by a displayed operator in the
1018 * transition from this set to its predecessor. Hence we
1019 * write an open paren, then recurse into the first
1020 * operand, then write the operator, then the second
1021 * operand, and finally close the paren.
1022 */
1023 char *op;
1024 int parens, thispri, thisassoc;
1025
1026 /*
1027 * Determine whether we need parentheses.
1028 */
1029 thispri = s->ops[a->po]->priority;
1030 thisassoc = s->ops[a->po]->assoc;
1031 parens = (thispri < priority ||
1032 (thispri == priority && (assoc & child)));
1033
1034 if (parens)
1035 putchar('(');
1036
1037 if (s->ops[a->po]->flags & OPFLAG_UNARYPREFIX)
1038 for (op = s->ops[a->po]->text; *op; op++)
1039 putchar(*op);
1040
1041 if (s->ops[a->po]->flags & OPFLAG_FN)
1042 putchar('(');
1043
1044 print_recurse(s, a->prev, pathindex, a->pa, thispri, thisassoc, 1);
1045
1046 if (s->ops[a->po]->flags & OPFLAG_FN)
1047 putchar(')');
1048
1049 if (!(s->ops[a->po]->flags & OPFLAG_UNARYPREFIX))
1050 for (op = s->ops[a->po]->text; *op; op++)
1051 putchar(*op);
1052
1053 if (!(s->ops[a->po]->flags & OPFLAG_UNARY))
1054 print_recurse(s, a->prev, pathindex, a->pb, thispri, thisassoc, 2);
1055
1056 if (parens)
1057 putchar(')');
1058 } else {
1059 /*
1060 * This number is either an original, or something formed
1061 * by a non-displayed operator (concatenation). Either way,
1062 * we display it as is.
1063 */
1064 printf("%d", ss->numbers[2*index]);
1065 if (ss->numbers[2*index+1] != 1)
1066 printf("/%d", ss->numbers[2*index+1]);
1067 }
1068}
1069void print_recurse(struct sets *s, struct set *ss, int pathindex, int index,
1070 int priority, int assoc, int child)
1071{
1072 if (!ss->a.prev || pathindex < ss->a.prev->npaths) {
1073 print_recurse_inner(s, ss, &ss->a, pathindex,
1074 index, priority, assoc, child);
1075 } else {
1076 int i;
1077 pathindex -= ss->a.prev->npaths;
1078 for (i = 0; i < ss->nas; i++) {
1079 if (pathindex < ss->as[i].prev->npaths) {
1080 print_recurse_inner(s, ss, &ss->as[i], pathindex,
1081 index, priority, assoc, child);
1082 break;
1083 }
1084 pathindex -= ss->as[i].prev->npaths;
1085 }
1086 }
1087}
1088void print(int pathindex, struct sets *s, struct output *o)
1089{
1090 print_recurse(s, o->set, pathindex, o->index, 0, 0, 0);
1091}
1092
1093/*
1094 * gcc -g -O0 -o numgame numgame.c -I.. ../{malloc,tree234,nullfe}.c -lm
1095 */
1096int main(int argc, char **argv)
1097{
1098 int doing_opts = TRUE;
1099 const struct rules *rules = NULL;
1100 char *pname = argv[0];
1101 int got_target = FALSE, target = 0;
1102 int numbers[10], nnumbers = 0;
1103 int verbose = FALSE;
1104 int pathcounts = FALSE;
1105 int multiple = FALSE;
1106 int debug_bfs = FALSE;
1107 int got_range = FALSE, rangemin = 0, rangemax = 0;
1108
1109 struct output *o;
1110 struct sets *s;
1111 int i, start, limit;
1112
1113 while (--argc) {
1114 char *p = *++argv;
1115 int c;
1116
1117 if (doing_opts && *p == '-') {
1118 p++;
1119
1120 if (!strcmp(p, "-")) {
1121 doing_opts = FALSE;
1122 continue;
1123 } else if (*p == '-') {
1124 p++;
1125 if (!strcmp(p, "debug-bfs")) {
1126 debug_bfs = TRUE;
1127 } else {
1128 fprintf(stderr, "%s: option '--%s' not recognised\n",
1129 pname, p);
1130 }
1131 } else while (p && *p) switch (c = *p++) {
1132 case 'C':
1133 rules = &rules_countdown;
1134 break;
1135 case 'B':
1136 rules = &rules_3388;
1137 break;
1138 case 'D':
1139 rules = &rules_four4s;
1140 break;
1141 case 'A':
1142 rules = &rules_anythinggoes;
1143 break;
1144 case 'v':
1145 verbose = TRUE;
1146 break;
1147 case 'p':
1148 pathcounts = TRUE;
1149 break;
1150 case 'm':
1151 multiple = TRUE;
1152 break;
1153 case 't':
1154 case 'r':
1155 {
1156 char *v;
1157 if (*p) {
1158 v = p;
1159 p = NULL;
1160 } else if (--argc) {
1161 v = *++argv;
1162 } else {
1163 fprintf(stderr, "%s: option '-%c' expects an"
1164 " argument\n", pname, c);
1165 return 1;
1166 }
1167 switch (c) {
1168 case 't':
1169 got_target = TRUE;
1170 target = atoi(v);
1171 break;
1172 case 'r':
1173 {
1174 char *sep = strchr(v, '-');
1175 got_range = TRUE;
1176 if (sep) {
1177 rangemin = atoi(v);
1178 rangemax = atoi(sep+1);
1179 } else {
1180 rangemin = 0;
1181 rangemax = atoi(v);
1182 }
1183 }
1184 break;
1185 }
1186 }
1187 break;
1188 default:
1189 fprintf(stderr, "%s: option '-%c' not"
1190 " recognised\n", pname, c);
1191 return 1;
1192 }
1193 } else {
1194 if (nnumbers >= lenof(numbers)) {
1195 fprintf(stderr, "%s: internal limit of %d numbers exceeded\n",
1196 pname, (int)lenof(numbers));
1197 return 1;
1198 } else {
1199 numbers[nnumbers++] = atoi(p);
1200 }
1201 }
1202 }
1203
1204 if (!rules) {
1205 fprintf(stderr, "%s: no rule set specified; use -C,-B,-D,-A\n", pname);
1206 return 1;
1207 }
1208
1209 if (!nnumbers) {
1210 fprintf(stderr, "%s: no input numbers specified\n", pname);
1211 return 1;
1212 }
1213
1214 if (got_range) {
1215 if (got_target) {
1216 fprintf(stderr, "%s: only one of -t and -r may be specified\n", pname);
1217 return 1;
1218 }
1219 if (rangemin >= rangemax) {
1220 fprintf(stderr, "%s: range not sensible (%d - %d)\n", pname, rangemin, rangemax);
1221 return 1;
1222 }
1223 }
1224
1225 s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL),
1226 debug_bfs, multiple);
1227
1228 if (got_target) {
1229 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1230 REL234_LE, &start);
1231 if (!o)
1232 start = -1;
1233 o = findrelpos234(s->outputtree, &target, outputfindcmp,
1234 REL234_GE, &limit);
1235 if (!o)
1236 limit = -1;
1237 assert(start != -1 || limit != -1);
1238 if (start == -1)
1239 start = limit;
1240 else if (limit == -1)
1241 limit = start;
1242 limit++;
1243 } else if (got_range) {
1244 if (!findrelpos234(s->outputtree, &rangemin, outputfindcmp,
1245 REL234_GE, &start) ||
1246 !findrelpos234(s->outputtree, &rangemax, outputfindcmp,
1247 REL234_LE, &limit)) {
1248 printf("No solutions available in specified range %d-%d\n", rangemin, rangemax);
1249 return 1;
1250 }
1251 limit++;
1252 } else {
1253 start = 0;
1254 limit = count234(s->outputtree);
1255 }
1256
1257 for (i = start; i < limit; i++) {
1258 char buf[256];
1259
1260 o = index234(s->outputtree, i);
1261
1262 sprintf(buf, "%d", o->number);
1263
1264 if (pathcounts)
1265 sprintf(buf + strlen(buf), " [%d]", o->npaths);
1266
1267 if (got_target || verbose) {
1268 int j, npaths;
1269
1270 if (multiple)
1271 npaths = o->npaths;
1272 else
1273 npaths = 1;
1274
1275 for (j = 0; j < npaths; j++) {
1276 printf("%s = ", buf);
1277 print(j, s, o);
1278 putchar('\n');
1279 }
1280 } else {
1281 printf("%s\n", buf);
1282 }
1283 }
1284
1285 free_sets(s);
1286
1287 return 0;
1288}
1289
1290/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/unfinished/path.c b/apps/plugins/puzzles/src/unfinished/path.c
new file mode 100644
index 0000000000..61d6c61c6a
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/path.c
@@ -0,0 +1,786 @@
1/*
2 * Experimental grid generator for Nikoli's `Number Link' puzzle.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8#include "puzzles.h"
9
10/*
11 * 2005-07-08: This is currently a Path grid generator which will
12 * construct valid grids at a plausible speed. However, the grids
13 * are not of suitable quality to be used directly as puzzles.
14 *
15 * The basic strategy is to start with an empty grid, and
16 * repeatedly either (a) add a new path to it, or (b) extend one
17 * end of a path by one square in some direction and push other
18 * paths into new shapes in the process. The effect of this is that
19 * we are able to construct a set of paths which between them fill
20 * the entire grid.
21 *
22 * Quality issues: if we set the main loop to do (a) where possible
23 * and (b) only where necessary, we end up with a grid containing a
24 * few too many small paths, which therefore doesn't make for an
25 * interesting puzzle. If we reverse the priority so that we do (b)
26 * where possible and (a) only where necessary, we end up with some
27 * staggeringly interwoven grids with very very few separate paths,
28 * but the result of this is that there's invariably a solution
29 * other than the intended one which leaves many grid squares
30 * unfilled. There's also a separate problem which is that many
31 * grids have really boring and obvious paths in them, such as the
32 * entire bottom row of the grid being taken up by a single path.
33 *
34 * It's not impossible that a few tweaks might eliminate or reduce
35 * the incidence of boring paths, and might also find a happy
36 * medium between too many and too few. There remains the question
37 * of unique solutions, however. I fear there is no alternative but
38 * to write - somehow! - a solver.
39 *
40 * While I'm here, some notes on UI strategy for the parts of the
41 * puzzle implementation that _aren't_ the generator:
42 *
43 * - data model is to track connections between adjacent squares,
44 * so that you aren't limited to extending a path out from each
45 * number but can also mark sections of path which you know
46 * _will_ come in handy later.
47 *
48 * - user interface is to click in one square and drag to an
49 * adjacent one, thus creating a link between them. We can
50 * probably tolerate rapid mouse motion causing a drag directly
51 * to a square which is a rook move away, but any other rapid
52 * motion is ambiguous and probably the best option is to wait
53 * until the mouse returns to a square we know how to reach.
54 *
55 * - a drag causing the current path to backtrack has the effect
56 * of removing bits of it.
57 *
58 * - the UI should enforce at all times the constraint that at
59 * most two links can come into any square.
60 *
61 * - my Cunning Plan for actually implementing this: the game_ui
62 * contains a grid-sized array, which is copied from the current
63 * game_state on starting a drag. While a drag is active, the
64 * contents of the game_ui is adjusted with every mouse motion,
65 * and is displayed _in place_ of the game_state itself. On
66 * termination of a drag, the game_ui array is copied back into
67 * the new game_state (or rather, a string move is encoded which
68 * has precisely the set of link changes to cause that effect).
69 */
70
71/*
72 * Standard notation for directions.
73 */
74#define L 0
75#define U 1
76#define R 2
77#define D 3
78#define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0)
79#define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0)
80
81/*
82 * Perform a breadth-first search over a grid of squares with the
83 * colour of square (X,Y) given by grid[Y*w+X]. The search begins
84 * at (x,y), and finds all squares which are the same colour as
85 * (x,y) and reachable from it by orthogonal moves. On return:
86 * - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if
87 * unreachable or a different colour
88 * - the returned value is the number of reachable squares,
89 * including (x,y) itself
90 * - list[0] up to list[returned value - 1] list those squares, in
91 * increasing order of distance from (x,y) (and in arbitrary
92 * order within that).
93 */
94static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list)
95{
96 int i, j, c, listsize, listdone;
97
98 /*
99 * Start by clearing the output arrays.
100 */
101 for (i = 0; i < w*h; i++)
102 dist[i] = list[i] = -1;
103
104 /*
105 * Set up the initial list.
106 */
107 listsize = 1;
108 listdone = 0;
109 list[0] = y*w+x;
110 dist[y*w+x] = 0;
111 c = grid[y*w+x];
112
113 /*
114 * Repeatedly process a square and add any extra squares to the
115 * end of list.
116 */
117 while (listdone < listsize) {
118 i = list[listdone++];
119 y = i / w;
120 x = i % w;
121 for (j = 0; j < 4; j++) {
122 int xx, yy, ii;
123
124 xx = x + DX(j);
125 yy = y + DY(j);
126 ii = yy*w+xx;
127
128 if (xx >= 0 && xx < w && yy >= 0 && yy < h &&
129 grid[ii] == c && dist[ii] == -1) {
130 dist[ii] = dist[i] + 1;
131 assert(listsize < w*h);
132 list[listsize++] = ii;
133 }
134 }
135 }
136
137 return listsize;
138}
139
140struct genctx {
141 int w, h;
142 int *grid, *sparegrid, *sparegrid2, *sparegrid3;
143 int *dist, *list;
144
145 int npaths, pathsize;
146 int *pathends, *sparepathends; /* 2*npaths entries */
147 int *pathspare; /* npaths entries */
148 int *extends; /* 8*npaths entries */
149};
150
151static struct genctx *new_genctx(int w, int h)
152{
153 struct genctx *ctx = snew(struct genctx);
154 ctx->w = w;
155 ctx->h = h;
156 ctx->grid = snewn(w * h, int);
157 ctx->sparegrid = snewn(w * h, int);
158 ctx->sparegrid2 = snewn(w * h, int);
159 ctx->sparegrid3 = snewn(w * h, int);
160 ctx->dist = snewn(w * h, int);
161 ctx->list = snewn(w * h, int);
162 ctx->npaths = ctx->pathsize = 0;
163 ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL;
164 return ctx;
165}
166
167static void free_genctx(struct genctx *ctx)
168{
169 sfree(ctx->grid);
170 sfree(ctx->sparegrid);
171 sfree(ctx->sparegrid2);
172 sfree(ctx->sparegrid3);
173 sfree(ctx->dist);
174 sfree(ctx->list);
175 sfree(ctx->pathends);
176 sfree(ctx->sparepathends);
177 sfree(ctx->pathspare);
178 sfree(ctx->extends);
179}
180
181static int newpath(struct genctx *ctx)
182{
183 int n;
184
185 n = ctx->npaths++;
186 if (ctx->npaths > ctx->pathsize) {
187 ctx->pathsize += 16;
188 ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int);
189 ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int);
190 ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int);
191 ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int);
192 }
193 return n;
194}
195
196static int is_endpoint(struct genctx *ctx, int x, int y)
197{
198 int w = ctx->w, h = ctx->h, c;
199
200 assert(x >= 0 && x < w && y >= 0 && y < h);
201
202 c = ctx->grid[y*w+x];
203 if (c < 0)
204 return FALSE; /* empty square is not an endpoint! */
205 assert(c >= 0 && c < ctx->npaths);
206 if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x)
207 return TRUE;
208 return FALSE;
209}
210
211/*
212 * Tries to extend a path by one square in the given direction,
213 * pushing other paths around if necessary. Returns TRUE on success
214 * or FALSE on failure.
215 */
216static int extend_path(struct genctx *ctx, int path, int end, int direction)
217{
218 int w = ctx->w, h = ctx->h;
219 int x, y, xe, ye, cut;
220 int i, j, jp, n, first, last;
221
222 assert(path >= 0 && path < ctx->npaths);
223 assert(end == 0 || end == 1);
224
225 /*
226 * Find the endpoint of the path and the point we plan to
227 * extend it into.
228 */
229 y = ctx->pathends[path * 2 + end] / w;
230 x = ctx->pathends[path * 2 + end] % w;
231 assert(x >= 0 && x < w && y >= 0 && y < h);
232
233 xe = x + DX(direction);
234 ye = y + DY(direction);
235 if (xe < 0 || xe >= w || ye < 0 || ye >= h)
236 return FALSE; /* could not extend in this direction */
237
238 /*
239 * We don't extend paths _directly_ into endpoints of other
240 * paths, although we don't mind too much if a knock-on effect
241 * of an extension is to push part of another path into a third
242 * path's endpoint.
243 */
244 if (is_endpoint(ctx, xe, ye))
245 return FALSE;
246
247 /*
248 * We can't extend a path back the way it came.
249 */
250 if (ctx->grid[ye*w+xe] == path)
251 return FALSE;
252
253 /*
254 * Paths may not double back on themselves. Check if the new
255 * point is adjacent to any point of this path other than (x,y).
256 */
257 for (j = 0; j < 4; j++) {
258 int xf, yf;
259
260 xf = xe + DX(j);
261 yf = ye + DY(j);
262
263 if (xf >= 0 && xf < w && yf >= 0 && yf < h &&
264 (xf != x || yf != y) && ctx->grid[yf*w+xf] == path)
265 return FALSE;
266 }
267
268 /*
269 * Now we're convinced it's valid to _attempt_ the extension.
270 * It may still fail if we run out of space to push other paths
271 * into.
272 *
273 * So now we can set up our temporary data structures. We will
274 * need:
275 *
276 * - a spare copy of the grid on which to gradually move paths
277 * around (sparegrid)
278 *
279 * - a second spare copy with which to remember how paths
280 * looked just before being cut (sparegrid2). FIXME: is
281 * sparegrid2 necessary? right now it's never different from
282 * grid itself
283 *
284 * - a third spare copy with which to do the internal
285 * calculations involved in reconstituting a cut path
286 * (sparegrid3)
287 *
288 * - something to track which paths currently need
289 * reconstituting after being cut, and which have already
290 * been cut (pathspare)
291 *
292 * - a spare copy of pathends to store the altered states in
293 * (sparepathends)
294 */
295 memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int));
296 memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int));
297 memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int));
298 for (i = 0; i < ctx->npaths; i++)
299 ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */
300
301 /*
302 * Working in sparegrid, actually extend the path. If it cuts
303 * another, begin a loop in which we restore any cut path by
304 * moving it out of the way.
305 */
306 cut = ctx->sparegrid[ye*w+xe];
307 ctx->sparegrid[ye*w+xe] = path;
308 ctx->sparepathends[path*2+end] = ye*w+xe;
309 ctx->pathspare[path] = 2; /* this one is sacrosanct */
310 if (cut >= 0) {
311 assert(cut >= 0 && cut < ctx->npaths);
312 ctx->pathspare[cut] = 1; /* broken */
313
314 while (1) {
315 for (i = 0; i < ctx->npaths; i++)
316 if (ctx->pathspare[i] == 1)
317 break;
318 if (i == ctx->npaths)
319 break; /* we're done */
320
321 /*
322 * Path i needs restoring. So walk along its original
323 * track (as given in sparegrid2) and see where it's
324 * been cut. Where it has, surround the cut points in
325 * the same colour, without overwriting already-fixed
326 * paths.
327 */
328 memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int));
329 n = bfs(w, h, ctx->sparegrid2,
330 ctx->pathends[i*2] % w, ctx->pathends[i*2] / w,
331 ctx->dist, ctx->list);
332 first = last = -1;
333if (ctx->sparegrid3[ctx->pathends[i*2]] != i ||
334 ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return FALSE;/* FIXME */
335 for (j = 0; j < n; j++) {
336 jp = ctx->list[j];
337 assert(ctx->dist[jp] == j);
338 assert(ctx->sparegrid2[jp] == i);
339
340 /*
341 * Wipe out the original path in sparegrid.
342 */
343 if (ctx->sparegrid[jp] == i)
344 ctx->sparegrid[jp] = -1;
345
346 /*
347 * Be prepared to shorten the path at either end if
348 * the endpoints have been stomped on.
349 */
350 if (ctx->sparegrid3[jp] == i) {
351 if (first < 0)
352 first = jp;
353 last = jp;
354 }
355
356 if (ctx->sparegrid3[jp] != i) {
357 int jx = jp % w, jy = jp / w;
358 int dx, dy;
359 for (dy = -1; dy <= +1; dy++)
360 for (dx = -1; dx <= +1; dx++) {
361 int newp, newv;
362 if (!dy && !dx)
363 continue; /* central square */
364 if (jx+dx < 0 || jx+dx >= w ||
365 jy+dy < 0 || jy+dy >= h)
366 continue; /* out of range */
367 newp = (jy+dy)*w+(jx+dx);
368 newv = ctx->sparegrid3[newp];
369 if (newv >= 0 && (newv == i ||
370 ctx->pathspare[newv] == 2))
371 continue; /* can't use this square */
372 ctx->sparegrid3[newp] = i;
373 }
374 }
375 }
376
377 if (first < 0 || last < 0)
378 return FALSE; /* path is completely wiped out! */
379
380 /*
381 * Now we've covered sparegrid3 in possible squares for
382 * the new layout of path i. Find the actual layout
383 * we're going to use by bfs: we want the shortest path
384 * from one endpoint to the other.
385 */
386 n = bfs(w, h, ctx->sparegrid3, first % w, first / w,
387 ctx->dist, ctx->list);
388 if (ctx->dist[last] < 2) {
389 /*
390 * Either there is no way to get between the path's
391 * endpoints, or the remaining endpoints simply
392 * aren't far enough apart to make the path viable
393 * any more. This means the entire push operation
394 * has failed.
395 */
396 return FALSE;
397 }
398
399 /*
400 * Write the new path into sparegrid. Also save the new
401 * endpoint locations, in case they've changed.
402 */
403 jp = last;
404 j = ctx->dist[jp];
405 while (1) {
406 int d;
407
408 if (ctx->sparegrid[jp] >= 0) {
409 if (ctx->pathspare[ctx->sparegrid[jp]] == 2)
410 return FALSE; /* somehow we've hit a fixed path */
411 ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */
412 }
413 ctx->sparegrid[jp] = i;
414
415 if (j == 0)
416 break;
417
418 /*
419 * Now look at the neighbours of jp to find one
420 * which has dist[] one less.
421 */
422 for (d = 0; d < 4; d++) {
423 int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d);
424 if (jx >= 0 && jx < w && jy >= 0 && jy < w &&
425 ctx->dist[jy*w+jx] == j-1) {
426 jp = jy*w+jx;
427 j--;
428 break;
429 }
430 }
431 assert(d < 4);
432 }
433
434 ctx->sparepathends[i*2] = first;
435 ctx->sparepathends[i*2+1] = last;
436//printf("new ends of path %d: %d,%d\n", i, first, last);
437 ctx->pathspare[i] = 2; /* fixed */
438 }
439 }
440
441 /*
442 * If we got here, the extension was successful!
443 */
444 memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int));
445 memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int));
446 return TRUE;
447}
448
449/*
450 * Tries to add a new path to the grid.
451 */
452static int add_path(struct genctx *ctx, random_state *rs)
453{
454 int w = ctx->w, h = ctx->h;
455 int i, ii, n;
456
457 /*
458 * Our strategy is:
459 * - randomly choose an empty square in the grid
460 * - do a BFS from that point to find a long path starting
461 * from it
462 * - if we run out of viable empty squares, return failure.
463 */
464
465 /*
466 * Use `sparegrid' to collect a list of empty squares.
467 */
468 n = 0;
469 for (i = 0; i < w*h; i++)
470 if (ctx->grid[i] == -1)
471 ctx->sparegrid[n++] = i;
472
473 /*
474 * Shuffle the grid.
475 */
476 for (i = n; i-- > 1 ;) {
477 int k = random_upto(rs, i+1);
478 if (k != i) {
479 int t = ctx->sparegrid[i];
480 ctx->sparegrid[i] = ctx->sparegrid[k];
481 ctx->sparegrid[k] = t;
482 }
483 }
484
485 /*
486 * Loop over it trying to add paths. This looks like a
487 * horrifying N^4 algorithm (that is, (w*h)^2), but I predict
488 * that in fact the worst case will very rarely arise because
489 * when there's lots of grid space an attempt will succeed very
490 * quickly.
491 */
492 for (ii = 0; ii < n; ii++) {
493 int i = ctx->sparegrid[ii];
494 int y = i / w, x = i % w, nsq;
495 int r, c, j;
496
497 /*
498 * BFS from here to find long paths.
499 */
500 nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list);
501
502 /*
503 * If there aren't any long enough, give up immediately.
504 */
505 assert(nsq > 0); /* must be the start square at least! */
506 if (ctx->dist[ctx->list[nsq-1]] < 3)
507 continue;
508
509 /*
510 * Find the first viable endpoint in ctx->list (i.e. the
511 * first point with distance at least three). I could
512 * binary-search for this, but that would be O(log N)
513 * whereas in fact I can get a constant time bound by just
514 * searching up from the start - after all, there can be at
515 * most 13 points at _less_ than distance 3 from the
516 * starting one!
517 */
518 for (j = 0; j < nsq; j++)
519 if (ctx->dist[ctx->list[j]] >= 3)
520 break;
521 assert(j < nsq); /* we tested above that there was one */
522
523 /*
524 * Now we know that any element of `list' between j and nsq
525 * would be valid in principle. However, we want a few long
526 * paths rather than many small ones, so select only those
527 * elements which are either the maximum length or one
528 * below it.
529 */
530 while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]])
531 j++;
532 r = j + random_upto(rs, nsq - j);
533 j = ctx->list[r];
534
535 /*
536 * And that's our endpoint. Mark the new path on the grid.
537 */
538 c = newpath(ctx);
539 ctx->pathends[c*2] = i;
540 ctx->pathends[c*2+1] = j;
541 ctx->grid[j] = c;
542 while (j != i) {
543 int d, np, index, pts[4];
544 np = 0;
545 for (d = 0; d < 4; d++) {
546 int xn = (j % w) + DX(d), yn = (j / w) + DY(d);
547 if (xn >= 0 && xn < w && yn >= 0 && yn < w &&
548 ctx->dist[yn*w+xn] == ctx->dist[j] - 1)
549 pts[np++] = yn*w+xn;
550 }
551 if (np > 1)
552 index = random_upto(rs, np);
553 else
554 index = 0;
555 j = pts[index];
556 ctx->grid[j] = c;
557 }
558
559 return TRUE;
560 }
561
562 return FALSE;
563}
564
565/*
566 * The main grid generation loop.
567 */
568static void gridgen_mainloop(struct genctx *ctx, random_state *rs)
569{
570 int w = ctx->w, h = ctx->h;
571 int i, n;
572
573 /*
574 * The generation algorithm doesn't always converge. Loop round
575 * until it does.
576 */
577 while (1) {
578 for (i = 0; i < w*h; i++)
579 ctx->grid[i] = -1;
580 ctx->npaths = 0;
581
582 while (1) {
583 /*
584 * See if the grid is full.
585 */
586 for (i = 0; i < w*h; i++)
587 if (ctx->grid[i] < 0)
588 break;
589 if (i == w*h)
590 return;
591
592#ifdef GENERATION_DIAGNOSTICS
593 {
594 int x, y;
595 for (y = 0; y < h; y++) {
596 printf("|");
597 for (x = 0; x < w; x++) {
598 if (ctx->grid[y*w+x] >= 0)
599 printf("%2d", ctx->grid[y*w+x]);
600 else
601 printf(" .");
602 }
603 printf(" |\n");
604 }
605 }
606#endif
607 /*
608 * Try adding a path.
609 */
610 if (add_path(ctx, rs)) {
611#ifdef GENERATION_DIAGNOSTICS
612 printf("added path\n");
613#endif
614 continue;
615 }
616
617 /*
618 * Try extending a path. First list all the possible
619 * extensions.
620 */
621 for (i = 0; i < ctx->npaths * 8; i++)
622 ctx->extends[i] = i;
623 n = i;
624
625 /*
626 * Then shuffle the list.
627 */
628 for (i = n; i-- > 1 ;) {
629 int k = random_upto(rs, i+1);
630 if (k != i) {
631 int t = ctx->extends[i];
632 ctx->extends[i] = ctx->extends[k];
633 ctx->extends[k] = t;
634 }
635 }
636
637 /*
638 * Now try each one in turn until one works.
639 */
640 for (i = 0; i < n; i++) {
641 int p, d, e;
642 p = ctx->extends[i];
643 d = p % 4;
644 p /= 4;
645 e = p % 2;
646 p /= 2;
647
648#ifdef GENERATION_DIAGNOSTICS
649 printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e,
650 ctx->pathends[p*2+e] % w,
651 ctx->pathends[p*2+e] / w, d);
652#endif
653 if (extend_path(ctx, p, e, d)) {
654#ifdef GENERATION_DIAGNOSTICS
655 printf("extended path %d end %d (%d,%d) in dir %d\n", p, e,
656 ctx->pathends[p*2+e] % w,
657 ctx->pathends[p*2+e] / w, d);
658#endif
659 break;
660 }
661 }
662
663 if (i < n)
664 continue;
665
666 break;
667 }
668 }
669}
670
671/*
672 * Wrapper function which deals with the boring bits such as
673 * removing the solution from the generated grid, shuffling the
674 * numeric labels and creating/disposing of the context structure.
675 */
676static int *gridgen(int w, int h, random_state *rs)
677{
678 struct genctx *ctx;
679 int *ret;
680 int i;
681
682 ctx = new_genctx(w, h);
683
684 gridgen_mainloop(ctx, rs);
685
686 /*
687 * There is likely to be an ordering bias in the numbers
688 * (longer paths on lower numbers due to there having been more
689 * grid space when laying them down). So we must shuffle the
690 * numbers. We use ctx->pathspare for this.
691 *
692 * This is also as good a time as any to shift to numbering
693 * from 1, for display to the user.
694 */
695 for (i = 0; i < ctx->npaths; i++)
696 ctx->pathspare[i] = i+1;
697 for (i = ctx->npaths; i-- > 1 ;) {
698 int k = random_upto(rs, i+1);
699 if (k != i) {
700 int t = ctx->pathspare[i];
701 ctx->pathspare[i] = ctx->pathspare[k];
702 ctx->pathspare[k] = t;
703 }
704 }
705
706 /* FIXME: remove this at some point! */
707 {
708 int y, x;
709 for (y = 0; y < h; y++) {
710 printf("|");
711 for (x = 0; x < w; x++) {
712 assert(ctx->grid[y*w+x] >= 0);
713 printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]);
714 }
715 printf(" |\n");
716 }
717 printf("\n");
718 }
719
720 /*
721 * Clear the grid, and write in just the endpoints.
722 */
723 for (i = 0; i < w*h; i++)
724 ctx->grid[i] = 0;
725 for (i = 0; i < ctx->npaths; i++) {
726 ctx->grid[ctx->pathends[i*2]] =
727 ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i];
728 }
729
730 ret = ctx->grid;
731 ctx->grid = NULL;
732
733 free_genctx(ctx);
734
735 return ret;
736}
737
738#ifdef TEST_GEN
739
740#define TEST_GENERAL
741
742int main(void)
743{
744 int w = 10, h = 8;
745 random_state *rs = random_init("12345", 5);
746 int x, y, i, *grid;
747
748 for (i = 0; i < 10; i++) {
749 grid = gridgen(w, h, rs);
750
751 for (y = 0; y < h; y++) {
752 printf("|");
753 for (x = 0; x < w; x++) {
754 if (grid[y*w+x] > 0)
755 printf("%2d", grid[y*w+x]);
756 else
757 printf(" .");
758 }
759 printf(" |\n");
760 }
761 printf("\n");
762
763 sfree(grid);
764 }
765
766 return 0;
767}
768#endif
769
770#ifdef TEST_GENERAL
771#include <stdarg.h>
772
773void fatal(char *fmt, ...)
774{
775 va_list ap;
776
777 fprintf(stderr, "fatal error: ");
778
779 va_start(ap, fmt);
780 vfprintf(stderr, fmt, ap);
781 va_end(ap);
782
783 fprintf(stderr, "\n");
784 exit(1);
785}
786#endif
diff --git a/apps/plugins/puzzles/src/unfinished/separate.R b/apps/plugins/puzzles/src/unfinished/separate.R
new file mode 100644
index 0000000000..f861c8f4fe
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/separate.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3SEPARATE_EXTRA = divvy dsf
4
5separate : [X] GTK COMMON separate SEPARATE_EXTRA separate-icon|no-icon
6
7separate : [G] WINDOWS COMMON separate SEPARATE_EXTRA separate.res|noicon.res
8
9ALL += separate[COMBINED] SEPARATE_EXTRA
10
11!begin am gtk
12GAMES += separate
13!end
14
15!begin >list.c
16 A(separate) \
17!end
18
19!begin >gamedesc.txt
20separate:separate.exe:Separate:Rectangle-dividing puzzle:Partition the grid into regions containing one of each letter.
21!end
diff --git a/apps/plugins/puzzles/src/unfinished/separate.c b/apps/plugins/puzzles/src/unfinished/separate.c
new file mode 100644
index 0000000000..a7b4fc96e1
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/separate.c
@@ -0,0 +1,859 @@
1/*
2 * separate.c: Implementation of `Block Puzzle', a Japanese-only
3 * Nikoli puzzle seen at
4 * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/
5 *
6 * It's difficult to be absolutely sure of the rules since online
7 * Japanese translators are so bad, but looking at the sample
8 * puzzle it seems fairly clear that the rules of this one are
9 * very simple. You have an mxn grid in which every square
10 * contains a letter, there are k distinct letters with k dividing
11 * mn, and every letter occurs the same number of times; your aim
12 * is to find a partition of the grid into disjoint k-ominoes such
13 * that each k-omino contains exactly one of each letter.
14 *
15 * (It may be that Nikoli always have m,n,k equal to one another.
16 * However, I don't see that that's critical to the puzzle; k|mn
17 * is the only really important constraint, and even that could
18 * probably be dispensed with if some squares were marked as
19 * unused.)
20 */
21
22/*
23 * Current status: only the solver/generator is yet written, and
24 * although working in principle it's _very_ slow. It generates
25 * 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and
26 * 7x7n7 only with a serious strain. I haven't dared try it higher
27 * than that yet.
28 *
29 * One idea to speed it up is to implement more of the solver.
30 * Ideas I've so far had include:
31 *
32 * - Generalise the deduction currently expressed as `an
33 * undersized chain with only one direction to extend must take
34 * it'. More generally, the deduction should say `if all the
35 * possible k-ominoes containing a given chain also contain
36 * square x, then mark square x as part of that k-omino'.
37 * + For example, consider this case:
38 *
39 * a ? b This represents the top left of a board; the letters
40 * ? ? ? a,b,c do not represent the letters used in the puzzle,
41 * c ? ? but indicate that those three squares are known to be
42 * of different ominoes. Now if k >= 4, we can immediately
43 * deduce that the square midway between b and c belongs to the
44 * same omino as a, because there is no way we can make a 4-or-
45 * more-omino containing a which does not also contain that square.
46 * (Most easily seen by imagining cutting that square out of the
47 * grid; then, clearly, the omino containing a has only two
48 * squares to expand into, and needs at least three.)
49 *
50 * The key difficulty with this mode of reasoning is
51 * identifying such squares. I can't immediately think of a
52 * simple algorithm for finding them on a wholesale basis.
53 *
54 * - Bfs out from a chain looking for the letters it lacks. For
55 * example, in this situation (top three rows of a 7x7n7 grid):
56 *
57 * +-----------+-+
58 * |E-A-F-B-C D|D|
59 * +------- ||
60 * |E-C-G-D G|G E|
61 * +-+--- |
62 * |E|E G A B F A|
63 *
64 * In this situation we can be sure that the top left chain
65 * E-A-F-B-C does extend rightwards to the D, because there is
66 * no other D within reach of that chain. Note also that the
67 * bfs can skip squares which are known to belong to other
68 * ominoes than this one.
69 *
70 * (This deduction, I fear, should only be used in an
71 * emergency, because it relies on _all_ squares within range
72 * of the bfs having particular values and so using it during
73 * incremental generation rather nails down a lot of the grid.)
74 *
75 * It's conceivable that another thing we could do would be to
76 * increase the flexibility in the grid generator: instead of
77 * nailing down the _value_ of any square depended on, merely nail
78 * down its equivalence to other squares. Unfortunately this turns
79 * the letter-selection phase of generation into a general graph
80 * colouring problem (we must draw a graph with equivalence
81 * classes of squares as the vertices, and an edge between any two
82 * vertices representing equivalence classes which contain squares
83 * that share an omino, and then k-colour the result) and hence
84 * requires recursion, which bodes ill for something we're doing
85 * that many times per generation.
86 *
87 * I suppose a simple thing I could try would be tuning the retry
88 * count, just in case it's set too high or too low for efficient
89 * generation.
90 */
91
92#include <stdio.h>
93#include <stdlib.h>
94#include <string.h>
95#include <assert.h>
96#include <ctype.h>
97#include <math.h>
98
99#include "puzzles.h"
100
101enum {
102 COL_BACKGROUND,
103 NCOLOURS
104};
105
106struct game_params {
107 int w, h, k;
108};
109
110struct game_state {
111 int FIXME;
112};
113
114static game_params *default_params(void)
115{
116 game_params *ret = snew(game_params);
117
118 ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */
119
120 return ret;
121}
122
123static int game_fetch_preset(int i, char **name, game_params **params)
124{
125 return FALSE;
126}
127
128static void free_params(game_params *params)
129{
130 sfree(params);
131}
132
133static game_params *dup_params(const game_params *params)
134{
135 game_params *ret = snew(game_params);
136 *ret = *params; /* structure copy */
137 return ret;
138}
139
140static void decode_params(game_params *params, char const *string)
141{
142 params->w = params->h = params->k = atoi(string);
143 while (*string && isdigit((unsigned char)*string)) string++;
144 if (*string == 'x') {
145 string++;
146 params->h = atoi(string);
147 while (*string && isdigit((unsigned char)*string)) string++;
148 }
149 if (*string == 'n') {
150 string++;
151 params->k = atoi(string);
152 while (*string && isdigit((unsigned char)*string)) string++;
153 }
154}
155
156static char *encode_params(const game_params *params, int full)
157{
158 char buf[256];
159 sprintf(buf, "%dx%dn%d", params->w, params->h, params->k);
160 return dupstr(buf);
161}
162
163static config_item *game_configure(const game_params *params)
164{
165 return NULL;
166}
167
168static game_params *custom_params(const config_item *cfg)
169{
170 return NULL;
171}
172
173static char *validate_params(const game_params *params, int full)
174{
175 return NULL;
176}
177
178/* ----------------------------------------------------------------------
179 * Solver and generator.
180 */
181
182struct solver_scratch {
183 int w, h, k;
184
185 /*
186 * Tracks connectedness between squares.
187 */
188 int *dsf;
189
190 /*
191 * size[dsf_canonify(dsf, yx)] tracks the size of the
192 * connected component containing yx.
193 */
194 int *size;
195
196 /*
197 * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not
198 * the connected component containing yx includes letter i. If
199 * the value is -1, it doesn't; otherwise its value is the
200 * index in the main grid of the square which contributes that
201 * letter to the component.
202 */
203 int *contents;
204
205 /*
206 * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)]
207 * tracks whether or not the connected components containing
208 * yx1 and yx2 are known to be distinct.
209 */
210 unsigned char *disconnect;
211
212 /*
213 * Temporary space used only inside particular solver loops.
214 */
215 int *tmp;
216};
217
218struct solver_scratch *solver_scratch_new(int w, int h, int k)
219{
220 int wh = w*h;
221 struct solver_scratch *sc = snew(struct solver_scratch);
222
223 sc->w = w;
224 sc->h = h;
225 sc->k = k;
226
227 sc->dsf = snew_dsf(wh);
228 sc->size = snewn(wh, int);
229 sc->contents = snewn(wh * k, int);
230 sc->disconnect = snewn(wh*wh, unsigned char);
231 sc->tmp = snewn(wh, int);
232
233 return sc;
234}
235
236void solver_scratch_free(struct solver_scratch *sc)
237{
238 sfree(sc->dsf);
239 sfree(sc->size);
240 sfree(sc->contents);
241 sfree(sc->disconnect);
242 sfree(sc->tmp);
243 sfree(sc);
244}
245
246void solver_connect(struct solver_scratch *sc, int yx1, int yx2)
247{
248 int w = sc->w, h = sc->h, k = sc->k;
249 int wh = w*h;
250 int i, yxnew;
251
252 yx1 = dsf_canonify(sc->dsf, yx1);
253 yx2 = dsf_canonify(sc->dsf, yx2);
254 assert(yx1 != yx2);
255
256 /*
257 * To connect two components together into a bigger one, we
258 * start by merging them in the dsf itself.
259 */
260 dsf_merge(sc->dsf, yx1, yx2);
261 yxnew = dsf_canonify(sc->dsf, yx2);
262
263 /*
264 * The size of the new component is the sum of the sizes of the
265 * old ones.
266 */
267 sc->size[yxnew] = sc->size[yx1] + sc->size[yx2];
268
269 /*
270 * The contents bitmap of the new component is the union of the
271 * contents of the old ones.
272 *
273 * Given two numbers at most one of which is not -1, we can
274 * find the other one by adding the two and adding 1; this
275 * will yield -1 if both were -1 to begin with, otherwise the
276 * other.
277 *
278 * (A neater approach would be to take their bitwise AND, but
279 * this is unfortunately not well-defined standard C when done
280 * to signed integers.)
281 */
282 for (i = 0; i < k; i++) {
283 assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0);
284 sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] +
285 sc->contents[yx2*k+i] + 1);
286 }
287
288 /*
289 * We must combine the rows _and_ the columns in the disconnect
290 * matrix.
291 */
292 for (i = 0; i < wh; i++)
293 sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] ||
294 sc->disconnect[yx2*wh+i]);
295 for (i = 0; i < wh; i++)
296 sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] ||
297 sc->disconnect[i*wh+yx2]);
298}
299
300void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2)
301{
302 int w = sc->w, h = sc->h;
303 int wh = w*h;
304
305 yx1 = dsf_canonify(sc->dsf, yx1);
306 yx2 = dsf_canonify(sc->dsf, yx2);
307 assert(yx1 != yx2);
308 assert(!sc->disconnect[yx1*wh+yx2]);
309 assert(!sc->disconnect[yx2*wh+yx1]);
310
311 /*
312 * Mark the components as disconnected from each other in the
313 * disconnect matrix.
314 */
315 sc->disconnect[yx1*wh+yx2] = sc->disconnect[yx2*wh+yx1] = 1;
316}
317
318void solver_init(struct solver_scratch *sc)
319{
320 int w = sc->w, h = sc->h;
321 int wh = w*h;
322 int i;
323
324 /*
325 * Set up most of the scratch space. We don't set up the
326 * contents array, however, because this will change if we
327 * adjust the letter arrangement and re-run the solver.
328 */
329 dsf_init(sc->dsf, wh);
330 for (i = 0; i < wh; i++) sc->size[i] = 1;
331 memset(sc->disconnect, 0, wh*wh);
332}
333
334int solver_attempt(struct solver_scratch *sc, const unsigned char *grid,
335 unsigned char *gen_lock)
336{
337 int w = sc->w, h = sc->h, k = sc->k;
338 int wh = w*h;
339 int i, x, y;
340 int done_something_overall = FALSE;
341
342 /*
343 * Set up the contents array from the grid.
344 */
345 for (i = 0; i < wh*k; i++)
346 sc->contents[i] = -1;
347 for (i = 0; i < wh; i++)
348 sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i;
349
350 while (1) {
351 int done_something = FALSE;
352
353 /*
354 * Go over the grid looking for reasons to add to the
355 * disconnect matrix. We're after pairs of squares which:
356 *
357 * - are adjacent in the grid
358 * - belong to distinct dsf components
359 * - their components are not already marked as
360 * disconnected
361 * - their components share a letter in common.
362 */
363 for (y = 0; y < h; y++) {
364 for (x = 0; x < w; x++) {
365 int dir;
366 for (dir = 0; dir < 2; dir++) {
367 int x2 = x + dir, y2 = y + 1 - dir;
368 int yx = y*w+x, yx2 = y2*w+x2;
369
370 if (x2 >= w || y2 >= h)
371 continue; /* one square is outside the grid */
372
373 yx = dsf_canonify(sc->dsf, yx);
374 yx2 = dsf_canonify(sc->dsf, yx2);
375 if (yx == yx2)
376 continue; /* same dsf component */
377
378 if (sc->disconnect[yx*wh+yx2])
379 continue; /* already known disconnected */
380
381 for (i = 0; i < k; i++)
382 if (sc->contents[yx*k+i] >= 0 &&
383 sc->contents[yx2*k+i] >= 0)
384 break;
385 if (i == k)
386 continue; /* no letter in common */
387
388 /*
389 * We've found one. Mark yx and yx2 as
390 * disconnected from each other.
391 */
392#ifdef SOLVER_DIAGNOSTICS
393 printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i);
394#endif
395 solver_disconnect(sc, yx, yx2);
396 done_something = done_something_overall = TRUE;
397
398 /*
399 * We have just made a deduction which hinges
400 * on two particular grid squares being the
401 * same. If we are feeding back to a generator
402 * loop, we must therefore mark those squares
403 * as fixed in the generator, so that future
404 * rearrangement of the grid will not break
405 * the information on which we have already
406 * based deductions.
407 */
408 if (gen_lock) {
409 gen_lock[sc->contents[yx*k+i]] = 1;
410 gen_lock[sc->contents[yx2*k+i]] = 1;
411 }
412 }
413 }
414 }
415
416 /*
417 * Now go over the grid looking for dsf components which
418 * are below maximum size and only have one way to extend,
419 * and extending them.
420 */
421 for (i = 0; i < wh; i++)
422 sc->tmp[i] = -1;
423 for (y = 0; y < h; y++) {
424 for (x = 0; x < w; x++) {
425 int yx = dsf_canonify(sc->dsf, y*w+x);
426 int dir;
427
428 if (sc->size[yx] == k)
429 continue;
430
431 for (dir = 0; dir < 4; dir++) {
432 int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0);
433 int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0);
434 int yx2, yx2c;
435
436 if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w)
437 continue;
438 yx2 = y2*w+x2;
439 yx2c = dsf_canonify(sc->dsf, yx2);
440
441 if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) {
442 /*
443 * Component yx can be extended into square
444 * yx2.
445 */
446 if (sc->tmp[yx] == -1)
447 sc->tmp[yx] = yx2;
448 else if (sc->tmp[yx] != yx2)
449 sc->tmp[yx] = -2; /* multiple choices found */
450 }
451 }
452 }
453 }
454 for (i = 0; i < wh; i++) {
455 if (sc->tmp[i] >= 0) {
456 /*
457 * Make sure we haven't connected the two already
458 * during this loop (which could happen if for
459 * _both_ components this was the only way to
460 * extend them).
461 */
462 if (dsf_canonify(sc->dsf, i) ==
463 dsf_canonify(sc->dsf, sc->tmp[i]))
464 continue;
465
466#ifdef SOLVER_DIAGNOSTICS
467 printf("Connecting %d and %d\n", i, sc->tmp[i]);
468#endif
469 solver_connect(sc, i, sc->tmp[i]);
470 done_something = done_something_overall = TRUE;
471 break;
472 }
473 }
474
475 if (!done_something)
476 break;
477 }
478
479 /*
480 * Return 0 if we haven't made any progress; 1 if we've done
481 * something but not solved it completely; 2 if we've solved
482 * it completely.
483 */
484 for (i = 0; i < wh; i++)
485 if (sc->size[dsf_canonify(sc->dsf, i)] != k)
486 break;
487 if (i == wh)
488 return 2;
489 if (done_something_overall)
490 return 1;
491 return 0;
492}
493
494unsigned char *generate(int w, int h, int k, random_state *rs)
495{
496 int wh = w*h;
497 int n = wh/k;
498 struct solver_scratch *sc;
499 unsigned char *grid;
500 unsigned char *shuffled;
501 int i, j, m, retries;
502 int *permutation;
503 unsigned char *gen_lock;
504 extern int *divvy_rectangle(int w, int h, int k, random_state *rs);
505
506 sc = solver_scratch_new(w, h, k);
507 grid = snewn(wh, unsigned char);
508 shuffled = snewn(k, unsigned char);
509 permutation = snewn(wh, int);
510 gen_lock = snewn(wh, unsigned char);
511
512 do {
513 int *dsf = divvy_rectangle(w, h, k, rs);
514
515 /*
516 * Go through the dsf and find the indices of all the
517 * squares involved in each omino, in a manner conducive
518 * to per-omino indexing. We set permutation[i*k+j] to be
519 * the index of the jth square (ordered arbitrarily) in
520 * omino i.
521 */
522 for (i = j = 0; i < wh; i++)
523 if (dsf_canonify(dsf, i) == i) {
524 sc->tmp[i] = j;
525 /*
526 * During this loop and the following one, we use
527 * the last element of each row of permutation[]
528 * as a counter of the number of indices so far
529 * placed in it. When we place the final index of
530 * an omino, that counter is overwritten, but that
531 * doesn't matter because we'll never use it
532 * again. Of course this depends critically on
533 * divvy_rectangle() having returned correct
534 * results, or else chaos would ensue.
535 */
536 permutation[j*k+k-1] = 0;
537 j++;
538 }
539 for (i = 0; i < wh; i++) {
540 j = sc->tmp[dsf_canonify(dsf, i)];
541 m = permutation[j*k+k-1]++;
542 permutation[j*k+m] = i;
543 }
544
545 /*
546 * Track which squares' letters we have already depended
547 * on for deductions. This is gradually updated by
548 * solver_attempt().
549 */
550 memset(gen_lock, 0, wh);
551
552 /*
553 * Now repeatedly fill the grid with letters, and attempt
554 * to solve it. If the solver makes progress but does not
555 * fail completely, then gen_lock will have been updated
556 * and we try again. On a complete failure, though, we
557 * have no option but to give up and abandon this set of
558 * ominoes.
559 */
560 solver_init(sc);
561 retries = k*k;
562 while (1) {
563 /*
564 * Fill the grid with letters. We can safely use
565 * sc->tmp to hold the set of letters required at each
566 * stage, since it's at least size k and is currently
567 * unused.
568 */
569 for (i = 0; i < n; i++) {
570 /*
571 * First, determine the set of letters already
572 * placed in this omino by gen_lock.
573 */
574 for (j = 0; j < k; j++)
575 sc->tmp[j] = j;
576 for (j = 0; j < k; j++) {
577 int index = permutation[i*k+j];
578 int letter = grid[index];
579 if (gen_lock[index])
580 sc->tmp[letter] = -1;
581 }
582 /*
583 * Now collect together all the remaining letters
584 * and randomly shuffle them.
585 */
586 for (j = m = 0; j < k; j++)
587 if (sc->tmp[j] >= 0)
588 sc->tmp[m++] = sc->tmp[j];
589 shuffle(sc->tmp, m, sizeof(*sc->tmp), rs);
590 /*
591 * Finally, write the shuffled letters into the
592 * grid.
593 */
594 for (j = 0; j < k; j++) {
595 int index = permutation[i*k+j];
596 if (!gen_lock[index])
597 grid[index] = sc->tmp[--m];
598 }
599 assert(m == 0);
600 }
601
602 /*
603 * Now we have a candidate grid. Attempt to progress
604 * the solution.
605 */
606 m = solver_attempt(sc, grid, gen_lock);
607 if (m == 2 || /* success */
608 (m == 0 && retries-- <= 0)) /* failure */
609 break;
610 if (m == 1)
611 retries = k*k; /* reset this counter, and continue */
612 }
613
614 sfree(dsf);
615 } while (m == 0);
616
617 sfree(gen_lock);
618 sfree(permutation);
619 sfree(shuffled);
620 solver_scratch_free(sc);
621
622 return grid;
623}
624
625/* ----------------------------------------------------------------------
626 * End of solver/generator code.
627 */
628
629static char *new_game_desc(const game_params *params, random_state *rs,
630 char **aux, int interactive)
631{
632 int w = params->w, h = params->h, wh = w*h, k = params->k;
633 unsigned char *grid;
634 char *desc;
635 int i;
636
637 grid = generate(w, h, k, rs);
638
639 desc = snewn(wh+1, char);
640 for (i = 0; i < wh; i++)
641 desc[i] = 'A' + grid[i];
642 desc[wh] = '\0';
643
644 sfree(grid);
645
646 return desc;
647}
648
649static char *validate_desc(const game_params *params, const char *desc)
650{
651 return NULL;
652}
653
654static game_state *new_game(midend *me, const game_params *params,
655 const char *desc)
656{
657 game_state *state = snew(game_state);
658
659 state->FIXME = 0;
660
661 return state;
662}
663
664static game_state *dup_game(const game_state *state)
665{
666 game_state *ret = snew(game_state);
667
668 ret->FIXME = state->FIXME;
669
670 return ret;
671}
672
673static void free_game(game_state *state)
674{
675 sfree(state);
676}
677
678static char *solve_game(const game_state *state, const game_state *currstate,
679 const char *aux, char **error)
680{
681 return NULL;
682}
683
684static int game_can_format_as_text_now(const game_params *params)
685{
686 return TRUE;
687}
688
689static char *game_text_format(const game_state *state)
690{
691 return NULL;
692}
693
694static game_ui *new_ui(const game_state *state)
695{
696 return NULL;
697}
698
699static void free_ui(game_ui *ui)
700{
701}
702
703static char *encode_ui(const game_ui *ui)
704{
705 return NULL;
706}
707
708static void decode_ui(game_ui *ui, const char *encoding)
709{
710}
711
712static void game_changed_state(game_ui *ui, const game_state *oldstate,
713 const game_state *newstate)
714{
715}
716
717struct game_drawstate {
718 int tilesize;
719 int FIXME;
720};
721
722static char *interpret_move(const game_state *state, game_ui *ui,
723 const game_drawstate *ds,
724 int x, int y, int button)
725{
726 return NULL;
727}
728
729static game_state *execute_move(const game_state *state, const char *move)
730{
731 return NULL;
732}
733
734/* ----------------------------------------------------------------------
735 * Drawing routines.
736 */
737
738static void game_compute_size(const game_params *params, int tilesize,
739 int *x, int *y)
740{
741 *x = *y = 10 * tilesize; /* FIXME */
742}
743
744static void game_set_size(drawing *dr, game_drawstate *ds,
745 const game_params *params, int tilesize)
746{
747 ds->tilesize = tilesize;
748}
749
750static float *game_colours(frontend *fe, int *ncolours)
751{
752 float *ret = snewn(3 * NCOLOURS, float);
753
754 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
755
756 *ncolours = NCOLOURS;
757 return ret;
758}
759
760static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
761{
762 struct game_drawstate *ds = snew(struct game_drawstate);
763
764 ds->tilesize = 0;
765 ds->FIXME = 0;
766
767 return ds;
768}
769
770static void game_free_drawstate(drawing *dr, game_drawstate *ds)
771{
772 sfree(ds);
773}
774
775static void game_redraw(drawing *dr, game_drawstate *ds,
776 const game_state *oldstate, const game_state *state,
777 int dir, const game_ui *ui,
778 float animtime, float flashtime)
779{
780 /*
781 * The initial contents of the window are not guaranteed and
782 * can vary with front ends. To be on the safe side, all games
783 * should start by drawing a big background-colour rectangle
784 * covering the whole window.
785 */
786 draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
787}
788
789static float game_anim_length(const game_state *oldstate,
790 const game_state *newstate, int dir, game_ui *ui)
791{
792 return 0.0F;
793}
794
795static float game_flash_length(const game_state *oldstate,
796 const game_state *newstate, int dir, game_ui *ui)
797{
798 return 0.0F;
799}
800
801static int game_status(const game_state *state)
802{
803 return 0;
804}
805
806static int game_timing_state(const game_state *state, game_ui *ui)
807{
808 return TRUE;
809}
810
811static void game_print_size(const game_params *params, float *x, float *y)
812{
813}
814
815static void game_print(drawing *dr, const game_state *state, int tilesize)
816{
817}
818
819#ifdef COMBINED
820#define thegame separate
821#endif
822
823const struct game thegame = {
824 "Separate", NULL, NULL,
825 default_params,
826 game_fetch_preset, NULL,
827 decode_params,
828 encode_params,
829 free_params,
830 dup_params,
831 FALSE, game_configure, custom_params,
832 validate_params,
833 new_game_desc,
834 validate_desc,
835 new_game,
836 dup_game,
837 free_game,
838 FALSE, solve_game,
839 FALSE, game_can_format_as_text_now, game_text_format,
840 new_ui,
841 free_ui,
842 encode_ui,
843 decode_ui,
844 game_changed_state,
845 interpret_move,
846 execute_move,
847 20 /* FIXME */, game_compute_size, game_set_size,
848 game_colours,
849 game_new_drawstate,
850 game_free_drawstate,
851 game_redraw,
852 game_anim_length,
853 game_flash_length,
854 game_status,
855 FALSE, FALSE, game_print_size, game_print,
856 FALSE, /* wants_statusbar */
857 FALSE, game_timing_state,
858 0, /* flags */
859};
diff --git a/apps/plugins/puzzles/src/unfinished/slide.R b/apps/plugins/puzzles/src/unfinished/slide.R
new file mode 100644
index 0000000000..189ed652d1
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/slide.R
@@ -0,0 +1,24 @@
1# -*- makefile -*-
2
3SLIDE_EXTRA = dsf tree234
4
5slide : [X] GTK COMMON slide SLIDE_EXTRA slide-icon|no-icon
6
7slide : [G] WINDOWS COMMON slide SLIDE_EXTRA slide.res|noicon.res
8
9slidesolver : [U] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE
10slidesolver : [C] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE
11
12ALL += slide[COMBINED] SLIDE_EXTRA
13
14!begin am gtk
15GAMES += slide
16!end
17
18!begin >list.c
19 A(slide) \
20!end
21
22!begin >gamedesc.txt
23slide:slide.exe:Slide:Sliding block puzzle:Slide the blocks to let the key block out.
24!end
diff --git a/apps/plugins/puzzles/src/unfinished/slide.c b/apps/plugins/puzzles/src/unfinished/slide.c
new file mode 100644
index 0000000000..9d4fce1461
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/slide.c
@@ -0,0 +1,2445 @@
1/*
2 * slide.c: Implementation of the block-sliding puzzle `Klotski'.
3 */
4
5/*
6 * TODO:
7 *
8 * - Improve the generator.
9 * * actually, we seem to be mostly sensible already now. I
10 * want more choice over the type of main block and location
11 * of the exit/target, and I think I probably ought to give
12 * up on compactness and just bite the bullet and have the
13 * target area right outside the main wall, but mostly I
14 * think it's OK.
15 * * the move limit tends to make the game _slower_ to
16 * generate, which is odd. Perhaps investigate why.
17 *
18 * - Improve the graphics.
19 * * All the colours are a bit wishy-washy. _Some_ dark
20 * colours would surely not be excessive? Probably darken
21 * the tiles, the walls and the main block, and leave the
22 * target marker pale.
23 * * The cattle grid effect is still disgusting. Think of
24 * something completely different.
25 * * The highlight for next-piece-to-move in the solver is
26 * excessive, and the shadow blends in too well with the
27 * piece lowlights. Adjust both.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <assert.h>
34#include <ctype.h>
35#include <math.h>
36
37#include "puzzles.h"
38#include "tree234.h"
39
40/*
41 * The implementation of this game revolves around the insight
42 * which makes an exhaustive-search solver feasible: although
43 * there are many blocks which can be rearranged in many ways, any
44 * two blocks of the same shape are _indistinguishable_ and hence
45 * the number of _distinct_ board layouts is generally much
46 * smaller. So we adopt a representation for board layouts which
47 * is inherently canonical, i.e. there are no two distinct
48 * representations which encode indistinguishable layouts.
49 *
50 * The way we do this is to encode each square of the board, in
51 * the normal left-to-right top-to-bottom order, as being one of
52 * the following things:
53 * - the first square (in the given order) of a block (`anchor')
54 * - special case of the above: the anchor for the _main_ block
55 * (i.e. the one which the aim of the game is to get to the
56 * target position)
57 * - a subsequent square of a block whose previous square was N
58 * squares ago
59 * - an impassable wall
60 *
61 * (We also separately store data about which board positions are
62 * forcefields only passable by the main block. We can't encode
63 * that in the main board data, because then the main block would
64 * destroy forcefields as it went over them.)
65 *
66 * Hence, for example, a 2x2 square block would be encoded as
67 * ANCHOR, followed by DIST(1), and w-2 squares later on there
68 * would be DIST(w-1) followed by DIST(1). So if you start at the
69 * last of those squares, the DIST numbers give you a linked list
70 * pointing back through all the other squares in the same block.
71 *
72 * So the solver simply does a bfs over all reachable positions,
73 * encoding them in this format and storing them in a tree234 to
74 * ensure it doesn't ever revisit an already-analysed position.
75 */
76
77enum {
78 /*
79 * The colours are arranged here so that every base colour is
80 * directly followed by its highlight colour and then its
81 * lowlight colour. Do not break this, or draw_tile() will get
82 * confused.
83 */
84 COL_BACKGROUND,
85 COL_HIGHLIGHT,
86 COL_LOWLIGHT,
87 COL_DRAGGING,
88 COL_DRAGGING_HIGHLIGHT,
89 COL_DRAGGING_LOWLIGHT,
90 COL_MAIN,
91 COL_MAIN_HIGHLIGHT,
92 COL_MAIN_LOWLIGHT,
93 COL_MAIN_DRAGGING,
94 COL_MAIN_DRAGGING_HIGHLIGHT,
95 COL_MAIN_DRAGGING_LOWLIGHT,
96 COL_TARGET,
97 COL_TARGET_HIGHLIGHT,
98 COL_TARGET_LOWLIGHT,
99 NCOLOURS
100};
101
102/*
103 * Board layout is a simple array of bytes. Each byte holds:
104 */
105#define ANCHOR 255 /* top-left-most square of some piece */
106#define MAINANCHOR 254 /* anchor of _main_ piece */
107#define EMPTY 253 /* empty square */
108#define WALL 252 /* immovable wall */
109#define MAXDIST 251
110/* all other values indicate distance back to previous square of same block */
111#define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 )
112#define DIST(x) (x)
113#define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR )
114#define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) )
115
116/*
117 * MAXDIST is the largest DIST value we can encode. This must
118 * therefore also be the maximum puzzle width in theory (although
119 * solver running time will dictate a much smaller limit in
120 * practice).
121 */
122#define MAXWID MAXDIST
123
124struct game_params {
125 int w, h;
126 int maxmoves;
127};
128
129struct game_immutable_state {
130 int refcount;
131 unsigned char *forcefield;
132};
133
134struct game_solution {
135 int nmoves;
136 int *moves; /* just like from solve_board() */
137 int refcount;
138};
139
140struct game_state {
141 int w, h;
142 unsigned char *board;
143 int tx, ty; /* target coords for MAINANCHOR */
144 int minmoves; /* for display only */
145 int lastmoved, lastmoved_pos; /* for move counting */
146 int movecount;
147 int completed;
148 int cheated;
149 struct game_immutable_state *imm;
150 struct game_solution *soln;
151 int soln_index;
152};
153
154static game_params *default_params(void)
155{
156 game_params *ret = snew(game_params);
157
158 ret->w = 7;
159 ret->h = 6;
160 ret->maxmoves = 40;
161
162 return ret;
163}
164
165static const struct game_params slide_presets[] = {
166 {7, 6, 25},
167 {7, 6, -1},
168 {8, 6, -1},
169};
170
171static int game_fetch_preset(int i, char **name, game_params **params)
172{
173 game_params *ret;
174 char str[80];
175
176 if (i < 0 || i >= lenof(slide_presets))
177 return FALSE;
178
179 ret = snew(game_params);
180 *ret = slide_presets[i];
181
182 sprintf(str, "%dx%d", ret->w, ret->h);
183 if (ret->maxmoves >= 0)
184 sprintf(str + strlen(str), ", max %d moves", ret->maxmoves);
185 else
186 sprintf(str + strlen(str), ", no move limit");
187
188 *name = dupstr(str);
189 *params = ret;
190 return TRUE;
191}
192
193static void free_params(game_params *params)
194{
195 sfree(params);
196}
197
198static game_params *dup_params(const game_params *params)
199{
200 game_params *ret = snew(game_params);
201 *ret = *params; /* structure copy */
202 return ret;
203}
204
205static void decode_params(game_params *params, char const *string)
206{
207 params->w = params->h = atoi(string);
208 while (*string && isdigit((unsigned char)*string)) string++;
209 if (*string == 'x') {
210 string++;
211 params->h = atoi(string);
212 while (*string && isdigit((unsigned char)*string)) string++;
213 }
214 if (*string == 'm') {
215 string++;
216 params->maxmoves = atoi(string);
217 while (*string && isdigit((unsigned char)*string)) string++;
218 } else if (*string == 'u') {
219 string++;
220 params->maxmoves = -1;
221 }
222}
223
224static char *encode_params(const game_params *params, int full)
225{
226 char data[256];
227
228 sprintf(data, "%dx%d", params->w, params->h);
229 if (params->maxmoves >= 0)
230 sprintf(data + strlen(data), "m%d", params->maxmoves);
231 else
232 sprintf(data + strlen(data), "u");
233
234 return dupstr(data);
235}
236
237static config_item *game_configure(const game_params *params)
238{
239 config_item *ret;
240 char buf[80];
241
242 ret = snewn(4, config_item);
243
244 ret[0].name = "Width";
245 ret[0].type = C_STRING;
246 sprintf(buf, "%d", params->w);
247 ret[0].sval = dupstr(buf);
248 ret[0].ival = 0;
249
250 ret[1].name = "Height";
251 ret[1].type = C_STRING;
252 sprintf(buf, "%d", params->h);
253 ret[1].sval = dupstr(buf);
254 ret[1].ival = 0;
255
256 ret[2].name = "Solution length limit";
257 ret[2].type = C_STRING;
258 sprintf(buf, "%d", params->maxmoves);
259 ret[2].sval = dupstr(buf);
260 ret[2].ival = 0;
261
262 ret[3].name = NULL;
263 ret[3].type = C_END;
264 ret[3].sval = NULL;
265 ret[3].ival = 0;
266
267 return ret;
268}
269
270static game_params *custom_params(const config_item *cfg)
271{
272 game_params *ret = snew(game_params);
273
274 ret->w = atoi(cfg[0].sval);
275 ret->h = atoi(cfg[1].sval);
276 ret->maxmoves = atoi(cfg[2].sval);
277
278 return ret;
279}
280
281static char *validate_params(const game_params *params, int full)
282{
283 if (params->w > MAXWID)
284 return "Width must be at most " STR(MAXWID);
285
286 if (params->w < 5)
287 return "Width must be at least 5";
288 if (params->h < 4)
289 return "Height must be at least 4";
290
291 return NULL;
292}
293
294static char *board_text_format(int w, int h, unsigned char *data,
295 unsigned char *forcefield)
296{
297 int wh = w*h;
298 int *dsf = snew_dsf(wh);
299 int i, x, y;
300 int retpos, retlen = (w*2+2)*(h*2+1)+1;
301 char *ret = snewn(retlen, char);
302
303 for (i = 0; i < wh; i++)
304 if (ISDIST(data[i]))
305 dsf_merge(dsf, i - data[i], i);
306 retpos = 0;
307 for (y = 0; y < 2*h+1; y++) {
308 for (x = 0; x < 2*w+1; x++) {
309 int v;
310 int i = (y/2)*w+(x/2);
311
312#define dtype(i) (ISBLOCK(data[i]) ? \
313 dsf_canonify(dsf, i) : data[i])
314#define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \
315 data[t] == MAINANCHOR ? '*' : '%')
316
317 if (y % 2 && x % 2) {
318 int j = dtype(i);
319 v = dchar(j);
320 } else if (y % 2 && !(x % 2)) {
321 int j1 = (x > 0 ? dtype(i-1) : -1);
322 int j2 = (x < 2*w ? dtype(i) : -1);
323 if (j1 != j2)
324 v = '|';
325 else
326 v = dchar(j1);
327 } else if (!(y % 2) && (x % 2)) {
328 int j1 = (y > 0 ? dtype(i-w) : -1);
329 int j2 = (y < 2*h ? dtype(i) : -1);
330 if (j1 != j2)
331 v = '-';
332 else
333 v = dchar(j1);
334 } else {
335 int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1);
336 int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1);
337 int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1);
338 int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1);
339 if (j1 == j2 && j2 == j3 && j3 == j4)
340 v = dchar(j1);
341 else if (j1 == j2 && j3 == j4)
342 v = '|';
343 else if (j1 == j3 && j2 == j4)
344 v = '-';
345 else
346 v = '+';
347 }
348
349 assert(retpos < retlen);
350 ret[retpos++] = v;
351 }
352 assert(retpos < retlen);
353 ret[retpos++] = '\n';
354 }
355 assert(retpos < retlen);
356 ret[retpos++] = '\0';
357 assert(retpos == retlen);
358
359 return ret;
360}
361
362/* ----------------------------------------------------------------------
363 * Solver.
364 */
365
366/*
367 * During solver execution, the set of visited board positions is
368 * stored as a tree234 of the following structures. `w', `h' and
369 * `data' are obvious in meaning; `dist' represents the minimum
370 * distance to reach this position from the starting point.
371 *
372 * `prev' links each board to the board position from which it was
373 * most efficiently derived.
374 */
375struct board {
376 int w, h;
377 int dist;
378 struct board *prev;
379 unsigned char *data;
380};
381
382static int boardcmp(void *av, void *bv)
383{
384 struct board *a = (struct board *)av;
385 struct board *b = (struct board *)bv;
386 return memcmp(a->data, b->data, a->w * a->h);
387}
388
389static struct board *newboard(int w, int h, unsigned char *data)
390{
391 struct board *b = malloc(sizeof(struct board) + w*h);
392 b->data = (unsigned char *)b + sizeof(struct board);
393 memcpy(b->data, data, w*h);
394 b->w = w;
395 b->h = h;
396 b->dist = -1;
397 b->prev = NULL;
398 return b;
399}
400
401/*
402 * The actual solver. Given a board, attempt to find the minimum
403 * length of move sequence which moves MAINANCHOR to (tx,ty), or
404 * -1 if no solution exists. Returns that minimum length.
405 *
406 * Also, if `moveout' is provided, writes out the moves in the
407 * form of a sequence of pairs of integers indicating the source
408 * and destination points of the anchor of the moved piece in each
409 * move. Exactly twice as many integers are written as the number
410 * returned from solve_board(), and `moveout' receives an int *
411 * which is a pointer to a dynamically allocated array.
412 */
413static int solve_board(int w, int h, unsigned char *board,
414 unsigned char *forcefield, int tx, int ty,
415 int movelimit, int **moveout)
416{
417 int wh = w*h;
418 struct board *b, *b2, *b3;
419 int *next, *anchors, *which;
420 int *movereached, *movequeue, mqhead, mqtail;
421 tree234 *sorted, *queue;
422 int i, j, dir;
423 int qlen, lastdist;
424 int ret;
425
426#ifdef SOLVER_DIAGNOSTICS
427 {
428 char *t = board_text_format(w, h, board);
429 for (i = 0; i < h; i++) {
430 for (j = 0; j < w; j++) {
431 int c = board[i*w+j];
432 if (ISDIST(c))
433 printf("D%-3d", c);
434 else if (c == MAINANCHOR)
435 printf("M ");
436 else if (c == ANCHOR)
437 printf("A ");
438 else if (c == WALL)
439 printf("W ");
440 else if (c == EMPTY)
441 printf("E ");
442 }
443 printf("\n");
444 }
445
446 printf("Starting solver for:\n%s\n", t);
447 sfree(t);
448 }
449#endif
450
451 sorted = newtree234(boardcmp);
452 queue = newtree234(NULL);
453
454 b = newboard(w, h, board);
455 b->dist = 0;
456 add234(sorted, b);
457 addpos234(queue, b, 0);
458 qlen = 1;
459
460 next = snewn(wh, int);
461 anchors = snewn(wh, int);
462 which = snewn(wh, int);
463 movereached = snewn(wh, int);
464 movequeue = snewn(wh, int);
465 lastdist = -1;
466
467 while ((b = delpos234(queue, 0)) != NULL) {
468 qlen--;
469 if (movelimit >= 0 && b->dist >= movelimit) {
470 /*
471 * The problem is not soluble in under `movelimit'
472 * moves, so we can quit right now.
473 */
474 b2 = NULL;
475 goto done;
476 }
477 if (b->dist != lastdist) {
478#ifdef SOLVER_DIAGNOSTICS
479 printf("dist %d (%d)\n", b->dist, count234(sorted));
480#endif
481 lastdist = b->dist;
482 }
483 /*
484 * Find all the anchors and form a linked list of the
485 * squares within each block.
486 */
487 for (i = 0; i < wh; i++) {
488 next[i] = -1;
489 anchors[i] = FALSE;
490 which[i] = -1;
491 if (ISANCHOR(b->data[i])) {
492 anchors[i] = TRUE;
493 which[i] = i;
494 } else if (ISDIST(b->data[i])) {
495 j = i - b->data[i];
496 next[j] = i;
497 which[i] = which[j];
498 }
499 }
500
501 /*
502 * For each anchor, do an array-based BFS to find all the
503 * places we can slide it to.
504 */
505 for (i = 0; i < wh; i++) {
506 if (!anchors[i])
507 continue;
508
509 mqhead = mqtail = 0;
510 for (j = 0; j < wh; j++)
511 movereached[j] = FALSE;
512 movequeue[mqtail++] = i;
513 while (mqhead < mqtail) {
514 int pos = movequeue[mqhead++];
515
516 /*
517 * Try to move in each direction from here.
518 */
519 for (dir = 0; dir < 4; dir++) {
520 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
521 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
522 int offset = dy*w + dx;
523 int newpos = pos + offset;
524 int d = newpos - i;
525
526 /*
527 * For each square involved in this block,
528 * check to see if the square d spaces away
529 * from it is either empty or part of the same
530 * block.
531 */
532 for (j = i; j >= 0; j = next[j]) {
533 int jy = (pos+j-i) / w + dy, jx = (pos+j-i) % w + dx;
534 if (jy >= 0 && jy < h && jx >= 0 && jx < w &&
535 ((b->data[j+d] == EMPTY || which[j+d] == i) &&
536 (b->data[i] == MAINANCHOR || !forcefield[j+d])))
537 /* ok */;
538 else
539 break;
540 }
541 if (j >= 0)
542 continue; /* this direction wasn't feasible */
543
544 /*
545 * If we've already tried moving this piece
546 * here, leave it.
547 */
548 if (movereached[newpos])
549 continue;
550 movereached[newpos] = TRUE;
551 movequeue[mqtail++] = newpos;
552
553 /*
554 * We have a viable move. Make it.
555 */
556 b2 = newboard(w, h, b->data);
557 for (j = i; j >= 0; j = next[j])
558 b2->data[j] = EMPTY;
559 for (j = i; j >= 0; j = next[j])
560 b2->data[j+d] = b->data[j];
561
562 b3 = add234(sorted, b2);
563 if (b3 != b2) {
564 sfree(b2); /* we already got one */
565 } else {
566 b2->dist = b->dist + 1;
567 b2->prev = b;
568 addpos234(queue, b2, qlen++);
569 if (b2->data[ty*w+tx] == MAINANCHOR)
570 goto done; /* search completed! */
571 }
572 }
573 }
574 }
575 }
576 b2 = NULL;
577
578 done:
579
580 if (b2) {
581 ret = b2->dist;
582 if (moveout) {
583 /*
584 * Now b2 represents the solved position. Backtrack to
585 * output the solution.
586 */
587 *moveout = snewn(ret * 2, int);
588 j = ret * 2;
589
590 while (b2->prev) {
591 int from = -1, to = -1;
592
593 b = b2->prev;
594
595 /*
596 * Scan b and b2 to find out which piece has
597 * moved.
598 */
599 for (i = 0; i < wh; i++) {
600 if (ISANCHOR(b->data[i]) && !ISANCHOR(b2->data[i])) {
601 assert(from == -1);
602 from = i;
603 } else if (!ISANCHOR(b->data[i]) && ISANCHOR(b2->data[i])){
604 assert(to == -1);
605 to = i;
606 }
607 }
608
609 assert(from >= 0 && to >= 0);
610 assert(j >= 2);
611 (*moveout)[--j] = to;
612 (*moveout)[--j] = from;
613
614 b2 = b;
615 }
616 assert(j == 0);
617 }
618 } else {
619 ret = -1; /* no solution */
620 if (moveout)
621 *moveout = NULL;
622 }
623
624 freetree234(queue);
625
626 while ((b = delpos234(sorted, 0)) != NULL)
627 sfree(b);
628 freetree234(sorted);
629
630 sfree(next);
631 sfree(anchors);
632 sfree(movereached);
633 sfree(movequeue);
634 sfree(which);
635
636 return ret;
637}
638
639/* ----------------------------------------------------------------------
640 * Random board generation.
641 */
642
643static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves,
644 random_state *rs, unsigned char **rboard,
645 unsigned char **rforcefield, int movelimit)
646{
647 int wh = w*h;
648 unsigned char *board, *board2, *forcefield;
649 unsigned char *tried_merge;
650 int *dsf;
651 int *list, nlist, pos;
652 int tx, ty;
653 int i, j;
654 int moves = 0; /* placate optimiser */
655
656 /*
657 * Set up a board and fill it with singletons, except for a
658 * border of walls.
659 */
660 board = snewn(wh, unsigned char);
661 forcefield = snewn(wh, unsigned char);
662 board2 = snewn(wh, unsigned char);
663 memset(board, ANCHOR, wh);
664 memset(forcefield, FALSE, wh);
665 for (i = 0; i < w; i++)
666 board[i] = board[i+w*(h-1)] = WALL;
667 for (i = 0; i < h; i++)
668 board[i*w] = board[i*w+(w-1)] = WALL;
669
670 tried_merge = snewn(wh * wh, unsigned char);
671 memset(tried_merge, 0, wh*wh);
672 dsf = snew_dsf(wh);
673
674 /*
675 * Invent a main piece at one extreme. (FIXME: vary the
676 * extreme, and the piece.)
677 */
678 board[w+1] = MAINANCHOR;
679 board[w+2] = DIST(1);
680 board[w*2+1] = DIST(w-1);
681 board[w*2+2] = DIST(1);
682
683 /*
684 * Invent a target position. (FIXME: vary this too.)
685 */
686 tx = w-2;
687 ty = h-3;
688 forcefield[ty*w+tx+1] = forcefield[(ty+1)*w+tx+1] = TRUE;
689 board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY;
690
691 /*
692 * Gradually remove singletons until the game becomes soluble.
693 */
694 for (j = w; j-- > 0 ;)
695 for (i = h; i-- > 0 ;)
696 if (board[i*w+j] == ANCHOR) {
697 /*
698 * See if the board is already soluble.
699 */
700 if ((moves = solve_board(w, h, board, forcefield,
701 tx, ty, movelimit, NULL)) >= 0)
702 goto soluble;
703
704 /*
705 * Otherwise, remove this piece.
706 */
707 board[i*w+j] = EMPTY;
708 }
709 assert(!"We shouldn't get here");
710 soluble:
711
712 /*
713 * Make a list of all the inter-block edges on the board.
714 */
715 list = snewn(wh*2, int);
716 nlist = 0;
717 for (i = 0; i+1 < w; i++)
718 for (j = 0; j < h; j++)
719 list[nlist++] = (j*w+i) * 2 + 0; /* edge to the right of j*w+i */
720 for (j = 0; j+1 < h; j++)
721 for (i = 0; i < w; i++)
722 list[nlist++] = (j*w+i) * 2 + 1; /* edge below j*w+i */
723
724 /*
725 * Now go through that list in random order, trying to merge
726 * the blocks on each side of each edge.
727 */
728 shuffle(list, nlist, sizeof(*list), rs);
729 while (nlist > 0) {
730 int x1, y1, p1, c1;
731 int x2, y2, p2, c2;
732
733 pos = list[--nlist];
734 y1 = y2 = pos / (w*2);
735 x1 = x2 = (pos / 2) % w;
736 if (pos % 2)
737 y2++;
738 else
739 x2++;
740 p1 = y1*w+x1;
741 p2 = y2*w+x2;
742
743 /*
744 * Immediately abandon the attempt if we've already tried
745 * to merge the same pair of blocks along a different
746 * edge.
747 */
748 c1 = dsf_canonify(dsf, p1);
749 c2 = dsf_canonify(dsf, p2);
750 if (tried_merge[c1 * wh + c2])
751 continue;
752
753 /*
754 * In order to be mergeable, these two squares must each
755 * either be, or belong to, a non-main anchor, and their
756 * anchors must also be distinct.
757 */
758 if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2]))
759 continue;
760 while (ISDIST(board[p1]))
761 p1 -= board[p1];
762 while (ISDIST(board[p2]))
763 p2 -= board[p2];
764 if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2)
765 continue;
766
767 /*
768 * We can merge these blocks. Try it, and see if the
769 * puzzle remains soluble.
770 */
771 memcpy(board2, board, wh);
772 j = -1;
773 while (p1 < wh || p2 < wh) {
774 /*
775 * p1 and p2 are the squares at the head of each block
776 * list. Pick the smaller one and put it on the output
777 * block list.
778 */
779 i = min(p1, p2);
780 if (j < 0) {
781 board[i] = ANCHOR;
782 } else {
783 assert(i - j <= MAXDIST);
784 board[i] = DIST(i - j);
785 }
786 j = i;
787
788 /*
789 * Now advance whichever list that came from.
790 */
791 if (i == p1) {
792 do {
793 p1++;
794 } while (p1 < wh && board[p1] != DIST(p1-i));
795 } else {
796 do {
797 p2++;
798 } while (p2 < wh && board[p2] != DIST(p2-i));
799 }
800 }
801 j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL);
802 if (j < 0) {
803 /*
804 * Didn't work. Revert the merge.
805 */
806 memcpy(board, board2, wh);
807 tried_merge[c1 * wh + c2] = tried_merge[c2 * wh + c1] = TRUE;
808 } else {
809 int c;
810
811 moves = j;
812
813 dsf_merge(dsf, c1, c2);
814 c = dsf_canonify(dsf, c1);
815 for (i = 0; i < wh; i++)
816 tried_merge[c*wh+i] = (tried_merge[c1*wh+i] |
817 tried_merge[c2*wh+i]);
818 for (i = 0; i < wh; i++)
819 tried_merge[i*wh+c] = (tried_merge[i*wh+c1] |
820 tried_merge[i*wh+c2]);
821 }
822 }
823
824 sfree(dsf);
825 sfree(list);
826 sfree(tried_merge);
827 sfree(board2);
828
829 *rtx = tx;
830 *rty = ty;
831 *rboard = board;
832 *rforcefield = forcefield;
833 *minmoves = moves;
834}
835
836/* ----------------------------------------------------------------------
837 * End of solver/generator code.
838 */
839
840static char *new_game_desc(const game_params *params, random_state *rs,
841 char **aux, int interactive)
842{
843 int w = params->w, h = params->h, wh = w*h;
844 int tx, ty, minmoves;
845 unsigned char *board, *forcefield;
846 char *ret, *p;
847 int i;
848
849 generate_board(params->w, params->h, &tx, &ty, &minmoves, rs,
850 &board, &forcefield, params->maxmoves);
851#ifdef GENERATOR_DIAGNOSTICS
852 {
853 char *t = board_text_format(params->w, params->h, board);
854 printf("%s\n", t);
855 sfree(t);
856 }
857#endif
858
859 /*
860 * Encode as a game ID.
861 */
862 ret = snewn(wh * 6 + 40, char);
863 p = ret;
864 i = 0;
865 while (i < wh) {
866 if (ISDIST(board[i])) {
867 p += sprintf(p, "d%d", board[i]);
868 i++;
869 } else {
870 int count = 1;
871 int b = board[i], f = forcefield[i];
872 int c = (b == ANCHOR ? 'a' :
873 b == MAINANCHOR ? 'm' :
874 b == EMPTY ? 'e' :
875 /* b == WALL ? */ 'w');
876 if (f) *p++ = 'f';
877 *p++ = c;
878 i++;
879 while (i < wh && board[i] == b && forcefield[i] == f)
880 i++, count++;
881 if (count > 1)
882 p += sprintf(p, "%d", count);
883 }
884 }
885 p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves);
886 ret = sresize(ret, p+1 - ret, char);
887
888 sfree(board);
889 sfree(forcefield);
890
891 return ret;
892}
893
894static char *validate_desc(const game_params *params, const char *desc)
895{
896 int w = params->w, h = params->h, wh = w*h;
897 int *active, *link;
898 int mains = 0;
899 int i, tx, ty, minmoves;
900 char *ret;
901
902 active = snewn(wh, int);
903 link = snewn(wh, int);
904 i = 0;
905
906 while (*desc && *desc != ',') {
907 if (i >= wh) {
908 ret = "Too much data in game description";
909 goto done;
910 }
911 link[i] = -1;
912 active[i] = FALSE;
913 if (*desc == 'f' || *desc == 'F') {
914 desc++;
915 if (!*desc) {
916 ret = "Expected another character after 'f' in game "
917 "description";
918 goto done;
919 }
920 }
921
922 if (*desc == 'd' || *desc == 'D') {
923 int dist;
924
925 desc++;
926 if (!isdigit((unsigned char)*desc)) {
927 ret = "Expected a number after 'd' in game description";
928 goto done;
929 }
930 dist = atoi(desc);
931 while (*desc && isdigit((unsigned char)*desc)) desc++;
932
933 if (dist <= 0 || dist > i) {
934 ret = "Out-of-range number after 'd' in game description";
935 goto done;
936 }
937
938 if (!active[i - dist]) {
939 ret = "Invalid back-reference in game description";
940 goto done;
941 }
942
943 link[i] = i - dist;
944
945 active[i] = TRUE;
946 active[link[i]] = FALSE;
947 i++;
948 } else {
949 int c = *desc++;
950 int count = 1;
951
952 if (!strchr("aAmMeEwW", c)) {
953 ret = "Invalid character in game description";
954 goto done;
955 }
956 if (isdigit((unsigned char)*desc)) {
957 count = atoi(desc);
958 while (*desc && isdigit((unsigned char)*desc)) desc++;
959 }
960 if (i + count > wh) {
961 ret = "Too much data in game description";
962 goto done;
963 }
964 while (count-- > 0) {
965 active[i] = (strchr("aAmM", c) != NULL);
966 link[i] = -1;
967 if (strchr("mM", c) != NULL) {
968 mains++;
969 }
970 i++;
971 }
972 }
973 }
974 if (mains != 1) {
975 ret = (mains == 0 ? "No main piece specified in game description" :
976 "More than one main piece specified in game description");
977 goto done;
978 }
979 if (i < wh) {
980 ret = "Not enough data in game description";
981 goto done;
982 }
983
984 /*
985 * Now read the target coordinates.
986 */
987 i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves);
988 if (i < 2) {
989 ret = "No target coordinates specified";
990 goto done;
991 /*
992 * (but minmoves is optional)
993 */
994 }
995
996 ret = NULL;
997
998 done:
999 sfree(active);
1000 sfree(link);
1001 return ret;
1002}
1003
1004static game_state *new_game(midend *me, const game_params *params,
1005 const char *desc)
1006{
1007 int w = params->w, h = params->h, wh = w*h;
1008 game_state *state;
1009 int i;
1010
1011 state = snew(game_state);
1012 state->w = w;
1013 state->h = h;
1014 state->board = snewn(wh, unsigned char);
1015 state->lastmoved = state->lastmoved_pos = -1;
1016 state->movecount = 0;
1017 state->imm = snew(struct game_immutable_state);
1018 state->imm->refcount = 1;
1019 state->imm->forcefield = snewn(wh, unsigned char);
1020
1021 i = 0;
1022
1023 while (*desc && *desc != ',') {
1024 int f = FALSE;
1025
1026 assert(i < wh);
1027
1028 if (*desc == 'f') {
1029 f = TRUE;
1030 desc++;
1031 assert(*desc);
1032 }
1033
1034 if (*desc == 'd' || *desc == 'D') {
1035 int dist;
1036
1037 desc++;
1038 dist = atoi(desc);
1039 while (*desc && isdigit((unsigned char)*desc)) desc++;
1040
1041 state->board[i] = DIST(dist);
1042 state->imm->forcefield[i] = f;
1043
1044 i++;
1045 } else {
1046 int c = *desc++;
1047 int count = 1;
1048
1049 if (isdigit((unsigned char)*desc)) {
1050 count = atoi(desc);
1051 while (*desc && isdigit((unsigned char)*desc)) desc++;
1052 }
1053 assert(i + count <= wh);
1054
1055 c = (c == 'a' || c == 'A' ? ANCHOR :
1056 c == 'm' || c == 'M' ? MAINANCHOR :
1057 c == 'e' || c == 'E' ? EMPTY :
1058 /* c == 'w' || c == 'W' ? */ WALL);
1059
1060 while (count-- > 0) {
1061 state->board[i] = c;
1062 state->imm->forcefield[i] = f;
1063 i++;
1064 }
1065 }
1066 }
1067
1068 /*
1069 * Now read the target coordinates.
1070 */
1071 state->tx = state->ty = 0;
1072 state->minmoves = -1;
1073 i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves);
1074
1075 if (state->board[state->ty*w+state->tx] == MAINANCHOR)
1076 state->completed = 0; /* already complete! */
1077 else
1078 state->completed = -1;
1079
1080 state->cheated = FALSE;
1081 state->soln = NULL;
1082 state->soln_index = -1;
1083
1084 return state;
1085}
1086
1087static game_state *dup_game(const game_state *state)
1088{
1089 int w = state->w, h = state->h, wh = w*h;
1090 game_state *ret = snew(game_state);
1091
1092 ret->w = state->w;
1093 ret->h = state->h;
1094 ret->board = snewn(wh, unsigned char);
1095 memcpy(ret->board, state->board, wh);
1096 ret->tx = state->tx;
1097 ret->ty = state->ty;
1098 ret->minmoves = state->minmoves;
1099 ret->lastmoved = state->lastmoved;
1100 ret->lastmoved_pos = state->lastmoved_pos;
1101 ret->movecount = state->movecount;
1102 ret->completed = state->completed;
1103 ret->cheated = state->cheated;
1104 ret->imm = state->imm;
1105 ret->imm->refcount++;
1106 ret->soln = state->soln;
1107 ret->soln_index = state->soln_index;
1108 if (ret->soln)
1109 ret->soln->refcount++;
1110
1111 return ret;
1112}
1113
1114static void free_game(game_state *state)
1115{
1116 if (--state->imm->refcount <= 0) {
1117 sfree(state->imm->forcefield);
1118 sfree(state->imm);
1119 }
1120 if (state->soln && --state->soln->refcount <= 0) {
1121 sfree(state->soln->moves);
1122 sfree(state->soln);
1123 }
1124 sfree(state->board);
1125 sfree(state);
1126}
1127
1128static char *solve_game(const game_state *state, const game_state *currstate,
1129 const char *aux, char **error)
1130{
1131 int *moves;
1132 int nmoves;
1133 int i;
1134 char *ret, *p, sep;
1135
1136 /*
1137 * Run the solver and attempt to find the shortest solution
1138 * from the current position.
1139 */
1140 nmoves = solve_board(state->w, state->h, state->board,
1141 state->imm->forcefield, state->tx, state->ty,
1142 -1, &moves);
1143
1144 if (nmoves < 0) {
1145 *error = "Unable to find a solution to this puzzle";
1146 return NULL;
1147 }
1148 if (nmoves == 0) {
1149 *error = "Puzzle is already solved";
1150 return NULL;
1151 }
1152
1153 /*
1154 * Encode the resulting solution as a move string.
1155 */
1156 ret = snewn(nmoves * 40, char);
1157 p = ret;
1158 sep = 'S';
1159
1160 for (i = 0; i < nmoves; i++) {
1161 p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]);
1162 sep = ',';
1163 }
1164
1165 sfree(moves);
1166 assert(p - ret < nmoves * 40);
1167 ret = sresize(ret, p+1 - ret, char);
1168
1169 return ret;
1170}
1171
1172static int game_can_format_as_text_now(const game_params *params)
1173{
1174 return TRUE;
1175}
1176
1177static char *game_text_format(const game_state *state)
1178{
1179 return board_text_format(state->w, state->h, state->board,
1180 state->imm->forcefield);
1181}
1182
1183struct game_ui {
1184 int dragging;
1185 int drag_anchor;
1186 int drag_offset_x, drag_offset_y;
1187 int drag_currpos;
1188 unsigned char *reachable;
1189 int *bfs_queue; /* used as scratch in interpret_move */
1190};
1191
1192static game_ui *new_ui(const game_state *state)
1193{
1194 int w = state->w, h = state->h, wh = w*h;
1195 game_ui *ui = snew(game_ui);
1196
1197 ui->dragging = FALSE;
1198 ui->drag_anchor = ui->drag_currpos = -1;
1199 ui->drag_offset_x = ui->drag_offset_y = -1;
1200 ui->reachable = snewn(wh, unsigned char);
1201 memset(ui->reachable, 0, wh);
1202 ui->bfs_queue = snewn(wh, int);
1203
1204 return ui;
1205}
1206
1207static void free_ui(game_ui *ui)
1208{
1209 sfree(ui->bfs_queue);
1210 sfree(ui->reachable);
1211 sfree(ui);
1212}
1213
1214static char *encode_ui(const game_ui *ui)
1215{
1216 return NULL;
1217}
1218
1219static void decode_ui(game_ui *ui, const char *encoding)
1220{
1221}
1222
1223static void game_changed_state(game_ui *ui, const game_state *oldstate,
1224 const game_state *newstate)
1225{
1226}
1227
1228#define PREFERRED_TILESIZE 32
1229#define TILESIZE (ds->tilesize)
1230#define BORDER (TILESIZE/2)
1231#define COORD(x) ( (x) * TILESIZE + BORDER )
1232#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1233#define BORDER_WIDTH (1 + TILESIZE/20)
1234#define HIGHLIGHT_WIDTH (1 + TILESIZE/16)
1235
1236#define FLASH_INTERVAL 0.10F
1237#define FLASH_TIME 3*FLASH_INTERVAL
1238
1239struct game_drawstate {
1240 int tilesize;
1241 int w, h;
1242 unsigned long *grid; /* what's currently displayed */
1243 int started;
1244};
1245
1246static char *interpret_move(const game_state *state, game_ui *ui,
1247 const game_drawstate *ds,
1248 int x, int y, int button)
1249{
1250 int w = state->w, h = state->h, wh = w*h;
1251 int tx, ty, i, j;
1252 int qhead, qtail;
1253
1254 if (button == LEFT_BUTTON) {
1255 tx = FROMCOORD(x);
1256 ty = FROMCOORD(y);
1257
1258 if (tx < 0 || tx >= w || ty < 0 || ty >= h ||
1259 !ISBLOCK(state->board[ty*w+tx]))
1260 return NULL; /* this click has no effect */
1261
1262 /*
1263 * User has clicked on a block. Find the block's anchor
1264 * and register that we've started dragging it.
1265 */
1266 i = ty*w+tx;
1267 while (ISDIST(state->board[i]))
1268 i -= state->board[i];
1269 assert(i >= 0 && i < wh);
1270
1271 ui->dragging = TRUE;
1272 ui->drag_anchor = i;
1273 ui->drag_offset_x = tx - (i % w);
1274 ui->drag_offset_y = ty - (i / w);
1275 ui->drag_currpos = i;
1276
1277 /*
1278 * Now we immediately bfs out from the current location of
1279 * the anchor, to find all the places to which this block
1280 * can be dragged.
1281 */
1282 memset(ui->reachable, FALSE, wh);
1283 qhead = qtail = 0;
1284 ui->reachable[i] = TRUE;
1285 ui->bfs_queue[qtail++] = i;
1286 for (j = i; j < wh; j++)
1287 if (state->board[j] == DIST(j - i))
1288 i = j;
1289 while (qhead < qtail) {
1290 int pos = ui->bfs_queue[qhead++];
1291 int x = pos % w, y = pos / w;
1292 int dir;
1293
1294 for (dir = 0; dir < 4; dir++) {
1295 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
1296 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
1297 int newpos;
1298
1299 if (x + dx < 0 || x + dx >= w ||
1300 y + dy < 0 || y + dy >= h)
1301 continue;
1302
1303 newpos = pos + dy*w + dx;
1304 if (ui->reachable[newpos])
1305 continue; /* already done this one */
1306
1307 /*
1308 * Now search the grid to see if the block we're
1309 * dragging could fit into this space.
1310 */
1311 for (j = i; j >= 0; j = (ISDIST(state->board[j]) ?
1312 j - state->board[j] : -1)) {
1313 int jx = (j+pos-ui->drag_anchor) % w;
1314 int jy = (j+pos-ui->drag_anchor) / w;
1315 int j2;
1316
1317 if (jx + dx < 0 || jx + dx >= w ||
1318 jy + dy < 0 || jy + dy >= h)
1319 break; /* this position isn't valid at all */
1320
1321 j2 = (j+pos-ui->drag_anchor) + dy*w + dx;
1322
1323 if (state->board[j2] == EMPTY &&
1324 (!state->imm->forcefield[j2] ||
1325 state->board[ui->drag_anchor] == MAINANCHOR))
1326 continue;
1327 while (ISDIST(state->board[j2]))
1328 j2 -= state->board[j2];
1329 assert(j2 >= 0 && j2 < wh);
1330 if (j2 == ui->drag_anchor)
1331 continue;
1332 else
1333 break;
1334 }
1335
1336 if (j < 0) {
1337 /*
1338 * If we got to the end of that loop without
1339 * disqualifying this position, mark it as
1340 * reachable for this drag.
1341 */
1342 ui->reachable[newpos] = TRUE;
1343 ui->bfs_queue[qtail++] = newpos;
1344 }
1345 }
1346 }
1347
1348 /*
1349 * And that's it. Update the display to reflect the start
1350 * of a drag.
1351 */
1352 return "";
1353 } else if (button == LEFT_DRAG && ui->dragging) {
1354 int dist, distlimit, dx, dy, s, px, py;
1355
1356 tx = FROMCOORD(x);
1357 ty = FROMCOORD(y);
1358
1359 tx -= ui->drag_offset_x;
1360 ty -= ui->drag_offset_y;
1361
1362 /*
1363 * Now search outwards from (tx,ty), in order of Manhattan
1364 * distance, until we find a reachable square.
1365 */
1366 distlimit = w+tx;
1367 distlimit = max(distlimit, h+ty);
1368 distlimit = max(distlimit, tx);
1369 distlimit = max(distlimit, ty);
1370 for (dist = 0; dist <= distlimit; dist++) {
1371 for (dx = -dist; dx <= dist; dx++)
1372 for (s = -1; s <= +1; s += 2) {
1373 dy = s * (dist - abs(dx));
1374 px = tx + dx;
1375 py = ty + dy;
1376 if (px >= 0 && px < w && py >= 0 && py < h &&
1377 ui->reachable[py*w+px]) {
1378 ui->drag_currpos = py*w+px;
1379 return "";
1380 }
1381 }
1382 }
1383 return NULL; /* give up - this drag has no effect */
1384 } else if (button == LEFT_RELEASE && ui->dragging) {
1385 char data[256], *str;
1386
1387 /*
1388 * Terminate the drag, and if the piece has actually moved
1389 * then return a move string quoting the old and new
1390 * locations of the piece's anchor.
1391 */
1392 if (ui->drag_anchor != ui->drag_currpos) {
1393 sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos);
1394 str = dupstr(data);
1395 } else
1396 str = ""; /* null move; just update the UI */
1397
1398 ui->dragging = FALSE;
1399 ui->drag_anchor = ui->drag_currpos = -1;
1400 ui->drag_offset_x = ui->drag_offset_y = -1;
1401 memset(ui->reachable, 0, wh);
1402
1403 return str;
1404 } else if (button == ' ' && state->soln) {
1405 /*
1406 * Make the next move in the stored solution.
1407 */
1408 char data[256];
1409 int a1, a2;
1410
1411 a1 = state->soln->moves[state->soln_index*2];
1412 a2 = state->soln->moves[state->soln_index*2+1];
1413 if (a1 == state->lastmoved_pos)
1414 a1 = state->lastmoved;
1415
1416 sprintf(data, "M%d-%d", a1, a2);
1417 return dupstr(data);
1418 }
1419
1420 return NULL;
1421}
1422
1423static int move_piece(int w, int h, const unsigned char *src,
1424 unsigned char *dst, unsigned char *ff, int from, int to)
1425{
1426 int wh = w*h;
1427 int i, j;
1428
1429 if (!ISANCHOR(dst[from]))
1430 return FALSE;
1431
1432 /*
1433 * Scan to the far end of the piece's linked list.
1434 */
1435 for (i = j = from; j < wh; j++)
1436 if (src[j] == DIST(j - i))
1437 i = j;
1438
1439 /*
1440 * Remove the piece from its old location in the new
1441 * game state.
1442 */
1443 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1))
1444 dst[j] = EMPTY;
1445
1446 /*
1447 * And put it back in at the new location.
1448 */
1449 for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) {
1450 int jn = j + to - from;
1451 if (jn < 0 || jn >= wh)
1452 return FALSE;
1453 if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) {
1454 dst[jn] = src[j];
1455 } else {
1456 return FALSE;
1457 }
1458 }
1459
1460 return TRUE;
1461}
1462
1463static game_state *execute_move(const game_state *state, const char *move)
1464{
1465 int w = state->w, h = state->h /* , wh = w*h */;
1466 char c;
1467 int a1, a2, n, movesize;
1468 game_state *ret = dup_game(state);
1469
1470 while (*move) {
1471 c = *move;
1472 if (c == 'S') {
1473 /*
1474 * This is a solve move, so we just set up a stored
1475 * solution path.
1476 */
1477 if (ret->soln && --ret->soln->refcount <= 0) {
1478 sfree(ret->soln->moves);
1479 sfree(ret->soln);
1480 }
1481 ret->soln = snew(struct game_solution);
1482 ret->soln->nmoves = 0;
1483 ret->soln->moves = NULL;
1484 ret->soln->refcount = 1;
1485 ret->soln_index = 0;
1486 ret->cheated = TRUE;
1487
1488 movesize = 0;
1489 move++;
1490 while (1) {
1491 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) {
1492 free_game(ret);
1493 return NULL;
1494 }
1495
1496 /*
1497 * Special case: if the first move in the solution
1498 * involves the piece for which we already have a
1499 * partial stored move, adjust the source point to
1500 * the original starting point of that piece.
1501 */
1502 if (ret->soln->nmoves == 0 && a1 == ret->lastmoved)
1503 a1 = ret->lastmoved_pos;
1504
1505 if (ret->soln->nmoves >= movesize) {
1506 movesize = (ret->soln->nmoves + 48) * 4 / 3;
1507 ret->soln->moves = sresize(ret->soln->moves,
1508 2*movesize, int);
1509 }
1510
1511 ret->soln->moves[2*ret->soln->nmoves] = a1;
1512 ret->soln->moves[2*ret->soln->nmoves+1] = a2;
1513 ret->soln->nmoves++;
1514 move += n;
1515 if (*move != ',')
1516 break;
1517 move++; /* eat comma */
1518 }
1519 } else if (c == 'M') {
1520 move++;
1521 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 ||
1522 !move_piece(w, h, state->board, ret->board,
1523 state->imm->forcefield, a1, a2)) {
1524 free_game(ret);
1525 return NULL;
1526 }
1527 if (a1 == ret->lastmoved) {
1528 /*
1529 * If the player has moved the same piece as they
1530 * moved last time, don't increment the move
1531 * count. In fact, if they've put the piece back
1532 * where it started from, _decrement_ the move
1533 * count.
1534 */
1535 if (a2 == ret->lastmoved_pos) {
1536 ret->movecount--; /* reverted last move */
1537 ret->lastmoved = ret->lastmoved_pos = -1;
1538 } else {
1539 ret->lastmoved = a2;
1540 /* don't change lastmoved_pos */
1541 }
1542 } else {
1543 ret->lastmoved = a2;
1544 ret->lastmoved_pos = a1;
1545 ret->movecount++;
1546 }
1547
1548 /*
1549 * If we have a stored solution path, see if we've
1550 * strayed from it or successfully made the next move
1551 * along it.
1552 */
1553 if (ret->soln && ret->lastmoved_pos >= 0) {
1554 if (ret->lastmoved_pos !=
1555 ret->soln->moves[ret->soln_index*2]) {
1556 /* strayed from the path */
1557 ret->soln->refcount--;
1558 assert(ret->soln->refcount > 0);
1559 /* `state' at least still exists */
1560 ret->soln = NULL;
1561 ret->soln_index = -1;
1562 } else if (ret->lastmoved ==
1563 ret->soln->moves[ret->soln_index*2+1]) {
1564 /* advanced along the path */
1565 ret->soln_index++;
1566 if (ret->soln_index >= ret->soln->nmoves) {
1567 /* finished the path! */
1568 ret->soln->refcount--;
1569 assert(ret->soln->refcount > 0);
1570 /* `state' at least still exists */
1571 ret->soln = NULL;
1572 ret->soln_index = -1;
1573 }
1574 }
1575 }
1576
1577 if (ret->board[a2] == MAINANCHOR &&
1578 a2 == ret->ty * w + ret->tx && ret->completed < 0)
1579 ret->completed = ret->movecount;
1580 move += n;
1581 } else {
1582 free_game(ret);
1583 return NULL;
1584 }
1585 if (*move == ';')
1586 move++;
1587 else if (*move) {
1588 free_game(ret);
1589 return NULL;
1590 }
1591 }
1592
1593 return ret;
1594}
1595
1596/* ----------------------------------------------------------------------
1597 * Drawing routines.
1598 */
1599
1600static void game_compute_size(const game_params *params, int tilesize,
1601 int *x, int *y)
1602{
1603 /* fool the macros */
1604 struct dummy { int tilesize; } dummy, *ds = &dummy;
1605 dummy.tilesize = tilesize;
1606
1607 *x = params->w * TILESIZE + 2*BORDER;
1608 *y = params->h * TILESIZE + 2*BORDER;
1609}
1610
1611static void game_set_size(drawing *dr, game_drawstate *ds,
1612 const game_params *params, int tilesize)
1613{
1614 ds->tilesize = tilesize;
1615}
1616
1617static void raise_colour(float *target, float *src, float *limit)
1618{
1619 int i;
1620 for (i = 0; i < 3; i++)
1621 target[i] = (2*src[i] + limit[i]) / 3;
1622}
1623
1624static float *game_colours(frontend *fe, int *ncolours)
1625{
1626 float *ret = snewn(3 * NCOLOURS, float);
1627
1628 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1629
1630 /*
1631 * When dragging a tile, we light it up a bit.
1632 */
1633 raise_colour(ret+3*COL_DRAGGING,
1634 ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT);
1635 raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT,
1636 ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT);
1637 raise_colour(ret+3*COL_DRAGGING_LOWLIGHT,
1638 ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT);
1639
1640 /*
1641 * The main tile is tinted blue.
1642 */
1643 ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1644 ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1];
1645 ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2];
1646 game_mkhighlight_specific(fe, ret, COL_MAIN,
1647 COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT);
1648
1649 /*
1650 * And we light that up a bit too when dragging.
1651 */
1652 raise_colour(ret+3*COL_MAIN_DRAGGING,
1653 ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT);
1654 raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT,
1655 ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1656 raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT,
1657 ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1658
1659 /*
1660 * The target area on the floor is tinted green.
1661 */
1662 ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1663 ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1];
1664 ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1665 game_mkhighlight_specific(fe, ret, COL_TARGET,
1666 COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT);
1667
1668 *ncolours = NCOLOURS;
1669 return ret;
1670}
1671
1672static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1673{
1674 int w = state->w, h = state->h, wh = w*h;
1675 struct game_drawstate *ds = snew(struct game_drawstate);
1676 int i;
1677
1678 ds->tilesize = 0;
1679 ds->w = w;
1680 ds->h = h;
1681 ds->started = FALSE;
1682 ds->grid = snewn(wh, unsigned long);
1683 for (i = 0; i < wh; i++)
1684 ds->grid[i] = ~(unsigned long)0;
1685
1686 return ds;
1687}
1688
1689static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1690{
1691 sfree(ds->grid);
1692 sfree(ds);
1693}
1694
1695#define BG_NORMAL 0x00000001UL
1696#define BG_TARGET 0x00000002UL
1697#define BG_FORCEFIELD 0x00000004UL
1698#define FLASH_LOW 0x00000008UL
1699#define FLASH_HIGH 0x00000010UL
1700#define FG_WALL 0x00000020UL
1701#define FG_MAIN 0x00000040UL
1702#define FG_NORMAL 0x00000080UL
1703#define FG_DRAGGING 0x00000100UL
1704#define FG_SHADOW 0x00000200UL
1705#define FG_SOLVEPIECE 0x00000400UL
1706#define FG_MAINPIECESH 11
1707#define FG_SHADOWSH 19
1708
1709#define PIECE_LBORDER 0x00000001UL
1710#define PIECE_TBORDER 0x00000002UL
1711#define PIECE_RBORDER 0x00000004UL
1712#define PIECE_BBORDER 0x00000008UL
1713#define PIECE_TLCORNER 0x00000010UL
1714#define PIECE_TRCORNER 0x00000020UL
1715#define PIECE_BLCORNER 0x00000040UL
1716#define PIECE_BRCORNER 0x00000080UL
1717#define PIECE_MASK 0x000000FFUL
1718
1719/*
1720 * Utility function.
1721 */
1722#define TYPE_MASK 0xF000
1723#define COL_MASK 0x0FFF
1724#define TYPE_RECT 0x0000
1725#define TYPE_TLCIRC 0x4000
1726#define TYPE_TRCIRC 0x5000
1727#define TYPE_BLCIRC 0x6000
1728#define TYPE_BRCIRC 0x7000
1729static void maybe_rect(drawing *dr, int x, int y, int w, int h,
1730 int coltype, int col2)
1731{
1732 int colour = coltype & COL_MASK, type = coltype & TYPE_MASK;
1733
1734 if (colour > NCOLOURS)
1735 return;
1736 if (type == TYPE_RECT) {
1737 draw_rect(dr, x, y, w, h, colour);
1738 } else {
1739 int cx, cy, r;
1740
1741 clip(dr, x, y, w, h);
1742
1743 cx = x;
1744 cy = y;
1745 r = w-1;
1746 if (type & 0x1000)
1747 cx += r;
1748 if (type & 0x2000)
1749 cy += r;
1750
1751 if (col2 == -1 || col2 == coltype) {
1752 assert(w == h);
1753 draw_circle(dr, cx, cy, r, colour, colour);
1754 } else {
1755 /*
1756 * We aim to draw a quadrant of a circle in two
1757 * different colours. We do this using Bresenham's
1758 * algorithm directly, because the Puzzles drawing API
1759 * doesn't have a draw-sector primitive.
1760 */
1761 int bx, by, bd, bd2;
1762 int xm = (type & 0x1000 ? -1 : +1);
1763 int ym = (type & 0x2000 ? -1 : +1);
1764
1765 by = r;
1766 bx = 0;
1767 bd = 0;
1768 while (by >= bx) {
1769 /*
1770 * Plot the point.
1771 */
1772 {
1773 int x1 = cx+xm*bx, y1 = cy+ym*bx;
1774 int x2, y2;
1775
1776 x2 = cx+xm*by; y2 = y1;
1777 draw_rect(dr, min(x1,x2), min(y1,y2),
1778 abs(x1-x2)+1, abs(y1-y2)+1, colour);
1779 x2 = x1; y2 = cy+ym*by;
1780 draw_rect(dr, min(x1,x2), min(y1,y2),
1781 abs(x1-x2)+1, abs(y1-y2)+1, col2);
1782 }
1783
1784 bd += 2*bx + 1;
1785 bd2 = bd - (2*by - 1);
1786 if (abs(bd2) < abs(bd)) {
1787 bd = bd2;
1788 by--;
1789 }
1790 bx++;
1791 }
1792 }
1793
1794 unclip(dr);
1795 }
1796}
1797
1798static void draw_wallpart(drawing *dr, game_drawstate *ds,
1799 int tx, int ty, unsigned long val,
1800 int cl, int cc, int ch)
1801{
1802 int coords[6];
1803
1804 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
1805 if (val & PIECE_LBORDER)
1806 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE,
1807 ch);
1808 if (val & PIECE_RBORDER)
1809 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1810 HIGHLIGHT_WIDTH, TILESIZE, cl);
1811 if (val & PIECE_TBORDER)
1812 draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch);
1813 if (val & PIECE_BBORDER)
1814 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1815 TILESIZE, HIGHLIGHT_WIDTH, cl);
1816 if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) {
1817 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1818 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1819 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1820 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1821 coords[0] = tx - 1;
1822 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1823 coords[2] = tx + HIGHLIGHT_WIDTH;
1824 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1825 coords[4] = tx - 1;
1826 coords[5] = ty + TILESIZE;
1827 draw_polygon(dr, coords, 3, ch, ch);
1828 unclip(dr);
1829 } else if (val & PIECE_BLCORNER) {
1830 draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1831 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1832 clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1833 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1834 coords[0] = tx - 1;
1835 coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1836 coords[2] = tx + HIGHLIGHT_WIDTH;
1837 coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1838 coords[4] = tx - 1;
1839 coords[5] = ty + TILESIZE;
1840 draw_polygon(dr, coords, 3, cl, cl);
1841 unclip(dr);
1842 }
1843 if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) {
1844 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1845 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1846 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1847 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1848 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1849 coords[1] = ty - 1;
1850 coords[2] = tx + TILESIZE;
1851 coords[3] = ty - 1;
1852 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1853 coords[5] = ty + HIGHLIGHT_WIDTH;
1854 draw_polygon(dr, coords, 3, ch, ch);
1855 unclip(dr);
1856 } else if (val & PIECE_TRCORNER) {
1857 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1858 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1859 clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1860 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1861 coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1862 coords[1] = ty - 1;
1863 coords[2] = tx + TILESIZE;
1864 coords[3] = ty - 1;
1865 coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1866 coords[5] = ty + HIGHLIGHT_WIDTH;
1867 draw_polygon(dr, coords, 3, cl, cl);
1868 unclip(dr);
1869 }
1870 if (val & PIECE_TLCORNER)
1871 draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1872 if (val & PIECE_BRCORNER)
1873 draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH,
1874 ty+TILESIZE-HIGHLIGHT_WIDTH,
1875 HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1876}
1877
1878static void draw_piecepart(drawing *dr, game_drawstate *ds,
1879 int tx, int ty, unsigned long val,
1880 int cl, int cc, int ch)
1881{
1882 int x[6], y[6];
1883
1884 /*
1885 * Drawing the blocks is hellishly fiddly. The blocks don't
1886 * stretch to the full size of the tile; there's a border
1887 * around them of size BORDER_WIDTH. Then they have bevelled
1888 * borders of size HIGHLIGHT_WIDTH, and also rounded corners.
1889 *
1890 * I tried for some time to find a clean and clever way to
1891 * figure out what needed drawing from the corner and border
1892 * flags, but in the end the cleanest way I could find was the
1893 * following. We divide the grid square into 25 parts by
1894 * ruling four horizontal and four vertical lines across it;
1895 * those lines are at BORDER_WIDTH and BORDER_WIDTH +
1896 * HIGHLIGHT_WIDTH from the top, from the bottom, from the
1897 * left and from the right. Then we carefully consider each of
1898 * the resulting 25 sections of square, and decide separately
1899 * what needs to go in it based on the flags. In complicated
1900 * cases there can be up to five possibilities affecting any
1901 * given section (no corner or border flags, just the corner
1902 * flag, one border flag, the other border flag, both border
1903 * flags). So there's a lot of very fiddly logic here and all
1904 * I could really think to do was give it my best shot and
1905 * then test it and correct all the typos. Not fun to write,
1906 * and I'm sure it isn't fun to read either, but it seems to
1907 * work.
1908 */
1909
1910 x[0] = tx;
1911 x[1] = x[0] + BORDER_WIDTH;
1912 x[2] = x[1] + HIGHLIGHT_WIDTH;
1913 x[5] = tx + TILESIZE;
1914 x[4] = x[5] - BORDER_WIDTH;
1915 x[3] = x[4] - HIGHLIGHT_WIDTH;
1916
1917 y[0] = ty;
1918 y[1] = y[0] + BORDER_WIDTH;
1919 y[2] = y[1] + HIGHLIGHT_WIDTH;
1920 y[5] = ty + TILESIZE;
1921 y[4] = y[5] - BORDER_WIDTH;
1922 y[3] = y[4] - HIGHLIGHT_WIDTH;
1923
1924#define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q]
1925
1926 maybe_rect(dr, RECT(0,0),
1927 (val & (PIECE_TLCORNER | PIECE_TBORDER |
1928 PIECE_LBORDER)) ? -1 : cc, -1);
1929 maybe_rect(dr, RECT(1,0),
1930 (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 :
1931 (val & PIECE_LBORDER) ? ch : cc, -1);
1932 maybe_rect(dr, RECT(2,0),
1933 (val & PIECE_TBORDER) ? -1 : cc, -1);
1934 maybe_rect(dr, RECT(3,0),
1935 (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 :
1936 (val & PIECE_RBORDER) ? cl : cc, -1);
1937 maybe_rect(dr, RECT(4,0),
1938 (val & (PIECE_TRCORNER | PIECE_TBORDER |
1939 PIECE_RBORDER)) ? -1 : cc, -1);
1940 maybe_rect(dr, RECT(0,1),
1941 (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 :
1942 (val & PIECE_TBORDER) ? ch : cc, -1);
1943 maybe_rect(dr, RECT(1,1),
1944 (val & PIECE_TLCORNER) ? cc : -1, -1);
1945 maybe_rect(dr, RECT(1,1),
1946 (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC :
1947 !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC :
1948 (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1);
1949 maybe_rect(dr, RECT(2,1),
1950 (val & PIECE_TBORDER) ? ch : cc, -1);
1951 maybe_rect(dr, RECT(3,1),
1952 (val & PIECE_TRCORNER) ? cc : -1, -1);
1953 maybe_rect(dr, RECT(3,1),
1954 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch :
1955 (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl :
1956 !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC :
1957 (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC :
1958 cc, ch);
1959 maybe_rect(dr, RECT(4,1),
1960 (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 :
1961 (val & PIECE_TBORDER) ? ch : cc, -1);
1962 maybe_rect(dr, RECT(0,2),
1963 (val & PIECE_LBORDER) ? -1 : cc, -1);
1964 maybe_rect(dr, RECT(1,2),
1965 (val & PIECE_LBORDER) ? ch : cc, -1);
1966 maybe_rect(dr, RECT(2,2),
1967 cc, -1);
1968 maybe_rect(dr, RECT(3,2),
1969 (val & PIECE_RBORDER) ? cl : cc, -1);
1970 maybe_rect(dr, RECT(4,2),
1971 (val & PIECE_RBORDER) ? -1 : cc, -1);
1972 maybe_rect(dr, RECT(0,3),
1973 (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 :
1974 (val & PIECE_BBORDER) ? cl : cc, -1);
1975 maybe_rect(dr, RECT(1,3),
1976 (val & PIECE_BLCORNER) ? cc : -1, -1);
1977 maybe_rect(dr, RECT(1,3),
1978 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl :
1979 (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch :
1980 !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC :
1981 (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC :
1982 cc, cl);
1983 maybe_rect(dr, RECT(2,3),
1984 (val & PIECE_BBORDER) ? cl : cc, -1);
1985 maybe_rect(dr, RECT(3,3),
1986 (val & PIECE_BRCORNER) ? cc : -1, -1);
1987 maybe_rect(dr, RECT(3,3),
1988 (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC :
1989 !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC :
1990 (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1);
1991 maybe_rect(dr, RECT(4,3),
1992 (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 :
1993 (val & PIECE_BBORDER) ? cl : cc, -1);
1994 maybe_rect(dr, RECT(0,4),
1995 (val & (PIECE_BLCORNER | PIECE_BBORDER |
1996 PIECE_LBORDER)) ? -1 : cc, -1);
1997 maybe_rect(dr, RECT(1,4),
1998 (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 :
1999 (val & PIECE_LBORDER) ? ch : cc, -1);
2000 maybe_rect(dr, RECT(2,4),
2001 (val & PIECE_BBORDER) ? -1 : cc, -1);
2002 maybe_rect(dr, RECT(3,4),
2003 (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 :
2004 (val & PIECE_RBORDER) ? cl : cc, -1);
2005 maybe_rect(dr, RECT(4,4),
2006 (val & (PIECE_BRCORNER | PIECE_BBORDER |
2007 PIECE_RBORDER)) ? -1 : cc, -1);
2008
2009#undef RECT
2010}
2011
2012static void draw_tile(drawing *dr, game_drawstate *ds,
2013 int x, int y, unsigned long val)
2014{
2015 int tx = COORD(x), ty = COORD(y);
2016 int cc, ch, cl;
2017
2018 /*
2019 * Draw the tile background.
2020 */
2021 if (val & BG_TARGET)
2022 cc = COL_TARGET;
2023 else
2024 cc = COL_BACKGROUND;
2025 ch = cc+1;
2026 cl = cc+2;
2027 if (val & FLASH_LOW)
2028 cc = cl;
2029 else if (val & FLASH_HIGH)
2030 cc = ch;
2031
2032 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
2033 if (val & BG_FORCEFIELD) {
2034 /*
2035 * Cattle-grid effect to indicate that nothing but the
2036 * main block can slide over this square.
2037 */
2038 int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH));
2039 int i;
2040
2041 for (i = 1; i < n; i += 3) {
2042 draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl);
2043 draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl);
2044 }
2045 }
2046
2047 /*
2048 * Draw the tile midground: a shadow of a block, for
2049 * displaying partial solutions.
2050 */
2051 if (val & FG_SHADOW) {
2052 draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK,
2053 cl, cl, cl);
2054 }
2055
2056 /*
2057 * Draw the tile foreground, i.e. some section of a block or
2058 * wall.
2059 */
2060 if (val & FG_WALL) {
2061 cc = COL_BACKGROUND;
2062 ch = cc+1;
2063 cl = cc+2;
2064 if (val & FLASH_LOW)
2065 cc = cl;
2066 else if (val & FLASH_HIGH)
2067 cc = ch;
2068
2069 draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2070 cl, cc, ch);
2071 } else if (val & (FG_MAIN | FG_NORMAL)) {
2072 if (val & FG_DRAGGING)
2073 cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING);
2074 else
2075 cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND);
2076 ch = cc+1;
2077 cl = cc+2;
2078
2079 if (val & FLASH_LOW)
2080 cc = cl;
2081 else if (val & (FLASH_HIGH | FG_SOLVEPIECE))
2082 cc = ch;
2083
2084 draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2085 cl, cc, ch);
2086 }
2087
2088 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
2089}
2090
2091static unsigned long find_piecepart(int w, int h, int *dsf, int x, int y)
2092{
2093 int i = y*w+x;
2094 int canon = dsf_canonify(dsf, i);
2095 unsigned long val = 0;
2096
2097 if (x == 0 || canon != dsf_canonify(dsf, i-1))
2098 val |= PIECE_LBORDER;
2099 if (y== 0 || canon != dsf_canonify(dsf, i-w))
2100 val |= PIECE_TBORDER;
2101 if (x == w-1 || canon != dsf_canonify(dsf, i+1))
2102 val |= PIECE_RBORDER;
2103 if (y == h-1 || canon != dsf_canonify(dsf, i+w))
2104 val |= PIECE_BBORDER;
2105 if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) &&
2106 canon != dsf_canonify(dsf, i-1-w))
2107 val |= PIECE_TLCORNER;
2108 if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) &&
2109 canon != dsf_canonify(dsf, i+1-w))
2110 val |= PIECE_TRCORNER;
2111 if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) &&
2112 canon != dsf_canonify(dsf, i-1+w))
2113 val |= PIECE_BLCORNER;
2114 if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) &&
2115 canon != dsf_canonify(dsf, i+1+w))
2116 val |= PIECE_BRCORNER;
2117 return val;
2118}
2119
2120static void game_redraw(drawing *dr, game_drawstate *ds,
2121 const game_state *oldstate, const game_state *state,
2122 int dir, const game_ui *ui,
2123 float animtime, float flashtime)
2124{
2125 int w = state->w, h = state->h, wh = w*h;
2126 unsigned char *board;
2127 int *dsf;
2128 int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst;
2129
2130 if (!ds->started) {
2131 /*
2132 * The initial contents of the window are not guaranteed
2133 * and can vary with front ends. To be on the safe side,
2134 * all games should start by drawing a big
2135 * background-colour rectangle covering the whole window.
2136 */
2137 draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
2138 ds->started = TRUE;
2139 }
2140
2141 /*
2142 * Construct the board we'll be displaying (which may be
2143 * different from the one in state if ui describes a drag in
2144 * progress).
2145 */
2146 board = snewn(wh, unsigned char);
2147 memcpy(board, state->board, wh);
2148 if (ui->dragging) {
2149 int mpret = move_piece(w, h, state->board, board,
2150 state->imm->forcefield,
2151 ui->drag_anchor, ui->drag_currpos);
2152 assert(mpret);
2153 }
2154
2155 if (state->soln) {
2156 solvesrc = state->soln->moves[state->soln_index*2];
2157 solvedst = state->soln->moves[state->soln_index*2+1];
2158 if (solvesrc == state->lastmoved_pos)
2159 solvesrc = state->lastmoved;
2160 if (solvesrc == ui->drag_anchor)
2161 solvesrc = ui->drag_currpos;
2162 } else
2163 solvesrc = solvedst = -1;
2164
2165 /*
2166 * Build a dsf out of that board, so we can conveniently tell
2167 * which edges are connected and which aren't.
2168 */
2169 dsf = snew_dsf(wh);
2170 mainanchor = -1;
2171 for (y = 0; y < h; y++)
2172 for (x = 0; x < w; x++) {
2173 int i = y*w+x;
2174
2175 if (ISDIST(board[i]))
2176 dsf_merge(dsf, i, i - board[i]);
2177 if (board[i] == MAINANCHOR)
2178 mainanchor = i;
2179 if (board[i] == WALL) {
2180 if (x > 0 && board[i-1] == WALL)
2181 dsf_merge(dsf, i, i-1);
2182 if (y > 0 && board[i-w] == WALL)
2183 dsf_merge(dsf, i, i-w);
2184 }
2185 }
2186 assert(mainanchor >= 0);
2187 mainpos = dsf_canonify(dsf, mainanchor);
2188 dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1;
2189 solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1;
2190
2191 /*
2192 * Now we can construct the data about what we want to draw.
2193 */
2194 for (y = 0; y < h; y++)
2195 for (x = 0; x < w; x++) {
2196 int i = y*w+x;
2197 int j;
2198 unsigned long val;
2199 int canon;
2200
2201 /*
2202 * See if this square is part of the target area.
2203 */
2204 j = i + mainanchor - (state->ty * w + state->tx);
2205 while (j >= 0 && j < wh && ISDIST(board[j]))
2206 j -= board[j];
2207 if (j == mainanchor)
2208 val = BG_TARGET;
2209 else
2210 val = BG_NORMAL;
2211
2212 if (state->imm->forcefield[i])
2213 val |= BG_FORCEFIELD;
2214
2215 if (flashtime > 0) {
2216 int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1;
2217 val |= (flashtype ? FLASH_LOW : FLASH_HIGH);
2218 }
2219
2220 if (board[i] != EMPTY) {
2221 canon = dsf_canonify(dsf, i);
2222
2223 if (board[i] == WALL)
2224 val |= FG_WALL;
2225 else if (canon == mainpos)
2226 val |= FG_MAIN;
2227 else
2228 val |= FG_NORMAL;
2229 if (canon == dragpos)
2230 val |= FG_DRAGGING;
2231 if (canon == solvepos)
2232 val |= FG_SOLVEPIECE;
2233
2234 /*
2235 * Now look around to see if other squares
2236 * belonging to the same block are adjacent to us.
2237 */
2238 val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH;
2239 }
2240
2241 /*
2242 * If we're in the middle of showing a solution,
2243 * display a shadow piece for the target of the
2244 * current move.
2245 */
2246 if (solvepos >= 0) {
2247 int si = i - solvedst + solvesrc;
2248 if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) {
2249 val |= find_piecepart(w, h, dsf,
2250 si % w, si / w) << FG_SHADOWSH;
2251 val |= FG_SHADOW;
2252 }
2253 }
2254
2255 if (val != ds->grid[i]) {
2256 draw_tile(dr, ds, x, y, val);
2257 ds->grid[i] = val;
2258 }
2259 }
2260
2261 /*
2262 * Update the status bar.
2263 */
2264 {
2265 char statusbuf[256];
2266
2267 sprintf(statusbuf, "%sMoves: %d",
2268 (state->completed >= 0 ?
2269 (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
2270 (state->cheated ? "Auto-solver used. " : "")),
2271 (state->completed >= 0 ? state->completed : state->movecount));
2272 if (state->minmoves >= 0)
2273 sprintf(statusbuf+strlen(statusbuf), " (min %d)",
2274 state->minmoves);
2275
2276 status_bar(dr, statusbuf);
2277 }
2278
2279 sfree(dsf);
2280 sfree(board);
2281}
2282
2283static float game_anim_length(const game_state *oldstate,
2284 const game_state *newstate, int dir, game_ui *ui)
2285{
2286 return 0.0F;
2287}
2288
2289static float game_flash_length(const game_state *oldstate,
2290 const game_state *newstate, int dir, game_ui *ui)
2291{
2292 if (oldstate->completed < 0 && newstate->completed >= 0)
2293 return FLASH_TIME;
2294
2295 return 0.0F;
2296}
2297
2298static int game_status(const game_state *state)
2299{
2300 return state->completed ? +1 : 0;
2301}
2302
2303static int game_timing_state(const game_state *state, game_ui *ui)
2304{
2305 return TRUE;
2306}
2307
2308static void game_print_size(const game_params *params, float *x, float *y)
2309{
2310}
2311
2312static void game_print(drawing *dr, const game_state *state, int tilesize)
2313{
2314}
2315
2316#ifdef COMBINED
2317#define thegame slide
2318#endif
2319
2320const struct game thegame = {
2321 "Slide", NULL, NULL,
2322 default_params,
2323 game_fetch_preset, NULL,
2324 decode_params,
2325 encode_params,
2326 free_params,
2327 dup_params,
2328 TRUE, game_configure, custom_params,
2329 validate_params,
2330 new_game_desc,
2331 validate_desc,
2332 new_game,
2333 dup_game,
2334 free_game,
2335 TRUE, solve_game,
2336 TRUE, game_can_format_as_text_now, game_text_format,
2337 new_ui,
2338 free_ui,
2339 encode_ui,
2340 decode_ui,
2341 game_changed_state,
2342 interpret_move,
2343 execute_move,
2344 PREFERRED_TILESIZE, game_compute_size, game_set_size,
2345 game_colours,
2346 game_new_drawstate,
2347 game_free_drawstate,
2348 game_redraw,
2349 game_anim_length,
2350 game_flash_length,
2351 game_status,
2352 FALSE, FALSE, game_print_size, game_print,
2353 TRUE, /* wants_statusbar */
2354 FALSE, game_timing_state,
2355 0, /* flags */
2356};
2357
2358#ifdef STANDALONE_SOLVER
2359
2360#include <stdarg.h>
2361
2362int main(int argc, char **argv)
2363{
2364 game_params *p;
2365 game_state *s;
2366 char *id = NULL, *desc, *err;
2367 int count = FALSE;
2368 int ret;
2369 int *moves;
2370
2371 while (--argc > 0) {
2372 char *p = *++argv;
2373 /*
2374 if (!strcmp(p, "-v")) {
2375 verbose = TRUE;
2376 } else
2377 */
2378 if (!strcmp(p, "-c")) {
2379 count = TRUE;
2380 } else if (*p == '-') {
2381 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2382 return 1;
2383 } else {
2384 id = p;
2385 }
2386 }
2387
2388 if (!id) {
2389 fprintf(stderr, "usage: %s [-c | -v] <game_id>\n", argv[0]);
2390 return 1;
2391 }
2392
2393 desc = strchr(id, ':');
2394 if (!desc) {
2395 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2396 return 1;
2397 }
2398 *desc++ = '\0';
2399
2400 p = default_params();
2401 decode_params(p, id);
2402 err = validate_desc(p, desc);
2403 if (err) {
2404 fprintf(stderr, "%s: %s\n", argv[0], err);
2405 return 1;
2406 }
2407 s = new_game(NULL, p, desc);
2408
2409 ret = solve_board(s->w, s->h, s->board, s->imm->forcefield,
2410 s->tx, s->ty, -1, &moves);
2411 if (ret < 0) {
2412 printf("No solution found\n");
2413 } else {
2414 int index = 0;
2415 if (count) {
2416 printf("%d moves required\n", ret);
2417 return 0;
2418 }
2419 while (1) {
2420 int moveret;
2421 char *text = board_text_format(s->w, s->h, s->board,
2422 s->imm->forcefield);
2423 game_state *s2;
2424
2425 printf("position %d:\n%s", index, text);
2426
2427 if (index >= ret)
2428 break;
2429
2430 s2 = dup_game(s);
2431 moveret = move_piece(s->w, s->h, s->board,
2432 s2->board, s->imm->forcefield,
2433 moves[index*2], moves[index*2+1]);
2434 assert(moveret);
2435
2436 free_game(s);
2437 s = s2;
2438 index++;
2439 }
2440 }
2441
2442 return 0;
2443}
2444
2445#endif
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.R b/apps/plugins/puzzles/src/unfinished/sokoban.R
new file mode 100644
index 0000000000..3b6dab56ba
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/sokoban.R
@@ -0,0 +1,19 @@
1# -*- makefile -*-
2
3sokoban : [X] GTK COMMON sokoban sokoban-icon|no-icon
4
5sokoban : [G] WINDOWS COMMON sokoban sokoban.res?
6
7ALL += sokoban[COMBINED]
8
9!begin am gtk
10GAMES += sokoban
11!end
12
13!begin >list.c
14 A(sokoban) \
15!end
16
17!begin >gamedesc.txt
18sokoban:sokoban.exe:Sokoban:Barrel-pushing puzzle:Push all the barrels into the target squares.
19!end
diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.c b/apps/plugins/puzzles/src/unfinished/sokoban.c
new file mode 100644
index 0000000000..2f0af35bc2
--- /dev/null
+++ b/apps/plugins/puzzles/src/unfinished/sokoban.c
@@ -0,0 +1,1479 @@
1/*
2 * sokoban.c: An implementation of the well-known Sokoban barrel-
3 * pushing game. Random generation is too simplistic to be
4 * credible, but the rest of the gameplay works well enough to use
5 * it with hand-written level descriptions.
6 */
7
8/*
9 * TODO:
10 *
11 * - I think it would be better to ditch the `prev' array, and
12 * instead make the `dist' array strictly monotonic (by having
13 * each distance be something like I*A+S, where A is the grid
14 * area, I the number of INITIAL squares trampled on, and S the
15 * number of harmless spaces moved through). This would permit
16 * the path-tracing when a pull is actually made to choose
17 * randomly from all the possible shortest routes, which would
18 * be superior in terms of eliminating directional bias.
19 * + So when tracing the path back to the current px,py, we
20 * look at all four adjacent squares, find the minimum
21 * distance, check that it's _strictly smaller_ than that of
22 * the current square, and restrict our choice to precisely
23 * those squares with that minimum distance.
24 * + The other place `prev' is currently used is in the check
25 * for consistency of a pull. We would have to replace the
26 * check for whether prev[ny*w+nx]==oy*w+ox with a check that
27 * made sure there was at least one adjacent square with a
28 * smaller distance which _wasn't_ oy*w+ox. Then when we did
29 * the path-tracing we'd also have to take this special case
30 * into account.
31 *
32 * - More discriminating choice of pull. (Snigger.)
33 * + favour putting targets in clumps
34 * + try to shoot for a reasonably consistent number of barrels
35 * (adjust willingness to generate a new barrel depending on
36 * how many are already present)
37 * + adjust willingness to break new ground depending on how
38 * much is already broken
39 *
40 * - generation time parameters:
41 * + enable NetHack mode (and find a better place for the hole)
42 * + decide how many of the remaining Is should be walls
43 *
44 * - at the end of generation, randomly position the starting
45 * player coordinates, probably by (somehow) reusing the same
46 * bfs currently inside the loop.
47 *
48 * - possible backtracking?
49 *
50 * - IWBNI we could spot completely unreachable bits of level at
51 * the outside, and not bother drawing grid lines for them. The
52 * NH levels currently look a bit weird with grid lines on the
53 * outside of the boundary.
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <assert.h>
60#include <ctype.h>
61#include <math.h>
62
63#include "puzzles.h"
64
65/*
66 * Various subsets of these constants are used during game
67 * generation, game play, game IDs and the game_drawstate.
68 */
69#define INITIAL 'i' /* used only in game generation */
70#define SPACE 's'
71#define WALL 'w'
72#define PIT 'p'
73#define DEEP_PIT 'd'
74#define TARGET 't'
75#define BARREL 'b'
76#define BARRELTARGET 'f' /* target is 'f'illed */
77#define PLAYER 'u' /* yo'u'; used in game IDs */
78#define PLAYERTARGET 'v' /* bad letter: v is to u as t is to s */
79#define INVALID '!' /* used in drawstate to force redraw */
80/*
81 * We also support the use of any capital letter as a barrel, which
82 * will be displayed with that letter as a label. (This facilitates
83 * people distributing annotated game IDs for particular Sokoban
84 * levels, so they can accompany them with verbal instructions
85 * about pushing particular barrels in particular ways.) Therefore,
86 * to find out whether something is a barrel, we need a test
87 * function which does a bit more than just comparing to BARREL.
88 *
89 * When resting on target squares, capital-letter barrels are
90 * replaced with their control-character value (A -> ^A).
91 */
92#define IS_PLAYER(c) ( (c)==PLAYER || (c)==PLAYERTARGET )
93#define IS_BARREL(c) ( (c)==BARREL || (c)==BARRELTARGET || \
94 ((c)>='A' && (c)<='Z') || ((c)>=1 && (c)<=26) )
95#define IS_ON_TARGET(c) ( (c)==TARGET || (c)==BARRELTARGET || \
96 (c)==PLAYERTARGET || ((c)>=1 && (c)<=26) )
97#define TARGETISE(b) ( (b)==BARREL ? BARRELTARGET : (b)-('A'-1) )
98#define DETARGETISE(b) ( (b)==BARRELTARGET ? BARREL : (b)+('A'-1) )
99#define BARREL_LABEL(b) ( (b)>='A'&&(b)<='Z' ? (b) : \
100 (b)>=1 && (b)<=26 ? (b)+('A'-1) : 0 )
101
102#define DX(d) (d == 0 ? -1 : d == 2 ? +1 : 0)
103#define DY(d) (d == 1 ? -1 : d == 3 ? +1 : 0)
104
105#define FLASH_LENGTH 0.3F
106
107enum {
108 COL_BACKGROUND,
109 COL_TARGET,
110 COL_PIT,
111 COL_DEEP_PIT,
112 COL_BARREL,
113 COL_PLAYER,
114 COL_TEXT,
115 COL_GRID,
116 COL_OUTLINE,
117 COL_HIGHLIGHT,
118 COL_LOWLIGHT,
119 COL_WALL,
120 NCOLOURS
121};
122
123struct game_params {
124 int w, h;
125 /*
126 * FIXME: a parameter involving degree of filling in?
127 */
128};
129
130struct game_state {
131 game_params p;
132 unsigned char *grid;
133 int px, py;
134 int completed;
135};
136
137static game_params *default_params(void)
138{
139 game_params *ret = snew(game_params);
140
141 ret->w = 12;
142 ret->h = 10;
143
144 return ret;
145}
146
147static void free_params(game_params *params)
148{
149 sfree(params);
150}
151
152static game_params *dup_params(const game_params *params)
153{
154 game_params *ret = snew(game_params);
155 *ret = *params; /* structure copy */
156 return ret;
157}
158
159static const struct game_params sokoban_presets[] = {
160 { 12, 10 },
161 { 16, 12 },
162 { 20, 16 },
163};
164
165static int game_fetch_preset(int i, char **name, game_params **params)
166{
167 game_params p, *ret;
168 char *retname;
169 char namebuf[80];
170
171 if (i < 0 || i >= lenof(sokoban_presets))
172 return FALSE;
173
174 p = sokoban_presets[i];
175 ret = dup_params(&p);
176 sprintf(namebuf, "%dx%d", ret->w, ret->h);
177 retname = dupstr(namebuf);
178
179 *params = ret;
180 *name = retname;
181 return TRUE;
182}
183
184static void decode_params(game_params *params, char const *string)
185{
186 params->w = params->h = atoi(string);
187 while (*string && isdigit((unsigned char)*string)) string++;
188 if (*string == 'x') {
189 string++;
190 params->h = atoi(string);
191 }
192}
193
194static char *encode_params(const game_params *params, int full)
195{
196 char data[256];
197
198 sprintf(data, "%dx%d", params->w, params->h);
199
200 return dupstr(data);
201}
202
203static config_item *game_configure(const game_params *params)
204{
205 config_item *ret;
206 char buf[80];
207
208 ret = snewn(3, config_item);
209
210 ret[0].name = "Width";
211 ret[0].type = C_STRING;
212 sprintf(buf, "%d", params->w);
213 ret[0].sval = dupstr(buf);
214 ret[0].ival = 0;
215
216 ret[1].name = "Height";
217 ret[1].type = C_STRING;
218 sprintf(buf, "%d", params->h);
219 ret[1].sval = dupstr(buf);
220 ret[1].ival = 0;
221
222 ret[2].name = NULL;
223 ret[2].type = C_END;
224 ret[2].sval = NULL;
225 ret[2].ival = 0;
226
227 return ret;
228}
229
230static game_params *custom_params(const config_item *cfg)
231{
232 game_params *ret = snew(game_params);
233
234 ret->w = atoi(cfg[0].sval);
235 ret->h = atoi(cfg[1].sval);
236
237 return ret;
238}
239
240static char *validate_params(const game_params *params, int full)
241{
242 if (params->w < 4 || params->h < 4)
243 return "Width and height must both be at least 4";
244
245 return NULL;
246}
247
248/* ----------------------------------------------------------------------
249 * Game generation mechanism.
250 *
251 * To generate a Sokoban level, we begin with a completely blank
252 * grid and make valid inverse moves. Grid squares can be in a
253 * number of states. The states are:
254 *
255 * - INITIAL: this square has not as yet been touched by any
256 * inverse move, which essentially means we haven't decided what
257 * it is yet.
258 *
259 * - SPACE: this square is a space.
260 *
261 * - TARGET: this square is a space which is also the target for a
262 * barrel.
263 *
264 * - BARREL: this square contains a barrel.
265 *
266 * - BARRELTARGET: this square contains a barrel _on_ a target.
267 *
268 * - WALL: this square is a wall.
269 *
270 * - PLAYER: this square contains the player.
271 *
272 * - PLAYERTARGET: this square contains the player on a target.
273 *
274 * We begin with every square of the in state INITIAL, apart from a
275 * solid ring of WALLs around the edge. We randomly position the
276 * PLAYER somewhere. Thereafter our valid moves are:
277 *
278 * - to move the PLAYER in one direction _pulling_ a barrel after
279 * us. For this to work, we must have SPACE or INITIAL in the
280 * direction we're moving, and BARREL or BARRELTARGET in the
281 * direction we're moving away from. We leave SPACE or TARGET
282 * respectively in the vacated square.
283 *
284 * - to create a new barrel by transforming an INITIAL square into
285 * BARRELTARGET.
286 *
287 * - to move the PLAYER freely through SPACE and TARGET squares,
288 * leaving SPACE or TARGET where it started.
289 *
290 * - to move the player through INITIAL squares, carving a tunnel
291 * of SPACEs as it goes.
292 *
293 * We try to avoid destroying INITIAL squares wherever possible (if
294 * there's a path to where we want to be using only SPACE, then we
295 * should always use that). At the end of generation, every square
296 * still in state INITIAL is one which was not required at any
297 * point during generation, which means we can randomly choose
298 * whether to make it SPACE or WALL.
299 *
300 * It's unclear as yet what the right strategy for wall placement
301 * should be. Too few WALLs will yield many alternative solutions
302 * to the puzzle, whereas too many might rule out so many
303 * possibilities that the intended solution becomes obvious.
304 */
305
306static void sokoban_generate(int w, int h, unsigned char *grid, int moves,
307 int nethack, random_state *rs)
308{
309 struct pull {
310 int ox, oy, nx, ny, score;
311 };
312
313 struct pull *pulls;
314 int *dist, *prev, *heap;
315 int x, y, px, py, i, j, d, heapsize, npulls;
316
317 pulls = snewn(w * h * 4, struct pull);
318 dist = snewn(w * h, int);
319 prev = snewn(w * h, int);
320 heap = snewn(w * h, int);
321
322 /*
323 * Configure the initial grid.
324 */
325 for (y = 0; y < h; y++)
326 for (x = 0; x < w; x++)
327 grid[y*w+x] = (x == 0 || y == 0 || x == w-1 || y == h-1 ?
328 WALL : INITIAL);
329 if (nethack)
330 grid[1] = DEEP_PIT;
331
332 /*
333 * Place the player.
334 */
335 i = random_upto(rs, (w-2) * (h-2));
336 x = 1 + i % (w-2);
337 y = 1 + i / (w-2);
338 grid[y*w+x] = SPACE;
339 px = x;
340 py = y;
341
342 /*
343 * Now loop around making random inverse Sokoban moves. In this
344 * loop we aim to make one actual barrel-pull per iteration,
345 * plus as many free moves as are necessary to get into
346 * position for that pull.
347 */
348 while (moves-- >= 0) {
349 /*
350 * First enumerate all the viable barrel-pulls we can
351 * possibly make, counting two pulls of the same barrel in
352 * different directions as different. We also include pulls
353 * we can perform by creating a new barrel. Each pull is
354 * marked with the amount of violence it would have to do
355 * to the grid.
356 */
357 npulls = 0;
358 for (y = 0; y < h; y++)
359 for (x = 0; x < w; x++)
360 for (d = 0; d < 4; d++) {
361 int dx = DX(d);
362 int dy = DY(d);
363 int nx = x + dx, ny = y + dy;
364 int npx = nx + dx, npy = ny + dy;
365 int score = 0;
366
367 /*
368 * The candidate move is to put the player at
369 * (nx,ny), and move him to (npx,npy), pulling
370 * a barrel at (x,y) to (nx,ny). So first we
371 * must check that all those squares are within
372 * the boundaries of the grid. For this it is
373 * sufficient to check npx,npy.
374 */
375 if (npx < 0 || npx >= w || npy < 0 || npy >= h)
376 continue;
377
378 /*
379 * (x,y) must either be a barrel, or a square
380 * which we can convert into a barrel.
381 */
382 switch (grid[y*w+x]) {
383 case BARREL: case BARRELTARGET:
384 break;
385 case INITIAL:
386 if (nethack)
387 continue;
388 score += 10 /* new_barrel_score */;
389 break;
390 case DEEP_PIT:
391 if (!nethack)
392 continue;
393 break;
394 default:
395 continue;
396 }
397
398 /*
399 * (nx,ny) must either be a space, or a square
400 * which we can convert into a space.
401 */
402 switch (grid[ny*w+nx]) {
403 case SPACE: case TARGET:
404 break;
405 case INITIAL:
406 score += 3 /* new_space_score */;
407 break;
408 default:
409 continue;
410 }
411
412 /*
413 * (npx,npy) must also either be a space, or a
414 * square which we can convert into a space.
415 */
416 switch (grid[npy*w+npx]) {
417 case SPACE: case TARGET:
418 break;
419 case INITIAL:
420 score += 3 /* new_space_score */;
421 break;
422 default:
423 continue;
424 }
425
426 /*
427 * That's sufficient to tag this as a possible
428 * pull right now. We still don't know if we
429 * can reach the required player position, but
430 * that's a job for the subsequent BFS phase to
431 * tell us.
432 */
433 pulls[npulls].ox = x;
434 pulls[npulls].oy = y;
435 pulls[npulls].nx = nx;
436 pulls[npulls].ny = ny;
437 pulls[npulls].score = score;
438#ifdef GENERATION_DIAGNOSTICS
439 printf("found potential pull: (%d,%d)-(%d,%d) cost %d\n",
440 pulls[npulls].ox, pulls[npulls].oy,
441 pulls[npulls].nx, pulls[npulls].ny,
442 pulls[npulls].score);
443#endif
444 npulls++;
445 }
446#ifdef GENERATION_DIAGNOSTICS
447 printf("found %d potential pulls\n", npulls);
448#endif
449
450 /*
451 * If there are no pulls available at all, we give up.
452 *
453 * (FIXME: or perhaps backtrack?)
454 */
455 if (npulls == 0)
456 break;
457
458 /*
459 * Now we do a BFS from our current position, to find all
460 * the squares we can get the player into.
461 *
462 * This BFS is unusually tricky. We want to give a positive
463 * distance only to squares which we have to carve through
464 * INITIALs to get to, which means we can't just stick
465 * every square we reach on the end of our to-do list.
466 * Instead, we must maintain our list as a proper priority
467 * queue.
468 */
469 for (i = 0; i < w*h; i++)
470 dist[i] = prev[i] = -1;
471
472 heap[0] = py*w+px;
473 heapsize = 1;
474 dist[py*w+px] = 0;
475
476#define PARENT(n) ( ((n)-1)/2 )
477#define LCHILD(n) ( 2*(n)+1 )
478#define RCHILD(n) ( 2*(n)+2 )
479#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
480
481 while (heapsize > 0) {
482 /*
483 * Pull the smallest element off the heap: it's at
484 * position 0. Move the arbitrary element from the very
485 * end of the heap into position 0.
486 */
487 y = heap[0] / w;
488 x = heap[0] % w;
489
490 heapsize--;
491 heap[0] = heap[heapsize];
492
493 /*
494 * Now repeatedly move that arbitrary element down the
495 * heap by swapping it with the more suitable of its
496 * children.
497 */
498 i = 0;
499 while (1) {
500 int lc, rc;
501
502 lc = LCHILD(i);
503 rc = RCHILD(i);
504
505 if (lc >= heapsize)
506 break; /* we've hit bottom */
507
508 if (rc >= heapsize) {
509 /*
510 * Special case: there is only one child to
511 * check.
512 */
513 if (dist[heap[i]] > dist[heap[lc]])
514 SWAP(heap[i], heap[lc]);
515
516 /* _Now_ we've hit bottom. */
517 break;
518 } else {
519 /*
520 * The common case: there are two children and
521 * we must check them both.
522 */
523 if (dist[heap[i]] > dist[heap[lc]] ||
524 dist[heap[i]] > dist[heap[rc]]) {
525 /*
526 * Pick the more appropriate child to swap with
527 * (i.e. the one which would want to be the
528 * parent if one were above the other - as one
529 * is about to be).
530 */
531 if (dist[heap[lc]] > dist[heap[rc]]) {
532 SWAP(heap[i], heap[rc]);
533 i = rc;
534 } else {
535 SWAP(heap[i], heap[lc]);
536 i = lc;
537 }
538 } else {
539 /* This element is in the right place; we're done. */
540 break;
541 }
542 }
543 }
544
545 /*
546 * OK, that's given us (x,y) for this phase of the
547 * search. Now try all directions from here.
548 */
549
550 for (d = 0; d < 4; d++) {
551 int dx = DX(d);
552 int dy = DY(d);
553 int nx = x + dx, ny = y + dy;
554 if (nx < 0 || nx >= w || ny < 0 || ny >= h)
555 continue;
556 if (grid[ny*w+nx] != SPACE && grid[ny*w+nx] != TARGET &&
557 grid[ny*w+nx] != INITIAL)
558 continue;
559 if (dist[ny*w+nx] == -1) {
560 dist[ny*w+nx] = dist[y*w+x] + (grid[ny*w+nx] == INITIAL);
561 prev[ny*w+nx] = y*w+x;
562
563 /*
564 * Now insert ny*w+nx at the end of the heap,
565 * and move it down to its appropriate resting
566 * place.
567 */
568 i = heapsize;
569 heap[heapsize++] = ny*w+nx;
570
571 /*
572 * Swap element n with its parent repeatedly to
573 * preserve the heap property.
574 */
575
576 while (i > 0) {
577 int p = PARENT(i);
578
579 if (dist[heap[p]] > dist[heap[i]]) {
580 SWAP(heap[p], heap[i]);
581 i = p;
582 } else
583 break;
584 }
585 }
586 }
587 }
588
589#undef PARENT
590#undef LCHILD
591#undef RCHILD
592#undef SWAP
593
594#ifdef GENERATION_DIAGNOSTICS
595 printf("distance map:\n");
596 for (i = 0; i < h; i++) {
597 for (j = 0; j < w; j++) {
598 int d = dist[i*w+j];
599 int c;
600 if (d < 0)
601 c = '#';
602 else if (d >= 36)
603 c = '!';
604 else if (d >= 10)
605 c = 'A' - 10 + d;
606 else
607 c = '0' + d;
608 putchar(c);
609 }
610 putchar('\n');
611 }
612#endif
613
614 /*
615 * Now we can go back through the `pulls' array, adjusting
616 * the score for each pull depending on how hard it is to
617 * reach its starting point, and also throwing out any
618 * whose starting points are genuinely unreachable even
619 * with the possibility of carving through INITIAL squares.
620 */
621 for (i = j = 0; i < npulls; i++) {
622#ifdef GENERATION_DIAGNOSTICS
623 printf("potential pull (%d,%d)-(%d,%d)",
624 pulls[i].ox, pulls[i].oy,
625 pulls[i].nx, pulls[i].ny);
626#endif
627 x = pulls[i].nx;
628 y = pulls[i].ny;
629 if (dist[y*w+x] < 0) {
630#ifdef GENERATION_DIAGNOSTICS
631 printf(" unreachable\n");
632#endif
633 continue; /* this pull isn't feasible at all */
634 } else {
635 /*
636 * Another nasty special case we have to check is
637 * whether the initial barrel location (ox,oy) is
638 * on the path used to reach the square. This can
639 * occur if that square is in state INITIAL: the
640 * pull is initially considered valid on the basis
641 * that the INITIAL can become BARRELTARGET, and
642 * it's also considered reachable on the basis that
643 * INITIAL can be turned into SPACE, but it can't
644 * be both at once.
645 *
646 * Fortunately, if (ox,oy) is on the path at all,
647 * it must be only one space from the end, so this
648 * is easy to spot and rule out.
649 */
650 if (prev[y*w+x] == pulls[i].oy*w+pulls[i].ox) {
651#ifdef GENERATION_DIAGNOSTICS
652 printf(" goes through itself\n");
653#endif
654 continue; /* this pull isn't feasible at all */
655 }
656 pulls[j] = pulls[i]; /* structure copy */
657 pulls[j].score += dist[y*w+x] * 3 /* new_space_score */;
658#ifdef GENERATION_DIAGNOSTICS
659 printf(" reachable at distance %d (cost now %d)\n",
660 dist[y*w+x], pulls[j].score);
661#endif
662 j++;
663 }
664 }
665 npulls = j;
666
667 /*
668 * Again, if there are no pulls available at all, we give
669 * up.
670 *
671 * (FIXME: or perhaps backtrack?)
672 */
673 if (npulls == 0)
674 break;
675
676 /*
677 * Now choose which pull to make. On the one hand we should
678 * prefer pulls which do less damage to the INITIAL squares
679 * (thus, ones for which we can already get into position
680 * via existing SPACEs, and for which the barrel already
681 * exists and doesn't have to be invented); on the other,
682 * we want to avoid _always_ preferring such pulls, on the
683 * grounds that that will lead to levels without very much
684 * stuff in.
685 *
686 * When creating new barrels, we prefer creations which are
687 * next to existing TARGET squares.
688 *
689 * FIXME: for the moment I'll make this very simple indeed.
690 */
691 i = random_upto(rs, npulls);
692
693 /*
694 * Actually make the pull, including carving a path to get
695 * to the site if necessary.
696 */
697 x = pulls[i].nx;
698 y = pulls[i].ny;
699 while (prev[y*w+x] >= 0) {
700 int p;
701
702 if (grid[y*w+x] == INITIAL)
703 grid[y*w+x] = SPACE;
704
705 p = prev[y*w+x];
706 y = p / w;
707 x = p % w;
708 }
709 px = 2*pulls[i].nx - pulls[i].ox;
710 py = 2*pulls[i].ny - pulls[i].oy;
711 if (grid[py*w+px] == INITIAL)
712 grid[py*w+px] = SPACE;
713 if (grid[pulls[i].ny*w+pulls[i].nx] == TARGET)
714 grid[pulls[i].ny*w+pulls[i].nx] = BARRELTARGET;
715 else
716 grid[pulls[i].ny*w+pulls[i].nx] = BARREL;
717 if (grid[pulls[i].oy*w+pulls[i].ox] == BARREL)
718 grid[pulls[i].oy*w+pulls[i].ox] = SPACE;
719 else if (grid[pulls[i].oy*w+pulls[i].ox] != DEEP_PIT)
720 grid[pulls[i].oy*w+pulls[i].ox] = TARGET;
721 }
722
723 sfree(heap);
724 sfree(prev);
725 sfree(dist);
726 sfree(pulls);
727
728 if (grid[py*w+px] == TARGET)
729 grid[py*w+px] = PLAYERTARGET;
730 else
731 grid[py*w+px] = PLAYER;
732}
733
734static char *new_game_desc(const game_params *params, random_state *rs,
735 char **aux, int interactive)
736{
737 int w = params->w, h = params->h;
738 char *desc;
739 int desclen, descpos, descsize, prev, count;
740 unsigned char *grid;
741 int i, j;
742
743 /*
744 * FIXME: perhaps some more interesting means of choosing how
745 * many moves to try?
746 */
747 grid = snewn(w*h, unsigned char);
748 sokoban_generate(w, h, grid, w*h, FALSE, rs);
749
750 desclen = descpos = descsize = 0;
751 desc = NULL;
752 prev = -1;
753 count = 0;
754 for (i = 0; i < w*h; i++) {
755 if (descsize < desclen + 40) {
756 descsize = desclen + 100;
757 desc = sresize(desc, descsize, char);
758 desc[desclen] = '\0';
759 }
760 switch (grid[i]) {
761 case INITIAL:
762 j = 'w'; /* FIXME: make some of these 's'? */
763 break;
764 case SPACE:
765 j = 's';
766 break;
767 case WALL:
768 j = 'w';
769 break;
770 case TARGET:
771 j = 't';
772 break;
773 case BARREL:
774 j = 'b';
775 break;
776 case BARRELTARGET:
777 j = 'f';
778 break;
779 case DEEP_PIT:
780 j = 'd';
781 break;
782 case PLAYER:
783 j = 'u';
784 break;
785 case PLAYERTARGET:
786 j = 'v';
787 break;
788 default:
789 j = '?';
790 break;
791 }
792 assert(j != '?');
793 if (j != prev) {
794 desc[desclen++] = j;
795 descpos = desclen;
796 prev = j;
797 count = 1;
798 } else {
799 count++;
800 desclen = descpos + sprintf(desc+descpos, "%d", count);
801 }
802 }
803
804 sfree(grid);
805
806 return desc;
807}
808
809static char *validate_desc(const game_params *params, const char *desc)
810{
811 int w = params->w, h = params->h;
812 int area = 0;
813 int nplayers = 0;
814
815 while (*desc) {
816 int c = *desc++;
817 int n = 1;
818 if (*desc && isdigit((unsigned char)*desc)) {
819 n = atoi(desc);
820 while (*desc && isdigit((unsigned char)*desc)) desc++;
821 }
822
823 area += n;
824
825 if (c == PLAYER || c == PLAYERTARGET)
826 nplayers += n;
827 else if (c == INITIAL || c == SPACE || c == WALL || c == TARGET ||
828 c == PIT || c == DEEP_PIT || IS_BARREL(c))
829 /* ok */;
830 else
831 return "Invalid character in game description";
832 }
833
834 if (area > w*h)
835 return "Too much data in game description";
836 if (area < w*h)
837 return "Too little data in game description";
838 if (nplayers < 1)
839 return "No starting player position specified";
840 if (nplayers > 1)
841 return "More than one starting player position specified";
842
843 return NULL;
844}
845
846static game_state *new_game(midend *me, const game_params *params,
847 const char *desc)
848{
849 int w = params->w, h = params->h;
850 game_state *state = snew(game_state);
851 int i;
852
853 state->p = *params; /* structure copy */
854 state->grid = snewn(w*h, unsigned char);
855 state->px = state->py = -1;
856 state->completed = FALSE;
857
858 i = 0;
859
860 while (*desc) {
861 int c = *desc++;
862 int n = 1;
863 if (*desc && isdigit((unsigned char)*desc)) {
864 n = atoi(desc);
865 while (*desc && isdigit((unsigned char)*desc)) desc++;
866 }
867
868 if (c == PLAYER || c == PLAYERTARGET) {
869 state->py = i / w;
870 state->px = i % w;
871 c = IS_ON_TARGET(c) ? TARGET : SPACE;
872 }
873
874 while (n-- > 0)
875 state->grid[i++] = c;
876 }
877
878 assert(i == w*h);
879 assert(state->px != -1 && state->py != -1);
880
881 return state;
882}
883
884static game_state *dup_game(const game_state *state)
885{
886 int w = state->p.w, h = state->p.h;
887 game_state *ret = snew(game_state);
888
889 ret->p = state->p; /* structure copy */
890 ret->grid = snewn(w*h, unsigned char);
891 memcpy(ret->grid, state->grid, w*h);
892 ret->px = state->px;
893 ret->py = state->py;
894 ret->completed = state->completed;
895
896 return ret;
897}
898
899static void free_game(game_state *state)
900{
901 sfree(state->grid);
902 sfree(state);
903}
904
905static char *solve_game(const game_state *state, const game_state *currstate,
906 const char *aux, char **error)
907{
908 return NULL;
909}
910
911static int game_can_format_as_text_now(const game_params *params)
912{
913 return TRUE;
914}
915
916static char *game_text_format(const game_state *state)
917{
918 return NULL;
919}
920
921static game_ui *new_ui(const game_state *state)
922{
923 return NULL;
924}
925
926static void free_ui(game_ui *ui)
927{
928}
929
930static char *encode_ui(const game_ui *ui)
931{
932 return NULL;
933}
934
935static void decode_ui(game_ui *ui, const char *encoding)
936{
937}
938
939static void game_changed_state(game_ui *ui, const game_state *oldstate,
940 const game_state *newstate)
941{
942}
943
944struct game_drawstate {
945 game_params p;
946 int tilesize;
947 int started;
948 unsigned short *grid;
949};
950
951#define PREFERRED_TILESIZE 32
952#define TILESIZE (ds->tilesize)
953#define BORDER (TILESIZE)
954#define HIGHLIGHT_WIDTH (TILESIZE / 10)
955#define COORD(x) ( (x) * TILESIZE + BORDER )
956#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
957
958/*
959 * I'm going to need to do most of the move-type analysis in both
960 * interpret_move and execute_move, so I'll abstract it out into a
961 * subfunction. move_type() returns -1 for an illegal move, 0 for a
962 * movement, and 1 for a push.
963 */
964int move_type(game_state *state, int dx, int dy)
965{
966 int w = state->p.w, h = state->p.h;
967 int px = state->px, py = state->py;
968 int nx, ny, nbx, nby;
969
970 assert(dx >= -1 && dx <= +1);
971 assert(dy >= -1 && dy <= +1);
972 assert(dx || dy);
973
974 nx = px + dx;
975 ny = py + dy;
976
977 /*
978 * Disallow any move that goes off the grid.
979 */
980 if (nx < 0 || nx >= w || ny < 0 || ny >= h)
981 return -1;
982
983 /*
984 * Examine the target square of the move to see whether it's a
985 * space, a barrel, or a wall.
986 */
987
988 if (state->grid[ny*w+nx] == WALL ||
989 state->grid[ny*w+nx] == PIT ||
990 state->grid[ny*w+nx] == DEEP_PIT)
991 return -1; /* this one's easy; just disallow it */
992
993 if (IS_BARREL(state->grid[ny*w+nx])) {
994 /*
995 * This is a push move. For a start, that means it must not
996 * be diagonal.
997 */
998 if (dy && dx)
999 return -1;
1000
1001 /*
1002 * Now find the location of the third square involved in
1003 * the push, and stop if it's off the edge.
1004 */
1005 nbx = nx + dx;
1006 nby = ny + dy;
1007 if (nbx < 0 || nbx >= w || nby < 0 || nby >= h)
1008 return -1;
1009
1010 /*
1011 * That third square must be able to accept a barrel.
1012 */
1013 if (state->grid[nby*w+nbx] == SPACE ||
1014 state->grid[nby*w+nbx] == TARGET ||
1015 state->grid[nby*w+nbx] == PIT ||
1016 state->grid[nby*w+nbx] == DEEP_PIT) {
1017 /*
1018 * The push is valid.
1019 */
1020 return 1;
1021 } else {
1022 return -1;
1023 }
1024 } else {
1025 /*
1026 * This is just an ordinary move. We've already checked the
1027 * target square, so the only thing left to check is that a
1028 * diagonal move has a space on one side to have notionally
1029 * gone through.
1030 */
1031 if (dx && dy &&
1032 state->grid[(py+dy)*w+px] != SPACE &&
1033 state->grid[(py+dy)*w+px] != TARGET &&
1034 state->grid[py*w+(px+dx)] != SPACE &&
1035 state->grid[py*w+(px+dx)] != TARGET)
1036 return -1;
1037
1038 /*
1039 * Otherwise, the move is valid.
1040 */
1041 return 0;
1042 }
1043}
1044
1045static char *interpret_move(const game_state *state, game_ui *ui,
1046 const game_drawstate *ds,
1047 int x, int y, int button)
1048{
1049 int dx=0, dy=0;
1050 char *move;
1051
1052 /*
1053 * Diagonal movement is supported as it is in NetHack: it's
1054 * for movement only (never pushing), and one of the two
1055 * squares adjacent to both the source and destination
1056 * squares must be free to move through. In other words, it
1057 * is only a shorthand for two orthogonal moves and cannot
1058 * change the nature of the actual puzzle game.
1059 */
1060 if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
1061 dx = 0, dy = -1;
1062 else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
1063 dx = 0, dy = +1;
1064 else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
1065 dx = -1, dy = 0;
1066 else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
1067 dx = +1, dy = 0;
1068 else if (button == (MOD_NUM_KEYPAD | '7'))
1069 dx = -1, dy = -1;
1070 else if (button == (MOD_NUM_KEYPAD | '9'))
1071 dx = +1, dy = -1;
1072 else if (button == (MOD_NUM_KEYPAD | '1'))
1073 dx = -1, dy = +1;
1074 else if (button == (MOD_NUM_KEYPAD | '3'))
1075 dx = +1, dy = +1;
1076 else if (button == LEFT_BUTTON)
1077 {
1078 if(x < COORD(state->px))
1079 dx = -1;
1080 else if (x > COORD(state->px + 1))
1081 dx = 1;
1082 if(y < COORD(state->py))
1083 dy = -1;
1084 else if (y > COORD(state->py + 1))
1085 dy = 1;
1086 }
1087 else
1088 return NULL;
1089
1090 if((dx == 0) && (dy == 0))
1091 return(NULL);
1092
1093 if (move_type(state, dx, dy) < 0)
1094 return NULL;
1095
1096 move = snewn(2, char);
1097 move[1] = '\0';
1098 move[0] = '5' - 3*dy + dx;
1099 return move;
1100}
1101
1102static game_state *execute_move(const game_state *state, const char *move)
1103{
1104 int w = state->p.w, h = state->p.h;
1105 int px = state->px, py = state->py;
1106 int dx, dy, nx, ny, nbx, nby, type, m, i, freebarrels, freetargets;
1107 game_state *ret;
1108
1109 if (*move < '1' || *move == '5' || *move > '9' || move[1])
1110 return NULL; /* invalid move string */
1111
1112 m = *move - '0';
1113 dx = (m + 2) % 3 - 1;
1114 dy = 2 - (m + 2) / 3;
1115 type = move_type(state, dx, dy);
1116 if (type < 0)
1117 return NULL;
1118
1119 ret = dup_game(state);
1120
1121 nx = px + dx;
1122 ny = py + dy;
1123 nbx = nx + dx;
1124 nby = ny + dy;
1125
1126 if (type) {
1127 int b;
1128
1129 /*
1130 * Push.
1131 */
1132 b = ret->grid[ny*w+nx];
1133 if (IS_ON_TARGET(b)) {
1134 ret->grid[ny*w+nx] = TARGET;
1135 b = DETARGETISE(b);
1136 } else
1137 ret->grid[ny*w+nx] = SPACE;
1138
1139 if (ret->grid[nby*w+nbx] == PIT)
1140 ret->grid[nby*w+nbx] = SPACE;
1141 else if (ret->grid[nby*w+nbx] == DEEP_PIT)
1142 /* do nothing - the pit eats the barrel and remains there */;
1143 else if (ret->grid[nby*w+nbx] == TARGET)
1144 ret->grid[nby*w+nbx] = TARGETISE(b);
1145 else
1146 ret->grid[nby*w+nbx] = b;
1147 }
1148
1149 ret->px = nx;
1150 ret->py = ny;
1151
1152 /*
1153 * Check for completion. This is surprisingly complicated,
1154 * given the presence of pits and deep pits, and also the fact
1155 * that some Sokoban levels with pits have fewer pits than
1156 * barrels (due to providing spares, e.g. NetHack's). I think
1157 * the completion condition in fact must be that the game
1158 * cannot become any _more_ complete. That is, _either_ there
1159 * are no remaining barrels not on targets, _or_ there is a
1160 * good reason why any such barrels cannot be placed. The only
1161 * available good reason is that there are no remaining pits,
1162 * no free target squares, and no deep pits at all.
1163 */
1164 if (!ret->completed) {
1165 freebarrels = FALSE;
1166 freetargets = FALSE;
1167 for (i = 0; i < w*h; i++) {
1168 int v = ret->grid[i];
1169
1170 if (IS_BARREL(v) && !IS_ON_TARGET(v))
1171 freebarrels = TRUE;
1172 if (v == DEEP_PIT || v == PIT ||
1173 (!IS_BARREL(v) && IS_ON_TARGET(v)))
1174 freetargets = TRUE;
1175 }
1176
1177 if (!freebarrels || !freetargets)
1178 ret->completed = TRUE;
1179 }
1180
1181 return ret;
1182}
1183
1184/* ----------------------------------------------------------------------
1185 * Drawing routines.
1186 */
1187
1188static void game_compute_size(const game_params *params, int tilesize,
1189 int *x, int *y)
1190{
1191 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1192 struct { int tilesize; } ads, *ds = &ads;
1193 ads.tilesize = tilesize;
1194
1195 *x = 2 * BORDER + 1 + params->w * TILESIZE;
1196 *y = 2 * BORDER + 1 + params->h * TILESIZE;
1197}
1198
1199static void game_set_size(drawing *dr, game_drawstate *ds,
1200 const game_params *params, int tilesize)
1201{
1202 ds->tilesize = tilesize;
1203}
1204
1205static float *game_colours(frontend *fe, int *ncolours)
1206{
1207 float *ret = snewn(3 * NCOLOURS, float);
1208 int i;
1209
1210 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1211
1212 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1213 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1214 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1215
1216 ret[COL_PLAYER * 3 + 0] = 0.0F;
1217 ret[COL_PLAYER * 3 + 1] = 1.0F;
1218 ret[COL_PLAYER * 3 + 2] = 0.0F;
1219
1220 ret[COL_BARREL * 3 + 0] = 0.6F;
1221 ret[COL_BARREL * 3 + 1] = 0.3F;
1222 ret[COL_BARREL * 3 + 2] = 0.0F;
1223
1224 ret[COL_TARGET * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1225 ret[COL_TARGET * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1226 ret[COL_TARGET * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1227
1228 ret[COL_PIT * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0] / 2;
1229 ret[COL_PIT * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1] / 2;
1230 ret[COL_PIT * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2] / 2;
1231
1232 ret[COL_DEEP_PIT * 3 + 0] = 0.0F;
1233 ret[COL_DEEP_PIT * 3 + 1] = 0.0F;
1234 ret[COL_DEEP_PIT * 3 + 2] = 0.0F;
1235
1236 ret[COL_TEXT * 3 + 0] = 1.0F;
1237 ret[COL_TEXT * 3 + 1] = 1.0F;
1238 ret[COL_TEXT * 3 + 2] = 1.0F;
1239
1240 ret[COL_GRID * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0];
1241 ret[COL_GRID * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1];
1242 ret[COL_GRID * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2];
1243
1244 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1245 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1246 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1247
1248 for (i = 0; i < 3; i++) {
1249 ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] +
1250 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4;
1251 }
1252
1253 *ncolours = NCOLOURS;
1254 return ret;
1255}
1256
1257static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1258{
1259 int w = state->p.w, h = state->p.h;
1260 struct game_drawstate *ds = snew(struct game_drawstate);
1261 int i;
1262
1263 ds->tilesize = 0;
1264 ds->p = state->p; /* structure copy */
1265 ds->grid = snewn(w*h, unsigned short);
1266 for (i = 0; i < w*h; i++)
1267 ds->grid[i] = INVALID;
1268 ds->started = FALSE;
1269
1270 return ds;
1271}
1272
1273static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1274{
1275 sfree(ds->grid);
1276 sfree(ds);
1277}
1278
1279static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v)
1280{
1281 int tx = COORD(x), ty = COORD(y);
1282 int bg = (v & 0x100 ? COL_HIGHLIGHT : COL_BACKGROUND);
1283
1284 v &= 0xFF;
1285
1286 clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1);
1287 draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg);
1288
1289 if (v == WALL) {
1290 int coords[6];
1291
1292 coords[0] = tx + TILESIZE;
1293 coords[1] = ty + TILESIZE;
1294 coords[2] = tx + TILESIZE;
1295 coords[3] = ty + 1;
1296 coords[4] = tx + 1;
1297 coords[5] = ty + TILESIZE;
1298 draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
1299
1300 coords[0] = tx + 1;
1301 coords[1] = ty + 1;
1302 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
1303
1304 draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH,
1305 TILESIZE - 2*HIGHLIGHT_WIDTH,
1306 TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL);
1307 } else if (v == PIT) {
1308 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1309 TILESIZE*3/7, COL_PIT, COL_OUTLINE);
1310 } else if (v == DEEP_PIT) {
1311 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1312 TILESIZE*3/7, COL_DEEP_PIT, COL_OUTLINE);
1313 } else {
1314 if (IS_ON_TARGET(v)) {
1315 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1316 TILESIZE*3/7, COL_TARGET, COL_OUTLINE);
1317 }
1318 if (IS_PLAYER(v)) {
1319 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1320 TILESIZE/3, COL_PLAYER, COL_OUTLINE);
1321 } else if (IS_BARREL(v)) {
1322 char str[2];
1323
1324 draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1325 TILESIZE/3, COL_BARREL, COL_OUTLINE);
1326 str[1] = '\0';
1327 str[0] = BARREL_LABEL(v);
1328 if (str[0]) {
1329 draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2,
1330 FONT_VARIABLE, TILESIZE/2,
1331 ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str);
1332 }
1333 }
1334 }
1335
1336 unclip(dr);
1337 draw_update(dr, tx, ty, TILESIZE, TILESIZE);
1338}
1339
1340static void game_redraw(drawing *dr, game_drawstate *ds,
1341 const game_state *oldstate, const game_state *state,
1342 int dir, const game_ui *ui,
1343 float animtime, float flashtime)
1344{
1345 int w = state->p.w, h = state->p.h /*, wh = w*h */;
1346 int x, y;
1347 int flashtype;
1348
1349 if (flashtime &&
1350 !((int)(flashtime * 3 / FLASH_LENGTH) % 2))
1351 flashtype = 0x100;
1352 else
1353 flashtype = 0;
1354
1355 /*
1356 * Initialise a fresh drawstate.
1357 */
1358 if (!ds->started) {
1359 int wid, ht;
1360
1361 /*
1362 * Blank out the window initially.
1363 */
1364 game_compute_size(&ds->p, TILESIZE, &wid, &ht);
1365 draw_rect(dr, 0, 0, wid, ht, COL_BACKGROUND);
1366 draw_update(dr, 0, 0, wid, ht);
1367
1368 /*
1369 * Draw the grid lines.
1370 */
1371 for (y = 0; y <= h; y++)
1372 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y),
1373 COL_LOWLIGHT);
1374 for (x = 0; x <= w; x++)
1375 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h),
1376 COL_LOWLIGHT);
1377
1378 ds->started = TRUE;
1379 }
1380
1381 /*
1382 * Draw the grid contents.
1383 */
1384 for (y = 0; y < h; y++)
1385 for (x = 0; x < w; x++) {
1386 int v = state->grid[y*w+x];
1387 if (y == state->py && x == state->px) {
1388 if (v == TARGET)
1389 v = PLAYERTARGET;
1390 else {
1391 assert(v == SPACE);
1392 v = PLAYER;
1393 }
1394 }
1395
1396 v |= flashtype;
1397
1398 if (ds->grid[y*w+x] != v) {
1399 draw_tile(dr, ds, x, y, v);
1400 ds->grid[y*w+x] = v;
1401 }
1402 }
1403
1404}
1405
1406static float game_anim_length(const game_state *oldstate,
1407 const game_state *newstate, int dir, game_ui *ui)
1408{
1409 return 0.0F;
1410}
1411
1412static float game_flash_length(const game_state *oldstate,
1413 const game_state *newstate, int dir, game_ui *ui)
1414{
1415 if (!oldstate->completed && newstate->completed)
1416 return FLASH_LENGTH;
1417 else
1418 return 0.0F;
1419}
1420
1421static int game_status(const game_state *state)
1422{
1423 return state->completed ? +1 : 0;
1424}
1425
1426static int game_timing_state(const game_state *state, game_ui *ui)
1427{
1428 return TRUE;
1429}
1430
1431static void game_print_size(const game_params *params, float *x, float *y)
1432{
1433}
1434
1435static void game_print(drawing *dr, const game_state *state, int tilesize)
1436{
1437}
1438
1439#ifdef COMBINED
1440#define thegame sokoban
1441#endif
1442
1443const struct game thegame = {
1444 "Sokoban", NULL, NULL,
1445 default_params,
1446 game_fetch_preset, NULL,
1447 decode_params,
1448 encode_params,
1449 free_params,
1450 dup_params,
1451 TRUE, game_configure, custom_params,
1452 validate_params,
1453 new_game_desc,
1454 validate_desc,
1455 new_game,
1456 dup_game,
1457 free_game,
1458 FALSE, solve_game,
1459 FALSE, game_can_format_as_text_now, game_text_format,
1460 new_ui,
1461 free_ui,
1462 encode_ui,
1463 decode_ui,
1464 game_changed_state,
1465 interpret_move,
1466 execute_move,
1467 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1468 game_colours,
1469 game_new_drawstate,
1470 game_free_drawstate,
1471 game_redraw,
1472 game_anim_length,
1473 game_flash_length,
1474 game_status,
1475 FALSE, FALSE, game_print_size, game_print,
1476 FALSE, /* wants_statusbar */
1477 FALSE, game_timing_state,
1478 0, /* flags */
1479};
diff --git a/apps/plugins/puzzles/src/unruly.R b/apps/plugins/puzzles/src/unruly.R
new file mode 100644
index 0000000000..064ccc35ba
--- /dev/null
+++ b/apps/plugins/puzzles/src/unruly.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3unruly : [X] GTK COMMON unruly unruly-icon|no-icon
4unruly : [G] WINDOWS COMMON unruly unruly.res|noicon.res
5
6unrulysolver : [U] unruly[STANDALONE_SOLVER] STANDALONE
7unrulysolver : [C] unruly[STANDALONE_SOLVER] STANDALONE
8
9ALL += unruly[COMBINED]
10
11!begin am gtk
12GAMES += unruly
13!end
14
15!begin >list.c
16 A(unruly) \
17!end
18
19!begin >gamedesc.txt
20unruly:unruly.exe:Unruly:Black and white grid puzzle:Fill in the black and white grid to avoid runs of three.
21!end
diff --git a/apps/plugins/puzzles/src/unruly.c b/apps/plugins/puzzles/src/unruly.c
new file mode 100644
index 0000000000..f418efa776
--- /dev/null
+++ b/apps/plugins/puzzles/src/unruly.c
@@ -0,0 +1,2071 @@
1/*
2 * unruly.c: Implementation for Binary Puzzles.
3 * (C) 2012 Lennard Sprong
4 * Created for Simon Tatham's Portable Puzzle Collection
5 * See LICENCE for licence details
6 *
7 * Objective of the game: Fill the grid with zeros and ones, with the
8 * following rules:
9 * - There can't be a run of three or more equal numbers.
10 * - Each row and column contains an equal amount of zeros and ones.
11 *
12 * This puzzle type is known under several names, including
13 * Tohu-Wa-Vohu, One and Two and Binairo.
14 *
15 * Some variants include an extra constraint, stating that no two rows or two
16 * columns may contain the same exact sequence of zeros and ones.
17 * This rule is rarely used, so it is not enabled in the default presets
18 * (but it can be selected via the Custom configurer).
19 *
20 * More information:
21 * http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm
22 */
23
24/*
25 * Possible future improvements:
26 *
27 * More solver cleverness
28 *
29 * - a counting-based deduction in which you find groups of squares
30 * which must each contain at least one of a given colour, plus
31 * other squares which are already known to be that colour, and see
32 * if you have any squares left over when you've worked out where
33 * they all have to be. This is a generalisation of the current
34 * check_near_complete: where that only covers rows with three
35 * unfilled squares, this would handle more, such as
36 * 0 . . 1 0 1 . . 0 .
37 * in which each of the two-square gaps must contain a 0, and there
38 * are three 0s placed, and that means the rightmost square can't
39 * be a 0.
40 *
41 * - an 'Unreasonable' difficulty level, supporting recursion and
42 * backtracking.
43 */
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <assert.h>
49#include <ctype.h>
50#include <math.h>
51
52#include "puzzles.h"
53
54#ifdef STANDALONE_SOLVER
55int solver_verbose = FALSE;
56#endif
57
58enum {
59 COL_BACKGROUND,
60 COL_GRID,
61 COL_EMPTY,
62 /*
63 * When editing this enum, maintain the invariants
64 * COL_n_HIGHLIGHT = COL_n + 1
65 * COL_n_LOWLIGHT = COL_n + 2
66 */
67 COL_0,
68 COL_0_HIGHLIGHT,
69 COL_0_LOWLIGHT,
70 COL_1,
71 COL_1_HIGHLIGHT,
72 COL_1_LOWLIGHT,
73 COL_CURSOR,
74 COL_ERROR,
75 NCOLOURS
76};
77
78struct game_params {
79 int w2, h2; /* full grid width and height respectively */
80 int unique; /* should row and column patterns be unique? */
81 int diff;
82};
83#define DIFFLIST(A) \
84 A(EASY,Easy, e) \
85 A(NORMAL,Normal, n) \
86
87#define ENUM(upper,title,lower) DIFF_ ## upper,
88#define TITLE(upper,title,lower) #title,
89#define ENCODE(upper,title,lower) #lower
90#define CONFIG(upper,title,lower) ":" #title
91enum { DIFFLIST(ENUM) DIFFCOUNT };
92static char const *const unruly_diffnames[] = { DIFFLIST(TITLE) };
93
94static char const unruly_diffchars[] = DIFFLIST(ENCODE);
95#define DIFFCONFIG DIFFLIST(CONFIG)
96
97const static struct game_params unruly_presets[] = {
98 { 8, 8, FALSE, DIFF_EASY},
99 { 8, 8, FALSE, DIFF_NORMAL},
100 {10, 10, FALSE, DIFF_EASY},
101 {10, 10, FALSE, DIFF_NORMAL},
102 {14, 14, FALSE, DIFF_EASY},
103 {14, 14, FALSE, DIFF_NORMAL}
104};
105
106#define DEFAULT_PRESET 0
107
108enum {
109 EMPTY,
110 N_ONE,
111 N_ZERO,
112 BOGUS
113};
114
115#define FE_HOR_ROW_LEFT 0x0001
116#define FE_HOR_ROW_MID 0x0003
117#define FE_HOR_ROW_RIGHT 0x0002
118
119#define FE_VER_ROW_TOP 0x0004
120#define FE_VER_ROW_MID 0x000C
121#define FE_VER_ROW_BOTTOM 0x0008
122
123#define FE_COUNT 0x0010
124
125#define FE_ROW_MATCH 0x0020
126#define FE_COL_MATCH 0x0040
127
128#define FF_ONE 0x0080
129#define FF_ZERO 0x0100
130#define FF_CURSOR 0x0200
131
132#define FF_FLASH1 0x0400
133#define FF_FLASH2 0x0800
134#define FF_IMMUTABLE 0x1000
135
136struct game_state {
137 int w2, h2;
138 int unique;
139 char *grid;
140 unsigned char *immutable;
141
142 int completed, cheated;
143};
144
145static game_params *default_params(void)
146{
147 game_params *ret = snew(game_params);
148
149 *ret = unruly_presets[DEFAULT_PRESET]; /* structure copy */
150
151 return ret;
152}
153
154static int game_fetch_preset(int i, char **name, game_params **params)
155{
156 game_params *ret;
157 char buf[80];
158
159 if (i < 0 || i >= lenof(unruly_presets))
160 return FALSE;
161
162 ret = snew(game_params);
163 *ret = unruly_presets[i]; /* structure copy */
164
165 sprintf(buf, "%dx%d %s", ret->w2, ret->h2, unruly_diffnames[ret->diff]);
166
167 *name = dupstr(buf);
168 *params = ret;
169 return TRUE;
170}
171
172static void free_params(game_params *params)
173{
174 sfree(params);
175}
176
177static game_params *dup_params(const game_params *params)
178{
179 game_params *ret = snew(game_params);
180 *ret = *params; /* structure copy */
181 return ret;
182}
183
184static void decode_params(game_params *params, char const *string)
185{
186 char const *p = string;
187
188 params->unique = FALSE;
189
190 params->w2 = atoi(p);
191 while (*p && isdigit((unsigned char)*p)) p++;
192 if (*p == 'x') {
193 p++;
194 params->h2 = atoi(p);
195 while (*p && isdigit((unsigned char)*p)) p++;
196 } else {
197 params->h2 = params->w2;
198 }
199
200 if (*p == 'u') {
201 p++;
202 params->unique = TRUE;
203 }
204
205 if (*p == 'd') {
206 int i;
207 p++;
208 params->diff = DIFFCOUNT + 1; /* ...which is invalid */
209 if (*p) {
210 for (i = 0; i < DIFFCOUNT; i++) {
211 if (*p == unruly_diffchars[i])
212 params->diff = i;
213 }
214 p++;
215 }
216 }
217}
218
219static char *encode_params(const game_params *params, int full)
220{
221 char buf[80];
222
223 sprintf(buf, "%dx%d", params->w2, params->h2);
224 if (params->unique)
225 strcat(buf, "u");
226 if (full)
227 sprintf(buf + strlen(buf), "d%c", unruly_diffchars[params->diff]);
228
229 return dupstr(buf);
230}
231
232static config_item *game_configure(const game_params *params)
233{
234 config_item *ret;
235 char buf[80];
236
237 ret = snewn(5, config_item);
238
239 ret[0].name = "Width";
240 ret[0].type = C_STRING;
241 sprintf(buf, "%d", params->w2);
242 ret[0].sval = dupstr(buf);
243 ret[0].ival = 0;
244
245 ret[1].name = "Height";
246 ret[1].type = C_STRING;
247 sprintf(buf, "%d", params->h2);
248 ret[1].sval = dupstr(buf);
249 ret[1].ival = 0;
250
251 ret[2].name = "Unique rows and columns";
252 ret[2].type = C_BOOLEAN;
253 ret[2].ival = params->unique;
254
255 ret[3].name = "Difficulty";
256 ret[3].type = C_CHOICES;
257 ret[3].sval = DIFFCONFIG;
258 ret[3].ival = params->diff;
259
260 ret[4].name = NULL;
261 ret[4].type = C_END;
262 ret[4].sval = NULL;
263 ret[4].ival = 0;
264
265 return ret;
266}
267
268static game_params *custom_params(const config_item *cfg)
269{
270 game_params *ret = snew(game_params);
271
272 ret->w2 = atoi(cfg[0].sval);
273 ret->h2 = atoi(cfg[1].sval);
274 ret->unique = cfg[2].ival;
275 ret->diff = cfg[3].ival;
276
277 return ret;
278}
279
280static char *validate_params(const game_params *params, int full)
281{
282 if ((params->w2 & 1) || (params->h2 & 1))
283 return "Width and height must both be even";
284 if (params->w2 < 6 || params->h2 < 6)
285 return "Width and height must be at least 6";
286 if (params->unique) {
287 static const long A177790[] = {
288 /*
289 * The nth element of this array gives the number of
290 * distinct possible Unruly rows of length 2n (that is,
291 * containing exactly n 1s and n 0s and not containing
292 * three consecutive elements the same) for as long as
293 * those numbers fit in a 32-bit signed int.
294 *
295 * So in unique-rows mode, if the puzzle width is 2n, then
296 * the height must be at most (this array)[n], and vice
297 * versa.
298 *
299 * This is sequence A177790 in the Online Encyclopedia of
300 * Integer Sequences: http://oeis.org/A177790
301 */
302 1L, 2L, 6L, 14L, 34L, 84L, 208L, 518L, 1296L, 3254L,
303 8196L, 20700L, 52404L, 132942L, 337878L, 860142L,
304 2192902L, 5598144L, 14308378L, 36610970L, 93770358L,
305 240390602L, 616787116L, 1583765724L
306 };
307 if (params->w2 < 2*lenof(A177790) &&
308 params->h2 > A177790[params->w2/2]) {
309 return "Puzzle is too tall for unique-rows mode";
310 }
311 if (params->h2 < 2*lenof(A177790) &&
312 params->w2 > A177790[params->h2/2]) {
313 return "Puzzle is too long for unique-rows mode";
314 }
315 }
316 if (params->diff >= DIFFCOUNT)
317 return "Unknown difficulty rating";
318
319 return NULL;
320}
321
322static char *validate_desc(const game_params *params, const char *desc)
323{
324 int w2 = params->w2, h2 = params->h2;
325 int s = w2 * h2;
326
327 const char *p = desc;
328 int pos = 0;
329
330 while (*p) {
331 if (*p >= 'a' && *p < 'z')
332 pos += 1 + (*p - 'a');
333 else if (*p >= 'A' && *p < 'Z')
334 pos += 1 + (*p - 'A');
335 else if (*p == 'Z' || *p == 'z')
336 pos += 25;
337 else
338 return "Description contains invalid characters";
339
340 ++p;
341 }
342
343 if (pos < s+1)
344 return "Description too short";
345 if (pos > s+1)
346 return "Description too long";
347
348 return NULL;
349}
350
351static game_state *blank_state(int w2, int h2, int unique)
352{
353 game_state *state = snew(game_state);
354 int s = w2 * h2;
355
356 state->w2 = w2;
357 state->h2 = h2;
358 state->unique = unique;
359 state->grid = snewn(s, char);
360 state->immutable = snewn(s, unsigned char);
361
362 memset(state->grid, EMPTY, s);
363 memset(state->immutable, FALSE, s);
364
365 state->completed = state->cheated = FALSE;
366
367 return state;
368}
369
370static game_state *new_game(midend *me, const game_params *params,
371 const char *desc)
372{
373 int w2 = params->w2, h2 = params->h2;
374 int s = w2 * h2;
375
376 game_state *state = blank_state(w2, h2, params->unique);
377
378 const char *p = desc;
379 int pos = 0;
380
381 while (*p) {
382 if (*p >= 'a' && *p < 'z') {
383 pos += (*p - 'a');
384 if (pos < s) {
385 state->grid[pos] = N_ZERO;
386 state->immutable[pos] = TRUE;
387 }
388 pos++;
389 } else if (*p >= 'A' && *p < 'Z') {
390 pos += (*p - 'A');
391 if (pos < s) {
392 state->grid[pos] = N_ONE;
393 state->immutable[pos] = TRUE;
394 }
395 pos++;
396 } else if (*p == 'Z' || *p == 'z') {
397 pos += 25;
398 } else
399 assert(!"Description contains invalid characters");
400
401 ++p;
402 }
403 assert(pos == s+1);
404
405 return state;
406}
407
408static game_state *dup_game(const game_state *state)
409{
410 int w2 = state->w2, h2 = state->h2;
411 int s = w2 * h2;
412
413 game_state *ret = blank_state(w2, h2, state->unique);
414
415 memcpy(ret->grid, state->grid, s);
416 memcpy(ret->immutable, state->immutable, s);
417
418 ret->completed = state->completed;
419 ret->cheated = state->cheated;
420
421 return ret;
422}
423
424static void free_game(game_state *state)
425{
426 sfree(state->grid);
427 sfree(state->immutable);
428
429 sfree(state);
430}
431
432static int game_can_format_as_text_now(const game_params *params)
433{
434 return TRUE;
435}
436
437static char *game_text_format(const game_state *state)
438{
439 int w2 = state->w2, h2 = state->h2;
440 int lr = w2*2 + 1;
441
442 char *ret = snewn(lr * h2 + 1, char);
443 char *p = ret;
444
445 int x, y;
446 for (y = 0; y < h2; y++) {
447 for (x = 0; x < w2; x++) {
448 /* Place number */
449 char c = state->grid[y * w2 + x];
450 *p++ = (c == N_ONE ? '1' : c == N_ZERO ? '0' : '.');
451 *p++ = ' ';
452 }
453 /* End line */
454 *p++ = '\n';
455 }
456 /* End with NUL */
457 *p++ = '\0';
458
459 return ret;
460}
461
462/* ****** *
463 * Solver *
464 * ****** */
465
466struct unruly_scratch {
467 int *ones_rows;
468 int *ones_cols;
469 int *zeros_rows;
470 int *zeros_cols;
471};
472
473static void unruly_solver_update_remaining(const game_state *state,
474 struct unruly_scratch *scratch)
475{
476 int w2 = state->w2, h2 = state->h2;
477 int x, y;
478
479 /* Reset all scratch data */
480 memset(scratch->ones_rows, 0, h2 * sizeof(int));
481 memset(scratch->ones_cols, 0, w2 * sizeof(int));
482 memset(scratch->zeros_rows, 0, h2 * sizeof(int));
483 memset(scratch->zeros_cols, 0, w2 * sizeof(int));
484
485 for (x = 0; x < w2; x++)
486 for (y = 0; y < h2; y++) {
487 if (state->grid[y * w2 + x] == N_ONE) {
488 scratch->ones_rows[y]++;
489 scratch->ones_cols[x]++;
490 } else if (state->grid[y * w2 + x] == N_ZERO) {
491 scratch->zeros_rows[y]++;
492 scratch->zeros_cols[x]++;
493 }
494 }
495}
496
497static struct unruly_scratch *unruly_new_scratch(const game_state *state)
498{
499 int w2 = state->w2, h2 = state->h2;
500
501 struct unruly_scratch *ret = snew(struct unruly_scratch);
502
503 ret->ones_rows = snewn(h2, int);
504 ret->ones_cols = snewn(w2, int);
505 ret->zeros_rows = snewn(h2, int);
506 ret->zeros_cols = snewn(w2, int);
507
508 unruly_solver_update_remaining(state, ret);
509
510 return ret;
511}
512
513static void unruly_free_scratch(struct unruly_scratch *scratch)
514{
515 sfree(scratch->ones_rows);
516 sfree(scratch->ones_cols);
517 sfree(scratch->zeros_rows);
518 sfree(scratch->zeros_cols);
519
520 sfree(scratch);
521}
522
523static int unruly_solver_check_threes(game_state *state, int *rowcount,
524 int *colcount, int horizontal,
525 char check, char block)
526{
527 int w2 = state->w2, h2 = state->h2;
528
529 int dx = horizontal ? 1 : 0, dy = 1 - dx;
530 int sx = dx, sy = dy;
531 int ex = w2 - dx, ey = h2 - dy;
532
533 int x, y;
534 int ret = 0;
535
536 /* Check for any three squares which almost form three in a row */
537 for (y = sy; y < ey; y++) {
538 for (x = sx; x < ex; x++) {
539 int i1 = (y-dy) * w2 + (x-dx);
540 int i2 = y * w2 + x;
541 int i3 = (y+dy) * w2 + (x+dx);
542
543 if (state->grid[i1] == check && state->grid[i2] == check
544 && state->grid[i3] == EMPTY) {
545 ret++;
546#ifdef STANDALONE_SOLVER
547 if (solver_verbose) {
548 printf("Solver: %i,%i and %i,%i confirm %c at %i,%i\n",
549 i1 % w2, i1 / w2, i2 % w2, i2 / w2,
550 (block == N_ONE ? '1' : '0'), i3 % w2,
551 i3 / w2);
552 }
553#endif
554 state->grid[i3] = block;
555 rowcount[i3 / w2]++;
556 colcount[i3 % w2]++;
557 }
558 if (state->grid[i1] == check && state->grid[i2] == EMPTY
559 && state->grid[i3] == check) {
560 ret++;
561#ifdef STANDALONE_SOLVER
562 if (solver_verbose) {
563 printf("Solver: %i,%i and %i,%i confirm %c at %i,%i\n",
564 i1 % w2, i1 / w2, i3 % w2, i3 / w2,
565 (block == N_ONE ? '1' : '0'), i2 % w2,
566 i2 / w2);
567 }
568#endif
569 state->grid[i2] = block;
570 rowcount[i2 / w2]++;
571 colcount[i2 % w2]++;
572 }
573 if (state->grid[i1] == EMPTY && state->grid[i2] == check
574 && state->grid[i3] == check) {
575 ret++;
576#ifdef STANDALONE_SOLVER
577 if (solver_verbose) {
578 printf("Solver: %i,%i and %i,%i confirm %c at %i,%i\n",
579 i2 % w2, i2 / w2, i3 % w2, i3 / w2,
580 (block == N_ONE ? '1' : '0'), i1 % w2,
581 i1 / w2);
582 }
583#endif
584 state->grid[i1] = block;
585 rowcount[i1 / w2]++;
586 colcount[i1 % w2]++;
587 }
588 }
589 }
590
591 return ret;
592}
593
594static int unruly_solver_check_all_threes(game_state *state,
595 struct unruly_scratch *scratch)
596{
597 int ret = 0;
598
599 ret +=
600 unruly_solver_check_threes(state, scratch->zeros_rows,
601 scratch->zeros_cols, TRUE, N_ONE, N_ZERO);
602 ret +=
603 unruly_solver_check_threes(state, scratch->ones_rows,
604 scratch->ones_cols, TRUE, N_ZERO, N_ONE);
605 ret +=
606 unruly_solver_check_threes(state, scratch->zeros_rows,
607 scratch->zeros_cols, FALSE, N_ONE,
608 N_ZERO);
609 ret +=
610 unruly_solver_check_threes(state, scratch->ones_rows,
611 scratch->ones_cols, FALSE, N_ZERO, N_ONE);
612
613 return ret;
614}
615
616static int unruly_solver_check_uniques(game_state *state, int *rowcount,
617 int horizontal, char check, char block,
618 struct unruly_scratch *scratch)
619{
620 int w2 = state->w2, h2 = state->h2;
621
622 int rmult = (horizontal ? w2 : 1);
623 int cmult = (horizontal ? 1 : w2);
624 int nr = (horizontal ? h2 : w2);
625 int nc = (horizontal ? w2 : h2);
626 int max = nc / 2;
627
628 int r, r2, c;
629 int ret = 0;
630
631 /*
632 * Find each row that has max entries of type 'check', and see if
633 * all those entries match those in any row with max-1 entries. If
634 * so, set the last non-matching entry of the latter row to ensure
635 * that it's different.
636 */
637 for (r = 0; r < nr; r++) {
638 if (rowcount[r] != max)
639 continue;
640 for (r2 = 0; r2 < nr; r2++) {
641 int nmatch = 0, nonmatch = -1;
642 if (rowcount[r2] != max-1)
643 continue;
644 for (c = 0; c < nc; c++) {
645 if (state->grid[r*rmult + c*cmult] == check) {
646 if (state->grid[r2*rmult + c*cmult] == check)
647 nmatch++;
648 else
649 nonmatch = c;
650 }
651 }
652 if (nmatch == max-1) {
653 int i1 = r2 * rmult + nonmatch * cmult;
654 assert(nonmatch != -1);
655 if (state->grid[i1] == block)
656 continue;
657 assert(state->grid[i1] == EMPTY);
658#ifdef STANDALONE_SOLVER
659 if (solver_verbose) {
660 printf("Solver: matching %s %i, %i gives %c at %i,%i\n",
661 horizontal ? "rows" : "cols",
662 r, r2, (block == N_ONE ? '1' : '0'), i1 % w2,
663 i1 / w2);
664 }
665#endif
666 state->grid[i1] = block;
667 if (block == N_ONE) {
668 scratch->ones_rows[i1 / w2]++;
669 scratch->ones_cols[i1 % w2]++;
670 } else {
671 scratch->zeros_rows[i1 / w2]++;
672 scratch->zeros_cols[i1 % w2]++;
673 }
674 ret++;
675 }
676 }
677 }
678 return ret;
679}
680
681static int unruly_solver_check_all_uniques(game_state *state,
682 struct unruly_scratch *scratch)
683{
684 int ret = 0;
685
686 ret += unruly_solver_check_uniques(state, scratch->ones_rows,
687 TRUE, N_ONE, N_ZERO, scratch);
688 ret += unruly_solver_check_uniques(state, scratch->zeros_rows,
689 TRUE, N_ZERO, N_ONE, scratch);
690 ret += unruly_solver_check_uniques(state, scratch->ones_cols,
691 FALSE, N_ONE, N_ZERO, scratch);
692 ret += unruly_solver_check_uniques(state, scratch->zeros_cols,
693 FALSE, N_ZERO, N_ONE, scratch);
694
695 return ret;
696}
697
698static int unruly_solver_fill_row(game_state *state, int i, int horizontal,
699 int *rowcount, int *colcount, char fill)
700{
701 int ret = 0;
702 int w2 = state->w2, h2 = state->h2;
703 int j;
704
705#ifdef STANDALONE_SOLVER
706 if (solver_verbose) {
707 printf("Solver: Filling %s %i with %c:",
708 (horizontal ? "Row" : "Col"), i,
709 (fill == N_ZERO ? '0' : '1'));
710 }
711#endif
712 /* Place a number in every empty square in a row/column */
713 for (j = 0; j < (horizontal ? w2 : h2); j++) {
714 int p = (horizontal ? i * w2 + j : j * w2 + i);
715
716 if (state->grid[p] == EMPTY) {
717#ifdef STANDALONE_SOLVER
718 if (solver_verbose) {
719 printf(" (%i,%i)", (horizontal ? j : i),
720 (horizontal ? i : j));
721 }
722#endif
723 ret++;
724 state->grid[p] = fill;
725 rowcount[(horizontal ? i : j)]++;
726 colcount[(horizontal ? j : i)]++;
727 }
728 }
729
730#ifdef STANDALONE_SOLVER
731 if (solver_verbose) {
732 printf("\n");
733 }
734#endif
735
736 return ret;
737}
738
739static int unruly_solver_check_complete_nums(game_state *state,
740 int *complete, int horizontal,
741 int *rowcount, int *colcount,
742 char fill)
743{
744 int w2 = state->w2, h2 = state->h2;
745 int count = (horizontal ? h2 : w2); /* number of rows to check */
746 int target = (horizontal ? w2 : h2) / 2; /* target number of 0s/1s */
747 int *other = (horizontal ? rowcount : colcount);
748
749 int ret = 0;
750
751 int i;
752 /* Check for completed rows/cols for one number, then fill in the rest */
753 for (i = 0; i < count; i++) {
754 if (complete[i] == target && other[i] < target) {
755#ifdef STANDALONE_SOLVER
756 if (solver_verbose) {
757 printf("Solver: Row %i satisfied for %c\n", i,
758 (fill != N_ZERO ? '0' : '1'));
759 }
760#endif
761 ret += unruly_solver_fill_row(state, i, horizontal, rowcount,
762 colcount, fill);
763 }
764 }
765
766 return ret;
767}
768
769static int unruly_solver_check_all_complete_nums(game_state *state,
770 struct unruly_scratch *scratch)
771{
772 int ret = 0;
773
774 ret +=
775 unruly_solver_check_complete_nums(state, scratch->ones_rows, TRUE,
776 scratch->zeros_rows,
777 scratch->zeros_cols, N_ZERO);
778 ret +=
779 unruly_solver_check_complete_nums(state, scratch->ones_cols, FALSE,
780 scratch->zeros_rows,
781 scratch->zeros_cols, N_ZERO);
782 ret +=
783 unruly_solver_check_complete_nums(state, scratch->zeros_rows, TRUE,
784 scratch->ones_rows,
785 scratch->ones_cols, N_ONE);
786 ret +=
787 unruly_solver_check_complete_nums(state, scratch->zeros_cols, FALSE,
788 scratch->ones_rows,
789 scratch->ones_cols, N_ONE);
790
791 return ret;
792}
793
794static int unruly_solver_check_near_complete(game_state *state,
795 int *complete, int horizontal,
796 int *rowcount, int *colcount,
797 char fill)
798{
799 int w2 = state->w2, h2 = state->h2;
800 int w = w2/2, h = h2/2;
801
802 int dx = horizontal ? 1 : 0, dy = 1 - dx;
803
804 int sx = dx, sy = dy;
805 int ex = w2 - dx, ey = h2 - dy;
806
807 int x, y;
808 int ret = 0;
809
810 /*
811 * This function checks for a row with one Y remaining, then looks
812 * for positions that could cause the remaining squares in the row
813 * to make 3 X's in a row. Example:
814 *
815 * Consider the following row:
816 * 1 1 0 . . .
817 * If the last 1 was placed in the last square, the remaining
818 * squares would be 0:
819 * 1 1 0 0 0 1
820 * This violates the 3 in a row rule. We now know that the last 1
821 * shouldn't be in the last cell.
822 * 1 1 0 . . 0
823 */
824
825 /* Check for any two blank and one filled square */
826 for (y = sy; y < ey; y++) {
827 /* One type must have 1 remaining, the other at least 2 */
828 if (horizontal && (complete[y] < w - 1 || rowcount[y] > w - 2))
829 continue;
830
831 for (x = sx; x < ex; x++) {
832 int i, i1, i2, i3;
833 if (!horizontal
834 && (complete[x] < h - 1 || colcount[x] > h - 2))
835 continue;
836
837 i = (horizontal ? y : x);
838 i1 = (y-dy) * w2 + (x-dx);
839 i2 = y * w2 + x;
840 i3 = (y+dy) * w2 + (x+dx);
841
842 if (state->grid[i1] == fill && state->grid[i2] == EMPTY
843 && state->grid[i3] == EMPTY) {
844 /*
845 * Temporarily fill the empty spaces with something else.
846 * This avoids raising the counts for the row and column
847 */
848 state->grid[i2] = BOGUS;
849 state->grid[i3] = BOGUS;
850
851#ifdef STANDALONE_SOLVER
852 if (solver_verbose) {
853 printf("Solver: Row %i nearly satisfied for %c\n", i,
854 (fill != N_ZERO ? '0' : '1'));
855 }
856#endif
857 ret +=
858 unruly_solver_fill_row(state, i, horizontal, rowcount,
859 colcount, fill);
860
861 state->grid[i2] = EMPTY;
862 state->grid[i3] = EMPTY;
863 }
864
865 else if (state->grid[i1] == EMPTY && state->grid[i2] == fill
866 && state->grid[i3] == EMPTY) {
867 state->grid[i1] = BOGUS;
868 state->grid[i3] = BOGUS;
869
870#ifdef STANDALONE_SOLVER
871 if (solver_verbose) {
872 printf("Solver: Row %i nearly satisfied for %c\n", i,
873 (fill != N_ZERO ? '0' : '1'));
874 }
875#endif
876 ret +=
877 unruly_solver_fill_row(state, i, horizontal, rowcount,
878 colcount, fill);
879
880 state->grid[i1] = EMPTY;
881 state->grid[i3] = EMPTY;
882 }
883
884 else if (state->grid[i1] == EMPTY && state->grid[i2] == EMPTY
885 && state->grid[i3] == fill) {
886 state->grid[i1] = BOGUS;
887 state->grid[i2] = BOGUS;
888
889#ifdef STANDALONE_SOLVER
890 if (solver_verbose) {
891 printf("Solver: Row %i nearly satisfied for %c\n", i,
892 (fill != N_ZERO ? '0' : '1'));
893 }
894#endif
895 ret +=
896 unruly_solver_fill_row(state, i, horizontal, rowcount,
897 colcount, fill);
898
899 state->grid[i1] = EMPTY;
900 state->grid[i2] = EMPTY;
901 }
902
903 else if (state->grid[i1] == EMPTY && state->grid[i2] == EMPTY
904 && state->grid[i3] == EMPTY) {
905 state->grid[i1] = BOGUS;
906 state->grid[i2] = BOGUS;
907 state->grid[i3] = BOGUS;
908
909#ifdef STANDALONE_SOLVER
910 if (solver_verbose) {
911 printf("Solver: Row %i nearly satisfied for %c\n", i,
912 (fill != N_ZERO ? '0' : '1'));
913 }
914#endif
915 ret +=
916 unruly_solver_fill_row(state, i, horizontal, rowcount,
917 colcount, fill);
918
919 state->grid[i1] = EMPTY;
920 state->grid[i2] = EMPTY;
921 state->grid[i3] = EMPTY;
922 }
923 }
924 }
925
926 return ret;
927}
928
929static int unruly_solver_check_all_near_complete(game_state *state,
930 struct unruly_scratch *scratch)
931{
932 int ret = 0;
933
934 ret +=
935 unruly_solver_check_near_complete(state, scratch->ones_rows, TRUE,
936 scratch->zeros_rows,
937 scratch->zeros_cols, N_ZERO);
938 ret +=
939 unruly_solver_check_near_complete(state, scratch->ones_cols, FALSE,
940 scratch->zeros_rows,
941 scratch->zeros_cols, N_ZERO);
942 ret +=
943 unruly_solver_check_near_complete(state, scratch->zeros_rows, TRUE,
944 scratch->ones_rows,
945 scratch->ones_cols, N_ONE);
946 ret +=
947 unruly_solver_check_near_complete(state, scratch->zeros_cols, FALSE,
948 scratch->ones_rows,
949 scratch->ones_cols, N_ONE);
950
951 return ret;
952}
953
954static int unruly_validate_rows(const game_state *state, int horizontal,
955 char check, int *errors)
956{
957 int w2 = state->w2, h2 = state->h2;
958
959 int dx = horizontal ? 1 : 0, dy = 1 - dx;
960
961 int sx = dx, sy = dy;
962 int ex = w2 - dx, ey = h2 - dy;
963
964 int x, y;
965 int ret = 0;
966
967 int err1 = (horizontal ? FE_HOR_ROW_LEFT : FE_VER_ROW_TOP);
968 int err2 = (horizontal ? FE_HOR_ROW_MID : FE_VER_ROW_MID);
969 int err3 = (horizontal ? FE_HOR_ROW_RIGHT : FE_VER_ROW_BOTTOM);
970
971 /* Check for any three in a row, and mark errors accordingly (if
972 * required) */
973 for (y = sy; y < ey; y++) {
974 for (x = sx; x < ex; x++) {
975 int i1 = (y-dy) * w2 + (x-dx);
976 int i2 = y * w2 + x;
977 int i3 = (y+dy) * w2 + (x+dx);
978
979 if (state->grid[i1] == check && state->grid[i2] == check
980 && state->grid[i3] == check) {
981 ret++;
982 if (errors) {
983 errors[i1] |= err1;
984 errors[i2] |= err2;
985 errors[i3] |= err3;
986 }
987 }
988 }
989 }
990
991 return ret;
992}
993
994static int unruly_validate_unique(const game_state *state, int horizontal,
995 int *errors)
996{
997 int w2 = state->w2, h2 = state->h2;
998
999 int rmult = (horizontal ? w2 : 1);
1000 int cmult = (horizontal ? 1 : w2);
1001 int nr = (horizontal ? h2 : w2);
1002 int nc = (horizontal ? w2 : h2);
1003 int err = (horizontal ? FE_ROW_MATCH : FE_COL_MATCH);
1004
1005 int r, r2, c;
1006 int ret = 0;
1007
1008 /* Check for any two full rows matching exactly, and mark errors
1009 * accordingly (if required) */
1010 for (r = 0; r < nr; r++) {
1011 int nfull = 0;
1012 for (c = 0; c < nc; c++)
1013 if (state->grid[r*rmult + c*cmult] != EMPTY)
1014 nfull++;
1015 if (nfull != nc)
1016 continue;
1017 for (r2 = r+1; r2 < nr; r2++) {
1018 int match = TRUE;
1019 for (c = 0; c < nc; c++)
1020 if (state->grid[r*rmult + c*cmult] !=
1021 state->grid[r2*rmult + c*cmult])
1022 match = FALSE;
1023 if (match) {
1024 if (errors) {
1025 for (c = 0; c < nc; c++) {
1026 errors[r*rmult + c*cmult] |= err;
1027 errors[r2*rmult + c*cmult] |= err;
1028 }
1029 }
1030 ret++;
1031 }
1032 }
1033 }
1034
1035 return ret;
1036}
1037
1038static int unruly_validate_all_rows(const game_state *state, int *errors)
1039{
1040 int errcount = 0;
1041
1042 errcount += unruly_validate_rows(state, TRUE, N_ONE, errors);
1043 errcount += unruly_validate_rows(state, FALSE, N_ONE, errors);
1044 errcount += unruly_validate_rows(state, TRUE, N_ZERO, errors);
1045 errcount += unruly_validate_rows(state, FALSE, N_ZERO, errors);
1046
1047 if (state->unique) {
1048 errcount += unruly_validate_unique(state, TRUE, errors);
1049 errcount += unruly_validate_unique(state, FALSE, errors);
1050 }
1051
1052 if (errcount)
1053 return -1;
1054 return 0;
1055}
1056
1057static int unruly_validate_counts(const game_state *state,
1058 struct unruly_scratch *scratch, int *errors)
1059{
1060 int w2 = state->w2, h2 = state->h2;
1061 int w = w2/2, h = h2/2;
1062 char below = FALSE;
1063 char above = FALSE;
1064 int i;
1065
1066 /* See if all rows/columns are satisfied. If one is exceeded,
1067 * mark it as an error (if required)
1068 */
1069
1070 char hasscratch = TRUE;
1071 if (!scratch) {
1072 scratch = unruly_new_scratch(state);
1073 hasscratch = FALSE;
1074 }
1075
1076 for (i = 0; i < w2; i++) {
1077 if (scratch->ones_cols[i] < h)
1078 below = TRUE;
1079 if (scratch->zeros_cols[i] < h)
1080 below = TRUE;
1081
1082 if (scratch->ones_cols[i] > h) {
1083 above = TRUE;
1084 if (errors)
1085 errors[2*h2 + i] = TRUE;
1086 } else if (errors)
1087 errors[2*h2 + i] = FALSE;
1088
1089 if (scratch->zeros_cols[i] > h) {
1090 above = TRUE;
1091 if (errors)
1092 errors[2*h2 + w2 + i] = TRUE;
1093 } else if (errors)
1094 errors[2*h2 + w2 + i] = FALSE;
1095 }
1096 for (i = 0; i < h2; i++) {
1097 if (scratch->ones_rows[i] < w)
1098 below = TRUE;
1099 if (scratch->zeros_rows[i] < w)
1100 below = TRUE;
1101
1102 if (scratch->ones_rows[i] > w) {
1103 above = TRUE;
1104 if (errors)
1105 errors[i] = TRUE;
1106 } else if (errors)
1107 errors[i] = FALSE;
1108
1109 if (scratch->zeros_rows[i] > w) {
1110 above = TRUE;
1111 if (errors)
1112 errors[h2 + i] = TRUE;
1113 } else if (errors)
1114 errors[h2 + i] = FALSE;
1115 }
1116
1117 if (!hasscratch)
1118 unruly_free_scratch(scratch);
1119
1120 return (above ? -1 : below ? 1 : 0);
1121}
1122
1123static int unruly_solve_game(game_state *state,
1124 struct unruly_scratch *scratch, int diff)
1125{
1126 int done, maxdiff = -1;
1127
1128 while (TRUE) {
1129 done = 0;
1130
1131 /* Check for impending 3's */
1132 done += unruly_solver_check_all_threes(state, scratch);
1133
1134 /* Keep using the simpler techniques while they produce results */
1135 if (done) {
1136 if (maxdiff < DIFF_EASY)
1137 maxdiff = DIFF_EASY;
1138 continue;
1139 }
1140
1141 /* Check for completed rows */
1142 done += unruly_solver_check_all_complete_nums(state, scratch);
1143
1144 if (done) {
1145 if (maxdiff < DIFF_EASY)
1146 maxdiff = DIFF_EASY;
1147 continue;
1148 }
1149
1150 /* Check for impending failures of row/column uniqueness, if
1151 * it's enabled in this game mode */
1152 if (state->unique) {
1153 done += unruly_solver_check_all_uniques(state, scratch);
1154
1155 if (done) {
1156 if (maxdiff < DIFF_EASY)
1157 maxdiff = DIFF_EASY;
1158 continue;
1159 }
1160 }
1161
1162 /* Normal techniques */
1163 if (diff < DIFF_NORMAL)
1164 break;
1165
1166 /* Check for nearly completed rows */
1167 done += unruly_solver_check_all_near_complete(state, scratch);
1168
1169 if (done) {
1170 if (maxdiff < DIFF_NORMAL)
1171 maxdiff = DIFF_NORMAL;
1172 continue;
1173 }
1174
1175 break;
1176 }
1177 return maxdiff;
1178}
1179
1180static char *solve_game(const game_state *state, const game_state *currstate,
1181 const char *aux, char **error)
1182{
1183 game_state *solved = dup_game(state);
1184 struct unruly_scratch *scratch = unruly_new_scratch(solved);
1185 char *ret = NULL;
1186 int result;
1187
1188 unruly_solve_game(solved, scratch, DIFFCOUNT);
1189
1190 result = unruly_validate_counts(solved, scratch, NULL);
1191 if (unruly_validate_all_rows(solved, NULL) == -1)
1192 result = -1;
1193
1194 if (result == 0) {
1195 int w2 = solved->w2, h2 = solved->h2;
1196 int s = w2 * h2;
1197 char *p;
1198 int i;
1199
1200 ret = snewn(s + 2, char);
1201 p = ret;
1202 *p++ = 'S';
1203
1204 for (i = 0; i < s; i++)
1205 *p++ = (solved->grid[i] == N_ONE ? '1' : '0');
1206
1207 *p++ = '\0';
1208 } else if (result == 1)
1209 *error = "No solution found.";
1210 else if (result == -1)
1211 *error = "Puzzle is invalid.";
1212
1213 free_game(solved);
1214 unruly_free_scratch(scratch);
1215 return ret;
1216}
1217
1218/* ********* *
1219 * Generator *
1220 * ********* */
1221
1222static int unruly_fill_game(game_state *state, struct unruly_scratch *scratch,
1223 random_state *rs)
1224{
1225
1226 int w2 = state->w2, h2 = state->h2;
1227 int s = w2 * h2;
1228 int i, j;
1229 int *spaces;
1230
1231#ifdef STANDALONE_SOLVER
1232 if (solver_verbose) {
1233 printf("Generator: Attempt to fill grid\n");
1234 }
1235#endif
1236
1237 /* Generate random array of spaces */
1238 spaces = snewn(s, int);
1239 for (i = 0; i < s; i++)
1240 spaces[i] = i;
1241 shuffle(spaces, s, sizeof(*spaces), rs);
1242
1243 /*
1244 * Construct a valid filled grid by repeatedly picking an unfilled
1245 * space and fill it, then calling the solver to fill in any
1246 * spaces forced by the change.
1247 */
1248 for (j = 0; j < s; j++) {
1249 i = spaces[j];
1250
1251 if (state->grid[i] != EMPTY)
1252 continue;
1253
1254 if (random_upto(rs, 2)) {
1255 state->grid[i] = N_ONE;
1256 scratch->ones_rows[i / w2]++;
1257 scratch->ones_cols[i % w2]++;
1258 } else {
1259 state->grid[i] = N_ZERO;
1260 scratch->zeros_rows[i / w2]++;
1261 scratch->zeros_cols[i % w2]++;
1262 }
1263
1264 unruly_solve_game(state, scratch, DIFFCOUNT);
1265 }
1266 sfree(spaces);
1267
1268 if (unruly_validate_all_rows(state, NULL) != 0
1269 || unruly_validate_counts(state, scratch, NULL) != 0)
1270 return FALSE;
1271
1272 return TRUE;
1273}
1274
1275static char *new_game_desc(const game_params *params, random_state *rs,
1276 char **aux, int interactive)
1277{
1278#ifdef STANDALONE_SOLVER
1279 char *debug;
1280 int temp_verbose = FALSE;
1281#endif
1282
1283 int w2 = params->w2, h2 = params->h2;
1284 int s = w2 * h2;
1285 int *spaces;
1286 int i, j, run;
1287 char *ret, *p;
1288
1289 game_state *state;
1290 struct unruly_scratch *scratch;
1291
1292 int attempts = 0;
1293
1294 while (1) {
1295
1296 while (TRUE) {
1297 attempts++;
1298 state = blank_state(w2, h2, params->unique);
1299 scratch = unruly_new_scratch(state);
1300 if (unruly_fill_game(state, scratch, rs))
1301 break;
1302 free_game(state);
1303 unruly_free_scratch(scratch);
1304 }
1305
1306#ifdef STANDALONE_SOLVER
1307 if (solver_verbose) {
1308 printf("Puzzle generated in %i attempts\n", attempts);
1309 debug = game_text_format(state);
1310 fputs(debug, stdout);
1311 sfree(debug);
1312
1313 temp_verbose = solver_verbose;
1314 solver_verbose = FALSE;
1315 }
1316#endif
1317
1318 unruly_free_scratch(scratch);
1319
1320 /* Generate random array of spaces */
1321 spaces = snewn(s, int);
1322 for (i = 0; i < s; i++)
1323 spaces[i] = i;
1324 shuffle(spaces, s, sizeof(*spaces), rs);
1325
1326 /*
1327 * Winnow the clues by starting from our filled grid, repeatedly
1328 * picking a filled space and emptying it, as long as the solver
1329 * reports that the puzzle can still be solved after doing so.
1330 */
1331 for (j = 0; j < s; j++) {
1332 char c;
1333 game_state *solver;
1334
1335 i = spaces[j];
1336
1337 c = state->grid[i];
1338 state->grid[i] = EMPTY;
1339
1340 solver = dup_game(state);
1341 scratch = unruly_new_scratch(state);
1342
1343 unruly_solve_game(solver, scratch, params->diff);
1344
1345 if (unruly_validate_counts(solver, scratch, NULL) != 0)
1346 state->grid[i] = c;
1347
1348 free_game(solver);
1349 unruly_free_scratch(scratch);
1350 }
1351 sfree(spaces);
1352
1353#ifdef STANDALONE_SOLVER
1354 if (temp_verbose) {
1355 solver_verbose = TRUE;
1356
1357 printf("Final puzzle:\n");
1358 debug = game_text_format(state);
1359 fputs(debug, stdout);
1360 sfree(debug);
1361 }
1362#endif
1363
1364 /*
1365 * See if the game has accidentally come out too easy.
1366 */
1367 if (params->diff > 0) {
1368 int ok;
1369 game_state *solver;
1370
1371 solver = dup_game(state);
1372 scratch = unruly_new_scratch(state);
1373
1374 unruly_solve_game(solver, scratch, params->diff - 1);
1375
1376 ok = unruly_validate_counts(solver, scratch, NULL);
1377
1378 free_game(solver);
1379 unruly_free_scratch(scratch);
1380
1381 if (ok)
1382 break;
1383 } else {
1384 /*
1385 * Puzzles of the easiest difficulty can't be too easy.
1386 */
1387 break;
1388 }
1389 }
1390
1391 /* Encode description */
1392 ret = snewn(s + 1, char);
1393 p = ret;
1394 run = 0;
1395 for (i = 0; i < s+1; i++) {
1396 if (i == s || state->grid[i] == N_ZERO) {
1397 while (run > 24) {
1398 *p++ = 'z';
1399 run -= 25;
1400 }
1401 *p++ = 'a' + run;
1402 run = 0;
1403 } else if (state->grid[i] == N_ONE) {
1404 while (run > 24) {
1405 *p++ = 'Z';
1406 run -= 25;
1407 }
1408 *p++ = 'A' + run;
1409 run = 0;
1410 } else {
1411 run++;
1412 }
1413 }
1414 *p = '\0';
1415
1416 free_game(state);
1417
1418 return ret;
1419}
1420
1421/* ************** *
1422 * User Interface *
1423 * ************** */
1424
1425struct game_ui {
1426 int cx, cy;
1427 char cursor;
1428};
1429
1430static game_ui *new_ui(const game_state *state)
1431{
1432 game_ui *ret = snew(game_ui);
1433
1434 ret->cx = ret->cy = 0;
1435 ret->cursor = FALSE;
1436
1437 return ret;
1438}
1439
1440static void free_ui(game_ui *ui)
1441{
1442 sfree(ui);
1443}
1444
1445static char *encode_ui(const game_ui *ui)
1446{
1447 return NULL;
1448}
1449
1450static void decode_ui(game_ui *ui, const char *encoding)
1451{
1452}
1453
1454static void game_changed_state(game_ui *ui, const game_state *oldstate,
1455 const game_state *newstate)
1456{
1457}
1458
1459struct game_drawstate {
1460 int tilesize;
1461 int w2, h2;
1462 int started;
1463
1464 int *gridfs;
1465 int *rowfs;
1466
1467 int *grid;
1468};
1469
1470static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1471{
1472 struct game_drawstate *ds = snew(struct game_drawstate);
1473
1474 int w2 = state->w2, h2 = state->h2;
1475 int s = w2 * h2;
1476 int i;
1477
1478 ds->tilesize = 0;
1479 ds->w2 = w2;
1480 ds->h2 = h2;
1481 ds->started = FALSE;
1482
1483 ds->gridfs = snewn(s, int);
1484 ds->rowfs = snewn(2 * (w2 + h2), int);
1485
1486 ds->grid = snewn(s, int);
1487 for (i = 0; i < s; i++)
1488 ds->grid[i] = -1;
1489
1490 return ds;
1491}
1492
1493static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1494{
1495 sfree(ds->gridfs);
1496 sfree(ds->rowfs);
1497 sfree(ds->grid);
1498 sfree(ds);
1499}
1500
1501#define COORD(x) ( (x) * ds->tilesize + ds->tilesize/2 )
1502#define FROMCOORD(x) ( ((x)-(ds->tilesize/2)) / ds->tilesize )
1503
1504static char *interpret_move(const game_state *state, game_ui *ui,
1505 const game_drawstate *ds,
1506 int ox, int oy, int button)
1507{
1508 int hx = ui->cx;
1509 int hy = ui->cy;
1510
1511 int gx = FROMCOORD(ox);
1512 int gy = FROMCOORD(oy);
1513
1514 int w2 = state->w2, h2 = state->h2;
1515
1516 button &= ~MOD_MASK;
1517
1518 /* Mouse click */
1519 if (button == LEFT_BUTTON || button == RIGHT_BUTTON ||
1520 button == MIDDLE_BUTTON) {
1521 if (ox >= (ds->tilesize / 2) && gx < w2
1522 && oy >= (ds->tilesize / 2) && gy < h2) {
1523 hx = gx;
1524 hy = gy;
1525 ui->cursor = FALSE;
1526 } else
1527 return NULL;
1528 }
1529
1530 /* Keyboard move */
1531 if (IS_CURSOR_MOVE(button)) {
1532 move_cursor(button, &ui->cx, &ui->cy, w2, h2, 0);
1533 ui->cursor = TRUE;
1534 return "";
1535 }
1536
1537 /* Place one */
1538 if ((ui->cursor && (button == CURSOR_SELECT || button == CURSOR_SELECT2
1539 || button == '\b' || button == '0' || button == '1'
1540 || button == '2')) ||
1541 button == LEFT_BUTTON || button == RIGHT_BUTTON ||
1542 button == MIDDLE_BUTTON) {
1543 char buf[80];
1544 char c, i;
1545
1546 if (state->immutable[hy * w2 + hx])
1547 return NULL;
1548
1549 c = '-';
1550 i = state->grid[hy * w2 + hx];
1551
1552 if (button == '0' || button == '2')
1553 c = '0';
1554 else if (button == '1')
1555 c = '1';
1556 else if (button == MIDDLE_BUTTON)
1557 c = '-';
1558
1559 /* Cycle through options */
1560 else if (button == CURSOR_SELECT2 || button == RIGHT_BUTTON)
1561 c = (i == EMPTY ? '0' : i == N_ZERO ? '1' : '-');
1562 else if (button == CURSOR_SELECT || button == LEFT_BUTTON)
1563 c = (i == EMPTY ? '1' : i == N_ONE ? '0' : '-');
1564
1565 if (state->grid[hy * w2 + hx] ==
1566 (c == '0' ? N_ZERO : c == '1' ? N_ONE : EMPTY))
1567 return NULL; /* don't put no-ops on the undo chain */
1568
1569 sprintf(buf, "P%c,%d,%d", c, hx, hy);
1570
1571 return dupstr(buf);
1572 }
1573 return NULL;
1574}
1575
1576static game_state *execute_move(const game_state *state, const char *move)
1577{
1578 int w2 = state->w2, h2 = state->h2;
1579 int s = w2 * h2;
1580 int x, y, i;
1581 char c;
1582
1583 game_state *ret;
1584
1585 if (move[0] == 'S') {
1586 const char *p;
1587
1588 ret = dup_game(state);
1589 p = move + 1;
1590
1591 for (i = 0; i < s; i++) {
1592
1593 if (!*p || !(*p == '1' || *p == '0')) {
1594 free_game(ret);
1595 return NULL;
1596 }
1597
1598 ret->grid[i] = (*p == '1' ? N_ONE : N_ZERO);
1599 p++;
1600 }
1601
1602 ret->completed = ret->cheated = TRUE;
1603 return ret;
1604 } else if (move[0] == 'P'
1605 && sscanf(move + 1, "%c,%d,%d", &c, &x, &y) == 3 && x >= 0
1606 && x < w2 && y >= 0 && y < h2 && (c == '-' || c == '0'
1607 || c == '1')) {
1608 ret = dup_game(state);
1609 i = y * w2 + x;
1610
1611 if (state->immutable[i]) {
1612 free_game(ret);
1613 return NULL;
1614 }
1615
1616 ret->grid[i] = (c == '1' ? N_ONE : c == '0' ? N_ZERO : EMPTY);
1617
1618 if (!ret->completed && unruly_validate_counts(ret, NULL, NULL) == 0
1619 && (unruly_validate_all_rows(ret, NULL) == 0))
1620 ret->completed = TRUE;
1621
1622 return ret;
1623 }
1624
1625 return NULL;
1626}
1627
1628/* ----------------------------------------------------------------------
1629 * Drawing routines.
1630 */
1631
1632static void game_compute_size(const game_params *params, int tilesize,
1633 int *x, int *y)
1634{
1635 *x = tilesize * (params->w2 + 1);
1636 *y = tilesize * (params->h2 + 1);
1637}
1638
1639static void game_set_size(drawing *dr, game_drawstate *ds,
1640 const game_params *params, int tilesize)
1641{
1642 ds->tilesize = tilesize;
1643}
1644
1645static float *game_colours(frontend *fe, int *ncolours)
1646{
1647 float *ret = snewn(3 * NCOLOURS, float);
1648 int i;
1649
1650 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1651
1652 for (i = 0; i < 3; i++) {
1653 ret[COL_1 * 3 + i] = 0.2F;
1654 ret[COL_1_HIGHLIGHT * 3 + i] = 0.4F;
1655 ret[COL_1_LOWLIGHT * 3 + i] = 0.0F;
1656 ret[COL_0 * 3 + i] = 0.95F;
1657 ret[COL_0_HIGHLIGHT * 3 + i] = 1.0F;
1658 ret[COL_0_LOWLIGHT * 3 + i] = 0.9F;
1659 ret[COL_EMPTY * 3 + i] = 0.5F;
1660 ret[COL_GRID * 3 + i] = 0.3F;
1661 }
1662 game_mkhighlight_specific(fe, ret, COL_0, COL_0_HIGHLIGHT, COL_0_LOWLIGHT);
1663 game_mkhighlight_specific(fe, ret, COL_1, COL_1_HIGHLIGHT, COL_1_LOWLIGHT);
1664
1665 ret[COL_ERROR * 3 + 0] = 1.0F;
1666 ret[COL_ERROR * 3 + 1] = 0.0F;
1667 ret[COL_ERROR * 3 + 2] = 0.0F;
1668
1669 ret[COL_CURSOR * 3 + 0] = 0.0F;
1670 ret[COL_CURSOR * 3 + 1] = 0.7F;
1671 ret[COL_CURSOR * 3 + 2] = 0.0F;
1672
1673 *ncolours = NCOLOURS;
1674 return ret;
1675}
1676
1677static void unruly_draw_err_rectangle(drawing *dr, int x, int y, int w, int h,
1678 int tilesize)
1679{
1680 double thick = tilesize / 10;
1681 double margin = tilesize / 20;
1682
1683 draw_rect(dr, x+margin, y+margin, w-2*margin, thick, COL_ERROR);
1684 draw_rect(dr, x+margin, y+margin, thick, h-2*margin, COL_ERROR);
1685 draw_rect(dr, x+margin, y+h-margin-thick, w-2*margin, thick, COL_ERROR);
1686 draw_rect(dr, x+w-margin-thick, y+margin, thick, h-2*margin, COL_ERROR);
1687}
1688
1689static void unruly_draw_tile(drawing *dr, int x, int y, int tilesize, int tile)
1690{
1691 clip(dr, x, y, tilesize, tilesize);
1692
1693 /* Draw the grid edge first, so the tile can overwrite it */
1694 draw_rect(dr, x, y, tilesize, tilesize, COL_GRID);
1695
1696 /* Background of the tile */
1697 {
1698 int val = (tile & FF_ZERO ? 0 : tile & FF_ONE ? 2 : 1);
1699 val = (val == 0 ? COL_0 : val == 2 ? COL_1 : COL_EMPTY);
1700
1701 if ((tile & (FF_FLASH1 | FF_FLASH2)) &&
1702 (val == COL_0 || val == COL_1)) {
1703 val += (tile & FF_FLASH1 ? 1 : 2);
1704 }
1705
1706 draw_rect(dr, x, y, tilesize-1, tilesize-1, val);
1707
1708 if ((val == COL_0 || val == COL_1) && (tile & FF_IMMUTABLE)) {
1709 draw_rect(dr, x + tilesize/6, y + tilesize/6,
1710 tilesize - 2*(tilesize/6) - 2, 1, val + 2);
1711 draw_rect(dr, x + tilesize/6, y + tilesize/6,
1712 1, tilesize - 2*(tilesize/6) - 2, val + 2);
1713 draw_rect(dr, x + tilesize/6 + 1, y + tilesize - tilesize/6 - 2,
1714 tilesize - 2*(tilesize/6) - 2, 1, val + 1);
1715 draw_rect(dr, x + tilesize - tilesize/6 - 2, y + tilesize/6 + 1,
1716 1, tilesize - 2*(tilesize/6) - 2, val + 1);
1717 }
1718 }
1719
1720 /* 3-in-a-row errors */
1721 if (tile & (FE_HOR_ROW_LEFT | FE_HOR_ROW_RIGHT)) {
1722 int left = x, right = x + tilesize - 1;
1723 if ((tile & FE_HOR_ROW_LEFT))
1724 right += tilesize/2;
1725 if ((tile & FE_HOR_ROW_RIGHT))
1726 left -= tilesize/2;
1727 unruly_draw_err_rectangle(dr, left, y, right-left, tilesize-1, tilesize);
1728 }
1729 if (tile & (FE_VER_ROW_TOP | FE_VER_ROW_BOTTOM)) {
1730 int top = y, bottom = y + tilesize - 1;
1731 if ((tile & FE_VER_ROW_TOP))
1732 bottom += tilesize/2;
1733 if ((tile & FE_VER_ROW_BOTTOM))
1734 top -= tilesize/2;
1735 unruly_draw_err_rectangle(dr, x, top, tilesize-1, bottom-top, tilesize);
1736 }
1737
1738 /* Count errors */
1739 if (tile & FE_COUNT) {
1740 draw_text(dr, x + tilesize/2, y + tilesize/2, FONT_VARIABLE,
1741 tilesize/2, ALIGN_HCENTRE | ALIGN_VCENTRE, COL_ERROR, "!");
1742 }
1743
1744 /* Row-match errors */
1745 if (tile & FE_ROW_MATCH) {
1746 draw_rect(dr, x, y+tilesize/2-tilesize/12,
1747 tilesize, 2*(tilesize/12), COL_ERROR);
1748 }
1749 if (tile & FE_COL_MATCH) {
1750 draw_rect(dr, x+tilesize/2-tilesize/12, y,
1751 2*(tilesize/12), tilesize, COL_ERROR);
1752 }
1753
1754 /* Cursor rectangle */
1755 if (tile & FF_CURSOR) {
1756 draw_rect(dr, x, y, tilesize/12, tilesize-1, COL_CURSOR);
1757 draw_rect(dr, x, y, tilesize-1, tilesize/12, COL_CURSOR);
1758 draw_rect(dr, x+tilesize-1-tilesize/12, y, tilesize/12, tilesize-1,
1759 COL_CURSOR);
1760 draw_rect(dr, x, y+tilesize-1-tilesize/12, tilesize-1, tilesize/12,
1761 COL_CURSOR);
1762 }
1763
1764 unclip(dr);
1765 draw_update(dr, x, y, tilesize, tilesize);
1766}
1767
1768#define TILE_SIZE (ds->tilesize)
1769#define DEFAULT_TILE_SIZE 32
1770#define FLASH_FRAME 0.12F
1771#define FLASH_TIME (FLASH_FRAME * 3)
1772
1773static void game_redraw(drawing *dr, game_drawstate *ds,
1774 const game_state *oldstate, const game_state *state,
1775 int dir, const game_ui *ui,
1776 float animtime, float flashtime)
1777{
1778 int w2 = state->w2, h2 = state->h2;
1779 int s = w2 * h2;
1780 int flash;
1781 int x, y, i;
1782
1783 if (!ds->started) {
1784 /* Main window background */
1785 draw_rect(dr, 0, 0, TILE_SIZE * (w2+1), TILE_SIZE * (h2+1),
1786 COL_BACKGROUND);
1787 /* Outer edge of grid */
1788 draw_rect(dr, COORD(0)-TILE_SIZE/10, COORD(0)-TILE_SIZE/10,
1789 TILE_SIZE*w2 + 2*(TILE_SIZE/10) - 1,
1790 TILE_SIZE*h2 + 2*(TILE_SIZE/10) - 1, COL_GRID);
1791
1792 draw_update(dr, 0, 0, TILE_SIZE * (w2+1), TILE_SIZE * (h2+1));
1793 ds->started = TRUE;
1794 }
1795
1796 flash = 0;
1797 if (flashtime > 0)
1798 flash = (int)(flashtime / FLASH_FRAME) == 1 ? FF_FLASH2 : FF_FLASH1;
1799
1800 for (i = 0; i < s; i++)
1801 ds->gridfs[i] = 0;
1802 unruly_validate_all_rows(state, ds->gridfs);
1803 for (i = 0; i < 2 * (h2 + w2); i++)
1804 ds->rowfs[i] = 0;
1805 unruly_validate_counts(state, NULL, ds->rowfs);
1806
1807 for (y = 0; y < h2; y++) {
1808 for (x = 0; x < w2; x++) {
1809 int tile;
1810
1811 i = y * w2 + x;
1812
1813 tile = ds->gridfs[i];
1814
1815 if (state->grid[i] == N_ONE) {
1816 tile |= FF_ONE;
1817 if (ds->rowfs[y] || ds->rowfs[2*h2 + x])
1818 tile |= FE_COUNT;
1819 } else if (state->grid[i] == N_ZERO) {
1820 tile |= FF_ZERO;
1821 if (ds->rowfs[h2 + y] || ds->rowfs[2*h2 + w2 + x])
1822 tile |= FE_COUNT;
1823 }
1824
1825 tile |= flash;
1826
1827 if (state->immutable[i])
1828 tile |= FF_IMMUTABLE;
1829
1830 if (ui->cursor && ui->cx == x && ui->cy == y)
1831 tile |= FF_CURSOR;
1832
1833 if (ds->grid[i] != tile) {
1834 ds->grid[i] = tile;
1835 unruly_draw_tile(dr, COORD(x), COORD(y), TILE_SIZE, tile);
1836 }
1837 }
1838 }
1839}
1840
1841static float game_anim_length(const game_state *oldstate,
1842 const game_state *newstate, int dir, game_ui *ui)
1843{
1844 return 0.0F;
1845}
1846
1847static float game_flash_length(const game_state *oldstate,
1848 const game_state *newstate, int dir, game_ui *ui)
1849{
1850 if (!oldstate->completed && newstate->completed &&
1851 !oldstate->cheated && !newstate->cheated)
1852 return FLASH_TIME;
1853 return 0.0F;
1854}
1855
1856static int game_status(const game_state *state)
1857{
1858 return state->completed ? +1 : 0;
1859}
1860
1861static int game_timing_state(const game_state *state, game_ui *ui)
1862{
1863 return TRUE;
1864}
1865
1866static void game_print_size(const game_params *params, float *x, float *y)
1867{
1868 int pw, ph;
1869
1870 /* Using 7mm squares */
1871 game_compute_size(params, 700, &pw, &ph);
1872 *x = pw / 100.0F;
1873 *y = ph / 100.0F;
1874}
1875
1876static void game_print(drawing *dr, const game_state *state, int tilesize)
1877{
1878 int w2 = state->w2, h2 = state->h2;
1879 int x, y;
1880
1881 int ink = print_mono_colour(dr, 0);
1882
1883 for (y = 0; y < h2; y++)
1884 for (x = 0; x < w2; x++) {
1885 int tx = x * tilesize + (tilesize / 2);
1886 int ty = y * tilesize + (tilesize / 2);
1887
1888 /* Draw the border */
1889 int coords[8];
1890 coords[0] = tx;
1891 coords[1] = ty - 1;
1892 coords[2] = tx + tilesize;
1893 coords[3] = ty - 1;
1894 coords[4] = tx + tilesize;
1895 coords[5] = ty + tilesize - 1;
1896 coords[6] = tx;
1897 coords[7] = ty + tilesize - 1;
1898 draw_polygon(dr, coords, 4, -1, ink);
1899
1900 if (state->grid[y * w2 + x] == N_ONE)
1901 draw_rect(dr, tx, ty, tilesize, tilesize, ink);
1902 else if (state->grid[y * w2 + x] == N_ZERO)
1903 draw_circle(dr, tx + tilesize/2, ty + tilesize/2,
1904 tilesize/12, ink, ink);
1905 }
1906}
1907
1908#ifdef COMBINED
1909#define thegame unruly
1910#endif
1911
1912const struct game thegame = {
1913 "Unruly", "games.unruly", "unruly",
1914 default_params,
1915 game_fetch_preset, NULL,
1916 decode_params,
1917 encode_params,
1918 free_params,
1919 dup_params,
1920 TRUE, game_configure, custom_params,
1921 validate_params,
1922 new_game_desc,
1923 validate_desc,
1924 new_game,
1925 dup_game,
1926 free_game,
1927 TRUE, solve_game,
1928 TRUE, game_can_format_as_text_now, game_text_format,
1929 new_ui,
1930 free_ui,
1931 encode_ui,
1932 decode_ui,
1933 game_changed_state,
1934 interpret_move,
1935 execute_move,
1936 DEFAULT_TILE_SIZE, game_compute_size, game_set_size,
1937 game_colours,
1938 game_new_drawstate,
1939 game_free_drawstate,
1940 game_redraw,
1941 game_anim_length,
1942 game_flash_length,
1943 game_status,
1944 TRUE, FALSE, game_print_size, game_print,
1945 FALSE, /* wants_statusbar */
1946 FALSE, game_timing_state,
1947 0, /* flags */
1948};
1949
1950/* ***************** *
1951 * Standalone solver *
1952 * ***************** */
1953
1954#ifdef STANDALONE_SOLVER
1955#include <time.h>
1956#include <stdarg.h>
1957
1958/* Most of the standalone solver code was copied from unequal.c and singles.c */
1959
1960const char *quis;
1961
1962static void usage_exit(const char *msg)
1963{
1964 if (msg)
1965 fprintf(stderr, "%s: %s\n", quis, msg);
1966 fprintf(stderr,
1967 "Usage: %s [-v] [--seed SEED] <params> | [game_id [game_id ...]]\n",
1968 quis);
1969 exit(1);
1970}
1971
1972int main(int argc, char *argv[])
1973{
1974 random_state *rs;
1975 time_t seed = time(NULL);
1976
1977 game_params *params = NULL;
1978
1979 char *id = NULL, *desc = NULL, *err;
1980
1981 quis = argv[0];
1982
1983 while (--argc > 0) {
1984 char *p = *++argv;
1985 if (!strcmp(p, "--seed")) {
1986 if (argc == 0)
1987 usage_exit("--seed needs an argument");
1988 seed = (time_t) atoi(*++argv);
1989 argc--;
1990 } else if (!strcmp(p, "-v"))
1991 solver_verbose = TRUE;
1992 else if (*p == '-')
1993 usage_exit("unrecognised option");
1994 else
1995 id = p;
1996 }
1997
1998 if (id) {
1999 desc = strchr(id, ':');
2000 if (desc)
2001 *desc++ = '\0';
2002
2003 params = default_params();
2004 decode_params(params, id);
2005 err = validate_params(params, TRUE);
2006 if (err) {
2007 fprintf(stderr, "Parameters are invalid\n");
2008 fprintf(stderr, "%s: %s", argv[0], err);
2009 exit(1);
2010 }
2011 }
2012
2013 if (!desc) {
2014 char *desc_gen, *aux;
2015 rs = random_new((void *) &seed, sizeof(time_t));
2016 if (!params)
2017 params = default_params();
2018 printf("Generating puzzle with parameters %s\n",
2019 encode_params(params, TRUE));
2020 desc_gen = new_game_desc(params, rs, &aux, FALSE);
2021
2022 if (!solver_verbose) {
2023 char *fmt = game_text_format(new_game(NULL, params, desc_gen));
2024 fputs(fmt, stdout);
2025 sfree(fmt);
2026 }
2027
2028 printf("Game ID: %s\n", desc_gen);
2029 } else {
2030 game_state *input;
2031 struct unruly_scratch *scratch;
2032 int maxdiff, errcode;
2033
2034 err = validate_desc(params, desc);
2035 if (err) {
2036 fprintf(stderr, "Description is invalid\n");
2037 fprintf(stderr, "%s", err);
2038 exit(1);
2039 }
2040
2041 input = new_game(NULL, params, desc);
2042 scratch = unruly_new_scratch(input);
2043
2044 maxdiff = unruly_solve_game(input, scratch, DIFFCOUNT);
2045
2046 errcode = unruly_validate_counts(input, scratch, NULL);
2047 if (unruly_validate_all_rows(input, NULL) == -1)
2048 errcode = -1;
2049
2050 if (errcode != -1) {
2051 char *fmt = game_text_format(input);
2052 fputs(fmt, stdout);
2053 sfree(fmt);
2054 if (maxdiff < 0)
2055 printf("Difficulty: already solved!\n");
2056 else
2057 printf("Difficulty: %s\n", unruly_diffnames[maxdiff]);
2058 }
2059
2060 if (errcode == 1)
2061 printf("No solution found.\n");
2062 else if (errcode == -1)
2063 printf("Puzzle is invalid.\n");
2064
2065 free_game(input);
2066 unruly_free_scratch(scratch);
2067 }
2068
2069 return 0;
2070}
2071#endif
diff --git a/apps/plugins/puzzles/src/unruly.html b/apps/plugins/puzzles/src/unruly.html
new file mode 100644
index 0000000000..92d19ad50a
--- /dev/null
+++ b/apps/plugins/puzzles/src/unruly.html
@@ -0,0 +1,63 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Unruly</title>
7<link rel="previous" href="undead.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="flood.html">
12</head>
13<body>
14<p><a href="undead.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="flood.html">Next</a></p>
15<h1><a name="C38"></a>Chapter 38: <a name="i0"></a>Unruly</h1>
16<p>
17You are given a grid of squares, which you must colour either black or white. Some squares are provided as clues; the rest are left for you to fill in. Each row and column must contain the same number of black and white squares, and no row or column may contain three consecutive squares of the same colour.
18</p>
19<p>
20This puzzle type was invented by Adolfo Zanellati, under the name &#8216;Tohu wa Vohu&#8217;. See <a href="#p0">[21]</a> for more details.
21</p>
22<p>
23Unruly was contributed to this collection by Lennard Sprong.
24</p>
25<p><a name="p0"></a>
26[21] <a href="http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm"><code>http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm</code></a>
27</p>
28<h2><a name="S38.1"></a>38.1 <a name="i1"></a>Unruly controls</h2>
29<p>
30To play Unruly, click the mouse in a square to change its colour. Left-clicking an empty square will turn it black, and right-clicking will turn it white. Keep clicking the same button to cycle through the three possible states for the square. If you middle-click in a square it will be reset to empty.
31</p>
32<p>
33You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn an empty square black or white respectively (and then cycle the colours in the same way as the mouse buttons), and pressing Backspace will reset a square to empty.
34</p>
35<p>
36(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
37</p>
38<h2><a name="S38.2"></a>38.2 <a name="i2"></a>Unruly parameters</h2>
39<p>
40These parameters are available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu.
41</p>
42<dl><dt>
43<em>Width</em>, <em>Height</em>
44</dt>
45<dd>
46Size of grid in squares. (Note that the rules of the game require both the width and height to be even numbers.)
47</dd>
48<dt>
49<em>Difficulty</em>
50</dt>
51<dd>
52Controls the difficulty of the generated puzzle.
53</dd>
54<dt>
55<em>Unique rows and columns</em>
56</dt>
57<dd>
58If enabled, no two rows are permitted to have exactly the same pattern, and likewise columns. (A row and a column can match, though.)
59</dd>
60</dl>
61
62<hr><address></address></body>
63</html>
diff --git a/apps/plugins/puzzles/src/untangle.R b/apps/plugins/puzzles/src/untangle.R
new file mode 100644
index 0000000000..a57f1e56fd
--- /dev/null
+++ b/apps/plugins/puzzles/src/untangle.R
@@ -0,0 +1,21 @@
1# -*- makefile -*-
2
3UNTANGLE_EXTRA = tree234
4
5untangle : [X] GTK COMMON untangle UNTANGLE_EXTRA untangle-icon|no-icon
6
7untangle : [G] WINDOWS COMMON untangle UNTANGLE_EXTRA untangle.res|noicon.res
8
9ALL += untangle[COMBINED] UNTANGLE_EXTRA
10
11!begin am gtk
12GAMES += untangle
13!end
14
15!begin >list.c
16 A(untangle) \
17!end
18
19!begin >gamedesc.txt
20untangle:untangle.exe:Untangle:Planar graph layout puzzle:Reposition the points so that the lines do not cross.
21!end
diff --git a/apps/plugins/puzzles/src/untangle.c b/apps/plugins/puzzles/src/untangle.c
new file mode 100644
index 0000000000..47e839e875
--- /dev/null
+++ b/apps/plugins/puzzles/src/untangle.c
@@ -0,0 +1,1637 @@
1/*
2 * untangle.c: Game about planar graphs. You are given a graph
3 * represented by points and straight lines, with some lines
4 * crossing; your task is to drag the points into a configuration
5 * where none of the lines cross.
6 *
7 * Cloned from a Flash game called `Planarity', by John Tantalo.
8 * <http://home.cwru.edu/~jnt5/Planarity> at the time of writing
9 * this. The Flash game had a fixed set of levels; my added value,
10 * as usual, is automatic generation of random games to order.
11 */
12
13/*
14 * TODO:
15 *
16 * - This puzzle, perhaps uniquely among the collection, could use
17 * support for non-aspect-ratio-preserving resizes. This would
18 * require some sort of fairly large redesign, unfortunately (since
19 * it would invalidate the basic assumption that puzzles' size
20 * requirements are adequately expressed by a single scalar tile
21 * size), and probably complicate the rest of the puzzles' API as a
22 * result. So I'm not sure I really want to do it.
23 *
24 * - It would be nice if we could somehow auto-detect a real `long
25 * long' type on the host platform and use it in place of my
26 * hand-hacked int64s. It'd be faster and more reliable.
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <assert.h>
33#include <ctype.h>
34#include <math.h>
35
36#include "puzzles.h"
37#include "tree234.h"
38
39#define CIRCLE_RADIUS 6
40#define DRAG_THRESHOLD (CIRCLE_RADIUS * 2)
41#define PREFERRED_TILESIZE 64
42#define CURSOR_GRANULARITY 5
43
44#define FLASH_TIME 0.30F
45#define ANIM_TIME 0.13F
46#define SOLVEANIM_TIME 0.50F
47
48enum {
49 COL_SYSBACKGROUND,
50 COL_BACKGROUND,
51 COL_LINE,
52#ifdef SHOW_CROSSINGS
53 COL_CROSSEDLINE,
54#endif
55 COL_OUTLINE,
56 COL_POINT,
57 COL_DRAGPOINT,
58 COL_CURSORPOINT,
59 COL_NEIGHBOUR,
60 COL_FLASH1,
61 COL_FLASH2,
62 NCOLOURS
63};
64
65typedef struct point {
66 /*
67 * Points are stored using rational coordinates, with the same
68 * denominator for both coordinates.
69 */
70 long x, y, d;
71} point;
72
73typedef struct edge {
74 /*
75 * This structure is implicitly associated with a particular
76 * point set, so all it has to do is to store two point
77 * indices. It is required to store them in the order (lower,
78 * higher), i.e. a < b always.
79 */
80 int a, b;
81} edge;
82
83struct game_params {
84 int n; /* number of points */
85};
86
87struct graph {
88 int refcount; /* for deallocation */
89 tree234 *edges; /* stores `edge' structures */
90};
91
92struct game_state {
93 game_params params;
94 int w, h; /* extent of coordinate system only */
95 point *pts;
96#ifdef SHOW_CROSSINGS
97 int *crosses; /* mark edges which are crossed */
98#endif
99 struct graph *graph;
100 int completed, cheated, just_solved;
101};
102
103static int edgecmpC(const void *av, const void *bv)
104{
105 const edge *a = (const edge *)av;
106 const edge *b = (const edge *)bv;
107
108 if (a->a < b->a)
109 return -1;
110 else if (a->a > b->a)
111 return +1;
112 else if (a->b < b->b)
113 return -1;
114 else if (a->b > b->b)
115 return +1;
116 return 0;
117}
118
119static int edgecmp(void *av, void *bv) { return edgecmpC(av, bv); }
120
121static game_params *default_params(void)
122{
123 game_params *ret = snew(game_params);
124
125 ret->n = 10;
126
127 return ret;
128}
129
130static int game_fetch_preset(int i, char **name, game_params **params)
131{
132 game_params *ret;
133 int n;
134 char buf[80];
135
136 switch (i) {
137 case 0: n = 6; break;
138 case 1: n = 10; break;
139 case 2: n = 15; break;
140 case 3: n = 20; break;
141 case 4: n = 25; break;
142 default: return FALSE;
143 }
144
145 sprintf(buf, "%d points", n);
146 *name = dupstr(buf);
147
148 *params = ret = snew(game_params);
149 ret->n = n;
150
151 return TRUE;
152}
153
154static void free_params(game_params *params)
155{
156 sfree(params);
157}
158
159static game_params *dup_params(const game_params *params)
160{
161 game_params *ret = snew(game_params);
162 *ret = *params; /* structure copy */
163 return ret;
164}
165
166static void decode_params(game_params *params, char const *string)
167{
168 params->n = atoi(string);
169}
170
171static char *encode_params(const game_params *params, int full)
172{
173 char buf[80];
174
175 sprintf(buf, "%d", params->n);
176
177 return dupstr(buf);
178}
179
180static config_item *game_configure(const game_params *params)
181{
182 config_item *ret;
183 char buf[80];
184
185 ret = snewn(3, config_item);
186
187 ret[0].name = "Number of points";
188 ret[0].type = C_STRING;
189 sprintf(buf, "%d", params->n);
190 ret[0].sval = dupstr(buf);
191 ret[0].ival = 0;
192
193 ret[1].name = NULL;
194 ret[1].type = C_END;
195 ret[1].sval = NULL;
196 ret[1].ival = 0;
197
198 return ret;
199}
200
201static game_params *custom_params(const config_item *cfg)
202{
203 game_params *ret = snew(game_params);
204
205 ret->n = atoi(cfg[0].sval);
206
207 return ret;
208}
209
210static char *validate_params(const game_params *params, int full)
211{
212 if (params->n < 4)
213 return "Number of points must be at least four";
214 return NULL;
215}
216
217/* ----------------------------------------------------------------------
218 * Small number of 64-bit integer arithmetic operations, to prevent
219 * integer overflow at the very core of cross().
220 */
221
222typedef struct {
223 long hi;
224 unsigned long lo;
225} int64;
226
227#define greater64(i,j) ( (i).hi>(j).hi || ((i).hi==(j).hi && (i).lo>(j).lo))
228#define sign64(i) ((i).hi < 0 ? -1 : (i).hi==0 && (i).lo==0 ? 0 : +1)
229
230static int64 mulu32to64(unsigned long x, unsigned long y)
231{
232 unsigned long a, b, c, d, t;
233 int64 ret;
234
235 a = (x & 0xFFFF) * (y & 0xFFFF);
236 b = (x & 0xFFFF) * (y >> 16);
237 c = (x >> 16) * (y & 0xFFFF);
238 d = (x >> 16) * (y >> 16);
239
240 ret.lo = a;
241 ret.hi = d + (b >> 16) + (c >> 16);
242 t = (b & 0xFFFF) << 16;
243 ret.lo += t;
244 if (ret.lo < t)
245 ret.hi++;
246 t = (c & 0xFFFF) << 16;
247 ret.lo += t;
248 if (ret.lo < t)
249 ret.hi++;
250
251#ifdef DIAGNOSTIC_VIA_LONGLONG
252 assert(((unsigned long long)ret.hi << 32) + ret.lo ==
253 (unsigned long long)x * y);
254#endif
255
256 return ret;
257}
258
259static int64 mul32to64(long x, long y)
260{
261 int sign = +1;
262 int64 ret;
263#ifdef DIAGNOSTIC_VIA_LONGLONG
264 long long realret = (long long)x * y;
265#endif
266
267 if (x < 0)
268 x = -x, sign = -sign;
269 if (y < 0)
270 y = -y, sign = -sign;
271
272 ret = mulu32to64(x, y);
273
274 if (sign < 0) {
275 ret.hi = -ret.hi;
276 ret.lo = -ret.lo;
277 if (ret.lo)
278 ret.hi--;
279 }
280
281#ifdef DIAGNOSTIC_VIA_LONGLONG
282 assert(((unsigned long long)ret.hi << 32) + ret.lo == realret);
283#endif
284
285 return ret;
286}
287
288static int64 dotprod64(long a, long b, long p, long q)
289{
290 int64 ab, pq;
291
292 ab = mul32to64(a, b);
293 pq = mul32to64(p, q);
294 ab.hi += pq.hi;
295 ab.lo += pq.lo;
296 if (ab.lo < pq.lo)
297 ab.hi++;
298 return ab;
299}
300
301/*
302 * Determine whether the line segments between a1 and a2, and
303 * between b1 and b2, intersect. We count it as an intersection if
304 * any of the endpoints lies _on_ the other line.
305 */
306static int cross(point a1, point a2, point b1, point b2)
307{
308 long b1x, b1y, b2x, b2y, px, py;
309 int64 d1, d2, d3;
310
311 /*
312 * The condition for crossing is that b1 and b2 are on opposite
313 * sides of the line a1-a2, and vice versa. We determine this
314 * by taking the dot product of b1-a1 with a vector
315 * perpendicular to a2-a1, and similarly with b2-a1, and seeing
316 * if they have different signs.
317 */
318
319 /*
320 * Construct the vector b1-a1. We don't have to worry too much
321 * about the denominator, because we're only going to check the
322 * sign of this vector; we just need to get the numerator
323 * right.
324 */
325 b1x = b1.x * a1.d - a1.x * b1.d;
326 b1y = b1.y * a1.d - a1.y * b1.d;
327 /* Now construct b2-a1, and a vector perpendicular to a2-a1,
328 * in the same way. */
329 b2x = b2.x * a1.d - a1.x * b2.d;
330 b2y = b2.y * a1.d - a1.y * b2.d;
331 px = a1.y * a2.d - a2.y * a1.d;
332 py = a2.x * a1.d - a1.x * a2.d;
333 /* Take the dot products. Here we resort to 64-bit arithmetic. */
334 d1 = dotprod64(b1x, px, b1y, py);
335 d2 = dotprod64(b2x, px, b2y, py);
336 /* If they have the same non-zero sign, the lines do not cross. */
337 if ((sign64(d1) > 0 && sign64(d2) > 0) ||
338 (sign64(d1) < 0 && sign64(d2) < 0))
339 return FALSE;
340
341 /*
342 * If the dot products are both exactly zero, then the two line
343 * segments are collinear. At this point the intersection
344 * condition becomes whether or not they overlap within their
345 * line.
346 */
347 if (sign64(d1) == 0 && sign64(d2) == 0) {
348 /* Construct the vector a2-a1. */
349 px = a2.x * a1.d - a1.x * a2.d;
350 py = a2.y * a1.d - a1.y * a2.d;
351 /* Determine the dot products of b1-a1 and b2-a1 with this. */
352 d1 = dotprod64(b1x, px, b1y, py);
353 d2 = dotprod64(b2x, px, b2y, py);
354 /* If they're both strictly negative, the lines do not cross. */
355 if (sign64(d1) < 0 && sign64(d2) < 0)
356 return FALSE;
357 /* Otherwise, take the dot product of a2-a1 with itself. If
358 * the other two dot products both exceed this, the lines do
359 * not cross. */
360 d3 = dotprod64(px, px, py, py);
361 if (greater64(d1, d3) && greater64(d2, d3))
362 return FALSE;
363 }
364
365 /*
366 * We've eliminated the only important special case, and we
367 * have determined that b1 and b2 are on opposite sides of the
368 * line a1-a2. Now do the same thing the other way round and
369 * we're done.
370 */
371 b1x = a1.x * b1.d - b1.x * a1.d;
372 b1y = a1.y * b1.d - b1.y * a1.d;
373 b2x = a2.x * b1.d - b1.x * a2.d;
374 b2y = a2.y * b1.d - b1.y * a2.d;
375 px = b1.y * b2.d - b2.y * b1.d;
376 py = b2.x * b1.d - b1.x * b2.d;
377 d1 = dotprod64(b1x, px, b1y, py);
378 d2 = dotprod64(b2x, px, b2y, py);
379 if ((sign64(d1) > 0 && sign64(d2) > 0) ||
380 (sign64(d1) < 0 && sign64(d2) < 0))
381 return FALSE;
382
383 /*
384 * The lines must cross.
385 */
386 return TRUE;
387}
388
389static unsigned long squarert(unsigned long n) {
390 unsigned long d, a, b, di;
391
392 d = n;
393 a = 0;
394 b = 1L << 30; /* largest available power of 4 */
395 do {
396 a >>= 1;
397 di = 2*a + b;
398 if (di <= d) {
399 d -= di;
400 a += b;
401 }
402 b >>= 2;
403 } while (b);
404
405 return a;
406}
407
408/*
409 * Our solutions are arranged on a square grid big enough that n
410 * points occupy about 1/POINTDENSITY of the grid.
411 */
412#define POINTDENSITY 3
413#define MAXDEGREE 4
414#define COORDLIMIT(n) squarert((n) * POINTDENSITY)
415
416static void addedge(tree234 *edges, int a, int b)
417{
418 edge *e = snew(edge);
419
420 assert(a != b);
421
422 e->a = min(a, b);
423 e->b = max(a, b);
424
425 add234(edges, e);
426}
427
428static int isedge(tree234 *edges, int a, int b)
429{
430 edge e;
431
432 assert(a != b);
433
434 e.a = min(a, b);
435 e.b = max(a, b);
436
437 return find234(edges, &e, NULL) != NULL;
438}
439
440typedef struct vertex {
441 int param;
442 int vindex;
443} vertex;
444
445static int vertcmpC(const void *av, const void *bv)
446{
447 const vertex *a = (vertex *)av;
448 const vertex *b = (vertex *)bv;
449
450 if (a->param < b->param)
451 return -1;
452 else if (a->param > b->param)
453 return +1;
454 else if (a->vindex < b->vindex)
455 return -1;
456 else if (a->vindex > b->vindex)
457 return +1;
458 return 0;
459}
460static int vertcmp(void *av, void *bv) { return vertcmpC(av, bv); }
461
462/*
463 * Construct point coordinates for n points arranged in a circle,
464 * within the bounding box (0,0) to (w,w).
465 */
466static void make_circle(point *pts, int n, int w)
467{
468 long d, r, c, i;
469
470 /*
471 * First, decide on a denominator. Although in principle it
472 * would be nice to set this really high so as to finely
473 * distinguish all the points on the circle, I'm going to set
474 * it at a fixed size to prevent integer overflow problems.
475 */
476 d = PREFERRED_TILESIZE;
477
478 /*
479 * Leave a little space outside the circle.
480 */
481 c = d * w / 2;
482 r = d * w * 3 / 7;
483
484 /*
485 * Place the points.
486 */
487 for (i = 0; i < n; i++) {
488 double angle = i * 2 * PI / n;
489 double x = r * sin(angle), y = - r * cos(angle);
490 pts[i].x = (long)(c + x + 0.5);
491 pts[i].y = (long)(c + y + 0.5);
492 pts[i].d = d;
493 }
494}
495
496static char *new_game_desc(const game_params *params, random_state *rs,
497 char **aux, int interactive)
498{
499 int n = params->n, i;
500 long w, h, j, k, m;
501 point *pts, *pts2;
502 long *tmp;
503 tree234 *edges, *vertices;
504 edge *e, *e2;
505 vertex *v, *vs, *vlist;
506 char *ret;
507
508 w = h = COORDLIMIT(n);
509
510 /*
511 * Choose n points from this grid.
512 */
513 pts = snewn(n, point);
514 tmp = snewn(w*h, long);
515 for (i = 0; i < w*h; i++)
516 tmp[i] = i;
517 shuffle(tmp, w*h, sizeof(*tmp), rs);
518 for (i = 0; i < n; i++) {
519 pts[i].x = tmp[i] % w;
520 pts[i].y = tmp[i] / w;
521 pts[i].d = 1;
522 }
523 sfree(tmp);
524
525 /*
526 * Now start adding edges between the points.
527 *
528 * At all times, we attempt to add an edge to the lowest-degree
529 * vertex we currently have, and we try the other vertices as
530 * candidate second endpoints in order of distance from this
531 * one. We stop as soon as we find an edge which
532 *
533 * (a) does not increase any vertex's degree beyond MAXDEGREE
534 * (b) does not cross any existing edges
535 * (c) does not intersect any actual point.
536 */
537 vs = snewn(n, vertex);
538 vertices = newtree234(vertcmp);
539 for (i = 0; i < n; i++) {
540 v = vs + i;
541 v->param = 0; /* in this tree, param is the degree */
542 v->vindex = i;
543 add234(vertices, v);
544 }
545 edges = newtree234(edgecmp);
546 vlist = snewn(n, vertex);
547 while (1) {
548 int added = FALSE;
549
550 for (i = 0; i < n; i++) {
551 v = index234(vertices, i);
552 j = v->vindex;
553
554 if (v->param >= MAXDEGREE)
555 break; /* nothing left to add! */
556
557 /*
558 * Sort the other vertices into order of their distance
559 * from this one. Don't bother looking below i, because
560 * we've already tried those edges the other way round.
561 * Also here we rule out target vertices with too high
562 * a degree, and (of course) ones to which we already
563 * have an edge.
564 */
565 m = 0;
566 for (k = i+1; k < n; k++) {
567 vertex *kv = index234(vertices, k);
568 int ki = kv->vindex;
569 int dx, dy;
570
571 if (kv->param >= MAXDEGREE || isedge(edges, ki, j))
572 continue;
573
574 vlist[m].vindex = ki;
575 dx = pts[ki].x - pts[j].x;
576 dy = pts[ki].y - pts[j].y;
577 vlist[m].param = dx*dx + dy*dy;
578 m++;
579 }
580
581 qsort(vlist, m, sizeof(*vlist), vertcmpC);
582
583 for (k = 0; k < m; k++) {
584 int p;
585 int ki = vlist[k].vindex;
586
587 /*
588 * Check to see whether this edge intersects any
589 * existing edge or point.
590 */
591 for (p = 0; p < n; p++)
592 if (p != ki && p != j && cross(pts[ki], pts[j],
593 pts[p], pts[p]))
594 break;
595 if (p < n)
596 continue;
597 for (p = 0; (e = index234(edges, p)) != NULL; p++)
598 if (e->a != ki && e->a != j &&
599 e->b != ki && e->b != j &&
600 cross(pts[ki], pts[j], pts[e->a], pts[e->b]))
601 break;
602 if (e)
603 continue;
604
605 /*
606 * We're done! Add this edge, modify the degrees of
607 * the two vertices involved, and break.
608 */
609 addedge(edges, j, ki);
610 added = TRUE;
611 del234(vertices, vs+j);
612 vs[j].param++;
613 add234(vertices, vs+j);
614 del234(vertices, vs+ki);
615 vs[ki].param++;
616 add234(vertices, vs+ki);
617 break;
618 }
619
620 if (k < m)
621 break;
622 }
623
624 if (!added)
625 break; /* we're done. */
626 }
627
628 /*
629 * That's our graph. Now shuffle the points, making sure that
630 * they come out with at least one crossed line when arranged
631 * in a circle (so that the puzzle isn't immediately solved!).
632 */
633 tmp = snewn(n, long);
634 for (i = 0; i < n; i++)
635 tmp[i] = i;
636 pts2 = snewn(n, point);
637 make_circle(pts2, n, w);
638 while (1) {
639 shuffle(tmp, n, sizeof(*tmp), rs);
640 for (i = 0; (e = index234(edges, i)) != NULL; i++) {
641 for (j = i+1; (e2 = index234(edges, j)) != NULL; j++) {
642 if (e2->a == e->a || e2->a == e->b ||
643 e2->b == e->a || e2->b == e->b)
644 continue;
645 if (cross(pts2[tmp[e2->a]], pts2[tmp[e2->b]],
646 pts2[tmp[e->a]], pts2[tmp[e->b]]))
647 break;
648 }
649 if (e2)
650 break;
651 }
652 if (e)
653 break; /* we've found a crossing */
654 }
655
656 /*
657 * We're done. Now encode the graph in a string format. Let's
658 * use a comma-separated list of dash-separated vertex number
659 * pairs, numbered from zero. We'll sort the list to prevent
660 * side channels.
661 */
662 ret = NULL;
663 {
664 char *sep;
665 char buf[80];
666 int retlen;
667 edge *ea;
668
669 retlen = 0;
670 m = count234(edges);
671 ea = snewn(m, edge);
672 for (i = 0; (e = index234(edges, i)) != NULL; i++) {
673 assert(i < m);
674 ea[i].a = min(tmp[e->a], tmp[e->b]);
675 ea[i].b = max(tmp[e->a], tmp[e->b]);
676 retlen += 1 + sprintf(buf, "%d-%d", ea[i].a, ea[i].b);
677 }
678 assert(i == m);
679 qsort(ea, m, sizeof(*ea), edgecmpC);
680
681 ret = snewn(retlen, char);
682 sep = "";
683 k = 0;
684
685 for (i = 0; i < m; i++) {
686 k += sprintf(ret + k, "%s%d-%d", sep, ea[i].a, ea[i].b);
687 sep = ",";
688 }
689 assert(k < retlen);
690
691 sfree(ea);
692 }
693
694 /*
695 * Encode the solution we started with as an aux_info string.
696 */
697 {
698 char buf[80];
699 char *auxstr;
700 int auxlen;
701
702 auxlen = 2; /* leading 'S' and trailing '\0' */
703 for (i = 0; i < n; i++) {
704 j = tmp[i];
705 pts2[j] = pts[i];
706 if (pts2[j].d & 1) {
707 pts2[j].x *= 2;
708 pts2[j].y *= 2;
709 pts2[j].d *= 2;
710 }
711 pts2[j].x += pts2[j].d / 2;
712 pts2[j].y += pts2[j].d / 2;
713 auxlen += sprintf(buf, ";P%d:%ld,%ld/%ld", i,
714 pts2[j].x, pts2[j].y, pts2[j].d);
715 }
716 k = 0;
717 auxstr = snewn(auxlen, char);
718 auxstr[k++] = 'S';
719 for (i = 0; i < n; i++)
720 k += sprintf(auxstr+k, ";P%d:%ld,%ld/%ld", i,
721 pts2[i].x, pts2[i].y, pts2[i].d);
722 assert(k < auxlen);
723 *aux = auxstr;
724 }
725 sfree(pts2);
726
727 sfree(tmp);
728 sfree(vlist);
729 freetree234(vertices);
730 sfree(vs);
731 while ((e = delpos234(edges, 0)) != NULL)
732 sfree(e);
733 freetree234(edges);
734 sfree(pts);
735
736 return ret;
737}
738
739static char *validate_desc(const game_params *params, const char *desc)
740{
741 int a, b;
742
743 while (*desc) {
744 a = atoi(desc);
745 if (a < 0 || a >= params->n)
746 return "Number out of range in game description";
747 while (*desc && isdigit((unsigned char)*desc)) desc++;
748 if (*desc != '-')
749 return "Expected '-' after number in game description";
750 desc++; /* eat dash */
751 b = atoi(desc);
752 if (b < 0 || b >= params->n)
753 return "Number out of range in game description";
754 while (*desc && isdigit((unsigned char)*desc)) desc++;
755 if (*desc) {
756 if (*desc != ',')
757 return "Expected ',' after number in game description";
758 desc++; /* eat comma */
759 }
760 }
761
762 return NULL;
763}
764
765static void mark_crossings(game_state *state)
766{
767 int ok = TRUE;
768 int i, j;
769 edge *e, *e2;
770
771#ifdef SHOW_CROSSINGS
772 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++)
773 state->crosses[i] = FALSE;
774#endif
775
776 /*
777 * Check correctness: for every pair of edges, see whether they
778 * cross.
779 */
780 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) {
781 for (j = i+1; (e2 = index234(state->graph->edges, j)) != NULL; j++) {
782 if (e2->a == e->a || e2->a == e->b ||
783 e2->b == e->a || e2->b == e->b)
784 continue;
785 if (cross(state->pts[e2->a], state->pts[e2->b],
786 state->pts[e->a], state->pts[e->b])) {
787 ok = FALSE;
788#ifdef SHOW_CROSSINGS
789 state->crosses[i] = state->crosses[j] = TRUE;
790#else
791 goto done; /* multi-level break - sorry */
792#endif
793 }
794 }
795 }
796
797 /*
798 * e == NULL if we've gone through all the edge pairs
799 * without finding a crossing.
800 */
801#ifndef SHOW_CROSSINGS
802 done:
803#endif
804 if (ok)
805 state->completed = TRUE;
806}
807
808static game_state *new_game(midend *me, const game_params *params,
809 const char *desc)
810{
811 int n = params->n;
812 game_state *state = snew(game_state);
813 int a, b;
814
815 state->params = *params;
816 state->w = state->h = COORDLIMIT(n);
817 state->pts = snewn(n, point);
818 make_circle(state->pts, n, state->w);
819 state->graph = snew(struct graph);
820 state->graph->refcount = 1;
821 state->graph->edges = newtree234(edgecmp);
822 state->completed = state->cheated = state->just_solved = FALSE;
823
824 while (*desc) {
825 a = atoi(desc);
826 assert(a >= 0 && a < params->n);
827 while (*desc && isdigit((unsigned char)*desc)) desc++;
828 assert(*desc == '-');
829 desc++; /* eat dash */
830 b = atoi(desc);
831 assert(b >= 0 && b < params->n);
832 while (*desc && isdigit((unsigned char)*desc)) desc++;
833 if (*desc) {
834 assert(*desc == ',');
835 desc++; /* eat comma */
836 }
837 addedge(state->graph->edges, a, b);
838 }
839
840#ifdef SHOW_CROSSINGS
841 state->crosses = snewn(count234(state->graph->edges), int);
842 mark_crossings(state); /* sets up `crosses' and `completed' */
843#endif
844
845 return state;
846}
847
848static game_state *dup_game(const game_state *state)
849{
850 int n = state->params.n;
851 game_state *ret = snew(game_state);
852
853 ret->params = state->params;
854 ret->w = state->w;
855 ret->h = state->h;
856 ret->pts = snewn(n, point);
857 memcpy(ret->pts, state->pts, n * sizeof(point));
858 ret->graph = state->graph;
859 ret->graph->refcount++;
860 ret->completed = state->completed;
861 ret->cheated = state->cheated;
862 ret->just_solved = state->just_solved;
863#ifdef SHOW_CROSSINGS
864 ret->crosses = snewn(count234(ret->graph->edges), int);
865 memcpy(ret->crosses, state->crosses,
866 count234(ret->graph->edges) * sizeof(int));
867#endif
868
869 return ret;
870}
871
872static void free_game(game_state *state)
873{
874 if (--state->graph->refcount <= 0) {
875 edge *e;
876 while ((e = delpos234(state->graph->edges, 0)) != NULL)
877 sfree(e);
878 freetree234(state->graph->edges);
879 sfree(state->graph);
880 }
881 sfree(state->pts);
882 sfree(state);
883}
884
885static char *solve_game(const game_state *state, const game_state *currstate,
886 const char *aux, char **error)
887{
888 int n = state->params.n;
889 int matrix[4];
890 point *pts;
891 int i, j, besti;
892 float bestd;
893 char buf[80], *ret;
894 int retlen, retsize;
895
896 if (!aux) {
897 *error = "Solution not known for this puzzle";
898 return NULL;
899 }
900
901 /*
902 * Decode the aux_info to get the original point positions.
903 */
904 pts = snewn(n, point);
905 aux++; /* eat 'S' */
906 for (i = 0; i < n; i++) {
907 int p, k;
908 long x, y, d;
909 int ret = sscanf(aux, ";P%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k);
910 if (ret != 4 || p != i) {
911 *error = "Internal error: aux_info badly formatted";
912 sfree(pts);
913 return NULL;
914 }
915 pts[i].x = x;
916 pts[i].y = y;
917 pts[i].d = d;
918 aux += k;
919 }
920
921 /*
922 * Now go through eight possible symmetries of the point set.
923 * For each one, work out the sum of the Euclidean distances
924 * between the points' current positions and their new ones.
925 *
926 * We're squaring distances here, which means we're at risk of
927 * integer overflow. Fortunately, there's no real need to be
928 * massively careful about rounding errors, since this is a
929 * non-essential bit of the code; so I'll just work in floats
930 * internally.
931 */
932 besti = -1;
933 bestd = 0.0F;
934
935 for (i = 0; i < 8; i++) {
936 float d;
937
938 matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0;
939 matrix[i & 1] = (i & 2) ? +1 : -1;
940 matrix[3-(i&1)] = (i & 4) ? +1 : -1;
941
942 d = 0.0F;
943 for (j = 0; j < n; j++) {
944 float px = (float)pts[j].x / pts[j].d;
945 float py = (float)pts[j].y / pts[j].d;
946 float sx = (float)currstate->pts[j].x / currstate->pts[j].d;
947 float sy = (float)currstate->pts[j].y / currstate->pts[j].d;
948 float cx = (float)currstate->w / 2;
949 float cy = (float)currstate->h / 2;
950 float ox, oy, dx, dy;
951
952 px -= cx;
953 py -= cy;
954
955 ox = matrix[0] * px + matrix[1] * py;
956 oy = matrix[2] * px + matrix[3] * py;
957
958 ox += cx;
959 oy += cy;
960
961 dx = ox - sx;
962 dy = oy - sy;
963
964 d += dx*dx + dy*dy;
965 }
966
967 if (besti < 0 || bestd > d) {
968 besti = i;
969 bestd = d;
970 }
971 }
972
973 assert(besti >= 0);
974
975 /*
976 * Now we know which symmetry is closest to the points' current
977 * positions. Use it.
978 */
979 matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0;
980 matrix[besti & 1] = (besti & 2) ? +1 : -1;
981 matrix[3-(besti&1)] = (besti & 4) ? +1 : -1;
982
983 retsize = 256;
984 ret = snewn(retsize, char);
985 retlen = 0;
986 ret[retlen++] = 'S';
987 ret[retlen] = '\0';
988
989 for (i = 0; i < n; i++) {
990 float px = (float)pts[i].x / pts[i].d;
991 float py = (float)pts[i].y / pts[i].d;
992 float cx = (float)currstate->w / 2;
993 float cy = (float)currstate->h / 2;
994 float ox, oy;
995 int extra;
996
997 px -= cx;
998 py -= cy;
999
1000 ox = matrix[0] * px + matrix[1] * py;
1001 oy = matrix[2] * px + matrix[3] * py;
1002
1003 ox += cx;
1004 oy += cy;
1005
1006 /*
1007 * Use a fixed denominator of 2, because we know the
1008 * original points were on an integer grid offset by 1/2.
1009 */
1010 pts[i].d = 2;
1011 ox *= pts[i].d;
1012 oy *= pts[i].d;
1013 pts[i].x = (long)(ox + 0.5F);
1014 pts[i].y = (long)(oy + 0.5F);
1015
1016 extra = sprintf(buf, ";P%d:%ld,%ld/%ld", i,
1017 pts[i].x, pts[i].y, pts[i].d);
1018 if (retlen + extra >= retsize) {
1019 retsize = retlen + extra + 256;
1020 ret = sresize(ret, retsize, char);
1021 }
1022 strcpy(ret + retlen, buf);
1023 retlen += extra;
1024 }
1025
1026 sfree(pts);
1027
1028 return ret;
1029}
1030
1031static int game_can_format_as_text_now(const game_params *params)
1032{
1033 return TRUE;
1034}
1035
1036static char *game_text_format(const game_state *state)
1037{
1038 return NULL;
1039}
1040
1041struct game_ui {
1042 int dragpoint; /* point being dragged; -1 if none */
1043
1044 int cursorpoint; /* point being highlighted, but
1045 * not dragged by the cursor,
1046 * again -1 if none */
1047
1048 point newpoint; /* where it's been dragged to so far */
1049 int just_dragged; /* reset in game_changed_state */
1050 int just_moved; /* _set_ in game_changed_state */
1051 float anim_length;
1052};
1053
1054static game_ui *new_ui(const game_state *state)
1055{
1056 game_ui *ui = snew(game_ui);
1057 ui->dragpoint = -1;
1058 ui->cursorpoint = -1;
1059 ui->just_moved = ui->just_dragged = FALSE;
1060 return ui;
1061}
1062
1063static void free_ui(game_ui *ui)
1064{
1065 sfree(ui);
1066}
1067
1068static char *encode_ui(const game_ui *ui)
1069{
1070 return NULL;
1071}
1072
1073static void decode_ui(game_ui *ui, const char *encoding)
1074{
1075}
1076
1077static void game_changed_state(game_ui *ui, const game_state *oldstate,
1078 const game_state *newstate)
1079{
1080 ui->dragpoint = -1;
1081 ui->just_moved = ui->just_dragged;
1082 ui->just_dragged = FALSE;
1083}
1084
1085struct game_drawstate {
1086 long tilesize;
1087 int bg, dragpoint, cursorpoint;
1088 long *x, *y;
1089};
1090
1091static char *interpret_move(const game_state *state, game_ui *ui,
1092 const game_drawstate *ds,
1093 int x, int y, int button)
1094{
1095 int n = state->params.n;
1096
1097 if (IS_MOUSE_DOWN(button)) {
1098 int i, best;
1099 long bestd;
1100
1101 /*
1102 * Begin drag. We drag the vertex _nearest_ to the pointer,
1103 * just in case one is nearly on top of another and we want
1104 * to drag the latter. However, we drag nothing at all if
1105 * the nearest vertex is outside DRAG_THRESHOLD.
1106 */
1107 best = -1;
1108 bestd = 0;
1109
1110 for (i = 0; i < n; i++) {
1111 long px = state->pts[i].x * ds->tilesize / state->pts[i].d;
1112 long py = state->pts[i].y * ds->tilesize / state->pts[i].d;
1113 long dx = px - x;
1114 long dy = py - y;
1115 long d = dx*dx + dy*dy;
1116
1117 if (best == -1 || bestd > d) {
1118 best = i;
1119 bestd = d;
1120 }
1121 }
1122
1123 if (bestd <= DRAG_THRESHOLD * DRAG_THRESHOLD) {
1124 ui->dragpoint = best;
1125 ui->newpoint.x = x;
1126 ui->newpoint.y = y;
1127 ui->newpoint.d = ds->tilesize;
1128 return "";
1129 }
1130
1131 } else if (IS_MOUSE_DRAG(button) && ui->dragpoint >= 0) {
1132 ui->newpoint.x = x;
1133 ui->newpoint.y = y;
1134 ui->newpoint.d = ds->tilesize;
1135 return "";
1136 } else if (IS_MOUSE_RELEASE(button) && ui->dragpoint >= 0) {
1137 int p = ui->dragpoint;
1138 char buf[80];
1139
1140 ui->dragpoint = -1; /* terminate drag, no matter what */
1141
1142 /*
1143 * First, see if we're within range. The user can cancel a
1144 * drag by dragging the point right off the window.
1145 */
1146 if (ui->newpoint.x < 0 ||
1147 ui->newpoint.x >= (long)state->w*ui->newpoint.d ||
1148 ui->newpoint.y < 0 ||
1149 ui->newpoint.y >= (long)state->h*ui->newpoint.d)
1150 return "";
1151
1152 /*
1153 * We aren't cancelling the drag. Construct a move string
1154 * indicating where this point is going to.
1155 */
1156 sprintf(buf, "P%d:%ld,%ld/%ld", p,
1157 ui->newpoint.x, ui->newpoint.y, ui->newpoint.d);
1158 ui->just_dragged = TRUE;
1159 return dupstr(buf);
1160 }
1161 else if(IS_CURSOR_MOVE(button))
1162 {
1163 if(ui->dragpoint < 0)
1164 {
1165 /* We're selecting a point here. */
1166 /* Search all the points and find the closest one (2-D) in
1167 * the given direction. */
1168 int i, best;
1169 long bestd;
1170
1171 if(ui->cursorpoint < 0)
1172 {
1173 ui->cursorpoint = 0;
1174 }
1175
1176 /*
1177 * Begin drag. We drag the vertex _nearest_ to the pointer,
1178 * just in case one is nearly on top of another and we want
1179 * to drag the latter. However, we drag nothing at all if
1180 * the nearest vertex is outside DRAG_THRESHOLD.
1181 */
1182 best = -1;
1183 bestd = 0;
1184
1185 for (i = 0; i < n; i++) {
1186 if(i == ui->cursorpoint)
1187 continue;
1188
1189 long px = state->pts[i].x * ds->tilesize / state->pts[i].d;
1190 long py = state->pts[i].y * ds->tilesize / state->pts[i].d;
1191 long dx = px - state->pts[ui->cursorpoint].x * ds->tilesize / state->pts[ui->cursorpoint].d;
1192 long dy = py - state->pts[ui->cursorpoint].y * ds->tilesize / state->pts[ui->cursorpoint].d;
1193 long d = dx*dx + dy*dy;
1194
1195 /* Figure out if this point falls into a 90 degree
1196 * range extending from the current point */
1197
1198 float angle = atan2(-dy, dx); /* negate y to adjust for raster coordinates */
1199
1200 /* offset to [0..2*PI] */
1201 if(angle < 0)
1202 angle += 2*PI;
1203
1204 int right_direction = FALSE;
1205
1206 if((button == CURSOR_UP && (1*PI/4 <= angle && angle <= 3*PI/4)) ||
1207 (button == CURSOR_LEFT && (3*PI/4 <= angle && angle <= 5*PI/4)) ||
1208 (button == CURSOR_DOWN && (5*PI/4 <= angle && angle <= 7*PI/4)) ||
1209 (button == CURSOR_RIGHT && (angle >= 7*PI/4 || angle <= 1*PI/4)))
1210 right_direction = TRUE;
1211
1212 if ((best == -1 || bestd > d) && right_direction) {
1213 best = i;
1214 bestd = d;
1215 }
1216 }
1217
1218 if(best >= 0)
1219 {
1220 ui->cursorpoint = best;
1221 return "";
1222 }
1223 }
1224 else if(ui->dragpoint >= 0)
1225 {
1226 /* dragging */
1227 switch(button)
1228 {
1229 case CURSOR_UP:
1230 ui->newpoint.y -= ds->tilesize / CURSOR_GRANULARITY;
1231 return "";
1232 case CURSOR_DOWN:
1233 ui->newpoint.y += ds->tilesize / CURSOR_GRANULARITY;
1234 return "";
1235 case CURSOR_LEFT:
1236 ui->newpoint.x -= ds->tilesize / CURSOR_GRANULARITY;
1237 return "";
1238 case CURSOR_RIGHT:
1239 ui->newpoint.x += ds->tilesize / CURSOR_GRANULARITY;
1240 return "";
1241 default:
1242 break;
1243 }
1244 }
1245 }
1246 else if(IS_CURSOR_SELECT(button))
1247 {
1248 if(ui->dragpoint < 0 && ui->cursorpoint >= 0)
1249 {
1250 /* begin drag */
1251 ui->dragpoint = ui->cursorpoint;
1252 ui->cursorpoint = -1;
1253 ui->newpoint.x = state->pts[ui->dragpoint].x * ds->tilesize / state->pts[ui->dragpoint].d;
1254 ui->newpoint.y = state->pts[ui->dragpoint].y * ds->tilesize / state->pts[ui->dragpoint].d;
1255 ui->newpoint.d = ds->tilesize;
1256 return "";
1257 }
1258 else if(ui->dragpoint >= 0)
1259 {
1260 /* end drag */
1261 int p = ui->dragpoint;
1262 char buf[80];
1263
1264 ui->cursorpoint = ui->dragpoint;
1265 ui->dragpoint = -1; /* terminate drag, no matter what */
1266
1267 /*
1268 * First, see if we're within range. The user can cancel a
1269 * drag by dragging the point right off the window.
1270 */
1271 if (ui->newpoint.x < 0 ||
1272 ui->newpoint.x >= (long)state->w*ui->newpoint.d ||
1273 ui->newpoint.y < 0 ||
1274 ui->newpoint.y >= (long)state->h*ui->newpoint.d)
1275 return "";
1276
1277 /*
1278 * We aren't cancelling the drag. Construct a move string
1279 * indicating where this point is going to.
1280 */
1281 sprintf(buf, "P%d:%ld,%ld/%ld", p,
1282 ui->newpoint.x, ui->newpoint.y, ui->newpoint.d);
1283 ui->just_dragged = TRUE;
1284 return dupstr(buf);
1285 }
1286 else if(ui->cursorpoint < 0)
1287 ui->cursorpoint = 0;
1288 }
1289
1290 return NULL;
1291}
1292
1293static game_state *execute_move(const game_state *state, const char *move)
1294{
1295 int n = state->params.n;
1296 int p, k;
1297 long x, y, d;
1298 game_state *ret = dup_game(state);
1299
1300 ret->just_solved = FALSE;
1301
1302 while (*move) {
1303 if (*move == 'S') {
1304 move++;
1305 if (*move == ';') move++;
1306 ret->cheated = ret->just_solved = TRUE;
1307 }
1308 if (*move == 'P' &&
1309 sscanf(move+1, "%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k) == 4 &&
1310 p >= 0 && p < n && d > 0) {
1311 ret->pts[p].x = x;
1312 ret->pts[p].y = y;
1313 ret->pts[p].d = d;
1314
1315 move += k+1;
1316 if (*move == ';') move++;
1317 } else {
1318 free_game(ret);
1319 return NULL;
1320 }
1321 }
1322
1323 mark_crossings(ret);
1324
1325 return ret;
1326}
1327
1328/* ----------------------------------------------------------------------
1329 * Drawing routines.
1330 */
1331
1332static void game_compute_size(const game_params *params, int tilesize,
1333 int *x, int *y)
1334{
1335 *x = *y = COORDLIMIT(params->n) * tilesize;
1336}
1337
1338static void game_set_size(drawing *dr, game_drawstate *ds,
1339 const game_params *params, int tilesize)
1340{
1341 ds->tilesize = tilesize;
1342}
1343
1344static float *game_colours(frontend *fe, int *ncolours)
1345{
1346 float *ret = snewn(3 * NCOLOURS, float);
1347
1348 /*
1349 * COL_BACKGROUND is what we use as the normal background colour.
1350 * Unusually, though, it isn't colour #0: COL_SYSBACKGROUND, a bit
1351 * darker, takes that place. This means that if the user resizes
1352 * an Untangle window so as to change its aspect ratio, the
1353 * still-square playable area will be distinguished from the dead
1354 * space around it.
1355 */
1356 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_SYSBACKGROUND);
1357
1358 ret[COL_LINE * 3 + 0] = 0.0F;
1359 ret[COL_LINE * 3 + 1] = 0.0F;
1360 ret[COL_LINE * 3 + 2] = 0.0F;
1361
1362#ifdef SHOW_CROSSINGS
1363 ret[COL_CROSSEDLINE * 3 + 0] = 1.0F;
1364 ret[COL_CROSSEDLINE * 3 + 1] = 0.0F;
1365 ret[COL_CROSSEDLINE * 3 + 2] = 0.0F;
1366#endif
1367
1368 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1369 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1370 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1371
1372 ret[COL_POINT * 3 + 0] = 0.0F;
1373 ret[COL_POINT * 3 + 1] = 0.0F;
1374 ret[COL_POINT * 3 + 2] = 1.0F;
1375
1376 ret[COL_DRAGPOINT * 3 + 0] = 1.0F;
1377 ret[COL_DRAGPOINT * 3 + 1] = 1.0F;
1378 ret[COL_DRAGPOINT * 3 + 2] = 1.0F;
1379
1380 ret[COL_CURSORPOINT * 3 + 0] = 0.5F;
1381 ret[COL_CURSORPOINT * 3 + 1] = 0.5F;
1382 ret[COL_CURSORPOINT * 3 + 2] = 0.5F;
1383
1384 ret[COL_NEIGHBOUR * 3 + 0] = 1.0F;
1385 ret[COL_NEIGHBOUR * 3 + 1] = 0.0F;
1386 ret[COL_NEIGHBOUR * 3 + 2] = 0.0F;
1387
1388 ret[COL_FLASH1 * 3 + 0] = 0.5F;
1389 ret[COL_FLASH1 * 3 + 1] = 0.5F;
1390 ret[COL_FLASH1 * 3 + 2] = 0.5F;
1391
1392 ret[COL_FLASH2 * 3 + 0] = 1.0F;
1393 ret[COL_FLASH2 * 3 + 1] = 1.0F;
1394 ret[COL_FLASH2 * 3 + 2] = 1.0F;
1395
1396 *ncolours = NCOLOURS;
1397 return ret;
1398}
1399
1400static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1401{
1402 struct game_drawstate *ds = snew(struct game_drawstate);
1403 int i;
1404
1405 ds->tilesize = 0;
1406 ds->x = snewn(state->params.n, long);
1407 ds->y = snewn(state->params.n, long);
1408 for (i = 0; i < state->params.n; i++)
1409 ds->x[i] = ds->y[i] = -1;
1410 ds->bg = -1;
1411 ds->dragpoint = -1;
1412 ds->cursorpoint = -1;
1413
1414 return ds;
1415}
1416
1417static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1418{
1419 sfree(ds->y);
1420 sfree(ds->x);
1421 sfree(ds);
1422}
1423
1424static point mix(point a, point b, float distance)
1425{
1426 point ret;
1427
1428 ret.d = a.d * b.d;
1429 ret.x = (long)(a.x * b.d + distance * (b.x * a.d - a.x * b.d));
1430 ret.y = (long)(a.y * b.d + distance * (b.y * a.d - a.y * b.d));
1431
1432 return ret;
1433}
1434
1435static void game_redraw(drawing *dr, game_drawstate *ds,
1436 const game_state *oldstate, const game_state *state,
1437 int dir, const game_ui *ui,
1438 float animtime, float flashtime)
1439{
1440 int w, h;
1441 edge *e;
1442 int i, j;
1443 int bg, points_moved;
1444
1445 /*
1446 * There's no terribly sensible way to do partial redraws of
1447 * this game, so I'm going to have to resort to redrawing the
1448 * whole thing every time.
1449 */
1450
1451 if (flashtime == 0)
1452 bg = COL_BACKGROUND;
1453 else if ((int)(flashtime * 4 / FLASH_TIME) % 2 == 0)
1454 bg = COL_FLASH1;
1455 else
1456 bg = COL_FLASH2;
1457
1458 /*
1459 * To prevent excessive spinning on redraw during a completion
1460 * flash, we first check to see if _either_ the flash
1461 * background colour has changed _or_ at least one point has
1462 * moved _or_ a drag has begun or ended, and abandon the redraw
1463 * if neither is the case.
1464 *
1465 * Also in this loop we work out the coordinates of all the
1466 * points for this redraw.
1467 */
1468 points_moved = FALSE;
1469 for (i = 0; i < state->params.n; i++) {
1470 point p = state->pts[i];
1471 long x, y;
1472
1473 if (ui->dragpoint == i)
1474 p = ui->newpoint;
1475
1476 if (oldstate)
1477 p = mix(oldstate->pts[i], p, animtime / ui->anim_length);
1478
1479 x = p.x * ds->tilesize / p.d;
1480 y = p.y * ds->tilesize / p.d;
1481
1482 if (ds->x[i] != x || ds->y[i] != y)
1483 points_moved = TRUE;
1484
1485 ds->x[i] = x;
1486 ds->y[i] = y;
1487 }
1488
1489 if (ds->bg == bg && ds->dragpoint == ui->dragpoint && ds->cursorpoint == ui->cursorpoint && !points_moved)
1490 return; /* nothing to do */
1491
1492 ds->dragpoint = ui->dragpoint;
1493 ds->bg = bg;
1494
1495 game_compute_size(&state->params, ds->tilesize, &w, &h);
1496
1497 clip(dr, 0, 0, w, h);
1498 draw_rect(dr, 0, 0, w, h, bg);
1499
1500 /*
1501 * Draw the edges.
1502 */
1503
1504 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) {
1505 draw_line(dr, ds->x[e->a], ds->y[e->a], ds->x[e->b], ds->y[e->b],
1506#ifdef SHOW_CROSSINGS
1507 (oldstate?oldstate:state)->crosses[i] ?
1508 COL_CROSSEDLINE :
1509#endif
1510 COL_LINE);
1511 }
1512
1513 /*
1514 * Draw the points.
1515 *
1516 * When dragging, we should not only vary the colours, but
1517 * leave the point being dragged until last.
1518 */
1519 for (j = 0; j < 4; j++) {
1520 int thisc = (j == 0 ? COL_POINT :
1521 (j == 1 ? COL_NEIGHBOUR :
1522 j == 2 ? COL_CURSORPOINT : COL_DRAGPOINT));
1523 for (i = 0; i < state->params.n; i++) {
1524 int c;
1525
1526 if (ui->dragpoint == i) {
1527 c = COL_DRAGPOINT;
1528 } else if(ui->cursorpoint == i) {
1529 c = COL_CURSORPOINT;
1530 } else if (ui->dragpoint >= 0 &&
1531 isedge(state->graph->edges, ui->dragpoint, i)) {
1532 c = COL_NEIGHBOUR;
1533 } else {
1534 c = COL_POINT;
1535 }
1536
1537 if (c == thisc) {
1538#ifdef VERTEX_NUMBERS
1539 draw_circle(dr, ds->x[i], ds->y[i], DRAG_THRESHOLD, bg, bg);
1540 {
1541 char buf[80];
1542 sprintf(buf, "%d", i);
1543 draw_text(dr, ds->x[i], ds->y[i], FONT_VARIABLE,
1544 DRAG_THRESHOLD*3/2,
1545 ALIGN_VCENTRE|ALIGN_HCENTRE, c, buf);
1546 }
1547#else
1548 draw_circle(dr, ds->x[i], ds->y[i], CIRCLE_RADIUS,
1549 c, COL_OUTLINE);
1550#endif
1551 }
1552 }
1553 }
1554
1555 draw_update(dr, 0, 0, w, h);
1556}
1557
1558static float game_anim_length(const game_state *oldstate,
1559 const game_state *newstate, int dir, game_ui *ui)
1560{
1561 if (ui->just_moved)
1562 return 0.0F;
1563 if ((dir < 0 ? oldstate : newstate)->just_solved)
1564 ui->anim_length = SOLVEANIM_TIME;
1565 else
1566 ui->anim_length = ANIM_TIME;
1567 return ui->anim_length;
1568}
1569
1570static float game_flash_length(const game_state *oldstate,
1571 const game_state *newstate, int dir, game_ui *ui)
1572{
1573 if (!oldstate->completed && newstate->completed &&
1574 !oldstate->cheated && !newstate->cheated)
1575 return FLASH_TIME;
1576 return 0.0F;
1577}
1578
1579static int game_status(const game_state *state)
1580{
1581 return state->completed ? +1 : 0;
1582}
1583
1584static int game_timing_state(const game_state *state, game_ui *ui)
1585{
1586 return TRUE;
1587}
1588
1589static void game_print_size(const game_params *params, float *x, float *y)
1590{
1591}
1592
1593static void game_print(drawing *dr, const game_state *state, int tilesize)
1594{
1595}
1596
1597#ifdef COMBINED
1598#define thegame untangle
1599#endif
1600
1601const struct game thegame = {
1602 "Untangle", "games.untangle", "untangle",
1603 default_params,
1604 game_fetch_preset, NULL,
1605 decode_params,
1606 encode_params,
1607 free_params,
1608 dup_params,
1609 TRUE, game_configure, custom_params,
1610 validate_params,
1611 new_game_desc,
1612 validate_desc,
1613 new_game,
1614 dup_game,
1615 free_game,
1616 TRUE, solve_game,
1617 FALSE, game_can_format_as_text_now, game_text_format,
1618 new_ui,
1619 free_ui,
1620 encode_ui,
1621 decode_ui,
1622 game_changed_state,
1623 interpret_move,
1624 execute_move,
1625 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1626 game_colours,
1627 game_new_drawstate,
1628 game_free_drawstate,
1629 game_redraw,
1630 game_anim_length,
1631 game_flash_length,
1632 game_status,
1633 FALSE, FALSE, game_print_size, game_print,
1634 FALSE, /* wants_statusbar */
1635 FALSE, game_timing_state,
1636 SOLVE_ANIMATES, /* flags */
1637};
diff --git a/apps/plugins/puzzles/src/untangle.html b/apps/plugins/puzzles/src/untangle.html
new file mode 100644
index 0000000000..340c0095cf
--- /dev/null
+++ b/apps/plugins/puzzles/src/untangle.html
@@ -0,0 +1,45 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2"http://www.w3.org/TR/html4/strict.dtd">
3<html>
4<head>
5<meta http-equiv="content-type" content="text/html; charset=US-ASCII">
6<title>Untangle</title>
7<link rel="previous" href="dominosa.html">
8<link rel="ToC" href="index.html">
9<link rel="up" href="index.html">
10<link rel="index" href="docindex.html">
11<link rel="next" href="blackbox.html">
12</head>
13<body>
14<p><a href="dominosa.html">Previous</a> | <a href="index.html">Contents</a> | <a href="docindex.html">Index</a> | <a href="blackbox.html">Next</a></p>
15<h1><a name="C18"></a>Chapter 18: <a name="i0"></a>Untangle</h1>
16<p>
17You are given a number of points, some of which have lines drawn between them. You can move the points about arbitrarily; your aim is to position the points so that no line crosses another.
18</p>
19<p>
20I originally saw this in the form of a Flash game called <a name="i1"></a>Planarity <a href="#p0">[7]</a>, written by John Tantalo.
21</p>
22<p><a name="p0"></a>
23[7] <a href="http://planarity.net"><code>http://planarity.net</code></a>
24</p>
25<h2><a name="S18.1"></a>18.1 <a name="i2"></a>Untangle controls</h2>
26<p>
27To move a point, click on it with the left mouse button and drag it into a new position.
28</p>
29<p>
30(All the actions described in <a href="common.html#S2.1">section 2.1</a> are also available.)
31</p>
32<h2><a name="S18.2"></a>18.2 <a name="i3"></a>Untangle parameters</h2>
33<p>
34There is only one parameter available from the &#8216;Custom...&#8217; option on the &#8216;Type&#8217; menu:
35</p>
36<dl><dt>
37<em>Number of points</em>
38</dt>
39<dd>
40Controls the size of the puzzle, by specifying the number of points in the generated graph.
41</dd>
42</dl>
43
44<hr><address></address></body>
45</html>
diff --git a/apps/plugins/puzzles/src/version.c b/apps/plugins/puzzles/src/version.c
new file mode 100644
index 0000000000..1cef29feb7
--- /dev/null
+++ b/apps/plugins/puzzles/src/version.c
@@ -0,0 +1,7 @@
1/*
2 * Puzzles version numbering.
3 */
4
5#include "version.h"
6
7char ver[] = VER;
diff --git a/apps/plugins/puzzles/src/version.h b/apps/plugins/puzzles/src/version.h
new file mode 100644
index 0000000000..997e00592b
--- /dev/null
+++ b/apps/plugins/puzzles/src/version.h
@@ -0,0 +1,11 @@
1/*
2 * This header file provides the version #define for a particular
3 * build of Puzzles.
4 *
5 * When my automated build system does a full build, Buildscr
6 * completely overwrites this file with information appropriate to
7 * that build. The information _here_ is default stuff used for local
8 * development runs of 'make'.
9 */
10
11#define VER "Unidentified build"
diff --git a/apps/plugins/puzzles/src/wceinf.pl b/apps/plugins/puzzles/src/wceinf.pl
new file mode 100644
index 0000000000..4756f3c2b8
--- /dev/null
+++ b/apps/plugins/puzzles/src/wceinf.pl
@@ -0,0 +1,65 @@
1#!/usr/bin/perl
2
3# Perl script to generate a .INF file for building a Pocket PC .CAB
4# archive of Puzzles. This has to be scripted so that it can read
5# gamedesc.txt and automatically adjust to the current available
6# set of puzzles.
7
8# Usage:
9#
10# $ ./wceinf.pl gamedesc.txt > puzzles.inf
11
12$desc = shift @ARGV;
13open DESC, "<", $desc;
14while (<DESC>) {
15 chomp;
16 @_ = split /:/;
17 push @exes, $_[1];
18 $names{$_[1]} = $_[2];
19}
20close DESC;
21
22print '[Version]'."\n";
23print 'Signature = "$Windows NT$" ; required as-is'."\n";
24print 'Provider = "Simon Tatham" ; full app name will be "<Provider> <AppName>"'."\n";
25print 'CESignature = "$Windows CE$" ; required as-is'."\n";
26print ''."\n";
27print '[CEStrings]'."\n";
28print 'AppName = "Puzzle Collection" ; full app name will be "<Provider> <AppName>"'."\n";
29print 'InstallDir = %CE8%\%AppName% ; "\Program Files\Games\Puzzle Collection" (default install directory)'."\n";
30print ''."\n";
31print '[CEDevice.x86]'."\n";
32print 'ProcessorType = 686'."\n";
33print ''."\n";
34print '[CEDevice.ARM]'."\n";
35print 'ProcessorType = 2577'."\n";
36print ''."\n";
37print '[SourceDisksNames.x86] ; CPU-dependent files'."\n";
38print '2 = ,"x86 Files",,.'."\n";
39print ''."\n";
40print '[SourceDisksNames.ARMV4] ; CPU-dependent files'."\n";
41print '2 = ,"ARM Files",,.'."\n";
42print ''."\n";
43print '[SourceDisksFiles]'."\n";
44for $exe (@exes) {
45 print $exe.' = 2'."\n";
46}
47print ''."\n";
48print '[DefaultInstall]'."\n";
49print 'CopyFiles = PuzzleFiles'."\n";
50print 'CEShortcuts = Links'."\n";
51print ''."\n";
52print '[DestinationDirs]'."\n";
53print 'PuzzleFiles = 0, %InstallDir%'."\n";
54print 'Links = 0, %CE14%\Puzzles'."\n";
55print ''."\n";
56print ';File copy list.'."\n";
57print '[PuzzleFiles]'."\n";
58for $exe (@exes) {
59 print $exe."\n";
60}
61print ''."\n";
62print '[Links]'."\n";
63for $exe (@exes) {
64 print '"'.$names{$exe}.'",0,'.$exe."\n";
65}
diff --git a/apps/plugins/puzzles/src/webpage.pl b/apps/plugins/puzzles/src/webpage.pl
new file mode 100755
index 0000000000..3a0779ef0a
--- /dev/null
+++ b/apps/plugins/puzzles/src/webpage.pl
@@ -0,0 +1,69 @@
1#!/usr/bin/perl
2
3# Construct the two pieces of my main puzzle collection web page that
4# need to vary with the set of puzzles: the big list of <span>s with
5# puzzle pictures and links etc, and the list of Windows executable
6# files down in the downloads section.
7
8use strict;
9use warnings;
10use HTML::Entities;
11
12open my $desc, "<", "gamedesc.txt"
13 or die "gamedesc.txt: open: $!\n";
14
15open my $spans, ">", "wwwspans.html"
16 or die "wwwspans.html: open: $!\n";
17
18open my $links, ">", "wwwlinks.html"
19 or die "wwwspans.html: open: $!\n";
20
21my $n = 0;
22while (<$desc>) {
23 chomp;
24 my ($id, $win, $displayname, $description, $summary) = split /:/, $_;
25
26 printf $spans
27 '<span class="puzzle"><table>'.
28 '<tr><th align="center">%s</th></tr>'.
29 '<tr><td align="center">'.
30 '<img style="margin: 0.5em" alt="" title="%s" width=150 height=150 border=0 src="%s-web.png" />'.
31 '</td></tr>'.
32 '<tr><td align="center" style="font-size: 70%%"><code>[</code>'.
33 ' <a href="java/%s.html">java</a> '.
34 '|'.
35 ' <a href="js/%s.html">js</a> '.
36 '|'.
37 ' <a href="doc/%s.html#%s">manual</a> '.
38 '<code>]</code><br><code>[</code>'.
39 ' <a href="%s"><code>%s</code></a> '.
40 '<code>]</code></td></tr>'.
41 '<tr><td align="center">%s</td></tr></table></span>'.
42 "\n",
43 encode_entities($displayname),
44 encode_entities($description),
45 encode_entities($id),
46 encode_entities($id),
47 encode_entities($id),
48 encode_entities($id),
49 encode_entities($id),
50 encode_entities($win),
51 encode_entities($win),
52 encode_entities($summary);
53
54 if ($n > 0) {
55 if ($n % 5 == 0) {
56 print $links "<br />";
57 } else {
58 print $links " | ";
59 }
60 }
61 printf $links '<a href="%s">%s</a>',
62 encode_entities($win), encode_entities($win);
63
64 $n++;
65}
66
67close $desc;
68close $spans;
69close $links;
diff --git a/apps/plugins/puzzles/src/website.url b/apps/plugins/puzzles/src/website.url
new file mode 100644
index 0000000000..2ab37f6faf
--- /dev/null
+++ b/apps/plugins/puzzles/src/website.url
@@ -0,0 +1,2 @@
1[InternetShortcut]
2URL=http://www.chiark.greenend.org.uk/~sgtatham/puzzles/
diff --git a/apps/plugins/puzzles/src/windows.c b/apps/plugins/puzzles/src/windows.c
new file mode 100644
index 0000000000..d4b30386a6
--- /dev/null
+++ b/apps/plugins/puzzles/src/windows.c
@@ -0,0 +1,3800 @@
1/*
2 * windows.c: Windows front end for my puzzle collection.
3 */
4
5#include <windows.h>
6#include <commctrl.h>
7#ifndef NO_HTMLHELP
8#include <htmlhelp.h>
9#endif /* NO_HTMLHELP */
10
11#ifdef _WIN32_WCE
12#include <commdlg.h>
13#include <aygshell.h>
14#endif
15
16#include <stdio.h>
17#include <assert.h>
18#include <ctype.h>
19#include <stdarg.h>
20#include <stdlib.h>
21#include <limits.h>
22#include <time.h>
23
24#include "puzzles.h"
25
26#ifdef _WIN32_WCE
27#include "resource.h"
28#endif
29
30#define IDM_NEW 0x0010
31#define IDM_RESTART 0x0020
32#define IDM_UNDO 0x0030
33#define IDM_REDO 0x0040
34#define IDM_COPY 0x0050
35#define IDM_SOLVE 0x0060
36#define IDM_QUIT 0x0070
37#define IDM_CONFIG 0x0080
38#define IDM_DESC 0x0090
39#define IDM_SEED 0x00A0
40#define IDM_HELPC 0x00B0
41#define IDM_GAMEHELP 0x00C0
42#define IDM_ABOUT 0x00D0
43#define IDM_SAVE 0x00E0
44#define IDM_LOAD 0x00F0
45#define IDM_PRINT 0x0100
46#define IDM_PRESETS 0x0110
47#define IDM_GAMES 0x0300
48
49#define IDM_KEYEMUL 0x0400
50
51#define HELP_FILE_NAME "puzzles.hlp"
52#define HELP_CNT_NAME "puzzles.cnt"
53#ifndef NO_HTMLHELP
54#define CHM_FILE_NAME "puzzles.chm"
55#endif /* NO_HTMLHELP */
56
57#ifndef NO_HTMLHELP
58typedef HWND (CALLBACK *htmlhelp_t)(HWND, LPCSTR, UINT, DWORD);
59static htmlhelp_t htmlhelp;
60static HINSTANCE hh_dll;
61#endif /* NO_HTMLHELP */
62enum { NONE, HLP, CHM } help_type;
63char *help_path;
64int help_has_contents;
65
66#ifndef FILENAME_MAX
67#define FILENAME_MAX (260)
68#endif
69
70#ifndef HGDI_ERROR
71#define HGDI_ERROR ((HANDLE)GDI_ERROR)
72#endif
73
74#ifdef COMBINED
75#define CLASSNAME "Puzzles"
76#else
77#define CLASSNAME thegame.name
78#endif
79
80#ifdef _WIN32_WCE
81
82/*
83 * Wrapper implementations of functions not supplied by the
84 * PocketPC API.
85 */
86
87#define SHGetSubMenu(hWndMB,ID_MENU) (HMENU)SendMessage((hWndMB), SHCMBM_GETSUBMENU, (WPARAM)0, (LPARAM)ID_MENU)
88
89#undef MessageBox
90
91int MessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
92{
93 TCHAR wText[2048];
94 TCHAR wCaption[2048];
95
96 MultiByteToWideChar (CP_ACP, 0, lpText, -1, wText, 2048);
97 MultiByteToWideChar (CP_ACP, 0, lpCaption, -1, wCaption, 2048);
98
99 return MessageBoxW (hWnd, wText, wCaption, uType);
100}
101
102BOOL SetDlgItemTextA(HWND hDlg, int nIDDlgItem, LPCSTR lpString)
103{
104 TCHAR wText[256];
105
106 MultiByteToWideChar (CP_ACP, 0, lpString, -1, wText, 256);
107 return SetDlgItemTextW(hDlg, nIDDlgItem, wText);
108}
109
110LPCSTR getenv(LPCSTR buf)
111{
112 return NULL;
113}
114
115BOOL GetKeyboardState(PBYTE pb)
116{
117 return FALSE;
118}
119
120static TCHAR wClassName[256], wGameName[256];
121
122#endif
123
124#ifdef DEBUGGING
125static FILE *debug_fp = NULL;
126static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
127static int debug_got_console = 0;
128
129void dputs(char *buf)
130{
131 /*DWORD dw;
132
133 if (!debug_got_console) {
134 if (AllocConsole()) {
135 debug_got_console = 1;
136 debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
137 }
138 }
139 if (!debug_fp) {
140 debug_fp = fopen("debug.log", "w");
141 }
142
143 if (debug_hdl != INVALID_HANDLE_VALUE) {
144 WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
145 }
146 if (debug_fp) {
147 fputs(buf, debug_fp);
148 fflush(debug_fp);
149 }*/
150 OutputDebugString(buf);
151}
152
153void debug_printf(char *fmt, ...)
154{
155 char buf[4096];
156 va_list ap;
157 static int debugging = -1;
158
159 if (debugging == -1)
160 debugging = getenv("DEBUG_PUZZLES") ? 1 : 0;
161
162 if (debugging) {
163 va_start(ap, fmt);
164 _vsnprintf(buf, 4095, fmt, ap);
165 dputs(buf);
166 va_end(ap);
167 }
168}
169#endif
170
171#ifndef _WIN32_WCE
172#define WINFLAGS (WS_OVERLAPPEDWINDOW &~ \
173 (WS_MAXIMIZEBOX | WS_OVERLAPPED))
174#else
175#define WINFLAGS (WS_CAPTION | WS_SYSMENU)
176#endif
177
178static void new_game_size(frontend *fe, float scale);
179
180struct font {
181 HFONT font;
182 int type;
183 int size;
184};
185
186struct cfg_aux {
187 int ctlid;
188};
189
190struct blitter {
191 HBITMAP bitmap;
192 frontend *fe;
193 int x, y, w, h;
194};
195
196enum { CFG_PRINT = CFG_FRONTEND_SPECIFIC };
197
198struct preset_menuitemref {
199 HMENU which_menu;
200 int item_index;
201};
202
203struct frontend {
204 const game *game;
205 midend *me;
206 HWND hwnd, statusbar, cfgbox;
207#ifdef _WIN32_WCE
208 HWND numpad; /* window handle for the numeric pad */
209#endif
210 HINSTANCE inst;
211 HBITMAP bitmap, prevbm;
212 RECT bitmapPosition; /* game bitmap position within game window */
213 HDC hdc;
214 COLORREF *colours;
215 HBRUSH *brushes;
216 HPEN *pens;
217 HRGN clip;
218 HMENU gamemenu, typemenu;
219 UINT timer;
220 DWORD timer_last_tickcount;
221 struct preset_menu *preset_menu;
222 struct preset_menuitemref *preset_menuitems;
223 int n_preset_menuitems;
224 struct font *fonts;
225 int nfonts, fontsize;
226 config_item *cfg;
227 struct cfg_aux *cfgaux;
228 int cfg_which, dlg_done;
229 HFONT cfgfont;
230 HBRUSH oldbr;
231 HPEN oldpen;
232 int help_running;
233 enum { DRAWING, PRINTING, NOTHING } drawstatus;
234 DOCINFO di;
235 int printcount, printw, printh, printsolns, printcurr, printcolour;
236 float printscale;
237 int printoffsetx, printoffsety;
238 float printpixelscale;
239 int fontstart;
240 int linewidth, linedotted;
241 drawing *dr;
242 int xmin, ymin;
243 float puzz_scale;
244};
245
246void frontend_free(frontend *fe)
247{
248 midend_free(fe->me);
249
250 sfree(fe->colours);
251 sfree(fe->brushes);
252 sfree(fe->pens);
253 sfree(fe->fonts);
254
255 sfree(fe);
256}
257
258static void update_type_menu_tick(frontend *fe);
259static void update_copy_menu_greying(frontend *fe);
260
261void fatal(char *fmt, ...)
262{
263 char buf[2048];
264 va_list ap;
265
266 va_start(ap, fmt);
267 vsprintf(buf, fmt, ap);
268 va_end(ap);
269
270 MessageBox(NULL, buf, "Fatal error", MB_ICONEXCLAMATION | MB_OK);
271
272 exit(1);
273}
274
275char *geterrstr(void)
276{
277 LPVOID lpMsgBuf;
278 DWORD dw = GetLastError();
279 char *ret;
280
281 FormatMessage(
282 FORMAT_MESSAGE_ALLOCATE_BUFFER |
283 FORMAT_MESSAGE_FROM_SYSTEM,
284 NULL,
285 dw,
286 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
287 (LPTSTR) &lpMsgBuf,
288 0, NULL );
289
290 ret = dupstr(lpMsgBuf);
291
292 LocalFree(lpMsgBuf);
293
294 return ret;
295}
296
297void get_random_seed(void **randseed, int *randseedsize)
298{
299 SYSTEMTIME *st = snew(SYSTEMTIME);
300
301 GetLocalTime(st);
302
303 *randseed = (void *)st;
304 *randseedsize = sizeof(SYSTEMTIME);
305}
306
307static void win_status_bar(void *handle, char *text)
308{
309#ifdef _WIN32_WCE
310 TCHAR wText[255];
311#endif
312 frontend *fe = (frontend *)handle;
313
314#ifdef _WIN32_WCE
315 MultiByteToWideChar (CP_ACP, 0, text, -1, wText, 255);
316 SendMessage(fe->statusbar, SB_SETTEXT,
317 (WPARAM) 255 | SBT_NOBORDERS,
318 (LPARAM) wText);
319#else
320 SetWindowText(fe->statusbar, text);
321#endif
322}
323
324static blitter *win_blitter_new(void *handle, int w, int h)
325{
326 blitter *bl = snew(blitter);
327
328 memset(bl, 0, sizeof(blitter));
329 bl->w = w;
330 bl->h = h;
331 bl->bitmap = 0;
332
333 return bl;
334}
335
336static void win_blitter_free(void *handle, blitter *bl)
337{
338 if (bl->bitmap) DeleteObject(bl->bitmap);
339 sfree(bl);
340}
341
342static void blitter_mkbitmap(frontend *fe, blitter *bl)
343{
344 HDC hdc = GetDC(fe->hwnd);
345 bl->bitmap = CreateCompatibleBitmap(hdc, bl->w, bl->h);
346 ReleaseDC(fe->hwnd, hdc);
347}
348
349/* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */
350
351static void win_blitter_save(void *handle, blitter *bl, int x, int y)
352{
353 frontend *fe = (frontend *)handle;
354 HDC hdc_win, hdc_blit;
355 HBITMAP prev_blit;
356
357 assert(fe->drawstatus == DRAWING);
358
359 if (!bl->bitmap) blitter_mkbitmap(fe, bl);
360
361 bl->x = x; bl->y = y;
362
363 hdc_win = GetDC(fe->hwnd);
364 hdc_blit = CreateCompatibleDC(hdc_win);
365 if (!hdc_blit) fatal("hdc_blit failed: 0x%x", GetLastError());
366
367 prev_blit = SelectObject(hdc_blit, bl->bitmap);
368 if (prev_blit == NULL || prev_blit == HGDI_ERROR)
369 fatal("SelectObject for hdc_main failed: 0x%x", GetLastError());
370
371 if (!BitBlt(hdc_blit, 0, 0, bl->w, bl->h,
372 fe->hdc, x, y, SRCCOPY))
373 fatal("BitBlt failed: 0x%x", GetLastError());
374
375 SelectObject(hdc_blit, prev_blit);
376 DeleteDC(hdc_blit);
377 ReleaseDC(fe->hwnd, hdc_win);
378}
379
380static void win_blitter_load(void *handle, blitter *bl, int x, int y)
381{
382 frontend *fe = (frontend *)handle;
383 HDC hdc_win, hdc_blit;
384 HBITMAP prev_blit;
385
386 assert(fe->drawstatus == DRAWING);
387
388 assert(bl->bitmap); /* we should always have saved before loading */
389
390 if (x == BLITTER_FROMSAVED) x = bl->x;
391 if (y == BLITTER_FROMSAVED) y = bl->y;
392
393 hdc_win = GetDC(fe->hwnd);
394 hdc_blit = CreateCompatibleDC(hdc_win);
395
396 prev_blit = SelectObject(hdc_blit, bl->bitmap);
397
398 BitBlt(fe->hdc, x, y, bl->w, bl->h,
399 hdc_blit, 0, 0, SRCCOPY);
400
401 SelectObject(hdc_blit, prev_blit);
402 DeleteDC(hdc_blit);
403 ReleaseDC(fe->hwnd, hdc_win);
404}
405
406void frontend_default_colour(frontend *fe, float *output)
407{
408 DWORD c = GetSysColor(COLOR_MENU); /* ick */
409
410 output[0] = (float)(GetRValue(c) / 255.0);
411 output[1] = (float)(GetGValue(c) / 255.0);
412 output[2] = (float)(GetBValue(c) / 255.0);
413}
414
415static POINT win_transform_point(frontend *fe, int x, int y)
416{
417 POINT ret;
418
419 assert(fe->drawstatus != NOTHING);
420
421 if (fe->drawstatus == PRINTING) {
422 ret.x = (int)(fe->printoffsetx + fe->printpixelscale * x);
423 ret.y = (int)(fe->printoffsety + fe->printpixelscale * y);
424 } else {
425 ret.x = x;
426 ret.y = y;
427 }
428
429 return ret;
430}
431
432static void win_text_colour(frontend *fe, int colour)
433{
434 assert(fe->drawstatus != NOTHING);
435
436 if (fe->drawstatus == PRINTING) {
437 int hatch;
438 float r, g, b;
439 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
440
441 /*
442 * Displaying text in hatched colours is not permitted.
443 */
444 assert(hatch < 0);
445
446 SetTextColor(fe->hdc, RGB(r * 255, g * 255, b * 255));
447 } else {
448 SetTextColor(fe->hdc, fe->colours[colour]);
449 }
450}
451
452static void win_set_brush(frontend *fe, int colour)
453{
454 HBRUSH br;
455 assert(fe->drawstatus != NOTHING);
456
457 if (fe->drawstatus == PRINTING) {
458 int hatch;
459 float r, g, b;
460 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
461
462 if (hatch < 0) {
463 br = CreateSolidBrush(RGB(r * 255, g * 255, b * 255));
464 } else {
465#ifdef _WIN32_WCE
466 /*
467 * This is only ever required during printing, and the
468 * PocketPC port doesn't support printing.
469 */
470 fatal("CreateHatchBrush not supported");
471#else
472 br = CreateHatchBrush(hatch == HATCH_BACKSLASH ? HS_FDIAGONAL :
473 hatch == HATCH_SLASH ? HS_BDIAGONAL :
474 hatch == HATCH_HORIZ ? HS_HORIZONTAL :
475 hatch == HATCH_VERT ? HS_VERTICAL :
476 hatch == HATCH_PLUS ? HS_CROSS :
477 /* hatch == HATCH_X ? */ HS_DIAGCROSS,
478 RGB(0,0,0));
479#endif
480 }
481 } else {
482 br = fe->brushes[colour];
483 }
484 fe->oldbr = SelectObject(fe->hdc, br);
485}
486
487static void win_reset_brush(frontend *fe)
488{
489 HBRUSH br;
490
491 assert(fe->drawstatus != NOTHING);
492
493 br = SelectObject(fe->hdc, fe->oldbr);
494 if (fe->drawstatus == PRINTING)
495 DeleteObject(br);
496}
497
498static void win_set_pen(frontend *fe, int colour, int thin)
499{
500 HPEN pen;
501 assert(fe->drawstatus != NOTHING);
502
503 if (fe->drawstatus == PRINTING) {
504 int hatch;
505 float r, g, b;
506 int width = thin ? 0 : fe->linewidth;
507
508 if (fe->linedotted)
509 width = 0;
510
511 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
512 /*
513 * Stroking in hatched colours is not permitted.
514 */
515 assert(hatch < 0);
516 pen = CreatePen(fe->linedotted ? PS_DOT : PS_SOLID,
517 width, RGB(r * 255, g * 255, b * 255));
518 } else {
519 pen = fe->pens[colour];
520 }
521 fe->oldpen = SelectObject(fe->hdc, pen);
522}
523
524static void win_reset_pen(frontend *fe)
525{
526 HPEN pen;
527
528 assert(fe->drawstatus != NOTHING);
529
530 pen = SelectObject(fe->hdc, fe->oldpen);
531 if (fe->drawstatus == PRINTING)
532 DeleteObject(pen);
533}
534
535static void win_clip(void *handle, int x, int y, int w, int h)
536{
537 frontend *fe = (frontend *)handle;
538 POINT p, q;
539
540 if (fe->drawstatus == NOTHING)
541 return;
542
543 p = win_transform_point(fe, x, y);
544 q = win_transform_point(fe, x+w, y+h);
545 IntersectClipRect(fe->hdc, p.x, p.y, q.x, q.y);
546}
547
548static void win_unclip(void *handle)
549{
550 frontend *fe = (frontend *)handle;
551
552 if (fe->drawstatus == NOTHING)
553 return;
554
555 SelectClipRgn(fe->hdc, NULL);
556}
557
558static void win_draw_text(void *handle, int x, int y, int fonttype,
559 int fontsize, int align, int colour, char *text)
560{
561 frontend *fe = (frontend *)handle;
562 POINT xy;
563 int i;
564 LOGFONT lf;
565
566 if (fe->drawstatus == NOTHING)
567 return;
568
569 if (fe->drawstatus == PRINTING)
570 fontsize = (int)(fontsize * fe->printpixelscale);
571
572 xy = win_transform_point(fe, x, y);
573
574 /*
575 * Find or create the font.
576 */
577 for (i = fe->fontstart; i < fe->nfonts; i++)
578 if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
579 break;
580
581 if (i == fe->nfonts) {
582 if (fe->fontsize <= fe->nfonts) {
583 fe->fontsize = fe->nfonts + 10;
584 fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
585 }
586
587 fe->nfonts++;
588
589 fe->fonts[i].type = fonttype;
590 fe->fonts[i].size = fontsize;
591
592 memset (&lf, 0, sizeof(LOGFONT));
593 lf.lfHeight = -fontsize;
594 lf.lfWeight = (fe->drawstatus == PRINTING ? 0 : FW_BOLD);
595 lf.lfCharSet = DEFAULT_CHARSET;
596 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
597 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
598 lf.lfQuality = DEFAULT_QUALITY;
599 lf.lfPitchAndFamily = (fonttype == FONT_FIXED ?
600 FIXED_PITCH | FF_DONTCARE :
601 VARIABLE_PITCH | FF_SWISS);
602#ifdef _WIN32_WCE
603 wcscpy(lf.lfFaceName, TEXT("Tahoma"));
604#endif
605
606 fe->fonts[i].font = CreateFontIndirect(&lf);
607 }
608
609 /*
610 * Position and draw the text.
611 */
612 {
613 HFONT oldfont;
614 TEXTMETRIC tm;
615 SIZE size;
616 WCHAR wText[256];
617 MultiByteToWideChar (CP_UTF8, 0, text, -1, wText, 256);
618
619 oldfont = SelectObject(fe->hdc, fe->fonts[i].font);
620 if (GetTextMetrics(fe->hdc, &tm)) {
621 if (align & ALIGN_VCENTRE)
622 xy.y -= (tm.tmAscent+tm.tmDescent)/2;
623 else
624 xy.y -= tm.tmAscent;
625 }
626 if (GetTextExtentPoint32W(fe->hdc, wText, wcslen(wText), &size))
627 {
628 if (align & ALIGN_HCENTRE)
629 xy.x -= size.cx / 2;
630 else if (align & ALIGN_HRIGHT)
631 xy.x -= size.cx;
632 }
633 SetBkMode(fe->hdc, TRANSPARENT);
634 win_text_colour(fe, colour);
635 ExtTextOutW(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL);
636 SelectObject(fe->hdc, oldfont);
637 }
638}
639
640static void win_draw_rect(void *handle, int x, int y, int w, int h, int colour)
641{
642 frontend *fe = (frontend *)handle;
643 POINT p, q;
644
645 if (fe->drawstatus == NOTHING)
646 return;
647
648 if (fe->drawstatus == DRAWING && w == 1 && h == 1) {
649 /*
650 * Rectangle() appears to get uppity if asked to draw a 1x1
651 * rectangle, presumably on the grounds that that's beneath
652 * its dignity and you ought to be using SetPixel instead.
653 * So I will.
654 */
655 SetPixel(fe->hdc, x, y, fe->colours[colour]);
656 } else {
657 win_set_brush(fe, colour);
658 win_set_pen(fe, colour, TRUE);
659 p = win_transform_point(fe, x, y);
660 q = win_transform_point(fe, x+w, y+h);
661 Rectangle(fe->hdc, p.x, p.y, q.x, q.y);
662 win_reset_brush(fe);
663 win_reset_pen(fe);
664 }
665}
666
667static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
668{
669 frontend *fe = (frontend *)handle;
670 POINT pp[2];
671
672 if (fe->drawstatus == NOTHING)
673 return;
674
675 win_set_pen(fe, colour, FALSE);
676 pp[0] = win_transform_point(fe, x1, y1);
677 pp[1] = win_transform_point(fe, x2, y2);
678 Polyline(fe->hdc, pp, 2);
679 if (fe->drawstatus == DRAWING)
680 SetPixel(fe->hdc, pp[1].x, pp[1].y, fe->colours[colour]);
681 win_reset_pen(fe);
682}
683
684static void win_draw_circle(void *handle, int cx, int cy, int radius,
685 int fillcolour, int outlinecolour)
686{
687 frontend *fe = (frontend *)handle;
688 POINT p, q;
689
690 assert(outlinecolour >= 0);
691
692 if (fe->drawstatus == NOTHING)
693 return;
694
695 if (fillcolour >= 0)
696 win_set_brush(fe, fillcolour);
697 else
698 fe->oldbr = SelectObject(fe->hdc, GetStockObject(NULL_BRUSH));
699
700 win_set_pen(fe, outlinecolour, FALSE);
701 p = win_transform_point(fe, cx - radius, cy - radius);
702 q = win_transform_point(fe, cx + radius, cy + radius);
703 Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1);
704 win_reset_brush(fe);
705 win_reset_pen(fe);
706}
707
708static void win_draw_polygon(void *handle, int *coords, int npoints,
709 int fillcolour, int outlinecolour)
710{
711 frontend *fe = (frontend *)handle;
712 POINT *pts;
713 int i;
714
715 if (fe->drawstatus == NOTHING)
716 return;
717
718 pts = snewn(npoints+1, POINT);
719
720 for (i = 0; i <= npoints; i++) {
721 int j = (i < npoints ? i : 0);
722 pts[i] = win_transform_point(fe, coords[j*2], coords[j*2+1]);
723 }
724
725 assert(outlinecolour >= 0);
726
727 if (fillcolour >= 0) {
728 win_set_brush(fe, fillcolour);
729 win_set_pen(fe, outlinecolour, FALSE);
730 Polygon(fe->hdc, pts, npoints);
731 win_reset_brush(fe);
732 win_reset_pen(fe);
733 } else {
734 win_set_pen(fe, outlinecolour, FALSE);
735 Polyline(fe->hdc, pts, npoints+1);
736 win_reset_pen(fe);
737 }
738
739 sfree(pts);
740}
741
742static void win_start_draw(void *handle)
743{
744 frontend *fe = (frontend *)handle;
745 HDC hdc_win;
746
747 assert(fe->drawstatus == NOTHING);
748
749 hdc_win = GetDC(fe->hwnd);
750 fe->hdc = CreateCompatibleDC(hdc_win);
751 fe->prevbm = SelectObject(fe->hdc, fe->bitmap);
752 ReleaseDC(fe->hwnd, hdc_win);
753 fe->clip = NULL;
754#ifndef _WIN32_WCE
755 SetMapMode(fe->hdc, MM_TEXT);
756#endif
757 fe->drawstatus = DRAWING;
758}
759
760static void win_draw_update(void *handle, int x, int y, int w, int h)
761{
762 frontend *fe = (frontend *)handle;
763 RECT r;
764
765 if (fe->drawstatus != DRAWING)
766 return;
767
768 r.left = x;
769 r.top = y;
770 r.right = x + w;
771 r.bottom = y + h;
772
773 OffsetRect(&r, fe->bitmapPosition.left, fe->bitmapPosition.top);
774 InvalidateRect(fe->hwnd, &r, FALSE);
775}
776
777static void win_end_draw(void *handle)
778{
779 frontend *fe = (frontend *)handle;
780 assert(fe->drawstatus == DRAWING);
781 SelectObject(fe->hdc, fe->prevbm);
782 DeleteDC(fe->hdc);
783 if (fe->clip) {
784 DeleteObject(fe->clip);
785 fe->clip = NULL;
786 }
787 fe->drawstatus = NOTHING;
788}
789
790static void win_line_width(void *handle, float width)
791{
792 frontend *fe = (frontend *)handle;
793
794 assert(fe->drawstatus != DRAWING);
795 if (fe->drawstatus == NOTHING)
796 return;
797
798 fe->linewidth = (int)(width * fe->printpixelscale);
799}
800
801static void win_line_dotted(void *handle, int dotted)
802{
803 frontend *fe = (frontend *)handle;
804
805 assert(fe->drawstatus != DRAWING);
806 if (fe->drawstatus == NOTHING)
807 return;
808
809 fe->linedotted = dotted;
810}
811
812static void win_begin_doc(void *handle, int pages)
813{
814 frontend *fe = (frontend *)handle;
815
816 assert(fe->drawstatus != DRAWING);
817 if (fe->drawstatus == NOTHING)
818 return;
819
820 if (StartDoc(fe->hdc, &fe->di) <= 0) {
821 char *e = geterrstr();
822 MessageBox(fe->hwnd, e, "Error starting to print",
823 MB_ICONERROR | MB_OK);
824 sfree(e);
825 fe->drawstatus = NOTHING;
826 }
827
828 /*
829 * Push a marker on the font stack so that we won't use the
830 * same fonts for printing and drawing. (This is because
831 * drawing seems to look generally better in bold, but printing
832 * is better not in bold.)
833 */
834 fe->fontstart = fe->nfonts;
835}
836
837static void win_begin_page(void *handle, int number)
838{
839 frontend *fe = (frontend *)handle;
840
841 assert(fe->drawstatus != DRAWING);
842 if (fe->drawstatus == NOTHING)
843 return;
844
845 if (StartPage(fe->hdc) <= 0) {
846 char *e = geterrstr();
847 MessageBox(fe->hwnd, e, "Error starting a page",
848 MB_ICONERROR | MB_OK);
849 sfree(e);
850 fe->drawstatus = NOTHING;
851 }
852}
853
854static void win_begin_puzzle(void *handle, float xm, float xc,
855 float ym, float yc, int pw, int ph, float wmm)
856{
857 frontend *fe = (frontend *)handle;
858 int ppw, pph, pox, poy;
859 float mmpw, mmph, mmox, mmoy;
860 float scale;
861
862 assert(fe->drawstatus != DRAWING);
863 if (fe->drawstatus == NOTHING)
864 return;
865
866 ppw = GetDeviceCaps(fe->hdc, HORZRES);
867 pph = GetDeviceCaps(fe->hdc, VERTRES);
868 mmpw = (float)GetDeviceCaps(fe->hdc, HORZSIZE);
869 mmph = (float)GetDeviceCaps(fe->hdc, VERTSIZE);
870
871 /*
872 * Compute the puzzle's position on the logical page.
873 */
874 mmox = xm * mmpw + xc;
875 mmoy = ym * mmph + yc;
876
877 /*
878 * Work out what that comes to in pixels.
879 */
880 pox = (int)(mmox * (float)ppw / mmpw);
881 poy = (int)(mmoy * (float)pph / mmph);
882
883 /*
884 * And determine the scale.
885 *
886 * I need a scale such that the maximum puzzle-coordinate
887 * extent of the rectangle (pw * scale) is equal to the pixel
888 * equivalent of the puzzle's millimetre width (wmm * ppw /
889 * mmpw).
890 */
891 scale = (wmm * ppw) / (mmpw * pw);
892
893 /*
894 * Now store pox, poy and scale for use in the main drawing
895 * functions.
896 */
897 fe->printoffsetx = pox;
898 fe->printoffsety = poy;
899 fe->printpixelscale = scale;
900
901 fe->linewidth = 1;
902 fe->linedotted = FALSE;
903}
904
905static void win_end_puzzle(void *handle)
906{
907 /* Nothing needs to be done here. */
908}
909
910static void win_end_page(void *handle, int number)
911{
912 frontend *fe = (frontend *)handle;
913
914 assert(fe->drawstatus != DRAWING);
915
916 if (fe->drawstatus == NOTHING)
917 return;
918
919 if (EndPage(fe->hdc) <= 0) {
920 char *e = geterrstr();
921 MessageBox(fe->hwnd, e, "Error finishing a page",
922 MB_ICONERROR | MB_OK);
923 sfree(e);
924 fe->drawstatus = NOTHING;
925 }
926}
927
928static void win_end_doc(void *handle)
929{
930 frontend *fe = (frontend *)handle;
931
932 assert(fe->drawstatus != DRAWING);
933
934 /*
935 * Free all the fonts created since we began printing.
936 */
937 while (fe->nfonts > fe->fontstart) {
938 fe->nfonts--;
939 DeleteObject(fe->fonts[fe->nfonts].font);
940 }
941 fe->fontstart = 0;
942
943 /*
944 * The MSDN web site sample code doesn't bother to call EndDoc
945 * if an error occurs half way through printing. I expect doing
946 * so would cause the erroneous document to actually be
947 * printed, or something equally undesirable.
948 */
949 if (fe->drawstatus == NOTHING)
950 return;
951
952 if (EndDoc(fe->hdc) <= 0) {
953 char *e = geterrstr();
954 MessageBox(fe->hwnd, e, "Error finishing printing",
955 MB_ICONERROR | MB_OK);
956 sfree(e);
957 fe->drawstatus = NOTHING;
958 }
959}
960
961char *win_text_fallback(void *handle, const char *const *strings, int nstrings)
962{
963 /*
964 * We assume Windows can cope with any UTF-8 likely to be
965 * emitted by a puzzle.
966 */
967 return dupstr(strings[0]);
968}
969
970const struct drawing_api win_drawing = {
971 win_draw_text,
972 win_draw_rect,
973 win_draw_line,
974 win_draw_polygon,
975 win_draw_circle,
976 win_draw_update,
977 win_clip,
978 win_unclip,
979 win_start_draw,
980 win_end_draw,
981 win_status_bar,
982 win_blitter_new,
983 win_blitter_free,
984 win_blitter_save,
985 win_blitter_load,
986 win_begin_doc,
987 win_begin_page,
988 win_begin_puzzle,
989 win_end_puzzle,
990 win_end_page,
991 win_end_doc,
992 win_line_width,
993 win_line_dotted,
994 win_text_fallback,
995};
996
997void print(frontend *fe)
998{
999#ifndef _WIN32_WCE
1000 PRINTDLG pd;
1001 char doctitle[256];
1002 document *doc;
1003 midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */
1004 int i;
1005 char *err = NULL;
1006
1007 /*
1008 * Create our document structure and fill it up with puzzles.
1009 */
1010 doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F);
1011 for (i = 0; i < fe->printcount; i++) {
1012 if (i == 0 && fe->printcurr) {
1013 err = midend_print_puzzle(fe->me, doc, fe->printsolns);
1014 } else {
1015 if (!nme) {
1016 game_params *params;
1017
1018 nme = midend_new(NULL, fe->game, NULL, NULL);
1019
1020 /*
1021 * Set the non-interactive mid-end to have the same
1022 * parameters as the standard one.
1023 */
1024 params = midend_get_params(fe->me);
1025 midend_set_params(nme, params);
1026 fe->game->free_params(params);
1027 }
1028
1029 midend_new_game(nme);
1030 err = midend_print_puzzle(nme, doc, fe->printsolns);
1031 }
1032 if (err)
1033 break;
1034 }
1035 if (nme)
1036 midend_free(nme);
1037
1038 if (err) {
1039 MessageBox(fe->hwnd, err, "Error preparing puzzles for printing",
1040 MB_ICONERROR | MB_OK);
1041 document_free(doc);
1042 return;
1043 }
1044
1045 memset(&pd, 0, sizeof(pd));
1046 pd.lStructSize = sizeof(pd);
1047 pd.hwndOwner = fe->hwnd;
1048 pd.hDevMode = NULL;
1049 pd.hDevNames = NULL;
1050 pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC |
1051 PD_NOPAGENUMS | PD_NOSELECTION;
1052 pd.nCopies = 1;
1053 pd.nFromPage = pd.nToPage = 0xFFFF;
1054 pd.nMinPage = pd.nMaxPage = 1;
1055
1056 if (!PrintDlg(&pd)) {
1057 document_free(doc);
1058 return;
1059 }
1060
1061 /*
1062 * Now pd.hDC is a device context for the printer.
1063 */
1064
1065 /*
1066 * FIXME: IWBNI we put up an Abort box here.
1067 */
1068
1069 memset(&fe->di, 0, sizeof(fe->di));
1070 fe->di.cbSize = sizeof(fe->di);
1071 sprintf(doctitle, "Printed puzzles from %s (from Simon Tatham's"
1072 " Portable Puzzle Collection)", fe->game->name);
1073 fe->di.lpszDocName = doctitle;
1074 fe->di.lpszOutput = NULL;
1075 fe->di.lpszDatatype = NULL;
1076 fe->di.fwType = 0;
1077
1078 fe->drawstatus = PRINTING;
1079 fe->hdc = pd.hDC;
1080
1081 fe->dr = drawing_new(&win_drawing, NULL, fe);
1082 document_print(doc, fe->dr);
1083 drawing_free(fe->dr);
1084 fe->dr = NULL;
1085
1086 fe->drawstatus = NOTHING;
1087
1088 DeleteDC(pd.hDC);
1089 document_free(doc);
1090#endif
1091}
1092
1093void deactivate_timer(frontend *fe)
1094{
1095 if (!fe)
1096 return; /* for non-interactive midend */
1097 if (fe->hwnd) KillTimer(fe->hwnd, fe->timer);
1098 fe->timer = 0;
1099}
1100
1101void activate_timer(frontend *fe)
1102{
1103 if (!fe)
1104 return; /* for non-interactive midend */
1105 if (!fe->timer) {
1106 fe->timer = SetTimer(fe->hwnd, 1, 20, NULL);
1107 fe->timer_last_tickcount = GetTickCount();
1108 }
1109}
1110
1111void write_clip(HWND hwnd, char *data)
1112{
1113 HGLOBAL clipdata;
1114 int len, i, j;
1115 char *data2;
1116 void *lock;
1117
1118 /*
1119 * Windows expects CRLF in the clipboard, so we must convert
1120 * any \n that has come out of the puzzle backend.
1121 */
1122 len = 0;
1123 for (i = 0; data[i]; i++) {
1124 if (data[i] == '\n')
1125 len++;
1126 len++;
1127 }
1128 data2 = snewn(len+1, char);
1129 j = 0;
1130 for (i = 0; data[i]; i++) {
1131 if (data[i] == '\n')
1132 data2[j++] = '\r';
1133 data2[j++] = data[i];
1134 }
1135 assert(j == len);
1136 data2[j] = '\0';
1137
1138 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
1139 if (!clipdata) {
1140 sfree(data2);
1141 return;
1142 }
1143 lock = GlobalLock(clipdata);
1144 if (!lock) {
1145 GlobalFree(clipdata);
1146 sfree(data2);
1147 return;
1148 }
1149 memcpy(lock, data2, len);
1150 ((unsigned char *) lock)[len] = 0;
1151 GlobalUnlock(clipdata);
1152
1153 if (OpenClipboard(hwnd)) {
1154 EmptyClipboard();
1155 SetClipboardData(CF_TEXT, clipdata);
1156 CloseClipboard();
1157 } else
1158 GlobalFree(clipdata);
1159
1160 sfree(data2);
1161}
1162
1163/*
1164 * Set up Help and see if we can find a help file.
1165 */
1166static void init_help(void)
1167{
1168#ifndef _WIN32_WCE
1169 char b[2048], *p, *q, *r;
1170 FILE *fp;
1171
1172 /*
1173 * Find the executable file path, so we can look alongside
1174 * it for help files. Trim the filename off the end.
1175 */
1176 GetModuleFileName(NULL, b, sizeof(b) - 1);
1177 r = b;
1178 p = strrchr(b, '\\');
1179 if (p && p >= r) r = p+1;
1180 q = strrchr(b, ':');
1181 if (q && q >= r) r = q+1;
1182
1183#ifndef NO_HTMLHELP
1184 /*
1185 * Try HTML Help first.
1186 */
1187 strcpy(r, CHM_FILE_NAME);
1188 if ( (fp = fopen(b, "r")) != NULL) {
1189 fclose(fp);
1190
1191 /*
1192 * We have a .CHM. See if we can use it.
1193 */
1194 hh_dll = LoadLibrary("hhctrl.ocx");
1195 if (hh_dll) {
1196 htmlhelp = (htmlhelp_t)GetProcAddress(hh_dll, "HtmlHelpA");
1197 if (!htmlhelp)
1198 FreeLibrary(hh_dll);
1199 }
1200 if (htmlhelp) {
1201 help_path = dupstr(b);
1202 help_type = CHM;
1203 return;
1204 }
1205 }
1206#endif /* NO_HTMLHELP */
1207
1208 /*
1209 * Now try old-style .HLP.
1210 */
1211 strcpy(r, HELP_FILE_NAME);
1212 if ( (fp = fopen(b, "r")) != NULL) {
1213 fclose(fp);
1214
1215 help_path = dupstr(b);
1216 help_type = HLP;
1217
1218 /*
1219 * See if there's a .CNT file alongside it.
1220 */
1221 strcpy(r, HELP_CNT_NAME);
1222 if ( (fp = fopen(b, "r")) != NULL) {
1223 fclose(fp);
1224 help_has_contents = TRUE;
1225 } else
1226 help_has_contents = FALSE;
1227
1228 return;
1229 }
1230
1231 help_type = NONE; /* didn't find any */
1232#endif
1233}
1234
1235#ifndef _WIN32_WCE
1236
1237/*
1238 * Start Help.
1239 */
1240static void start_help(frontend *fe, const char *topic)
1241{
1242 char *str = NULL;
1243 int cmd;
1244
1245 switch (help_type) {
1246 case HLP:
1247 assert(help_path);
1248 if (topic) {
1249 str = snewn(10+strlen(topic), char);
1250 sprintf(str, "JI(`',`%s')", topic);
1251 cmd = HELP_COMMAND;
1252 } else if (help_has_contents) {
1253 cmd = HELP_FINDER;
1254 } else {
1255 cmd = HELP_CONTENTS;
1256 }
1257 WinHelp(fe->hwnd, help_path, cmd, (DWORD)str);
1258 fe->help_running = TRUE;
1259 break;
1260 case CHM:
1261#ifndef NO_HTMLHELP
1262 assert(help_path);
1263 assert(htmlhelp);
1264 if (topic) {
1265 str = snewn(20 + strlen(topic) + strlen(help_path), char);
1266 sprintf(str, "%s::/%s.html>main", help_path, topic);
1267 } else {
1268 str = dupstr(help_path);
1269 }
1270 htmlhelp(fe->hwnd, str, HH_DISPLAY_TOPIC, 0);
1271 fe->help_running = TRUE;
1272 break;
1273#endif /* NO_HTMLHELP */
1274 case NONE:
1275 assert(!"This shouldn't happen");
1276 break;
1277 }
1278
1279 sfree(str);
1280}
1281
1282/*
1283 * Stop Help on window cleanup.
1284 */
1285static void stop_help(frontend *fe)
1286{
1287 if (fe->help_running) {
1288 switch (help_type) {
1289 case HLP:
1290 WinHelp(fe->hwnd, help_path, HELP_QUIT, 0);
1291 break;
1292 case CHM:
1293#ifndef NO_HTMLHELP
1294 assert(htmlhelp);
1295 htmlhelp(NULL, NULL, HH_CLOSE_ALL, 0);
1296 break;
1297#endif /* NO_HTMLHELP */
1298 case NONE:
1299 assert(!"This shouldn't happen");
1300 break;
1301 }
1302 fe->help_running = FALSE;
1303 }
1304}
1305
1306#endif
1307
1308/*
1309 * Terminate Help on process exit.
1310 */
1311static void cleanup_help(void)
1312{
1313 /* Nothing to do currently.
1314 * (If we were running HTML Help single-threaded, this is where we'd
1315 * call HH_UNINITIALIZE.) */
1316}
1317
1318static int get_statusbar_height(frontend *fe)
1319{
1320 int sy;
1321 if (fe->statusbar) {
1322 RECT sr;
1323 GetWindowRect(fe->statusbar, &sr);
1324 sy = sr.bottom - sr.top;
1325 } else {
1326 sy = 0;
1327 }
1328 return sy;
1329}
1330
1331static void adjust_statusbar(frontend *fe, RECT *r)
1332{
1333 int sy;
1334
1335 if (!fe->statusbar) return;
1336
1337 sy = get_statusbar_height(fe);
1338#ifndef _WIN32_WCE
1339 SetWindowPos(fe->statusbar, NULL, 0, r->bottom-r->top-sy, r->right-r->left,
1340 sy, SWP_NOZORDER);
1341#endif
1342}
1343
1344static void get_menu_size(HWND wh, RECT *r)
1345{
1346 HMENU bar = GetMenu(wh);
1347 RECT rect;
1348 int i;
1349
1350 SetRect(r, 0, 0, 0, 0);
1351 for (i = 0; i < GetMenuItemCount(bar); i++) {
1352 GetMenuItemRect(wh, bar, i, &rect);
1353 UnionRect(r, r, &rect);
1354 }
1355}
1356
1357/*
1358 * Given a proposed new puzzle size (cx,cy), work out the actual
1359 * puzzle size that would be (px,py) and the window size including
1360 * furniture (wx,wy).
1361 */
1362
1363static int check_window_resize(frontend *fe, int cx, int cy,
1364 int *px, int *py,
1365 int *wx, int *wy)
1366{
1367 RECT r;
1368 int x, y, sy = get_statusbar_height(fe), changed = 0;
1369
1370 /* disallow making window thinner than menu bar */
1371 x = max(cx, fe->xmin);
1372 y = max(cy - sy, fe->ymin);
1373
1374 /*
1375 * See if we actually got the window size we wanted, and adjust
1376 * the puzzle size if not.
1377 */
1378 midend_size(fe->me, &x, &y, TRUE);
1379 if (x != cx || y != cy) {
1380 /*
1381 * Resize the window, now we know what size we _really_
1382 * want it to be.
1383 */
1384 r.left = r.top = 0;
1385 r.right = x;
1386 r.bottom = y + sy;
1387 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1388 *wx = r.right - r.left;
1389 *wy = r.bottom - r.top;
1390 changed = 1;
1391 }
1392
1393 *px = x;
1394 *py = y;
1395
1396 fe->puzz_scale =
1397 (float)midend_tilesize(fe->me) / (float)fe->game->preferred_tilesize;
1398
1399 return changed;
1400}
1401
1402/*
1403 * Given the current window size, make sure it's sane for the
1404 * current puzzle and resize if necessary.
1405 */
1406
1407static void check_window_size(frontend *fe, int *px, int *py)
1408{
1409 RECT r;
1410 int wx, wy, cx, cy;
1411
1412 GetClientRect(fe->hwnd, &r);
1413 cx = r.right - r.left;
1414 cy = r.bottom - r.top;
1415
1416 if (check_window_resize(fe, cx, cy, px, py, &wx, &wy)) {
1417#ifdef _WIN32_WCE
1418 SetWindowPos(fe->hwnd, NULL, 0, 0, wx, wy,
1419 SWP_NOMOVE | SWP_NOZORDER);
1420#endif
1421 ;
1422 }
1423
1424 GetClientRect(fe->hwnd, &r);
1425 adjust_statusbar(fe, &r);
1426}
1427
1428static void get_max_puzzle_size(frontend *fe, int *x, int *y)
1429{
1430 RECT r, sr;
1431
1432 if (SystemParametersInfo(SPI_GETWORKAREA, 0, &sr, FALSE)) {
1433 *x = sr.right - sr.left;
1434 *y = sr.bottom - sr.top;
1435 r.left = 100;
1436 r.right = 200;
1437 r.top = 100;
1438 r.bottom = 200;
1439 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1440 *x -= r.right - r.left - 100;
1441 *y -= r.bottom - r.top - 100;
1442 } else {
1443 *x = *y = INT_MAX;
1444 }
1445
1446 if (fe->statusbar != NULL) {
1447 GetWindowRect(fe->statusbar, &sr);
1448 *y -= sr.bottom - sr.top;
1449 }
1450}
1451
1452#ifdef _WIN32_WCE
1453/* Toolbar buttons on the numeric pad */
1454static TBBUTTON tbNumpadButtons[] =
1455{
1456 {0, IDM_KEYEMUL + '1', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1457 {1, IDM_KEYEMUL + '2', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1458 {2, IDM_KEYEMUL + '3', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1459 {3, IDM_KEYEMUL + '4', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1460 {4, IDM_KEYEMUL + '5', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1461 {5, IDM_KEYEMUL + '6', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1462 {6, IDM_KEYEMUL + '7', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1463 {7, IDM_KEYEMUL + '8', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1464 {8, IDM_KEYEMUL + '9', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1465 {9, IDM_KEYEMUL + ' ', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}
1466};
1467#endif
1468
1469/*
1470 * Allocate a new frontend structure and create its main window.
1471 */
1472static frontend *frontend_new(HINSTANCE inst)
1473{
1474 frontend *fe;
1475 const char *nogame = "Puzzles (no game selected)";
1476
1477 fe = snew(frontend);
1478
1479 fe->inst = inst;
1480
1481 fe->game = NULL;
1482 fe->me = NULL;
1483
1484 fe->timer = 0;
1485 fe->hwnd = NULL;
1486
1487 fe->help_running = FALSE;
1488
1489 fe->drawstatus = NOTHING;
1490 fe->dr = NULL;
1491 fe->fontstart = 0;
1492
1493 fe->fonts = NULL;
1494 fe->nfonts = fe->fontsize = 0;
1495
1496 fe->colours = NULL;
1497 fe->brushes = NULL;
1498 fe->pens = NULL;
1499
1500 fe->puzz_scale = 1.0;
1501
1502 #ifdef _WIN32_WCE
1503 MultiByteToWideChar (CP_ACP, 0, nogame, -1, wGameName, 256);
1504 fe->hwnd = CreateWindowEx(0, wClassName, wGameName,
1505 WS_VISIBLE,
1506 CW_USEDEFAULT, CW_USEDEFAULT,
1507 CW_USEDEFAULT, CW_USEDEFAULT,
1508 NULL, NULL, inst, NULL);
1509
1510 {
1511 SHMENUBARINFO mbi;
1512 RECT rc, rcBar, rcTB, rcClient;
1513
1514 memset (&mbi, 0, sizeof(SHMENUBARINFO));
1515 mbi.cbSize = sizeof(SHMENUBARINFO);
1516 mbi.hwndParent = fe->hwnd;
1517 mbi.nToolBarId = IDR_MENUBAR1;
1518 mbi.hInstRes = inst;
1519
1520 SHCreateMenuBar(&mbi);
1521
1522 GetWindowRect(fe->hwnd, &rc);
1523 GetWindowRect(mbi.hwndMB, &rcBar);
1524 rc.bottom -= rcBar.bottom - rcBar.top;
1525 MoveWindow(fe->hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
1526
1527 fe->numpad = NULL;
1528 }
1529#else
1530 fe->hwnd = CreateWindowEx(0, CLASSNAME, nogame,
1531 WS_OVERLAPPEDWINDOW &~
1532 (WS_MAXIMIZEBOX),
1533 CW_USEDEFAULT, CW_USEDEFAULT,
1534 CW_USEDEFAULT, CW_USEDEFAULT,
1535 NULL, NULL, inst, NULL);
1536 if (!fe->hwnd) {
1537 DWORD lerr = GetLastError();
1538 printf("no window: 0x%x\n", (unsigned)lerr);
1539 }
1540#endif
1541
1542 fe->gamemenu = NULL;
1543 fe->preset_menu = NULL;
1544
1545 fe->statusbar = NULL;
1546 fe->bitmap = NULL;
1547
1548 SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe);
1549
1550 return fe;
1551}
1552
1553static void savefile_write(void *wctx, void *buf, int len)
1554{
1555 FILE *fp = (FILE *)wctx;
1556 fwrite(buf, 1, len, fp);
1557}
1558
1559static int savefile_read(void *wctx, void *buf, int len)
1560{
1561 FILE *fp = (FILE *)wctx;
1562 int ret;
1563
1564 ret = fread(buf, 1, len, fp);
1565 return (ret == len);
1566}
1567
1568/*
1569 * Create an appropriate midend structure to go in a puzzle window,
1570 * given a game type and/or a command-line argument.
1571 *
1572 * 'arg' can be either a game ID string (descriptive, random, or a
1573 * plain set of parameters) or the filename of a save file. The two
1574 * boolean flag arguments indicate which possibilities are
1575 * permissible.
1576 */
1577static midend *midend_for_new_game(frontend *fe, const game *cgame,
1578 char *arg, int maybe_game_id,
1579 int maybe_save_file, char **error)
1580{
1581 midend *me = NULL;
1582
1583 if (!arg) {
1584 if (me) midend_free(me);
1585 me = midend_new(fe, cgame, &win_drawing, fe);
1586 midend_new_game(me);
1587 } else {
1588 FILE *fp;
1589 char *err_param, *err_load;
1590
1591 /*
1592 * See if arg is a valid filename of a save game file.
1593 */
1594 err_load = NULL;
1595 if (maybe_save_file && (fp = fopen(arg, "r")) != NULL) {
1596 const game *loadgame;
1597
1598#ifdef COMBINED
1599 /*
1600 * Find out what kind of game is stored in the save
1601 * file; if we're going to end up loading that, it
1602 * will have to override our caller's judgment as to
1603 * what game to initialise our midend with.
1604 */
1605 char *id_name;
1606 err_load = identify_game(&id_name, savefile_read, fp);
1607 if (!err_load) {
1608 int i;
1609 for (i = 0; i < gamecount; i++)
1610 if (!strcmp(id_name, gamelist[i]->name))
1611 break;
1612 if (i == gamecount) {
1613 err_load = "Save file is for a game not supported by"
1614 " this program";
1615 } else {
1616 loadgame = gamelist[i];
1617 rewind(fp); /* go back to the start for actual load */
1618 }
1619 }
1620#else
1621 loadgame = cgame;
1622#endif
1623 if (!err_load) {
1624 if (me) midend_free(me);
1625 me = midend_new(fe, loadgame, &win_drawing, fe);
1626 err_load = midend_deserialise(me, savefile_read, fp);
1627 }
1628 } else {
1629 err_load = "Unable to open file";
1630 }
1631
1632 if (maybe_game_id && (!maybe_save_file || err_load)) {
1633 /*
1634 * See if arg is a game description.
1635 */
1636 if (me) midend_free(me);
1637 me = midend_new(fe, cgame, &win_drawing, fe);
1638 err_param = midend_game_id(me, arg);
1639 if (!err_param) {
1640 midend_new_game(me);
1641 } else {
1642 if (maybe_save_file) {
1643 *error = snewn(256 + strlen(arg) + strlen(err_param) +
1644 strlen(err_load), char);
1645 sprintf(*error, "Supplied argument \"%s\" is neither a"
1646 " game ID (%s) nor a save file (%s)",
1647 arg, err_param, err_load);
1648 } else {
1649 *error = dupstr(err_param);
1650 }
1651 midend_free(me);
1652 sfree(fe);
1653 return NULL;
1654 }
1655 } else if (err_load) {
1656 *error = dupstr(err_load);
1657 midend_free(me);
1658 sfree(fe);
1659 return NULL;
1660 }
1661 }
1662
1663 return me;
1664}
1665
1666static void populate_preset_menu(frontend *fe,
1667 struct preset_menu *menu, HMENU winmenu)
1668{
1669 int i;
1670 for (i = 0; i < menu->n_entries; i++) {
1671 struct preset_menu_entry *entry = &menu->entries[i];
1672 UINT_PTR id_or_sub;
1673 UINT flags = MF_ENABLED;
1674
1675 if (entry->params) {
1676 id_or_sub = (UINT_PTR)(IDM_PRESETS + 0x10 * entry->id);
1677
1678 fe->preset_menuitems[entry->id].which_menu = winmenu;
1679 fe->preset_menuitems[entry->id].item_index =
1680 GetMenuItemCount(winmenu);
1681 } else {
1682 HMENU winsubmenu = CreateMenu();
1683 id_or_sub = (UINT_PTR)winsubmenu;
1684 flags |= MF_POPUP;
1685
1686 populate_preset_menu(fe, entry->submenu, winsubmenu);
1687 }
1688
1689 /*
1690 * FIXME: we ought to go through and do something with ampersands
1691 * here.
1692 */
1693
1694#ifndef _WIN32_WCE
1695 AppendMenu(winmenu, flags, id_or_sub, entry->title);
1696#else
1697 {
1698 TCHAR wName[255];
1699 MultiByteToWideChar(CP_ACP, 0, entry->title, -1, wName, 255);
1700 AppendMenu(winmenu, flags, id_or_sub, wName);
1701 }
1702#endif
1703 }
1704}
1705
1706/*
1707 * Populate a frontend structure with a new midend structure, and
1708 * create any window furniture that it needs.
1709 *
1710 * Previously-allocated memory and window furniture will be freed by
1711 * this function.
1712 *
1713 */
1714static int fe_set_midend(frontend *fe, midend *me)
1715{
1716 int x, y;
1717 RECT r;
1718
1719 if (fe->me) midend_free(fe->me);
1720 fe->me = me;
1721 fe->game = midend_which_game(fe->me);
1722
1723 {
1724 int i, ncolours;
1725 float *colours;
1726
1727 colours = midend_colours(fe->me, &ncolours);
1728
1729 if (fe->colours) sfree(fe->colours);
1730 if (fe->brushes) sfree(fe->brushes);
1731 if (fe->pens) sfree(fe->pens);
1732
1733 fe->colours = snewn(ncolours, COLORREF);
1734 fe->brushes = snewn(ncolours, HBRUSH);
1735 fe->pens = snewn(ncolours, HPEN);
1736
1737 for (i = 0; i < ncolours; i++) {
1738 fe->colours[i] = RGB(255 * colours[i*3+0],
1739 255 * colours[i*3+1],
1740 255 * colours[i*3+2]);
1741 fe->brushes[i] = CreateSolidBrush(fe->colours[i]);
1742 fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]);
1743 }
1744 sfree(colours);
1745 }
1746
1747 if (fe->statusbar)
1748 DestroyWindow(fe->statusbar);
1749 if (midend_wants_statusbar(fe->me)) {
1750 fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME,
1751 TEXT(DEFAULT_STATUSBAR_TEXT),
1752 WS_CHILD | WS_VISIBLE,
1753 0, 0, 0, 0, /* status bar does these */
1754 NULL, NULL, fe->inst, NULL);
1755 } else
1756 fe->statusbar = NULL;
1757
1758 get_max_puzzle_size(fe, &x, &y);
1759 midend_size(fe->me, &x, &y, FALSE);
1760
1761 r.left = r.top = 0;
1762 r.right = x;
1763 r.bottom = y;
1764 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1765
1766#ifdef _WIN32_WCE
1767 if (fe->numpad)
1768 DestroyWindow(fe->numpad);
1769 if (fe->game->flags & REQUIRE_NUMPAD)
1770 {
1771 fe->numpad = CreateToolbarEx (fe->hwnd,
1772 WS_VISIBLE | WS_CHILD | CCS_NOPARENTALIGN | TBSTYLE_FLAT,
1773 0, 10, fe->inst, IDR_PADTOOLBAR,
1774 tbNumpadButtons, sizeof (tbNumpadButtons) / sizeof (TBBUTTON),
1775 0, 0, 14, 15, sizeof (TBBUTTON));
1776 GetWindowRect(fe->numpad, &rcTB);
1777 GetClientRect(fe->hwnd, &rcClient);
1778 MoveWindow(fe->numpad,
1779 0,
1780 rcClient.bottom - (rcTB.bottom - rcTB.top) - 1,
1781 rcClient.right,
1782 rcTB.bottom - rcTB.top,
1783 FALSE);
1784 SendMessage(fe->numpad, TB_SETINDENT, (rcClient.right - (10 * 21)) / 2, 0);
1785 }
1786 else {
1787 fe->numpad = NULL;
1788 }
1789 MultiByteToWideChar (CP_ACP, 0, fe->game->name, -1, wGameName, 256);
1790 SetWindowText(fe->hwnd, wGameName);
1791#else
1792 SetWindowText(fe->hwnd, fe->game->name);
1793#endif
1794
1795 if (fe->statusbar)
1796 DestroyWindow(fe->statusbar);
1797 if (midend_wants_statusbar(fe->me)) {
1798 RECT sr;
1799 fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"),
1800 WS_CHILD | WS_VISIBLE,
1801 0, 0, 0, 0, /* status bar does these */
1802 fe->hwnd, NULL, fe->inst, NULL);
1803#ifdef _WIN32_WCE
1804 /* Flat status bar looks better on the Pocket PC */
1805 SendMessage(fe->statusbar, SB_SIMPLE, (WPARAM) TRUE, 0);
1806 SendMessage(fe->statusbar, SB_SETTEXT,
1807 (WPARAM) 255 | SBT_NOBORDERS,
1808 (LPARAM) L"");
1809#endif
1810
1811 /*
1812 * Now resize the window to take account of the status bar.
1813 */
1814 GetWindowRect(fe->statusbar, &sr);
1815 GetWindowRect(fe->hwnd, &r);
1816#ifndef _WIN32_WCE
1817 SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left,
1818 r.bottom - r.top + sr.bottom - sr.top,
1819 SWP_NOMOVE | SWP_NOZORDER);
1820#endif
1821 } else {
1822 fe->statusbar = NULL;
1823 }
1824
1825 {
1826 HMENU oldmenu = GetMenu(fe->hwnd);
1827
1828#ifndef _WIN32_WCE
1829 HMENU bar = CreateMenu();
1830 HMENU menu = CreateMenu();
1831 RECT menusize;
1832
1833 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "&Game");
1834#else
1835 HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME);
1836 DeleteMenu(menu, 0, MF_BYPOSITION);
1837#endif
1838 fe->gamemenu = menu;
1839 AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("&New"));
1840 AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("&Restart"));
1841#ifndef _WIN32_WCE
1842 /* ...here I run out of sensible accelerator characters. */
1843 AppendMenu(menu, MF_ENABLED, IDM_DESC, TEXT("Speci&fic..."));
1844 AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Rando&m Seed..."));
1845#endif
1846
1847 if (!fe->preset_menu) {
1848 int i;
1849 fe->preset_menu = midend_get_presets(
1850 fe->me, &fe->n_preset_menuitems);
1851 fe->preset_menuitems = snewn(fe->n_preset_menuitems,
1852 struct preset_menuitemref);
1853 for (i = 0; i < fe->n_preset_menuitems; i++)
1854 fe->preset_menuitems[i].which_menu = NULL;
1855 }
1856 if (fe->preset_menu->n_entries > 0 || fe->game->can_configure) {
1857#ifndef _WIN32_WCE
1858 HMENU sub = CreateMenu();
1859
1860 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "&Type");
1861#else
1862 HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE);
1863 DeleteMenu(sub, 0, MF_BYPOSITION);
1864#endif
1865
1866 populate_preset_menu(fe, fe->preset_menu, sub);
1867
1868 if (fe->game->can_configure) {
1869 AppendMenu(sub, MF_ENABLED, IDM_CONFIG, TEXT("&Custom..."));
1870 }
1871
1872 fe->typemenu = sub;
1873 } else {
1874 fe->typemenu = INVALID_HANDLE_VALUE;
1875 }
1876
1877#ifdef COMBINED
1878#ifdef _WIN32_WCE
1879#error Windows CE does not support COMBINED build.
1880#endif
1881 {
1882 HMENU games = CreateMenu();
1883 int i;
1884
1885 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1886 AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)games, "&Other");
1887 for (i = 0; i < gamecount; i++) {
1888 if (strcmp(gamelist[i]->name, fe->game->name) != 0) {
1889 /* only include those games that aren't the same as the
1890 * game we're currently playing. */
1891 AppendMenu(games, MF_ENABLED, IDM_GAMES + i, gamelist[i]->name);
1892 }
1893 }
1894 }
1895#endif
1896
1897 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1898#ifndef _WIN32_WCE
1899 AppendMenu(menu, MF_ENABLED, IDM_LOAD, TEXT("&Load..."));
1900 AppendMenu(menu, MF_ENABLED, IDM_SAVE, TEXT("&Save..."));
1901 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1902 if (fe->game->can_print) {
1903 AppendMenu(menu, MF_ENABLED, IDM_PRINT, TEXT("&Print..."));
1904 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1905 }
1906#endif
1907 AppendMenu(menu, MF_ENABLED, IDM_UNDO, TEXT("Undo"));
1908 AppendMenu(menu, MF_ENABLED, IDM_REDO, TEXT("Redo"));
1909#ifndef _WIN32_WCE
1910 if (fe->game->can_format_as_text_ever) {
1911 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1912 AppendMenu(menu, MF_ENABLED, IDM_COPY, TEXT("&Copy"));
1913 }
1914#endif
1915 if (fe->game->can_solve) {
1916 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1917 AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Sol&ve"));
1918 }
1919 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1920#ifndef _WIN32_WCE
1921 AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("E&xit"));
1922 menu = CreateMenu();
1923 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, TEXT("&Help"));
1924#endif
1925 AppendMenu(menu, MF_ENABLED, IDM_ABOUT, TEXT("&About"));
1926#ifndef _WIN32_WCE
1927 if (help_type != NONE) {
1928 char *item;
1929 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1930 AppendMenu(menu, MF_ENABLED, IDM_HELPC, TEXT("&Contents"));
1931 assert(fe->game->name);
1932 item = snewn(10+strlen(fe->game->name), char); /*ick*/
1933 sprintf(item, "&Help on %s", fe->game->name);
1934 AppendMenu(menu, MF_ENABLED, IDM_GAMEHELP, item);
1935 sfree(item);
1936 }
1937 DestroyMenu(oldmenu);
1938 SetMenu(fe->hwnd, bar);
1939 get_menu_size(fe->hwnd, &menusize);
1940 fe->xmin = (menusize.right - menusize.left) + 25;
1941#endif
1942 }
1943
1944 if (fe->bitmap) DeleteObject(fe->bitmap);
1945 fe->bitmap = NULL;
1946 new_game_size(fe, fe->puzz_scale); /* initialises fe->bitmap */
1947
1948 return 0;
1949}
1950
1951static void show_window(frontend *fe)
1952{
1953 ShowWindow(fe->hwnd, SW_SHOWNORMAL);
1954 SetForegroundWindow(fe->hwnd);
1955
1956 update_type_menu_tick(fe);
1957 update_copy_menu_greying(fe);
1958
1959 midend_redraw(fe->me);
1960}
1961
1962#ifdef _WIN32_WCE
1963static HFONT dialog_title_font()
1964{
1965 static HFONT hf = NULL;
1966 LOGFONT lf;
1967
1968 if (hf)
1969 return hf;
1970
1971 memset (&lf, 0, sizeof(LOGFONT));
1972 lf.lfHeight = -11; /* - ((8 * GetDeviceCaps(hdc, LOGPIXELSY)) / 72) */
1973 lf.lfWeight = FW_BOLD;
1974 wcscpy(lf.lfFaceName, TEXT("Tahoma"));
1975
1976 return hf = CreateFontIndirect(&lf);
1977}
1978
1979static void make_dialog_full_screen(HWND hwnd)
1980{
1981 SHINITDLGINFO shidi;
1982
1983 /* Make dialog full screen */
1984 shidi.dwMask = SHIDIM_FLAGS;
1985 shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN |
1986 SHIDIF_EMPTYMENU;
1987 shidi.hDlg = hwnd;
1988 SHInitDialog(&shidi);
1989}
1990#endif
1991
1992static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg,
1993 WPARAM wParam, LPARAM lParam)
1994{
1995 frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
1996
1997 switch (msg) {
1998 case WM_INITDIALOG:
1999#ifdef _WIN32_WCE
2000 {
2001 char title[256];
2002
2003 make_dialog_full_screen(hwnd);
2004
2005 sprintf(title, "About %.250s", fe->game->name);
2006 SetDlgItemTextA(hwnd, IDC_ABOUT_CAPTION, title);
2007
2008 SendDlgItemMessage(hwnd, IDC_ABOUT_CAPTION, WM_SETFONT,
2009 (WPARAM) dialog_title_font(), 0);
2010
2011 SetDlgItemTextA(hwnd, IDC_ABOUT_GAME, fe->game->name);
2012 SetDlgItemTextA(hwnd, IDC_ABOUT_VERSION, ver);
2013 }
2014#endif
2015 return TRUE;
2016
2017 case WM_COMMAND:
2018 if (LOWORD(wParam) == IDOK)
2019#ifdef _WIN32_WCE
2020 EndDialog(hwnd, 1);
2021#else
2022 fe->dlg_done = 1;
2023#endif
2024 return 0;
2025
2026 case WM_CLOSE:
2027#ifdef _WIN32_WCE
2028 EndDialog(hwnd, 1);
2029#else
2030 fe->dlg_done = 1;
2031#endif
2032 return 0;
2033 }
2034
2035 return 0;
2036}
2037
2038/*
2039 * Wrappers on midend_{get,set}_config, which extend the CFG_*
2040 * enumeration to add CFG_PRINT.
2041 */
2042static config_item *frontend_get_config(frontend *fe, int which,
2043 char **wintitle)
2044{
2045 if (which < CFG_FRONTEND_SPECIFIC) {
2046 return midend_get_config(fe->me, which, wintitle);
2047 } else if (which == CFG_PRINT) {
2048 config_item *ret;
2049 int i;
2050
2051 *wintitle = snewn(40 + strlen(fe->game->name), char);
2052 sprintf(*wintitle, "%s print setup", fe->game->name);
2053
2054 ret = snewn(8, config_item);
2055
2056 i = 0;
2057
2058 ret[i].name = "Number of puzzles to print";
2059 ret[i].type = C_STRING;
2060 ret[i].sval = dupstr("1");
2061 ret[i].ival = 0;
2062 i++;
2063
2064 ret[i].name = "Number of puzzles across the page";
2065 ret[i].type = C_STRING;
2066 ret[i].sval = dupstr("1");
2067 ret[i].ival = 0;
2068 i++;
2069
2070 ret[i].name = "Number of puzzles down the page";
2071 ret[i].type = C_STRING;
2072 ret[i].sval = dupstr("1");
2073 ret[i].ival = 0;
2074 i++;
2075
2076 ret[i].name = "Percentage of standard size";
2077 ret[i].type = C_STRING;
2078 ret[i].sval = dupstr("100.0");
2079 ret[i].ival = 0;
2080 i++;
2081
2082 ret[i].name = "Include currently shown puzzle";
2083 ret[i].type = C_BOOLEAN;
2084 ret[i].sval = NULL;
2085 ret[i].ival = TRUE;
2086 i++;
2087
2088 ret[i].name = "Print solutions";
2089 ret[i].type = C_BOOLEAN;
2090 ret[i].sval = NULL;
2091 ret[i].ival = FALSE;
2092 i++;
2093
2094 if (fe->game->can_print_in_colour) {
2095 ret[i].name = "Print in colour";
2096 ret[i].type = C_BOOLEAN;
2097 ret[i].sval = NULL;
2098 ret[i].ival = FALSE;
2099 i++;
2100 }
2101
2102 ret[i].name = NULL;
2103 ret[i].type = C_END;
2104 ret[i].sval = NULL;
2105 ret[i].ival = 0;
2106 i++;
2107
2108 return ret;
2109 } else {
2110 assert(!"We should never get here");
2111 return NULL;
2112 }
2113}
2114
2115static char *frontend_set_config(frontend *fe, int which, config_item *cfg)
2116{
2117 if (which < CFG_FRONTEND_SPECIFIC) {
2118 return midend_set_config(fe->me, which, cfg);
2119 } else if (which == CFG_PRINT) {
2120 if ((fe->printcount = atoi(cfg[0].sval)) <= 0)
2121 return "Number of puzzles to print should be at least one";
2122 if ((fe->printw = atoi(cfg[1].sval)) <= 0)
2123 return "Number of puzzles across the page should be at least one";
2124 if ((fe->printh = atoi(cfg[2].sval)) <= 0)
2125 return "Number of puzzles down the page should be at least one";
2126 if ((fe->printscale = (float)atof(cfg[3].sval)) <= 0)
2127 return "Print size should be positive";
2128 fe->printcurr = cfg[4].ival;
2129 fe->printsolns = cfg[5].ival;
2130 fe->printcolour = fe->game->can_print_in_colour && cfg[6].ival;
2131 return NULL;
2132 } else {
2133 assert(!"We should never get here");
2134 return "Internal error";
2135 }
2136}
2137
2138#ifdef _WIN32_WCE
2139/* Separate version of mkctrl function for the Pocket PC. */
2140/* Control coordinates should be specified in dialog units. */
2141HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
2142 LPCTSTR wclass, int wstyle,
2143 int exstyle, const char *wtext, int wid)
2144{
2145 RECT rc;
2146 TCHAR wwtext[256];
2147
2148 /* Convert dialog units into pixels */
2149 rc.left = x1; rc.right = x2;
2150 rc.top = y1; rc.bottom = y2;
2151 MapDialogRect(fe->cfgbox, &rc);
2152
2153 MultiByteToWideChar (CP_ACP, 0, wtext, -1, wwtext, 256);
2154
2155 return CreateWindowEx(exstyle, wclass, wwtext,
2156 wstyle | WS_CHILD | WS_VISIBLE,
2157 rc.left, rc.top,
2158 rc.right - rc.left, rc.bottom - rc.top,
2159 fe->cfgbox, (HMENU) wid, fe->inst, NULL);
2160}
2161
2162static void create_config_controls(frontend * fe)
2163{
2164 int id, nctrls;
2165 int col1l, col1r, col2l, col2r, y;
2166 config_item *i;
2167 struct cfg_aux *j;
2168 HWND ctl;
2169
2170 /* Control placement done in dialog units */
2171 col1l = 4; col1r = 96; /* Label column */
2172 col2l = 100; col2r = 154; /* Input column (edit boxes and combo boxes) */
2173
2174 /*
2175 * Count the controls so we can allocate cfgaux.
2176 */
2177 for (nctrls = 0, i = fe->cfg; i->type != C_END; i++)
2178 nctrls++;
2179 fe->cfgaux = snewn(nctrls, struct cfg_aux);
2180
2181 id = 1000;
2182 y = 22; /* Leave some room for the dialog title */
2183 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2184 switch (i->type) {
2185 case C_STRING:
2186 /*
2187 * Edit box with a label beside it.
2188 */
2189 mkctrl(fe, col1l, col1r, y + 1, y + 11,
2190 TEXT("Static"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
2191 mkctrl(fe, col2l, col2r, y, y + 12,
2192 TEXT("EDIT"), WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,
2193 0, "", (j->ctlid = id++));
2194 SetDlgItemTextA(fe->cfgbox, j->ctlid, i->sval);
2195 break;
2196
2197 case C_BOOLEAN:
2198 /*
2199 * Simple checkbox.
2200 */
2201 mkctrl(fe, col1l, col2r, y + 1, y + 11, TEXT("BUTTON"),
2202 BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
2203 0, i->name, (j->ctlid = id++));
2204 CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0));
2205 break;
2206
2207 case C_CHOICES:
2208 /*
2209 * Drop-down list with a label beside it.
2210 */
2211 mkctrl(fe, col1l, col1r, y + 1, y + 11,
2212 TEXT("STATIC"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
2213 ctl = mkctrl(fe, col2l, col2r, y, y + 48,
2214 TEXT("COMBOBOX"), WS_BORDER | WS_TABSTOP |
2215 CBS_DROPDOWNLIST | CBS_HASSTRINGS,
2216 0, "", (j->ctlid = id++));
2217 {
2218 char c, *p, *q, *str;
2219
2220 p = i->sval;
2221 c = *p++;
2222 while (*p) {
2223 q = p;
2224 while (*q && *q != c) q++;
2225 str = snewn(q-p+1, char);
2226 strncpy(str, p, q-p);
2227 str[q-p] = '\0';
2228 {
2229 TCHAR ws[50];
2230 MultiByteToWideChar (CP_ACP, 0, str, -1, ws, 50);
2231 SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)ws);
2232 }
2233
2234 sfree(str);
2235 if (*q) q++;
2236 p = q;
2237 }
2238 }
2239 SendMessage(ctl, CB_SETCURSEL, i->ival, 0);
2240 break;
2241 }
2242
2243 y += 15;
2244 }
2245
2246}
2247#endif
2248
2249static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg,
2250 WPARAM wParam, LPARAM lParam)
2251{
2252 frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
2253 config_item *i;
2254 struct cfg_aux *j;
2255
2256 switch (msg) {
2257 case WM_INITDIALOG:
2258#ifdef _WIN32_WCE
2259 {
2260 char *title;
2261
2262 fe = (frontend *) lParam;
2263 SetWindowLong(hwnd, GWL_USERDATA, lParam);
2264 fe->cfgbox = hwnd;
2265
2266 fe->cfg = frontend_get_config(fe, fe->cfg_which, &title);
2267
2268 make_dialog_full_screen(hwnd);
2269
2270 SetDlgItemTextA(hwnd, IDC_CONFIG_CAPTION, title);
2271 SendDlgItemMessage(hwnd, IDC_CONFIG_CAPTION, WM_SETFONT,
2272 (WPARAM) dialog_title_font(), 0);
2273
2274 create_config_controls(fe);
2275 }
2276#endif
2277 return TRUE;
2278
2279 case WM_COMMAND:
2280 /*
2281 * OK and Cancel are special cases.
2282 */
2283 if ((LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
2284 if (LOWORD(wParam) == IDOK) {
2285 char *err = frontend_set_config(fe, fe->cfg_which, fe->cfg);
2286
2287 if (err) {
2288 MessageBox(hwnd, err, "Validation error",
2289 MB_ICONERROR | MB_OK);
2290 } else {
2291#ifdef _WIN32_WCE
2292 EndDialog(hwnd, 2);
2293#else
2294 fe->dlg_done = 2;
2295#endif
2296 }
2297 } else {
2298#ifdef _WIN32_WCE
2299 EndDialog(hwnd, 1);
2300#else
2301 fe->dlg_done = 1;
2302#endif
2303 }
2304 return 0;
2305 }
2306
2307 /*
2308 * First find the control whose id this is.
2309 */
2310 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2311 if (j->ctlid == LOWORD(wParam))
2312 break;
2313 }
2314 if (i->type == C_END)
2315 return 0; /* not our problem */
2316
2317 if (i->type == C_STRING && HIWORD(wParam) == EN_CHANGE) {
2318 char buffer[4096];
2319#ifdef _WIN32_WCE
2320 TCHAR wBuffer[4096];
2321 GetDlgItemText(fe->cfgbox, j->ctlid, wBuffer, 4096);
2322 WideCharToMultiByte(CP_ACP, 0, wBuffer, -1, buffer, 4096, NULL, NULL);
2323#else
2324 GetDlgItemText(fe->cfgbox, j->ctlid, buffer, lenof(buffer));
2325#endif
2326 buffer[lenof(buffer)-1] = '\0';
2327 sfree(i->sval);
2328 i->sval = dupstr(buffer);
2329 } else if (i->type == C_BOOLEAN &&
2330 (HIWORD(wParam) == BN_CLICKED ||
2331 HIWORD(wParam) == BN_DBLCLK)) {
2332 i->ival = IsDlgButtonChecked(fe->cfgbox, j->ctlid);
2333 } else if (i->type == C_CHOICES &&
2334 HIWORD(wParam) == CBN_SELCHANGE) {
2335 i->ival = SendDlgItemMessage(fe->cfgbox, j->ctlid,
2336 CB_GETCURSEL, 0, 0);
2337 }
2338
2339 return 0;
2340
2341 case WM_CLOSE:
2342 fe->dlg_done = 1;
2343 return 0;
2344 }
2345
2346 return 0;
2347}
2348
2349#ifndef _WIN32_WCE
2350HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
2351 char *wclass, int wstyle,
2352 int exstyle, const char *wtext, int wid)
2353{
2354 HWND ret;
2355 ret = CreateWindowEx(exstyle, wclass, wtext,
2356 wstyle | WS_CHILD | WS_VISIBLE, x1, y1, x2-x1, y2-y1,
2357 fe->cfgbox, (HMENU) wid, fe->inst, NULL);
2358 SendMessage(ret, WM_SETFONT, (WPARAM)fe->cfgfont, MAKELPARAM(TRUE, 0));
2359 return ret;
2360}
2361#endif
2362
2363static void about(frontend *fe)
2364{
2365#ifdef _WIN32_WCE
2366 DialogBox(fe->inst, MAKEINTRESOURCE(IDD_ABOUT), fe->hwnd, AboutDlgProc);
2367#else
2368 int i;
2369 WNDCLASS wc;
2370 MSG msg;
2371 TEXTMETRIC tm;
2372 HDC hdc;
2373 HFONT oldfont;
2374 SIZE size;
2375 int gm, id;
2376 int winwidth, winheight, y;
2377 int height, width, maxwid;
2378 const char *strings[16];
2379 int lengths[16];
2380 int nstrings = 0;
2381 char titlebuf[512];
2382
2383 sprintf(titlebuf, "About %.250s", fe->game->name);
2384
2385 strings[nstrings++] = fe->game->name;
2386 strings[nstrings++] = "from Simon Tatham's Portable Puzzle Collection";
2387 strings[nstrings++] = ver;
2388
2389 wc.style = CS_DBLCLKS | CS_SAVEBITS;
2390 wc.lpfnWndProc = DefDlgProc;
2391 wc.cbClsExtra = 0;
2392 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
2393 wc.hInstance = fe->inst;
2394 wc.hIcon = NULL;
2395 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2396 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
2397 wc.lpszMenuName = NULL;
2398 wc.lpszClassName = "GameAboutBox";
2399 RegisterClass(&wc);
2400
2401 hdc = GetDC(fe->hwnd);
2402 SetMapMode(hdc, MM_TEXT);
2403
2404 fe->dlg_done = FALSE;
2405
2406 fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2407 0, 0, 0, 0,
2408 FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2409 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2410 DEFAULT_QUALITY,
2411 FF_SWISS,
2412 "MS Shell Dlg");
2413
2414 oldfont = SelectObject(hdc, fe->cfgfont);
2415 if (GetTextMetrics(hdc, &tm)) {
2416 height = tm.tmAscent + tm.tmDescent;
2417 width = tm.tmAveCharWidth;
2418 } else {
2419 height = width = 30;
2420 }
2421
2422 /*
2423 * Figure out the layout of the About box by measuring the
2424 * length of each piece of text.
2425 */
2426 maxwid = 0;
2427 winheight = height/2;
2428
2429 for (i = 0; i < nstrings; i++) {
2430 if (GetTextExtentPoint32(hdc, strings[i], strlen(strings[i]), &size))
2431 lengths[i] = size.cx;
2432 else
2433 lengths[i] = 0; /* *shrug* */
2434 if (maxwid < lengths[i])
2435 maxwid = lengths[i];
2436 winheight += height * 3 / 2 + (height / 2);
2437 }
2438
2439 winheight += height + height * 7 / 4; /* OK button */
2440 winwidth = maxwid + 4*width;
2441
2442 SelectObject(hdc, oldfont);
2443 ReleaseDC(fe->hwnd, hdc);
2444
2445 /*
2446 * Create the dialog, now that we know its size.
2447 */
2448 {
2449 RECT r, r2;
2450
2451 r.left = r.top = 0;
2452 r.right = winwidth;
2453 r.bottom = winheight;
2454
2455 AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*|
2456 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2457 WS_CAPTION | WS_SYSMENU*/) &~
2458 (WS_MAXIMIZEBOX | WS_OVERLAPPED),
2459 FALSE, 0);
2460
2461 /*
2462 * Centre the dialog on its parent window.
2463 */
2464 r.right -= r.left;
2465 r.bottom -= r.top;
2466 GetWindowRect(fe->hwnd, &r2);
2467 r.left = (r2.left + r2.right - r.right) / 2;
2468 r.top = (r2.top + r2.bottom - r.bottom) / 2;
2469 r.right += r.left;
2470 r.bottom += r.top;
2471
2472 fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, titlebuf,
2473 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2474 WS_CAPTION | WS_SYSMENU,
2475 r.left, r.top,
2476 r.right-r.left, r.bottom-r.top,
2477 fe->hwnd, NULL, fe->inst, NULL);
2478 }
2479
2480 SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE);
2481
2482 SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe);
2483 SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)AboutDlgProc);
2484
2485 id = 1000;
2486 y = height/2;
2487 for (i = 0; i < nstrings; i++) {
2488 int border = width*2 + (maxwid - lengths[i]) / 2;
2489 mkctrl(fe, border, border+lengths[i], y+height*1/8, y+height*9/8,
2490 "Static", 0, 0, strings[i], id++);
2491 y += height*3/2;
2492
2493 assert(y < winheight);
2494 y += height/2;
2495 }
2496
2497 y += height/2; /* extra space before OK */
2498 mkctrl(fe, width*2, maxwid+width*2, y, y+height*7/4, "BUTTON",
2499 BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
2500 "OK", IDOK);
2501
2502 SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0);
2503
2504 EnableWindow(fe->hwnd, FALSE);
2505 ShowWindow(fe->cfgbox, SW_SHOWNORMAL);
2506 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
2507 if (!IsDialogMessage(fe->cfgbox, &msg))
2508 DispatchMessage(&msg);
2509 if (fe->dlg_done)
2510 break;
2511 }
2512 EnableWindow(fe->hwnd, TRUE);
2513 SetForegroundWindow(fe->hwnd);
2514 DestroyWindow(fe->cfgbox);
2515 DeleteObject(fe->cfgfont);
2516#endif
2517}
2518
2519static int get_config(frontend *fe, int which)
2520{
2521#ifdef _WIN32_WCE
2522 fe->cfg_which = which;
2523
2524 return DialogBoxParam(fe->inst,
2525 MAKEINTRESOURCE(IDD_CONFIG),
2526 fe->hwnd, ConfigDlgProc,
2527 (LPARAM) fe) == 2;
2528#else
2529 config_item *i;
2530 struct cfg_aux *j;
2531 char *title;
2532 WNDCLASS wc;
2533 MSG msg;
2534 TEXTMETRIC tm;
2535 HDC hdc;
2536 HFONT oldfont;
2537 SIZE size;
2538 HWND ctl;
2539 int gm, id, nctrls;
2540 int winwidth, winheight, col1l, col1r, col2l, col2r, y;
2541 int height, width, maxlabel, maxcheckbox;
2542
2543 wc.style = CS_DBLCLKS | CS_SAVEBITS;
2544 wc.lpfnWndProc = DefDlgProc;
2545 wc.cbClsExtra = 0;
2546 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
2547 wc.hInstance = fe->inst;
2548 wc.hIcon = NULL;
2549 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2550 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
2551 wc.lpszMenuName = NULL;
2552 wc.lpszClassName = "GameConfigBox";
2553 RegisterClass(&wc);
2554
2555 hdc = GetDC(fe->hwnd);
2556 SetMapMode(hdc, MM_TEXT);
2557
2558 fe->dlg_done = FALSE;
2559
2560 fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2561 0, 0, 0, 0,
2562 FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2563 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2564 DEFAULT_QUALITY,
2565 FF_SWISS,
2566 "MS Shell Dlg");
2567
2568 oldfont = SelectObject(hdc, fe->cfgfont);
2569 if (GetTextMetrics(hdc, &tm)) {
2570 height = tm.tmAscent + tm.tmDescent;
2571 width = tm.tmAveCharWidth;
2572 } else {
2573 height = width = 30;
2574 }
2575
2576 fe->cfg = frontend_get_config(fe, which, &title);
2577 fe->cfg_which = which;
2578
2579 /*
2580 * Figure out the layout of the config box by measuring the
2581 * length of each piece of text.
2582 */
2583 maxlabel = maxcheckbox = 0;
2584 winheight = height/2;
2585
2586 for (i = fe->cfg; i->type != C_END; i++) {
2587 switch (i->type) {
2588 case C_STRING:
2589 case C_CHOICES:
2590 /*
2591 * Both these control types have a label filling only
2592 * the left-hand column of the box.
2593 */
2594 if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) &&
2595 maxlabel < size.cx)
2596 maxlabel = size.cx;
2597 winheight += height * 3 / 2 + (height / 2);
2598 break;
2599
2600 case C_BOOLEAN:
2601 /*
2602 * Checkboxes take up the whole of the box width.
2603 */
2604 if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) &&
2605 maxcheckbox < size.cx)
2606 maxcheckbox = size.cx;
2607 winheight += height + (height / 2);
2608 break;
2609 }
2610 }
2611
2612 winheight += height + height * 7 / 4; /* OK / Cancel buttons */
2613
2614 col1l = 2*width;
2615 col1r = col1l + maxlabel;
2616 col2l = col1r + 2*width;
2617 col2r = col2l + 30*width;
2618 if (col2r < col1l+2*height+maxcheckbox)
2619 col2r = col1l+2*height+maxcheckbox;
2620 winwidth = col2r + 2*width;
2621
2622 SelectObject(hdc, oldfont);
2623 ReleaseDC(fe->hwnd, hdc);
2624
2625 /*
2626 * Create the dialog, now that we know its size.
2627 */
2628 {
2629 RECT r, r2;
2630
2631 r.left = r.top = 0;
2632 r.right = winwidth;
2633 r.bottom = winheight;
2634
2635 AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*|
2636 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2637 WS_CAPTION | WS_SYSMENU*/) &~
2638 (WS_MAXIMIZEBOX | WS_OVERLAPPED),
2639 FALSE, 0);
2640
2641 /*
2642 * Centre the dialog on its parent window.
2643 */
2644 r.right -= r.left;
2645 r.bottom -= r.top;
2646 GetWindowRect(fe->hwnd, &r2);
2647 r.left = (r2.left + r2.right - r.right) / 2;
2648 r.top = (r2.top + r2.bottom - r.bottom) / 2;
2649 r.right += r.left;
2650 r.bottom += r.top;
2651
2652 fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, title,
2653 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2654 WS_CAPTION | WS_SYSMENU,
2655 r.left, r.top,
2656 r.right-r.left, r.bottom-r.top,
2657 fe->hwnd, NULL, fe->inst, NULL);
2658 sfree(title);
2659 }
2660
2661 SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE);
2662
2663 SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe);
2664 SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)ConfigDlgProc);
2665
2666 /*
2667 * Count the controls so we can allocate cfgaux.
2668 */
2669 for (nctrls = 0, i = fe->cfg; i->type != C_END; i++)
2670 nctrls++;
2671 fe->cfgaux = snewn(nctrls, struct cfg_aux);
2672
2673 id = 1000;
2674 y = height/2;
2675 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2676 switch (i->type) {
2677 case C_STRING:
2678 /*
2679 * Edit box with a label beside it.
2680 */
2681 mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8,
2682 "Static", 0, 0, i->name, id++);
2683 ctl = mkctrl(fe, col2l, col2r, y, y+height*3/2,
2684 "EDIT", WS_TABSTOP | ES_AUTOHSCROLL,
2685 WS_EX_CLIENTEDGE, "", (j->ctlid = id++));
2686 SetWindowText(ctl, i->sval);
2687 y += height*3/2;
2688 break;
2689
2690 case C_BOOLEAN:
2691 /*
2692 * Simple checkbox.
2693 */
2694 mkctrl(fe, col1l, col2r, y, y+height, "BUTTON",
2695 BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
2696 0, i->name, (j->ctlid = id++));
2697 CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0));
2698 y += height;
2699 break;
2700
2701 case C_CHOICES:
2702 /*
2703 * Drop-down list with a label beside it.
2704 */
2705 mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8,
2706 "STATIC", 0, 0, i->name, id++);
2707 ctl = mkctrl(fe, col2l, col2r, y, y+height*41/2,
2708 "COMBOBOX", WS_TABSTOP |
2709 CBS_DROPDOWNLIST | CBS_HASSTRINGS,
2710 WS_EX_CLIENTEDGE, "", (j->ctlid = id++));
2711 {
2712 char c, *p, *q, *str;
2713
2714 SendMessage(ctl, CB_RESETCONTENT, 0, 0);
2715 p = i->sval;
2716 c = *p++;
2717 while (*p) {
2718 q = p;
2719 while (*q && *q != c) q++;
2720 str = snewn(q-p+1, char);
2721 strncpy(str, p, q-p);
2722 str[q-p] = '\0';
2723 SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)str);
2724 sfree(str);
2725 if (*q) q++;
2726 p = q;
2727 }
2728 }
2729
2730 SendMessage(ctl, CB_SETCURSEL, i->ival, 0);
2731
2732 y += height*3/2;
2733 break;
2734 }
2735
2736 assert(y < winheight);
2737 y += height/2;
2738 }
2739
2740 y += height/2; /* extra space before OK and Cancel */
2741 mkctrl(fe, col1l, (col1l+col2r)/2-width, y, y+height*7/4, "BUTTON",
2742 BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
2743 "OK", IDOK);
2744 mkctrl(fe, (col1l+col2r)/2+width, col2r, y, y+height*7/4, "BUTTON",
2745 BS_PUSHBUTTON | WS_TABSTOP, 0, "Cancel", IDCANCEL);
2746
2747 SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0);
2748
2749 EnableWindow(fe->hwnd, FALSE);
2750 ShowWindow(fe->cfgbox, SW_SHOWNORMAL);
2751 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
2752 if (!IsDialogMessage(fe->cfgbox, &msg))
2753 DispatchMessage(&msg);
2754 if (fe->dlg_done)
2755 break;
2756 }
2757 EnableWindow(fe->hwnd, TRUE);
2758 SetForegroundWindow(fe->hwnd);
2759 DestroyWindow(fe->cfgbox);
2760 DeleteObject(fe->cfgfont);
2761
2762 free_cfg(fe->cfg);
2763 sfree(fe->cfgaux);
2764
2765 return (fe->dlg_done == 2);
2766#endif
2767}
2768
2769#ifdef _WIN32_WCE
2770static void calculate_bitmap_position(frontend *fe, int x, int y)
2771{
2772 /* Pocket PC - center the game in the full screen window */
2773 int yMargin;
2774 RECT rcClient;
2775
2776 GetClientRect(fe->hwnd, &rcClient);
2777 fe->bitmapPosition.left = (rcClient.right - x) / 2;
2778 yMargin = rcClient.bottom - y;
2779
2780 if (fe->numpad != NULL) {
2781 RECT rcPad;
2782 GetWindowRect(fe->numpad, &rcPad);
2783 yMargin -= rcPad.bottom - rcPad.top;
2784 }
2785
2786 if (fe->statusbar != NULL) {
2787 RECT rcStatus;
2788 GetWindowRect(fe->statusbar, &rcStatus);
2789 yMargin -= rcStatus.bottom - rcStatus.top;
2790 }
2791
2792 fe->bitmapPosition.top = yMargin / 2;
2793
2794 fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2795 fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2796}
2797#else
2798static void calculate_bitmap_position(frontend *fe, int x, int y)
2799{
2800 /* Plain Windows - position the game in the upper-left corner */
2801 fe->bitmapPosition.left = 0;
2802 fe->bitmapPosition.top = 0;
2803 fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2804 fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2805}
2806#endif
2807
2808static void new_bitmap(frontend *fe, int x, int y)
2809{
2810 HDC hdc;
2811
2812 if (fe->bitmap) DeleteObject(fe->bitmap);
2813
2814 hdc = GetDC(fe->hwnd);
2815 fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
2816 calculate_bitmap_position(fe, x, y);
2817 ReleaseDC(fe->hwnd, hdc);
2818}
2819
2820static void new_game_size(frontend *fe, float scale)
2821{
2822 RECT r, sr;
2823 int x, y;
2824
2825 get_max_puzzle_size(fe, &x, &y);
2826 midend_size(fe->me, &x, &y, FALSE);
2827
2828 if (scale != 1.0) {
2829 x = (int)((float)x * fe->puzz_scale);
2830 y = (int)((float)y * fe->puzz_scale);
2831 midend_size(fe->me, &x, &y, TRUE);
2832 }
2833 fe->ymin = (fe->xmin * y) / x;
2834
2835 r.left = r.top = 0;
2836 r.right = x;
2837 r.bottom = y;
2838 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
2839
2840 if (fe->statusbar != NULL) {
2841 GetWindowRect(fe->statusbar, &sr);
2842 } else {
2843 sr.left = sr.right = sr.top = sr.bottom = 0;
2844 }
2845#ifndef _WIN32_WCE
2846 SetWindowPos(fe->hwnd, NULL, 0, 0,
2847 r.right - r.left,
2848 r.bottom - r.top + sr.bottom - sr.top,
2849 SWP_NOMOVE | SWP_NOZORDER);
2850#endif
2851
2852 check_window_size(fe, &x, &y);
2853
2854#ifndef _WIN32_WCE
2855 if (fe->statusbar != NULL)
2856 SetWindowPos(fe->statusbar, NULL, 0, y, x,
2857 sr.bottom - sr.top, SWP_NOZORDER);
2858#endif
2859
2860 new_bitmap(fe, x, y);
2861
2862#ifdef _WIN32_WCE
2863 InvalidateRect(fe->hwnd, NULL, TRUE);
2864#endif
2865 midend_redraw(fe->me);
2866}
2867
2868/*
2869 * Given a proposed new window rect, work out the resulting
2870 * difference in client size (from current), and use to try
2871 * and resize the puzzle, returning (wx,wy) as the actual
2872 * new window size.
2873 */
2874
2875static void adjust_game_size(frontend *fe, RECT *proposed, int isedge,
2876 int *wx_r, int *wy_r)
2877{
2878 RECT cr, wr;
2879 int nx, ny, xdiff, ydiff, wx, wy;
2880
2881 /* Work out the current window sizing, and thus the
2882 * difference in size we're asking for. */
2883 GetClientRect(fe->hwnd, &cr);
2884 wr = cr;
2885 AdjustWindowRectEx(&wr, WINFLAGS, TRUE, 0);
2886
2887 xdiff = (proposed->right - proposed->left) - (wr.right - wr.left);
2888 ydiff = (proposed->bottom - proposed->top) - (wr.bottom - wr.top);
2889
2890 if (isedge) {
2891 /* These next four lines work around the fact that midend_size
2892 * is happy to shrink _but not grow_ if you change one dimension
2893 * but not the other. */
2894 if (xdiff > 0 && ydiff == 0)
2895 ydiff = (xdiff * (wr.right - wr.left)) / (wr.bottom - wr.top);
2896 if (xdiff == 0 && ydiff > 0)
2897 xdiff = (ydiff * (wr.bottom - wr.top)) / (wr.right - wr.left);
2898 }
2899
2900 if (check_window_resize(fe,
2901 (cr.right - cr.left) + xdiff,
2902 (cr.bottom - cr.top) + ydiff,
2903 &nx, &ny, &wx, &wy)) {
2904 new_bitmap(fe, nx, ny);
2905 midend_force_redraw(fe->me);
2906 } else {
2907 /* reset size to current window size */
2908 wx = wr.right - wr.left;
2909 wy = wr.bottom - wr.top;
2910 }
2911 /* Re-fetch rectangle; size limits mean we might not have
2912 * taken it quite to the mouse drag positions. */
2913 GetClientRect(fe->hwnd, &cr);
2914 adjust_statusbar(fe, &cr);
2915
2916 *wx_r = wx; *wy_r = wy;
2917}
2918
2919static void update_type_menu_tick(frontend *fe)
2920{
2921 int total, n, i;
2922
2923 if (fe->typemenu == INVALID_HANDLE_VALUE)
2924 return;
2925
2926 n = midend_which_preset(fe->me);
2927
2928 for (i = 0; i < fe->n_preset_menuitems; i++) {
2929 if (fe->preset_menuitems[i].which_menu) {
2930 int flag = (i == n ? MF_CHECKED : MF_UNCHECKED);
2931 CheckMenuItem(fe->preset_menuitems[i].which_menu,
2932 fe->preset_menuitems[i].item_index,
2933 MF_BYPOSITION | flag);
2934 }
2935 }
2936
2937 if (fe->game->can_configure) {
2938 int flag = (n < 0 ? MF_CHECKED : MF_UNCHECKED);
2939 /* "Custom" menu item is at the bottom of the top-level Type menu */
2940 total = GetMenuItemCount(fe->typemenu);
2941 CheckMenuItem(fe->typemenu, total - 1, MF_BYPOSITION | flag);
2942 }
2943
2944 DrawMenuBar(fe->hwnd);
2945}
2946
2947static void update_copy_menu_greying(frontend *fe)
2948{
2949 UINT enable = (midend_can_format_as_text_now(fe->me) ?
2950 MF_ENABLED : MF_GRAYED);
2951 EnableMenuItem(fe->gamemenu, IDM_COPY, MF_BYCOMMAND | enable);
2952}
2953
2954static void new_game_type(frontend *fe)
2955{
2956 midend_new_game(fe->me);
2957 new_game_size(fe, 1.0);
2958 update_type_menu_tick(fe);
2959 update_copy_menu_greying(fe);
2960}
2961
2962static int is_alt_pressed(void)
2963{
2964 BYTE keystate[256];
2965 int r = GetKeyboardState(keystate);
2966 if (!r)
2967 return FALSE;
2968 if (keystate[VK_MENU] & 0x80)
2969 return TRUE;
2970 if (keystate[VK_RMENU] & 0x80)
2971 return TRUE;
2972 return FALSE;
2973}
2974
2975static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
2976 WPARAM wParam, LPARAM lParam)
2977{
2978 frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
2979 int cmd;
2980
2981 switch (message) {
2982 case WM_CLOSE:
2983 DestroyWindow(hwnd);
2984 return 0;
2985 case WM_COMMAND:
2986#ifdef _WIN32_WCE
2987 /* Numeric pad sends WM_COMMAND messages */
2988 if ((wParam >= IDM_KEYEMUL) && (wParam < IDM_KEYEMUL + 256))
2989 {
2990 midend_process_key(fe->me, 0, 0, wParam - IDM_KEYEMUL);
2991 }
2992#endif
2993 cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */
2994 switch (cmd) {
2995 case IDM_NEW:
2996 if (!midend_process_key(fe->me, 0, 0, 'n'))
2997 PostQuitMessage(0);
2998 break;
2999 case IDM_RESTART:
3000 midend_restart_game(fe->me);
3001 break;
3002 case IDM_UNDO:
3003 if (!midend_process_key(fe->me, 0, 0, 'u'))
3004 PostQuitMessage(0);
3005 break;
3006 case IDM_REDO:
3007 if (!midend_process_key(fe->me, 0, 0, '\x12'))
3008 PostQuitMessage(0);
3009 break;
3010 case IDM_COPY:
3011 {
3012 char *text = midend_text_format(fe->me);
3013 if (text)
3014 write_clip(hwnd, text);
3015 else
3016 MessageBeep(MB_ICONWARNING);
3017 sfree(text);
3018 }
3019 break;
3020 case IDM_SOLVE:
3021 {
3022 char *msg = midend_solve(fe->me);
3023 if (msg)
3024 MessageBox(hwnd, msg, "Unable to solve",
3025 MB_ICONERROR | MB_OK);
3026 }
3027 break;
3028 case IDM_QUIT:
3029 if (!midend_process_key(fe->me, 0, 0, 'q'))
3030 PostQuitMessage(0);
3031 break;
3032 case IDM_CONFIG:
3033 if (get_config(fe, CFG_SETTINGS))
3034 new_game_type(fe);
3035 break;
3036 case IDM_SEED:
3037 if (get_config(fe, CFG_SEED))
3038 new_game_type(fe);
3039 break;
3040 case IDM_DESC:
3041 if (get_config(fe, CFG_DESC))
3042 new_game_type(fe);
3043 break;
3044 case IDM_PRINT:
3045 if (get_config(fe, CFG_PRINT))
3046 print(fe);
3047 break;
3048 case IDM_ABOUT:
3049 about(fe);
3050 break;
3051 case IDM_LOAD:
3052 case IDM_SAVE:
3053 {
3054 OPENFILENAME of;
3055 char filename[FILENAME_MAX];
3056 int ret;
3057
3058 memset(&of, 0, sizeof(of));
3059 of.hwndOwner = hwnd;
3060 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
3061 of.lpstrCustomFilter = NULL;
3062 of.nFilterIndex = 1;
3063 of.lpstrFile = filename;
3064 filename[0] = '\0';
3065 of.nMaxFile = lenof(filename);
3066 of.lpstrFileTitle = NULL;
3067 of.lpstrTitle = (cmd == IDM_SAVE ?
3068 "Enter name of game file to save" :
3069 "Enter name of saved game file to load");
3070 of.Flags = 0;
3071#ifdef OPENFILENAME_SIZE_VERSION_400
3072 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
3073#else
3074 of.lStructSize = sizeof(of);
3075#endif
3076 of.lpstrInitialDir = NULL;
3077
3078 if (cmd == IDM_SAVE)
3079 ret = GetSaveFileName(&of);
3080 else
3081 ret = GetOpenFileName(&of);
3082
3083 if (ret) {
3084 if (cmd == IDM_SAVE) {
3085 FILE *fp;
3086
3087 if ((fp = fopen(filename, "r")) != NULL) {
3088 char buf[256 + FILENAME_MAX];
3089 fclose(fp);
3090 /* file exists */
3091
3092 sprintf(buf, "Are you sure you want to overwrite"
3093 " the file \"%.*s\"?",
3094 FILENAME_MAX, filename);
3095 if (MessageBox(hwnd, buf, "Question",
3096 MB_YESNO | MB_ICONQUESTION)
3097 != IDYES)
3098 break;
3099 }
3100
3101 fp = fopen(filename, "w");
3102
3103 if (!fp) {
3104 MessageBox(hwnd, "Unable to open save file",
3105 "Error", MB_ICONERROR | MB_OK);
3106 break;
3107 }
3108
3109 midend_serialise(fe->me, savefile_write, fp);
3110
3111 fclose(fp);
3112 } else {
3113 FILE *fp = fopen(filename, "r");
3114 char *err = NULL;
3115 midend *me = fe->me;
3116#ifdef COMBINED
3117 char *id_name;
3118#endif
3119
3120 if (!fp) {
3121 MessageBox(hwnd, "Unable to open saved game file",
3122 "Error", MB_ICONERROR | MB_OK);
3123 break;
3124 }
3125
3126#ifdef COMBINED
3127 /*
3128 * This save file might be from a different
3129 * game.
3130 */
3131 err = identify_game(&id_name, savefile_read, fp);
3132 if (!err) {
3133 int i;
3134 for (i = 0; i < gamecount; i++)
3135 if (!strcmp(id_name, gamelist[i]->name))
3136 break;
3137 if (i == gamecount) {
3138 err = "Save file is for a game not "
3139 "supported by this program";
3140 } else {
3141 me = midend_for_new_game(fe, gamelist[i], NULL,
3142 FALSE, FALSE, &err);
3143 rewind(fp); /* for the actual load */
3144 }
3145 sfree(id_name);
3146 }
3147#endif
3148 if (!err)
3149 err = midend_deserialise(me, savefile_read, fp);
3150
3151 fclose(fp);
3152
3153 if (err) {
3154 MessageBox(hwnd, err, "Error", MB_ICONERROR|MB_OK);
3155 break;
3156 }
3157
3158 if (fe->me != me)
3159 fe_set_midend(fe, me);
3160 new_game_size(fe, 1.0);
3161 }
3162 }
3163 }
3164
3165 break;
3166#ifndef _WIN32_WCE
3167 case IDM_HELPC:
3168 start_help(fe, NULL);
3169 break;
3170 case IDM_GAMEHELP:
3171 assert(help_type != NONE);
3172 start_help(fe, help_type == CHM ?
3173 fe->game->htmlhelp_topic : fe->game->winhelp_topic);
3174 break;
3175#endif
3176 default:
3177#ifdef COMBINED
3178 if (wParam >= IDM_GAMES && wParam < (IDM_GAMES + (WPARAM)gamecount)) {
3179 int p = wParam - IDM_GAMES;
3180 char *error = NULL;
3181 fe_set_midend(fe, midend_for_new_game(fe, gamelist[p], NULL,
3182 FALSE, FALSE, &error));
3183 sfree(error);
3184 } else
3185#endif
3186 {
3187 game_params *preset = preset_menu_lookup_by_id(
3188 fe->preset_menu,
3189 ((wParam &~ 0xF) - IDM_PRESETS) / 0x10);
3190
3191 if (preset) {
3192 midend_set_params(fe->me, preset);
3193 new_game_type(fe);
3194 }
3195 }
3196 break;
3197 }
3198 break;
3199 case WM_DESTROY:
3200#ifndef _WIN32_WCE
3201 stop_help(fe);
3202#endif
3203 frontend_free(fe);
3204 PostQuitMessage(0);
3205 return 0;
3206 case WM_PAINT:
3207 {
3208 PAINTSTRUCT p;
3209 HDC hdc, hdc2;
3210 HBITMAP prevbm;
3211 RECT rcDest;
3212
3213 hdc = BeginPaint(hwnd, &p);
3214 hdc2 = CreateCompatibleDC(hdc);
3215 prevbm = SelectObject(hdc2, fe->bitmap);
3216#ifdef _WIN32_WCE
3217 FillRect(hdc, &(p.rcPaint), (HBRUSH) GetStockObject(WHITE_BRUSH));
3218#endif
3219 IntersectRect(&rcDest, &(fe->bitmapPosition), &(p.rcPaint));
3220 BitBlt(hdc,
3221 rcDest.left, rcDest.top,
3222 rcDest.right - rcDest.left,
3223 rcDest.bottom - rcDest.top,
3224 hdc2,
3225 rcDest.left - fe->bitmapPosition.left,
3226 rcDest.top - fe->bitmapPosition.top,
3227 SRCCOPY);
3228 SelectObject(hdc2, prevbm);
3229 DeleteDC(hdc2);
3230 EndPaint(hwnd, &p);
3231 }
3232 return 0;
3233 case WM_KEYDOWN:
3234 {
3235 int key = -1;
3236 BYTE keystate[256];
3237 int r = GetKeyboardState(keystate);
3238 int shift = (r && (keystate[VK_SHIFT] & 0x80)) ? MOD_SHFT : 0;
3239 int ctrl = (r && (keystate[VK_CONTROL] & 0x80)) ? MOD_CTRL : 0;
3240
3241 switch (wParam) {
3242 case VK_LEFT:
3243 if (!(lParam & 0x01000000))
3244 key = MOD_NUM_KEYPAD | '4';
3245 else
3246 key = shift | ctrl | CURSOR_LEFT;
3247 break;
3248 case VK_RIGHT:
3249 if (!(lParam & 0x01000000))
3250 key = MOD_NUM_KEYPAD | '6';
3251 else
3252 key = shift | ctrl | CURSOR_RIGHT;
3253 break;
3254 case VK_UP:
3255 if (!(lParam & 0x01000000))
3256 key = MOD_NUM_KEYPAD | '8';
3257 else
3258 key = shift | ctrl | CURSOR_UP;
3259 break;
3260 case VK_DOWN:
3261 if (!(lParam & 0x01000000))
3262 key = MOD_NUM_KEYPAD | '2';
3263 else
3264 key = shift | ctrl | CURSOR_DOWN;
3265 break;
3266 /*
3267 * Diagonal keys on the numeric keypad.
3268 */
3269 case VK_PRIOR:
3270 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '9';
3271 break;
3272 case VK_NEXT:
3273 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '3';
3274 break;
3275 case VK_HOME:
3276 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '7';
3277 break;
3278 case VK_END:
3279 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '1';
3280 break;
3281 case VK_INSERT:
3282 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '0';
3283 break;
3284 case VK_CLEAR:
3285 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '5';
3286 break;
3287 /*
3288 * Numeric keypad keys with Num Lock on.
3289 */
3290 case VK_NUMPAD4: key = MOD_NUM_KEYPAD | '4'; break;
3291 case VK_NUMPAD6: key = MOD_NUM_KEYPAD | '6'; break;
3292 case VK_NUMPAD8: key = MOD_NUM_KEYPAD | '8'; break;
3293 case VK_NUMPAD2: key = MOD_NUM_KEYPAD | '2'; break;
3294 case VK_NUMPAD5: key = MOD_NUM_KEYPAD | '5'; break;
3295 case VK_NUMPAD9: key = MOD_NUM_KEYPAD | '9'; break;
3296 case VK_NUMPAD3: key = MOD_NUM_KEYPAD | '3'; break;
3297 case VK_NUMPAD7: key = MOD_NUM_KEYPAD | '7'; break;
3298 case VK_NUMPAD1: key = MOD_NUM_KEYPAD | '1'; break;
3299 case VK_NUMPAD0: key = MOD_NUM_KEYPAD | '0'; break;
3300 }
3301
3302 if (key != -1) {
3303 if (!midend_process_key(fe->me, 0, 0, key))
3304 PostQuitMessage(0);
3305 } else {
3306 MSG m;
3307 m.hwnd = hwnd;
3308 m.message = WM_KEYDOWN;
3309 m.wParam = wParam;
3310 m.lParam = lParam & 0xdfff;
3311 TranslateMessage(&m);
3312 }
3313 }
3314 break;
3315 case WM_LBUTTONDOWN:
3316 case WM_RBUTTONDOWN:
3317 case WM_MBUTTONDOWN:
3318 {
3319 int button;
3320
3321 /*
3322 * Shift-clicks count as middle-clicks, since otherwise
3323 * two-button Windows users won't have any kind of
3324 * middle click to use.
3325 */
3326 if (message == WM_MBUTTONDOWN || (wParam & MK_SHIFT))
3327 button = MIDDLE_BUTTON;
3328 else if (message == WM_RBUTTONDOWN || is_alt_pressed())
3329 button = RIGHT_BUTTON;
3330 else
3331#ifndef _WIN32_WCE
3332 button = LEFT_BUTTON;
3333#else
3334 if ((fe->game->flags & REQUIRE_RBUTTON) == 0)
3335 button = LEFT_BUTTON;
3336 else
3337 {
3338 SHRGINFO shrgi;
3339
3340 shrgi.cbSize = sizeof(SHRGINFO);
3341 shrgi.hwndClient = hwnd;
3342 shrgi.ptDown.x = (signed short)LOWORD(lParam);
3343 shrgi.ptDown.y = (signed short)HIWORD(lParam);
3344 shrgi.dwFlags = SHRG_RETURNCMD;
3345
3346 if (GN_CONTEXTMENU == SHRecognizeGesture(&shrgi))
3347 button = RIGHT_BUTTON;
3348 else
3349 button = LEFT_BUTTON;
3350 }
3351#endif
3352
3353 if (!midend_process_key(fe->me,
3354 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3355 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3356 button))
3357 PostQuitMessage(0);
3358
3359 SetCapture(hwnd);
3360 }
3361 break;
3362 case WM_LBUTTONUP:
3363 case WM_RBUTTONUP:
3364 case WM_MBUTTONUP:
3365 {
3366 int button;
3367
3368 /*
3369 * Shift-clicks count as middle-clicks, since otherwise
3370 * two-button Windows users won't have any kind of
3371 * middle click to use.
3372 */
3373 if (message == WM_MBUTTONUP || (wParam & MK_SHIFT))
3374 button = MIDDLE_RELEASE;
3375 else if (message == WM_RBUTTONUP || is_alt_pressed())
3376 button = RIGHT_RELEASE;
3377 else
3378 button = LEFT_RELEASE;
3379
3380 if (!midend_process_key(fe->me,
3381 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3382 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3383 button))
3384 PostQuitMessage(0);
3385
3386 ReleaseCapture();
3387 }
3388 break;
3389 case WM_MOUSEMOVE:
3390 {
3391 int button;
3392
3393 if (wParam & (MK_MBUTTON | MK_SHIFT))
3394 button = MIDDLE_DRAG;
3395 else if (wParam & MK_RBUTTON || is_alt_pressed())
3396 button = RIGHT_DRAG;
3397 else
3398 button = LEFT_DRAG;
3399
3400 if (!midend_process_key(fe->me,
3401 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3402 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3403 button))
3404 PostQuitMessage(0);
3405 }
3406 break;
3407 case WM_CHAR:
3408 if (!midend_process_key(fe->me, 0, 0, (unsigned char)wParam))
3409 PostQuitMessage(0);
3410 return 0;
3411 case WM_TIMER:
3412 if (fe->timer) {
3413 DWORD now = GetTickCount();
3414 float elapsed = (float) (now - fe->timer_last_tickcount) * 0.001F;
3415 midend_timer(fe->me, elapsed);
3416 fe->timer_last_tickcount = now;
3417 }
3418 return 0;
3419#ifndef _WIN32_WCE
3420 case WM_SIZING:
3421 {
3422 RECT *sr = (RECT *)lParam;
3423 int wx, wy, isedge = 0;
3424
3425 if (wParam == WMSZ_TOP ||
3426 wParam == WMSZ_RIGHT ||
3427 wParam == WMSZ_BOTTOM ||
3428 wParam == WMSZ_LEFT) isedge = 1;
3429 adjust_game_size(fe, sr, isedge, &wx, &wy);
3430
3431 /* Given the window size the puzzles constrain
3432 * us to, work out which edge we should be moving. */
3433 if (wParam == WMSZ_TOP ||
3434 wParam == WMSZ_TOPLEFT ||
3435 wParam == WMSZ_TOPRIGHT) {
3436 sr->top = sr->bottom - wy;
3437 } else {
3438 sr->bottom = sr->top + wy;
3439 }
3440 if (wParam == WMSZ_LEFT ||
3441 wParam == WMSZ_TOPLEFT ||
3442 wParam == WMSZ_BOTTOMLEFT) {
3443 sr->left = sr->right - wx;
3444 } else {
3445 sr->right = sr->left + wx;
3446 }
3447 return TRUE;
3448 }
3449 break;
3450#endif
3451 }
3452
3453 return DefWindowProc(hwnd, message, wParam, lParam);
3454}
3455
3456#ifdef _WIN32_WCE
3457static int FindPreviousInstance()
3458{
3459 /* Check if application is running. If it's running then focus on the window */
3460 HWND hOtherWnd = NULL;
3461
3462 hOtherWnd = FindWindow (wGameName, wGameName);
3463 if (hOtherWnd)
3464 {
3465 SetForegroundWindow (hOtherWnd);
3466 return TRUE;
3467 }
3468
3469 return FALSE;
3470}
3471#endif
3472
3473/*
3474 * Split a complete command line into argc/argv, attempting to do it
3475 * exactly the same way the Visual Studio C library would do it (so
3476 * that our console utilities, which receive argc and argv already
3477 * broken apart by the C library, will have their command lines
3478 * processed in the same way as the GUI utilities which get a whole
3479 * command line and must call this function).
3480 *
3481 * Does not modify the input command line.
3482 *
3483 * The final parameter (argstart) is used to return a second array
3484 * of char * pointers, the same length as argv, each one pointing
3485 * at the start of the corresponding element of argv in the
3486 * original command line. So if you get half way through processing
3487 * your command line in argc/argv form and then decide you want to
3488 * treat the rest as a raw string, you can. If you don't want to,
3489 * `argstart' can be safely left NULL.
3490 */
3491void split_into_argv(char *cmdline, int *argc, char ***argv,
3492 char ***argstart)
3493{
3494 char *p;
3495 char *outputline, *q;
3496 char **outputargv, **outputargstart;
3497 int outputargc;
3498
3499 /*
3500 * These argument-breaking rules apply to Visual Studio 7, which
3501 * is currently the compiler expected to be used for the Windows
3502 * port of my puzzles. Visual Studio 10 has different rules,
3503 * lacking the curious mod 3 behaviour of consecutive quotes
3504 * described below; I presume they fixed a bug. As and when we
3505 * migrate to a newer compiler, we'll have to adjust this to
3506 * match; however, for the moment we faithfully imitate in our GUI
3507 * utilities what our CLI utilities can't be prevented from doing.
3508 *
3509 * When I investigated this, at first glance the rules appeared to
3510 * be:
3511 *
3512 * - Single quotes are not special characters.
3513 *
3514 * - Double quotes are removed, but within them spaces cease
3515 * to be special.
3516 *
3517 * - Backslashes are _only_ special when a sequence of them
3518 * appear just before a double quote. In this situation,
3519 * they are treated like C backslashes: so \" just gives a
3520 * literal quote, \\" gives a literal backslash and then
3521 * opens or closes a double-quoted segment, \\\" gives a
3522 * literal backslash and then a literal quote, \\\\" gives
3523 * two literal backslashes and then opens/closes a
3524 * double-quoted segment, and so forth. Note that this
3525 * behaviour is identical inside and outside double quotes.
3526 *
3527 * - Two successive double quotes become one literal double
3528 * quote, but only _inside_ a double-quoted segment.
3529 * Outside, they just form an empty double-quoted segment
3530 * (which may cause an empty argument word).
3531 *
3532 * - That only leaves the interesting question of what happens
3533 * when one or more backslashes precedes two or more double
3534 * quotes, starting inside a double-quoted string. And the
3535 * answer to that appears somewhat bizarre. Here I tabulate
3536 * number of backslashes (across the top) against number of
3537 * quotes (down the left), and indicate how many backslashes
3538 * are output, how many quotes are output, and whether a
3539 * quoted segment is open at the end of the sequence:
3540 *
3541 * backslashes
3542 *
3543 * 0 1 2 3 4
3544 *
3545 * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y
3546 * --------+-----------------------------
3547 * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n
3548 * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n
3549 * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y
3550 * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n
3551 * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n
3552 * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y
3553 * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n
3554 * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n
3555 * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y
3556 * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n
3557 * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n
3558 *
3559 *
3560 * [Test fragment was of the form "a\\\"""b c" d.]
3561 *
3562 * There is very weird mod-3 behaviour going on here in the
3563 * number of quotes, and it even applies when there aren't any
3564 * backslashes! How ghastly.
3565 *
3566 * With a bit of thought, this extremely odd diagram suddenly
3567 * coalesced itself into a coherent, if still ghastly, model of
3568 * how things work:
3569 *
3570 * - As before, backslashes are only special when one or more
3571 * of them appear contiguously before at least one double
3572 * quote. In this situation the backslashes do exactly what
3573 * you'd expect: each one quotes the next thing in front of
3574 * it, so you end up with n/2 literal backslashes (if n is
3575 * even) or (n-1)/2 literal backslashes and a literal quote
3576 * (if n is odd). In the latter case the double quote
3577 * character right after the backslashes is used up.
3578 *
3579 * - After that, any remaining double quotes are processed. A
3580 * string of contiguous unescaped double quotes has a mod-3
3581 * behaviour:
3582 *
3583 * * inside a quoted segment, a quote ends the segment.
3584 * * _immediately_ after ending a quoted segment, a quote
3585 * simply produces a literal quote.
3586 * * otherwise, outside a quoted segment, a quote begins a
3587 * quoted segment.
3588 *
3589 * So, for example, if we started inside a quoted segment
3590 * then two contiguous quotes would close the segment and
3591 * produce a literal quote; three would close the segment,
3592 * produce a literal quote, and open a new segment. If we
3593 * started outside a quoted segment, then two contiguous
3594 * quotes would open and then close a segment, producing no
3595 * output (but potentially creating a zero-length argument);
3596 * but three quotes would open and close a segment and then
3597 * produce a literal quote.
3598 */
3599
3600 /*
3601 * First deal with the simplest of all special cases: if there
3602 * aren't any arguments, return 0,NULL,NULL.
3603 */
3604 while (*cmdline && isspace(*cmdline)) cmdline++;
3605 if (!*cmdline) {
3606 if (argc) *argc = 0;
3607 if (argv) *argv = NULL;
3608 if (argstart) *argstart = NULL;
3609 return;
3610 }
3611
3612 /*
3613 * This will guaranteeably be big enough; we can realloc it
3614 * down later.
3615 */
3616 outputline = snewn(1+strlen(cmdline), char);
3617 outputargv = snewn(strlen(cmdline)+1 / 2, char *);
3618 outputargstart = snewn(strlen(cmdline)+1 / 2, char *);
3619
3620 p = cmdline; q = outputline; outputargc = 0;
3621
3622 while (*p) {
3623 int quote;
3624
3625 /* Skip whitespace searching for start of argument. */
3626 while (*p && isspace(*p)) p++;
3627 if (!*p) break;
3628
3629 /* We have an argument; start it. */
3630 outputargv[outputargc] = q;
3631 outputargstart[outputargc] = p;
3632 outputargc++;
3633 quote = 0;
3634
3635 /* Copy data into the argument until it's finished. */
3636 while (*p) {
3637 if (!quote && isspace(*p))
3638 break; /* argument is finished */
3639
3640 if (*p == '"' || *p == '\\') {
3641 /*
3642 * We have a sequence of zero or more backslashes
3643 * followed by a sequence of zero or more quotes.
3644 * Count up how many of each, and then deal with
3645 * them as appropriate.
3646 */
3647 int i, slashes = 0, quotes = 0;
3648 while (*p == '\\') slashes++, p++;
3649 while (*p == '"') quotes++, p++;
3650
3651 if (!quotes) {
3652 /*
3653 * Special case: if there are no quotes,
3654 * slashes are not special at all, so just copy
3655 * n slashes to the output string.
3656 */
3657 while (slashes--) *q++ = '\\';
3658 } else {
3659 /* Slashes annihilate in pairs. */
3660 while (slashes >= 2) slashes -= 2, *q++ = '\\';
3661
3662 /* One remaining slash takes out the first quote. */
3663 if (slashes) quotes--, *q++ = '"';
3664
3665 if (quotes > 0) {
3666 /* Outside a quote segment, a quote starts one. */
3667 if (!quote) quotes--, quote = 1;
3668
3669 /* Now we produce (n+1)/3 literal quotes... */
3670 for (i = 3; i <= quotes+1; i += 3) *q++ = '"';
3671
3672 /* ... and end in a quote segment iff 3 divides n. */
3673 quote = (quotes % 3 == 0);
3674 }
3675 }
3676 } else {
3677 *q++ = *p++;
3678 }
3679 }
3680
3681 /* At the end of an argument, just append a trailing NUL. */
3682 *q++ = '\0';
3683 }
3684
3685 outputargv = sresize(outputargv, outputargc, char *);
3686 outputargstart = sresize(outputargstart, outputargc, char *);
3687
3688 if (argc) *argc = outputargc;
3689 if (argv) *argv = outputargv; else sfree(outputargv);
3690 if (argstart) *argstart = outputargstart; else sfree(outputargstart);
3691}
3692
3693int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
3694{
3695 MSG msg;
3696 char *error = NULL;
3697 const game *gg;
3698 frontend *fe;
3699 midend *me;
3700 int argc;
3701 char **argv;
3702
3703 split_into_argv(cmdline, &argc, &argv, NULL);
3704
3705#ifdef _WIN32_WCE
3706 MultiByteToWideChar (CP_ACP, 0, CLASSNAME, -1, wClassName, 256);
3707 if (FindPreviousInstance ())
3708 return 0;
3709#endif
3710
3711 InitCommonControls();
3712
3713 if (!prev) {
3714 WNDCLASS wndclass;
3715
3716 wndclass.style = 0;
3717 wndclass.lpfnWndProc = WndProc;
3718 wndclass.cbClsExtra = 0;
3719 wndclass.cbWndExtra = 0;
3720 wndclass.hInstance = inst;
3721 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(200));
3722#ifndef _WIN32_WCE
3723 if (!wndclass.hIcon) /* in case resource file is absent */
3724 wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION);
3725#endif
3726 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
3727 wndclass.hbrBackground = NULL;
3728 wndclass.lpszMenuName = NULL;
3729#ifdef _WIN32_WCE
3730 wndclass.lpszClassName = wClassName;
3731#else
3732 wndclass.lpszClassName = CLASSNAME;
3733#endif
3734
3735 RegisterClass(&wndclass);
3736 }
3737
3738 while (*cmdline && isspace((unsigned char)*cmdline))
3739 cmdline++;
3740
3741 init_help();
3742
3743#ifdef COMBINED
3744 gg = gamelist[0];
3745 if (argc > 0) {
3746 int i;
3747 for (i = 0; i < gamecount; i++) {
3748 const char *p = gamelist[i]->name;
3749 char *q = argv[0];
3750 while (*p && *q) {
3751 if (isspace((unsigned char)*p)) {
3752 while (*q && isspace((unsigned char)*q))
3753 q++;
3754 } else {
3755 if (tolower((unsigned char)*p) !=
3756 tolower((unsigned char)*q))
3757 break;
3758 q++;
3759 }
3760 p++;
3761 }
3762 if (!*p) {
3763 gg = gamelist[i];
3764 --argc;
3765 ++argv;
3766 break;
3767 }
3768 }
3769 }
3770#else
3771 gg = &thegame;
3772#endif
3773
3774 fe = frontend_new(inst);
3775 me = midend_for_new_game(fe, gg, argc > 0 ? argv[0] : NULL,
3776 TRUE, TRUE, &error);
3777 if (!me) {
3778 char buf[128];
3779#ifdef COMBINED
3780 sprintf(buf, "Puzzles Error");
3781#else
3782 sprintf(buf, "%.100s Error", gg->name);
3783#endif
3784 MessageBox(NULL, error, buf, MB_OK|MB_ICONERROR);
3785 sfree(error);
3786 return 1;
3787 }
3788 fe_set_midend(fe, me);
3789 show_window(fe);
3790
3791 while (GetMessage(&msg, NULL, 0, 0)) {
3792 DispatchMessage(&msg);
3793 }
3794
3795 DestroyWindow(fe->hwnd);
3796 cleanup_help();
3797
3798 return msg.wParam;
3799}
3800/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/src/winiss.pl b/apps/plugins/puzzles/src/winiss.pl
new file mode 100755
index 0000000000..eca02d3b15
--- /dev/null
+++ b/apps/plugins/puzzles/src/winiss.pl
@@ -0,0 +1,79 @@
1#!/usr/bin/perl
2
3# Perl script to generate an Inno Setup installer script for
4# Puzzles. This has to be scripted so that it can read gamedesc.txt
5# and automatically adjust to the current available set of puzzles.
6
7# Usage:
8#
9# $ ./winiss.pl 20140922.sdfsdf gamedesc.txt > puzzles.iss
10#
11# where the first argument is the version number which will be encoded
12# in the installer's version indicators. The first component of that
13# version number will be expected to be a YYYYMMDD-format date.
14
15use warnings;
16use Time::Local;
17
18$ver = shift @ARGV;
19
20# Parse the date out of $ver, and convert it into an integer number of
21# days since an arbitrary epoch. This number is used for the Windows
22# version resource (which wants a monotonic 16-bit integer). The epoch
23# is chosen so that the first build using this date-based mechanism
24# has a higher number than the last build in which that number was
25# derived from a Subversion revision.
26die "bad date format" if $ver !~ /^(\d{4})(\d{2})(\d{2})/;
27$date = timegm(0,0,0,$3,$2-1,$1);
28$integer_date = int($date / 86400) - 6000;
29
30$desc = shift @ARGV;
31open DESC, "<", $desc;
32while (<DESC>) {
33 chomp;
34 @_ = split /:/;
35 push @exes, $_[1];
36 $names{$_[1]} = $_[2];
37}
38close DESC;
39
40print '; -*- no -*-'."\n";
41print ';'."\n";
42print '; -- Inno Setup installer script for Puzzles.'."\n";
43print ''."\n";
44print '[Setup]'."\n";
45print 'AppName=Simon Tatham\'s Portable Puzzle Collection'."\n";
46print 'AppVerName=Puzzles version '.$ver."\n";
47print 'VersionInfoTextVersion=Version '.$ver."\n";
48print 'AppVersion=r'.$ver."\n";
49print 'VersionInfoVersion=0.0.'.$integer_date.'.0'."\n";
50print 'AppPublisher=Simon Tatham'."\n";
51print 'AppPublisherURL=http://www.chiark.greenend.org.uk/~sgtatham/puzzles/'."\n";
52print 'DefaultDirName={pf}\Simon Tatham\'s Portable Puzzle Collection'."\n";
53print 'DefaultGroupName=Simon Tatham\'s Puzzles'."\n";
54# print 'SetupIconFile=fixmethinkoneup.ico'."\n";
55# print 'UninstallDisplayIcon={app}\fixmethinkoneup.exe'."\n";
56print 'ChangesAssociations=no'."\n";
57print 'Compression=zip/9'."\n";
58print 'AllowNoIcons=yes'."\n";
59print 'OutputBaseFilename=installer'."\n";
60print ''."\n";
61print '[Files]'."\n";
62for $exe (@exes) {
63 print 'Source: "'.$exe.'"; DestDir: "{app}"; Flags: promptifolder replacesameversion uninsrestartdelete'."\n";
64}
65print 'Source: "website.url"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n";
66print 'Source: "puzzles.chm"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n";
67print 'Source: "puzzles.hlp"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n";
68print 'Source: "puzzles.cnt"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n";
69print 'Source: "LICENCE"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n";
70print ''."\n";
71print '[Icons]'."\n";
72for $exe (@exes) {
73 print 'Name: "{group}\\'.$names{$exe}.'"; Filename: "{app}\\'.$exe.'"'."\n";
74}
75print '; We have to fall back from the .chm to the older .hlp file on some Windows'."\n";
76print '; versions.'."\n";
77print 'Name: "{group}\Puzzles Manual"; Filename: "{app}\puzzles.chm"; MinVersion: 4.1,5.0'."\n";
78print 'Name: "{group}\Puzzles Manual"; Filename: "{app}\puzzles.hlp"; OnlyBelowVersion: 4.1,5.0'."\n";
79print 'Name: "{group}\Puzzles Web Site"; Filename: "{app}\website.url"'."\n";
diff --git a/apps/plugins/puzzles/src/winwix.mc b/apps/plugins/puzzles/src/winwix.mc
new file mode 100644
index 0000000000..1a1e620b82
--- /dev/null
+++ b/apps/plugins/puzzles/src/winwix.mc
@@ -0,0 +1,334 @@
1% # Source code for the Puzzles installer.
2% #
3% # The installer is built using WiX, but this file itself is not valid
4% # XML input to WiX's candle.exe; instead, this is a template intended
5% # to be processed through the standalone 'mason.pl' script provided
6% # with Perl's Mason module.
7
8<%class>
9has 'version' => (required => 1);
10has 'descfile' => (required => 1);
11</%class>
12
13<?xml version="1.0" encoding="utf-8"?>
14
15<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
16
17% # Product tag. The Id component is set to "*", which causes WiX to
18% # make up a new GUID every time it's run, whereas UpgradeCode is set
19% # to a fixed GUID. This combination allows Windows to
20% # recognise each new installer as different (because of Id)
21% # versions of the same underlying thing (because of the common
22% # UpgradeCode).
23 <Product
24 Name="Simon Tatham's Portable Puzzle Collection"
25 Manufacturer="Simon Tatham"
26 Id="*"
27 UpgradeCode="<% invent_guid('upgradecode') %>"
28 Language="1033" Codepage="1252" Version="<% $winver %>">
29
30% # We force the install scope to perMachine, largely because I
31% # don't really understand how to make it usefully switchable
32% # between the two. If anyone is a WiX expert and does want to
33% # install Puzzles locally in a user account, I hope they'll send a
34% # well explained patch!
35 <Package Id="*" Keywords="Installer"
36 Description="Simon Tatham's Portable Puzzle Collection installer, version <% $.version %>"
37 Manufacturer="Simon Tatham"
38 InstallerVersion="100" Languages="1033"
39 Compressed="yes" SummaryCodepage="1252"
40 InstallScope="perMachine" />
41
42% # Permit installing an arbitrary one of these installers
43% # over the top of an existing one, whether it's an upgrade or a
44% # downgrade.
45% #
46% # Setting the REINSTALLMODE property to "amus" (from its default
47% # of "omus") forces every component replaced by a different
48% # version of the installer to be _actually_ reinstalled; the 'o'
49% # flag in the default setting breaks the downgrade case by
50% # causing Windows to disallow installation of an older version
51% # over the top of a newer one - and to do so _silently_, so the
52% # installer claims to have worked fine but the files that would have
53% # been downgraded aren't there.
54 <MajorUpgrade AllowDowngrades="yes" MigrateFeatures="yes" />
55 <Property Id="REINSTALLMODE" Value="amus"/>
56
57% # Boilerplate
58 <Media Id="1" Cabinet="puzzles.cab" EmbedCab="yes" />
59
60% # The actual directory structure and list of 'components'
61% # (individual files or shortcuts or additions to PATH) that are
62% # installed.
63 <Directory Id="TARGETDIR" Name="SourceDir">
64 <Directory Id="ProgramFilesFolder" Name="PFiles">
65 <Directory Id="INSTALLDIR" Name="Simon Tatham's Portable Puzzle Collection">
66
67% # The following components all install things in the main
68% # install directory (implicitly, by being nested where
69% # they are in the XML structure). Most of them also put a
70% # shortcut in a subdir of the Start menu, though some of
71% # the more obscure things like LICENCE are just there for
72% # the sake of being _somewhere_ and don't rate a shortcut.
73
74<%method file_component ($filename, $shortcutname)>
75% my $filename_id = file_component_name($filename);
76<Component Id="File_Component_<% $filename_id %>"
77 Guid="<% invent_guid('file:' . $filename) %>">
78 <File Id="File_<% $filename_id %>"
79 Source="<% $filename %>" KeyPath="yes">
80% if (defined $shortcutname) {
81 <Shortcut Id="startmenu_<% $filename_id %>"
82 Directory="ProgramMenuDir" WorkingDirectory="INSTALLDIR"
83 Name="<% $shortcutname %>" Advertise="no" />
84% }
85 </File>
86</Component>
87</%method>
88
89% for my $exe (@exes) {
90<% $.file_component($exe, $names{$exe}) %>
91% }
92
93<% $.file_component("puzzles.chm", "Puzzles Manual") %>
94<% $.file_component("website.url", "Puzzles Web Site") %>
95<% $.file_component("LICENCE") %>
96
97 </Directory>
98 </Directory>
99
100% # This component doesn't actually install anything, but it
101% # arranges for the Start Menu _directory_ to be removed again
102% # on uninstall. All the actual shortcuts inside the directory
103% # are placed by code above here.
104 <Directory Id="ProgramMenuFolder" Name="Programs">
105 <Directory Id="ProgramMenuDir" Name="Simon Tatham's Portable Puzzle Collection">
106 <Component Id="ProgramMenuDir"
107 Guid="<% invent_guid('programmenudir') %>">
108 <RemoveFolder Id="ProgramMenuDir" On="uninstall" />
109 <RegistryValue Root="HKLM"
110 Key="Software\SimonTatham\Puzzles\StartMenu"
111 Type="string" Value="" KeyPath="yes" />
112 </Component>
113 </Directory>
114 </Directory>
115 </Directory>
116
117% # Detect an installation of Puzzles made by the old Inno Setup
118% # installer, and refuse to run if we find one. I don't know what
119% # would happen if you tried anyway, but since they install files
120% # at the same pathnames, it surely wouldn't end well.
121% #
122% # It could be argued that a better approach would be to actually
123% # _launch_ the Inno Setup uninstaller automatically at this
124% # point (prompting the user first, of course), but I'm not
125% # nearly skilled enough with WiX to know how, or even if it's
126% # feasible.
127 <Property Id="LEGACYINNOSETUPINSTALLERNATIVE32PROPERTY">
128 <RegistrySearch
129 Id="LegacyInnoSetupInstallerNative32RegSearch"
130 Root="HKLM"
131 Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Simon Tatham's Portable Puzzle Collection_is1"
132 Name="QuietUninstallString" Type="raw" />
133 </Property>
134 <Property Id="LEGACYINNOSETUPINSTALLER32ON64PROPERTY">
135 <RegistrySearch
136 Id="LegacyInnoSetupInstaller32On64RegSearch"
137 Root="HKLM"
138 Key="SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Simon Tatham's Portable Puzzle Collection_is1"
139 Name="QuietUninstallString" Type="raw" />
140 </Property>
141 <Condition Message="A version of this software is already installed on this system using its old Inno Setup installer. Please uninstall that before running the new installer.">
142 <![CDATA[Installed OR
143 (LEGACYINNOSETUPINSTALLERNATIVE32PROPERTY = "" AND
144 LEGACYINNOSETUPINSTALLER32ON64PROPERTY = "")]]>
145 </Condition>
146
147% # Separate the installation into 'features', which are parts of
148% # the install that can be chosen separately. Trivial: there is only
149% # one feature here.
150 <Feature Id="FilesFeature" Level="1" Absent="disallow" AllowAdvertise="no"
151 Title="Install puzzle games">
152% for my $exe (@exes) {
153 <ComponentRef Id="File_Component_<% file_component_name($exe) %>" />
154% }
155 <ComponentRef Id="File_Component_<% file_component_name("puzzles.chm") %>" />
156 <ComponentRef Id="File_Component_<% file_component_name("website.url") %>" />
157 <ComponentRef Id="File_Component_<% file_component_name("LICENCE") %>" />
158 <ComponentRef Id="ProgramMenuDir" />
159 </Feature>
160
161% # Installer user interface.
162% #
163% # Basically like WixUI_InstallDir, only I've ripped out the EULA.
164 <UIRef Id="WixUI_Common" />
165
166 <UI>
167 <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
168 <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
169 <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
170
171 <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
172 <Property Id="WixUI_Mode" Value="InstallDir" />
173
174 <DialogRef Id="BrowseDlg" />
175 <DialogRef Id="DiskCostDlg" />
176 <DialogRef Id="ErrorDlg" />
177 <DialogRef Id="FatalError" />
178 <DialogRef Id="FilesInUse" />
179 <DialogRef Id="MsiRMFilesInUse" />
180 <DialogRef Id="PrepareDlg" />
181 <DialogRef Id="ProgressDlg" />
182 <DialogRef Id="ResumeDlg" />
183 <DialogRef Id="UserExit" />
184
185 <Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
186 <Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
187
188 <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
189
190 <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg">NOT Installed</Publish>
191 <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed</Publish>
192
193 <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
194 <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
195 <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
196 <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
197 <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"</Publish>
198 <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
199 <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
200
201 <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1">NOT Installed</Publish>
202 <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
203 <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">Installed AND PATCH</Publish>
204
205 <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
206
207 <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
208 <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
209 <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
210
211% # This ARPNOMODIFY flag prohibits changing the set of
212% # installed features, because we don't have any.
213 <Property Id="ARPNOMODIFY" Value="1" />
214 </UI>
215
216% # Glue: tell the install dir part of the UI what id my actual
217% # install dir is known by. Otherwise the former won't know how
218% # to alter the setting of the latter.
219 <Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
220
221% # Include my custom installer artwork, created in Buildscr. FIXME:
222% # create some!
223% # <WixVariable Id="WixUIDialogBmp" Value="msidialog.bmp" />
224% # <WixVariable Id="WixUIBannerBmp" Value="msibanner.bmp" />
225
226 </Product>
227</Wix>
228
229<%init>
230
231use Digest::SHA qw(sha512_hex);
232use Time::Local;
233
234die "bad date format" if $.version !~ /^(\d{4})(\d{2})(\d{2})/;
235my $date = timegm(0,0,0,$3,$2-1,$1);
236my $integer_date = int($date / 86400) - 6000;
237my $winver = sprintf "0.0.%d.0", $integer_date;
238
239my @exes;
240my %names;
241{
242 open my $descfh, "<", $.descfile or die "$.descfile: open: $!\n";
243 while (<$descfh>) {
244 chomp;
245 my @fields = split /:/;
246 push @exes, $fields[1];
247 $names{$fields[1]} = $fields[2];
248 }
249 close $descfh;
250}
251
252sub file_component_name($) {
253 my ($id) = @_;
254 $id =~ s!.*\\!!;
255 $id =~ y!A-Za-z0-9!_!cs;
256 return $id;
257}
258
259sub invent_guid($) {
260 my ($name) = @_;
261
262 # Invent a GUID. We'll need a lot of these in this installer - one
263 # per puzzle game and a few standard ones - and we'd like to
264 # arrange for them to be stable between versions of the installer,
265 # while not having to _manually_ invent one every time.
266 #
267 # So we generate our GUIDs by hashing a pile of fixed (but
268 # originally randomly generated) data with an identifying string
269 # that acts as source for the particular GUID. For example,
270 # hashing (fixed_random_data || "upgradecode") produces the GUID
271 # used as the upgrade code, and (fixed_random_data ||
272 # "installfile:" || filename) gives the GUID for an MSI component
273 # that installs a specific file.
274 #
275 # Hashing _just_ the id strings would clearly be cheating (it's
276 # quite conceivable that someone might hash the same string for
277 # another reason and so generate a colliding GUID), but hashing a
278 # whole SHA-512 data block of random gibberish as well should make
279 # these GUIDs pseudo-random enough to not collide with anyone
280 # else's.
281
282 my $randdata = pack "N*",
283 0xCCAA8D31,0x42931BD9,0xA9D9878A,0x72E4FB9C,0xEA9B11DE,0x4FF17AEC,
284 0x1AFA2DEC,0xB662640A,0x780143F5,0xBFFFF0FC,0x01CB46CF,0x832AE842,
285 0xBCCDDA12,0x4DB24889,0xEC7A9BCD,0xBCCF70ED,0x85800659,0x8ABA9524,
286 0xE484F8D6,0x5CBE55B3,0x95AD9B3D,0x0555F432,0x46737F89,0xE981471C,
287 0x4B3419AD,0xD4E49DF4,0xB3EF69DE,0x2A7E391E,0xF3C3D370,0x3EA5E9FC,
288 0xB35A57ED,0x52B21099,0x9BD99092,0x7B5097AE,0x4DBE59BD,0x2FCC709B,
289 0xC555A779,0x4AE2E5AB,0xB7C74314,0x7F9194CF,0x8FFBCA88,0x46263306,
290 0x4C714DF7,0x07FE8CEE,0x28974885,0x0869865D,0xBB5B0EA4,0x4064A928,
291 0x28C41910,0x07030758,0x19E66319,0x050C9D4E,0xD79A37FB,0xF232D98B,
292 0x0C3E4C25,0xC94F865B,0xB6D86BED,0x87DB718D,0xC65D4C43,0x7C8BBF6A,
293 0x9DFDD26A,0x8C478976,0x53E47640,0x263E04AA,0xDD7C5456,0x766BDF50,
294 0x86946E34,0xE80D8DE3,0xFB92949E,0x691FDAD0,0x96AB210D,0xB278D60B,
295 0x71C7DC6B,0xD49846FC,0x38953F66,0x39777D72,0x4A0F80E5,0xFE1DD1A4,
296 0xDA9D191A,0xA32844AD,0x171BFBCC,0xA7D556F6,0xF9F6D944,0xF9687060,
297 0xDDDB81D0,0xE9AF4F2F,0xEF84685A,0x8A172492,0x50615EFC,0x20817881,
298 0xC3E507E5,0x33189771,0xB9E2EBBD,0x2AAE34A3,0x8D3E7486,0x0A448F13,
299 0x94F92A81,0x5150A34F,0x5ED50563,0xAD801A42,0xD0617AFA,0xB78F85F7,
300 0x0019D384,0xF0F1C06F,0x6EF8D5B3,0x38092D09,0xC87CD4B3,0xE9D8C84F,
301 0xE036648C,0xF2500BD9,0xCF069B5C,0x835326BA,0xCD107B6A,0x64F152B3,
302 0xA9663E24,0x33ED5E08,0xC3B24F7E,0xA83205C8,0xA0560F30,0xDFF1226E,
303 0xF1A404B7,0x9C2B4EF2,0x62802F88,0xE393A05F,0xC7F72F7B,0x1CD989BD,
304 0x725AB516,0xA84F7E39,0xACC3572A,0x820ACB2D,0xAFF5BF06,0x476A2405,
305 0x90953482,0x8E8035E1,0x1FB95F6E,0x01FD6766,0x1E63D78E,0xD7D42220,
306 0x188D23E4,0x1941BCC5,0xEE1E6487,0x6E197F82,0x32772265,0x9B79D0C8,
307 0xB4B617A1,0xCD2475B4,0xDE0F410B,0xE9CF69E4,0x831AC9A4,0xD549A00E,
308 0x12ECC561,0x3D636A43,0x1FFFC99A,0xF79401C5,0xAA1D8251,0x84D29609,
309 0x5464CB71,0xB28AAE5A,0x4AD934FC,0x347E8A5D,0xC87BCBA0,0x67172E33,
310 0xEC70E245,0x4289A9EF,0xA8AF6BA5,0x1528FE0C,0xA87CBFF8,0x79AE1554,
311 0xBD59DB9E,0xF1879F94,0x14D7E9F6,0x85196447,0xC4363A67,0x7E02A325,
312 0x54051E05,0xABAFE646,0x65D5DF96,0xD3F8173B,0x09D475E7,0x9BF7BD0C,
313 0x2DAF371A,0x793D063C,0xA68FD47B,0xBE2500A7,0x0D5071C4,0x08384AC8,
314 0xF6CFE74E,0x124A5086,0x03475917,0x47267765,0x56F7DF31,0xE5696381,
315 0xEB2B4668,0x78345B5B,0x6E2AFC0F,0x3AD0D43B,0x5C3C2BC9,0x833AB4A0,
316 0x1DE2CDBF,0x4DDDCF58,0xEA25D69B,0x36E9B3B0,0xC8B11465,0x066A997E,
317 0x9D51C7CD,0x8C6AE686,0xAFB06CE6,0xCC3F3017,0x6E4E4CC0,0x85A34875,
318 0x498FE759,0xC24B6332,0xEBCD2257,0xE70FC659,0x439EC788,0xB47F2A06,
319 0x696EE8A7,0xF70A31B8,0xECD840F7,0x80AE5E7A,0xC6EDF8AE,0x8165EAFD,
320 0x5DAE5ADE,0x9FFD39CE,0xFC6B4C23,0x02BCA024,0xC1497A68,0xD18153EF,
321 0xD787EA51,0x91386720,0xBF6E2200,0x98638391,0x144B9148,0x9A554DE1,
322 0xA940DC7F,0x37C08265,0x7B782C60,0xC081FDD7,0x62B47201,0x43427563,
323 0x1B66E983,0x3DAC0305,0x21E9DEA8,0xA9490EE0,0xE2EFD37D,0x3501F306,
324 0x16361BD5,0x668B219D,0x17177844,0x3861A9A4,0x222403B2,0xB29F6714,
325 0x7A2A938A,0xBC797418,0x3B241624,0x9163C3F2;
326 my $digest = sha512_hex($name . "\0" . $randdata);
327 return sprintf("%s-%s-%04x-%04x-%s",
328 substr($digest,0,8),
329 substr($digest,8,4),
330 0x4000 | (0xFFF & hex(substr($digest,12,4))),
331 0x8000 | (0x3FFF & hex(substr($digest,16,4))),
332 substr($digest,20,12));
333}
334</%init>