summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/CATEGORIES40
-rw-r--r--apps/plugins/SOURCES4
-rw-r--r--apps/plugins/SUBDIRS1
-rw-r--r--apps/plugins/puzzles/Buildscr194
-rw-r--r--apps/plugins/puzzles/CHECKLST.txt70
-rw-r--r--apps/plugins/puzzles/LICENCE25
-rw-r--r--apps/plugins/puzzles/PuzzleApplet.java617
-rw-r--r--apps/plugins/puzzles/README54
-rw-r--r--apps/plugins/puzzles/Recipe157
-rw-r--r--apps/plugins/puzzles/SOURCES26
-rwxr-xr-xapps/plugins/puzzles/benchmark.pl197
-rwxr-xr-xapps/plugins/puzzles/benchmark.sh27
-rw-r--r--apps/plugins/puzzles/blackbox.R19
-rw-r--r--apps/plugins/puzzles/blackbox.c1543
-rw-r--r--apps/plugins/puzzles/bridges.R21
-rw-r--r--apps/plugins/puzzles/bridges.c3262
-rw-r--r--apps/plugins/puzzles/chm.but21
-rw-r--r--apps/plugins/puzzles/chm.css7
-rw-r--r--apps/plugins/puzzles/combi.c110
-rw-r--r--apps/plugins/puzzles/configure.ac85
-rw-r--r--apps/plugins/puzzles/cube.R19
-rw-r--r--apps/plugins/puzzles/cube.c1774
-rwxr-xr-xapps/plugins/puzzles/desktop.pl52
-rw-r--r--apps/plugins/puzzles/devel.but4777
-rw-r--r--apps/plugins/puzzles/divvy.c781
-rw-r--r--apps/plugins/puzzles/dominosa.R21
-rw-r--r--apps/plugins/puzzles/dominosa.c1748
-rw-r--r--apps/plugins/puzzles/drawing.c351
-rw-r--r--apps/plugins/puzzles/dsf.c192
-rw-r--r--apps/plugins/puzzles/emcc.c867
-rw-r--r--apps/plugins/puzzles/emcclib.js757
-rw-r--r--apps/plugins/puzzles/emccpre.js364
-rw-r--r--apps/plugins/puzzles/emccx.json29
-rw-r--r--apps/plugins/puzzles/fifteen.R22
-rw-r--r--apps/plugins/puzzles/fifteen.c1215
-rw-r--r--apps/plugins/puzzles/filling.R24
-rw-r--r--apps/plugins/puzzles/filling.c2179
-rw-r--r--apps/plugins/puzzles/findloop.c500
-rw-r--r--apps/plugins/puzzles/flip.R21
-rw-r--r--apps/plugins/puzzles/flip.c1349
-rw-r--r--apps/plugins/puzzles/flood.R19
-rw-r--r--apps/plugins/puzzles/flood.c1372
-rw-r--r--apps/plugins/puzzles/galaxies.R28
-rw-r--r--apps/plugins/puzzles/galaxies.c3995
-rw-r--r--apps/plugins/puzzles/grid.c2896
-rw-r--r--apps/plugins/puzzles/grid.h132
-rw-r--r--apps/plugins/puzzles/gtk.c3215
-rw-r--r--apps/plugins/puzzles/guess.R19
-rw-r--r--apps/plugins/puzzles/guess.c1518
-rw-r--r--apps/plugins/puzzles/html/blackbox.html16
-rw-r--r--apps/plugins/puzzles/html/bridges.html13
-rw-r--r--apps/plugins/puzzles/html/cube.html14
-rw-r--r--apps/plugins/puzzles/html/dominosa.html10
-rw-r--r--apps/plugins/puzzles/html/fifteen.html6
-rw-r--r--apps/plugins/puzzles/html/filling.html12
-rw-r--r--apps/plugins/puzzles/html/flip.html10
-rw-r--r--apps/plugins/puzzles/html/flood.html8
-rw-r--r--apps/plugins/puzzles/html/galaxies.html11
-rw-r--r--apps/plugins/puzzles/html/group.html52
-rw-r--r--apps/plugins/puzzles/html/guess.html12
-rw-r--r--apps/plugins/puzzles/html/inertia.html14
-rwxr-xr-xapps/plugins/puzzles/html/javapage.pl104
-rwxr-xr-xapps/plugins/puzzles/html/jspage.pl120
-rw-r--r--apps/plugins/puzzles/html/keen.html15
-rw-r--r--apps/plugins/puzzles/html/lightup.html10
-rw-r--r--apps/plugins/puzzles/html/loopy.html13
-rw-r--r--apps/plugins/puzzles/html/magnets.html17
-rw-r--r--apps/plugins/puzzles/html/map.html15
-rw-r--r--apps/plugins/puzzles/html/mines.html18
-rw-r--r--apps/plugins/puzzles/html/net.html17
-rw-r--r--apps/plugins/puzzles/html/netslide.html14
-rw-r--r--apps/plugins/puzzles/html/palisade.html11
-rw-r--r--apps/plugins/puzzles/html/pattern.html12
-rw-r--r--apps/plugins/puzzles/html/pearl.html13
-rw-r--r--apps/plugins/puzzles/html/pegs.html8
-rw-r--r--apps/plugins/puzzles/html/range.html21
-rw-r--r--apps/plugins/puzzles/html/rect.html10
-rw-r--r--apps/plugins/puzzles/html/samegame.html14
-rw-r--r--apps/plugins/puzzles/html/signpost.html14
-rw-r--r--apps/plugins/puzzles/html/singles.html11
-rw-r--r--apps/plugins/puzzles/html/sixteen.html8
-rw-r--r--apps/plugins/puzzles/html/slant.html9
-rw-r--r--apps/plugins/puzzles/html/solo.html20
-rw-r--r--apps/plugins/puzzles/html/tents.html20
-rw-r--r--apps/plugins/puzzles/html/towers.html22
-rw-r--r--apps/plugins/puzzles/html/tracks.html19
-rw-r--r--apps/plugins/puzzles/html/twiddle.html15
-rw-r--r--apps/plugins/puzzles/html/undead.html22
-rw-r--r--apps/plugins/puzzles/html/unequal.html14
-rw-r--r--apps/plugins/puzzles/html/unruly.html11
-rw-r--r--apps/plugins/puzzles/html/untangle.html5
-rw-r--r--apps/plugins/puzzles/icons/Makefile153
-rw-r--r--apps/plugins/puzzles/icons/blackbox.sav27
-rw-r--r--apps/plugins/puzzles/icons/bridges.sav72
-rwxr-xr-xapps/plugins/puzzles/icons/cicon.pl27
-rwxr-xr-xapps/plugins/puzzles/icons/crop.sh37
-rw-r--r--apps/plugins/puzzles/icons/cube.sav22
-rw-r--r--apps/plugins/puzzles/icons/dominosa.sav53
-rw-r--r--apps/plugins/puzzles/icons/fifteen.sav74
-rw-r--r--apps/plugins/puzzles/icons/filling.sav38
-rw-r--r--apps/plugins/puzzles/icons/flip.sav20
-rw-r--r--apps/plugins/puzzles/icons/flood.sav14
-rw-r--r--apps/plugins/puzzles/icons/galaxies.sav51
-rw-r--r--apps/plugins/puzzles/icons/guess.sav15
-rwxr-xr-xapps/plugins/puzzles/icons/icon.pl270
-rw-r--r--apps/plugins/puzzles/icons/inertia.sav30
-rw-r--r--apps/plugins/puzzles/icons/keen.sav62
-rw-r--r--apps/plugins/puzzles/icons/lightup.sav24
-rw-r--r--apps/plugins/puzzles/icons/loopy.sav120
-rw-r--r--apps/plugins/puzzles/icons/magnets.sav33
-rw-r--r--apps/plugins/puzzles/icons/map.sav27
-rw-r--r--apps/plugins/puzzles/icons/mines.sav67
-rw-r--r--apps/plugins/puzzles/icons/net.sav53
-rw-r--r--apps/plugins/puzzles/icons/netslide.sav47
-rw-r--r--apps/plugins/puzzles/icons/palisade.sav50
-rw-r--r--apps/plugins/puzzles/icons/pattern.sav29
-rw-r--r--apps/plugins/puzzles/icons/pearl.sav23
-rw-r--r--apps/plugins/puzzles/icons/pegs.sav16
-rw-r--r--apps/plugins/puzzles/icons/range.sav36
-rw-r--r--apps/plugins/puzzles/icons/rect.sav17
-rw-r--r--apps/plugins/puzzles/icons/samegame.sav34
-rwxr-xr-xapps/plugins/puzzles/icons/screenshot.sh25
-rw-r--r--apps/plugins/puzzles/icons/signpost.sav23
-rw-r--r--apps/plugins/puzzles/icons/singles.sav45
-rw-r--r--apps/plugins/puzzles/icons/sixteen.sav39
-rw-r--r--apps/plugins/puzzles/icons/slant.sav51
-rw-r--r--apps/plugins/puzzles/icons/solo.sav36
-rwxr-xr-xapps/plugins/puzzles/icons/square.pl95
-rw-r--r--apps/plugins/puzzles/icons/tents.sav32
-rw-r--r--apps/plugins/puzzles/icons/towers.sav26
-rw-r--r--apps/plugins/puzzles/icons/tracks.sav31
-rw-r--r--apps/plugins/puzzles/icons/twiddle.sav35
-rw-r--r--apps/plugins/puzzles/icons/undead.sav14
-rw-r--r--apps/plugins/puzzles/icons/unequal.sav25
-rw-r--r--apps/plugins/puzzles/icons/unruly.sav22
-rw-r--r--apps/plugins/puzzles/icons/untangle.sav16
-rw-r--r--apps/plugins/puzzles/icons/win16pal.xpm23
-rw-r--r--apps/plugins/puzzles/inertia.R19
-rw-r--r--apps/plugins/puzzles/inertia.c2249
-rw-r--r--apps/plugins/puzzles/keen.R25
-rw-r--r--apps/plugins/puzzles/keen.c2479
-rw-r--r--apps/plugins/puzzles/keymaps.h206
-rw-r--r--apps/plugins/puzzles/latin.c1436
-rw-r--r--apps/plugins/puzzles/latin.h122
-rw-r--r--apps/plugins/puzzles/laydomino.c291
-rw-r--r--apps/plugins/puzzles/lightup.R24
-rw-r--r--apps/plugins/puzzles/lightup.c2405
-rw-r--r--apps/plugins/puzzles/list.c55
-rw-r--r--apps/plugins/puzzles/loopgen.c536
-rw-r--r--apps/plugins/puzzles/loopgen.h35
-rw-r--r--apps/plugins/puzzles/loopy.R31
-rw-r--r--apps/plugins/puzzles/loopy.c3688
-rw-r--r--apps/plugins/puzzles/magnets.R24
-rw-r--r--apps/plugins/puzzles/magnets.c2641
-rwxr-xr-xapps/plugins/puzzles/makedist.sh47
-rw-r--r--apps/plugins/puzzles/malloc.c61
-rw-r--r--apps/plugins/puzzles/map.R24
-rw-r--r--apps/plugins/puzzles/map.c3340
-rw-r--r--apps/plugins/puzzles/maxflow.c461
-rw-r--r--apps/plugins/puzzles/maxflow.h95
-rw-r--r--apps/plugins/puzzles/midend.c2136
-rw-r--r--apps/plugins/puzzles/mines.R24
-rw-r--r--apps/plugins/puzzles/mines.c3250
-rw-r--r--apps/plugins/puzzles/misc.c363
-rwxr-xr-xapps/plugins/puzzles/mkauto.sh2
-rwxr-xr-xapps/plugins/puzzles/mkfiles.pl1807
-rw-r--r--apps/plugins/puzzles/nestedvm.c432
-rw-r--r--apps/plugins/puzzles/net.R23
-rw-r--r--apps/plugins/puzzles/net.c3210
-rw-r--r--apps/plugins/puzzles/netslide.R21
-rw-r--r--apps/plugins/puzzles/netslide.c1893
-rw-r--r--apps/plugins/puzzles/no-icon.c8
-rw-r--r--apps/plugins/puzzles/noicon.rc11
-rw-r--r--apps/plugins/puzzles/nullfe.c69
-rw-r--r--apps/plugins/puzzles/nullgame.R12
-rw-r--r--apps/plugins/puzzles/nullgame.c303
-rw-r--r--apps/plugins/puzzles/obfusc.c126
-rw-r--r--apps/plugins/puzzles/osx-help.but14
-rw-r--r--apps/plugins/puzzles/osx-info.plist34
-rw-r--r--apps/plugins/puzzles/osx.icnsbin0 -> 48589 bytes
-rw-r--r--apps/plugins/puzzles/osx.m1724
-rw-r--r--apps/plugins/puzzles/padtoolbar.bmpbin0 -> 1198 bytes
-rw-r--r--apps/plugins/puzzles/palisade.R21
-rw-r--r--apps/plugins/puzzles/palisade.c1383
-rw-r--r--apps/plugins/puzzles/pattern.R25
-rw-r--r--apps/plugins/puzzles/pattern.c2255
-rw-r--r--apps/plugins/puzzles/pearl.R23
-rw-r--r--apps/plugins/puzzles/pearl.c2772
-rw-r--r--apps/plugins/puzzles/pegs.R21
-rw-r--r--apps/plugins/puzzles/pegs.c1340
-rw-r--r--apps/plugins/puzzles/penrose.c629
-rw-r--r--apps/plugins/puzzles/penrose.h59
-rw-r--r--apps/plugins/puzzles/printing.c247
-rw-r--r--apps/plugins/puzzles/ps.c433
-rw-r--r--apps/plugins/puzzles/puzzles.but3401
-rw-r--r--apps/plugins/puzzles/puzzles.h636
-rw-r--r--apps/plugins/puzzles/puzzles.make113
-rw-r--r--apps/plugins/puzzles/puzzles.rc265
-rw-r--r--apps/plugins/puzzles/random.c350
-rw-r--r--apps/plugins/puzzles/range.R21
-rw-r--r--apps/plugins/puzzles/range.c1833
-rw-r--r--apps/plugins/puzzles/rbassert.h13
-rw-r--r--apps/plugins/puzzles/rbcompat.h62
-rw-r--r--apps/plugins/puzzles/rbwrappers.c2378
-rw-r--r--apps/plugins/puzzles/rect.R19
-rw-r--r--apps/plugins/puzzles/rect.c3000
-rw-r--r--apps/plugins/puzzles/resource.h20
-rw-r--r--apps/plugins/puzzles/rockbox.c1682
-rw-r--r--apps/plugins/puzzles/samegame.R19
-rw-r--r--apps/plugins/puzzles/samegame.c1679
-rw-r--r--apps/plugins/puzzles/signpost.R23
-rw-r--r--apps/plugins/puzzles/signpost.c2480
-rw-r--r--apps/plugins/puzzles/singles.R23
-rw-r--r--apps/plugins/puzzles/singles.c2004
-rw-r--r--apps/plugins/puzzles/sixteen.R19
-rw-r--r--apps/plugins/puzzles/sixteen.c1214
-rw-r--r--apps/plugins/puzzles/slant.R24
-rw-r--r--apps/plugins/puzzles/slant.c2278
-rw-r--r--apps/plugins/puzzles/solo.R24
-rw-r--r--apps/plugins/puzzles/solo.c5656
-rw-r--r--apps/plugins/puzzles/tdq.c88
-rw-r--r--apps/plugins/puzzles/tents.R24
-rw-r--r--apps/plugins/puzzles/tents.c2740
-rw-r--r--apps/plugins/puzzles/towers.R25
-rw-r--r--apps/plugins/puzzles/towers.c2104
-rw-r--r--apps/plugins/puzzles/tracks.R21
-rw-r--r--apps/plugins/puzzles/tracks.c2660
-rw-r--r--apps/plugins/puzzles/tree234.c2200
-rw-r--r--apps/plugins/puzzles/tree234.h202
-rw-r--r--apps/plugins/puzzles/twiddle.R19
-rw-r--r--apps/plugins/puzzles/twiddle.c1319
-rw-r--r--apps/plugins/puzzles/undead.R18
-rw-r--r--apps/plugins/puzzles/undead.c2738
-rw-r--r--apps/plugins/puzzles/unequal.R27
-rw-r--r--apps/plugins/puzzles/unequal.c2267
-rw-r--r--apps/plugins/puzzles/unfinished/README9
-rw-r--r--apps/plugins/puzzles/unfinished/group.R25
-rw-r--r--apps/plugins/puzzles/unfinished/group.c2198
-rw-r--r--apps/plugins/puzzles/unfinished/group.gap97
-rw-r--r--apps/plugins/puzzles/unfinished/numgame.c1290
-rw-r--r--apps/plugins/puzzles/unfinished/path.c786
-rw-r--r--apps/plugins/puzzles/unfinished/separate.R21
-rw-r--r--apps/plugins/puzzles/unfinished/separate.c859
-rw-r--r--apps/plugins/puzzles/unfinished/slide.R24
-rw-r--r--apps/plugins/puzzles/unfinished/slide.c2445
-rw-r--r--apps/plugins/puzzles/unfinished/sokoban.R19
-rw-r--r--apps/plugins/puzzles/unfinished/sokoban.c1479
-rw-r--r--apps/plugins/puzzles/unruly.R21
-rw-r--r--apps/plugins/puzzles/unruly.c2071
-rw-r--r--apps/plugins/puzzles/untangle.R21
-rw-r--r--apps/plugins/puzzles/untangle.c1491
-rw-r--r--apps/plugins/puzzles/version.c7
-rw-r--r--apps/plugins/puzzles/version.h11
-rw-r--r--apps/plugins/puzzles/wceinf.pl65
-rwxr-xr-xapps/plugins/puzzles/webpage.pl69
-rw-r--r--apps/plugins/puzzles/website.url2
-rw-r--r--apps/plugins/puzzles/windows.c3760
-rwxr-xr-xapps/plugins/puzzles/winiss.pl79
-rw-r--r--apps/plugins/puzzles/winwix.mc334
-rw-r--r--apps/plugins/sgt-puzzles.c33
260 files changed, 147263 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 28248802b7..4b1b8d635c 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -78,6 +78,7 @@ gif,viewers
78pong,games 78pong,games
79ppm,viewers 79ppm,viewers
80properties,viewers 80properties,viewers
81puzzles,games
81random_folder_advance_config,apps 82random_folder_advance_config,apps
82remote_control,apps 83remote_control,apps
83resistor,apps 84resistor,apps
@@ -92,6 +93,45 @@ rockpaint,apps
92search,viewers 93search,viewers
93searchengine,viewers 94searchengine,viewers
94settings_dumper,apps 95settings_dumper,apps
96sgt-blackbox,games
97sgt-bridges,games
98sgt-cube,games
99sgt-dominosa,games
100sgt-fifteen,games
101sgt-filling,games
102sgt-flip,games
103sgt-flood,games
104sgt-galaxies,games
105sgt-guess,games
106sgt-inertia,games
107sgt-keen,games
108sgt-lightup,games
109sgt-loopy,games
110sgt-magnets,games
111sgt-map,games
112sgt-mines,games
113sgt-net,games
114sgt-netslide,games
115sgt-palisade,games
116sgt-pattern,games
117sgt-pearl,games
118sgt-pegs,games
119sgt-range,games
120sgt-rect,games
121sgt-samegame,games
122sgt-signpost,games
123sgt-singles,games
124sgt-sixteen,games
125sgt-slant,games
126sgt-solo,games
127sgt-tents,games
128sgt-towers,games
129sgt-tracks,games
130sgt-twiddle,games
131sgt-undead,games
132sgt-unequal,games
133sgt-unruly,games
134sgt-untangle,games
95shopper,viewers 135shopper,viewers
96shortcuts_append,viewers 136shortcuts_append,viewers
97shortcuts_view,viewers 137shortcuts_view,viewers
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index c7a8cb69f7..98e727dbd4 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -73,6 +73,10 @@ iriverify.c
73#if (CONFIG_PLATFORM & PLATFORM_NATIVE) /* those plugins only run on hardware */ 73#if (CONFIG_PLATFORM & PLATFORM_NATIVE) /* those plugins only run on hardware */
74 74
75/* Overlays loaders */ 75/* Overlays loaders */
76
77/* use this if you want a monolithic 'puzzles' */
78/*sgt-puzzles.c*/
79
76#if PLUGIN_BUFFER_SIZE <= 0x20000 && defined(HAVE_LCD_BITMAP) 80#if PLUGIN_BUFFER_SIZE <= 0x20000 && defined(HAVE_LCD_BITMAP)
77 81
78#if CONFIG_KEYPAD != ONDIO_PAD && CONFIG_KEYPAD != SANSA_M200_PAD \ 82#if CONFIG_KEYPAD != ONDIO_PAD && CONFIG_KEYPAD != SANSA_M200_PAD \
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index d02073e29d..56ac665e40 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -16,6 +16,7 @@ clock
16#if defined(HAVE_LCD_COLOR) && \ 16#if defined(HAVE_LCD_COLOR) && \
17 (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE)) 17 (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
18xworld 18xworld
19puzzles
19#endif 20#endif
20 21
21#if (CONFIG_KEYPAD != ONDIO_PAD) /* not enough buttons */ \ 22#if (CONFIG_KEYPAD != ONDIO_PAD) /* not enough buttons */ \
diff --git a/apps/plugins/puzzles/Buildscr b/apps/plugins/puzzles/Buildscr
new file mode 100644
index 0000000000..910981f079
--- /dev/null
+++ b/apps/plugins/puzzles/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/CHECKLST.txt b/apps/plugins/puzzles/CHECKLST.txt
new file mode 100644
index 0000000000..2bef909e14
--- /dev/null
+++ b/apps/plugins/puzzles/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/LICENCE b/apps/plugins/puzzles/LICENCE
new file mode 100644
index 0000000000..4235005ea7
--- /dev/null
+++ b/apps/plugins/puzzles/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/PuzzleApplet.java b/apps/plugins/puzzles/PuzzleApplet.java
new file mode 100644
index 0000000000..0b0648ce9b
--- /dev/null
+++ b/apps/plugins/puzzles/PuzzleApplet.java
@@ -0,0 +1,617 @@
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 solveCommand;
32 private Color[] colors;
33 private JLabel statusBar;
34 private PuzzlePanel pp;
35 private Runtime runtime;
36 private String[] puzzle_args;
37 private Graphics2D gg;
38 private Timer timer;
39 private int xarg1, xarg2, xarg3;
40 private int[] xPoints, yPoints;
41 private BufferedImage[] blitters = new BufferedImage[512];
42 private ConfigDialog dlg;
43
44 static {
45 try {
46 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
47 } catch (Exception ex) {
48 ex.printStackTrace();
49 }
50 }
51
52 public void init() {
53 try {
54 Container cp = getContentPane();
55 cp.setLayout(new BorderLayout());
56 runtime = (Runtime) Class.forName("PuzzleEngine").newInstance();
57 runtime.setCallJavaCB(this);
58 JMenuBar menubar = new JMenuBar();
59 JMenu jm;
60 menubar.add(jm = new JMenu("Game"));
61 addMenuItemWithKey(jm, "New", 'n');
62 addMenuItemCallback(jm, "Restart", "jcallback_restart_event");
63 addMenuItemCallback(jm, "Specific...", "jcallback_config_event", CFG_DESC);
64 addMenuItemCallback(jm, "Random Seed...", "jcallback_config_event", CFG_SEED);
65 jm.addSeparator();
66 addMenuItemWithKey(jm, "Undo", 'u');
67 addMenuItemWithKey(jm, "Redo", 'r');
68 jm.addSeparator();
69 solveCommand = addMenuItemCallback(jm, "Solve", "jcallback_solve_event");
70 solveCommand.setEnabled(false);
71 if (mainWindow != null) {
72 jm.addSeparator();
73 addMenuItemWithKey(jm, "Exit", 'q');
74 }
75 menubar.add(typeMenu = new JMenu("Type"));
76 typeMenu.setVisible(false);
77 menubar.add(jm = new JMenu("Help"));
78 addMenuItemCallback(jm, "About", "jcallback_about_event");
79 setJMenuBar(menubar);
80 cp.add(pp = new PuzzlePanel(), BorderLayout.CENTER);
81 pp.addKeyListener(new KeyAdapter() {
82 public void keyPressed(KeyEvent e) {
83 int key = -1;
84 int shift = e.isShiftDown() ? MOD_SHFT : 0;
85 int ctrl = e.isControlDown() ? MOD_CTRL : 0;
86 switch (e.getKeyCode()) {
87 case KeyEvent.VK_LEFT:
88 case KeyEvent.VK_KP_LEFT:
89 key = shift | ctrl | CURSOR_LEFT;
90 break;
91 case KeyEvent.VK_RIGHT:
92 case KeyEvent.VK_KP_RIGHT:
93 key = shift | ctrl | CURSOR_RIGHT;
94 break;
95 case KeyEvent.VK_UP:
96 case KeyEvent.VK_KP_UP:
97 key = shift | ctrl | CURSOR_UP;
98 break;
99 case KeyEvent.VK_DOWN:
100 case KeyEvent.VK_KP_DOWN:
101 key = shift | ctrl | CURSOR_DOWN;
102 break;
103 case KeyEvent.VK_PAGE_UP:
104 key = shift | ctrl | MOD_NUM_KEYPAD | '9';
105 break;
106 case KeyEvent.VK_PAGE_DOWN:
107 key = shift | ctrl | MOD_NUM_KEYPAD | '3';
108 break;
109 case KeyEvent.VK_HOME:
110 key = shift | ctrl | MOD_NUM_KEYPAD | '7';
111 break;
112 case KeyEvent.VK_END:
113 key = shift | ctrl | MOD_NUM_KEYPAD | '1';
114 break;
115 default:
116 if (e.getKeyCode() >= KeyEvent.VK_NUMPAD0 && e.getKeyCode() <=KeyEvent.VK_NUMPAD9) {
117 key = MOD_NUM_KEYPAD | (e.getKeyCode() - KeyEvent.VK_NUMPAD0+'0');
118 }
119 break;
120 }
121 if (key != -1) {
122 runtimeCall("jcallback_key_event", new int[] {0, 0, key});
123 }
124 }
125 public void keyTyped(KeyEvent e) {
126 runtimeCall("jcallback_key_event", new int[] {0, 0, e.getKeyChar()});
127 }
128 });
129 pp.addMouseListener(new MouseAdapter() {
130 public void mouseReleased(MouseEvent e) {
131 mousePressedReleased(e, true);
132 }
133 public void mousePressed(MouseEvent e) {
134 pp.requestFocus();
135 mousePressedReleased(e, false);
136 }
137 private void mousePressedReleased(MouseEvent e, boolean released) {
138 int button;
139 if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
140 button = MIDDLE_BUTTON;
141 else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
142 button = RIGHT_BUTTON;
143 else if ((e.getModifiers() & (InputEvent.BUTTON1_MASK)) != 0)
144 button = LEFT_BUTTON;
145 else
146 return;
147 if (released)
148 button += LEFT_RELEASE - LEFT_BUTTON;
149 runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
150 }
151 });
152 pp.addMouseMotionListener(new MouseMotionAdapter() {
153 public void mouseDragged(MouseEvent e) {
154 int button;
155 if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
156 button = MIDDLE_DRAG;
157 else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
158 button = RIGHT_DRAG;
159 else
160 button = LEFT_DRAG;
161 runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
162 }
163 });
164 pp.addComponentListener(new ComponentAdapter() {
165 public void componentResized(ComponentEvent e) {
166 handleResized();
167 }
168 });
169 pp.setFocusable(true);
170 pp.requestFocus();
171 timer = new Timer(20, new ActionListener() {
172 public void actionPerformed(ActionEvent e) {
173 runtimeCall("jcallback_timer_func", new int[0]);
174 }
175 });
176 String gameid;
177 try {
178 gameid = getParameter("game_id");
179 } catch (java.lang.NullPointerException ex) {
180 gameid = null;
181 }
182 if (gameid == null) {
183 puzzle_args = null;
184 } else {
185 puzzle_args = new String[2];
186 puzzle_args[0] = "puzzle";
187 puzzle_args[1] = gameid;
188 }
189 SwingUtilities.invokeLater(new Runnable() {
190 public void run() {
191 runtime.start(puzzle_args);
192 runtime.execute();
193 }
194 });
195 } catch (Exception ex) {
196 ex.printStackTrace();
197 }
198 }
199
200 public void destroy() {
201 SwingUtilities.invokeLater(new Runnable() {
202 public void run() {
203 runtime.execute();
204 if (mainWindow != null) {
205 mainWindow.dispose();
206 System.exit(0);
207 }
208 }
209 });
210 }
211
212 protected void handleResized() {
213 pp.createBackBuffer(pp.getWidth(), pp.getHeight(), colors[0]);
214 runtimeCall("jcallback_resize", new int[] {pp.getWidth(), pp.getHeight()});
215 }
216
217 private void addMenuItemWithKey(JMenu jm, String name, int key) {
218 addMenuItemCallback(jm, name, "jcallback_menu_key_event", key);
219 }
220
221 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int arg) {
222 return addMenuItemCallback(jm, name, callback, new int[] {arg});
223 }
224
225 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback) {
226 return addMenuItemCallback(jm, name, callback, new int[0]);
227 }
228
229 private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int[] args) {
230 JMenuItem jmi;
231 if (jm == typeMenu)
232 typeMenu.add(jmi = new JCheckBoxMenuItem(name));
233 else
234 jm.add(jmi = new JMenuItem(name));
235 jmi.addActionListener(new ActionListener() {
236 public void actionPerformed(ActionEvent e) {
237 runtimeCall(callback, args);
238 }
239 });
240 return jmi;
241 }
242
243 protected void runtimeCall(String func, int[] args) {
244 if (runtimeCallWithResult(func, args) == 42 && mainWindow != null) {
245 destroy();
246 }
247 }
248
249 protected int runtimeCallWithResult(String func, int[] args) {
250 try {
251 return runtime.call(func, args);
252 } catch (Runtime.CallException ex) {
253 ex.printStackTrace();
254 return 42;
255 }
256 }
257
258 private void buildConfigureMenuItem() {
259 if (typeMenu.isVisible()) {
260 typeMenu.addSeparator();
261 } else {
262 typeMenu.setVisible(true);
263 }
264 addMenuItemCallback(typeMenu, "Custom...", "jcallback_config_event", CFG_SETTINGS);
265 }
266
267 private void addTypeItem(String name, final int ptrGameParams) {
268 typeMenu.setVisible(true);
269 addMenuItemCallback(typeMenu, name, "jcallback_preset_event", ptrGameParams);
270 }
271
272 public int call(int cmd, int arg1, int arg2, int arg3) {
273 try {
274 switch(cmd) {
275 case 0: // initialize
276 if (mainWindow != null) mainWindow.setTitle(runtime.cstring(arg1));
277 if ((arg2 & 1) != 0) buildConfigureMenuItem();
278 if ((arg2 & 2) != 0) addStatusBar();
279 if ((arg2 & 4) != 0) solveCommand.setEnabled(true);
280 colors = new Color[arg3];
281 return 0;
282 case 1: // Type menu item
283 addTypeItem(runtime.cstring(arg1), arg2);
284 return 0;
285 case 2: // MessageBox
286 JOptionPane.showMessageDialog(this, runtime.cstring(arg2), runtime.cstring(arg1), arg3 == 0 ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE);
287 return 0;
288 case 3: // Resize
289 pp.setPreferredSize(new Dimension(arg1, arg2));
290 if (mainWindow != null) mainWindow.pack();
291 handleResized();
292 if (mainWindow != null) mainWindow.setVisible(true);
293 return 0;
294 case 4: // drawing tasks
295 switch(arg1) {
296 case 0:
297 String text = runtime.cstring(arg2);
298 if (text.equals("")) text = " ";
299 statusBar.setText(text);
300 break;
301 case 1:
302 gg = pp.backBuffer.createGraphics();
303 if (arg2 != 0 || arg3 != 0 ||
304 arg2 + xarg2 != getWidth() ||
305 arg3 + xarg3 != getHeight()) {
306 int left = arg2, right = arg2 + xarg2;
307 int top = arg3, bottom = arg3 + xarg3;
308 int width = getWidth(), height = getHeight();
309 gg.setColor(colors != null ? colors[0] : Color.black);
310 gg.fillRect(0, 0, left, height);
311 gg.fillRect(right, 0, width-right, height);
312 gg.fillRect(0, 0, width, top);
313 gg.fillRect(0, bottom, width, height-bottom);
314 gg.setClip(left, top, right-left, bottom-top);
315 }
316 break;
317 case 2: gg.dispose(); pp.repaint(); break;
318 case 3: gg.setClip(arg2, arg3, xarg1, xarg2); break;
319 case 4:
320 if (arg2 == 0 && arg3 == 0) {
321 gg.setClip(0, 0, getWidth(), getHeight());
322 } else {
323 gg.setClip(arg2, arg3, getWidth()-2*arg2, getHeight()-2*arg3);
324 }
325 break;
326 case 5:
327 gg.setColor(colors[xarg3]);
328 gg.fillRect(arg2, arg3, xarg1, xarg2);
329 break;
330 case 6:
331 gg.setColor(colors[xarg3]);
332 gg.drawLine(arg2, arg3, xarg1, xarg2);
333 break;
334 case 7:
335 xPoints = new int[arg2];
336 yPoints = new int[arg2];
337 break;
338 case 8:
339 if (arg3 != -1) {
340 gg.setColor(colors[arg3]);
341 gg.fillPolygon(xPoints, yPoints, xPoints.length);
342 }
343 gg.setColor(colors[arg2]);
344 gg.drawPolygon(xPoints, yPoints, xPoints.length);
345 break;
346 case 9:
347 if (arg3 != -1) {
348 gg.setColor(colors[arg3]);
349 gg.fillOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
350 }
351 gg.setColor(colors[arg2]);
352 gg.drawOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
353 break;
354 case 10:
355 for(int i=0; i<blitters.length; i++) {
356 if (blitters[i] == null) {
357 blitters[i] = new BufferedImage(arg2, arg3, BufferedImage.TYPE_3BYTE_BGR);
358 return i;
359 }
360 }
361 throw new RuntimeException("No free blitter found!");
362 case 11: blitters[arg2] = null; break;
363 case 12:
364 timer.start(); break;
365 case 13:
366 timer.stop(); break;
367 }
368 return 0;
369 case 5: // more arguments
370 xarg1 = arg1;
371 xarg2 = arg2;
372 xarg3 = arg3;
373 return 0;
374 case 6: // polygon vertex
375 xPoints[arg1]=arg2;
376 yPoints[arg1]=arg3;
377 return 0;
378 case 7: // string
379 gg.setColor(colors[arg2]);
380 {
381 String text = runtime.utfstring(arg3);
382 Font ft = new Font((xarg3 & 0x10) != 0 ? "Monospaced" : "Dialog",
383 Font.PLAIN, 100);
384 int height100 = this.getFontMetrics(ft).getHeight();
385 ft = ft.deriveFont(arg1 * 100 / (float)height100);
386 FontMetrics fm = this.getFontMetrics(ft);
387 int asc = fm.getAscent(), desc = fm.getDescent();
388 if ((xarg3 & ALIGN_VCENTRE) != 0)
389 xarg2 += asc - (asc+desc)/2;
390 int wid = fm.stringWidth(text);
391 if ((xarg3 & ALIGN_HCENTRE) != 0)
392 xarg1 -= wid / 2;
393 else if ((xarg3 & ALIGN_HRIGHT) != 0)
394 xarg1 -= wid;
395 gg.setFont(ft);
396 gg.drawString(text, xarg1, xarg2);
397 }
398 return 0;
399 case 8: // blitter_save
400 Graphics g2 = blitters[arg1].createGraphics();
401 g2.drawImage(pp.backBuffer, 0, 0, blitters[arg1].getWidth(), blitters[arg1].getHeight(),
402 arg2, arg3, arg2 + blitters[arg1].getWidth(), arg3 + blitters[arg1].getHeight(), this);
403 g2.dispose();
404 return 0;
405 case 9: // blitter_load
406 gg.drawImage(blitters[arg1], arg2, arg3, this);
407 return 0;
408 case 10: // dialog_init
409 dlg= new ConfigDialog(this, runtime.cstring(arg1));
410 return 0;
411 case 11: // dialog_add_control
412 {
413 int sval_ptr = arg1;
414 int ival = arg2;
415 int ptr = xarg1;
416 int type=xarg2;
417 String name = runtime.cstring(xarg3);
418 switch(type) {
419 case C_STRING:
420 dlg.addTextBox(ptr, name, runtime.cstring(sval_ptr));
421 break;
422 case C_BOOLEAN:
423 dlg.addCheckBox(ptr, name, ival != 0);
424 break;
425 case C_CHOICES:
426 dlg.addComboBox(ptr, name, runtime.cstring(sval_ptr), ival);
427 }
428 }
429 return 0;
430 case 12:
431 dlg.finish();
432 dlg = null;
433 return 0;
434 case 13: // tick a menu item
435 if (arg1 < 0) arg1 = typeMenu.getItemCount() - 1;
436 for (int i = 0; i < typeMenu.getItemCount(); i++) {
437 if (typeMenu.getMenuComponent(i) instanceof JCheckBoxMenuItem) {
438 ((JCheckBoxMenuItem)typeMenu.getMenuComponent(i)).setSelected(arg1 == i);
439 }
440 }
441 return 0;
442 default:
443 if (cmd >= 1024 && cmd < 2048) { // palette
444 colors[cmd-1024] = new Color(arg1, arg2, arg3);
445 }
446 if (cmd == 1024) {
447 pp.setBackground(colors[0]);
448 if (statusBar != null) statusBar.setBackground(colors[0]);
449 this.setBackground(colors[0]);
450 }
451 return 0;
452 }
453 } catch (Throwable ex) {
454 ex.printStackTrace();
455 System.exit(-1);
456 return 0;
457 }
458 }
459
460 private void addStatusBar() {
461 statusBar = new JLabel("test");
462 statusBar.setBorder(new BevelBorder(BevelBorder.LOWERED));
463 getContentPane().add(BorderLayout.SOUTH,statusBar);
464 }
465
466 // Standalone runner
467 public static void main(String[] args) {
468 final PuzzleApplet a = new PuzzleApplet();
469 JFrame jf = new JFrame("Loading...");
470 jf.getContentPane().setLayout(new BorderLayout());
471 jf.getContentPane().add(a, BorderLayout.CENTER);
472 a.mainWindow=jf;
473 a.init();
474 a.start();
475 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
476 jf.addWindowListener(new WindowAdapter() {
477 public void windowClosing(WindowEvent e) {
478 a.stop();
479 a.destroy();
480 }
481 });
482 jf.setVisible(true);
483 }
484
485 public static class PuzzlePanel extends JPanel {
486
487 private static final long serialVersionUID = 1L;
488 protected BufferedImage backBuffer;
489
490 public PuzzlePanel() {
491 setPreferredSize(new Dimension(100,100));
492 createBackBuffer(100,100, Color.black);
493 }
494
495 public void createBackBuffer(int w, int h, Color bg) {
496 if (w > 0 && h > 0) {
497 backBuffer = new BufferedImage(w,h, BufferedImage.TYPE_3BYTE_BGR);
498 Graphics g = backBuffer.createGraphics();
499 g.setColor(bg);
500 g.fillRect(0, 0, w, h);
501 g.dispose();
502 }
503 }
504
505 protected void paintComponent(Graphics g) {
506 g.drawImage(backBuffer, 0, 0, this);
507 }
508 }
509
510 public static class ConfigComponent {
511 public int type;
512 public int configItemPointer;
513 public JComponent component;
514
515 public ConfigComponent(int type, int configItemPointer, JComponent component) {
516 this.type = type;
517 this.configItemPointer = configItemPointer;
518 this.component = component;
519 }
520 }
521
522 public class ConfigDialog extends JDialog {
523
524 private GridBagConstraints gbcLeft = new GridBagConstraints(
525 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1,
526 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
527 new Insets(0, 0, 0, 0), 0, 0);
528 private GridBagConstraints gbcRight = new GridBagConstraints(
529 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
530 GridBagConstraints.REMAINDER, 1, 1.0, 0,
531 GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
532 new Insets(5, 5, 5, 5), 0, 0);
533 private GridBagConstraints gbcBottom = new GridBagConstraints(
534 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
535 GridBagConstraints.REMAINDER, GridBagConstraints.REMAINDER,
536 1.0, 1.0, GridBagConstraints.CENTER,
537 GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0);
538
539 private static final long serialVersionUID = 1L;
540 private List components = new ArrayList();
541
542 public ConfigDialog(JApplet parent, String title) {
543 super(JOptionPane.getFrameForComponent(parent), title, true);
544 getContentPane().setLayout(new GridBagLayout());
545 }
546
547 public void addTextBox(int ptr, String name, String value) {
548 getContentPane().add(new JLabel(name), gbcLeft);
549 JComponent c = new JTextField(value, 25);
550 getContentPane().add(c, gbcRight);
551 components.add(new ConfigComponent(C_STRING, ptr, c));
552 }
553
554
555 public void addCheckBox(int ptr, String name, boolean selected) {
556 JComponent c = new JCheckBox(name, selected);
557 getContentPane().add(c, gbcRight);
558 components.add(new ConfigComponent(C_BOOLEAN, ptr, c));
559 }
560
561 public void addComboBox(int ptr, String name, String values, int selected) {
562 getContentPane().add(new JLabel(name), gbcLeft);
563 StringTokenizer st = new StringTokenizer(values.substring(1), values.substring(0,1));
564 JComboBox c = new JComboBox();
565 c.setEditable(false);
566 while(st.hasMoreTokens())
567 c.addItem(st.nextToken());
568 c.setSelectedIndex(selected);
569 getContentPane().add(c, gbcRight);
570 components.add(new ConfigComponent(C_CHOICES, ptr, c));
571 }
572
573 public void finish() {
574 JPanel buttons = new JPanel(new GridLayout(1, 2, 5, 5));
575 getContentPane().add(buttons, gbcBottom);
576 JButton b;
577 buttons.add(b=new JButton("OK"));
578 b.addActionListener(new ActionListener() {
579 public void actionPerformed(ActionEvent e) {
580 save();
581 dispose();
582 }
583 });
584 getRootPane().setDefaultButton(b);
585 buttons.add(b=new JButton("Cancel"));
586 b.addActionListener(new ActionListener() {
587 public void actionPerformed(ActionEvent e) {
588 dispose();
589 }
590 });
591 setDefaultCloseOperation(DISPOSE_ON_CLOSE);
592 pack();
593 setLocationRelativeTo(null);
594 setVisible(true);
595 }
596 private void save() {
597 for (int i = 0; i < components.size(); i++) {
598 ConfigComponent cc = (ConfigComponent) components.get(i);
599 switch(cc.type) {
600 case C_STRING:
601 JTextField jtf = (JTextField)cc.component;
602 runtimeCall("jcallback_config_set_string", new int[] {cc.configItemPointer, runtime.strdup(jtf.getText())});
603 break;
604 case C_BOOLEAN:
605 JCheckBox jcb = (JCheckBox)cc.component;
606 runtimeCall("jcallback_config_set_boolean", new int[] {cc.configItemPointer, jcb.isSelected()?1:0});
607 break;
608 case C_CHOICES:
609 JComboBox jcm = (JComboBox)cc.component;
610 runtimeCall("jcallback_config_set_choice", new int[] {cc.configItemPointer, jcm.getSelectedIndex()});
611 break;
612 }
613 }
614 runtimeCall("jcallback_config_ok", new int[0]);
615 }
616 }
617}
diff --git a/apps/plugins/puzzles/README b/apps/plugins/puzzles/README
new file mode 100644
index 0000000000..890db56771
--- /dev/null
+++ b/apps/plugins/puzzles/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/Recipe b/apps/plugins/puzzles/Recipe
new file mode 100644
index 0000000000..ba8317f51a
--- /dev/null
+++ b/apps/plugins/puzzles/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/SOURCES b/apps/plugins/puzzles/SOURCES
new file mode 100644
index 0000000000..9c41a00358
--- /dev/null
+++ b/apps/plugins/puzzles/SOURCES
@@ -0,0 +1,26 @@
1rockbox.c
2rbwrappers.c
3
4combi.c
5divvy.c
6drawing.c
7dsf.c
8findloop.c
9grid.c
10latin.c
11laydomino.c
12loopgen.c
13malloc.c
14maxflow.c
15midend.c
16misc.c
17penrose.c
18printing.c
19random.c
20tdq.c
21tree234.c
22version.c
23
24#ifdef COMBINED
25list.c
26#endif
diff --git a/apps/plugins/puzzles/benchmark.pl b/apps/plugins/puzzles/benchmark.pl
new file mode 100755
index 0000000000..98763859e8
--- /dev/null
+++ b/apps/plugins/puzzles/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/benchmark.sh b/apps/plugins/puzzles/benchmark.sh
new file mode 100755
index 0000000000..b3af27765e
--- /dev/null
+++ b/apps/plugins/puzzles/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/blackbox.R b/apps/plugins/puzzles/blackbox.R
new file mode 100644
index 0000000000..116225206d
--- /dev/null
+++ b/apps/plugins/puzzles/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/blackbox.c b/apps/plugins/puzzles/blackbox.c
new file mode 100644
index 0000000000..a4875e49d7
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/bridges.R b/apps/plugins/puzzles/bridges.R
new file mode 100644
index 0000000000..75df309152
--- /dev/null
+++ b/apps/plugins/puzzles/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/bridges.c b/apps/plugins/puzzles/bridges.c
new file mode 100644
index 0000000000..05a9b16823
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/chm.but b/apps/plugins/puzzles/chm.but
new file mode 100644
index 0000000000..e0237044e4
--- /dev/null
+++ b/apps/plugins/puzzles/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/chm.css b/apps/plugins/puzzles/chm.css
new file mode 100644
index 0000000000..d8c316bfc6
--- /dev/null
+++ b/apps/plugins/puzzles/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/combi.c b/apps/plugins/puzzles/combi.c
new file mode 100644
index 0000000000..d39e298405
--- /dev/null
+++ b/apps/plugins/puzzles/combi.c
@@ -0,0 +1,110 @@
1#include "rbassert.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/configure.ac b/apps/plugins/puzzles/configure.ac
new file mode 100644
index 0000000000..3a38c95602
--- /dev/null
+++ b/apps/plugins/puzzles/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/cube.R b/apps/plugins/puzzles/cube.R
new file mode 100644
index 0000000000..85b081ec76
--- /dev/null
+++ b/apps/plugins/puzzles/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/cube.c b/apps/plugins/puzzles/cube.c
new file mode 100644
index 0000000000..5a09648226
--- /dev/null
+++ b/apps/plugins/puzzles/cube.c
@@ -0,0 +1,1774 @@
1/*
2 * cube.c: Cube game.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include "rbassert.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 //float theight = 0.8660254037844386467;
365
366 for (row = 0; row < params->d1 + params->d2; row++) {
367 if (row < params->d2) {
368 other = +1;
369 rowlen = row + params->d1;
370 } else {
371 other = -1;
372 rowlen = 2*params->d2 + params->d1 - row;
373 }
374
375 /*
376 * There are `rowlen' down-pointing triangles.
377 */
378 for (i = 0; i < rowlen; i++) {
379 struct grid_square sq;
380 int ix;
381 float x, y;
382
383 ix = (2 * i - (rowlen-1));
384 x = ix * 0.5F;
385 y = theight * row;
386 sq.x = x;
387 sq.y = y + theight / 3;
388 sq.points[0] = x - 0.5F;
389 sq.points[1] = y;
390 sq.points[2] = x;
391 sq.points[3] = y + theight;
392 sq.points[4] = x + 0.5F;
393 sq.points[5] = y;
394 sq.npoints = 3;
395
396 sq.directions[LEFT] = 0x03; /* 0,1 */
397 sq.directions[RIGHT] = 0x06; /* 1,2 */
398 sq.directions[UP] = 0x05; /* 0,2 */
399 sq.directions[DOWN] = 0; /* invalid move */
400
401 /*
402 * Down-pointing triangle: both the up diagonals go
403 * up, and the down ones go left and right.
404 */
405 sq.directions[UP_LEFT] = sq.directions[UP_RIGHT] =
406 sq.directions[UP];
407 sq.directions[DOWN_LEFT] = sq.directions[LEFT];
408 sq.directions[DOWN_RIGHT] = sq.directions[RIGHT];
409
410 sq.flip = TRUE;
411
412 if (firstix < 0)
413 firstix = ix & 3;
414 ix -= firstix;
415 sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3);
416
417 callback(ctx, &sq);
418 }
419
420 /*
421 * There are `rowlen+other' up-pointing triangles.
422 */
423 for (i = 0; i < rowlen+other; i++) {
424 struct grid_square sq;
425 int ix;
426 float x, y;
427
428 ix = (2 * i - (rowlen+other-1));
429 x = ix * 0.5F;
430 y = theight * row;
431 sq.x = x;
432 sq.y = y + 2*theight / 3;
433 sq.points[0] = x + 0.5F;
434 sq.points[1] = y + theight;
435 sq.points[2] = x;
436 sq.points[3] = y;
437 sq.points[4] = x - 0.5F;
438 sq.points[5] = y + theight;
439 sq.npoints = 3;
440
441 sq.directions[LEFT] = 0x06; /* 1,2 */
442 sq.directions[RIGHT] = 0x03; /* 0,1 */
443 sq.directions[DOWN] = 0x05; /* 0,2 */
444 sq.directions[UP] = 0; /* invalid move */
445
446 /*
447 * Up-pointing triangle: both the down diagonals go
448 * down, and the up ones go left and right.
449 */
450 sq.directions[DOWN_LEFT] = sq.directions[DOWN_RIGHT] =
451 sq.directions[DOWN];
452 sq.directions[UP_LEFT] = sq.directions[LEFT];
453 sq.directions[UP_RIGHT] = sq.directions[RIGHT];
454
455 sq.flip = FALSE;
456
457 if (firstix < 0)
458 firstix = (ix - 1) & 3;
459 ix -= firstix;
460 sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3);
461
462 callback(ctx, &sq);
463 }
464 }
465 }
466}
467
468static int grid_area(int d1, int d2, int order)
469{
470 /*
471 * An NxM grid of squares has NM squares in it.
472 *
473 * A grid of triangles with dimensions A and B has a total of
474 * A^2 + B^2 + 4AB triangles in it. (You can divide it up into
475 * a side-A triangle containing A^2 subtriangles, a side-B
476 * triangle containing B^2, and two congruent parallelograms,
477 * each with side lengths A and B, each therefore containing AB
478 * two-triangle rhombuses.)
479 */
480 if (order == 4)
481 return d1 * d2;
482 else
483 return d1*d1 + d2*d2 + 4*d1*d2;
484}
485
486static config_item *game_configure(const game_params *params)
487{
488 config_item *ret = snewn(4, config_item);
489 char buf[80];
490
491 ret[0].name = "Type of solid";
492 ret[0].type = C_CHOICES;
493 ret[0].sval = ":Tetrahedron:Cube:Octahedron:Icosahedron";
494 ret[0].ival = params->solid;
495
496 ret[1].name = "Width / top";
497 ret[1].type = C_STRING;
498 sprintf(buf, "%d", params->d1);
499 ret[1].sval = dupstr(buf);
500 ret[1].ival = 0;
501
502 ret[2].name = "Height / bottom";
503 ret[2].type = C_STRING;
504 sprintf(buf, "%d", params->d2);
505 ret[2].sval = dupstr(buf);
506 ret[2].ival = 0;
507
508 ret[3].name = NULL;
509 ret[3].type = C_END;
510 ret[3].sval = NULL;
511 ret[3].ival = 0;
512
513 return ret;
514}
515
516static game_params *custom_params(const config_item *cfg)
517{
518 game_params *ret = snew(game_params);
519
520 ret->solid = cfg[0].ival;
521 ret->d1 = atoi(cfg[1].sval);
522 ret->d2 = atoi(cfg[2].sval);
523
524 return ret;
525}
526
527static void count_grid_square_callback(void *ctx, struct grid_square *sq)
528{
529 int *classes = (int *)ctx;
530 int thisclass;
531
532 if (classes[4] == 4)
533 thisclass = sq->tetra_class;
534 else if (classes[4] == 2)
535 thisclass = sq->flip;
536 else
537 thisclass = 0;
538
539 classes[thisclass]++;
540}
541
542static char *validate_params(const game_params *params, int full)
543{
544 int classes[5];
545 int i;
546
547 if (params->solid < 0 || params->solid >= lenof(solids))
548 return "Unrecognised solid type";
549
550 if (solids[params->solid]->order == 4) {
551 if (params->d1 <= 0 || params->d2 <= 0)
552 return "Both grid dimensions must be greater than zero";
553 } else {
554 if (params->d1 <= 0 && params->d2 <= 0)
555 return "At least one grid dimension must be greater than zero";
556 }
557
558 for (i = 0; i < 4; i++)
559 classes[i] = 0;
560 if (params->solid == TETRAHEDRON)
561 classes[4] = 4;
562 else if (params->solid == OCTAHEDRON)
563 classes[4] = 2;
564 else
565 classes[4] = 1;
566 enum_grid_squares(params, count_grid_square_callback, classes);
567
568 for (i = 0; i < classes[4]; i++)
569 if (classes[i] < solids[params->solid]->nfaces / classes[4])
570 return "Not enough grid space to place all blue faces";
571
572 if (grid_area(params->d1, params->d2, solids[params->solid]->order) <
573 solids[params->solid]->nfaces + 1)
574 return "Not enough space to place the solid on an empty square";
575
576 return NULL;
577}
578
579struct grid_data {
580 int *gridptrs[4];
581 int nsquares[4];
582 int nclasses;
583 int squareindex;
584};
585
586static void classify_grid_square_callback(void *ctx, struct grid_square *sq)
587{
588 struct grid_data *data = (struct grid_data *)ctx;
589 int thisclass;
590
591 if (data->nclasses == 4)
592 thisclass = sq->tetra_class;
593 else if (data->nclasses == 2)
594 thisclass = sq->flip;
595 else
596 thisclass = 0;
597
598 data->gridptrs[thisclass][data->nsquares[thisclass]++] =
599 data->squareindex++;
600}
601
602static char *new_game_desc(const game_params *params, random_state *rs,
603 char **aux, int interactive)
604{
605 struct grid_data data;
606 int i, j, k, m, area, facesperclass;
607 int *flags;
608 char *desc, *p;
609
610 /*
611 * Enumerate the grid squares, dividing them into equivalence
612 * classes as appropriate. (For the tetrahedron, there is one
613 * equivalence class for each face; for the octahedron there
614 * are two classes; for the other two solids there's only one.)
615 */
616
617 area = grid_area(params->d1, params->d2, solids[params->solid]->order);
618 if (params->solid == TETRAHEDRON)
619 data.nclasses = 4;
620 else if (params->solid == OCTAHEDRON)
621 data.nclasses = 2;
622 else
623 data.nclasses = 1;
624 data.gridptrs[0] = snewn(data.nclasses * area, int);
625 for (i = 0; i < data.nclasses; i++) {
626 data.gridptrs[i] = data.gridptrs[0] + i * area;
627 data.nsquares[i] = 0;
628 }
629 data.squareindex = 0;
630 enum_grid_squares(params, classify_grid_square_callback, &data);
631
632 facesperclass = solids[params->solid]->nfaces / data.nclasses;
633
634 for (i = 0; i < data.nclasses; i++)
635 assert(data.nsquares[i] >= facesperclass);
636 assert(data.squareindex == area);
637
638 /*
639 * So now we know how many faces to allocate in each class. Get
640 * on with it.
641 */
642 flags = snewn(area, int);
643 for (i = 0; i < area; i++)
644 flags[i] = FALSE;
645
646 for (i = 0; i < data.nclasses; i++) {
647 for (j = 0; j < facesperclass; j++) {
648 int n = random_upto(rs, data.nsquares[i]);
649
650 assert(!flags[data.gridptrs[i][n]]);
651 flags[data.gridptrs[i][n]] = TRUE;
652
653 /*
654 * Move everything else up the array. I ought to use a
655 * better data structure for this, but for such small
656 * numbers it hardly seems worth the effort.
657 */
658 while (n < data.nsquares[i]-1) {
659 data.gridptrs[i][n] = data.gridptrs[i][n+1];
660 n++;
661 }
662 data.nsquares[i]--;
663 }
664 }
665
666 /*
667 * Now we know precisely which squares are blue. Encode this
668 * information in hex. While we're looping over this, collect
669 * the non-blue squares into a list in the now-unused gridptrs
670 * array.
671 */
672 desc = snewn(area / 4 + 40, char);
673 p = desc;
674 j = 0;
675 k = 8;
676 m = 0;
677 for (i = 0; i < area; i++) {
678 if (flags[i]) {
679 j |= k;
680 } else {
681 data.gridptrs[0][m++] = i;
682 }
683 k >>= 1;
684 if (!k) {
685 *p++ = "0123456789ABCDEF"[j];
686 k = 8;
687 j = 0;
688 }
689 }
690 if (k != 8)
691 *p++ = "0123456789ABCDEF"[j];
692
693 /*
694 * Choose a non-blue square for the polyhedron.
695 */
696 sprintf(p, ",%d", data.gridptrs[0][random_upto(rs, m)]);
697
698 sfree(data.gridptrs[0]);
699 sfree(flags);
700
701 return desc;
702}
703
704static void add_grid_square_callback(void *ctx, struct grid_square *sq)
705{
706 game_grid *grid = (game_grid *)ctx;
707
708 grid->squares[grid->nsquares++] = *sq; /* structure copy */
709}
710
711static int lowest_face(const struct solid *solid)
712{
713 int i, j, best;
714 float zmin;
715
716 best = 0;
717 zmin = 0.0;
718 for (i = 0; i < solid->nfaces; i++) {
719 float z = 0;
720
721 for (j = 0; j < solid->order; j++) {
722 int f = solid->faces[i*solid->order + j];
723 z += solid->vertices[f*3+2];
724 }
725
726 if (i == 0 || zmin > z) {
727 zmin = z;
728 best = i;
729 }
730 }
731
732 return best;
733}
734
735static int align_poly(const struct solid *solid, struct grid_square *sq,
736 int *pkey)
737{
738 float zmin;
739 int i, j;
740 int flip = (sq->flip ? -1 : +1);
741
742 /*
743 * First, find the lowest z-coordinate present in the solid.
744 */
745 zmin = 0.0;
746 for (i = 0; i < solid->nvertices; i++)
747 if (zmin > solid->vertices[i*3+2])
748 zmin = solid->vertices[i*3+2];
749
750 /*
751 * Now go round the grid square. For each point in the grid
752 * square, we're looking for a point of the polyhedron with the
753 * same x- and y-coordinates (relative to the square's centre),
754 * and z-coordinate equal to zmin (near enough).
755 */
756 for (j = 0; j < sq->npoints; j++) {
757 int matches, index;
758
759 matches = 0;
760 index = -1;
761
762 for (i = 0; i < solid->nvertices; i++) {
763 float dist = 0;
764
765 dist += SQ(solid->vertices[i*3+0] * flip - sq->points[j*2+0] + sq->x);
766 dist += SQ(solid->vertices[i*3+1] * flip - sq->points[j*2+1] + sq->y);
767 dist += SQ(solid->vertices[i*3+2] - zmin);
768
769 if (dist < 0.1) {
770 matches++;
771 index = i;
772 }
773 }
774
775 if (matches != 1 || index < 0)
776 return FALSE;
777 pkey[j] = index;
778 }
779
780 return TRUE;
781}
782
783static void flip_poly(struct solid *solid, int flip)
784{
785 int i;
786
787 if (flip) {
788 for (i = 0; i < solid->nvertices; i++) {
789 solid->vertices[i*3+0] *= -1;
790 solid->vertices[i*3+1] *= -1;
791 }
792 for (i = 0; i < solid->nfaces; i++) {
793 solid->normals[i*3+0] *= -1;
794 solid->normals[i*3+1] *= -1;
795 }
796 }
797}
798
799static struct solid *transform_poly(const struct solid *solid, int flip,
800 int key0, int key1, float angle)
801{
802 struct solid *ret = snew(struct solid);
803 float vx, vy, ax, ay;
804 float vmatrix[9], amatrix[9], vmatrix2[9];
805 int i;
806
807 *ret = *solid; /* structure copy */
808
809 flip_poly(ret, flip);
810
811 /*
812 * Now rotate the polyhedron through the given angle. We must
813 * rotate about the Z-axis to bring the two vertices key0 and
814 * key1 into horizontal alignment, then rotate about the
815 * X-axis, then rotate back again.
816 */
817 vx = ret->vertices[key1*3+0] - ret->vertices[key0*3+0];
818 vy = ret->vertices[key1*3+1] - ret->vertices[key0*3+1];
819 assert(APPROXEQ(vx*vx + vy*vy, 1.0));
820
821 vmatrix[0] = vx; vmatrix[3] = vy; vmatrix[6] = 0;
822 vmatrix[1] = -vy; vmatrix[4] = vx; vmatrix[7] = 0;
823 vmatrix[2] = 0; vmatrix[5] = 0; vmatrix[8] = 1;
824
825 ax = (float)cos(angle);
826 ay = (float)sin(angle);
827
828 amatrix[0] = 1; amatrix[3] = 0; amatrix[6] = 0;
829 amatrix[1] = 0; amatrix[4] = ax; amatrix[7] = ay;
830 amatrix[2] = 0; amatrix[5] = -ay; amatrix[8] = ax;
831
832 memcpy(vmatrix2, vmatrix, sizeof(vmatrix));
833 vmatrix2[1] = vy;
834 vmatrix2[3] = -vy;
835
836 for (i = 0; i < ret->nvertices; i++) {
837 MATMUL(ret->vertices + 3*i, vmatrix, ret->vertices + 3*i);
838 MATMUL(ret->vertices + 3*i, amatrix, ret->vertices + 3*i);
839 MATMUL(ret->vertices + 3*i, vmatrix2, ret->vertices + 3*i);
840 }
841 for (i = 0; i < ret->nfaces; i++) {
842 MATMUL(ret->normals + 3*i, vmatrix, ret->normals + 3*i);
843 MATMUL(ret->normals + 3*i, amatrix, ret->normals + 3*i);
844 MATMUL(ret->normals + 3*i, vmatrix2, ret->normals + 3*i);
845 }
846
847 return ret;
848}
849
850static char *validate_desc(const game_params *params, const char *desc)
851{
852 int area = grid_area(params->d1, params->d2, solids[params->solid]->order);
853 int i, j;
854
855 i = (area + 3) / 4;
856 for (j = 0; j < i; j++) {
857 int c = desc[j];
858 if (c >= '0' && c <= '9') continue;
859 if (c >= 'A' && c <= 'F') continue;
860 if (c >= 'a' && c <= 'f') continue;
861 return "Not enough hex digits at start of string";
862 /* NB if desc[j]=='\0' that will also be caught here, so we're safe */
863 }
864
865 if (desc[i] != ',')
866 return "Expected ',' after hex digits";
867
868 i++;
869 do {
870 if (desc[i] < '0' || desc[i] > '9')
871 return "Expected decimal integer after ','";
872 i++;
873 } while (desc[i]);
874
875 return NULL;
876}
877
878static game_state *new_game(midend *me, const game_params *params,
879 const char *desc)
880{
881 game_grid *grid = snew(game_grid);
882 game_state *state = snew(game_state);
883 int area;
884
885 state->params = *params; /* structure copy */
886 state->solid = solids[params->solid];
887
888 area = grid_area(params->d1, params->d2, state->solid->order);
889 grid->squares = snewn(area, struct grid_square);
890 grid->nsquares = 0;
891 enum_grid_squares(params, add_grid_square_callback, grid);
892 assert(grid->nsquares == area);
893 state->grid = grid;
894 grid->refcount = 1;
895
896 state->facecolours = snewn(state->solid->nfaces, int);
897 memset(state->facecolours, 0, state->solid->nfaces * sizeof(int));
898
899 state->bluemask = snewn((state->grid->nsquares + 31) / 32, unsigned long);
900 memset(state->bluemask, 0, (state->grid->nsquares + 31) / 32 *
901 sizeof(unsigned long));
902
903 /*
904 * Set up the blue squares and polyhedron position according to
905 * the game description.
906 */
907 {
908 const char *p = desc;
909 int i, j, v;
910
911 j = 8;
912 v = 0;
913 for (i = 0; i < state->grid->nsquares; i++) {
914 if (j == 8) {
915 v = *p++;
916 if (v >= '0' && v <= '9')
917 v -= '0';
918 else if (v >= 'A' && v <= 'F')
919 v -= 'A' - 10;
920 else if (v >= 'a' && v <= 'f')
921 v -= 'a' - 10;
922 else
923 break;
924 }
925 if (v & j)
926 SET_SQUARE(state, i, TRUE);
927 j >>= 1;
928 if (j == 0)
929 j = 8;
930 }
931
932 if (*p == ',')
933 p++;
934
935 state->current = atoi(p);
936 if (state->current < 0 || state->current >= state->grid->nsquares)
937 state->current = 0; /* got to do _something_ */
938 }
939
940 /*
941 * Align the polyhedron with its grid square and determine
942 * initial key points.
943 */
944 {
945 int pkey[4];
946 int ret;
947
948 ret = align_poly(state->solid, &state->grid->squares[state->current], pkey);
949 assert(ret);
950
951 state->dpkey[0] = state->spkey[0] = pkey[0];
952 state->dpkey[1] = state->spkey[0] = pkey[1];
953 state->dgkey[0] = state->sgkey[0] = 0;
954 state->dgkey[1] = state->sgkey[0] = 1;
955 }
956
957 state->previous = state->current;
958 state->angle = 0.0;
959 state->completed = 0;
960 state->movecount = 0;
961
962 return state;
963}
964
965static game_state *dup_game(const game_state *state)
966{
967 game_state *ret = snew(game_state);
968
969 ret->params = state->params; /* structure copy */
970 ret->solid = state->solid;
971 ret->facecolours = snewn(ret->solid->nfaces, int);
972 memcpy(ret->facecolours, state->facecolours,
973 ret->solid->nfaces * sizeof(int));
974 ret->current = state->current;
975 ret->grid = state->grid;
976 ret->grid->refcount++;
977 ret->bluemask = snewn((ret->grid->nsquares + 31) / 32, unsigned long);
978 memcpy(ret->bluemask, state->bluemask, (ret->grid->nsquares + 31) / 32 *
979 sizeof(unsigned long));
980 ret->dpkey[0] = state->dpkey[0];
981 ret->dpkey[1] = state->dpkey[1];
982 ret->dgkey[0] = state->dgkey[0];
983 ret->dgkey[1] = state->dgkey[1];
984 ret->spkey[0] = state->spkey[0];
985 ret->spkey[1] = state->spkey[1];
986 ret->sgkey[0] = state->sgkey[0];
987 ret->sgkey[1] = state->sgkey[1];
988 ret->previous = state->previous;
989 ret->angle = state->angle;
990 ret->completed = state->completed;
991 ret->movecount = state->movecount;
992
993 return ret;
994}
995
996static void free_game(game_state *state)
997{
998 if (--state->grid->refcount <= 0) {
999 sfree(state->grid->squares);
1000 sfree(state->grid);
1001 }
1002 sfree(state->bluemask);
1003 sfree(state->facecolours);
1004 sfree(state);
1005}
1006
1007static char *solve_game(const game_state *state, const game_state *currstate,
1008 const char *aux, char **error)
1009{
1010 return NULL;
1011}
1012
1013static int game_can_format_as_text_now(const game_params *params)
1014{
1015 return TRUE;
1016}
1017
1018static char *game_text_format(const game_state *state)
1019{
1020 return NULL;
1021}
1022
1023static game_ui *new_ui(const game_state *state)
1024{
1025 return NULL;
1026}
1027
1028static void free_ui(game_ui *ui)
1029{
1030}
1031
1032static char *encode_ui(const game_ui *ui)
1033{
1034 return NULL;
1035}
1036
1037static void decode_ui(game_ui *ui, const char *encoding)
1038{
1039}
1040
1041static void game_changed_state(game_ui *ui, const game_state *oldstate,
1042 const game_state *newstate)
1043{
1044}
1045
1046struct game_drawstate {
1047 float gridscale;
1048 int ox, oy; /* pixel position of float origin */
1049};
1050
1051/*
1052 * Code shared between interpret_move() and execute_move().
1053 */
1054static int find_move_dest(const game_state *from, int direction,
1055 int *skey, int *dkey)
1056{
1057 int mask, dest, i, j;
1058 float points[4];
1059
1060 /*
1061 * Find the two points in the current grid square which
1062 * correspond to this move.
1063 */
1064 mask = from->grid->squares[from->current].directions[direction];
1065 if (mask == 0)
1066 return -1;
1067 for (i = j = 0; i < from->grid->squares[from->current].npoints; i++)
1068 if (mask & (1 << i)) {
1069 points[j*2] = from->grid->squares[from->current].points[i*2];
1070 points[j*2+1] = from->grid->squares[from->current].points[i*2+1];
1071 skey[j] = i;
1072 j++;
1073 }
1074 assert(j == 2);
1075
1076 /*
1077 * Now find the other grid square which shares those points.
1078 * This is our move destination.
1079 */
1080 dest = -1;
1081 for (i = 0; i < from->grid->nsquares; i++)
1082 if (i != from->current) {
1083 int match = 0;
1084 float dist;
1085
1086 for (j = 0; j < from->grid->squares[i].npoints; j++) {
1087 dist = (SQ(from->grid->squares[i].points[j*2] - points[0]) +
1088 SQ(from->grid->squares[i].points[j*2+1] - points[1]));
1089 if (dist < 0.1)
1090 dkey[match++] = j;
1091 dist = (SQ(from->grid->squares[i].points[j*2] - points[2]) +
1092 SQ(from->grid->squares[i].points[j*2+1] - points[3]));
1093 if (dist < 0.1)
1094 dkey[match++] = j;
1095 }
1096
1097 if (match == 2) {
1098 dest = i;
1099 break;
1100 }
1101 }
1102
1103 return dest;
1104}
1105
1106static char *interpret_move(const game_state *state, game_ui *ui,
1107 const game_drawstate *ds,
1108 int x, int y, int button)
1109{
1110 int direction, mask, i;
1111 int skey[2], dkey[2];
1112
1113 button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
1114
1115 /*
1116 * Moves can be made with the cursor keys or numeric keypad, or
1117 * alternatively you can left-click and the polyhedron will
1118 * move in the general direction of the mouse pointer.
1119 */
1120 if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
1121 direction = UP;
1122 else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
1123 direction = DOWN;
1124 else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
1125 direction = LEFT;
1126 else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
1127 direction = RIGHT;
1128 else if (button == (MOD_NUM_KEYPAD | '7'))
1129 direction = UP_LEFT;
1130 else if (button == (MOD_NUM_KEYPAD | '1'))
1131 direction = DOWN_LEFT;
1132 else if (button == (MOD_NUM_KEYPAD | '9'))
1133 direction = UP_RIGHT;
1134 else if (button == (MOD_NUM_KEYPAD | '3'))
1135 direction = DOWN_RIGHT;
1136 else if (button == LEFT_BUTTON) {
1137 /*
1138 * Find the bearing of the click point from the current
1139 * square's centre.
1140 */
1141 int cx, cy;
1142 double angle;
1143
1144 cx = (int)(state->grid->squares[state->current].x * GRID_SCALE) + ds->ox;
1145 cy = (int)(state->grid->squares[state->current].y * GRID_SCALE) + ds->oy;
1146
1147 if (x == cx && y == cy)
1148 return NULL; /* clicked in exact centre! */
1149 angle = atan2(y - cy, x - cx);
1150
1151 /*
1152 * There are three possibilities.
1153 *
1154 * - This square is a square, so we choose between UP,
1155 * DOWN, LEFT and RIGHT by dividing the available angle
1156 * at the 45-degree points.
1157 *
1158 * - This square is an up-pointing triangle, so we choose
1159 * between DOWN, LEFT and RIGHT by dividing into
1160 * 120-degree arcs.
1161 *
1162 * - This square is a down-pointing triangle, so we choose
1163 * between UP, LEFT and RIGHT in the inverse manner.
1164 *
1165 * Don't forget that since our y-coordinates increase
1166 * downwards, `angle' is measured _clockwise_ from the
1167 * x-axis, not anticlockwise as most mathematicians would
1168 * instinctively assume.
1169 */
1170 if (state->grid->squares[state->current].npoints == 4) {
1171 /* Square. */
1172 if (fabs(angle) > 3*PI/4)
1173 direction = LEFT;
1174 else if (fabs(angle) < PI/4)
1175 direction = RIGHT;
1176 else if (angle > 0)
1177 direction = DOWN;
1178 else
1179 direction = UP;
1180 } else if (state->grid->squares[state->current].directions[UP] == 0) {
1181 /* Up-pointing triangle. */
1182 if (angle < -PI/2 || angle > 5*PI/6)
1183 direction = LEFT;
1184 else if (angle > PI/6)
1185 direction = DOWN;
1186 else
1187 direction = RIGHT;
1188 } else {
1189 /* Down-pointing triangle. */
1190 assert(state->grid->squares[state->current].directions[DOWN] == 0);
1191 if (angle > PI/2 || angle < -5*PI/6)
1192 direction = LEFT;
1193 else if (angle < -PI/6)
1194 direction = UP;
1195 else
1196 direction = RIGHT;
1197 }
1198 } else
1199 return NULL;
1200
1201 mask = state->grid->squares[state->current].directions[direction];
1202 if (mask == 0)
1203 return NULL;
1204
1205 /*
1206 * Translate diagonal directions into orthogonal ones.
1207 */
1208 if (direction > DOWN) {
1209 for (i = LEFT; i <= DOWN; i++)
1210 if (state->grid->squares[state->current].directions[i] == mask) {
1211 direction = i;
1212 break;
1213 }
1214 assert(direction <= DOWN);
1215 }
1216
1217 if (find_move_dest(state, direction, skey, dkey) < 0)
1218 return NULL;
1219
1220 if (direction == LEFT) return dupstr("L");
1221 if (direction == RIGHT) return dupstr("R");
1222 if (direction == UP) return dupstr("U");
1223 if (direction == DOWN) return dupstr("D");
1224
1225 return NULL; /* should never happen */
1226}
1227
1228static game_state *execute_move(const game_state *from, const char *move)
1229{
1230 game_state *ret;
1231 float angle;
1232 struct solid *poly;
1233 int pkey[2];
1234 int skey[2], dkey[2];
1235 int i, j, dest;
1236 int direction;
1237
1238 switch (*move) {
1239 case 'L': direction = LEFT; break;
1240 case 'R': direction = RIGHT; break;
1241 case 'U': direction = UP; break;
1242 case 'D': direction = DOWN; break;
1243 default: return NULL;
1244 }
1245
1246 dest = find_move_dest(from, direction, skey, dkey);
1247 if (dest < 0)
1248 return NULL;
1249
1250 ret = dup_game(from);
1251 ret->current = dest;
1252
1253 /*
1254 * So we know what grid square we're aiming for, and we also
1255 * know the two key points (as indices in both the source and
1256 * destination grid squares) which are invariant between source
1257 * and destination.
1258 *
1259 * Next we must roll the polyhedron on to that square. So we
1260 * find the indices of the key points within the polyhedron's
1261 * vertex array, then use those in a call to transform_poly,
1262 * and align the result on the new grid square.
1263 */
1264 {
1265 int all_pkey[4];
1266 align_poly(from->solid, &from->grid->squares[from->current], all_pkey);
1267 pkey[0] = all_pkey[skey[0]];
1268 pkey[1] = all_pkey[skey[1]];
1269 /*
1270 * Now pkey[0] corresponds to skey[0] and dkey[0], and
1271 * likewise [1].
1272 */
1273 }
1274
1275 /*
1276 * Now find the angle through which to rotate the polyhedron.
1277 * Do this by finding the two faces that share the two vertices
1278 * we've found, and taking the dot product of their normals.
1279 */
1280 {
1281 int f[2], nf = 0;
1282 float dp;
1283
1284 for (i = 0; i < from->solid->nfaces; i++) {
1285 int match = 0;
1286 for (j = 0; j < from->solid->order; j++)
1287 if (from->solid->faces[i*from->solid->order + j] == pkey[0] ||
1288 from->solid->faces[i*from->solid->order + j] == pkey[1])
1289 match++;
1290 if (match == 2) {
1291 assert(nf < 2);
1292 f[nf++] = i;
1293 }
1294 }
1295
1296 assert(nf == 2);
1297
1298 dp = 0;
1299 for (i = 0; i < 3; i++)
1300 dp += (from->solid->normals[f[0]*3+i] *
1301 from->solid->normals[f[1]*3+i]);
1302 angle = (float)acos(dp);
1303 }
1304
1305 /*
1306 * Now transform the polyhedron. We aren't entirely sure
1307 * whether we need to rotate through angle or -angle, and the
1308 * simplest way round this is to try both and see which one
1309 * aligns successfully!
1310 *
1311 * Unfortunately, _both_ will align successfully if this is a
1312 * cube, which won't tell us anything much. So for that
1313 * particular case, I resort to gross hackery: I simply negate
1314 * the angle before trying the alignment, depending on the
1315 * direction. Which directions work which way is determined by
1316 * pure trial and error. I said it was gross :-/
1317 */
1318 {
1319 int all_pkey[4];
1320 int success;
1321
1322 if (from->solid->order == 4 && direction == UP)
1323 angle = -angle; /* HACK */
1324
1325 poly = transform_poly(from->solid,
1326 from->grid->squares[from->current].flip,
1327 pkey[0], pkey[1], angle);
1328 flip_poly(poly, from->grid->squares[ret->current].flip);
1329 success = align_poly(poly, &from->grid->squares[ret->current], all_pkey);
1330
1331 if (!success) {
1332 sfree(poly);
1333 angle = -angle;
1334 poly = transform_poly(from->solid,
1335 from->grid->squares[from->current].flip,
1336 pkey[0], pkey[1], angle);
1337 flip_poly(poly, from->grid->squares[ret->current].flip);
1338 success = align_poly(poly, &from->grid->squares[ret->current], all_pkey);
1339 }
1340
1341 assert(success);
1342 }
1343
1344 /*
1345 * Now we have our rotated polyhedron, which we expect to be
1346 * exactly congruent to the one we started with - but with the
1347 * faces permuted. So we map that congruence and thereby figure
1348 * out how to permute the faces as a result of the polyhedron
1349 * having rolled.
1350 */
1351 {
1352 int *newcolours = snewn(from->solid->nfaces, int);
1353
1354 for (i = 0; i < from->solid->nfaces; i++)
1355 newcolours[i] = -1;
1356
1357 for (i = 0; i < from->solid->nfaces; i++) {
1358 int nmatch = 0;
1359
1360 /*
1361 * Now go through the transformed polyhedron's faces
1362 * and figure out which one's normal is approximately
1363 * equal to this one.
1364 */
1365 for (j = 0; j < poly->nfaces; j++) {
1366 float dist;
1367 int k;
1368
1369 dist = 0;
1370
1371 for (k = 0; k < 3; k++)
1372 dist += SQ(poly->normals[j*3+k] -
1373 from->solid->normals[i*3+k]);
1374
1375 if (APPROXEQ(dist, 0)) {
1376 nmatch++;
1377 newcolours[i] = ret->facecolours[j];
1378 }
1379 }
1380
1381 assert(nmatch == 1);
1382 }
1383
1384 for (i = 0; i < from->solid->nfaces; i++)
1385 assert(newcolours[i] != -1);
1386
1387 sfree(ret->facecolours);
1388 ret->facecolours = newcolours;
1389 }
1390
1391 ret->movecount++;
1392
1393 /*
1394 * And finally, swap the colour between the bottom face of the
1395 * polyhedron and the face we've just landed on.
1396 *
1397 * We don't do this if the game is already complete, since we
1398 * allow the user to roll the fully blue polyhedron around the
1399 * grid as a feeble reward.
1400 */
1401 if (!ret->completed) {
1402 i = lowest_face(from->solid);
1403 j = ret->facecolours[i];
1404 ret->facecolours[i] = GET_SQUARE(ret, ret->current);
1405 SET_SQUARE(ret, ret->current, j);
1406
1407 /*
1408 * Detect game completion.
1409 */
1410 j = 0;
1411 for (i = 0; i < ret->solid->nfaces; i++)
1412 if (ret->facecolours[i])
1413 j++;
1414 if (j == ret->solid->nfaces)
1415 ret->completed = ret->movecount;
1416 }
1417
1418 sfree(poly);
1419
1420 /*
1421 * Align the normal polyhedron with its grid square, to get key
1422 * points for non-animated display.
1423 */
1424 {
1425 int pkey[4];
1426 int success;
1427
1428 success = align_poly(ret->solid, &ret->grid->squares[ret->current], pkey);
1429 assert(success);
1430
1431 ret->dpkey[0] = pkey[0];
1432 ret->dpkey[1] = pkey[1];
1433 ret->dgkey[0] = 0;
1434 ret->dgkey[1] = 1;
1435 }
1436
1437
1438 ret->spkey[0] = pkey[0];
1439 ret->spkey[1] = pkey[1];
1440 ret->sgkey[0] = skey[0];
1441 ret->sgkey[1] = skey[1];
1442 ret->previous = from->current;
1443 ret->angle = angle;
1444
1445 return ret;
1446}
1447
1448/* ----------------------------------------------------------------------
1449 * Drawing routines.
1450 */
1451
1452struct bbox {
1453 float l, r, u, d;
1454};
1455
1456static void find_bbox_callback(void *ctx, struct grid_square *sq)
1457{
1458 struct bbox *bb = (struct bbox *)ctx;
1459 int i;
1460
1461 for (i = 0; i < sq->npoints; i++) {
1462 if (bb->l > sq->points[i*2]) bb->l = sq->points[i*2];
1463 if (bb->r < sq->points[i*2]) bb->r = sq->points[i*2];
1464 if (bb->u > sq->points[i*2+1]) bb->u = sq->points[i*2+1];
1465 if (bb->d < sq->points[i*2+1]) bb->d = sq->points[i*2+1];
1466 }
1467}
1468
1469static struct bbox find_bbox(const game_params *params)
1470{
1471 struct bbox bb;
1472
1473 /*
1474 * These should be hugely more than the real bounding box will
1475 * be.
1476 */
1477 bb.l = 2.0F * (params->d1 + params->d2);
1478 bb.r = -2.0F * (params->d1 + params->d2);
1479 bb.u = 2.0F * (params->d1 + params->d2);
1480 bb.d = -2.0F * (params->d1 + params->d2);
1481 enum_grid_squares(params, find_bbox_callback, &bb);
1482
1483 return bb;
1484}
1485
1486#define XSIZE(gs, bb, solid) \
1487 ((int)(((bb).r - (bb).l + 2*(solid)->border) * gs))
1488#define YSIZE(gs, bb, solid) \
1489 ((int)(((bb).d - (bb).u + 2*(solid)->border) * gs))
1490
1491static void game_compute_size(const game_params *params, int tilesize,
1492 int *x, int *y)
1493{
1494 struct bbox bb = find_bbox(params);
1495
1496 *x = XSIZE(tilesize, bb, solids[params->solid]);
1497 *y = YSIZE(tilesize, bb, solids[params->solid]);
1498}
1499
1500static void game_set_size(drawing *dr, game_drawstate *ds,
1501 const game_params *params, int tilesize)
1502{
1503 struct bbox bb = find_bbox(params);
1504
1505 ds->gridscale = (float)tilesize;
1506 ds->ox = (int)(-(bb.l - solids[params->solid]->border) * ds->gridscale);
1507 ds->oy = (int)(-(bb.u - solids[params->solid]->border) * ds->gridscale);
1508}
1509
1510static float *game_colours(frontend *fe, int *ncolours)
1511{
1512 float *ret = snewn(3 * NCOLOURS, float);
1513
1514 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1515
1516 ret[COL_BORDER * 3 + 0] = 0.0;
1517 ret[COL_BORDER * 3 + 1] = 0.0;
1518 ret[COL_BORDER * 3 + 2] = 0.0;
1519
1520 ret[COL_BLUE * 3 + 0] = 0.0;
1521 ret[COL_BLUE * 3 + 1] = 0.0;
1522 ret[COL_BLUE * 3 + 2] = 1.0;
1523
1524 *ncolours = NCOLOURS;
1525 return ret;
1526}
1527
1528static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1529{
1530 struct game_drawstate *ds = snew(struct game_drawstate);
1531
1532 ds->ox = ds->oy = 0;
1533 ds->gridscale = 0.0F; /* not decided yet */
1534
1535 return ds;
1536}
1537
1538static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1539{
1540 sfree(ds);
1541}
1542
1543static void game_redraw(drawing *dr, game_drawstate *ds,
1544 const game_state *oldstate, const game_state *state,
1545 int dir, const game_ui *ui,
1546 float animtime, float flashtime)
1547{
1548 int i, j;
1549 struct bbox bb = find_bbox(&state->params);
1550 struct solid *poly;
1551 const int *pkey, *gkey;
1552 float t[3];
1553 float angle;
1554 int square;
1555
1556 draw_rect(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid),
1557 YSIZE(GRID_SCALE, bb, state->solid), COL_BACKGROUND);
1558
1559 if (dir < 0) {
1560 const game_state *t;
1561
1562 /*
1563 * This is an Undo. So reverse the order of the states, and
1564 * run the roll timer backwards.
1565 */
1566 assert(oldstate);
1567
1568 t = oldstate;
1569 oldstate = state;
1570 state = t;
1571
1572 animtime = ROLLTIME - animtime;
1573 }
1574
1575 if (!oldstate) {
1576 oldstate = state;
1577 angle = 0.0;
1578 square = state->current;
1579 pkey = state->dpkey;
1580 gkey = state->dgkey;
1581 } else {
1582 angle = state->angle * animtime / ROLLTIME;
1583 square = state->previous;
1584 pkey = state->spkey;
1585 gkey = state->sgkey;
1586 }
1587 state = oldstate;
1588
1589 for (i = 0; i < state->grid->nsquares; i++) {
1590 int coords[8];
1591
1592 for (j = 0; j < state->grid->squares[i].npoints; j++) {
1593 coords[2*j] = ((int)(state->grid->squares[i].points[2*j] * GRID_SCALE)
1594 + ds->ox);
1595 coords[2*j+1] = ((int)(state->grid->squares[i].points[2*j+1]*GRID_SCALE)
1596 + ds->oy);
1597 }
1598
1599 draw_polygon(dr, coords, state->grid->squares[i].npoints,
1600 GET_SQUARE(state, i) ? COL_BLUE : COL_BACKGROUND,
1601 COL_BORDER);
1602 }
1603
1604 /*
1605 * Now compute and draw the polyhedron.
1606 */
1607 poly = transform_poly(state->solid, state->grid->squares[square].flip,
1608 pkey[0], pkey[1], angle);
1609
1610 /*
1611 * Compute the translation required to align the two key points
1612 * on the polyhedron with the same key points on the current
1613 * face.
1614 */
1615 for (i = 0; i < 3; i++) {
1616 float tc = 0.0;
1617
1618 for (j = 0; j < 2; j++) {
1619 float grid_coord;
1620
1621 if (i < 2) {
1622 grid_coord =
1623 state->grid->squares[square].points[gkey[j]*2+i];
1624 } else {
1625 grid_coord = 0.0;
1626 }
1627
1628 tc += (grid_coord - poly->vertices[pkey[j]*3+i]);
1629 }
1630
1631 t[i] = tc / 2;
1632 }
1633 for (i = 0; i < poly->nvertices; i++)
1634 for (j = 0; j < 3; j++)
1635 poly->vertices[i*3+j] += t[j];
1636
1637 /*
1638 * Now actually draw each face.
1639 */
1640 for (i = 0; i < poly->nfaces; i++) {
1641 float points[8];
1642 int coords[8];
1643
1644 for (j = 0; j < poly->order; j++) {
1645 int f = poly->faces[i*poly->order + j];
1646 points[j*2] = (poly->vertices[f*3+0] -
1647 poly->vertices[f*3+2] * poly->shear);
1648 points[j*2+1] = (poly->vertices[f*3+1] -
1649 poly->vertices[f*3+2] * poly->shear);
1650 }
1651
1652 for (j = 0; j < poly->order; j++) {
1653 coords[j*2] = (int)floor(points[j*2] * GRID_SCALE) + ds->ox;
1654 coords[j*2+1] = (int)floor(points[j*2+1] * GRID_SCALE) + ds->oy;
1655 }
1656
1657 /*
1658 * Find out whether these points are in a clockwise or
1659 * anticlockwise arrangement. If the latter, discard the
1660 * face because it's facing away from the viewer.
1661 *
1662 * This would involve fiddly winding-number stuff for a
1663 * general polygon, but for the simple parallelograms we'll
1664 * be seeing here, all we have to do is check whether the
1665 * corners turn right or left. So we'll take the vector
1666 * from point 0 to point 1, turn it right 90 degrees,
1667 * and check the sign of the dot product with that and the
1668 * next vector (point 1 to point 2).
1669 */
1670 {
1671 float v1x = points[2]-points[0];
1672 float v1y = points[3]-points[1];
1673 float v2x = points[4]-points[2];
1674 float v2y = points[5]-points[3];
1675 float dp = v1x * v2y - v1y * v2x;
1676
1677 if (dp <= 0)
1678 continue;
1679 }
1680
1681 draw_polygon(dr, coords, poly->order,
1682 state->facecolours[i] ? COL_BLUE : COL_BACKGROUND,
1683 COL_BORDER);
1684 }
1685 sfree(poly);
1686
1687 draw_update(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid),
1688 YSIZE(GRID_SCALE, bb, state->solid));
1689
1690 /*
1691 * Update the status bar.
1692 */
1693 {
1694 char statusbuf[256];
1695
1696 sprintf(statusbuf, "%sMoves: %d",
1697 (state->completed ? "COMPLETED! " : ""),
1698 (state->completed ? state->completed : state->movecount));
1699
1700 status_bar(dr, statusbuf);
1701 }
1702}
1703
1704static float game_anim_length(const game_state *oldstate,
1705 const game_state *newstate, int dir, game_ui *ui)
1706{
1707 return ROLLTIME;
1708}
1709
1710static float game_flash_length(const game_state *oldstate,
1711 const game_state *newstate, int dir, game_ui *ui)
1712{
1713 return 0.0F;
1714}
1715
1716static int game_status(const game_state *state)
1717{
1718 return state->completed ? +1 : 0;
1719}
1720
1721static int game_timing_state(const game_state *state, game_ui *ui)
1722{
1723 return TRUE;
1724}
1725
1726static void game_print_size(const game_params *params, float *x, float *y)
1727{
1728}
1729
1730static void game_print(drawing *dr, const game_state *state, int tilesize)
1731{
1732}
1733
1734#ifdef COMBINED
1735#define thegame cube
1736#endif
1737
1738const struct game thegame = {
1739 "Cube", "games.cube", "cube",
1740 default_params,
1741 game_fetch_preset,
1742 decode_params,
1743 encode_params,
1744 free_params,
1745 dup_params,
1746 TRUE, game_configure, custom_params,
1747 validate_params,
1748 new_game_desc,
1749 validate_desc,
1750 new_game,
1751 dup_game,
1752 free_game,
1753 FALSE, solve_game,
1754 FALSE, game_can_format_as_text_now, game_text_format,
1755 new_ui,
1756 free_ui,
1757 encode_ui,
1758 decode_ui,
1759 game_changed_state,
1760 interpret_move,
1761 execute_move,
1762 PREFERRED_GRID_SCALE, game_compute_size, game_set_size,
1763 game_colours,
1764 game_new_drawstate,
1765 game_free_drawstate,
1766 game_redraw,
1767 game_anim_length,
1768 game_flash_length,
1769 game_status,
1770 FALSE, FALSE, game_print_size, game_print,
1771 TRUE, /* wants_statusbar */
1772 FALSE, game_timing_state,
1773 0, /* flags */
1774};
diff --git a/apps/plugins/puzzles/desktop.pl b/apps/plugins/puzzles/desktop.pl
new file mode 100755
index 0000000000..204c0ce262
--- /dev/null
+++ b/apps/plugins/puzzles/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/devel.but b/apps/plugins/puzzles/devel.but
new file mode 100644
index 0000000000..9befcadcb7
--- /dev/null
+++ b/apps/plugins/puzzles/devel.but
@@ -0,0 +1,4777 @@
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 used to populate the \q{Type} menu, which provides
395a list of conveniently accessible preset parameters for most games.
396
397The function is called with \c{i} equal to the index of the preset
398required (numbering from zero). It returns \cw{FALSE} if that preset
399does not exist (if \c{i} is less than zero or greater than the
400largest preset index). Otherwise, it sets \c{*params} to point at a
401newly allocated \c{game_params} structure containing the preset
402information, sets \c{*name} to point at a newly allocated C string
403containing the preset title (to go on the \q{Type} menu), and
404returns \cw{TRUE}.
405
406If the game does not wish to support any presets at all, this
407function is permitted to return \cw{FALSE} always.
408
409\S{backend-encode-params} \cw{encode_params()}
410
411\c char *(*encode_params)(const game_params *params, int full);
412
413The job of this function is to take a \c{game_params}, and encode it
414in a string form for use in game IDs. The return value must be a
415newly allocated C string, and \e{must} not contain a colon or a hash
416(since those characters are used to mark the end of the parameter
417section in a game ID).
418
419Ideally, it should also not contain any other potentially
420controversial punctuation; bear in mind when designing a string
421parameter format that it will probably be used on both Windows and
422Unix command lines under a variety of exciting shell quoting and
423metacharacter rules. Sticking entirely to alphanumerics is the
424safest thing; if you really need punctuation, you can probably get
425away with commas, periods or underscores without causing anybody any
426major inconvenience. If you venture far beyond that, you're likely
427to irritate \e{somebody}.
428
429(At the time of writing this, all existing games have purely
430alphanumeric string parameter formats. Usually these involve a
431letter denoting a parameter, followed optionally by a number giving
432the value of that parameter, with a few mandatory parts at the
433beginning such as numeric width and height separated by \cq{x}.)
434
435If the \c{full} parameter is \cw{TRUE}, this function should encode
436absolutely everything in the \c{game_params}, such that a subsequent
437call to \cw{decode_params()} (\k{backend-decode-params}) will yield
438an identical structure. If \c{full} is \cw{FALSE}, however, you
439should leave out anything which is not necessary to describe a
440\e{specific puzzle instance}, i.e. anything which only takes effect
441when a new puzzle is \e{generated}. For example, the Solo
442\c{game_params} includes a difficulty rating used when constructing
443new puzzles; but a Solo game ID need not explicitly include the
444difficulty, since to describe a puzzle once generated it's
445sufficient to give the grid dimensions and the location and contents
446of the clue squares. (Indeed, one might very easily type in a puzzle
447out of a newspaper without \e{knowing} what its difficulty level is
448in Solo's terminology.) Therefore, Solo's \cw{encode_params()} only
449encodes the difficulty level if \c{full} is set.
450
451\S{backend-decode-params} \cw{decode_params()}
452
453\c void (*decode_params)(game_params *params, char const *string);
454
455This function is the inverse of \cw{encode_params()}
456(\k{backend-encode-params}). It parses the supplied string and fills
457in the supplied \c{game_params} structure. Note that the structure
458will \e{already} have been allocated: this function is not expected
459to create a \e{new} \c{game_params}, but to modify an existing one.
460
461This function can receive a string which only encodes a subset of
462the parameters. The most obvious way in which this can happen is if
463the string was constructed by \cw{encode_params()} with its \c{full}
464parameter set to \cw{FALSE}; however, it could also happen if the
465user typed in a parameter set manually and missed something out. Be
466prepared to deal with a wide range of possibilities.
467
468When dealing with a parameter which is not specified in the input
469string, what to do requires a judgment call on the part of the
470programmer. Sometimes it makes sense to adjust other parameters to
471bring them into line with the new ones. In Mines, for example, you
472would probably not want to keep the same mine count if the user
473dropped the grid size and didn't specify one, since you might easily
474end up with more mines than would actually fit in the grid! On the
475other hand, sometimes it makes sense to leave the parameter alone: a
476Solo player might reasonably expect to be able to configure size and
477difficulty independently of one another.
478
479This function currently has no direct means of returning an error if
480the string cannot be parsed at all. However, the returned
481\c{game_params} is almost always subsequently passed to
482\cw{validate_params()} (\k{backend-validate-params}), so if you
483really want to signal parse errors, you could always have a \c{char
484*} in your parameters structure which stored an error message, and
485have \cw{validate_params()} return it if it is non-\cw{NULL}.
486
487\S{backend-free-params} \cw{free_params()}
488
489\c void (*free_params)(game_params *params);
490
491This function frees a \c{game_params} structure, and any subsidiary
492allocations contained within it.
493
494\S{backend-dup-params} \cw{dup_params()}
495
496\c game_params *(*dup_params)(const game_params *params);
497
498This function allocates a new \c{game_params} structure and
499initialises it with an exact copy of the information in the one
500provided as input. It returns a pointer to the new duplicate.
501
502\S{backend-can-configure} \c{can_configure}
503
504\c int can_configure;
505
506This boolean data element is set to \cw{TRUE} if the back end
507supports custom parameter configuration via a dialog box. If it is
508\cw{TRUE}, then the functions \cw{configure()} and
509\cw{custom_params()} are expected to work. See \k{backend-configure}
510and \k{backend-custom-params} for more details.
511
512\S{backend-configure} \cw{configure()}
513
514\c config_item *(*configure)(const game_params *params);
515
516This function is called when the user requests a dialog box for
517custom parameter configuration. It returns a newly allocated array
518of \cw{config_item} structures, describing the GUI elements required
519in the dialog box. The array should have one more element than the
520number of controls, since it is terminated with a \cw{C_END} marker
521(see below). Each array element describes the control together with
522its initial value; the front end will modify the value fields and
523return the updated array to \cw{custom_params()} (see
524\k{backend-custom-params}).
525
526The \cw{config_item} structure contains the following elements:
527
528\c char *name;
529\c int type;
530\c char *sval;
531\c int ival;
532
533\c{name} is an ASCII string giving the textual label for a GUI
534control. It is \e{not} expected to be dynamically allocated.
535
536\c{type} contains one of a small number of \c{enum} values defining
537what type of control is being described. The meaning of the \c{sval}
538and \c{ival} fields depends on the value in \c{type}. The valid
539values are:
540
541\dt \c{C_STRING}
542
543\dd Describes a text input box. (This is also used for numeric
544input. The back end does not bother informing the front end that the
545box is numeric rather than textual; some front ends do have the
546capacity to take this into account, but I decided it wasn't worth
547the extra complexity in the interface.) For this type, \c{ival} is
548unused, and \c{sval} contains a dynamically allocated string
549representing the contents of the input box.
550
551\dt \c{C_BOOLEAN}
552
553\dd Describes a simple checkbox. For this type, \c{sval} is unused,
554and \c{ival} is \cw{TRUE} or \cw{FALSE}.
555
556\dt \c{C_CHOICES}
557
558\dd Describes a drop-down list presenting one of a small number of
559fixed choices. For this type, \c{sval} contains a list of strings
560describing the choices; the very first character of \c{sval} is used
561as a delimiter when processing the rest (so that the strings
562\cq{:zero:one:two}, \cq{!zero!one!two} and \cq{xzeroxonextwo} all
563define a three-element list containing \cq{zero}, \cq{one} and
564\cq{two}). \c{ival} contains the index of the currently selected
565element, numbering from zero (so that in the above example, 0 would
566mean \cq{zero} and 2 would mean \cq{two}).
567
568\lcont{
569
570Note that for this control type, \c{sval} is \e{not} dynamically
571allocated, whereas it was for \c{C_STRING}.
572
573}
574
575\dt \c{C_END}
576
577\dd Marks the end of the array of \c{config_item}s. All other fields
578are unused.
579
580The array returned from this function is expected to have filled in
581the initial values of all the controls according to the input
582\c{game_params} structure.
583
584If the game's \c{can_configure} flag is set to \cw{FALSE}, this
585function is never called and need not do anything at all.
586
587\S{backend-custom-params} \cw{custom_params()}
588
589\c game_params *(*custom_params)(const config_item *cfg);
590
591This function is the counterpart to \cw{configure()}
592(\k{backend-configure}). It receives as input an array of
593\c{config_item}s which was originally created by \cw{configure()},
594but in which the control values have since been changed in
595accordance with user input. Its function is to read the new values
596out of the controls and return a newly allocated \c{game_params}
597structure representing the user's chosen parameter set.
598
599(The front end will have modified the controls' \e{values}, but
600there will still always be the same set of controls, in the same
601order, as provided by \cw{configure()}. It is not necessary to check
602the \c{name} and \c{type} fields, although you could use
603\cw{assert()} if you were feeling energetic.)
604
605This function is not expected to (and indeed \e{must not}) free the
606input \c{config_item} array. (If the parameters fail to validate,
607the dialog box will stay open.)
608
609If the game's \c{can_configure} flag is set to \cw{FALSE}, this
610function is never called and need not do anything at all.
611
612\S{backend-validate-params} \cw{validate_params()}
613
614\c char *(*validate_params)(const game_params *params, int full);
615
616This function takes a \c{game_params} structure as input, and checks
617that the parameters described in it fall within sensible limits. (At
618the very least, grid dimensions should almost certainly be strictly
619positive, for example.)
620
621Return value is \cw{NULL} if no problems were found, or
622alternatively a (non-dynamically-allocated) ASCII string describing
623the error in human-readable form.
624
625If the \c{full} parameter is set, full validation should be
626performed: any set of parameters which would not permit generation
627of a sensible puzzle should be faulted. If \c{full} is \e{not} set,
628the implication is that these parameters are not going to be used
629for \e{generating} a puzzle; so parameters which can't even sensibly
630\e{describe} a valid puzzle should still be faulted, but parameters
631which only affect puzzle generation should not be.
632
633(The \c{full} option makes a difference when parameter combinations
634are non-orthogonal. For example, Net has a boolean option
635controlling whether it enforces a unique solution; it turns out that
636it's impossible to generate a uniquely soluble puzzle with wrapping
637walls and width 2, so \cw{validate_params()} will complain if you
638ask for one. However, if the user had just been playing a unique
639wrapping puzzle of a more sensible width, and then pastes in a game
640ID acquired from somebody else which happens to describe a
641\e{non}-unique wrapping width-2 puzzle, then \cw{validate_params()}
642will be passed a \c{game_params} containing the width and wrapping
643settings from the new game ID and the uniqueness setting from the
644old one. This would be faulted, if it weren't for the fact that
645\c{full} is not set during this call, so Net ignores the
646inconsistency. The resulting \c{game_params} is never subsequently
647used to generate a puzzle; this is a promise made by the mid-end
648when it asks for a non-full validation.)
649
650\H{backend-descs} Handling game descriptions
651
652In this section I present the functions that deal with a textual
653description of a puzzle, i.e. the part that comes after the colon in
654a descriptive-format game ID.
655
656\S{backend-new-desc} \cw{new_desc()}
657
658\c char *(*new_desc)(const game_params *params, random_state *rs,
659\c char **aux, int interactive);
660
661This function is where all the really hard work gets done. This is
662the function whose job is to randomly generate a new puzzle,
663ensuring solubility and uniqueness as appropriate.
664
665As input it is given a \c{game_params} structure and a random state
666(see \k{utils-random} for the random number API). It must invent a
667puzzle instance, encode it in string form, and return a dynamically
668allocated C string containing that encoding.
669
670Additionally, it may return a second dynamically allocated string in
671\c{*aux}. (If it doesn't want to, then it can leave that parameter
672completely alone; it isn't required to set it to \cw{NULL}, although
673doing so is harmless.) That string, if present, will be passed to
674\cw{solve()} (\k{backend-solve}) later on; so if the puzzle is
675generated in such a way that a solution is known, then information
676about that solution can be saved in \c{*aux} for \cw{solve()} to
677use.
678
679The \c{interactive} parameter should be ignored by almost all
680puzzles. Its purpose is to distinguish between generating a puzzle
681within a GUI context for immediate play, and generating a puzzle in
682a command-line context for saving to be played later. The only
683puzzle that currently uses this distinction (and, I fervently hope,
684the only one which will \e{ever} need to use it) is Mines, which
685chooses a random first-click location when generating puzzles
686non-interactively, but which waits for the user to place the first
687click when interactive. If you think you have come up with another
688puzzle which needs to make use of this parameter, please think for
689at least ten minutes about whether there is \e{any} alternative!
690
691Note that game description strings are not required to contain an
692encoding of parameters such as grid size; a game description is
693never separated from the \c{game_params} it was generated with, so
694any information contained in that structure need not be encoded
695again in the game description.
696
697\S{backend-validate-desc} \cw{validate_desc()}
698
699\c char *(*validate_desc)(const game_params *params, const char *desc);
700
701This function is given a game description, and its job is to
702validate that it describes a puzzle which makes sense.
703
704To some extent it's up to the user exactly how far they take the
705phrase \q{makes sense}; there are no particularly strict rules about
706how hard the user is permitted to shoot themself in the foot when
707typing in a bogus game description by hand. (For example, Rectangles
708will not verify that the sum of all the numbers in the grid equals
709the grid's area. So a user could enter a puzzle which was provably
710not soluble, and the program wouldn't complain; there just wouldn't
711happen to be any sequence of moves which solved it.)
712
713The one non-negotiable criterion is that any game description which
714makes it through \cw{validate_desc()} \e{must not} subsequently
715cause a crash or an assertion failure when fed to \cw{new_game()}
716and thence to the rest of the back end.
717
718The return value is \cw{NULL} on success, or a
719non-dynamically-allocated C string containing an error message.
720
721\S{backend-new-game} \cw{new_game()}
722
723\c game_state *(*new_game)(midend *me, const game_params *params,
724\c const char *desc);
725
726This function takes a game description as input, together with its
727accompanying \c{game_params}, and constructs a \c{game_state}
728describing the initial state of the puzzle. It returns a newly
729allocated \c{game_state} structure.
730
731Almost all puzzles should ignore the \c{me} parameter. It is
732required by Mines, which needs it for later passing to
733\cw{midend_supersede_game_desc()} (see \k{backend-supersede}) once
734the user has placed the first click. I fervently hope that no other
735puzzle will be awkward enough to require it, so everybody else
736should ignore it. As with the \c{interactive} parameter in
737\cw{new_desc()} (\k{backend-new-desc}), if you think you have a
738reason to need this parameter, please try very hard to think of an
739alternative approach!
740
741\H{backend-states} Handling game states
742
743This section describes the functions which create and destroy
744\c{game_state} structures.
745
746(Well, except \cw{new_game()}, which is in \k{backend-new-game}
747instead of under here; but it deals with game descriptions \e{and}
748game states and it had to go in one section or the other.)
749
750\S{backend-dup-game} \cw{dup_game()}
751
752\c game_state *(*dup_game)(const game_state *state);
753
754This function allocates a new \c{game_state} structure and
755initialises it with an exact copy of the information in the one
756provided as input. It returns a pointer to the new duplicate.
757
758\S{backend-free-game} \cw{free_game()}
759
760\c void (*free_game)(game_state *state);
761
762This function frees a \c{game_state} structure, and any subsidiary
763allocations contained within it.
764
765\H{backend-ui} Handling \c{game_ui}
766
767\S{backend-new-ui} \cw{new_ui()}
768
769\c game_ui *(*new_ui)(const game_state *state);
770
771This function allocates and returns a new \c{game_ui} structure for
772playing a particular puzzle. It is passed a pointer to the initial
773\c{game_state}, in case it needs to refer to that when setting up
774the initial values for the new game.
775
776\S{backend-free-ui} \cw{free_ui()}
777
778\c void (*free_ui)(game_ui *ui);
779
780This function frees a \c{game_ui} structure, and any subsidiary
781allocations contained within it.
782
783\S{backend-encode-ui} \cw{encode_ui()}
784
785\c char *(*encode_ui)(const game_ui *ui);
786
787This function encodes any \e{important} data in a \c{game_ui}
788structure in string form. It is only called when saving a
789half-finished game to a file.
790
791It should be used sparingly. Almost all data in a \c{game_ui} is not
792important enough to save. The location of the keyboard-controlled
793cursor, for example, can be reset to a default position on reloading
794the game without impacting the user experience. If the user should
795somehow manage to save a game while a mouse drag was in progress,
796then discarding that mouse drag would be an outright \e{feature}.
797
798A typical thing that \e{would} be worth encoding in this function is
799the Mines death counter: it's in the \c{game_ui} rather than the
800\c{game_state} because it's too important to allow the user to
801revert it by using Undo, and therefore it's also too important to
802allow the user to revert it by saving and reloading. (Of course, the
803user could edit the save file by hand... But if the user is \e{that}
804determined to cheat, they could just as easily modify the game's
805source.)
806
807\S{backend-decode-ui} \cw{decode_ui()}
808
809\c void (*decode_ui)(game_ui *ui, const char *encoding);
810
811This function parses a string previously output by \cw{encode_ui()},
812and writes the decoded data back into the provided \c{game_ui}
813structure.
814
815\S{backend-changed-state} \cw{changed_state()}
816
817\c void (*changed_state)(game_ui *ui, const game_state *oldstate,
818\c const game_state *newstate);
819
820This function is called by the mid-end whenever the current game
821state changes, for any reason. Those reasons include:
822
823\b a fresh move being made by \cw{interpret_move()} and
824\cw{execute_move()}
825
826\b a solve operation being performed by \cw{solve()} and
827\cw{execute_move()}
828
829\b the user moving back and forth along the undo list by means of
830the Undo and Redo operations
831
832\b the user selecting Restart to go back to the initial game state.
833
834The job of \cw{changed_state()} is to update the \c{game_ui} for
835consistency with the new game state, if any update is necessary. For
836example, Same Game stores data about the currently selected tile
837group in its \c{game_ui}, and this data is intrinsically related to
838the game state it was derived from. So it's very likely to become
839invalid when the game state changes; thus, Same Game's
840\cw{changed_state()} function clears the current selection whenever
841it is called.
842
843When \cw{anim_length()} or \cw{flash_length()} are called, you can
844be sure that there has been a previous call to \cw{changed_state()}.
845So \cw{changed_state()} can set up data in the \c{game_ui} which will
846be read by \cw{anim_length()} and \cw{flash_length()}, and those
847functions will not have to worry about being called without the data
848having been initialised.
849
850\H{backend-moves} Making moves
851
852This section describes the functions which actually make moves in
853the game: that is, the functions which process user input and end up
854producing new \c{game_state}s.
855
856\S{backend-interpret-move} \cw{interpret_move()}
857
858\c char *(*interpret_move)(const game_state *state, game_ui *ui,
859\c const game_drawstate *ds,
860\c int x, int y, int button);
861
862This function receives user input and processes it. Its input
863parameters are the current \c{game_state}, the current \c{game_ui}
864and the current \c{game_drawstate}, plus details of the input event.
865\c{button} is either an ASCII value or a special code (listed below)
866indicating an arrow or function key or a mouse event; when
867\c{button} is a mouse event, \c{x} and \c{y} contain the pixel
868coordinates of the mouse pointer relative to the top left of the
869puzzle's drawing area.
870
871(The pointer to the \c{game_drawstate} is marked \c{const}, because
872\c{interpret_move} should not write to it. The normal use of that
873pointer will be to read the game's tile size parameter in order to
874divide mouse coordinates by it.)
875
876\cw{interpret_move()} may return in three different ways:
877
878\b Returning \cw{NULL} indicates that no action whatsoever occurred
879in response to the input event; the puzzle was not interested in it
880at all.
881
882\b Returning the empty string (\cw{""}) indicates that the input
883event has resulted in a change being made to the \c{game_ui} which
884will require a redraw of the game window, but that no actual
885\e{move} was made (i.e. no new \c{game_state} needs to be created).
886
887\b Returning anything else indicates that a move was made and that a
888new \c{game_state} must be created. However, instead of actually
889constructing a new \c{game_state} itself, this function is required
890to return a string description of the details of the move. This
891string will be passed to \cw{execute_move()}
892(\k{backend-execute-move}) to actually create the new
893\c{game_state}. (Encoding moves as strings in this way means that
894the mid-end can keep the strings as well as the game states, and the
895strings can be written to disk when saving the game and fed to
896\cw{execute_move()} again on reloading.)
897
898The return value from \cw{interpret_move()} is expected to be
899dynamically allocated if and only if it is not either \cw{NULL}
900\e{or} the empty string.
901
902After this function is called, the back end is permitted to rely on
903some subsequent operations happening in sequence:
904
905\b \cw{execute_move()} will be called to convert this move
906description into a new \c{game_state}
907
908\b \cw{changed_state()} will be called with the new \c{game_state}.
909
910This means that if \cw{interpret_move()} needs to do updates to the
911\c{game_ui} which are easier to perform by referring to the new
912\c{game_state}, it can safely leave them to be done in
913\cw{changed_state()} and not worry about them failing to happen.
914
915(Note, however, that \cw{execute_move()} may \e{also} be called in
916other circumstances. It is only \cw{interpret_move()} which can rely
917on a subsequent call to \cw{changed_state()}.)
918
919The special key codes supported by this function are:
920
921\dt \cw{LEFT_BUTTON}, \cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON}
922
923\dd Indicate that one of the mouse buttons was pressed down.
924
925\dt \cw{LEFT_DRAG}, \cw{MIDDLE_DRAG}, \cw{RIGHT_DRAG}
926
927\dd Indicate that the mouse was moved while one of the mouse buttons
928was still down. The mid-end guarantees that when one of these events
929is received, it will always have been preceded by a button-down
930event (and possibly other drag events) for the same mouse button,
931and no event involving another mouse button will have appeared in
932between.
933
934\dt \cw{LEFT_RELEASE}, \cw{MIDDLE_RELEASE}, \cw{RIGHT_RELEASE}
935
936\dd Indicate that a mouse button was released. The mid-end
937guarantees that when one of these events is received, it will always
938have been preceded by a button-down event (and possibly some drag
939events) for the same mouse button, and no event involving another
940mouse button will have appeared in between.
941
942\dt \cw{CURSOR_UP}, \cw{CURSOR_DOWN}, \cw{CURSOR_LEFT},
943\cw{CURSOR_RIGHT}
944
945\dd Indicate that an arrow key was pressed.
946
947\dt \cw{CURSOR_SELECT}
948
949\dd On platforms which have a prominent \q{select} button alongside
950their cursor keys, indicates that that button was pressed.
951
952In addition, there are some modifiers which can be bitwise-ORed into
953the \c{button} parameter:
954
955\dt \cw{MOD_CTRL}, \cw{MOD_SHFT}
956
957\dd These indicate that the Control or Shift key was pressed
958alongside the key. They only apply to the cursor keys, not to mouse
959buttons or anything else.
960
961\dt \cw{MOD_NUM_KEYPAD}
962
963\dd This applies to some ASCII values, and indicates that the key
964code was input via the numeric keypad rather than the main keyboard.
965Some puzzles may wish to treat this differently (for example, a
966puzzle might want to use the numeric keypad as an eight-way
967directional pad), whereas others might not (a game involving numeric
968input probably just wants to treat the numeric keypad as numbers).
969
970\dt \cw{MOD_MASK}
971
972\dd This mask is the bitwise OR of all the available modifiers; you
973can bitwise-AND with \cw{~MOD_MASK} to strip all the modifiers off
974any input value.
975
976\S{backend-execute-move} \cw{execute_move()}
977
978\c game_state *(*execute_move)(const game_state *state, char *move);
979
980This function takes an input \c{game_state} and a move string as
981output from \cw{interpret_move()}. It returns a newly allocated
982\c{game_state} which contains the result of applying the specified
983move to the input game state.
984
985This function may return \cw{NULL} if it cannot parse the move
986string (and this is definitely preferable to crashing or failing an
987assertion, since one way this can happen is if loading a corrupt
988save file). However, it must not return \cw{NULL} for any move
989string that really was output from \cw{interpret_move()}: this is
990punishable by assertion failure in the mid-end.
991
992\S{backend-can-solve} \c{can_solve}
993
994\c int can_solve;
995
996This boolean field is set to \cw{TRUE} if the game's \cw{solve()}
997function does something. If it's set to \cw{FALSE}, the game will
998not even offer the \q{Solve} menu option.
999
1000\S{backend-solve} \cw{solve()}
1001
1002\c char *(*solve)(const game_state *orig, const game_state *curr,
1003\c const char *aux, char **error);
1004
1005This function is called when the user selects the \q{Solve} option
1006from the menu.
1007
1008It is passed two input game states: \c{orig} is the game state from
1009the very start of the puzzle, and \c{curr} is the current one.
1010(Different games find one or other or both of these convenient.) It
1011is also passed the \c{aux} string saved by \cw{new_desc()}
1012(\k{backend-new-desc}), in case that encodes important information
1013needed to provide the solution.
1014
1015If this function is unable to produce a solution (perhaps, for
1016example, the game has no in-built solver so it can only solve
1017puzzles it invented internally and has an \c{aux} string for) then
1018it may return \cw{NULL}. If it does this, it must also set
1019\c{*error} to an error message to be presented to the user (such as
1020\q{Solution not known for this puzzle}); that error message is not
1021expected to be dynamically allocated.
1022
1023If this function \e{does} produce a solution, it returns a move string
1024suitable for feeding to \cw{execute_move()}
1025(\k{backend-execute-move}). Like a (non-empty) string returned from
1026\cw{interpret_move()}, the returned string should be dynamically
1027allocated.
1028
1029\H{backend-drawing} Drawing the game graphics
1030
1031This section discusses the back end functions that deal with
1032drawing.
1033
1034\S{backend-new-drawstate} \cw{new_drawstate()}
1035
1036\c game_drawstate *(*new_drawstate)(drawing *dr,
1037\c const game_state *state);
1038
1039This function allocates and returns a new \c{game_drawstate}
1040structure for drawing a particular puzzle. It is passed a pointer to
1041a \c{game_state}, in case it needs to refer to that when setting up
1042any initial data.
1043
1044This function may not rely on the puzzle having been newly started;
1045a new draw state can be constructed at any time if the front end
1046requests a forced redraw. For games like Pattern, in which initial
1047game states are much simpler than general ones, this might be
1048important to keep in mind.
1049
1050The parameter \c{dr} is a drawing object (see \k{drawing}) which the
1051function might need to use to allocate blitters. (However, this
1052isn't recommended; it's usually more sensible to wait to allocate a
1053blitter until \cw{set_size()} is called, because that way you can
1054tailor it to the scale at which the puzzle is being drawn.)
1055
1056\S{backend-free-drawstate} \cw{free_drawstate()}
1057
1058\c void (*free_drawstate)(drawing *dr, game_drawstate *ds);
1059
1060This function frees a \c{game_drawstate} structure, and any
1061subsidiary allocations contained within it.
1062
1063The parameter \c{dr} is a drawing object (see \k{drawing}), which
1064might be required if you are freeing a blitter.
1065
1066\S{backend-preferred-tilesize} \c{preferred_tilesize}
1067
1068\c int preferred_tilesize;
1069
1070Each game is required to define a single integer parameter which
1071expresses, in some sense, the scale at which it is drawn. This is
1072described in the APIs as \cq{tilesize}, since most puzzles are on a
1073square (or possibly triangular or hexagonal) grid and hence a
1074sensible interpretation of this parameter is to define it as the
1075size of one grid tile in pixels; however, there's no actual
1076requirement that the \q{tile size} be proportional to the game
1077window size. Window size is required to increase monotonically with
1078\q{tile size}, however.
1079
1080The data element \c{preferred_tilesize} indicates the tile size
1081which should be used in the absence of a good reason to do otherwise
1082(such as the screen being too small, or the user explicitly
1083requesting a resize if that ever gets implemented).
1084
1085\S{backend-compute-size} \cw{compute_size()}
1086
1087\c void (*compute_size)(const game_params *params, int tilesize,
1088\c int *x, int *y);
1089
1090This function is passed a \c{game_params} structure and a tile size.
1091It returns, in \c{*x} and \c{*y}, the size in pixels of the drawing
1092area that would be required to render a puzzle with those parameters
1093at that tile size.
1094
1095\S{backend-set-size} \cw{set_size()}
1096
1097\c void (*set_size)(drawing *dr, game_drawstate *ds,
1098\c const game_params *params, int tilesize);
1099
1100This function is responsible for setting up a \c{game_drawstate} to
1101draw at a given tile size. Typically this will simply involve
1102copying the supplied \c{tilesize} parameter into a \c{tilesize}
1103field inside the draw state; for some more complex games it might
1104also involve setting up other dimension fields, or possibly
1105allocating a blitter (see \k{drawing-blitter}).
1106
1107The parameter \c{dr} is a drawing object (see \k{drawing}), which is
1108required if a blitter needs to be allocated.
1109
1110Back ends may assume (and may enforce by assertion) that this
1111function will be called at most once for any \c{game_drawstate}. If
1112a puzzle needs to be redrawn at a different size, the mid-end will
1113create a fresh drawstate.
1114
1115\S{backend-colours} \cw{colours()}
1116
1117\c float *(*colours)(frontend *fe, int *ncolours);
1118
1119This function is responsible for telling the front end what colours
1120the puzzle will need to draw itself.
1121
1122It returns the number of colours required in \c{*ncolours}, and the
1123return value from the function itself is a dynamically allocated
1124array of three times that many \c{float}s, containing the red, green
1125and blue components of each colour respectively as numbers in the
1126range [0,1].
1127
1128The second parameter passed to this function is a front end handle.
1129The only things it is permitted to do with this handle are to call
1130the front-end function called \cw{frontend_default_colour()} (see
1131\k{frontend-default-colour}) or the utility function called
1132\cw{game_mkhighlight()} (see \k{utils-game-mkhighlight}). (The
1133latter is a wrapper on the former, so front end implementors only
1134need to provide \cw{frontend_default_colour()}.) This allows
1135\cw{colours()} to take local configuration into account when
1136deciding on its own colour allocations. Most games use the front
1137end's default colour as their background, apart from a few which
1138depend on drawing relief highlights so they adjust the background
1139colour if it's too light for highlights to show up against it.
1140
1141Note that the colours returned from this function are for
1142\e{drawing}, not for printing. Printing has an entirely different
1143colour allocation policy.
1144
1145\S{backend-anim-length} \cw{anim_length()}
1146
1147\c float (*anim_length)(const game_state *oldstate,
1148\c const game_state *newstate,
1149\c int dir, game_ui *ui);
1150
1151This function is called when a move is made, undone or redone. It is
1152given the old and the new \c{game_state}, and its job is to decide
1153whether the transition between the two needs to be animated or can
1154be instant.
1155
1156\c{oldstate} is the state that was current until this call;
1157\c{newstate} is the state that will be current after it. \c{dir}
1158specifies the chronological order of those states: if it is
1159positive, then the transition is the result of a move or a redo (and
1160so \c{newstate} is the later of the two moves), whereas if it is
1161negative then the transition is the result of an undo (so that
1162\c{newstate} is the \e{earlier} move).
1163
1164If this function decides the transition should be animated, it
1165returns the desired length of the animation in seconds. If not, it
1166returns zero.
1167
1168State changes as a result of a Restart operation are never animated;
1169the mid-end will handle them internally and never consult this
1170function at all. State changes as a result of Solve operations are
1171also not animated by default, although you can change this for a
1172particular game by setting a flag in \c{flags} (\k{backend-flags}).
1173
1174The function is also passed a pointer to the local \c{game_ui}. It
1175may refer to information in here to help with its decision (see
1176\k{writing-conditional-anim} for an example of this), and/or it may
1177\e{write} information about the nature of the animation which will
1178be read later by \cw{redraw()}.
1179
1180When this function is called, it may rely on \cw{changed_state()}
1181having been called previously, so if \cw{anim_length()} needs to
1182refer to information in the \c{game_ui}, then \cw{changed_state()}
1183is a reliable place to have set that information up.
1184
1185Move animations do not inhibit further input events. If the user
1186continues playing before a move animation is complete, the animation
1187will be abandoned and the display will jump straight to the final
1188state.
1189
1190\S{backend-flash-length} \cw{flash_length()}
1191
1192\c float (*flash_length)(const game_state *oldstate,
1193\c const game_state *newstate,
1194\c int dir, game_ui *ui);
1195
1196This function is called when a move is completed. (\q{Completed}
1197means that not only has the move been made, but any animation which
1198accompanied it has finished.) It decides whether the transition from
1199\c{oldstate} to \c{newstate} merits a \q{flash}.
1200
1201A flash is much like a move animation, but it is \e{not} interrupted
1202by further user interface activity; it runs to completion in
1203parallel with whatever else might be going on on the display. The
1204only thing which will rush a flash to completion is another flash.
1205
1206The purpose of flashes is to indicate that the game has been
1207completed. They were introduced as a separate concept from move
1208animations because of Net: the habit of most Net players (and
1209certainly me) is to rotate a tile into place and immediately lock
1210it, then move on to another tile. When you make your last move, at
1211the instant the final tile is rotated into place the screen starts
1212to flash to indicate victory \dash but if you then press the lock
1213button out of habit, then the move animation is cancelled, and the
1214victory flash does not complete. (And if you \e{don't} press the
1215lock button, the completed grid will look untidy because there will
1216be one unlocked square.) Therefore, I introduced a specific concept
1217of a \q{flash} which is separate from a move animation and can
1218proceed in parallel with move animations and any other display
1219activity, so that the victory flash in Net is not cancelled by that
1220final locking move.
1221
1222The input parameters to \cw{flash_length()} are exactly the same as
1223the ones to \cw{anim_length()}.
1224
1225Just like \cw{anim_length()}, when this function is called, it may
1226rely on \cw{changed_state()} having been called previously, so if it
1227needs to refer to information in the \c{game_ui} then
1228\cw{changed_state()} is a reliable place to have set that
1229information up.
1230
1231(Some games use flashes to indicate defeat as well as victory;
1232Mines, for example, flashes in a different colour when you tread on
1233a mine from the colour it uses when you complete the game. In order
1234to achieve this, its \cw{flash_length()} function has to store a
1235flag in the \c{game_ui} to indicate which flash type is required.)
1236
1237\S{backend-status} \cw{status()}
1238
1239\c int (*status)(const game_state *state);
1240
1241This function returns a status value indicating whether the current
1242game is still in play, or has been won, or has been conclusively lost.
1243The mid-end uses this to implement \cw{midend_status()}
1244(\k{midend-status}).
1245
1246The return value should be +1 if the game has been successfully
1247solved. If the game has been lost in a situation where further play is
1248unlikely, the return value should be -1. If neither is true (so play
1249is still ongoing), return zero.
1250
1251Front ends may wish to use a non-zero status as a cue to proactively
1252offer the option of starting a new game. Therefore, back ends should
1253not return -1 if the game has been \e{technically} lost but undoing
1254and continuing is still a realistic possibility.
1255
1256(For instance, games with hidden information such as Guess or Mines
1257might well return a non-zero status whenever they reveal the solution,
1258whether or not the player guessed it correctly, on the grounds that a
1259player would be unlikely to hide the solution and continue playing
1260after the answer was spoiled. On the other hand, games where you can
1261merely get into a dead end such as Same Game or Inertia might choose
1262to return 0 in that situation, on the grounds that the player would
1263quite likely press Undo and carry on playing.)
1264
1265\S{backend-redraw} \cw{redraw()}
1266
1267\c void (*redraw)(drawing *dr, game_drawstate *ds,
1268\c const game_state *oldstate,
1269\c const game_state *newstate,
1270\c int dir, const game_ui *ui,
1271\c float anim_time, float flash_time);
1272
1273This function is responsible for actually drawing the contents of
1274the game window, and for redrawing every time the game state or the
1275\c{game_ui} changes.
1276
1277The parameter \c{dr} is a drawing object which may be passed to the
1278drawing API functions (see \k{drawing} for documentation of the
1279drawing API). This function may not save \c{dr} and use it
1280elsewhere; it must only use it for calling back to the drawing API
1281functions within its own lifetime.
1282
1283\c{ds} is the local \c{game_drawstate}, of course, and \c{ui} is the
1284local \c{game_ui}.
1285
1286\c{newstate} is the semantically-current game state, and is always
1287non-\cw{NULL}. If \c{oldstate} is also non-\cw{NULL}, it means that
1288a move has recently been made and the game is still in the process
1289of displaying an animation linking the old and new states; in this
1290situation, \c{anim_time} will give the length of time (in seconds)
1291that the animation has already been running. If \c{oldstate} is
1292\cw{NULL}, then \c{anim_time} is unused (and will hopefully be set
1293to zero to avoid confusion).
1294
1295\c{flash_time}, if it is is non-zero, denotes that the game is in
1296the middle of a flash, and gives the time since the start of the
1297flash. See \k{backend-flash-length} for general discussion of
1298flashes.
1299
1300The very first time this function is called for a new
1301\c{game_drawstate}, it is expected to redraw the \e{entire} drawing
1302area. Since this often involves drawing visual furniture which is
1303never subsequently altered, it is often simplest to arrange this by
1304having a special \q{first time} flag in the draw state, and
1305resetting it after the first redraw.
1306
1307When this function (or any subfunction) calls the drawing API, it is
1308expected to pass colour indices which were previously defined by the
1309\cw{colours()} function.
1310
1311\H{backend-printing} Printing functions
1312
1313This section discusses the back end functions that deal with
1314printing puzzles out on paper.
1315
1316\S{backend-can-print} \c{can_print}
1317
1318\c int can_print;
1319
1320This flag is set to \cw{TRUE} if the puzzle is capable of printing
1321itself on paper. (This makes sense for some puzzles, such as Solo,
1322which can be filled in with a pencil. Other puzzles, such as
1323Twiddle, inherently involve moving things around and so would not
1324make sense to print.)
1325
1326If this flag is \cw{FALSE}, then the functions \cw{print_size()}
1327and \cw{print()} will never be called.
1328
1329\S{backend-can-print-in-colour} \c{can_print_in_colour}
1330
1331\c int can_print_in_colour;
1332
1333This flag is set to \cw{TRUE} if the puzzle is capable of printing
1334itself differently when colour is available. For example, Map can
1335actually print coloured regions in different \e{colours} rather than
1336resorting to cross-hatching.
1337
1338If the \c{can_print} flag is \cw{FALSE}, then this flag will be
1339ignored.
1340
1341\S{backend-print-size} \cw{print_size()}
1342
1343\c void (*print_size)(const game_params *params, float *x, float *y);
1344
1345This function is passed a \c{game_params} structure and a tile size.
1346It returns, in \c{*x} and \c{*y}, the preferred size in
1347\e{millimetres} of that puzzle if it were to be printed out on paper.
1348
1349If the \c{can_print} flag is \cw{FALSE}, this function will never be
1350called.
1351
1352\S{backend-print} \cw{print()}
1353
1354\c void (*print)(drawing *dr, const game_state *state, int tilesize);
1355
1356This function is called when a puzzle is to be printed out on paper.
1357It should use the drawing API functions (see \k{drawing}) to print
1358itself.
1359
1360This function is separate from \cw{redraw()} because it is often
1361very different:
1362
1363\b The printing function may not depend on pixel accuracy, since
1364printer resolution is variable. Draw as if your canvas had infinite
1365resolution.
1366
1367\b The printing function sometimes needs to display things in a
1368completely different style. Net, for example, is very different as
1369an on-screen puzzle and as a printed one.
1370
1371\b The printing function is often much simpler since it has no need
1372to deal with repeated partial redraws.
1373
1374However, there's no reason the printing and redraw functions can't
1375share some code if they want to.
1376
1377When this function (or any subfunction) calls the drawing API, the
1378colour indices it passes should be colours which have been allocated
1379by the \cw{print_*_colour()} functions within this execution of
1380\cw{print()}. This is very different from the fixed small number of
1381colours used in \cw{redraw()}, because printers do not have a
1382limitation on the total number of colours that may be used. Some
1383puzzles' printing functions might wish to allocate only one \q{ink}
1384colour and use it for all drawing; others might wish to allocate
1385\e{more} colours than are used on screen.
1386
1387One possible colour policy worth mentioning specifically is that a
1388puzzle's printing function might want to allocate the \e{same}
1389colour indices as are used by the redraw function, so that code
1390shared between drawing and printing does not have to keep switching
1391its colour indices. In order to do this, the simplest thing is to
1392make use of the fact that colour indices returned from
1393\cw{print_*_colour()} are guaranteed to be in increasing order from
1394zero. So if you have declared an \c{enum} defining three colours
1395\cw{COL_BACKGROUND}, \cw{COL_THIS} and \cw{COL_THAT}, you might then
1396write
1397
1398\c int c;
1399\c c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND);
1400\c c = print_mono_colour(dr, 0); assert(c == COL_THIS);
1401\c c = print_mono_colour(dr, 0); assert(c == COL_THAT);
1402
1403If the \c{can_print} flag is \cw{FALSE}, this function will never be
1404called.
1405
1406\H{backend-misc} Miscellaneous
1407
1408\S{backend-can-format-as-text-ever} \c{can_format_as_text_ever}
1409
1410\c int can_format_as_text_ever;
1411
1412This boolean field is \cw{TRUE} if the game supports formatting a
1413game state as ASCII text (typically ASCII art) for copying to the
1414clipboard and pasting into other applications. If it is \cw{FALSE},
1415front ends will not offer the \q{Copy} command at all.
1416
1417If this field is \cw{TRUE}, the game does not necessarily have to
1418support text formatting for \e{all} games: e.g. a game which can be
1419played on a square grid or a triangular one might only support copy
1420and paste for the former, because triangular grids in ASCII art are
1421just too difficult.
1422
1423If this field is \cw{FALSE}, the functions
1424\cw{can_format_as_text_now()} (\k{backend-can-format-as-text-now})
1425and \cw{text_format()} (\k{backend-text-format}) are never called.
1426
1427\S{backend-can-format-as-text-now} \c{can_format_as_text_now()}
1428
1429\c int (*can_format_as_text_now)(const game_params *params);
1430
1431This function is passed a \c{game_params} and returns a boolean,
1432which is \cw{TRUE} if the game can support ASCII text output for
1433this particular game type. If it returns \cw{FALSE}, front ends will
1434grey out or otherwise disable the \q{Copy} command.
1435
1436Games may enable and disable the copy-and-paste function for
1437different game \e{parameters}, but are currently constrained to
1438return the same answer from this function for all game \e{states}
1439sharing the same parameters. In other words, the \q{Copy} function
1440may enable or disable itself when the player changes game preset,
1441but will never change during play of a single game or when another
1442game of exactly the same type is generated.
1443
1444This function should not take into account aspects of the game
1445parameters which are not encoded by \cw{encode_params()}
1446(\k{backend-encode-params}) when the \c{full} parameter is set to
1447\cw{FALSE}. Such parameters will not necessarily match up between a
1448call to this function and a subsequent call to \cw{text_format()}
1449itself. (For instance, game \e{difficulty} should not affect whether
1450the game can be copied to the clipboard. Only the actual visible
1451\e{shape} of the game can affect that.)
1452
1453\S{backend-text-format} \cw{text_format()}
1454
1455\c char *(*text_format)(const game_state *state);
1456
1457This function is passed a \c{game_state}, and returns a newly
1458allocated C string containing an ASCII representation of that game
1459state. It is used to implement the \q{Copy} operation in many front
1460ends.
1461
1462This function will only ever be called if the back end field
1463\c{can_format_as_text_ever} (\k{backend-can-format-as-text-ever}) is
1464\cw{TRUE} \e{and} the function \cw{can_format_as_text_now()}
1465(\k{backend-can-format-as-text-now}) has returned \cw{TRUE} for the
1466currently selected game parameters.
1467
1468The returned string may contain line endings (and will probably want
1469to), using the normal C internal \cq{\\n} convention. For
1470consistency between puzzles, all multi-line textual puzzle
1471representations should \e{end} with a newline as well as containing
1472them internally. (There are currently no puzzles which have a
1473one-line ASCII representation, so there's no precedent yet for
1474whether that should come with a newline or not.)
1475
1476\S{backend-wants-statusbar} \cw{wants_statusbar}
1477
1478\c int wants_statusbar;
1479
1480This boolean field is set to \cw{TRUE} if the puzzle has a use for a
1481textual status line (to display score, completion status, currently
1482active tiles, etc).
1483
1484\S{backend-is-timed} \c{is_timed}
1485
1486\c int is_timed;
1487
1488This boolean field is \cw{TRUE} if the puzzle is time-critical. If
1489so, the mid-end will maintain a game timer while the user plays.
1490
1491If this field is \cw{FALSE}, then \cw{timing_state()} will never be
1492called and need not do anything.
1493
1494\S{backend-timing-state} \cw{timing_state()}
1495
1496\c int (*timing_state)(const game_state *state, game_ui *ui);
1497
1498This function is passed the current \c{game_state} and the local
1499\c{game_ui}; it returns \cw{TRUE} if the game timer should currently
1500be running.
1501
1502A typical use for the \c{game_ui} in this function is to note when
1503the game was first completed (by setting a flag in
1504\cw{changed_state()} \dash see \k{backend-changed-state}), and
1505freeze the timer thereafter so that the user can undo back through
1506their solution process without altering their time.
1507
1508\S{backend-flags} \c{flags}
1509
1510\c int flags;
1511
1512This field contains miscellaneous per-backend flags. It consists of
1513the bitwise OR of some combination of the following:
1514
1515\dt \cw{BUTTON_BEATS(x,y)}
1516
1517\dd Given any \cw{x} and \cw{y} from the set \{\cw{LEFT_BUTTON},
1518\cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON}\}, this macro evaluates to a
1519bit flag which indicates that when buttons \cw{x} and \cw{y} are
1520both pressed simultaneously, the mid-end should consider \cw{x} to
1521have priority. (In the absence of any such flags, the mid-end will
1522always consider the most recently pressed button to have priority.)
1523
1524\dt \cw{SOLVE_ANIMATES}
1525
1526\dd This flag indicates that moves generated by \cw{solve()}
1527(\k{backend-solve}) are candidates for animation just like any other
1528move. For most games, solve moves should not be animated, so the
1529mid-end doesn't even bother calling \cw{anim_length()}
1530(\k{backend-anim-length}), thus saving some special-case code in
1531each game. On the rare occasion that animated solve moves are
1532actually required, you can set this flag.
1533
1534\dt \cw{REQUIRE_RBUTTON}
1535
1536\dd This flag indicates that the puzzle cannot be usefully played
1537without the use of mouse buttons other than the left one. On some
1538PDA platforms, this flag is used by the front end to enable
1539right-button emulation through an appropriate gesture. Note that a
1540puzzle is not required to set this just because it \e{uses} the
1541right button, but only if its use of the right button is critical to
1542playing the game. (Slant, for example, uses the right button to
1543cycle through the three square states in the opposite order from the
1544left button, and hence can manage fine without it.)
1545
1546\dt \cw{REQUIRE_NUMPAD}
1547
1548\dd This flag indicates that the puzzle cannot be usefully played
1549without the use of number-key input. On some PDA platforms it causes
1550an emulated number pad to appear on the screen. Similarly to
1551\cw{REQUIRE_RBUTTON}, a puzzle need not specify this simply if its
1552use of the number keys is not critical.
1553
1554\H{backend-initiative} Things a back end may do on its own initiative
1555
1556This section describes a couple of things that a back end may choose
1557to do by calling functions elsewhere in the program, which would not
1558otherwise be obvious.
1559
1560\S{backend-newrs} Create a random state
1561
1562If a back end needs random numbers at some point during normal play,
1563it can create a fresh \c{random_state} by first calling
1564\c{get_random_seed} (\k{frontend-get-random-seed}) and then passing
1565the returned seed data to \cw{random_new()}.
1566
1567This is likely not to be what you want. If a puzzle needs randomness
1568in the middle of play, it's likely to be more sensible to store some
1569sort of random state within the \c{game_state}, so that the random
1570numbers are tied to the particular game state and hence the player
1571can't simply keep undoing their move until they get numbers they
1572like better.
1573
1574This facility is currently used only in Net, to implement the
1575\q{jumble} command, which sets every unlocked tile to a new random
1576orientation. This randomness \e{is} a reasonable use of the feature,
1577because it's non-adversarial \dash there's no advantage to the user
1578in getting different random numbers.
1579
1580\S{backend-supersede} Supersede its own game description
1581
1582In response to a move, a back end is (reluctantly) permitted to call
1583\cw{midend_supersede_game_desc()}:
1584
1585\c void midend_supersede_game_desc(midend *me,
1586\c char *desc, char *privdesc);
1587
1588When the user selects \q{New Game}, the mid-end calls
1589\cw{new_desc()} (\k{backend-new-desc}) to get a new game
1590description, and (as well as using that to generate an initial game
1591state) stores it for the save file and for telling to the user. The
1592function above overwrites that game description, and also splits it
1593in two. \c{desc} becomes the new game description which is provided
1594to the user on request, and is also the one used to construct a new
1595initial game state if the user selects \q{Restart}. \c{privdesc} is
1596a \q{private} game description, used to reconstruct the game's
1597initial state when reloading.
1598
1599The distinction between the two, as well as the need for this
1600function at all, comes from Mines. Mines begins with a blank grid
1601and no idea of where the mines actually are; \cw{new_desc()} does
1602almost no work in interactive mode, and simply returns a string
1603encoding the \c{random_state}. When the user first clicks to open a
1604tile, \e{then} Mines generates the mine positions, in such a way
1605that the game is soluble from that starting point. Then it uses this
1606function to supersede the random-state game description with a
1607proper one. But it needs two: one containing the initial click
1608location (because that's what you want to happen if you restart the
1609game, and also what you want to send to a friend so that they play
1610\e{the same game} as you), and one without the initial click
1611location (because when you save and reload the game, you expect to
1612see the same blank initial state as you had before saving).
1613
1614I should stress again that this function is a horrid hack. Nobody
1615should use it if they're not Mines; if you think you need to use it,
1616think again repeatedly in the hope of finding a better way to do
1617whatever it was you needed to do.
1618
1619\C{drawing} The drawing API
1620
1621The back end function \cw{redraw()} (\k{backend-redraw}) is required
1622to draw the puzzle's graphics on the window's drawing area, or on
1623paper if the puzzle is printable. To do this portably, it is
1624provided with a drawing API allowing it to talk directly to the
1625front end. In this chapter I document that API, both for the benefit
1626of back end authors trying to use it and for front end authors
1627trying to implement it.
1628
1629The drawing API as seen by the back end is a collection of global
1630functions, each of which takes a pointer to a \c{drawing} structure
1631(a \q{drawing object}). These objects are supplied as parameters to
1632the back end's \cw{redraw()} and \cw{print()} functions.
1633
1634In fact these global functions are not implemented directly by the
1635front end; instead, they are implemented centrally in \c{drawing.c}
1636and form a small piece of middleware. The drawing API as supplied by
1637the front end is a structure containing a set of function pointers,
1638plus a \cq{void *} handle which is passed to each of those
1639functions. This enables a single front end to switch between
1640multiple implementations of the drawing API if necessary. For
1641example, the Windows API supplies a printing mechanism integrated
1642into the same GDI which deals with drawing in windows, and therefore
1643the same API implementation can handle both drawing and printing;
1644but on Unix, the most common way for applications to print is by
1645producing PostScript output directly, and although it would be
1646\e{possible} to write a single (say) \cw{draw_rect()} function which
1647checked a global flag to decide whether to do GTK drawing operations
1648or output PostScript to a file, it's much nicer to have two separate
1649functions and switch between them as appropriate.
1650
1651When drawing, the puzzle window is indexed by pixel coordinates,
1652with the top left pixel defined as \cw{(0,0)} and the bottom right
1653pixel \cw{(w-1,h-1)}, where \c{w} and \c{h} are the width and height
1654values returned by the back end function \cw{compute_size()}
1655(\k{backend-compute-size}).
1656
1657When printing, the puzzle's print area is indexed in exactly the
1658same way (with an arbitrary tile size provided by the printing
1659module \c{printing.c}), to facilitate sharing of code between the
1660drawing and printing routines. However, when printing, puzzles may
1661no longer assume that the coordinate unit has any relationship to a
1662pixel; the printer's actual resolution might very well not even be
1663known at print time, so the coordinate unit might be smaller or
1664larger than a pixel. Puzzles' print functions should restrict
1665themselves to drawing geometric shapes rather than fiddly pixel
1666manipulation.
1667
1668\e{Puzzles' redraw functions may assume that the surface they draw
1669on is persistent}. It is the responsibility of every front end to
1670preserve the puzzle's window contents in the face of GUI window
1671expose issues and similar. It is not permissible to request that the
1672back end redraw any part of a window that it has already drawn,
1673unless something has actually changed as a result of making moves in
1674the puzzle.
1675
1676Most front ends accomplish this by having the drawing routines draw
1677on a stored bitmap rather than directly on the window, and copying
1678the bitmap to the window every time a part of the window needs to be
1679redrawn. Therefore, it is vitally important that whenever the back
1680end does any drawing it informs the front end of which parts of the
1681window it has accessed, and hence which parts need repainting. This
1682is done by calling \cw{draw_update()} (\k{drawing-draw-update}).
1683
1684Persistence of old drawing is convenient. However, a puzzle should
1685be very careful about how it updates its drawing area. The problem
1686is that some front ends do anti-aliased drawing: rather than simply
1687choosing between leaving each pixel untouched or painting it a
1688specified colour, an antialiased drawing function will \e{blend} the
1689original and new colours in pixels at a figure's boundary according
1690to the proportion of the pixel occupied by the figure (probably
1691modified by some heuristic fudge factors). All of this produces a
1692smoother appearance for curves and diagonal lines.
1693
1694An unfortunate effect of drawing an anti-aliased figure repeatedly
1695is that the pixels around the figure's boundary come steadily more
1696saturated with \q{ink} and the boundary appears to \q{spread out}.
1697Worse, redrawing a figure in a different colour won't fully paint
1698over the old boundary pixels, so the end result is a rather ugly
1699smudge.
1700
1701A good strategy to avoid unpleasant anti-aliasing artifacts is to
1702identify a number of rectangular areas which need to be redrawn,
1703clear them to the background colour, and then redraw their contents
1704from scratch, being careful all the while not to stray beyond the
1705boundaries of the original rectangles. The \cw{clip()} function
1706(\k{drawing-clip}) comes in very handy here. Games based on a square
1707grid can often do this fairly easily. Other games may need to be
1708somewhat more careful. For example, Loopy's redraw function first
1709identifies portions of the display which need to be updated. Then,
1710if the changes are fairly well localised, it clears and redraws a
1711rectangle containing each changed area. Otherwise, it gives up and
1712redraws the entire grid from scratch.
1713
1714It is possible to avoid clearing to background and redrawing from
1715scratch if one is very careful about which drawing functions one
1716uses: if a function is documented as not anti-aliasing under some
1717circumstances, you can rely on each pixel in a drawing either being
1718left entirely alone or being set to the requested colour, with no
1719blending being performed.
1720
1721In the following sections I first discuss the drawing API as seen by
1722the back end, and then the \e{almost} identical function-pointer
1723form seen by the front end.
1724
1725\H{drawing-backend} Drawing API as seen by the back end
1726
1727This section documents the back-end drawing API, in the form of
1728functions which take a \c{drawing} object as an argument.
1729
1730\S{drawing-draw-rect} \cw{draw_rect()}
1731
1732\c void draw_rect(drawing *dr, int x, int y, int w, int h,
1733\c int colour);
1734
1735Draws a filled rectangle in the puzzle window.
1736
1737\c{x} and \c{y} give the coordinates of the top left pixel of the
1738rectangle. \c{w} and \c{h} give its width and height. Thus, the
1739horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
1740inclusive, and the vertical extent from \c{y} to \c{y+h-1}
1741inclusive.
1742
1743\c{colour} is an integer index into the colours array returned by
1744the back end function \cw{colours()} (\k{backend-colours}).
1745
1746There is no separate pixel-plotting function. If you want to plot a
1747single pixel, the approved method is to use \cw{draw_rect()} with
1748width and height set to 1.
1749
1750Unlike many of the other drawing functions, this function is
1751guaranteed to be pixel-perfect: the rectangle will be sharply
1752defined and not anti-aliased or anything like that.
1753
1754This function may be used for both drawing and printing.
1755
1756\S{drawing-draw-rect-outline} \cw{draw_rect_outline()}
1757
1758\c void draw_rect_outline(drawing *dr, int x, int y, int w, int h,
1759\c int colour);
1760
1761Draws an outline rectangle in the puzzle window.
1762
1763\c{x} and \c{y} give the coordinates of the top left pixel of the
1764rectangle. \c{w} and \c{h} give its width and height. Thus, the
1765horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
1766inclusive, and the vertical extent from \c{y} to \c{y+h-1}
1767inclusive.
1768
1769\c{colour} is an integer index into the colours array returned by
1770the back end function \cw{colours()} (\k{backend-colours}).
1771
1772From a back end perspective, this function may be considered to be
1773part of the drawing API. However, front ends are not required to
1774implement it, since it is actually implemented centrally (in
1775\cw{misc.c}) as a wrapper on \cw{draw_polygon()}.
1776
1777This function may be used for both drawing and printing.
1778
1779\S{drawing-draw-line} \cw{draw_line()}
1780
1781\c void draw_line(drawing *dr, int x1, int y1, int x2, int y2,
1782\c int colour);
1783
1784Draws a straight line in the puzzle window.
1785
1786\c{x1} and \c{y1} give the coordinates of one end of the line.
1787\c{x2} and \c{y2} give the coordinates of the other end. The line
1788drawn includes both those points.
1789
1790\c{colour} is an integer index into the colours array returned by
1791the back end function \cw{colours()} (\k{backend-colours}).
1792
1793Some platforms may perform anti-aliasing on this function.
1794Therefore, do not assume that you can erase a line by drawing the
1795same line over it in the background colour; anti-aliasing might lead
1796to perceptible ghost artefacts around the vanished line. Horizontal
1797and vertical lines, however, are pixel-perfect and not anti-aliased.
1798
1799This function may be used for both drawing and printing.
1800
1801\S{drawing-draw-polygon} \cw{draw_polygon()}
1802
1803\c void draw_polygon(drawing *dr, int *coords, int npoints,
1804\c int fillcolour, int outlinecolour);
1805
1806Draws an outlined or filled polygon in the puzzle window.
1807
1808\c{coords} is an array of \cw{(2*npoints)} integers, containing the
1809\c{x} and \c{y} coordinates of \c{npoints} vertices.
1810
1811\c{fillcolour} and \c{outlinecolour} are integer indices into the
1812colours array returned by the back end function \cw{colours()}
1813(\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to
1814indicate that the polygon should be outlined only.
1815
1816The polygon defined by the specified list of vertices is first
1817filled in \c{fillcolour}, if specified, and then outlined in
1818\c{outlinecolour}.
1819
1820\c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour
1821(and front ends are permitted to enforce this by assertion). This is
1822because different platforms disagree on whether a filled polygon
1823should include its boundary line or not, so drawing \e{only} a
1824filled polygon would have non-portable effects. If you want your
1825filled polygon not to have a visible outline, you must set
1826\c{outlinecolour} to the same as \c{fillcolour}.
1827
1828Some platforms may perform anti-aliasing on this function.
1829Therefore, do not assume that you can erase a polygon by drawing the
1830same polygon over it in the background colour. Also, be prepared for
1831the polygon to extend a pixel beyond its obvious bounding box as a
1832result of this; if you really need it not to do this to avoid
1833interfering with other delicate graphics, you should probably use
1834\cw{clip()} (\k{drawing-clip}). You can rely on horizontal and
1835vertical lines not being anti-aliased.
1836
1837This function may be used for both drawing and printing.
1838
1839\S{drawing-draw-circle} \cw{draw_circle()}
1840
1841\c void draw_circle(drawing *dr, int cx, int cy, int radius,
1842\c int fillcolour, int outlinecolour);
1843
1844Draws an outlined or filled circle in the puzzle window.
1845
1846\c{cx} and \c{cy} give the coordinates of the centre of the circle.
1847\c{radius} gives its radius. The total horizontal pixel extent of
1848the circle is from \c{cx-radius+1} to \c{cx+radius-1} inclusive, and
1849the vertical extent similarly around \c{cy}.
1850
1851\c{fillcolour} and \c{outlinecolour} are integer indices into the
1852colours array returned by the back end function \cw{colours()}
1853(\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to
1854indicate that the circle should be outlined only.
1855
1856The circle is first filled in \c{fillcolour}, if specified, and then
1857outlined in \c{outlinecolour}.
1858
1859\c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour
1860(and front ends are permitted to enforce this by assertion). This is
1861because different platforms disagree on whether a filled circle
1862should include its boundary line or not, so drawing \e{only} a
1863filled circle would have non-portable effects. If you want your
1864filled circle not to have a visible outline, you must set
1865\c{outlinecolour} to the same as \c{fillcolour}.
1866
1867Some platforms may perform anti-aliasing on this function.
1868Therefore, do not assume that you can erase a circle by drawing the
1869same circle over it in the background colour. Also, be prepared for
1870the circle to extend a pixel beyond its obvious bounding box as a
1871result of this; if you really need it not to do this to avoid
1872interfering with other delicate graphics, you should probably use
1873\cw{clip()} (\k{drawing-clip}).
1874
1875This function may be used for both drawing and printing.
1876
1877\S{drawing-draw-thick-line} \cw{draw_thick_line()}
1878
1879\c void draw_thick_line(drawing *dr, float thickness,
1880\c float x1, float y1, float x2, float y2,
1881\c int colour)
1882
1883Draws a line in the puzzle window, giving control over the line's
1884thickness.
1885
1886\c{x1} and \c{y1} give the coordinates of one end of the line.
1887\c{x2} and \c{y2} give the coordinates of the other end.
1888\c{thickness} gives the thickness of the line, in pixels.
1889
1890Note that the coordinates and thickness are floating-point: the
1891continuous coordinate system is in effect here. It's important to
1892be able to address points with better-than-pixel precision in this
1893case, because one can't otherwise properly express the endpoints of
1894lines with both odd and even thicknesses.
1895
1896Some platforms may perform anti-aliasing on this function. The
1897precise pixels affected by a thick-line drawing operation may vary
1898between platforms, and no particular guarantees are provided.
1899Indeed, even horizontal or vertical lines may be anti-aliased.
1900
1901This function may be used for both drawing and printing.
1902
1903\S{drawing-draw-text} \cw{draw_text()}
1904
1905\c void draw_text(drawing *dr, int x, int y, int fonttype,
1906\c int fontsize, int align, int colour, char *text);
1907
1908Draws text in the puzzle window.
1909
1910\c{x} and \c{y} give the coordinates of a point. The relation of
1911this point to the location of the text is specified by \c{align},
1912which is a bitwise OR of horizontal and vertical alignment flags:
1913
1914\dt \cw{ALIGN_VNORMAL}
1915
1916\dd Indicates that \c{y} is aligned with the baseline of the text.
1917
1918\dt \cw{ALIGN_VCENTRE}
1919
1920\dd Indicates that \c{y} is aligned with the vertical centre of the
1921text. (In fact, it's aligned with the vertical centre of normal
1922\e{capitalised} text: displaying two pieces of text with
1923\cw{ALIGN_VCENTRE} at the same \cw{y}-coordinate will cause their
1924baselines to be aligned with one another, even if one is an ascender
1925and the other a descender.)
1926
1927\dt \cw{ALIGN_HLEFT}
1928
1929\dd Indicates that \c{x} is aligned with the left-hand end of the
1930text.
1931
1932\dt \cw{ALIGN_HCENTRE}
1933
1934\dd Indicates that \c{x} is aligned with the horizontal centre of
1935the text.
1936
1937\dt \cw{ALIGN_HRIGHT}
1938
1939\dd Indicates that \c{x} is aligned with the right-hand end of the
1940text.
1941
1942\c{fonttype} is either \cw{FONT_FIXED} or \cw{FONT_VARIABLE}, for a
1943monospaced or proportional font respectively. (No more detail than
1944that may be specified; it would only lead to portability issues
1945between different platforms.)
1946
1947\c{fontsize} is the desired size, in pixels, of the text. This size
1948corresponds to the overall point size of the text, not to any
1949internal dimension such as the cap-height.
1950
1951\c{colour} is an integer index into the colours array returned by
1952the back end function \cw{colours()} (\k{backend-colours}).
1953
1954This function may be used for both drawing and printing.
1955
1956The character set used to encode the text passed to this function is
1957specified \e{by the drawing object}, although it must be a superset
1958of ASCII. If a puzzle wants to display text that is not contained in
1959ASCII, it should use the \cw{text_fallback()} function
1960(\k{drawing-text-fallback}) to query the drawing object for an
1961appropriate representation of the characters it wants.
1962
1963\S{drawing-text-fallback} \cw{text_fallback()}
1964
1965\c char *text_fallback(drawing *dr, const char *const *strings,
1966\c int nstrings);
1967
1968This function is used to request a translation of UTF-8 text into
1969whatever character encoding is expected by the drawing object's
1970implementation of \cw{draw_text()}.
1971
1972The input is a list of strings encoded in UTF-8: \cw{nstrings} gives
1973the number of strings in the list, and \cw{strings[0]},
1974\cw{strings[1]}, ..., \cw{strings[nstrings-1]} are the strings
1975themselves.
1976
1977The returned string (which is dynamically allocated and must be
1978freed when finished with) is derived from the first string in the
1979list that the drawing object expects to be able to display reliably;
1980it will consist of that string translated into the character set
1981expected by \cw{draw_text()}.
1982
1983Drawing implementations are not required to handle anything outside
1984ASCII, but are permitted to assume that \e{some} string will be
1985successfully translated. So every call to this function must include
1986a string somewhere in the list (presumably the last element) which
1987consists of nothing but ASCII, to be used by any front end which
1988cannot handle anything else.
1989
1990For example, if a puzzle wished to display a string including a
1991multiplication sign (U+00D7 in Unicode, represented by the bytes C3
199297 in UTF-8), it might do something like this:
1993
1994\c static const char *const times_signs[] = { "\xC3\x97", "x" };
1995\c char *times_sign = text_fallback(dr, times_signs, 2);
1996\c sprintf(buffer, "%d%s%d", width, times_sign, height);
1997\c draw_text(dr, x, y, font, size, align, colour, buffer);
1998\c sfree(buffer);
1999
2000which would draw a string with a times sign in the middle on
2001platforms that support it, and fall back to a simple ASCII \cq{x}
2002where there was no alternative.
2003
2004\S{drawing-clip} \cw{clip()}
2005
2006\c void clip(drawing *dr, int x, int y, int w, int h);
2007
2008Establishes a clipping rectangle in the puzzle window.
2009
2010\c{x} and \c{y} give the coordinates of the top left pixel of the
2011clipping rectangle. \c{w} and \c{h} give its width and height. Thus,
2012the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
2013inclusive, and the vertical extent from \c{y} to \c{y+h-1}
2014inclusive. (These are exactly the same semantics as
2015\cw{draw_rect()}.)
2016
2017After this call, no drawing operation will affect anything outside
2018the specified rectangle. The effect can be reversed by calling
2019\cw{unclip()} (\k{drawing-unclip}). The clipping rectangle is
2020pixel-perfect: pixels within the rectangle are affected as usual by
2021drawing functions; pixels outside are completely untouched.
2022
2023Back ends should not assume that a clipping rectangle will be
2024automatically cleared up by the front end if it's left lying around;
2025that might work on current front ends, but shouldn't be relied upon.
2026Always explicitly call \cw{unclip()}.
2027
2028This function may be used for both drawing and printing.
2029
2030\S{drawing-unclip} \cw{unclip()}
2031
2032\c void unclip(drawing *dr);
2033
2034Reverts the effect of a previous call to \cw{clip()}. After this
2035call, all drawing operations will be able to affect the entire
2036puzzle window again.
2037
2038This function may be used for both drawing and printing.
2039
2040\S{drawing-draw-update} \cw{draw_update()}
2041
2042\c void draw_update(drawing *dr, int x, int y, int w, int h);
2043
2044Informs the front end that a rectangular portion of the puzzle
2045window has been drawn on and needs to be updated.
2046
2047\c{x} and \c{y} give the coordinates of the top left pixel of the
2048update rectangle. \c{w} and \c{h} give its width and height. Thus,
2049the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1}
2050inclusive, and the vertical extent from \c{y} to \c{y+h-1}
2051inclusive. (These are exactly the same semantics as
2052\cw{draw_rect()}.)
2053
2054The back end redraw function \e{must} call this function to report
2055any changes it has made to the window. Otherwise, those changes may
2056not become immediately visible, and may then appear at an
2057unpredictable subsequent time such as the next time the window is
2058covered and re-exposed.
2059
2060This function is only important when drawing. It may be called when
2061printing as well, but doing so is not compulsory, and has no effect.
2062(So if you have a shared piece of code between the drawing and
2063printing routines, that code may safely call \cw{draw_update()}.)
2064
2065\S{drawing-status-bar} \cw{status_bar()}
2066
2067\c void status_bar(drawing *dr, char *text);
2068
2069Sets the text in the game's status bar to \c{text}. The text is copied
2070from the supplied buffer, so the caller is free to deallocate or
2071modify the buffer after use.
2072
2073(This function is not exactly a \e{drawing} function, but it shares
2074with the drawing API the property that it may only be called from
2075within the back end redraw function, so this is as good a place as
2076any to document it.)
2077
2078The supplied text is filtered through the mid-end for optional
2079rewriting before being passed on to the front end; the mid-end will
2080prepend the current game time if the game is timed (and may in
2081future perform other rewriting if it seems like a good idea).
2082
2083This function is for drawing only; it must never be called during
2084printing.
2085
2086\S{drawing-blitter} Blitter functions
2087
2088This section describes a group of related functions which save and
2089restore a section of the puzzle window. This is most commonly used
2090to implement user interfaces involving dragging a puzzle element
2091around the window: at the end of each call to \cw{redraw()}, if an
2092object is currently being dragged, the back end saves the window
2093contents under that location and then draws the dragged object, and
2094at the start of the next \cw{redraw()} the first thing it does is to
2095restore the background.
2096
2097The front end defines an opaque type called a \c{blitter}, which is
2098capable of storing a rectangular area of a specified size.
2099
2100Blitter functions are for drawing only; they must never be called
2101during printing.
2102
2103\S2{drawing-blitter-new} \cw{blitter_new()}
2104
2105\c blitter *blitter_new(drawing *dr, int w, int h);
2106
2107Creates a new blitter object which stores a rectangle of size \c{w}
2108by \c{h} pixels. Returns a pointer to the blitter object.
2109
2110Blitter objects are best stored in the \c{game_drawstate}. A good
2111time to create them is in the \cw{set_size()} function
2112(\k{backend-set-size}), since it is at this point that you first
2113know how big a rectangle they will need to save.
2114
2115\S2{drawing-blitter-free} \cw{blitter_free()}
2116
2117\c void blitter_free(drawing *dr, blitter *bl);
2118
2119Disposes of a blitter object. Best called in \cw{free_drawstate()}.
2120(However, check that the blitter object is not \cw{NULL} before
2121attempting to free it; it is possible that a draw state might be
2122created and freed without ever having \cw{set_size()} called on it
2123in between.)
2124
2125\S2{drawing-blitter-save} \cw{blitter_save()}
2126
2127\c void blitter_save(drawing *dr, blitter *bl, int x, int y);
2128
2129This is a true drawing API function, in that it may only be called
2130from within the game redraw routine. It saves a rectangular portion
2131of the puzzle window into the specified blitter object.
2132
2133\c{x} and \c{y} give the coordinates of the top left corner of the
2134saved rectangle. The rectangle's width and height are the ones
2135specified when the blitter object was created.
2136
2137This function is required to cope and do the right thing if \c{x}
2138and \c{y} are out of range. (The right thing probably means saving
2139whatever part of the blitter rectangle overlaps with the visible
2140area of the puzzle window.)
2141
2142\S2{drawing-blitter-load} \cw{blitter_load()}
2143
2144\c void blitter_load(drawing *dr, blitter *bl, int x, int y);
2145
2146This is a true drawing API function, in that it may only be called
2147from within the game redraw routine. It restores a rectangular
2148portion of the puzzle window from the specified blitter object.
2149
2150\c{x} and \c{y} give the coordinates of the top left corner of the
2151rectangle to be restored. The rectangle's width and height are the
2152ones specified when the blitter object was created.
2153
2154Alternatively, you can specify both \c{x} and \c{y} as the special
2155value \cw{BLITTER_FROMSAVED}, in which case the rectangle will be
2156restored to exactly where it was saved from. (This is probably what
2157you want to do almost all the time, if you're using blitters to
2158implement draggable puzzle elements.)
2159
2160This function is required to cope and do the right thing if \c{x}
2161and \c{y} (or the equivalent ones saved in the blitter) are out of
2162range. (The right thing probably means restoring whatever part of
2163the blitter rectangle overlaps with the visible area of the puzzle
2164window.)
2165
2166If this function is called on a blitter which had previously been
2167saved from a partially out-of-range rectangle, then the parts of the
2168saved bitmap which were not visible at save time are undefined. If
2169the blitter is restored to a different position so as to make those
2170parts visible, the effect on the drawing area is undefined.
2171
2172\S{print-mono-colour} \cw{print_mono_colour()}
2173
2174\c int print_mono_colour(drawing *dr, int grey);
2175
2176This function allocates a colour index for a simple monochrome
2177colour during printing.
2178
2179\c{grey} must be 0 or 1. If \c{grey} is 0, the colour returned is
2180black; if \c{grey} is 1, the colour is white.
2181
2182\S{print-grey-colour} \cw{print_grey_colour()}
2183
2184\c int print_grey_colour(drawing *dr, float grey);
2185
2186This function allocates a colour index for a grey-scale colour
2187during printing.
2188
2189\c{grey} may be any number between 0 (black) and 1 (white); for
2190example, 0.5 indicates a medium grey.
2191
2192The chosen colour will be rendered to the limits of the printer's
2193halftoning capability.
2194
2195\S{print-hatched-colour} \cw{print_hatched_colour()}
2196
2197\c int print_hatched_colour(drawing *dr, int hatch);
2198
2199This function allocates a colour index which does not represent a
2200literal \e{colour}. Instead, regions shaded in this colour will be
2201hatched with parallel lines. The \c{hatch} parameter defines what
2202type of hatching should be used in place of this colour:
2203
2204\dt \cw{HATCH_SLASH}
2205
2206\dd This colour will be hatched by lines slanting to the right at 45
2207degrees.
2208
2209\dt \cw{HATCH_BACKSLASH}
2210
2211\dd This colour will be hatched by lines slanting to the left at 45
2212degrees.
2213
2214\dt \cw{HATCH_HORIZ}
2215
2216\dd This colour will be hatched by horizontal lines.
2217
2218\dt \cw{HATCH_VERT}
2219
2220\dd This colour will be hatched by vertical lines.
2221
2222\dt \cw{HATCH_PLUS}
2223
2224\dd This colour will be hatched by criss-crossing horizontal and
2225vertical lines.
2226
2227\dt \cw{HATCH_X}
2228
2229\dd This colour will be hatched by criss-crossing diagonal lines.
2230
2231Colours defined to use hatching may not be used for drawing lines or
2232text; they may only be used for filling areas. That is, they may be
2233used as the \c{fillcolour} parameter to \cw{draw_circle()} and
2234\cw{draw_polygon()}, and as the colour parameter to
2235\cw{draw_rect()}, but may not be used as the \c{outlinecolour}
2236parameter to \cw{draw_circle()} or \cw{draw_polygon()}, or with
2237\cw{draw_line()} or \cw{draw_text()}.
2238
2239\S{print-rgb-mono-colour} \cw{print_rgb_mono_colour()}
2240
2241\c int print_rgb_mono_colour(drawing *dr, float r, float g,
2242\c float b, float grey);
2243
2244This function allocates a colour index for a fully specified RGB
2245colour during printing.
2246
2247\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2248
2249If printing in black and white only, these values will be ignored,
2250and either pure black or pure white will be used instead, according
2251to the \q{grey} parameter. (The fallback colour is the same as the
2252one which would be allocated by \cw{print_mono_colour(grey)}.)
2253
2254\S{print-rgb-grey-colour} \cw{print_rgb_grey_colour()}
2255
2256\c int print_rgb_grey_colour(drawing *dr, float r, float g,
2257\c float b, float grey);
2258
2259This function allocates a colour index for a fully specified RGB
2260colour during printing.
2261
2262\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2263
2264If printing in black and white only, these values will be ignored,
2265and a shade of grey given by the \c{grey} parameter will be used
2266instead. (The fallback colour is the same as the one which would be
2267allocated by \cw{print_grey_colour(grey)}.)
2268
2269\S{print-rgb-hatched-colour} \cw{print_rgb_hatched_colour()}
2270
2271\c int print_rgb_hatched_colour(drawing *dr, float r, float g,
2272\c float b, float hatched);
2273
2274This function allocates a colour index for a fully specified RGB
2275colour during printing.
2276
2277\c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1.
2278
2279If printing in black and white only, these values will be ignored,
2280and a form of cross-hatching given by the \c{hatch} parameter will
2281be used instead; see \k{print-hatched-colour} for the possible
2282values of this parameter. (The fallback colour is the same as the
2283one which would be allocated by \cw{print_hatched_colour(hatch)}.)
2284
2285\S{print-line-width} \cw{print_line_width()}
2286
2287\c void print_line_width(drawing *dr, int width);
2288
2289This function is called to set the thickness of lines drawn during
2290printing. It is meaningless in drawing: all lines drawn by
2291\cw{draw_line()}, \cw{draw_circle} and \cw{draw_polygon()} are one
2292pixel in thickness. However, in printing there is no clear
2293definition of a pixel and so line widths must be explicitly
2294specified.
2295
2296The line width is specified in the usual coordinate system. Note,
2297however, that it is a hint only: the central printing system may
2298choose to vary line thicknesses at user request or due to printer
2299capabilities.
2300
2301\S{print-line-dotted} \cw{print_line_dotted()}
2302
2303\c void print_line_dotted(drawing *dr, int dotted);
2304
2305This function is called to toggle the drawing of dotted lines during
2306printing. It is not supported during drawing.
2307
2308The parameter \cq{dotted} is a boolean; \cw{TRUE} means that future
2309lines drawn by \cw{draw_line()}, \cw{draw_circle} and
2310\cw{draw_polygon()} will be dotted, and \cw{FALSE} means that they
2311will be solid.
2312
2313Some front ends may impose restrictions on the width of dotted
2314lines. Asking for a dotted line via this front end will override any
2315line width request if the front end requires it.
2316
2317\H{drawing-frontend} The drawing API as implemented by the front end
2318
2319This section describes the drawing API in the function-pointer form
2320in which it is implemented by a front end.
2321
2322(It isn't only platform-specific front ends which implement this
2323API; the platform-independent module \c{ps.c} also provides an
2324implementation of it which outputs PostScript. Thus, any platform
2325which wants to do PS printing can do so with minimum fuss.)
2326
2327The following entries all describe function pointer fields in a
2328structure called \c{drawing_api}. Each of the functions takes a
2329\cq{void *} context pointer, which it should internally cast back to
2330a more useful type. Thus, a drawing \e{object} (\c{drawing *)}
2331suitable for passing to the back end redraw or printing functions
2332is constructed by passing a \c{drawing_api} and a \cq{void *} to the
2333function \cw{drawing_new()} (see \k{drawing-new}).
2334
2335\S{drawingapi-draw-text} \cw{draw_text()}
2336
2337\c void (*draw_text)(void *handle, int x, int y, int fonttype,
2338\c int fontsize, int align, int colour, char *text);
2339
2340This function behaves exactly like the back end \cw{draw_text()}
2341function; see \k{drawing-draw-text}.
2342
2343\S{drawingapi-draw-rect} \cw{draw_rect()}
2344
2345\c void (*draw_rect)(void *handle, int x, int y, int w, int h,
2346\c int colour);
2347
2348This function behaves exactly like the back end \cw{draw_rect()}
2349function; see \k{drawing-draw-rect}.
2350
2351\S{drawingapi-draw-line} \cw{draw_line()}
2352
2353\c void (*draw_line)(void *handle, int x1, int y1, int x2, int y2,
2354\c int colour);
2355
2356This function behaves exactly like the back end \cw{draw_line()}
2357function; see \k{drawing-draw-line}.
2358
2359\S{drawingapi-draw-polygon} \cw{draw_polygon()}
2360
2361\c void (*draw_polygon)(void *handle, int *coords, int npoints,
2362\c int fillcolour, int outlinecolour);
2363
2364This function behaves exactly like the back end \cw{draw_polygon()}
2365function; see \k{drawing-draw-polygon}.
2366
2367\S{drawingapi-draw-circle} \cw{draw_circle()}
2368
2369\c void (*draw_circle)(void *handle, int cx, int cy, int radius,
2370\c int fillcolour, int outlinecolour);
2371
2372This function behaves exactly like the back end \cw{draw_circle()}
2373function; see \k{drawing-draw-circle}.
2374
2375\S{drawingapi-draw-thick-line} \cw{draw_thick_line()}
2376
2377\c void draw_thick_line(drawing *dr, float thickness,
2378\c float x1, float y1, float x2, float y2,
2379\c int colour)
2380
2381This function behaves exactly like the back end
2382\cw{draw_thick_line()} function; see \k{drawing-draw-thick-line}.
2383
2384An implementation of this API which doesn't provide high-quality
2385rendering of thick lines is permitted to define this function
2386pointer to be \cw{NULL}. The middleware in \cw{drawing.c} will notice
2387and provide a low-quality alternative using \cw{draw_polygon()}.
2388
2389\S{drawingapi-draw-update} \cw{draw_update()}
2390
2391\c void (*draw_update)(void *handle, int x, int y, int w, int h);
2392
2393This function behaves exactly like the back end \cw{draw_update()}
2394function; see \k{drawing-draw-update}.
2395
2396An implementation of this API which only supports printing is
2397permitted to define this function pointer to be \cw{NULL} rather
2398than bothering to define an empty function. The middleware in
2399\cw{drawing.c} will notice and avoid calling it.
2400
2401\S{drawingapi-clip} \cw{clip()}
2402
2403\c void (*clip)(void *handle, int x, int y, int w, int h);
2404
2405This function behaves exactly like the back end \cw{clip()}
2406function; see \k{drawing-clip}.
2407
2408\S{drawingapi-unclip} \cw{unclip()}
2409
2410\c void (*unclip)(void *handle);
2411
2412This function behaves exactly like the back end \cw{unclip()}
2413function; see \k{drawing-unclip}.
2414
2415\S{drawingapi-start-draw} \cw{start_draw()}
2416
2417\c void (*start_draw)(void *handle);
2418
2419This function is called at the start of drawing. It allows the front
2420end to initialise any temporary data required to draw with, such as
2421device contexts.
2422
2423Implementations of this API which do not provide drawing services
2424may define this function pointer to be \cw{NULL}; it will never be
2425called unless drawing is attempted.
2426
2427\S{drawingapi-end-draw} \cw{end_draw()}
2428
2429\c void (*end_draw)(void *handle);
2430
2431This function is called at the end of drawing. It allows the front
2432end to do cleanup tasks such as deallocating device contexts and
2433scheduling appropriate GUI redraw events.
2434
2435Implementations of this API which do not provide drawing services
2436may define this function pointer to be \cw{NULL}; it will never be
2437called unless drawing is attempted.
2438
2439\S{drawingapi-status-bar} \cw{status_bar()}
2440
2441\c void (*status_bar)(void *handle, char *text);
2442
2443This function behaves exactly like the back end \cw{status_bar()}
2444function; see \k{drawing-status-bar}.
2445
2446Front ends implementing this function need not worry about it being
2447called repeatedly with the same text; the middleware code in
2448\cw{status_bar()} will take care of this.
2449
2450Implementations of this API which do not provide drawing services
2451may define this function pointer to be \cw{NULL}; it will never be
2452called unless drawing is attempted.
2453
2454\S{drawingapi-blitter-new} \cw{blitter_new()}
2455
2456\c blitter *(*blitter_new)(void *handle, int w, int h);
2457
2458This function behaves exactly like the back end \cw{blitter_new()}
2459function; see \k{drawing-blitter-new}.
2460
2461Implementations of this API which do not provide drawing services
2462may define this function pointer to be \cw{NULL}; it will never be
2463called unless drawing is attempted.
2464
2465\S{drawingapi-blitter-free} \cw{blitter_free()}
2466
2467\c void (*blitter_free)(void *handle, blitter *bl);
2468
2469This function behaves exactly like the back end \cw{blitter_free()}
2470function; see \k{drawing-blitter-free}.
2471
2472Implementations of this API which do not provide drawing services
2473may define this function pointer to be \cw{NULL}; it will never be
2474called unless drawing is attempted.
2475
2476\S{drawingapi-blitter-save} \cw{blitter_save()}
2477
2478\c void (*blitter_save)(void *handle, blitter *bl, int x, int y);
2479
2480This function behaves exactly like the back end \cw{blitter_save()}
2481function; see \k{drawing-blitter-save}.
2482
2483Implementations of this API which do not provide drawing services
2484may define this function pointer to be \cw{NULL}; it will never be
2485called unless drawing is attempted.
2486
2487\S{drawingapi-blitter-load} \cw{blitter_load()}
2488
2489\c void (*blitter_load)(void *handle, blitter *bl, int x, int y);
2490
2491This function behaves exactly like the back end \cw{blitter_load()}
2492function; see \k{drawing-blitter-load}.
2493
2494Implementations of this API which do not provide drawing services
2495may define this function pointer to be \cw{NULL}; it will never be
2496called unless drawing is attempted.
2497
2498\S{drawingapi-begin-doc} \cw{begin_doc()}
2499
2500\c void (*begin_doc)(void *handle, int pages);
2501
2502This function is called at the beginning of a printing run. It gives
2503the front end an opportunity to initialise any required printing
2504subsystem. It also provides the number of pages in advance.
2505
2506Implementations of this API which do not provide printing services
2507may define this function pointer to be \cw{NULL}; it will never be
2508called unless printing is attempted.
2509
2510\S{drawingapi-begin-page} \cw{begin_page()}
2511
2512\c void (*begin_page)(void *handle, int number);
2513
2514This function is called during printing, at the beginning of each
2515page. It gives the page number (numbered from 1 rather than 0, so
2516suitable for use in user-visible contexts).
2517
2518Implementations of this API which do not provide printing services
2519may define this function pointer to be \cw{NULL}; it will never be
2520called unless printing is attempted.
2521
2522\S{drawingapi-begin-puzzle} \cw{begin_puzzle()}
2523
2524\c void (*begin_puzzle)(void *handle, float xm, float xc,
2525\c float ym, float yc, int pw, int ph, float wmm);
2526
2527This function is called during printing, just before printing a
2528single puzzle on a page. It specifies the size and location of the
2529puzzle on the page.
2530
2531\c{xm} and \c{xc} specify the horizontal position of the puzzle on
2532the page, as a linear function of the page width. The front end is
2533expected to multiply the page width by \c{xm}, add \c{xc} (measured
2534in millimetres), and use the resulting x-coordinate as the left edge
2535of the puzzle.
2536
2537Similarly, \c{ym} and \c{yc} specify the vertical position of the
2538puzzle as a function of the page height: the page height times
2539\c{ym}, plus \c{yc} millimetres, equals the desired distance from
2540the top of the page to the top of the puzzle.
2541
2542(This unwieldy mechanism is required because not all printing
2543systems can communicate the page size back to the software. The
2544PostScript back end, for example, writes out PS which determines the
2545page size at print time by means of calling \cq{clippath}, and
2546centres the puzzles within that. Thus, exactly the same PS file
2547works on A4 or on US Letter paper without needing local
2548configuration, which simplifies matters.)
2549
2550\cw{pw} and \cw{ph} give the size of the puzzle in drawing API
2551coordinates. The printing system will subsequently call the puzzle's
2552own print function, which will in turn call drawing API functions in
2553the expectation that an area \cw{pw} by \cw{ph} units is available
2554to draw the puzzle on.
2555
2556Finally, \cw{wmm} gives the desired width of the puzzle in
2557millimetres. (The aspect ratio is expected to be preserved, so if
2558the desired puzzle height is also needed then it can be computed as
2559\cw{wmm*ph/pw}.)
2560
2561Implementations of this API which do not provide printing services
2562may define this function pointer to be \cw{NULL}; it will never be
2563called unless printing is attempted.
2564
2565\S{drawingapi-end-puzzle} \cw{end_puzzle()}
2566
2567\c void (*end_puzzle)(void *handle);
2568
2569This function is called after the printing of a specific puzzle is
2570complete.
2571
2572Implementations of this API which do not provide printing services
2573may define this function pointer to be \cw{NULL}; it will never be
2574called unless printing is attempted.
2575
2576\S{drawingapi-end-page} \cw{end_page()}
2577
2578\c void (*end_page)(void *handle, int number);
2579
2580This function is called after the printing of a page is finished.
2581
2582Implementations of this API which do not provide printing services
2583may define this function pointer to be \cw{NULL}; it will never be
2584called unless printing is attempted.
2585
2586\S{drawingapi-end-doc} \cw{end_doc()}
2587
2588\c void (*end_doc)(void *handle);
2589
2590This function is called after the printing of the entire document is
2591finished. This is the moment to close files, send things to the
2592print spooler, or whatever the local convention is.
2593
2594Implementations of this API which do not provide printing services
2595may define this function pointer to be \cw{NULL}; it will never be
2596called unless printing is attempted.
2597
2598\S{drawingapi-line-width} \cw{line_width()}
2599
2600\c void (*line_width)(void *handle, float width);
2601
2602This function is called to set the line thickness, during printing
2603only. Note that the width is a \cw{float} here, where it was an
2604\cw{int} as seen by the back end. This is because \cw{drawing.c} may
2605have scaled it on the way past.
2606
2607However, the width is still specified in the same coordinate system
2608as the rest of the drawing.
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-text-fallback} \cw{text_fallback()}
2615
2616\c char *(*text_fallback)(void *handle, const char *const *strings,
2617\c int nstrings);
2618
2619This function behaves exactly like the back end \cw{text_fallback()}
2620function; see \k{drawing-text-fallback}.
2621
2622Implementations of this API which do not support any characters
2623outside ASCII may define this function pointer to be \cw{NULL}, in
2624which case the central code in \cw{drawing.c} will provide a default
2625implementation.
2626
2627\H{drawingapi-frontend} The drawing API as called by the front end
2628
2629There are a small number of functions provided in \cw{drawing.c}
2630which the front end needs to \e{call}, rather than helping to
2631implement. They are described in this section.
2632
2633\S{drawing-new} \cw{drawing_new()}
2634
2635\c drawing *drawing_new(const drawing_api *api, midend *me,
2636\c void *handle);
2637
2638This function creates a drawing object. It is passed a
2639\c{drawing_api}, which is a structure containing nothing but
2640function pointers; and also a \cq{void *} handle. The handle is
2641passed back to each function pointer when it is called.
2642
2643The \c{midend} parameter is used for rewriting the status bar
2644contents: \cw{status_bar()} (see \k{drawing-status-bar}) has to call
2645a function in the mid-end which might rewrite the status bar text.
2646If the drawing object is to be used only for printing, or if the
2647game is known not to call \cw{status_bar()}, this parameter may be
2648\cw{NULL}.
2649
2650\S{drawing-free} \cw{drawing_free()}
2651
2652\c void drawing_free(drawing *dr);
2653
2654This function frees a drawing object. Note that the \cq{void *}
2655handle is not freed; if that needs cleaning up it must be done by
2656the front end.
2657
2658\S{drawing-print-get-colour} \cw{print_get_colour()}
2659
2660\c void print_get_colour(drawing *dr, int colour, int printincolour,
2661\c int *hatch, float *r, float *g, float *b)
2662
2663This function is called by the implementations of the drawing API
2664functions when they are called in a printing context. It takes a
2665colour index as input, and returns the description of the colour as
2666requested by the back end.
2667
2668\c{printincolour} is \cw{TRUE} iff the implementation is printing in
2669colour. This will alter the results returned if the colour in
2670question was specified with a black-and-white fallback value.
2671
2672If the colour should be rendered by hatching, \c{*hatch} is filled
2673with the type of hatching desired. See \k{print-grey-colour} for
2674details of the values this integer can take.
2675
2676If the colour should be rendered as solid colour, \c{*hatch} is
2677given a negative value, and \c{*r}, \c{*g} and \c{*b} are filled
2678with the RGB values of the desired colour (if printing in colour),
2679or all filled with the grey-scale value (if printing in black and
2680white).
2681
2682\C{midend} The API provided by the mid-end
2683
2684This chapter documents the API provided by the mid-end to be called
2685by the front end. You probably only need to read this if you are a
2686front end implementor, i.e. you are porting Puzzles to a new
2687platform. If you're only interested in writing new puzzles, you can
2688safely skip this chapter.
2689
2690All the persistent state in the mid-end is encapsulated within a
2691\c{midend} structure, to facilitate having multiple mid-ends in any
2692port which supports multiple puzzle windows open simultaneously.
2693Each \c{midend} is intended to handle the contents of a single
2694puzzle window.
2695
2696\H{midend-new} \cw{midend_new()}
2697
2698\c midend *midend_new(frontend *fe, const game *ourgame,
2699\c const drawing_api *drapi, void *drhandle)
2700
2701Allocates and returns a new mid-end structure.
2702
2703The \c{fe} argument is stored in the mid-end. It will be used when
2704calling back to functions such as \cw{activate_timer()}
2705(\k{frontend-activate-timer}), and will be passed on to the back end
2706function \cw{colours()} (\k{backend-colours}).
2707
2708The parameters \c{drapi} and \c{drhandle} are passed to
2709\cw{drawing_new()} (\k{drawing-new}) to construct a drawing object
2710which will be passed to the back end function \cw{redraw()}
2711(\k{backend-redraw}). Hence, all drawing-related function pointers
2712defined in \c{drapi} can expect to be called with \c{drhandle} as
2713their first argument.
2714
2715The \c{ourgame} argument points to a container structure describing
2716a game back end. The mid-end thus created will only be capable of
2717handling that one game. (So even in a monolithic front end
2718containing all the games, this imposes the constraint that any
2719individual puzzle window is tied to a single game. Unless, of
2720course, you feel brave enough to change the mid-end for the window
2721without closing the window...)
2722
2723\H{midend-free} \cw{midend_free()}
2724
2725\c void midend_free(midend *me);
2726
2727Frees a mid-end structure and all its associated data.
2728
2729\H{midend-tilesize} \cw{midend_tilesize()}
2730
2731\c int midend_tilesize(midend *me);
2732
2733Returns the \cq{tilesize} parameter being used to display the
2734current puzzle (\k{backend-preferred-tilesize}).
2735
2736\H{midend-set-params} \cw{midend_set_params()}
2737
2738\c void midend_set_params(midend *me, game_params *params);
2739
2740Sets the current game parameters for a mid-end. Subsequent games
2741generated by \cw{midend_new_game()} (\k{midend-new-game}) will use
2742these parameters until further notice.
2743
2744The usual way in which the front end will have an actual
2745\c{game_params} structure to pass to this function is if it had
2746previously got it from \cw{midend_fetch_preset()}
2747(\k{midend-fetch-preset}). Thus, this function is usually called in
2748response to the user making a selection from the presets menu.
2749
2750\H{midend-get-params} \cw{midend_get_params()}
2751
2752\c game_params *midend_get_params(midend *me);
2753
2754Returns the current game parameters stored in this mid-end.
2755
2756The returned value is dynamically allocated, and should be freed
2757when finished with by passing it to the game's own
2758\cw{free_params()} function (see \k{backend-free-params}).
2759
2760\H{midend-size} \cw{midend_size()}
2761
2762\c void midend_size(midend *me, int *x, int *y, int user_size);
2763
2764Tells the mid-end to figure out its window size.
2765
2766On input, \c{*x} and \c{*y} should contain the maximum or requested
2767size for the window. (Typically this will be the size of the screen
2768that the window has to fit on, or similar.) The mid-end will
2769repeatedly call the back end function \cw{compute_size()}
2770(\k{backend-compute-size}), searching for a tile size that best
2771satisfies the requirements. On exit, \c{*x} and \c{*y} will contain
2772the size needed for the puzzle window's drawing area. (It is of
2773course up to the front end to adjust this for any additional window
2774furniture such as menu bars and window borders, if necessary. The
2775status bar is also not included in this size.)
2776
2777Use \c{user_size} to indicate whether \c{*x} and \c{*y} are a
2778requested size, or just a maximum size.
2779
2780If \c{user_size} is set to \cw{TRUE}, the mid-end will treat the
2781input size as a request, and will pick a tile size which
2782approximates it \e{as closely as possible}, going over the game's
2783preferred tile size if necessary to achieve this. The mid-end will
2784also use the resulting tile size as its preferred one until further
2785notice, on the assumption that this size was explicitly requested
2786by the user. Use this option if you want your front end to support
2787dynamic resizing of the puzzle window with automatic scaling of the
2788puzzle to fit.
2789
2790If \c{user_size} is set to \cw{FALSE}, then the game's tile size
2791will never go over its preferred one, although it may go under in
2792order to fit within the maximum bounds specified by \c{*x} and
2793\c{*y}. This is the recommended approach when opening a new window
2794at default size: the game will use its preferred size unless it has
2795to use a smaller one to fit on the screen. If the tile size is
2796shrunk for this reason, the change will not persist; if a smaller
2797grid is subsequently chosen, the tile size will recover.
2798
2799The mid-end will try as hard as it can to return a size which is
2800less than or equal to the input size, in both dimensions. In extreme
2801circumstances it may fail (if even the lowest possible tile size
2802gives window dimensions greater than the input), in which case it
2803will return a size greater than the input size. Front ends should be
2804prepared for this to happen (i.e. don't crash or fail an assertion),
2805but may handle it in any way they see fit: by rejecting the game
2806parameters which caused the problem, by opening a window larger than
2807the screen regardless of inconvenience, by introducing scroll bars
2808on the window, by drawing on a large bitmap and scaling it into a
2809smaller window, or by any other means you can think of. It is likely
2810that when the tile size is that small the game will be unplayable
2811anyway, so don't put \e{too} much effort into handling it
2812creatively.
2813
2814If your platform has no limit on window size (or if you're planning
2815to use scroll bars for large puzzles), you can pass dimensions of
2816\cw{INT_MAX} as input to this function. You should probably not do
2817that \e{and} set the \c{user_size} flag, though!
2818
2819The midend relies on the frontend calling \cw{midend_new_game()}
2820(\k{midend-new-game}) before calling \cw{midend_size()}.
2821
2822\H{midend-reset-tilesize} \cw{midend_reset_tilesize()}
2823
2824\c void midend_reset_tilesize(midend *me);
2825
2826This function resets the midend's preferred tile size to that of the
2827standard puzzle.
2828
2829As discussed in \k{midend-size}, puzzle resizes are typically
2830'sticky', in that once the user has dragged the puzzle to a different
2831window size, the resulting tile size will be remembered and used when
2832the puzzle configuration changes. If you \e{don't} want that, e.g. if
2833you want to provide a command to explicitly reset the puzzle size back
2834to its default, then you can call this just before calling
2835\cw{midend_size()} (which, in turn, you would probably call with
2836\c{user_size} set to \cw{FALSE}).
2837
2838\H{midend-new-game} \cw{midend_new_game()}
2839
2840\c void midend_new_game(midend *me);
2841
2842Causes the mid-end to begin a new game. Normally the game will be a
2843new randomly generated puzzle. However, if you have previously
2844called \cw{midend_game_id()} or \cw{midend_set_config()}, the game
2845generated might be dictated by the results of those functions. (In
2846particular, you \e{must} call \cw{midend_new_game()} after calling
2847either of those functions, or else no immediate effect will be
2848visible.)
2849
2850You will probably need to call \cw{midend_size()} after calling this
2851function, because if the game parameters have been changed since the
2852last new game then the window size might need to change. (If you
2853know the parameters \e{haven't} changed, you don't need to do this.)
2854
2855This function will create a new \c{game_drawstate}, but does not
2856actually perform a redraw (since you often need to call
2857\cw{midend_size()} before the redraw can be done). So after calling
2858this function and after calling \cw{midend_size()}, you should then
2859call \cw{midend_redraw()}. (It is not necessary to call
2860\cw{midend_force_redraw()}; that will discard the draw state and
2861create a fresh one, which is unnecessary in this case since there's
2862a fresh one already. It would work, but it's usually excessive.)
2863
2864\H{midend-restart-game} \cw{midend_restart_game()}
2865
2866\c void midend_restart_game(midend *me);
2867
2868This function causes the current game to be restarted. This is done
2869by placing a new copy of the original game state on the end of the
2870undo list (so that an accidental restart can be undone).
2871
2872This function automatically causes a redraw, i.e. the front end can
2873expect its drawing API to be called from \e{within} a call to this
2874function. Some back ends require that \cw{midend_size()}
2875(\k{midend-size}) is called before \cw{midend_restart_game()}.
2876
2877\H{midend-force-redraw} \cw{midend_force_redraw()}
2878
2879\c void midend_force_redraw(midend *me);
2880
2881Forces a complete redraw of the puzzle window, by means of
2882discarding the current \c{game_drawstate} and creating a new one
2883from scratch before calling the game's \cw{redraw()} function.
2884
2885The front end can expect its drawing API to be called from within a
2886call to this function. Some back ends require that \cw{midend_size()}
2887(\k{midend-size}) is called before \cw{midend_force_redraw()}.
2888
2889\H{midend-redraw} \cw{midend_redraw()}
2890
2891\c void midend_redraw(midend *me);
2892
2893Causes a partial redraw of the puzzle window, by means of simply
2894calling the game's \cw{redraw()} function. (That is, the only things
2895redrawn will be things that have changed since the last redraw.)
2896
2897The front end can expect its drawing API to be called from within a
2898call to this function. Some back ends require that \cw{midend_size()}
2899(\k{midend-size}) is called before \cw{midend_redraw()}.
2900
2901\H{midend-process-key} \cw{midend_process_key()}
2902
2903\c int midend_process_key(midend *me, int x, int y, int button);
2904
2905The front end calls this function to report a mouse or keyboard
2906event. The parameters \c{x}, \c{y} and \c{button} are almost
2907identical to the ones passed to the back end function
2908\cw{interpret_move()} (\k{backend-interpret-move}), except that the
2909front end is \e{not} required to provide the guarantees about mouse
2910event ordering. The mid-end will sort out multiple simultaneous
2911button presses and changes of button; the front end's responsibility
2912is simply to pass on the mouse events it receives as accurately as
2913possible.
2914
2915(Some platforms may need to emulate absent mouse buttons by means of
2916using a modifier key such as Shift with another mouse button. This
2917tends to mean that if Shift is pressed or released in the middle of
2918a mouse drag, the mid-end will suddenly stop receiving, say,
2919\cw{LEFT_DRAG} events and start receiving \cw{RIGHT_DRAG}s, with no
2920intervening button release or press events. This too is something
2921which the mid-end will sort out for you; the front end has no
2922obligation to maintain sanity in this area.)
2923
2924The front end \e{should}, however, always eventually send some kind
2925of button release. On some platforms this requires special effort:
2926Windows, for example, requires a call to the system API function
2927\cw{SetCapture()} in order to ensure that your window receives a
2928mouse-up event even if the pointer has left the window by the time
2929the mouse button is released. On any platform that requires this
2930sort of thing, the front end \e{is} responsible for doing it.
2931
2932Calling this function is very likely to result in calls back to the
2933front end's drawing API and/or \cw{activate_timer()}
2934(\k{frontend-activate-timer}).
2935
2936The return value from \cw{midend_process_key()} is non-zero, unless
2937the effect of the keypress was to request termination of the
2938program. A front end should shut down the puzzle in response to a
2939zero return.
2940
2941\H{midend-colours} \cw{midend_colours()}
2942
2943\c float *midend_colours(midend *me, int *ncolours);
2944
2945Returns an array of the colours required by the game, in exactly the
2946same format as that returned by the back end function \cw{colours()}
2947(\k{backend-colours}). Front ends should call this function rather
2948than calling the back end's version directly, since the mid-end adds
2949standard customisation facilities. (At the time of writing, those
2950customisation facilities are implemented hackily by means of
2951environment variables, but it's not impossible that they may become
2952more full and formal in future.)
2953
2954\H{midend-timer} \cw{midend_timer()}
2955
2956\c void midend_timer(midend *me, float tplus);
2957
2958If the mid-end has called \cw{activate_timer()}
2959(\k{frontend-activate-timer}) to request regular callbacks for
2960purposes of animation or timing, this is the function the front end
2961should call on a regular basis. The argument \c{tplus} gives the
2962time, in seconds, since the last time either this function was
2963called or \cw{activate_timer()} was invoked.
2964
2965One of the major purposes of timing in the mid-end is to perform
2966move animation. Therefore, calling this function is very likely to
2967result in calls back to the front end's drawing API.
2968
2969\H{midend-num-presets} \cw{midend_num_presets()}
2970
2971\c int midend_num_presets(midend *me);
2972
2973Returns the number of game parameter presets supplied by this game.
2974Front ends should use this function and \cw{midend_fetch_preset()}
2975to configure their presets menu rather than calling the back end
2976directly, since the mid-end adds standard customisation facilities.
2977(At the time of writing, those customisation facilities are
2978implemented hackily by means of environment variables, but it's not
2979impossible that they may become more full and formal in future.)
2980
2981\H{midend-fetch-preset} \cw{midend_fetch_preset()}
2982
2983\c void midend_fetch_preset(midend *me, int n,
2984\c char **name, game_params **params);
2985
2986Returns one of the preset game parameter structures for the game. On
2987input \c{n} must be a non-negative integer and less than the value
2988returned from \cw{midend_num_presets()}. On output, \c{*name} is set
2989to an ASCII string suitable for entering in the game's presets menu,
2990and \c{*params} is set to the corresponding \c{game_params}
2991structure.
2992
2993Both of the two output values are dynamically allocated, but they
2994are owned by the mid-end structure: the front end should not ever
2995free them directly, because they will be freed automatically during
2996\cw{midend_free()}.
2997
2998\H{midend-which-preset} \cw{midend_which_preset()}
2999
3000\c int midend_which_preset(midend *me);
3001
3002Returns the numeric index of the preset game parameter structure
3003which matches the current game parameters, or a negative number if
3004no preset matches. Front ends could use this to maintain a tick
3005beside one of the items in the menu (or tick the \q{Custom} option
3006if the return value is less than zero).
3007
3008\H{midend-wants-statusbar} \cw{midend_wants_statusbar()}
3009
3010\c int midend_wants_statusbar(midend *me);
3011
3012This function returns \cw{TRUE} if the puzzle has a use for a
3013textual status line (to display score, completion status, currently
3014active tiles, time, or anything else).
3015
3016Front ends should call this function rather than talking directly to
3017the back end.
3018
3019\H{midend-get-config} \cw{midend_get_config()}
3020
3021\c config_item *midend_get_config(midend *me, int which,
3022\c char **wintitle);
3023
3024Returns a dialog box description for user configuration.
3025
3026On input, \cw{which} should be set to one of three values, which
3027select which of the various dialog box descriptions is returned:
3028
3029\dt \cw{CFG_SETTINGS}
3030
3031\dd Requests the GUI parameter configuration box generated by the
3032puzzle itself. This should be used when the user selects \q{Custom}
3033from the game types menu (or equivalent). The mid-end passes this
3034request on to the back end function \cw{configure()}
3035(\k{backend-configure}).
3036
3037\dt \cw{CFG_DESC}
3038
3039\dd Requests a box suitable for entering a descriptive game ID (and
3040viewing the existing one). The mid-end generates this dialog box
3041description itself. This should be used when the user selects
3042\q{Specific} from the game menu (or equivalent).
3043
3044\dt \cw{CFG_SEED}
3045
3046\dd Requests a box suitable for entering a random-seed game ID (and
3047viewing the existing one). The mid-end generates this dialog box
3048description itself. This should be used when the user selects
3049\q{Random Seed} from the game menu (or equivalent).
3050
3051The returned value is an array of \cw{config_item}s, exactly as
3052described in \k{backend-configure}. Another returned value is an
3053ASCII string giving a suitable title for the configuration window,
3054in \c{*wintitle}.
3055
3056Both returned values are dynamically allocated and will need to be
3057freed. The window title can be freed in the obvious way; the
3058\cw{config_item} array is a slightly complex structure, so a utility
3059function \cw{free_cfg()} is provided to free it for you. See
3060\k{utils-free-cfg}.
3061
3062(Of course, you will probably not want to free the \cw{config_item}
3063array until the dialog box is dismissed, because before then you
3064will probably need to pass it to \cw{midend_set_config}.)
3065
3066\H{midend-set-config} \cw{midend_set_config()}
3067
3068\c char *midend_set_config(midend *me, int which,
3069\c config_item *cfg);
3070
3071Passes the mid-end the results of a configuration dialog box.
3072\c{which} should have the same value which it had when
3073\cw{midend_get_config()} was called; \c{cfg} should be the array of
3074\c{config_item}s returned from \cw{midend_get_config()}, modified to
3075contain the results of the user's editing operations.
3076
3077This function returns \cw{NULL} on success, or otherwise (if the
3078configuration data was in some way invalid) an ASCII string
3079containing an error message suitable for showing to the user.
3080
3081If the function succeeds, it is likely that the game parameters will
3082have been changed and it is certain that a new game will be
3083requested. The front end should therefore call
3084\cw{midend_new_game()}, and probably also re-think the window size
3085using \cw{midend_size()} and eventually perform a refresh using
3086\cw{midend_redraw()}.
3087
3088\H{midend-game-id} \cw{midend_game_id()}
3089
3090\c char *midend_game_id(midend *me, char *id);
3091
3092Passes the mid-end a string game ID (of any of the valid forms
3093\cq{params}, \cq{params:description} or \cq{params#seed}) which the
3094mid-end will process and use for the next generated game.
3095
3096This function returns \cw{NULL} on success, or otherwise (if the
3097configuration data was in some way invalid) an ASCII string
3098containing an error message (not dynamically allocated) suitable for
3099showing to the user. In the event of an error, the mid-end's
3100internal state will be left exactly as it was before the call.
3101
3102If the function succeeds, it is likely that the game parameters will
3103have been changed and it is certain that a new game will be
3104requested. The front end should therefore call
3105\cw{midend_new_game()}, and probably also re-think the window size
3106using \cw{midend_size()} and eventually case a refresh using
3107\cw{midend_redraw()}.
3108
3109\H{midend-get-game-id} \cw{midend_get_game_id()}
3110
3111\c char *midend_get_game_id(midend *me)
3112
3113Returns a descriptive game ID (i.e. one in the form
3114\cq{params:description}) describing the game currently active in the
3115mid-end. The returned string is dynamically allocated.
3116
3117\H{midend-get-random-seed} \cw{midend_get_random_seed()}
3118
3119\c char *midend_get_random_seed(midend *me)
3120
3121Returns a random game ID (i.e. one in the form \cq{params#seedstring})
3122describing the game currently active in the mid-end, if there is one.
3123If the game was created by entering a description, no random seed will
3124currently exist and this function will return \cw{NULL}.
3125
3126The returned string, if it is non-\cw{NULL}, is dynamically allocated.
3127
3128\H{midend-can-format-as-text-now} \cw{midend_can_format_as_text_now()}
3129
3130\c int midend_can_format_as_text_now(midend *me);
3131
3132Returns \cw{TRUE} if the game code is capable of formatting puzzles
3133of the currently selected game type as ASCII.
3134
3135If this returns \cw{FALSE}, then \cw{midend_text_format()}
3136(\k{midend-text-format}) will return \cw{NULL}.
3137
3138\H{midend-text-format} \cw{midend_text_format()}
3139
3140\c char *midend_text_format(midend *me);
3141
3142Formats the current game's current state as ASCII text suitable for
3143copying to the clipboard. The returned string is dynamically
3144allocated.
3145
3146If the game's \c{can_format_as_text_ever} flag is \cw{FALSE}, or if
3147its \cw{can_format_as_text_now()} function returns \cw{FALSE}, then
3148this function will return \cw{NULL}.
3149
3150If the returned string contains multiple lines (which is likely), it
3151will use the normal C line ending convention (\cw{\\n} only). On
3152platforms which use a different line ending convention for data in
3153the clipboard, it is the front end's responsibility to perform the
3154conversion.
3155
3156\H{midend-solve} \cw{midend_solve()}
3157
3158\c char *midend_solve(midend *me);
3159
3160Requests the mid-end to perform a Solve operation.
3161
3162On success, \cw{NULL} is returned. On failure, an error message (not
3163dynamically allocated) is returned, suitable for showing to the
3164user.
3165
3166The front end can expect its drawing API and/or
3167\cw{activate_timer()} to be called from within a call to this
3168function. Some back ends require that \cw{midend_size()}
3169(\k{midend-size}) is called before \cw{midend_solve()}.
3170
3171\H{midend-status} \cw{midend_status()}
3172
3173\c int midend_status(midend *me);
3174
3175This function returns +1 if the midend is currently displaying a game
3176in a solved state, -1 if the game is in a permanently lost state, or 0
3177otherwise. This function just calls the back end's \cw{status()}
3178function. Front ends may wish to use this as a cue to proactively
3179offer the option of starting a new game.
3180
3181(See \k{backend-status} for more detail about the back end's
3182\cw{status()} function and discussion of what should count as which
3183status code.)
3184
3185\H{midend-can-undo} \cw{midend_can_undo()}
3186
3187\c int midend_can_undo(midend *me);
3188
3189Returns \cw{TRUE} if the midend is currently in a state where the undo
3190operation is meaningful (i.e. at least one position exists on the undo
3191chain before the present one). Front ends may wish to use this to
3192visually activate and deactivate an undo button.
3193
3194\H{midend-can-redo} \cw{midend_can_redo()}
3195
3196\c int midend_can_redo(midend *me);
3197
3198Returns \cw{TRUE} if the midend is currently in a state where the redo
3199operation is meaningful (i.e. at least one position exists on the redo
3200chain after the present one). Front ends may wish to use this to
3201visually activate and deactivate a redo button.
3202
3203\H{midend-serialise} \cw{midend_serialise()}
3204
3205\c void midend_serialise(midend *me,
3206\c void (*write)(void *ctx, void *buf, int len),
3207\c void *wctx);
3208
3209Calling this function causes the mid-end to convert its entire
3210internal state into a long ASCII text string, and to pass that
3211string (piece by piece) to the supplied \c{write} function.
3212
3213Desktop implementations can use this function to save a game in any
3214state (including half-finished) to a disk file, by supplying a
3215\c{write} function which is a wrapper on \cw{fwrite()} (or local
3216equivalent). Other implementations may find other uses for it, such
3217as compressing the large and sprawling mid-end state into a
3218manageable amount of memory when a palmtop application is suspended
3219so that another one can run; in this case \cw{write} might want to
3220write to a memory buffer rather than a file. There may be other uses
3221for it as well.
3222
3223This function will call back to the supplied \c{write} function a
3224number of times, with the first parameter (\c{ctx}) equal to
3225\c{wctx}, and the other two parameters pointing at a piece of the
3226output string.
3227
3228\H{midend-deserialise} \cw{midend_deserialise()}
3229
3230\c char *midend_deserialise(midend *me,
3231\c int (*read)(void *ctx, void *buf, int len),
3232\c void *rctx);
3233
3234This function is the counterpart to \cw{midend_serialise()}. It
3235calls the supplied \cw{read} function repeatedly to read a quantity
3236of data, and attempts to interpret that data as a serialised mid-end
3237as output by \cw{midend_serialise()}.
3238
3239The \cw{read} function is called with the first parameter (\c{ctx})
3240equal to \c{rctx}, and should attempt to read \c{len} bytes of data
3241into the buffer pointed to by \c{buf}. It should return \cw{FALSE}
3242on failure or \cw{TRUE} on success. It should not report success
3243unless it has filled the entire buffer; on platforms which might be
3244reading from a pipe or other blocking data source, \c{read} is
3245responsible for looping until the whole buffer has been filled.
3246
3247If the de-serialisation operation is successful, the mid-end's
3248internal data structures will be replaced by the results of the
3249load, and \cw{NULL} will be returned. Otherwise, the mid-end's state
3250will be completely unchanged and an error message (typically some
3251variation on \q{save file is corrupt}) will be returned. As usual,
3252the error message string is not dynamically allocated.
3253
3254If this function succeeds, it is likely that the game parameters
3255will have been changed. The front end should therefore probably
3256re-think the window size using \cw{midend_size()}, and probably
3257cause a refresh using \cw{midend_redraw()}.
3258
3259Because each mid-end is tied to a specific game back end, this
3260function will fail if you attempt to read in a save file generated by
3261a different game from the one configured in this mid-end, even if your
3262application is a monolithic one containing all the puzzles. See
3263\k{identify-game} for a helper function which will allow you to
3264identify a save file before you instantiate your mid-end in the first
3265place.
3266
3267\H{identify-game} \cw{identify_game()}
3268
3269\c char *identify_game(char **name,
3270\c int (*read)(void *ctx, void *buf, int len),
3271\c void *rctx);
3272
3273This function examines a serialised midend stream, of the same kind
3274used by \cw{midend_serialise()} and \cw{midend_deserialise()}, and
3275returns the \cw{name} field of the game back end from which it was
3276saved.
3277
3278You might want this if your front end was a monolithic one containing
3279all the puzzles, and you wanted to be able to load an arbitrary save
3280file and automatically switch to the right game. Probably your next
3281step would be to iterate through \cw{gamelist} (\k{frontend-backend})
3282looking for a game structure whose \cw{name} field matched the
3283returned string, and give an error if you didn't find one.
3284
3285On success, the return value of this function is \cw{NULL}, and the
3286game name string is written into \cw{*name}. The caller should free
3287that string after using it.
3288
3289On failure, \cw{*name} is \cw{NULL}, and the return value is an error
3290message (which does not need freeing at all).
3291
3292(This isn't strictly speaking a midend function, since it doesn't
3293accept or return a pointer to a midend. You'd probably call it just
3294\e{before} deciding what kind of midend you wanted to instantiate.)
3295
3296\H{midend-request-id-changes} \cw{midend_request_id_changes()}
3297
3298\c void midend_request_id_changes(midend *me,
3299\c void (*notify)(void *), void *ctx);
3300
3301This function is called by the front end to request notification by
3302the mid-end when the current game IDs (either descriptive or
3303random-seed) change. This can occur as a result of keypresses ('n' for
3304New Game, for example) or when a puzzle supersedes its game
3305description (see \k{backend-supersede}). After this function is
3306called, any change of the game ids will cause the mid-end to call
3307\cw{notify(ctx)} after the change.
3308
3309This is for use by puzzles which want to present the game description
3310to the user constantly (e.g. as an HTML hyperlink) instead of only
3311showing it when the user explicitly requests it.
3312
3313This is a function I anticipate few front ends needing to implement,
3314so I make it a callback rather than a static function in order to
3315relieve most front ends of the need to provide an empty
3316implementation.
3317
3318\H{frontend-backend} Direct reference to the back end structure by
3319the front end
3320
3321Although \e{most} things the front end needs done should be done by
3322calling the mid-end, there are a few situations in which the front
3323end needs to refer directly to the game back end structure.
3324
3325The most obvious of these is
3326
3327\b passing the game back end as a parameter to \cw{midend_new()}.
3328
3329There are a few other back end features which are not wrapped by the
3330mid-end because there didn't seem much point in doing so:
3331
3332\b fetching the \c{name} field to use in window titles and similar
3333
3334\b reading the \c{can_configure}, \c{can_solve} and
3335\c{can_format_as_text_ever} fields to decide whether to add those
3336items to the menu bar or equivalent
3337
3338\b reading the \c{winhelp_topic} field (Windows only)
3339
3340\b the GTK front end provides a \cq{--generate} command-line option
3341which directly calls the back end to do most of its work. This is
3342not really part of the main front end code, though, and I'm not sure
3343it counts.
3344
3345In order to find the game back end structure, the front end does one
3346of two things:
3347
3348\b If the particular front end is compiling a separate binary per
3349game, then the back end structure is a global variable with the
3350standard name \cq{thegame}:
3351
3352\lcont{
3353
3354\c extern const game thegame;
3355
3356}
3357
3358\b If the front end is compiled as a monolithic application
3359containing all the puzzles together (in which case the preprocessor
3360symbol \cw{COMBINED} must be defined when compiling most of the code
3361base), then there will be two global variables defined:
3362
3363\lcont{
3364
3365\c extern const game *gamelist[];
3366\c extern const int gamecount;
3367
3368\c{gamelist} will be an array of \c{gamecount} game structures,
3369declared in the automatically constructed source module \c{list.c}.
3370The application should search that array for the game it wants,
3371probably by reaching into each game structure and looking at its
3372\c{name} field.
3373
3374}
3375
3376\H{frontend-api} Mid-end to front-end calls
3377
3378This section describes the small number of functions which a front
3379end must provide to be called by the mid-end or other standard
3380utility modules.
3381
3382\H{frontend-get-random-seed} \cw{get_random_seed()}
3383
3384\c void get_random_seed(void **randseed, int *randseedsize);
3385
3386This function is called by a new mid-end, and also occasionally by
3387game back ends. Its job is to return a piece of data suitable for
3388using as a seed for initialisation of a new \c{random_state}.
3389
3390On exit, \c{*randseed} should be set to point at a newly allocated
3391piece of memory containing some seed data, and \c{*randseedsize}
3392should be set to the length of that data.
3393
3394A simple and entirely adequate implementation is to return a piece
3395of data containing the current system time at the highest
3396conveniently available resolution.
3397
3398\H{frontend-activate-timer} \cw{activate_timer()}
3399
3400\c void activate_timer(frontend *fe);
3401
3402This is called by the mid-end to request that the front end begin
3403calling it back at regular intervals.
3404
3405The timeout interval is left up to the front end; the finer it is,
3406the smoother move animations will be, but the more CPU time will be
3407used. Current front ends use values around 20ms (i.e. 50Hz).
3408
3409After this function is called, the mid-end will expect to receive
3410calls to \cw{midend_timer()} on a regular basis.
3411
3412\H{frontend-deactivate-timer} \cw{deactivate_timer()}
3413
3414\c void deactivate_timer(frontend *fe);
3415
3416This is called by the mid-end to request that the front end stop
3417calling \cw{midend_timer()}.
3418
3419\H{frontend-fatal} \cw{fatal()}
3420
3421\c void fatal(char *fmt, ...);
3422
3423This is called by some utility functions if they encounter a
3424genuinely fatal error such as running out of memory. It is a
3425variadic function in the style of \cw{printf()}, and is expected to
3426show the formatted error message to the user any way it can and then
3427terminate the application. It must not return.
3428
3429\H{frontend-default-colour} \cw{frontend_default_colour()}
3430
3431\c void frontend_default_colour(frontend *fe, float *output);
3432
3433This function expects to be passed a pointer to an array of three
3434\cw{float}s. It returns the platform's local preferred background
3435colour in those three floats, as red, green and blue values (in that
3436order) ranging from \cw{0.0} to \cw{1.0}.
3437
3438This function should only ever be called by the back end function
3439\cw{colours()} (\k{backend-colours}). (Thus, it isn't a
3440\e{midend}-to-frontend function as such, but there didn't seem to be
3441anywhere else particularly good to put it. Sorry.)
3442
3443\C{utils} Utility APIs
3444
3445This chapter documents a variety of utility APIs provided for the
3446general use of the rest of the Puzzles code.
3447
3448\H{utils-random} Random number generation
3449
3450Platforms' local random number generators vary widely in quality and
3451seed size. Puzzles therefore supplies its own high-quality random
3452number generator, with the additional advantage of giving the same
3453results if fed the same seed data on different platforms. This
3454allows game random seeds to be exchanged between different ports of
3455Puzzles and still generate the same games.
3456
3457Unlike the ANSI C \cw{rand()} function, the Puzzles random number
3458generator has an \e{explicit} state object called a
3459\c{random_state}. One of these is managed by each mid-end, for
3460example, and passed to the back end to generate a game with.
3461
3462\S{utils-random-init} \cw{random_new()}
3463
3464\c random_state *random_new(char *seed, int len);
3465
3466Allocates, initialises and returns a new \c{random_state}. The input
3467data is used as the seed for the random number stream (i.e. using
3468the same seed at a later time will generate the same stream).
3469
3470The seed data can be any data at all; there is no requirement to use
3471printable ASCII, or NUL-terminated strings, or anything like that.
3472
3473\S{utils-random-copy} \cw{random_copy()}
3474
3475\c random_state *random_copy(random_state *tocopy);
3476
3477Allocates a new \c{random_state}, copies the contents of another
3478\c{random_state} into it, and returns the new state. If exactly the
3479same sequence of functions is subseqently called on both the copy and
3480the original, the results will be identical. This may be useful for
3481speculatively performing some operation using a given random state,
3482and later replaying that operation precisely.
3483
3484\S{utils-random-free} \cw{random_free()}
3485
3486\c void random_free(random_state *state);
3487
3488Frees a \c{random_state}.
3489
3490\S{utils-random-bits} \cw{random_bits()}
3491
3492\c unsigned long random_bits(random_state *state, int bits);
3493
3494Returns a random number from 0 to \cw{2^bits-1} inclusive. \c{bits}
3495should be between 1 and 32 inclusive.
3496
3497\S{utils-random-upto} \cw{random_upto()}
3498
3499\c unsigned long random_upto(random_state *state, unsigned long limit);
3500
3501Returns a random number from 0 to \cw{limit-1} inclusive.
3502
3503\S{utils-random-state-encode} \cw{random_state_encode()}
3504
3505\c char *random_state_encode(random_state *state);
3506
3507Encodes the entire contents of a \c{random_state} in printable
3508ASCII. Returns a dynamically allocated string containing that
3509encoding. This can subsequently be passed to
3510\cw{random_state_decode()} to reconstruct the same \c{random_state}.
3511
3512\S{utils-random-state-decode} \cw{random_state_decode()}
3513
3514\c random_state *random_state_decode(char *input);
3515
3516Decodes a string generated by \cw{random_state_encode()} and
3517reconstructs an equivalent \c{random_state} to the one encoded, i.e.
3518it should produce the same stream of random numbers.
3519
3520This function has no error reporting; if you pass it an invalid
3521string it will simply generate an arbitrary random state, which may
3522turn out to be noticeably non-random.
3523
3524\S{utils-shuffle} \cw{shuffle()}
3525
3526\c void shuffle(void *array, int nelts, int eltsize, random_state *rs);
3527
3528Shuffles an array into a random order. The interface is much like
3529ANSI C \cw{qsort()}, except that there's no need for a compare
3530function.
3531
3532\c{array} is a pointer to the first element of the array. \c{nelts}
3533is the number of elements in the array; \c{eltsize} is the size of a
3534single element (typically measured using \c{sizeof}). \c{rs} is a
3535\c{random_state} used to generate all the random numbers for the
3536shuffling process.
3537
3538\H{utils-alloc} Memory allocation
3539
3540Puzzles has some central wrappers on the standard memory allocation
3541functions, which provide compile-time type checking, and run-time
3542error checking by means of quitting the application if it runs out
3543of memory. This doesn't provide the best possible recovery from
3544memory shortage, but on the other hand it greatly simplifies the
3545rest of the code, because nothing else anywhere needs to worry about
3546\cw{NULL} returns from allocation.
3547
3548\S{utils-snew} \cw{snew()}
3549
3550\c var = snew(type);
3551\e iii iiii
3552
3553This macro takes a single argument which is a \e{type name}. It
3554allocates space for one object of that type. If allocation fails it
3555will call \cw{fatal()} and not return; so if it does return, you can
3556be confident that its return value is non-\cw{NULL}.
3557
3558The return value is cast to the specified type, so that the compiler
3559will type-check it against the variable you assign it into. Thus,
3560this ensures you don't accidentally allocate memory the size of the
3561wrong type and assign it into a variable of the right one (or vice
3562versa!).
3563
3564\S{utils-snewn} \cw{snewn()}
3565
3566\c var = snewn(n, type);
3567\e iii i iiii
3568
3569This macro is the array form of \cw{snew()}. It takes two arguments;
3570the first is a number, and the second is a type name. It allocates
3571space for that many objects of that type, and returns a type-checked
3572non-\cw{NULL} pointer just as \cw{snew()} does.
3573
3574\S{utils-sresize} \cw{sresize()}
3575
3576\c var = sresize(var, n, type);
3577\e iii iii i iiii
3578
3579This macro is a type-checked form of \cw{realloc()}. It takes three
3580arguments: an input memory block, a new size in elements, and a
3581type. It re-sizes the input memory block to a size sufficient to
3582contain that many elements of that type. It returns a type-checked
3583non-\cw{NULL} pointer, like \cw{snew()} and \cw{snewn()}.
3584
3585The input memory block can be \cw{NULL}, in which case this function
3586will behave exactly like \cw{snewn()}. (In principle any
3587ANSI-compliant \cw{realloc()} implementation ought to cope with
3588this, but I've never quite trusted it to work everywhere.)
3589
3590\S{utils-sfree} \cw{sfree()}
3591
3592\c void sfree(void *p);
3593
3594This function is pretty much equivalent to \cw{free()}. It is
3595provided with a dynamically allocated block, and frees it.
3596
3597The input memory block can be \cw{NULL}, in which case this function
3598will do nothing. (In principle any ANSI-compliant \cw{free()}
3599implementation ought to cope with this, but I've never quite trusted
3600it to work everywhere.)
3601
3602\S{utils-dupstr} \cw{dupstr()}
3603
3604\c char *dupstr(const char *s);
3605
3606This function dynamically allocates a duplicate of a C string. Like
3607the \cw{snew()} functions, it guarantees to return non-\cw{NULL} or
3608not return at all.
3609
3610(Many platforms provide the function \cw{strdup()}. As well as
3611guaranteeing never to return \cw{NULL}, my version has the advantage
3612of being defined \e{everywhere}, rather than inconveniently not
3613quite everywhere.)
3614
3615\S{utils-free-cfg} \cw{free_cfg()}
3616
3617\c void free_cfg(config_item *cfg);
3618
3619This function correctly frees an array of \c{config_item}s,
3620including walking the array until it gets to the end and freeing
3621precisely those \c{sval} fields which are expected to be dynamically
3622allocated.
3623
3624(See \k{backend-configure} for details of the \c{config_item}
3625structure.)
3626
3627\H{utils-tree234} Sorted and counted tree functions
3628
3629Many games require complex algorithms for generating random puzzles,
3630and some require moderately complex algorithms even during play. A
3631common requirement during these algorithms is for a means of
3632maintaining sorted or unsorted lists of items, such that items can
3633be removed and added conveniently.
3634
3635For general use, Puzzles provides the following set of functions
3636which maintain 2-3-4 trees in memory. (A 2-3-4 tree is a balanced
3637tree structure, with the property that all lookups, insertions,
3638deletions, splits and joins can be done in \cw{O(log N)} time.)
3639
3640All these functions expect you to be storing a tree of \c{void *}
3641pointers. You can put anything you like in those pointers.
3642
3643By the use of per-node element counts, these tree structures have
3644the slightly unusual ability to look elements up by their numeric
3645index within the list represented by the tree. This means that they
3646can be used to store an unsorted list (in which case, every time you
3647insert a new element, you must explicitly specify the position where
3648you wish to insert it). They can also do numeric lookups in a sorted
3649tree, which might be useful for (for example) tracking the median of
3650a changing data set.
3651
3652As well as storing sorted lists, these functions can be used for
3653storing \q{maps} (associative arrays), by defining each element of a
3654tree to be a (key, value) pair.
3655
3656\S{utils-newtree234} \cw{newtree234()}
3657
3658\c tree234 *newtree234(cmpfn234 cmp);
3659
3660Creates a new empty tree, and returns a pointer to it.
3661
3662The parameter \c{cmp} determines the sorting criterion on the tree.
3663Its prototype is
3664
3665\c typedef int (*cmpfn234)(void *, void *);
3666
3667If you want a sorted tree, you should provide a function matching
3668this prototype, which returns like \cw{strcmp()} does (negative if
3669the first argument is smaller than the second, positive if it is
3670bigger, zero if they compare equal). In this case, the function
3671\cw{addpos234()} will not be usable on your tree (because all
3672insertions must respect the sorting order).
3673
3674If you want an unsorted tree, pass \cw{NULL}. In this case you will
3675not be able to use either \cw{add234()} or \cw{del234()}, or any
3676other function such as \cw{find234()} which depends on a sorting
3677order. Your tree will become something more like an array, except
3678that it will efficiently support insertion and deletion as well as
3679lookups by numeric index.
3680
3681\S{utils-freetree234} \cw{freetree234()}
3682
3683\c void freetree234(tree234 *t);
3684
3685Frees a tree. This function will not free the \e{elements} of the
3686tree (because they might not be dynamically allocated, or you might
3687be storing the same set of elements in more than one tree); it will
3688just free the tree structure itself. If you want to free all the
3689elements of a tree, you should empty it before passing it to
3690\cw{freetree234()}, by means of code along the lines of
3691
3692\c while ((element = delpos234(tree, 0)) != NULL)
3693\c sfree(element); /* or some more complicated free function */
3694\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
3695
3696\S{utils-add234} \cw{add234()}
3697
3698\c void *add234(tree234 *t, void *e);
3699
3700Inserts a new element \c{e} into the tree \c{t}. This function
3701expects the tree to be sorted; the new element is inserted according
3702to the sort order.
3703
3704If an element comparing equal to \c{e} is already in the tree, then
3705the insertion will fail, and the return value will be the existing
3706element. Otherwise, the insertion succeeds, and \c{e} is returned.
3707
3708\S{utils-addpos234} \cw{addpos234()}
3709
3710\c void *addpos234(tree234 *t, void *e, int index);
3711
3712Inserts a new element into an unsorted tree. Since there is no
3713sorting order to dictate where the new element goes, you must
3714specify where you want it to go. Setting \c{index} to zero puts the
3715new element right at the start of the list; setting \c{index} to the
3716current number of elements in the tree puts the new element at the
3717end.
3718
3719Return value is \c{e}, in line with \cw{add234()} (although this
3720function cannot fail except by running out of memory, in which case
3721it will bomb out and die rather than returning an error indication).
3722
3723\S{utils-index234} \cw{index234()}
3724
3725\c void *index234(tree234 *t, int index);
3726
3727Returns a pointer to the \c{index}th element of the tree, or
3728\cw{NULL} if \c{index} is out of range. Elements of the tree are
3729numbered from zero.
3730
3731\S{utils-find234} \cw{find234()}
3732
3733\c void *find234(tree234 *t, void *e, cmpfn234 cmp);
3734
3735Searches for an element comparing equal to \c{e} in a sorted tree.
3736
3737If \c{cmp} is \cw{NULL}, the tree's ordinary comparison function
3738will be used to perform the search. However, sometimes you don't
3739want that; suppose, for example, each of your elements is a big
3740structure containing a \c{char *} name field, and you want to find
3741the element with a given name. You \e{could} achieve this by
3742constructing a fake element structure, setting its name field
3743appropriately, and passing it to \cw{find234()}, but you might find
3744it more convenient to pass \e{just} a name string to \cw{find234()},
3745supplying an alternative comparison function which expects one of
3746its arguments to be a bare name and the other to be a large
3747structure containing a name field.
3748
3749Therefore, if \c{cmp} is not \cw{NULL}, then it will be used to
3750compare \c{e} to elements of the tree. The first argument passed to
3751\c{cmp} will always be \c{e}; the second will be an element of the
3752tree.
3753
3754(See \k{utils-newtree234} for the definition of the \c{cmpfn234}
3755function pointer type.)
3756
3757The returned value is the element found, or \cw{NULL} if the search
3758is unsuccessful.
3759
3760\S{utils-findrel234} \cw{findrel234()}
3761
3762\c void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation);
3763
3764This function is like \cw{find234()}, but has the additional ability
3765to do a \e{relative} search. The additional parameter \c{relation}
3766can be one of the following values:
3767
3768\dt \cw{REL234_EQ}
3769
3770\dd Find only an element that compares equal to \c{e}. This is
3771exactly the behaviour of \cw{find234()}.
3772
3773\dt \cw{REL234_LT}
3774
3775\dd Find the greatest element that compares strictly less than
3776\c{e}. \c{e} may be \cw{NULL}, in which case it finds the greatest
3777element in the whole tree (which could also be done by
3778\cw{index234(t, count234(t)-1)}).
3779
3780\dt \cw{REL234_LE}
3781
3782\dd Find the greatest element that compares less than or equal to
3783\c{e}. (That is, find an element that compares equal to \c{e} if
3784possible, but failing that settle for something just less than it.)
3785
3786\dt \cw{REL234_GT}
3787
3788\dd Find the smallest element that compares strictly greater than
3789\c{e}. \c{e} may be \cw{NULL}, in which case it finds the smallest
3790element in the whole tree (which could also be done by
3791\cw{index234(t, 0)}).
3792
3793\dt \cw{REL234_GE}
3794
3795\dd Find the smallest element that compares greater than or equal to
3796\c{e}. (That is, find an element that compares equal to \c{e} if
3797possible, but failing that settle for something just bigger than
3798it.)
3799
3800Return value, as before, is the element found or \cw{NULL} if no
3801element satisfied the search criterion.
3802
3803\S{utils-findpos234} \cw{findpos234()}
3804
3805\c void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index);
3806
3807This function is like \cw{find234()}, but has the additional feature
3808of returning the index of the element found in the tree; that index
3809is written to \c{*index} in the event of a successful search (a
3810non-\cw{NULL} return value).
3811
3812\c{index} may be \cw{NULL}, in which case this function behaves
3813exactly like \cw{find234()}.
3814
3815\S{utils-findrelpos234} \cw{findrelpos234()}
3816
3817\c void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation,
3818\c int *index);
3819
3820This function combines all the features of \cw{findrel234()} and
3821\cw{findpos234()}.
3822
3823\S{utils-del234} \cw{del234()}
3824
3825\c void *del234(tree234 *t, void *e);
3826
3827Finds an element comparing equal to \c{e} in the tree, deletes it,
3828and returns it.
3829
3830The input tree must be sorted.
3831
3832The element found might be \c{e} itself, or might merely compare
3833equal to it.
3834
3835Return value is \cw{NULL} if no such element is found.
3836
3837\S{utils-delpos234} \cw{delpos234()}
3838
3839\c void *delpos234(tree234 *t, int index);
3840
3841Deletes the element at position \c{index} in the tree, and returns
3842it.
3843
3844Return value is \cw{NULL} if the index is out of range.
3845
3846\S{utils-count234} \cw{count234()}
3847
3848\c int count234(tree234 *t);
3849
3850Returns the number of elements currently in the tree.
3851
3852\S{utils-splitpos234} \cw{splitpos234()}
3853
3854\c tree234 *splitpos234(tree234 *t, int index, int before);
3855
3856Splits the input tree into two pieces at a given position, and
3857creates a new tree containing all the elements on one side of that
3858position.
3859
3860If \c{before} is \cw{TRUE}, then all the items at or after position
3861\c{index} are left in the input tree, and the items before that
3862point are returned in the new tree. Otherwise, the reverse happens:
3863all the items at or after \c{index} are moved into the new tree, and
3864those before that point are left in the old one.
3865
3866If \c{index} is equal to 0 or to the number of elements in the input
3867tree, then one of the two trees will end up empty (and this is not
3868an error condition). If \c{index} is further out of range in either
3869direction, the operation will fail completely and return \cw{NULL}.
3870
3871This operation completes in \cw{O(log N)} time, no matter how large
3872the tree or how balanced or unbalanced the split.
3873
3874\S{utils-split234} \cw{split234()}
3875
3876\c tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel);
3877
3878Splits a sorted tree according to its sort order.
3879
3880\c{rel} can be any of the relation constants described in
3881\k{utils-findrel234}, \e{except} for \cw{REL234_EQ}. All the
3882elements having that relation to \c{e} will be transferred into the
3883new tree; the rest will be left in the old one.
3884
3885The parameter \c{cmp} has the same semantics as it does in
3886\cw{find234()}: if it is not \cw{NULL}, it will be used in place of
3887the tree's own comparison function when comparing elements to \c{e},
3888in such a way that \c{e} itself is always the first of its two
3889operands.
3890
3891Again, this operation completes in \cw{O(log N)} time, no matter how
3892large the tree or how balanced or unbalanced the split.
3893
3894\S{utils-join234} \cw{join234()}
3895
3896\c tree234 *join234(tree234 *t1, tree234 *t2);
3897
3898Joins two trees together by concatenating the lists they represent.
3899All the elements of \c{t2} are moved into \c{t1}, in such a way that
3900they appear \e{after} the elements of \c{t1}. The tree \c{t2} is
3901freed; the return value is \c{t1}.
3902
3903If you apply this function to a sorted tree and it violates the sort
3904order (i.e. the smallest element in \c{t2} is smaller than or equal
3905to the largest element in \c{t1}), the operation will fail and
3906return \cw{NULL}.
3907
3908This operation completes in \cw{O(log N)} time, no matter how large
3909the trees being joined together.
3910
3911\S{utils-join234r} \cw{join234r()}
3912
3913\c tree234 *join234r(tree234 *t1, tree234 *t2);
3914
3915Joins two trees together in exactly the same way as \cw{join234()},
3916but this time the combined tree is returned in \c{t2}, and \c{t1} is
3917destroyed. The elements in \c{t1} still appear before those in
3918\c{t2}.
3919
3920Again, this operation completes in \cw{O(log N)} time, no matter how
3921large the trees being joined together.
3922
3923\S{utils-copytree234} \cw{copytree234()}
3924
3925\c tree234 *copytree234(tree234 *t, copyfn234 copyfn,
3926\c void *copyfnstate);
3927
3928Makes a copy of an entire tree.
3929
3930If \c{copyfn} is \cw{NULL}, the tree will be copied but the elements
3931will not be; i.e. the new tree will contain pointers to exactly the
3932same physical elements as the old one.
3933
3934If you want to copy each actual element during the operation, you
3935can instead pass a function in \c{copyfn} which makes a copy of each
3936element. That function has the prototype
3937
3938\c typedef void *(*copyfn234)(void *state, void *element);
3939
3940and every time it is called, the \c{state} parameter will be set to
3941the value you passed in as \c{copyfnstate}.
3942
3943\H{utils-misc} Miscellaneous utility functions and macros
3944
3945This section contains all the utility functions which didn't
3946sensibly fit anywhere else.
3947
3948\S{utils-truefalse} \cw{TRUE} and \cw{FALSE}
3949
3950The main Puzzles header file defines the macros \cw{TRUE} and
3951\cw{FALSE}, which are used throughout the code in place of 1 and 0
3952(respectively) to indicate that the values are in a boolean context.
3953For code base consistency, I'd prefer it if submissions of new code
3954followed this convention as well.
3955
3956\S{utils-maxmin} \cw{max()} and \cw{min()}
3957
3958The main Puzzles header file defines the pretty standard macros
3959\cw{max()} and \cw{min()}, each of which is given two arguments and
3960returns the one which compares greater or less respectively.
3961
3962These macros may evaluate their arguments multiple times. Avoid side
3963effects.
3964
3965\S{utils-pi} \cw{PI}
3966
3967The main Puzzles header file defines a macro \cw{PI} which expands
3968to a floating-point constant representing pi.
3969
3970(I've never understood why ANSI's \cw{<math.h>} doesn't define this.
3971It'd be so useful!)
3972
3973\S{utils-obfuscate-bitmap} \cw{obfuscate_bitmap()}
3974
3975\c void obfuscate_bitmap(unsigned char *bmp, int bits, int decode);
3976
3977This function obscures the contents of a piece of data, by
3978cryptographic methods. It is useful for games of hidden information
3979(such as Mines, Guess or Black Box), in which the game ID
3980theoretically reveals all the information the player is supposed to
3981be trying to guess. So in order that players should be able to send
3982game IDs to one another without accidentally spoiling the resulting
3983game by looking at them, these games obfuscate their game IDs using
3984this function.
3985
3986Although the obfuscation function is cryptographic, it cannot
3987properly be called encryption because it has no key. Therefore,
3988anybody motivated enough can re-implement it, or hack it out of the
3989Puzzles source, and strip the obfuscation off one of these game IDs
3990to see what lies beneath. (Indeed, they could usually do it much
3991more easily than that, by entering the game ID into their own copy
3992of the puzzle and hitting Solve.) The aim is not to protect against
3993a determined attacker; the aim is simply to protect people who
3994wanted to play the game honestly from \e{accidentally} spoiling
3995their own fun.
3996
3997The input argument \c{bmp} points at a piece of memory to be
3998obfuscated. \c{bits} gives the length of the data. Note that that
3999length is in \e{bits} rather than bytes: if you ask for obfuscation
4000of a partial number of bytes, then you will get it. Bytes are
4001considered to be used from the top down: thus, for example, setting
4002\c{bits} to 10 will cover the whole of \cw{bmp[0]} and the \e{top
4003two} bits of \cw{bmp[1]}. The remainder of a partially used byte is
4004undefined (i.e. it may be corrupted by the function).
4005
4006The parameter \c{decode} is \cw{FALSE} for an encoding operation,
4007and \cw{TRUE} for a decoding operation. Each is the inverse of the
4008other. (There's no particular reason you shouldn't obfuscate by
4009decoding and restore cleartext by encoding, if you really wanted to;
4010it should still work.)
4011
4012The input bitmap is processed in place.
4013
4014\S{utils-bin2hex} \cw{bin2hex()}
4015
4016\c char *bin2hex(const unsigned char *in, int inlen);
4017
4018This function takes an input byte array and converts it into an
4019ASCII string encoding those bytes in (lower-case) hex. It returns a
4020dynamically allocated string containing that encoding.
4021
4022This function is useful for encoding the result of
4023\cw{obfuscate_bitmap()} in printable ASCII for use in game IDs.
4024
4025\S{utils-hex2bin} \cw{hex2bin()}
4026
4027\c unsigned char *hex2bin(const char *in, int outlen);
4028
4029This function takes an ASCII string containing hex digits, and
4030converts it back into a byte array of length \c{outlen}. If there
4031aren't enough hex digits in the string, the contents of the
4032resulting array will be undefined.
4033
4034This function is the inverse of \cw{bin2hex()}.
4035
4036\S{utils-game-mkhighlight} \cw{game_mkhighlight()}
4037
4038\c void game_mkhighlight(frontend *fe, float *ret,
4039\c int background, int highlight, int lowlight);
4040
4041It's reasonably common for a puzzle game's graphics to use
4042highlights and lowlights to indicate \q{raised} or \q{lowered}
4043sections. Fifteen, Sixteen and Twiddle are good examples of this.
4044
4045Puzzles using this graphical style are running a risk if they just
4046use whatever background colour is supplied to them by the front end,
4047because that background colour might be too light to see any
4048highlights on at all. (In particular, it's not unheard of for the
4049front end to specify a default background colour of white.)
4050
4051Therefore, such puzzles can call this utility function from their
4052\cw{colours()} routine (\k{backend-colours}). You pass it your front
4053end handle, a pointer to the start of your return array, and three
4054colour indices. It will:
4055
4056\b call \cw{frontend_default_colour()} (\k{frontend-default-colour})
4057to fetch the front end's default background colour
4058
4059\b alter the brightness of that colour if it's unsuitable
4060
4061\b define brighter and darker variants of the colour to be used as
4062highlights and lowlights
4063
4064\b write those results into the relevant positions in the \c{ret}
4065array.
4066
4067Thus, \cw{ret[background*3]} to \cw{ret[background*3+2]} will be set
4068to RGB values defining a sensible background colour, and similary
4069\c{highlight} and \c{lowlight} will be set to sensible colours.
4070
4071\C{writing} How to write a new puzzle
4072
4073This chapter gives a guide to how to actually write a new puzzle:
4074where to start, what to do first, how to solve common problems.
4075
4076The previous chapters have been largely composed of facts. This one
4077is mostly advice.
4078
4079\H{writing-editorial} Choosing a puzzle
4080
4081Before you start writing a puzzle, you have to choose one. Your
4082taste in puzzle games is up to you, of course; and, in fact, you're
4083probably reading this guide because you've \e{already} thought of a
4084game you want to write. But if you want to get it accepted into the
4085official Puzzles distribution, then there's a criterion it has to
4086meet.
4087
4088The current Puzzles editorial policy is that all games should be
4089\e{fair}. A fair game is one which a player can only fail to
4090complete through demonstrable lack of skill \dash that is, such that
4091a better player in the same situation would have \e{known} to do
4092something different.
4093
4094For a start, that means every game presented to the user must have
4095\e{at least one solution}. Giving the unsuspecting user a puzzle
4096which is actually impossible is not acceptable. (There is an
4097exception: if the user has selected some non-default option which is
4098clearly labelled as potentially unfair, \e{then} you're allowed to
4099generate possibly insoluble puzzles, because the user isn't
4100unsuspecting any more. Same Game and Mines both have options of this
4101type.)
4102
4103Also, this actually \e{rules out} games such as Klondike, or the
4104normal form of Mahjong Solitaire. Those games have the property that
4105even if there is a solution (i.e. some sequence of moves which will
4106get from the start state to the solved state), the player doesn't
4107necessarily have enough information to \e{find} that solution. In
4108both games, it is possible to reach a dead end because you had an
4109arbitrary choice to make and made it the wrong way. This violates
4110the fairness criterion, because a better player couldn't have known
4111they needed to make the other choice.
4112
4113(GNOME has a variant on Mahjong Solitaire which makes it fair: there
4114is a Shuffle operation which randomly permutes all the remaining
4115tiles without changing their positions, which allows you to get out
4116of a sticky situation. Using this operation adds a 60-second penalty
4117to your solution time, so it's to the player's advantage to try to
4118minimise the chance of having to use it. It's still possible to
4119render the game uncompletable if you end up with only two tiles
4120vertically stacked, but that's easy to foresee and avoid using a
4121shuffle operation. This form of the game \e{is} fair. Implementing
4122it in Puzzles would require an infrastructure change so that the
4123back end could communicate time penalties to the mid-end, but that
4124would be easy enough.)
4125
4126Providing a \e{unique} solution is a little more negotiable; it
4127depends on the puzzle. Solo would have been of unacceptably low
4128quality if it didn't always have a unique solution, whereas Twiddle
4129inherently has multiple solutions by its very nature and it would
4130have been meaningless to even \e{suggest} making it uniquely
4131soluble. Somewhere in between, Flip could reasonably be made to have
4132unique solutions (by enforcing a zero-dimension kernel in every
4133generated matrix) but it doesn't seem like a serious quality problem
4134that it doesn't.
4135
4136Of course, you don't \e{have} to care about all this. There's
4137nothing stopping you implementing any puzzle you want to if you're
4138happy to maintain your puzzle yourself, distribute it from your own
4139web site, fork the Puzzles code completely, or anything like that.
4140It's free software; you can do what you like with it. But any game
4141that you want to be accepted into \e{my} Puzzles code base has to
4142satisfy the fairness criterion, which means all randomly generated
4143puzzles must have a solution (unless the user has deliberately
4144chosen otherwise) and it must be possible \e{in theory} to find that
4145solution without having to guess.
4146
4147\H{writing-gs} Getting started
4148
4149The simplest way to start writing a new puzzle is to copy
4150\c{nullgame.c}. This is a template puzzle source file which does
4151almost nothing, but which contains all the back end function
4152prototypes and declares the back end data structure correctly. It is
4153built every time the rest of Puzzles is built, to ensure that it
4154doesn't get out of sync with the code and remains buildable.
4155
4156So start by copying \c{nullgame.c} into your new source file. Then
4157you'll gradually add functionality until the very boring Null Game
4158turns into your real game.
4159
4160Next you'll need to add your puzzle to the Makefiles, in order to
4161compile it conveniently. \e{Do not edit the Makefiles}: they are
4162created automatically by the script \c{mkfiles.pl}, from the file
4163called \c{Recipe}. Edit \c{Recipe}, and then re-run \c{mkfiles.pl}.
4164
4165Also, don't forget to add your puzzle to \c{list.c}: if you don't,
4166then it will still run fine on platforms which build each puzzle
4167separately, but Mac OS X and other monolithic platforms will not
4168include your new puzzle in their single binary.
4169
4170Once your source file is building, you can move on to the fun bit.
4171
4172\S{writing-generation} Puzzle generation
4173
4174Randomly generating instances of your puzzle is almost certain to be
4175the most difficult part of the code, and also the task with the
4176highest chance of turning out to be completely infeasible. Therefore
4177I strongly recommend doing it \e{first}, so that if it all goes
4178horribly wrong you haven't wasted any more time than you absolutely
4179had to. What I usually do is to take an unmodified \c{nullgame.c},
4180and start adding code to \cw{new_game_desc()} which tries to
4181generate a puzzle instance and print it out using \cw{printf()}.
4182Once that's working, \e{then} I start connecting it up to the return
4183value of \cw{new_game_desc()}, populating other structures like
4184\c{game_params}, and generally writing the rest of the source file.
4185
4186There are many ways to generate a puzzle which is known to be
4187soluble. In this section I list all the methods I currently know of,
4188in case any of them can be applied to your puzzle. (Not all of these
4189methods will work, or in some cases even make sense, for all
4190puzzles.)
4191
4192Some puzzles are mathematically tractable, meaning you can work out
4193in advance which instances are soluble. Sixteen, for example, has a
4194parity constraint in some settings which renders exactly half the
4195game space unreachable, but it can be mathematically proved that any
4196position not in that half \e{is} reachable. Therefore, Sixteen's
4197grid generation simply consists of selecting at random from a well
4198defined subset of the game space. Cube in its default state is even
4199easier: \e{every} possible arrangement of the blue squares and the
4200cube's starting position is soluble!
4201
4202Another option is to redefine what you mean by \q{soluble}. Black
4203Box takes this approach. There are layouts of balls in the box which
4204are completely indistinguishable from one another no matter how many
4205beams you fire into the box from which angles, which would normally
4206be grounds for declaring those layouts unfair; but fortunately,
4207detecting that indistinguishability is computationally easy. So
4208Black Box doesn't demand that your ball placements match its own; it
4209merely demands that your ball placements be \e{indistinguishable}
4210from the ones it was thinking of. If you have an ambiguous puzzle,
4211then any of the possible answers is considered to be a solution.
4212Having redefined the rules in that way, any puzzle is soluble again.
4213
4214Those are the simple techniques. If they don't work, you have to get
4215cleverer.
4216
4217One way to generate a soluble puzzle is to start from the solved
4218state and make inverse moves until you reach a starting state. Then
4219you know there's a solution, because you can just list the inverse
4220moves you made and make them in the opposite order to return to the
4221solved state.
4222
4223This method can be simple and effective for puzzles where you get to
4224decide what's a starting state and what's not. In Pegs, for example,
4225the generator begins with one peg in the centre of the board and
4226makes inverse moves until it gets bored; in this puzzle, valid
4227inverse moves are easy to detect, and \e{any} state that's reachable
4228from the solved state by inverse moves is a reasonable starting
4229position. So Pegs just continues making inverse moves until the
4230board satisfies some criteria about extent and density, and then
4231stops and declares itself done.
4232
4233For other puzzles, it can be a lot more difficult. Same Game uses
4234this strategy too, and it's lucky to get away with it at all: valid
4235inverse moves aren't easy to find (because although it's easy to
4236insert additional squares in a Same Game position, it's difficult to
4237arrange that \e{after} the insertion they aren't adjacent to any
4238other squares of the same colour), so you're constantly at risk of
4239running out of options and having to backtrack or start again. Also,
4240Same Game grids never start off half-empty, which means you can't
4241just stop when you run out of moves \dash you have to find a way to
4242fill the grid up \e{completely}.
4243
4244The other way to generate a puzzle that's soluble is to start from
4245the other end, and actually write a \e{solver}. This tends to ensure
4246that a puzzle has a \e{unique} solution over and above having a
4247solution at all, so it's a good technique to apply to puzzles for
4248which that's important.
4249
4250One theoretical drawback of generating soluble puzzles by using a
4251solver is that your puzzles are restricted in difficulty to those
4252which the solver can handle. (Most solvers are not fully general:
4253many sets of puzzle rules are NP-complete or otherwise nasty, so
4254most solvers can only handle a subset of the theoretically soluble
4255puzzles.) It's been my experience in practice, however, that this
4256usually isn't a problem; computers are good at very different things
4257from humans, and what the computer thinks is nice and easy might
4258still be pleasantly challenging for a human. For example, when
4259solving Dominosa puzzles I frequently find myself using a variety of
4260reasoning techniques that my solver doesn't know about; in
4261principle, therefore, I should be able to solve the puzzle using
4262only those techniques it \e{does} know about, but this would involve
4263repeatedly searching the entire grid for the one simple deduction I
4264can make. Computers are good at this sort of exhaustive search, but
4265it's been my experience that human solvers prefer to do more complex
4266deductions than to spend ages searching for simple ones. So in many
4267cases I don't find my own playing experience to be limited by the
4268restrictions on the solver.
4269
4270(This isn't \e{always} the case. Solo is a counter-example;
4271generating Solo puzzles using a simple solver does lead to
4272qualitatively easier puzzles. Therefore I had to make the Solo
4273solver rather more advanced than most of them.)
4274
4275There are several different ways to apply a solver to the problem of
4276generating a soluble puzzle. I list a few of them below.
4277
4278The simplest approach is brute force: randomly generate a puzzle,
4279use the solver to see if it's soluble, and if not, throw it away and
4280try again until you get lucky. This is often a viable technique if
4281all else fails, but it tends not to scale well: for many puzzle
4282types, the probability of finding a uniquely soluble instance
4283decreases sharply as puzzle size goes up, so this technique might
4284work reasonably fast for small puzzles but take (almost) forever at
4285larger sizes. Still, if there's no other alternative it can be
4286usable: Pattern and Dominosa both use this technique. (However,
4287Dominosa has a means of tweaking the randomly generated grids to
4288increase the \e{probability} of them being soluble, by ruling out
4289one of the most common ambiguous cases. This improved generation
4290speed by over a factor of 10 on the highest preset!)
4291
4292An approach which can be more scalable involves generating a grid
4293and then tweaking it to make it soluble. This is the technique used
4294by Mines and also by Net: first a random puzzle is generated, and
4295then the solver is run to see how far it gets. Sometimes the solver
4296will get stuck; when that happens, examine the area it's having
4297trouble with, and make a small random change in that area to allow
4298it to make more progress. Continue solving (possibly even without
4299restarting the solver), tweaking as necessary, until the solver
4300finishes. Then restart the solver from the beginning to ensure that
4301the tweaks haven't caused new problems in the process of solving old
4302ones (which can sometimes happen).
4303
4304This strategy works well in situations where the usual solver
4305failure mode is to get stuck in an easily localised spot. Thus it
4306works well for Net and Mines, whose most common failure mode tends
4307to be that most of the grid is fine but there are a few widely
4308separated ambiguous sections; but it would work less well for
4309Dominosa, in which the way you get stuck is to have scoured the
4310whole grid and not found anything you can deduce \e{anywhere}. Also,
4311it relies on there being a low probability that tweaking the grid
4312introduces a new problem at the same time as solving the old one;
4313Mines and Net also have the property that most of their deductions
4314are local, so that it's very unlikely for a tweak to affect
4315something half way across the grid from the location where it was
4316applied. In Dominosa, by contrast, a lot of deductions use
4317information about half the grid (\q{out of all the sixes, only one
4318is next to a three}, which can depend on the values of up to 32 of
4319the 56 squares in the default setting!), so this tweaking strategy
4320would be rather less likely to work well.
4321
4322A more specialised strategy is that used in Solo and Slant. These
4323puzzles have the property that they derive their difficulty from not
4324presenting all the available clues. (In Solo's case, if all the
4325possible clues were provided then the puzzle would already be
4326solved; in Slant it would still require user action to fill in the
4327lines, but it would present no challenge at all). Therefore, a
4328simple generation technique is to leave the decision of which clues
4329to provide until the last minute. In other words, first generate a
4330random \e{filled} grid with all possible clues present, and then
4331gradually remove clues for as long as the solver reports that it's
4332still soluble. Unlike the methods described above, this technique
4333\e{cannot} fail \dash once you've got a filled grid, nothing can
4334stop you from being able to convert it into a viable puzzle.
4335However, it wouldn't even be meaningful to apply this technique to
4336(say) Pattern, in which clues can never be left out, so the only way
4337to affect the set of clues is by altering the solution.
4338
4339(Unfortunately, Solo is complicated by the need to provide puzzles
4340at varying difficulty levels. It's easy enough to generate a puzzle
4341of \e{at most} a given level of difficulty; you just have a solver
4342with configurable intelligence, and you set it to a given level and
4343apply the above technique, thus guaranteeing that the resulting grid
4344is solvable by someone with at most that much intelligence. However,
4345generating a puzzle of \e{at least} a given level of difficulty is
4346rather harder; if you go for \e{at most} Intermediate level, you're
4347likely to find that you've accidentally generated a Trivial grid a
4348lot of the time, because removing just one number is sufficient to
4349take the puzzle from Trivial straight to Ambiguous. In that
4350situation Solo has no remaining options but to throw the puzzle away
4351and start again.)
4352
4353A final strategy is to use the solver \e{during} puzzle
4354construction: lay out a bit of the grid, run the solver to see what
4355it allows you to deduce, and then lay out a bit more to allow the
4356solver to make more progress. There are articles on the web that
4357recommend constructing Sudoku puzzles by this method (which is
4358completely the opposite way round to how Solo does it); for Sudoku
4359it has the advantage that you get to specify your clue squares in
4360advance (so you can have them make pretty patterns).
4361
4362Rectangles uses a strategy along these lines. First it generates a
4363grid by placing the actual rectangles; then it has to decide where
4364in each rectangle to place a number. It uses a solver to help it
4365place the numbers in such a way as to ensure a unique solution. It
4366does this by means of running a test solver, but it runs the solver
4367\e{before} it's placed any of the numbers \dash which means the
4368solver must be capable of coping with uncertainty about exactly
4369where the numbers are! It runs the solver as far as it can until it
4370gets stuck; then it narrows down the possible positions of a number
4371in order to allow the solver to make more progress, and so on. Most
4372of the time this process terminates with the grid fully solved, at
4373which point any remaining number-placement decisions can be made at
4374random from the options not so far ruled out. Note that unlike the
4375Net/Mines tweaking strategy described above, this algorithm does not
4376require a checking run after it completes: if it finishes
4377successfully at all, then it has definitely produced a uniquely
4378soluble puzzle.
4379
4380Most of the strategies described above are not 100% reliable. Each
4381one has a failure rate: every so often it has to throw out the whole
4382grid and generate a fresh one from scratch. (Solo's strategy would
4383be the exception, if it weren't for the need to provide configurable
4384difficulty levels.) Occasional failures are not a fundamental
4385problem in this sort of work, however: it's just a question of
4386dividing the grid generation time by the success rate (if it takes
438710ms to generate a candidate grid and 1/5 of them work, then it will
4388take 50ms on average to generate a viable one), and seeing whether
4389the expected time taken to \e{successfully} generate a puzzle is
4390unacceptably slow. Dominosa's generator has a very low success rate
4391(about 1 out of 20 candidate grids turn out to be usable, and if you
4392think \e{that's} bad then go and look at the source code and find
4393the comment showing what the figures were before the generation-time
4394tweaks!), but the generator itself is very fast so this doesn't
4395matter. Rectangles has a slower generator, but fails well under 50%
4396of the time.
4397
4398So don't be discouraged if you have an algorithm that doesn't always
4399work: if it \e{nearly} always works, that's probably good enough.
4400The one place where reliability is important is that your algorithm
4401must never produce false positives: it must not claim a puzzle is
4402soluble when it isn't. It can produce false negatives (failing to
4403notice that a puzzle is soluble), and it can fail to generate a
4404puzzle at all, provided it doesn't do either so often as to become
4405slow.
4406
4407One last piece of advice: for grid-based puzzles, when writing and
4408testing your generation algorithm, it's almost always a good idea
4409\e{not} to test it initially on a grid that's square (i.e.
4410\cw{w==h}), because if the grid is square then you won't notice if
4411you mistakenly write \c{h} instead of \c{w} (or vice versa)
4412somewhere in the code. Use a rectangular grid for testing, and any
4413size of grid will be likely to work after that.
4414
4415\S{writing-textformats} Designing textual description formats
4416
4417Another aspect of writing a puzzle which is worth putting some
4418thought into is the design of the various text description formats:
4419the format of the game parameter encoding, the game description
4420encoding, and the move encoding.
4421
4422The first two of these should be reasonably intuitive for a user to
4423type in; so provide some flexibility where possible. Suppose, for
4424example, your parameter format consists of two numbers separated by
4425an \c{x} to specify the grid dimensions (\c{10x10} or \c{20x15}),
4426and then has some suffixes to specify other aspects of the game
4427type. It's almost always a good idea in this situation to arrange
4428that \cw{decode_params()} can handle the suffixes appearing in any
4429order, even if \cw{encode_params()} only ever generates them in one
4430order.
4431
4432These formats will also be expected to be reasonably stable: users
4433will expect to be able to exchange game IDs with other users who
4434aren't running exactly the same version of your game. So make them
4435robust and stable: don't build too many assumptions into the game ID
4436format which will have to be changed every time something subtle
4437changes in the puzzle code.
4438
4439\H{writing-howto} Common how-to questions
4440
4441This section lists some common things people want to do when writing
4442a puzzle, and describes how to achieve them within the Puzzles
4443framework.
4444
4445\S{writing-howto-cursor} Drawing objects at only one position
4446
4447A common phenomenon is to have an object described in the
4448\c{game_state} or the \c{game_ui} which can only be at one position.
4449A cursor \dash probably specified in the \c{game_ui} \dash is a good
4450example.
4451
4452In the \c{game_ui}, it would \e{obviously} be silly to have an array
4453covering the whole game grid with a boolean flag stating whether the
4454cursor was at each position. Doing that would waste space, would
4455make it difficult to find the cursor in order to do anything with
4456it, and would introduce the potential for synchronisation bugs in
4457which you ended up with two cursors or none. The obviously sensible
4458way to store a cursor in the \c{game_ui} is to have fields directly
4459encoding the cursor's coordinates.
4460
4461However, it is a mistake to assume that the same logic applies to
4462the \c{game_drawstate}. If you replicate the cursor position fields
4463in the draw state, the redraw code will get very complicated. In the
4464draw state, in fact, it \e{is} probably the right thing to have a
4465cursor flag for every position in the grid. You probably have an
4466array for the whole grid in the drawstate already (stating what is
4467currently displayed in the window at each position); the sensible
4468approach is to add a \q{cursor} flag to each element of that array.
4469Then the main redraw loop will look something like this
4470(pseudo-code):
4471
4472\c for (y = 0; y < h; y++) {
4473\c for (x = 0; x < w; x++) {
4474\c int value = state->symbol_at_position[y][x];
4475\c if (x == ui->cursor_x && y == ui->cursor_y)
4476\c value |= CURSOR;
4477\c if (ds->symbol_at_position[y][x] != value) {
4478\c symbol_drawing_subroutine(dr, ds, x, y, value);
4479\c ds->symbol_at_position[y][x] = value;
4480\c }
4481\c }
4482\c }
4483
4484This loop is very simple, pretty hard to get wrong, and
4485\e{automatically} deals both with erasing the previous cursor and
4486drawing the new one, with no special case code required.
4487
4488This type of loop is generally a sensible way to write a redraw
4489function, in fact. The best thing is to ensure that the information
4490stored in the draw state for each position tells you \e{everything}
4491about what was drawn there. A good way to ensure that is to pass
4492precisely the same information, and \e{only} that information, to a
4493subroutine that does the actual drawing; then you know there's no
4494additional information which affects the drawing but which you don't
4495notice changes in.
4496
4497\S{writing-keyboard-cursor} Implementing a keyboard-controlled cursor
4498
4499It is often useful to provide a keyboard control method in a
4500basically mouse-controlled game. A keyboard-controlled cursor is
4501best implemented by storing its location in the \c{game_ui} (since
4502if it were in the \c{game_state} then the user would have to
4503separately undo every cursor move operation). So the procedure would
4504be:
4505
4506\b Put cursor position fields in the \c{game_ui}.
4507
4508\b \cw{interpret_move()} responds to arrow keys by modifying the
4509cursor position fields and returning \cw{""}.
4510
4511\b \cw{interpret_move()} responds to some sort of fire button by
4512actually performing a move based on the current cursor location.
4513
4514\b You might want an additional \c{game_ui} field stating whether
4515the cursor is currently visible, and having it disappear when a
4516mouse action occurs (so that it doesn't clutter the display when not
4517actually in use).
4518
4519\b You might also want to automatically hide the cursor in
4520\cw{changed_state()} when the current game state changes to one in
4521which there is no move to make (which is the case in some types of
4522completed game).
4523
4524\b \cw{redraw()} draws the cursor using the technique described in
4525\k{writing-howto-cursor}.
4526
4527\S{writing-howto-dragging} Implementing draggable sprites
4528
4529Some games have a user interface which involves dragging some sort
4530of game element around using the mouse. If you need to show a
4531graphic moving smoothly over the top of other graphics, use a
4532blitter (see \k{drawing-blitter} for the blitter API) to save the
4533background underneath it. The typical scenario goes:
4534
4535\b Have a blitter field in the \c{game_drawstate}.
4536
4537\b Set the blitter field to \cw{NULL} in the game's
4538\cw{new_drawstate()} function, since you don't yet know how big the
4539piece of saved background needs to be.
4540
4541\b In the game's \cw{set_size()} function, once you know the size of
4542the object you'll be dragging around the display and hence the
4543required size of the blitter, actually allocate the blitter.
4544
4545\b In \cw{free_drawstate()}, free the blitter if it's not \cw{NULL}.
4546
4547\b In \cw{interpret_move()}, respond to mouse-down and mouse-drag
4548events by updating some fields in the \cw{game_ui} which indicate
4549that a drag is in progress.
4550
4551\b At the \e{very end} of \cw{redraw()}, after all other drawing has
4552been done, draw the moving object if there is one. First save the
4553background under the object in the blitter; then set a clip
4554rectangle covering precisely the area you just saved (just in case
4555anti-aliasing or some other error causes your drawing to go beyond
4556the area you saved). Then draw the object, and call \cw{unclip()}.
4557Finally, set a flag in the \cw{game_drawstate} that indicates that
4558the blitter needs restoring.
4559
4560\b At the very start of \cw{redraw()}, before doing anything else at
4561all, check the flag in the \cw{game_drawstate}, and if it says the
4562blitter needs restoring then restore it. (Then clear the flag, so
4563that this won't happen again in the next redraw if no moving object
4564is drawn this time.)
4565
4566This way, you will be able to write the rest of the redraw function
4567completely ignoring the dragged object, as if it were floating above
4568your bitmap and being completely separate.
4569
4570\S{writing-ref-counting} Sharing large invariant data between all
4571game states
4572
4573In some puzzles, there is a large amount of data which never changes
4574between game states. The array of numbers in Dominosa is a good
4575example.
4576
4577You \e{could} dynamically allocate a copy of that array in every
4578\c{game_state}, and have \cw{dup_game()} make a fresh copy of it for
4579every new \c{game_state}; but it would waste memory and time. A
4580more efficient way is to use a reference-counted structure.
4581
4582\b Define a structure type containing the data in question, and also
4583containing an integer reference count.
4584
4585\b Have a field in \c{game_state} which is a pointer to this
4586structure.
4587
4588\b In \cw{new_game()}, when creating a fresh game state at the start
4589of a new game, create an instance of this structure, initialise it
4590with the invariant data, and set its reference count to 1.
4591
4592\b In \cw{dup_game()}, rather than making a copy of the structure
4593for the new game state, simply set the new game state to point at
4594the same copy of the structure, and increment its reference count.
4595
4596\b In \cw{free_game()}, decrement the reference count in the
4597structure pointed to by the game state; if the count reaches zero,
4598free the structure.
4599
4600This way, the invariant data will persist for only as long as it's
4601genuinely needed; \e{as soon} as the last game state for a
4602particular puzzle instance is freed, the invariant data for that
4603puzzle will vanish as well. Reference counting is a very efficient
4604form of garbage collection, when it works at all. (Which it does in
4605this instance, of course, because there's no possibility of circular
4606references.)
4607
4608\S{writing-flash-types} Implementing multiple types of flash
4609
4610In some games you need to flash in more than one different way.
4611Mines, for example, flashes white when you win, and flashes red when
4612you tread on a mine and die.
4613
4614The simple way to do this is:
4615
4616\b Have a field in the \c{game_ui} which describes the type of flash.
4617
4618\b In \cw{flash_length()}, examine the old and new game states to
4619decide whether a flash is required and what type. Write the type of
4620flash to the \c{game_ui} field whenever you return non-zero.
4621
4622\b In \cw{redraw()}, when you detect that \c{flash_time} is
4623non-zero, examine the field in \c{game_ui} to decide which type of
4624flash to draw.
4625
4626\cw{redraw()} will never be called with \c{flash_time} non-zero
4627unless \cw{flash_length()} was first called to tell the mid-end that
4628a flash was required; so whenever \cw{redraw()} notices that
4629\c{flash_time} is non-zero, you can be sure that the field in
4630\c{game_ui} is correctly set.
4631
4632\S{writing-move-anim} Animating game moves
4633
4634A number of puzzle types benefit from a quick animation of each move
4635you make.
4636
4637For some games, such as Fifteen, this is particularly easy. Whenever
4638\cw{redraw()} is called with \c{oldstate} non-\cw{NULL}, Fifteen
4639simply compares the position of each tile in the two game states,
4640and if the tile is not in the same place then it draws it some
4641fraction of the way from its old position to its new position. This
4642method copes automatically with undo.
4643
4644Other games are less obvious. In Sixteen, for example, you can't
4645just draw each tile a fraction of the way from its old to its new
4646position: if you did that, the end tile would zip very rapidly past
4647all the others to get to the other end and that would look silly.
4648(Worse, it would look inconsistent if the end tile was drawn on top
4649going one way and on the bottom going the other way.)
4650
4651A useful trick here is to define a field or two in the game state
4652that indicates what the last move was.
4653
4654\b Add a \q{last move} field to the \c{game_state} (or two or more
4655fields if the move is complex enough to need them).
4656
4657\b \cw{new_game()} initialises this field to a null value for a new
4658game state.
4659
4660\b \cw{execute_move()} sets up the field to reflect the move it just
4661performed.
4662
4663\b \cw{redraw()} now needs to examine its \c{dir} parameter. If
4664\c{dir} is positive, it determines the move being animated by
4665looking at the last-move field in \c{newstate}; but if \c{dir} is
4666negative, it has to look at the last-move field in \c{oldstate}, and
4667invert whatever move it finds there.
4668
4669Note also that Sixteen needs to store the \e{direction} of the move,
4670because you can't quite determine it by examining the row or column
4671in question. You can in almost all cases, but when the row is
4672precisely two squares long it doesn't work since a move in either
4673direction looks the same. (You could argue that since moving a
46742-element row left and right has the same effect, it doesn't matter
4675which one you animate; but in fact it's very disorienting to click
4676the arrow left and find the row moving right, and almost as bad to
4677undo a move to the right and find the game animating \e{another}
4678move to the right.)
4679
4680\S{writing-conditional-anim} Animating drag operations
4681
4682In Untangle, moves are made by dragging a node from an old position
4683to a new position. Therefore, at the time when the move is initially
4684made, it should not be animated, because the node has already been
4685dragged to the right place and doesn't need moving there. However,
4686it's nice to animate the same move if it's later undone or redone.
4687This requires a bit of fiddling.
4688
4689The obvious approach is to have a flag in the \c{game_ui} which
4690inhibits move animation, and to set that flag in
4691\cw{interpret_move()}. The question is, when would the flag be reset
4692again? The obvious place to do so is \cw{changed_state()}, which
4693will be called once per move. But it will be called \e{before}
4694\cw{anim_length()}, so if it resets the flag then \cw{anim_length()}
4695will never see the flag set at all.
4696
4697The solution is to have \e{two} flags in a queue.
4698
4699\b Define two flags in \c{game_ui}; let's call them \q{current} and
4700\q{next}.
4701
4702\b Set both to \cw{FALSE} in \c{new_ui()}.
4703
4704\b When a drag operation completes in \cw{interpret_move()}, set the
4705\q{next} flag to \cw{TRUE}.
4706
4707\b Every time \cw{changed_state()} is called, set the value of
4708\q{current} to the value in \q{next}, and then set the value of
4709\q{next} to \cw{FALSE}.
4710
4711\b That way, \q{current} will be \cw{TRUE} \e{after} a call to
4712\cw{changed_state()} if and only if that call to
4713\cw{changed_state()} was the result of a drag operation processed by
4714\cw{interpret_move()}. Any other call to \cw{changed_state()}, due
4715to an Undo or a Redo or a Restart or a Solve, will leave \q{current}
4716\cw{FALSE}.
4717
4718\b So now \cw{anim_length()} can request a move animation if and
4719only if the \q{current} flag is \e{not} set.
4720
4721\S{writing-cheating} Inhibiting the victory flash when Solve is used
4722
4723Many games flash when you complete them, as a visual congratulation
4724for having got to the end of the puzzle. It often seems like a good
4725idea to disable that flash when the puzzle is brought to a solved
4726state by means of the Solve operation.
4727
4728This is easily done:
4729
4730\b Add a \q{cheated} flag to the \c{game_state}.
4731
4732\b Set this flag to \cw{FALSE} in \cw{new_game()}.
4733
4734\b Have \cw{solve()} return a move description string which clearly
4735identifies the move as a solve operation.
4736
4737\b Have \cw{execute_move()} respond to that clear identification by
4738setting the \q{cheated} flag in the returned \c{game_state}. The
4739flag will then be propagated to all subsequent game states, even if
4740the user continues fiddling with the game after it is solved.
4741
4742\b \cw{flash_length()} now returns non-zero if \c{oldstate} is not
4743completed and \c{newstate} is, \e{and} neither state has the
4744\q{cheated} flag set.
4745
4746\H{writing-testing} Things to test once your puzzle is written
4747
4748Puzzle implementations written in this framework are self-testing as
4749far as I could make them.
4750
4751Textual game and move descriptions, for example, are generated and
4752parsed as part of the normal process of play. Therefore, if you can
4753make moves in the game \e{at all} you can be reasonably confident
4754that the mid-end serialisation interface will function correctly and
4755you will be able to save your game. (By contrast, if I'd stuck with
4756a single \cw{make_move()} function performing the jobs of both
4757\cw{interpret_move()} and \cw{execute_move()}, and had separate
4758functions to encode and decode a game state in string form, then
4759those functions would not be used during normal play; so they could
4760have been completely broken, and you'd never know it until you tried
4761to save the game \dash which would have meant you'd have to test
4762game saving \e{extensively} and make sure to test every possible
4763type of game state. As an added bonus, doing it the way I did leads
4764to smaller save files.)
4765
4766There is one exception to this, which is the string encoding of the
4767\c{game_ui}. Most games do not store anything permanent in the
4768\c{game_ui}, and hence do not need to put anything in its encode and
4769decode functions; but if there is anything in there, you do need to
4770test game loading and saving to ensure those functions work
4771properly.
4772
4773It's also worth testing undo and redo of all operations, to ensure
4774that the redraw and the animations (if any) work properly. Failing
4775to animate undo properly seems to be a common error.
4776
4777Other than that, just use your common sense.
diff --git a/apps/plugins/puzzles/divvy.c b/apps/plugins/puzzles/divvy.c
new file mode 100644
index 0000000000..dfd409c9e0
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/dominosa.R b/apps/plugins/puzzles/dominosa.R
new file mode 100644
index 0000000000..99218366e6
--- /dev/null
+++ b/apps/plugins/puzzles/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/dominosa.c b/apps/plugins/puzzles/dominosa.c
new file mode 100644
index 0000000000..a2dd69ba86
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/drawing.c b/apps/plugins/puzzles/drawing.c
new file mode 100644
index 0000000000..8bf58d5530
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/dsf.c b/apps/plugins/puzzles/dsf.c
new file mode 100644
index 0000000000..1a2fa8c4be
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/emcc.c b/apps/plugins/puzzles/emcc.c
new file mode 100644
index 0000000000..5ffcb0b793
--- /dev/null
+++ b/apps/plugins/puzzles/emcc.c
@@ -0,0 +1,867 @@
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 "rbassert.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(const char *name);
65extern int js_get_selected_preset(void);
66extern void js_select_preset(int n);
67extern void js_get_date_64(unsigned *p);
68extern void js_update_permalinks(const char *desc, const char *seed);
69extern void js_enable_undo_redo(int undo, int redo);
70extern void js_activate_timer();
71extern void js_deactivate_timer();
72extern void js_canvas_start_draw(void);
73extern void js_canvas_draw_update(int x, int y, int w, int h);
74extern void js_canvas_end_draw(void);
75extern void js_canvas_draw_rect(int x, int y, int w, int h,
76 const char *colour);
77extern void js_canvas_clip_rect(int x, int y, int w, int h);
78extern void js_canvas_unclip(void);
79extern void js_canvas_draw_line(float x1, float y1, float x2, float y2,
80 int width, const char *colour);
81extern void js_canvas_draw_poly(int *points, int npoints,
82 const char *fillcolour,
83 const char *outlinecolour);
84extern void js_canvas_draw_circle(int x, int y, int r,
85 const char *fillcolour,
86 const char *outlinecolour);
87extern int js_canvas_find_font_midpoint(int height, const char *fontptr);
88extern void js_canvas_draw_text(int x, int y, int halign,
89 const char *colptr, const char *fontptr,
90 const char *text);
91extern int js_canvas_new_blitter(int w, int h);
92extern void js_canvas_free_blitter(int id);
93extern void js_canvas_copy_to_blitter(int id, int x, int y, int w, int h);
94extern void js_canvas_copy_from_blitter(int id, int x, int y, int w, int h);
95extern void js_canvas_make_statusbar(void);
96extern void js_canvas_set_statusbar(const char *text);
97extern void js_canvas_set_size(int w, int h);
98
99extern void js_dialog_init(const char *title);
100extern void js_dialog_string(int i, const char *title, const char *initvalue);
101extern void js_dialog_choices(int i, const char *title, const char *choicelist,
102 int initvalue);
103extern void js_dialog_boolean(int i, const char *title, int initvalue);
104extern void js_dialog_launch(void);
105extern void js_dialog_cleanup(void);
106extern void js_focus_canvas(void);
107
108/*
109 * Call JS to get the date, and use that to initialise our random
110 * number generator to invent the first game seed.
111 */
112void get_random_seed(void **randseed, int *randseedsize)
113{
114 unsigned *ret = snewn(2, unsigned);
115 js_get_date_64(ret);
116 *randseed = ret;
117 *randseedsize = 2*sizeof(unsigned);
118}
119
120/*
121 * Fatal error, called in cases of complete despair such as when
122 * malloc() has returned NULL.
123 */
124void fatal(char *fmt, ...)
125{
126 char buf[512];
127 va_list ap;
128
129 strcpy(buf, "puzzle fatal error: ");
130
131 va_start(ap, fmt);
132 vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, ap);
133 va_end(ap);
134
135 js_error_box(buf);
136}
137
138void debug_printf(char *fmt, ...)
139{
140 char buf[512];
141 va_list ap;
142 va_start(ap, fmt);
143 vsnprintf(buf, sizeof(buf), fmt, ap);
144 va_end(ap);
145 js_debug(buf);
146}
147
148/*
149 * Helper function that makes it easy to test strings that might be
150 * NULL.
151 */
152int strnullcmp(const char *a, const char *b)
153{
154 if (a == NULL || b == NULL)
155 return a != NULL ? +1 : b != NULL ? -1 : 0;
156 return strcmp(a, b);
157}
158
159/*
160 * HTMLish names for the colours allocated by the puzzle.
161 */
162char **colour_strings;
163int ncolours;
164
165/*
166 * The global midend object.
167 */
168midend *me;
169
170/* ----------------------------------------------------------------------
171 * Timing functions.
172 */
173int timer_active = FALSE;
174void deactivate_timer(frontend *fe)
175{
176 js_deactivate_timer();
177 timer_active = FALSE;
178}
179void activate_timer(frontend *fe)
180{
181 if (!timer_active) {
182 js_activate_timer();
183 timer_active = TRUE;
184 }
185}
186void timer_callback(double tplus)
187{
188 if (timer_active)
189 midend_timer(me, tplus);
190}
191
192/* ----------------------------------------------------------------------
193 * Helper functions to resize the canvas, and variables to remember
194 * its size for other functions (e.g. trimming blitter rectangles).
195 */
196static int canvas_w, canvas_h;
197
198/* Called when we resize as a result of changing puzzle settings */
199static void resize(void)
200{
201 int w, h;
202 w = h = INT_MAX;
203 midend_size(me, &w, &h, FALSE);
204 js_canvas_set_size(w, h);
205 canvas_w = w;
206 canvas_h = h;
207}
208
209/* Called from JS when the user uses the resize handle */
210void resize_puzzle(int w, int h)
211{
212 midend_size(me, &w, &h, TRUE);
213 if (canvas_w != w || canvas_h != h) {
214 js_canvas_set_size(w, h);
215 canvas_w = w;
216 canvas_h = h;
217 midend_force_redraw(me);
218 }
219}
220
221/* Called from JS when the user uses the restore button */
222void restore_puzzle_size(int w, int h)
223{
224 midend_reset_tilesize(me);
225 resize();
226 midend_force_redraw(me);
227}
228
229/*
230 * HTML doesn't give us a default frontend colour of its own, so we
231 * just make up a lightish grey ourselves.
232 */
233void frontend_default_colour(frontend *fe, float *output)
234{
235 output[0] = output[1] = output[2] = 0.9F;
236}
237
238/*
239 * Helper function called from all over the place to ensure the undo
240 * and redo buttons get properly enabled and disabled after every move
241 * or undo or new-game event.
242 */
243static void update_undo_redo(void)
244{
245 js_enable_undo_redo(midend_can_undo(me), midend_can_redo(me));
246}
247
248/*
249 * Mouse event handlers called from JS.
250 */
251void mousedown(int x, int y, int button)
252{
253 button = (button == 0 ? LEFT_BUTTON :
254 button == 1 ? MIDDLE_BUTTON : RIGHT_BUTTON);
255 midend_process_key(me, x, y, button);
256 update_undo_redo();
257}
258
259void mouseup(int x, int y, int button)
260{
261 button = (button == 0 ? LEFT_RELEASE :
262 button == 1 ? MIDDLE_RELEASE : RIGHT_RELEASE);
263 midend_process_key(me, x, y, button);
264 update_undo_redo();
265}
266
267void mousemove(int x, int y, int buttons)
268{
269 int button = (buttons & 2 ? MIDDLE_DRAG :
270 buttons & 4 ? RIGHT_DRAG : LEFT_DRAG);
271 midend_process_key(me, x, y, button);
272 update_undo_redo();
273}
274
275/*
276 * Keyboard handler called from JS.
277 */
278void key(int keycode, int charcode, const char *key, const char *chr,
279 int shift, int ctrl)
280{
281 int keyevent = -1;
282
283 if (!strnullcmp(key, "Backspace") || !strnullcmp(key, "Del") ||
284 keycode == 8 || keycode == 46) {
285 keyevent = 127; /* Backspace / Delete */
286 } else if (!strnullcmp(key, "Enter") || keycode == 13) {
287 keyevent = 13; /* return */
288 } else if (!strnullcmp(key, "Left") || keycode == 37) {
289 keyevent = CURSOR_LEFT;
290 } else if (!strnullcmp(key, "Up") || keycode == 38) {
291 keyevent = CURSOR_UP;
292 } else if (!strnullcmp(key, "Right") || keycode == 39) {
293 keyevent = CURSOR_RIGHT;
294 } else if (!strnullcmp(key, "Down") || keycode == 40) {
295 keyevent = CURSOR_DOWN;
296 } else if (!strnullcmp(key, "End") || keycode == 35) {
297 /*
298 * We interpret Home, End, PgUp and PgDn as numeric keypad
299 * controls regardless of whether they're the ones on the
300 * numeric keypad (since we can't tell). The effect of
301 * this should only be that the non-numeric-pad versions
302 * of those keys generate directions in 8-way movement
303 * puzzles like Cube and Inertia.
304 */
305 keyevent = MOD_NUM_KEYPAD | '1';
306 } else if (!strnullcmp(key, "PageDown") || keycode==34) {
307 keyevent = MOD_NUM_KEYPAD | '3';
308 } else if (!strnullcmp(key, "Home") || keycode==36) {
309 keyevent = MOD_NUM_KEYPAD | '7';
310 } else if (!strnullcmp(key, "PageUp") || keycode==33) {
311 keyevent = MOD_NUM_KEYPAD | '9';
312 } else if (chr && chr[0] && !chr[1]) {
313 keyevent = chr[0] & 0xFF;
314 } else if (keycode >= 96 && keycode < 106) {
315 keyevent = MOD_NUM_KEYPAD | ('0' + keycode - 96);
316 } else if (keycode >= 65 && keycode <= 90) {
317 keyevent = keycode + (shift ? 0 : 32);
318 } else if (keycode >= 48 && keycode <= 57) {
319 keyevent = keycode;
320 } else if (keycode == 32) { /* space / CURSOR_SELECT2 */
321 keyevent = keycode;
322 }
323
324 if (keyevent >= 0) {
325 if (shift && keyevent >= 0x100)
326 keyevent |= MOD_SHFT;
327
328 if (ctrl) {
329 if (keyevent >= 0x100)
330 keyevent |= MOD_CTRL;
331 else
332 keyevent &= 0x1F;
333 }
334
335 midend_process_key(me, 0, 0, keyevent);
336 update_undo_redo();
337 }
338}
339
340/*
341 * Helper function called from several places to update the permalinks
342 * whenever a new game is created.
343 */
344static void update_permalinks(void)
345{
346 char *desc, *seed;
347 desc = midend_get_game_id(me);
348 seed = midend_get_random_seed(me);
349 js_update_permalinks(desc, seed);
350 sfree(desc);
351 sfree(seed);
352}
353
354/*
355 * Callback from the midend when the game ids change, so we can update
356 * the permalinks.
357 */
358static void ids_changed(void *ignored)
359{
360 update_permalinks();
361}
362
363/* ----------------------------------------------------------------------
364 * Implementation of the drawing API by calling Javascript canvas
365 * drawing functions. (Well, half of it; the other half is on the JS
366 * side.)
367 */
368static void js_start_draw(void *handle)
369{
370 js_canvas_start_draw();
371}
372
373static void js_clip(void *handle, int x, int y, int w, int h)
374{
375 js_canvas_clip_rect(x, y, w, h);
376}
377
378static void js_unclip(void *handle)
379{
380 js_canvas_unclip();
381}
382
383static void js_draw_text(void *handle, int x, int y, int fonttype,
384 int fontsize, int align, int colour, char *text)
385{
386 char fontstyle[80];
387 int halign;
388
389 sprintf(fontstyle, "%dpx %s", fontsize,
390 fonttype == FONT_FIXED ? "monospace" : "sans-serif");
391
392 if (align & ALIGN_VCENTRE)
393 y += js_canvas_find_font_midpoint(fontsize, fontstyle);
394
395 if (align & ALIGN_HCENTRE)
396 halign = 1;
397 else if (align & ALIGN_HRIGHT)
398 halign = 2;
399 else
400 halign = 0;
401
402 js_canvas_draw_text(x, y, halign, colour_strings[colour], fontstyle, text);
403}
404
405static void js_draw_rect(void *handle, int x, int y, int w, int h, int colour)
406{
407 js_canvas_draw_rect(x, y, w, h, colour_strings[colour]);
408}
409
410static void js_draw_line(void *handle, int x1, int y1, int x2, int y2,
411 int colour)
412{
413 js_canvas_draw_line(x1, y1, x2, y2, 1, colour_strings[colour]);
414}
415
416static void js_draw_thick_line(void *handle, float thickness,
417 float x1, float y1, float x2, float y2,
418 int colour)
419{
420 js_canvas_draw_line(x1, y1, x2, y2, thickness, colour_strings[colour]);
421}
422
423static void js_draw_poly(void *handle, int *coords, int npoints,
424 int fillcolour, int outlinecolour)
425{
426 js_canvas_draw_poly(coords, npoints,
427 fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
428 colour_strings[outlinecolour]);
429}
430
431static void js_draw_circle(void *handle, int cx, int cy, int radius,
432 int fillcolour, int outlinecolour)
433{
434 js_canvas_draw_circle(cx, cy, radius,
435 fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
436 colour_strings[outlinecolour]);
437}
438
439struct blitter {
440 int id; /* allocated on the js side */
441 int w, h; /* easier to retain here */
442};
443
444static blitter *js_blitter_new(void *handle, int w, int h)
445{
446 blitter *bl = snew(blitter);
447 bl->w = w;
448 bl->h = h;
449 bl->id = js_canvas_new_blitter(w, h);
450 return bl;
451}
452
453static void js_blitter_free(void *handle, blitter *bl)
454{
455 js_canvas_free_blitter(bl->id);
456 sfree(bl);
457}
458
459static void trim_rect(int *x, int *y, int *w, int *h)
460{
461 int x0, x1, y0, y1;
462
463 /*
464 * Reduce the size of the copied rectangle to stop it going
465 * outside the bounds of the canvas.
466 */
467
468 /* Transform from x,y,w,h form into coordinates of all edges */
469 x0 = *x;
470 y0 = *y;
471 x1 = *x + *w;
472 y1 = *y + *h;
473
474 /* Clip each coordinate at both extremes of the canvas */
475 x0 = (x0 < 0 ? 0 : x0 > canvas_w ? canvas_w : x0);
476 x1 = (x1 < 0 ? 0 : x1 > canvas_w ? canvas_w : x1);
477 y0 = (y0 < 0 ? 0 : y0 > canvas_h ? canvas_h : y0);
478 y1 = (y1 < 0 ? 0 : y1 > canvas_h ? canvas_h : y1);
479
480 /* Transform back into x,y,w,h to return */
481 *x = x0;
482 *y = y0;
483 *w = x1 - x0;
484 *h = y1 - y0;
485}
486
487static void js_blitter_save(void *handle, blitter *bl, int x, int y)
488{
489 int w = bl->w, h = bl->h;
490 trim_rect(&x, &y, &w, &h);
491 if (w > 0 && h > 0)
492 js_canvas_copy_to_blitter(bl->id, x, y, w, h);
493}
494
495static void js_blitter_load(void *handle, blitter *bl, int x, int y)
496{
497 int w = bl->w, h = bl->h;
498 trim_rect(&x, &y, &w, &h);
499 if (w > 0 && h > 0)
500 js_canvas_copy_from_blitter(bl->id, x, y, w, h);
501}
502
503static void js_draw_update(void *handle, int x, int y, int w, int h)
504{
505 trim_rect(&x, &y, &w, &h);
506 if (w > 0 && h > 0)
507 js_canvas_draw_update(x, y, w, h);
508}
509
510static void js_end_draw(void *handle)
511{
512 js_canvas_end_draw();
513}
514
515static void js_status_bar(void *handle, char *text)
516{
517 js_canvas_set_statusbar(text);
518}
519
520static char *js_text_fallback(void *handle, const char *const *strings,
521 int nstrings)
522{
523 return dupstr(strings[0]); /* Emscripten has no trouble with UTF-8 */
524}
525
526const struct drawing_api js_drawing = {
527 js_draw_text,
528 js_draw_rect,
529 js_draw_line,
530 js_draw_poly,
531 js_draw_circle,
532 js_draw_update,
533 js_clip,
534 js_unclip,
535 js_start_draw,
536 js_end_draw,
537 js_status_bar,
538 js_blitter_new,
539 js_blitter_free,
540 js_blitter_save,
541 js_blitter_load,
542 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
543 NULL, NULL, /* line_width, line_dotted */
544 js_text_fallback,
545 js_draw_thick_line,
546};
547
548/* ----------------------------------------------------------------------
549 * Presets and game-configuration dialog support.
550 */
551static game_params **presets;
552static int npresets;
553int have_presets_dropdown;
554
555void select_appropriate_preset(void)
556{
557 if (have_presets_dropdown) {
558 int preset = midend_which_preset(me);
559 js_select_preset(preset < 0 ? -1 : preset);
560 }
561}
562
563static config_item *cfg = NULL;
564static int cfg_which;
565
566/*
567 * Set up a dialog box. This is pretty easy on the C side; most of the
568 * work is done in JS.
569 */
570static void cfg_start(int which)
571{
572 char *title;
573 int i;
574
575 cfg = midend_get_config(me, which, &title);
576 cfg_which = which;
577
578 js_dialog_init(title);
579 sfree(title);
580
581 for (i = 0; cfg[i].type != C_END; i++) {
582 switch (cfg[i].type) {
583 case C_STRING:
584 js_dialog_string(i, cfg[i].name, cfg[i].sval);
585 break;
586 case C_BOOLEAN:
587 js_dialog_boolean(i, cfg[i].name, cfg[i].ival);
588 break;
589 case C_CHOICES:
590 js_dialog_choices(i, cfg[i].name, cfg[i].sval, cfg[i].ival);
591 break;
592 }
593 }
594
595 js_dialog_launch();
596}
597
598/*
599 * Callbacks from JS when the OK button is clicked, to return the
600 * final state of each control.
601 */
602void dlg_return_sval(int index, const char *val)
603{
604 sfree(cfg[index].sval);
605 cfg[index].sval = dupstr(val);
606}
607void dlg_return_ival(int index, int val)
608{
609 cfg[index].ival = val;
610}
611
612/*
613 * Called when the user clicks OK or Cancel. use_results will be TRUE
614 * or FALSE respectively, in those cases. We terminate the dialog box,
615 * unless the user selected an invalid combination of parameters.
616 */
617static void cfg_end(int use_results)
618{
619 if (use_results) {
620 /*
621 * User hit OK.
622 */
623 char *err = midend_set_config(me, cfg_which, cfg);
624
625 if (err) {
626 /*
627 * The settings were unacceptable, so leave the config box
628 * open for the user to adjust them and try again.
629 */
630 js_error_box(err);
631 } else {
632 /*
633 * New settings are fine; start a new game and close the
634 * dialog.
635 */
636 select_appropriate_preset();
637 midend_new_game(me);
638 resize();
639 midend_redraw(me);
640 free_cfg(cfg);
641 js_dialog_cleanup();
642 }
643 } else {
644 /*
645 * User hit Cancel. Close the dialog, but also we must still
646 * reselect the right element of the dropdown list.
647 *
648 * (Because: imagine you have a preset selected, and then you
649 * select Custom from the list, but change your mind and hit
650 * Esc. The Custom option will now still be selected in the
651 * list, whereas obviously it should show the preset you still
652 * _actually_ have selected. Worse still, it'll be the visible
653 * rather than invisible Custom option - see the comment in
654 * js_add_preset in emcclib.js - so you won't even be able to
655 * select Custom without a faffy workaround.)
656 */
657 select_appropriate_preset();
658
659 free_cfg(cfg);
660 js_dialog_cleanup();
661 }
662}
663
664/* ----------------------------------------------------------------------
665 * Called from JS when a command is given to the puzzle by clicking a
666 * button or control of some sort.
667 */
668void command(int n)
669{
670 switch (n) {
671 case 0: /* specific game ID */
672 cfg_start(CFG_DESC);
673 break;
674 case 1: /* random game seed */
675 cfg_start(CFG_SEED);
676 break;
677 case 2: /* game parameter dropdown changed */
678 {
679 int i = js_get_selected_preset();
680 if (i < 0) {
681 /*
682 * The user selected 'Custom', so launch the config
683 * box.
684 */
685 if (thegame.can_configure) /* (double-check just in case) */
686 cfg_start(CFG_SETTINGS);
687 } else {
688 /*
689 * The user selected a preset, so just switch straight
690 * to that.
691 */
692 assert(i < npresets);
693 midend_set_params(me, presets[i]);
694 midend_new_game(me);
695 resize();
696 midend_redraw(me);
697 update_undo_redo();
698 js_focus_canvas();
699 select_appropriate_preset(); /* sort out Custom/Customise */
700 }
701 }
702 break;
703 case 3: /* OK clicked in a config box */
704 cfg_end(TRUE);
705 update_undo_redo();
706 break;
707 case 4: /* Cancel clicked in a config box */
708 cfg_end(FALSE);
709 update_undo_redo();
710 break;
711 case 5: /* New Game */
712 midend_process_key(me, 0, 0, 'n');
713 update_undo_redo();
714 js_focus_canvas();
715 break;
716 case 6: /* Restart */
717 midend_restart_game(me);
718 update_undo_redo();
719 js_focus_canvas();
720 break;
721 case 7: /* Undo */
722 midend_process_key(me, 0, 0, 'u');
723 update_undo_redo();
724 js_focus_canvas();
725 break;
726 case 8: /* Redo */
727 midend_process_key(me, 0, 0, 'r');
728 update_undo_redo();
729 js_focus_canvas();
730 break;
731 case 9: /* Solve */
732 if (thegame.can_solve) {
733 char *msg = midend_solve(me);
734 if (msg)
735 js_error_box(msg);
736 }
737 update_undo_redo();
738 js_focus_canvas();
739 break;
740 }
741}
742
743/* ----------------------------------------------------------------------
744 * Setup function called at page load time. It's called main() because
745 * that's the most convenient thing in Emscripten, but it's not main()
746 * in the usual sense of bounding the program's entire execution.
747 * Instead, this function returns once the initial puzzle is set up
748 * and working, and everything thereafter happens by means of JS event
749 * handlers sending us callbacks.
750 */
751int main(int argc, char **argv)
752{
753 char *param_err;
754 float *colours;
755 int i;
756
757 /*
758 * Instantiate a midend.
759 */
760 me = midend_new(NULL, &thegame, &js_drawing, NULL);
761
762 /*
763 * Chuck in the HTML fragment ID if we have one (trimming the
764 * leading # off the front first). If that's invalid, we retain
765 * the error message and will display it at the end, after setting
766 * up a random puzzle as usual.
767 */
768 if (argc > 1 && argv[1][0] == '#' && argv[1][1] != '\0')
769 param_err = midend_game_id(me, argv[1] + 1);
770 else
771 param_err = NULL;
772
773 /*
774 * Create either a random game or the specified one, and set the
775 * canvas size appropriately.
776 */
777 midend_new_game(me);
778 resize();
779
780 /*
781 * Create a status bar, if needed.
782 */
783 if (midend_wants_statusbar(me))
784 js_canvas_make_statusbar();
785
786 /*
787 * Set up the game-type dropdown with presets and/or the Custom
788 * option.
789 */
790 npresets = midend_num_presets(me);
791 if (npresets == 0) {
792 /*
793 * This puzzle doesn't have selectable game types at all.
794 * Completely remove the drop-down list from the page.
795 */
796 js_remove_type_dropdown();
797 have_presets_dropdown = FALSE;
798 } else {
799 presets = snewn(npresets, game_params *);
800 for (i = 0; i < npresets; i++) {
801 char *name;
802 midend_fetch_preset(me, i, &name, &presets[i]);
803 js_add_preset(name);
804 }
805 if (thegame.can_configure)
806 js_add_preset(NULL); /* the 'Custom' entry in the dropdown */
807
808 have_presets_dropdown = TRUE;
809
810 /*
811 * Now ensure the appropriate element of the presets menu
812 * starts off selected, in case it isn't the first one in the
813 * list (e.g. Slant).
814 */
815 select_appropriate_preset();
816 }
817
818 /*
819 * Remove the Solve button if the game doesn't support it.
820 */
821 if (!thegame.can_solve)
822 js_remove_solve_button();
823
824 /*
825 * Retrieve the game's colours, and convert them into #abcdef type
826 * hex ID strings.
827 */
828 colours = midend_colours(me, &ncolours);
829 colour_strings = snewn(ncolours, char *);
830 for (i = 0; i < ncolours; i++) {
831 char col[40];
832 sprintf(col, "#%02x%02x%02x",
833 (unsigned)(0.5 + 255 * colours[i*3+0]),
834 (unsigned)(0.5 + 255 * colours[i*3+1]),
835 (unsigned)(0.5 + 255 * colours[i*3+2]));
836 colour_strings[i] = dupstr(col);
837 }
838
839 /*
840 * Request notification when the game ids change (e.g. if the user
841 * presses 'n', and also when Mines supersedes its game
842 * description), so that we can proactively update the permalink.
843 */
844 midend_request_id_changes(me, ids_changed, NULL);
845
846 /*
847 * Draw the puzzle's initial state, and set up the permalinks and
848 * undo/redo greying out.
849 */
850 midend_redraw(me);
851 update_permalinks();
852 update_undo_redo();
853
854 /*
855 * If we were given an erroneous game ID in argv[1], now's the
856 * time to put up the error box about it, after we've fully set up
857 * a random puzzle. Then when the user clicks 'ok', we have a
858 * puzzle for them.
859 */
860 if (param_err)
861 js_error_box(param_err);
862
863 /*
864 * Done. Return to JS, and await callbacks!
865 */
866 return 0;
867}
diff --git a/apps/plugins/puzzles/emcclib.js b/apps/plugins/puzzles/emcclib.js
new file mode 100644
index 0000000000..385281ad0b
--- /dev/null
+++ b/apps/plugins/puzzles/emcclib.js
@@ -0,0 +1,757 @@
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 document.getElementById("gametype").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(const char *name);
63 *
64 * Add a preset to the drop-down types menu. The provided text is
65 * the name of the preset. (The corresponding game_params stays on
66 * the C side and never comes out this far; we just pass a numeric
67 * index back to the C code when a selection is made.)
68 *
69 * The special 'Custom' preset is requested by passing NULL to
70 * this function, rather than the string "Custom", since in that
71 * case we need to do something special - see below.
72 */
73 js_add_preset: function(ptr) {
74 var name = (ptr == 0 ? "Customise..." : Pointer_stringify(ptr));
75 var value = gametypeoptions.length;
76
77 var option = document.createElement("option");
78 option.value = value;
79 option.appendChild(document.createTextNode(name));
80 gametypeselector.appendChild(option);
81 gametypeoptions.push(option);
82
83 if (ptr == 0) {
84 // The option we've just created is the one for inventing
85 // a new custom setup.
86 gametypenewcustom = option;
87 option.value = -1;
88
89 // Now create another element called 'Custom', which will
90 // be auto-selected by us to indicate the custom settings
91 // you've previously selected. However, we don't add it to
92 // the game type selector; it will only appear when the
93 // user actually has custom settings selected.
94 option = document.createElement("option");
95 option.value = -2;
96 option.appendChild(document.createTextNode("Custom"));
97 gametypethiscustom = option;
98 }
99 },
100
101 /*
102 * int js_get_selected_preset(void);
103 *
104 * Return the index of the currently selected value in the type
105 * dropdown.
106 */
107 js_get_selected_preset: function() {
108 for (var i in gametypeoptions) {
109 if (gametypeoptions[i].selected) {
110 return gametypeoptions[i].value;
111 }
112 }
113 return 0;
114 },
115
116 /*
117 * void js_select_preset(int n);
118 *
119 * Cause a different value to be selected in the type dropdown
120 * (for when the user selects values from the Custom configurer
121 * which turn out to exactly match a preset).
122 */
123 js_select_preset: function(n) {
124 if (gametypethiscustom !== null) {
125 // Fiddle with the Custom/Customise options. If we're
126 // about to select the Custom option, then it should be in
127 // the menu, and the other one should read "Re-customise";
128 // if we're about to select another one, then the static
129 // Custom option should disappear and the other one should
130 // read "Customise".
131
132 if (gametypethiscustom.parentNode == gametypeselector)
133 gametypeselector.removeChild(gametypethiscustom);
134 if (gametypenewcustom.parentNode == gametypeselector)
135 gametypeselector.removeChild(gametypenewcustom);
136
137 if (n < 0) {
138 gametypeselector.appendChild(gametypethiscustom);
139 gametypenewcustom.lastChild.data = "Re-customise...";
140 } else {
141 gametypenewcustom.lastChild.data = "Customise...";
142 }
143 gametypeselector.appendChild(gametypenewcustom);
144 gametypenewcustom.selected = false;
145 }
146
147 if (n < 0) {
148 gametypethiscustom.selected = true;
149 } else {
150 gametypeoptions[n].selected = true;
151 }
152 },
153
154 /*
155 * void js_get_date_64(unsigned *p);
156 *
157 * Return the current date, in milliseconds since the epoch
158 * (Javascript's native format), as a 64-bit integer. Used to
159 * invent an initial random seed for puzzle generation.
160 */
161 js_get_date_64: function(ptr) {
162 var d = (new Date()).valueOf();
163 setValue(ptr, d, 'i64');
164 },
165
166 /*
167 * void js_update_permalinks(const char *desc, const char *seed);
168 *
169 * Update the permalinks on the web page for a new game
170 * description and optional random seed. desc can never be NULL,
171 * but seed might be (if the game was generated by entering a
172 * descriptive id by hand), in which case we suppress display of
173 * the random seed permalink.
174 */
175 js_update_permalinks: function(desc, seed) {
176 desc = Pointer_stringify(desc);
177 permalink_desc.href = "#" + desc;
178
179 if (seed == 0) {
180 permalink_seed.style.display = "none";
181 } else {
182 seed = Pointer_stringify(seed);
183 permalink_seed.href = "#" + seed;
184 permalink_seed.style.display = "inline";
185 }
186 },
187
188 /*
189 * void js_enable_undo_redo(int undo, int redo);
190 *
191 * Set the enabled/disabled states of the undo and redo buttons,
192 * after a move.
193 */
194 js_enable_undo_redo: function(undo, redo) {
195 undo_button.disabled = (undo == 0);
196 redo_button.disabled = (redo == 0);
197 },
198
199 /*
200 * void js_activate_timer();
201 *
202 * Start calling the C timer_callback() function every 20ms.
203 */
204 js_activate_timer: function() {
205 if (timer === null) {
206 timer_reference_date = (new Date()).valueOf();
207 timer = setInterval(function() {
208 var now = (new Date()).valueOf();
209 timer_callback((now - timer_reference_date) / 1000.0);
210 timer_reference_date = now;
211 return true;
212 }, 20);
213 }
214 },
215
216 /*
217 * void js_deactivate_timer();
218 *
219 * Stop calling the C timer_callback() function every 20ms.
220 */
221 js_deactivate_timer: function() {
222 if (timer !== null) {
223 clearInterval(timer);
224 timer = null;
225 }
226 },
227
228 /*
229 * void js_canvas_start_draw(void);
230 *
231 * Prepare to do some drawing on the canvas.
232 */
233 js_canvas_start_draw: function() {
234 ctx = offscreen_canvas.getContext('2d');
235 update_xmin = update_xmax = update_ymin = update_ymax = undefined;
236 },
237
238 /*
239 * void js_canvas_draw_update(int x, int y, int w, int h);
240 *
241 * Mark a rectangle of the off-screen canvas as needing to be
242 * copied to the on-screen one.
243 */
244 js_canvas_draw_update: function(x, y, w, h) {
245 /*
246 * Currently we do this in a really simple way, just by taking
247 * the smallest rectangle containing all updates so far. We
248 * could instead keep the data in a richer form (e.g. retain
249 * multiple smaller rectangles needing update, and only redraw
250 * the whole thing beyond a certain threshold) but this will
251 * do for now.
252 */
253 if (update_xmin === undefined || update_xmin > x) update_xmin = x;
254 if (update_ymin === undefined || update_ymin > y) update_ymin = y;
255 if (update_xmax === undefined || update_xmax < x+w) update_xmax = x+w;
256 if (update_ymax === undefined || update_ymax < y+h) update_ymax = y+h;
257 },
258
259 /*
260 * void js_canvas_end_draw(void);
261 *
262 * Finish the drawing, by actually copying the newly drawn stuff
263 * to the on-screen canvas.
264 */
265 js_canvas_end_draw: function() {
266 if (update_xmin !== undefined) {
267 var onscreen_ctx = onscreen_canvas.getContext('2d');
268 onscreen_ctx.drawImage(offscreen_canvas,
269 update_xmin, update_ymin,
270 update_xmax - update_xmin,
271 update_ymax - update_ymin,
272 update_xmin, update_ymin,
273 update_xmax - update_xmin,
274 update_ymax - update_ymin);
275 }
276 ctx = null;
277 },
278
279 /*
280 * void js_canvas_draw_rect(int x, int y, int w, int h,
281 * const char *colour);
282 *
283 * Draw a rectangle.
284 */
285 js_canvas_draw_rect: function(x, y, w, h, colptr) {
286 ctx.fillStyle = Pointer_stringify(colptr);
287 ctx.fillRect(x, y, w, h);
288 },
289
290 /*
291 * void js_canvas_clip_rect(int x, int y, int w, int h);
292 *
293 * Set a clipping rectangle.
294 */
295 js_canvas_clip_rect: function(x, y, w, h) {
296 ctx.save();
297 ctx.beginPath();
298 ctx.rect(x, y, w, h);
299 ctx.clip();
300 },
301
302 /*
303 * void js_canvas_unclip(void);
304 *
305 * Reset to no clipping.
306 */
307 js_canvas_unclip: function() {
308 ctx.restore();
309 },
310
311 /*
312 * void js_canvas_draw_line(float x1, float y1, float x2, float y2,
313 * int width, const char *colour);
314 *
315 * Draw a line. We must adjust the coordinates by 0.5 because
316 * Javascript's canvas coordinates appear to be pixel corners,
317 * whereas we want pixel centres. Also, we manually draw the pixel
318 * at each end of the line, which our clients will expect but
319 * Javascript won't reliably do by default (in common with other
320 * Postscriptish drawing frameworks).
321 */
322 js_canvas_draw_line: function(x1, y1, x2, y2, width, colour) {
323 colour = Pointer_stringify(colour);
324
325 ctx.beginPath();
326 ctx.moveTo(x1 + 0.5, y1 + 0.5);
327 ctx.lineTo(x2 + 0.5, y2 + 0.5);
328 ctx.lineWidth = width;
329 ctx.lineCap = 'round';
330 ctx.lineJoin = 'round';
331 ctx.strokeStyle = colour;
332 ctx.stroke();
333 ctx.fillStyle = colour;
334 ctx.fillRect(x1, y1, 1, 1);
335 ctx.fillRect(x2, y2, 1, 1);
336 },
337
338 /*
339 * void js_canvas_draw_poly(int *points, int npoints,
340 * const char *fillcolour,
341 * const char *outlinecolour);
342 *
343 * Draw a polygon.
344 */
345 js_canvas_draw_poly: function(pointptr, npoints, fill, outline) {
346 ctx.beginPath();
347 ctx.moveTo(getValue(pointptr , 'i32') + 0.5,
348 getValue(pointptr+4, 'i32') + 0.5);
349 for (var i = 1; i < npoints; i++)
350 ctx.lineTo(getValue(pointptr+8*i , 'i32') + 0.5,
351 getValue(pointptr+8*i+4, 'i32') + 0.5);
352 ctx.closePath();
353 if (fill != 0) {
354 ctx.fillStyle = Pointer_stringify(fill);
355 ctx.fill();
356 }
357 ctx.lineWidth = '1';
358 ctx.lineCap = 'round';
359 ctx.lineJoin = 'round';
360 ctx.strokeStyle = Pointer_stringify(outline);
361 ctx.stroke();
362 },
363
364 /*
365 * void js_canvas_draw_circle(int x, int y, int r,
366 * const char *fillcolour,
367 * const char *outlinecolour);
368 *
369 * Draw a circle.
370 */
371 js_canvas_draw_circle: function(x, y, r, fill, outline) {
372 ctx.beginPath();
373 ctx.arc(x + 0.5, y + 0.5, r, 0, 2*Math.PI);
374 if (fill != 0) {
375 ctx.fillStyle = Pointer_stringify(fill);
376 ctx.fill();
377 }
378 ctx.lineWidth = '1';
379 ctx.lineCap = 'round';
380 ctx.lineJoin = 'round';
381 ctx.strokeStyle = Pointer_stringify(outline);
382 ctx.stroke();
383 },
384
385 /*
386 * int js_canvas_find_font_midpoint(int height, const char *fontptr);
387 *
388 * Return the adjustment required for text displayed using
389 * ALIGN_VCENTRE. We want to place the midpoint between the
390 * baseline and the cap-height at the specified position; so this
391 * function returns the adjustment which, when added to the
392 * desired centre point, returns the y-coordinate at which you
393 * should put the baseline.
394 *
395 * There is no sensible method of querying this kind of font
396 * metric in Javascript, so instead we render a piece of test text
397 * to a throwaway offscreen canvas and then read the pixel data
398 * back out to find the highest and lowest pixels. That's good
399 * _enough_ (in that we only needed the answer to the nearest
400 * pixel anyway), but rather disgusting!
401 *
402 * Since this is a very expensive operation, we cache the results
403 * per (font,height) pair.
404 */
405 js_canvas_find_font_midpoint: function(height, font) {
406 font = Pointer_stringify(font);
407
408 // Reuse cached value if possible
409 if (midpoint_cache[font] !== undefined)
410 return midpoint_cache[font];
411
412 // Find the width of the string
413 var ctx1 = onscreen_canvas.getContext('2d');
414 ctx1.font = font;
415 var width = (ctx1.measureText(midpoint_test_str).width + 1) | 0;
416
417 // Construct a test canvas of appropriate size, initialise it to
418 // black, and draw the string on it in white
419 var measure_canvas = document.createElement('canvas');
420 var ctx2 = measure_canvas.getContext('2d');
421 ctx2.canvas.width = width;
422 ctx2.canvas.height = 2*height;
423 ctx2.fillStyle = "#000000";
424 ctx2.fillRect(0, 0, width, 2*height);
425 var baseline = (1.5*height) | 0;
426 ctx2.fillStyle = "#ffffff";
427 ctx2.font = font;
428 ctx2.fillText(midpoint_test_str, 0, baseline);
429
430 // Scan the contents of the test canvas to find the top and bottom
431 // set pixels.
432 var pixels = ctx2.getImageData(0, 0, width, 2*height).data;
433 var ymin = 2*height, ymax = -1;
434 for (var y = 0; y < 2*height; y++) {
435 for (var x = 0; x < width; x++) {
436 if (pixels[4*(y*width+x)] != 0) {
437 if (ymin > y) ymin = y;
438 if (ymax < y) ymax = y;
439 break;
440 }
441 }
442 }
443
444 var ret = (baseline - (ymin + ymax) / 2) | 0;
445 midpoint_cache[font] = ret;
446 return ret;
447 },
448
449 /*
450 * void js_canvas_draw_text(int x, int y, int halign,
451 * const char *colptr, const char *fontptr,
452 * const char *text);
453 *
454 * Draw text. Vertical alignment has been taken care of on the C
455 * side, by optionally calling the above function. Horizontal
456 * alignment is handled here, since we can get the canvas draw
457 * function to do it for us with almost no extra effort.
458 */
459 js_canvas_draw_text: function(x, y, halign, colptr, fontptr, text) {
460 ctx.font = Pointer_stringify(fontptr);
461 ctx.fillStyle = Pointer_stringify(colptr);
462 ctx.textAlign = (halign == 0 ? 'left' :
463 halign == 1 ? 'center' : 'right');
464 ctx.textBaseline = 'alphabetic';
465 ctx.fillText(Pointer_stringify(text), x, y);
466 },
467
468 /*
469 * int js_canvas_new_blitter(int w, int h);
470 *
471 * Create a new blitter object, which is just an offscreen canvas
472 * of the specified size.
473 */
474 js_canvas_new_blitter: function(w, h) {
475 var id = blittercount++;
476 blitters[id] = document.createElement("canvas");
477 blitters[id].width = w;
478 blitters[id].height = h;
479 return id;
480 },
481
482 /*
483 * void js_canvas_free_blitter(int id);
484 *
485 * Free a blitter (or rather, destroy our reference to it so JS
486 * can garbage-collect it, and also enforce that we don't
487 * accidentally use it again afterwards).
488 */
489 js_canvas_free_blitter: function(id) {
490 blitters[id] = null;
491 },
492
493 /*
494 * void js_canvas_copy_to_blitter(int id, int x, int y, int w, int h);
495 *
496 * Copy from the puzzle image to a blitter. The size is passed to
497 * us, partly so we don't have to remember the size of each
498 * blitter, but mostly so that the C side can adjust the copy
499 * rectangle in the case where it partially overlaps the edge of
500 * the screen.
501 */
502 js_canvas_copy_to_blitter: function(id, x, y, w, h) {
503 var blitter_ctx = blitters[id].getContext('2d');
504 blitter_ctx.drawImage(offscreen_canvas,
505 x, y, w, h,
506 0, 0, w, h);
507 },
508
509 /*
510 * void js_canvas_copy_from_blitter(int id, int x, int y, int w, int h);
511 *
512 * Copy from a blitter back to the puzzle image. As above, the
513 * size of the copied rectangle is passed to us from the C side
514 * and may already have been modified.
515 */
516 js_canvas_copy_from_blitter: function(id, x, y, w, h) {
517 ctx.drawImage(blitters[id],
518 0, 0, w, h,
519 x, y, w, h);
520 },
521
522 /*
523 * void js_canvas_make_statusbar(void);
524 *
525 * Cause a status bar to exist. Called at setup time if the puzzle
526 * back end turns out to want one.
527 */
528 js_canvas_make_statusbar: function() {
529 var statusholder = document.getElementById("statusbarholder");
530 statusbar = document.createElement("div");
531 statusbar.style.overflow = "hidden";
532 statusbar.style.width = (onscreen_canvas.width - 4) + "px";
533 statusholder.style.width = onscreen_canvas.width + "px";
534 statusbar.style.height = "1.2em";
535 statusbar.style.textAlign = "left";
536 statusbar.style.background = "#d8d8d8";
537 statusbar.style.borderLeft = '2px solid #c8c8c8';
538 statusbar.style.borderTop = '2px solid #c8c8c8';
539 statusbar.style.borderRight = '2px solid #e8e8e8';
540 statusbar.style.borderBottom = '2px solid #e8e8e8';
541 statusbar.appendChild(document.createTextNode(" "));
542 statusholder.appendChild(statusbar);
543 },
544
545 /*
546 * void js_canvas_set_statusbar(const char *text);
547 *
548 * Set the text in the status bar.
549 */
550 js_canvas_set_statusbar: function(ptr) {
551 var text = Pointer_stringify(ptr);
552 statusbar.replaceChild(document.createTextNode(text),
553 statusbar.lastChild);
554 },
555
556 /*
557 * void js_canvas_set_size(int w, int h);
558 *
559 * Set the size of the puzzle canvas. Called at setup, and every
560 * time the user picks new puzzle settings requiring a different
561 * size.
562 */
563 js_canvas_set_size: function(w, h) {
564 onscreen_canvas.width = w;
565 offscreen_canvas.width = w;
566 if (statusbar !== null) {
567 statusbar.style.width = (w - 4) + "px";
568 document.getElementById("statusbarholder").style.width = w + "px";
569 }
570 resizable_div.style.width = w + "px";
571
572 onscreen_canvas.height = h;
573 offscreen_canvas.height = h;
574 },
575
576 /*
577 * void js_dialog_init(const char *title);
578 *
579 * Begin constructing a 'dialog box' which will be popped up in an
580 * overlay on top of the rest of the puzzle web page.
581 */
582 js_dialog_init: function(titletext) {
583 // Create an overlay on the page which darkens everything
584 // beneath it.
585 dlg_dimmer = document.createElement("div");
586 dlg_dimmer.style.width = "100%";
587 dlg_dimmer.style.height = "100%";
588 dlg_dimmer.style.background = '#000000';
589 dlg_dimmer.style.position = 'fixed';
590 dlg_dimmer.style.opacity = 0.3;
591 dlg_dimmer.style.top = dlg_dimmer.style.left = 0;
592 dlg_dimmer.style["z-index"] = 99;
593
594 // Now create a form which sits on top of that in turn.
595 dlg_form = document.createElement("form");
596 dlg_form.style.width = (window.innerWidth * 2 / 3) + "px";
597 dlg_form.style.opacity = 1;
598 dlg_form.style.background = '#ffffff';
599 dlg_form.style.color = '#000000';
600 dlg_form.style.position = 'absolute';
601 dlg_form.style.border = "2px solid black";
602 dlg_form.style.padding = "20px";
603 dlg_form.style.top = (window.innerHeight / 10) + "px";
604 dlg_form.style.left = (window.innerWidth / 6) + "px";
605 dlg_form.style["z-index"] = 100;
606
607 var title = document.createElement("p");
608 title.style.marginTop = "0px";
609 title.appendChild(document.createTextNode
610 (Pointer_stringify(titletext)));
611 dlg_form.appendChild(title);
612
613 dlg_return_funcs = [];
614 dlg_next_id = 0;
615 },
616
617 /*
618 * void js_dialog_string(int i, const char *title, const char *initvalue);
619 *
620 * Add a string control (that is, an edit box) to the dialog under
621 * construction.
622 */
623 js_dialog_string: function(index, title, initialtext) {
624 dlg_form.appendChild(document.createTextNode(Pointer_stringify(title)));
625 var editbox = document.createElement("input");
626 editbox.type = "text";
627 editbox.value = Pointer_stringify(initialtext);
628 dlg_form.appendChild(editbox);
629 dlg_form.appendChild(document.createElement("br"));
630
631 dlg_return_funcs.push(function() {
632 dlg_return_sval(index, editbox.value);
633 });
634 },
635
636 /*
637 * void js_dialog_choices(int i, const char *title, const char *choicelist,
638 * int initvalue);
639 *
640 * Add a choices control (i.e. a drop-down list) to the dialog
641 * under construction. The 'choicelist' parameter is unchanged
642 * from the way the puzzle back end will have supplied it: i.e.
643 * it's still encoded as a single string whose first character
644 * gives the separator.
645 */
646 js_dialog_choices: function(index, title, choicelist, initvalue) {
647 dlg_form.appendChild(document.createTextNode(Pointer_stringify(title)));
648 var dropdown = document.createElement("select");
649 var choicestr = Pointer_stringify(choicelist);
650 var items = choicestr.slice(1).split(choicestr[0]);
651 var options = [];
652 for (var i in items) {
653 var option = document.createElement("option");
654 option.value = i;
655 option.appendChild(document.createTextNode(items[i]));
656 if (i == initvalue) option.selected = true;
657 dropdown.appendChild(option);
658 options.push(option);
659 }
660 dlg_form.appendChild(dropdown);
661 dlg_form.appendChild(document.createElement("br"));
662
663 dlg_return_funcs.push(function() {
664 var val = 0;
665 for (var i in options) {
666 if (options[i].selected) {
667 val = options[i].value;
668 break;
669 }
670 }
671 dlg_return_ival(index, val);
672 });
673 },
674
675 /*
676 * void js_dialog_boolean(int i, const char *title, int initvalue);
677 *
678 * Add a boolean control (a checkbox) to the dialog under
679 * construction. Checkboxes are generally expected to be sensitive
680 * on their label text as well as the box itself, so for this
681 * control we create an actual label rather than merely a text
682 * node (and hence we must allocate an id to the checkbox so that
683 * the label can refer to it).
684 */
685 js_dialog_boolean: function(index, title, initvalue) {
686 var checkbox = document.createElement("input");
687 checkbox.type = "checkbox";
688 checkbox.id = "cb" + String(dlg_next_id++);
689 checkbox.checked = (initvalue != 0);
690 dlg_form.appendChild(checkbox);
691 var checkboxlabel = document.createElement("label");
692 checkboxlabel.setAttribute("for", checkbox.id);
693 checkboxlabel.textContent = Pointer_stringify(title);
694 dlg_form.appendChild(checkboxlabel);
695 dlg_form.appendChild(document.createElement("br"));
696
697 dlg_return_funcs.push(function() {
698 dlg_return_ival(index, checkbox.checked ? 1 : 0);
699 });
700 },
701
702 /*
703 * void js_dialog_launch(void);
704 *
705 * Finish constructing a dialog, and actually display it, dimming
706 * everything else on the page.
707 */
708 js_dialog_launch: function() {
709 // Put in the OK and Cancel buttons at the bottom.
710 var button;
711
712 button = document.createElement("input");
713 button.type = "button";
714 button.value = "OK";
715 button.onclick = function(event) {
716 for (var i in dlg_return_funcs)
717 dlg_return_funcs[i]();
718 command(3);
719 }
720 dlg_form.appendChild(button);
721
722 button = document.createElement("input");
723 button.type = "button";
724 button.value = "Cancel";
725 button.onclick = function(event) {
726 command(4);
727 }
728 dlg_form.appendChild(button);
729
730 document.body.appendChild(dlg_dimmer);
731 document.body.appendChild(dlg_form);
732 },
733
734 /*
735 * void js_dialog_cleanup(void);
736 *
737 * Stop displaying a dialog, and clean up the internal state
738 * associated with it.
739 */
740 js_dialog_cleanup: function() {
741 document.body.removeChild(dlg_dimmer);
742 document.body.removeChild(dlg_form);
743 dlg_dimmer = dlg_form = null;
744 onscreen_canvas.focus();
745 },
746
747 /*
748 * void js_focus_canvas(void);
749 *
750 * Return keyboard focus to the puzzle canvas. Called after a
751 * puzzle-control button is pressed, which tends to have the side
752 * effect of taking focus away from the canvas.
753 */
754 js_focus_canvas: function() {
755 onscreen_canvas.focus();
756 }
757});
diff --git a/apps/plugins/puzzles/emccpre.js b/apps/plugins/puzzles/emccpre.js
new file mode 100644
index 0000000000..ebf67d1fc6
--- /dev/null
+++ b/apps/plugins/puzzles/emccpre.js
@@ -0,0 +1,364 @@
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 <select> object implementing the game-type drop-down, and a
83// list of the <option> objects inside it. Used by js_add_preset(),
84// js_get_selected_preset() and js_select_preset().
85//
86// gametypethiscustom is an option which indicates some custom game
87// params you've already set up, and which will be auto-selected on
88// return from the customisation dialog; gametypenewcustom is an
89// option which you select to indicate that you want to bring up the
90// customisation dialog and select a new configuration. Ideally I'd do
91// this with just one option serving both purposes, but instead we
92// have to do this a bit oddly because browsers don't send 'onchange'
93// events for a select element if you reselect the same one - so if
94// you've picked a custom setup and now want to change it, you need a
95// way to specify that.
96var gametypeselector = null, gametypeoptions = [];
97var gametypethiscustom = null, gametypehiddencustom = null;
98
99// The two anchors used to give permalinks to the current puzzle. Used
100// by js_update_permalinks().
101var permalink_seed, permalink_desc;
102
103// The undo and redo buttons. Used by js_enable_undo_redo().
104var undo_button, redo_button;
105
106// A div element enclosing both the puzzle and its status bar, used
107// for positioning the resize handle.
108var resizable_div;
109
110// Helper function to find the absolute position of a given DOM
111// element on a page, by iterating upwards through the DOM finding
112// each element's offset from its parent, and thus calculating the
113// page-relative position of the target element.
114function element_coords(element) {
115 var ex = 0, ey = 0;
116 while (element.offsetParent) {
117 ex += element.offsetLeft;
118 ey += element.offsetTop;
119 element = element.offsetParent;
120 }
121 return {x: ex, y:ey};
122}
123
124// Helper function which is passed a mouse event object and a DOM
125// element, and returns the coordinates of the mouse event relative to
126// the top left corner of the element by subtracting element_coords
127// from event.page{X,Y}.
128function relative_mouse_coords(event, element) {
129 var ecoords = element_coords(element);
130 return {x: event.pageX - ecoords.x,
131 y: event.pageY - ecoords.y};
132}
133
134// Init function called from body.onload.
135function initPuzzle() {
136 // Construct the off-screen canvas used for double buffering.
137 onscreen_canvas = document.getElementById("puzzlecanvas");
138 offscreen_canvas = document.createElement("canvas");
139 offscreen_canvas.width = onscreen_canvas.width;
140 offscreen_canvas.height = onscreen_canvas.height;
141
142 // Stop right-clicks on the puzzle from popping up a context menu.
143 // We need those right-clicks!
144 onscreen_canvas.oncontextmenu = function(event) { return false; }
145
146 // Set up mouse handlers. We do a bit of tracking of the currently
147 // pressed mouse buttons, to avoid sending mousemoves with no
148 // button down (our puzzles don't want those events).
149 mousedown = Module.cwrap('mousedown', 'void',
150 ['number', 'number', 'number']);
151 buttons_down = 0;
152 onscreen_canvas.onmousedown = function(event) {
153 var xy = relative_mouse_coords(event, onscreen_canvas);
154 mousedown(xy.x, xy.y, event.button);
155 buttons_down |= 1 << event.button;
156 onscreen_canvas.setCapture(true);
157 };
158 mousemove = Module.cwrap('mousemove', 'void',
159 ['number', 'number', 'number']);
160 onscreen_canvas.onmousemove = function(event) {
161 if (buttons_down) {
162 var xy = relative_mouse_coords(event, onscreen_canvas);
163 mousemove(xy.x, xy.y, buttons_down);
164 }
165 };
166 mouseup = Module.cwrap('mouseup', 'void',
167 ['number', 'number', 'number']);
168 onscreen_canvas.onmouseup = function(event) {
169 if (buttons_down & (1 << event.button)) {
170 buttons_down ^= 1 << event.button;
171 var xy = relative_mouse_coords(event, onscreen_canvas);
172 mouseup(xy.x, xy.y, event.button);
173 }
174 };
175
176 // Set up keyboard handlers. We do all the actual keyboard
177 // handling in onkeydown; but we also call event.preventDefault()
178 // in both the keydown and keypress handlers. This means that
179 // while the canvas itself has focus, _all_ keypresses go only to
180 // the puzzle - so users of this puzzle collection in other media
181 // can indulge their instinct to press ^R for redo, for example,
182 // without accidentally reloading the page.
183 key = Module.cwrap('key', 'void', ['number', 'number', 'string',
184 'string', 'number', 'number']);
185 onscreen_canvas.onkeydown = function(event) {
186 key(event.keyCode, event.charCode, event.key, event.char,
187 event.shiftKey ? 1 : 0, event.ctrlKey ? 1 : 0);
188 event.preventDefault();
189 };
190 onscreen_canvas.onkeypress = function(event) {
191 event.preventDefault();
192 };
193
194 // command() is a C function called to pass back events which
195 // don't fall into other categories like mouse and key events.
196 // Mostly those are button presses, but there's also one for the
197 // game-type dropdown having been changed.
198 command = Module.cwrap('command', 'void', ['number']);
199
200 // Event handlers for buttons and things, which call command().
201 document.getElementById("specific").onclick = function(event) {
202 // Ensure we don't accidentally process these events when a
203 // dialog is actually active, e.g. because the button still
204 // has keyboard focus
205 if (dlg_dimmer === null)
206 command(0);
207 };
208 document.getElementById("random").onclick = function(event) {
209 if (dlg_dimmer === null)
210 command(1);
211 };
212 document.getElementById("new").onclick = function(event) {
213 if (dlg_dimmer === null)
214 command(5);
215 };
216 document.getElementById("restart").onclick = function(event) {
217 if (dlg_dimmer === null)
218 command(6);
219 };
220 undo_button = document.getElementById("undo");
221 undo_button.onclick = function(event) {
222 if (dlg_dimmer === null)
223 command(7);
224 };
225 redo_button = document.getElementById("redo");
226 redo_button.onclick = function(event) {
227 if (dlg_dimmer === null)
228 command(8);
229 };
230 document.getElementById("solve").onclick = function(event) {
231 if (dlg_dimmer === null)
232 command(9);
233 };
234
235 gametypeselector = document.getElementById("gametype");
236 gametypeselector.onchange = function(event) {
237 if (dlg_dimmer === null)
238 command(2);
239 };
240
241 // In IE, the canvas doesn't automatically gain focus on a mouse
242 // click, so make sure it does
243 onscreen_canvas.addEventListener("mousedown", function(event) {
244 onscreen_canvas.focus();
245 });
246
247 // In our dialog boxes, Return and Escape should be like pressing
248 // OK and Cancel respectively
249 document.addEventListener("keydown", function(event) {
250
251 if (dlg_dimmer !== null && event.keyCode == 13) {
252 for (var i in dlg_return_funcs)
253 dlg_return_funcs[i]();
254 command(3);
255 }
256
257 if (dlg_dimmer !== null && event.keyCode == 27)
258 command(4);
259 });
260
261 // Set up the function pointers we haven't already grabbed.
262 dlg_return_sval = Module.cwrap('dlg_return_sval', 'void',
263 ['number','string']);
264 dlg_return_ival = Module.cwrap('dlg_return_ival', 'void',
265 ['number','number']);
266 timer_callback = Module.cwrap('timer_callback', 'void', ['number']);
267
268 // Save references to the two permalinks.
269 permalink_desc = document.getElementById("permalink-desc");
270 permalink_seed = document.getElementById("permalink-seed");
271
272 // Default to giving keyboard focus to the puzzle.
273 onscreen_canvas.focus();
274
275 // Create the resize handle.
276 var resize_handle = document.createElement("canvas");
277 resize_handle.width = 10;
278 resize_handle.height = 10;
279 {
280 var ctx = resize_handle.getContext("2d");
281 ctx.beginPath();
282 for (var i = 1; i <= 7; i += 3) {
283 ctx.moveTo(8.5, i + 0.5);
284 ctx.lineTo(i + 0.5, 8.5);
285 }
286 ctx.lineWidth = '1px';
287 ctx.lineCap = 'round';
288 ctx.lineJoin = 'round';
289 ctx.strokeStyle = '#000000';
290 ctx.stroke();
291 }
292 resizable_div = document.getElementById("resizable");
293 resizable_div.appendChild(resize_handle);
294 resize_handle.style.position = 'absolute';
295 resize_handle.style.zIndex = 98;
296 resize_handle.style.bottom = "0";
297 resize_handle.style.right = "0";
298 resize_handle.style.cursor = "se-resize";
299 resize_handle.title = "Drag to resize the puzzle. Right-click to restore the default size.";
300 var resize_xbase = null, resize_ybase = null, restore_pending = false;
301 var resize_xoffset = null, resize_yoffset = null;
302 var resize_puzzle = Module.cwrap('resize_puzzle',
303 'void', ['number', 'number']);
304 var restore_puzzle_size = Module.cwrap('restore_puzzle_size', 'void', []);
305 resize_handle.oncontextmenu = function(event) { return false; }
306 resize_handle.onmousedown = function(event) {
307 if (event.button == 0) {
308 var xy = element_coords(onscreen_canvas);
309 resize_xbase = xy.x + onscreen_canvas.width / 2;
310 resize_ybase = xy.y;
311 resize_xoffset = xy.x + onscreen_canvas.width - event.pageX;
312 resize_yoffset = xy.y + onscreen_canvas.height - event.pageY;
313 } else {
314 restore_pending = true;
315 }
316 resize_handle.setCapture(true);
317 event.preventDefault();
318 };
319 window.addEventListener("mousemove", function(event) {
320 if (resize_xbase !== null && resize_ybase !== null) {
321 resize_puzzle((event.pageX + resize_xoffset - resize_xbase) * 2,
322 (event.pageY + resize_yoffset - resize_ybase));
323 event.preventDefault();
324 // Chrome insists on selecting text during a resize drag
325 // no matter what I do
326 if (window.getSelection)
327 window.getSelection().removeAllRanges();
328 else
329 document.selection.empty(); }
330 });
331 window.addEventListener("mouseup", function(event) {
332 if (resize_xbase !== null && resize_ybase !== null) {
333 resize_xbase = null;
334 resize_ybase = null;
335 onscreen_canvas.focus(); // return focus to the puzzle
336 event.preventDefault();
337 } else if (restore_pending) {
338 // If you have the puzzle at larger than normal size and
339 // then right-click to restore, I haven't found any way to
340 // stop Chrome and IE popping up a context menu on the
341 // revealed piece of document when you release the button
342 // except by putting the actual restore into a setTimeout.
343 // Gah.
344 setTimeout(function() {
345 restore_pending = false;
346 restore_puzzle_size();
347 onscreen_canvas.focus();
348 }, 20);
349 event.preventDefault();
350 }
351 });
352
353 // Run the C setup function, passing argv[1] as the fragment
354 // identifier (so that permalinks of the form puzzle.html#game-id
355 // can launch the specified id).
356 Module.callMain([location.hash]);
357
358 // And if we get here with everything having gone smoothly, i.e.
359 // we haven't crashed for one reason or another during setup, then
360 // it's probably safe to hide the 'sorry, no puzzle here' div and
361 // show the div containing the actual puzzle.
362 document.getElementById("apology").style.display = "none";
363 document.getElementById("puzzle").style.display = "inline";
364}
diff --git a/apps/plugins/puzzles/emccx.json b/apps/plugins/puzzles/emccx.json
new file mode 100644
index 0000000000..e03f7e25c7
--- /dev/null
+++ b/apps/plugins/puzzles/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/fifteen.R b/apps/plugins/puzzles/fifteen.R
new file mode 100644
index 0000000000..b2292acc69
--- /dev/null
+++ b/apps/plugins/puzzles/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/fifteen.c b/apps/plugins/puzzles/fifteen.c
new file mode 100644
index 0000000000..0d5e7f5b0c
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/filling.R b/apps/plugins/puzzles/filling.R
new file mode 100644
index 0000000000..cffbafaa0a
--- /dev/null
+++ b/apps/plugins/puzzles/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/filling.c b/apps/plugins/puzzles/filling.c
new file mode 100644
index 0000000000..c2dfafc7ca
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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, 10);
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, 10);
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,
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/findloop.c b/apps/plugins/puzzles/findloop.c
new file mode 100644
index 0000000000..e6b2654cad
--- /dev/null
+++ b/apps/plugins/puzzles/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/flip.R b/apps/plugins/puzzles/flip.R
new file mode 100644
index 0000000000..03241f015b
--- /dev/null
+++ b/apps/plugins/puzzles/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/flip.c b/apps/plugins/puzzles/flip.c
new file mode 100644
index 0000000000..ffa6c07c94
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/flood.R b/apps/plugins/puzzles/flood.R
new file mode 100644
index 0000000000..359bbb5dce
--- /dev/null
+++ b/apps/plugins/puzzles/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/flood.c b/apps/plugins/puzzles/flood.c
new file mode 100644
index 0000000000..69ae3067a9
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/galaxies.R b/apps/plugins/puzzles/galaxies.R
new file mode 100644
index 0000000000..957e5dad90
--- /dev/null
+++ b/apps/plugins/puzzles/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/galaxies.c b/apps/plugins/puzzles/galaxies.c
new file mode 100644
index 0000000000..a18901d6e5
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/grid.c b/apps/plugins/puzzles/grid.c
new file mode 100644
index 0000000000..112162d4df
--- /dev/null
+++ b/apps/plugins/puzzles/grid.c
@@ -0,0 +1,2896 @@
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 "rbassert.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
2574typedef struct setface_ctx
2575{
2576 int xmin, xmax, ymin, ymax;
2577
2578 grid *g;
2579 tree234 *points;
2580} setface_ctx;
2581
2582static double round_int_nearest_away(double r)
2583{
2584 return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
2585}
2586
2587static int set_faces(penrose_state *state, vector *vs, int n, int depth)
2588{
2589 setface_ctx *sf_ctx = (setface_ctx *)state->ctx;
2590 int i;
2591 int xs[4], ys[4];
2592
2593 if (depth < state->max_depth) return 0;
2594#ifdef DEBUG_PENROSE
2595 if (n != 4) return 0; /* triangles are sent as debugging. */
2596#endif
2597
2598 for (i = 0; i < n; i++) {
2599 double tx = v_x(vs, i), ty = v_y(vs, i);
2600
2601 xs[i] = (int)round_int_nearest_away(tx);
2602 ys[i] = (int)round_int_nearest_away(ty);
2603
2604 if (xs[i] < sf_ctx->xmin || xs[i] > sf_ctx->xmax) return 0;
2605 if (ys[i] < sf_ctx->ymin || ys[i] > sf_ctx->ymax) return 0;
2606 }
2607
2608 grid_face_add_new(sf_ctx->g, n);
2609 debug(("penrose: new face l=%f gen=%d...",
2610 penrose_side_length(state->start_size, depth), depth));
2611 for (i = 0; i < n; i++) {
2612 grid_dot *d = grid_get_dot(sf_ctx->g, sf_ctx->points,
2613 xs[i], ys[i]);
2614 grid_face_set_dot(sf_ctx->g, d, i);
2615 debug((" ... dot 0x%x (%d,%d) (was %2.2f,%2.2f)",
2616 d, d->x, d->y, v_x(vs, i), v_y(vs, i)));
2617 }
2618
2619 return 0;
2620}
2621
2622#define PENROSE_TILESIZE 100
2623
2624static void grid_size_penrose(int width, int height,
2625 int *tilesize, int *xextent, int *yextent)
2626{
2627 int l = PENROSE_TILESIZE;
2628
2629 *tilesize = l;
2630 *xextent = l * width;
2631 *yextent = l * height;
2632}
2633
2634static grid *grid_new_penrose(int width, int height, int which, const char *desc); /* forward reference */
2635
2636static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs)
2637{
2638 int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff;
2639 double outer_radius;
2640 int inner_radius;
2641 char gd[255];
2642 int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
2643 grid *g;
2644
2645 while (1) {
2646 /* We want to produce a random bit of penrose tiling, so we
2647 * calculate a random offset (within the patch that penrose.c
2648 * calculates for us) and an angle (multiple of 36) to rotate
2649 * the patch. */
2650
2651 penrose_calculate_size(which, tilesize, width, height,
2652 &outer_radius, &startsz, &depth);
2653
2654 /* Calculate radius of (circumcircle of) patch, subtract from
2655 * radius calculated. */
2656 inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
2657
2658 /* Pick a random offset (the easy way: choose within outer
2659 * square, discarding while it's outside the circle) */
2660 do {
2661 xoff = random_upto(rs, 2*inner_radius) - inner_radius;
2662 yoff = random_upto(rs, 2*inner_radius) - inner_radius;
2663 } while (sqrt(xoff*xoff+yoff*yoff) > inner_radius);
2664
2665 aoff = random_upto(rs, 360/36) * 36;
2666
2667 debug(("grid_desc: ts %d, %dx%d patch, orad %2.2f irad %d",
2668 tilesize, width, height, outer_radius, inner_radius));
2669 debug((" -> xoff %d yoff %d aoff %d", xoff, yoff, aoff));
2670
2671 sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff);
2672
2673 /*
2674 * Now test-generate our grid, to make sure it actually
2675 * produces something.
2676 */
2677 g = grid_new_penrose(width, height, which, gd);
2678 if (g) {
2679 grid_free(g);
2680 break;
2681 }
2682 /* If not, go back to the top of this while loop and try again
2683 * with a different random offset. */
2684 }
2685
2686 return dupstr(gd);
2687}
2688
2689static char *grid_validate_desc_penrose(grid_type type, int width, int height,
2690 const char *desc)
2691{
2692 int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius;
2693 double outer_radius;
2694 int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3);
2695 grid *g;
2696
2697 if (!desc)
2698 return "Missing grid description string.";
2699
2700 penrose_calculate_size(which, tilesize, width, height,
2701 &outer_radius, &startsz, &depth);
2702 inner_radius = (int)(outer_radius - sqrt(width*width + height*height));
2703
2704 if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3)
2705 return "Invalid format grid description string.";
2706
2707 if (sqrt(xoff*xoff + yoff*yoff) > inner_radius)
2708 return "Patch offset out of bounds.";
2709 if ((aoff % 36) != 0 || aoff < 0 || aoff >= 360)
2710 return "Angle offset out of bounds.";
2711
2712 /*
2713 * Test-generate to ensure these parameters don't end us up with
2714 * no grid at all.
2715 */
2716 g = grid_new_penrose(width, height, which, desc);
2717 if (!g)
2718 return "Patch coordinates do not identify a usable grid fragment";
2719 grid_free(g);
2720
2721 return NULL;
2722}
2723
2724/*
2725 * We're asked for a grid of a particular size, and we generate enough
2726 * of the tiling so we can be sure to have enough random grid from which
2727 * to pick.
2728 */
2729
2730static grid *grid_new_penrose(int width, int height, int which, const char *desc)
2731{
2732 int max_faces, max_dots, tilesize = PENROSE_TILESIZE;
2733 int xsz, ysz, xoff, yoff, aoff;
2734 double rradius;
2735
2736 tree234 *points;
2737 grid *g;
2738
2739 penrose_state ps;
2740 setface_ctx sf_ctx;
2741
2742 penrose_calculate_size(which, tilesize, width, height,
2743 &rradius, &ps.start_size, &ps.max_depth);
2744
2745 debug(("penrose: w%d h%d, tile size %d, start size %d, depth %d",
2746 width, height, tilesize, ps.start_size, ps.max_depth));
2747
2748 ps.new_tile = set_faces;
2749 ps.ctx = &sf_ctx;
2750
2751 max_faces = (width*3) * (height*3); /* somewhat paranoid... */
2752 max_dots = max_faces * 4; /* ditto... */
2753
2754 g = grid_empty();
2755 g->tilesize = tilesize;
2756 g->faces = snewn(max_faces, grid_face);
2757 g->dots = snewn(max_dots, grid_dot);
2758
2759 points = newtree234(grid_point_cmp_fn);
2760
2761 memset(&sf_ctx, 0, sizeof(sf_ctx));
2762 sf_ctx.g = g;
2763 sf_ctx.points = points;
2764
2765 if (desc != NULL) {
2766 if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3)
2767 assert(!"Invalid grid description.");
2768 } else {
2769 xoff = yoff = aoff = 0;
2770 }
2771
2772 xsz = width * tilesize;
2773 ysz = height * tilesize;
2774
2775 sf_ctx.xmin = xoff - xsz/2;
2776 sf_ctx.xmax = xoff + xsz/2;
2777 sf_ctx.ymin = yoff - ysz/2;
2778 sf_ctx.ymax = yoff + ysz/2;
2779
2780 debug(("penrose: centre (%f, %f) xsz %f ysz %f",
2781 0.0, 0.0, xsz, ysz));
2782 debug(("penrose: x range (%f --> %f), y range (%f --> %f)",
2783 sf_ctx.xmin, sf_ctx.xmax, sf_ctx.ymin, sf_ctx.ymax));
2784
2785 penrose(&ps, which, aoff);
2786
2787 freetree234(points);
2788 assert(g->num_faces <= max_faces);
2789 assert(g->num_dots <= max_dots);
2790
2791 debug(("penrose: %d faces total (equivalent to %d wide by %d high)",
2792 g->num_faces, g->num_faces/height, g->num_faces/width));
2793
2794 /*
2795 * Return NULL if we ended up with an empty grid, either because
2796 * the initial generation was over too small a rectangle to
2797 * encompass any face or because grid_trim_vigorously ended up
2798 * removing absolutely everything.
2799 */
2800 if (g->num_faces == 0 || g->num_dots == 0) {
2801 grid_free(g);
2802 return NULL;
2803 }
2804 grid_trim_vigorously(g);
2805 if (g->num_faces == 0 || g->num_dots == 0) {
2806 grid_free(g);
2807 return NULL;
2808 }
2809
2810 grid_make_consistent(g);
2811
2812 /*
2813 * Centre the grid in its originally promised rectangle.
2814 */
2815 g->lowest_x -= ((sf_ctx.xmax - sf_ctx.xmin) -
2816 (g->highest_x - g->lowest_x)) / 2;
2817 g->highest_x = g->lowest_x + (sf_ctx.xmax - sf_ctx.xmin);
2818 g->lowest_y -= ((sf_ctx.ymax - sf_ctx.ymin) -
2819 (g->highest_y - g->lowest_y)) / 2;
2820 g->highest_y = g->lowest_y + (sf_ctx.ymax - sf_ctx.ymin);
2821
2822 return g;
2823}
2824
2825static void grid_size_penrose_p2_kite(int width, int height,
2826 int *tilesize, int *xextent, int *yextent)
2827{
2828 grid_size_penrose(width, height, tilesize, xextent, yextent);
2829}
2830
2831static void grid_size_penrose_p3_thick(int width, int height,
2832 int *tilesize, int *xextent, int *yextent)
2833{
2834 grid_size_penrose(width, height, tilesize, xextent, yextent);
2835}
2836
2837static grid *grid_new_penrose_p2_kite(int width, int height, const char *desc)
2838{
2839 return grid_new_penrose(width, height, PENROSE_P2, desc);
2840}
2841
2842static grid *grid_new_penrose_p3_thick(int width, int height, const char *desc)
2843{
2844 return grid_new_penrose(width, height, PENROSE_P3, desc);
2845}
2846
2847/* ----------- End of grid generators ------------- */
2848
2849#define FNNEW(upper,lower) &grid_new_ ## lower,
2850#define FNSZ(upper,lower) &grid_size_ ## lower,
2851
2852static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) };
2853static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) };
2854
2855char *grid_new_desc(grid_type type, int width, int height, random_state *rs)
2856{
2857 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
2858 return grid_new_desc_penrose(type, width, height, rs);
2859 } else if (type == GRID_TRIANGULAR) {
2860 return dupstr("0"); /* up-to-date version of triangular grid */
2861 } else {
2862 return NULL;
2863 }
2864}
2865
2866char *grid_validate_desc(grid_type type, int width, int height,
2867 const char *desc)
2868{
2869 if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) {
2870 return grid_validate_desc_penrose(type, width, height, desc);
2871 } else if (type == GRID_TRIANGULAR) {
2872 return grid_validate_desc_triangular(type, width, height, desc);
2873 } else {
2874 if (desc != NULL)
2875 return "Grid description strings not used with this grid type";
2876 return NULL;
2877 }
2878}
2879
2880grid *grid_new(grid_type type, int width, int height, const char *desc)
2881{
2882 char *err = grid_validate_desc(type, width, height, desc);
2883 if (err) assert(!"Invalid grid description.");
2884
2885 return grid_news[type](width, height, desc);
2886}
2887
2888void grid_compute_size(grid_type type, int width, int height,
2889 int *tilesize, int *xextent, int *yextent)
2890{
2891 grid_sizes[type](width, height, tilesize, xextent, yextent);
2892}
2893
2894/* ----------- End of grid helpers ------------- */
2895
2896/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/grid.h b/apps/plugins/puzzles/grid.h
new file mode 100644
index 0000000000..17d0aa1961
--- /dev/null
+++ b/apps/plugins/puzzles/grid.h
@@ -0,0 +1,132 @@
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(PENROSE_P2,penrose_p2_kite) \
109 A(PENROSE_P3,penrose_p3_thick)
110
111#define ENUM(upper,lower) GRID_ ## upper,
112typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type;
113#undef ENUM
114
115/* Free directly after use if non-NULL. Will never contain an underscore
116 * (so clients can safely use that as a separator). */
117char *grid_new_desc(grid_type type, int width, int height, random_state *rs);
118char *grid_validate_desc(grid_type type, int width, int height,
119 const char *desc);
120
121grid *grid_new(grid_type type, int width, int height, const char *desc);
122
123void grid_free(grid *g);
124
125grid_edge *grid_nearest_edge(grid *g, int x, int y);
126
127void grid_compute_size(grid_type type, int width, int height,
128 int *tilesize, int *xextent, int *yextent);
129
130void grid_find_incentre(grid_face *f);
131
132#endif /* PUZZLES_GRID_H */
diff --git a/apps/plugins/puzzles/gtk.c b/apps/plugins/puzzles/gtk.c
new file mode 100644
index 0000000000..aa3ba06eed
--- /dev/null
+++ b/apps/plugins/puzzles/gtk.c
@@ -0,0 +1,3215 @@
1/*
2 * gtk.c: GTK front end for my puzzle collection.
3 */
4
5#include <stdio.h>
6#include "rbassert.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 guint statusctx;
148 int w, h;
149 midend *me;
150#ifdef USE_CAIRO
151 const float *colours;
152 cairo_t *cr;
153 cairo_surface_t *image;
154#ifndef USE_CAIRO_WITHOUT_PIXMAP
155 GdkPixmap *pixmap;
156#endif
157 GdkColor background; /* for painting outside puzzle area */
158#else
159 GdkPixmap *pixmap;
160 GdkGC *gc;
161 GdkColor *colours;
162 GdkColormap *colmap;
163 int backgroundindex; /* which of colours[] is background */
164#endif
165 int ncolours;
166 int bbox_l, bbox_r, bbox_u, bbox_d;
167 int timer_active, timer_id;
168 struct timeval last_time;
169 struct font *fonts;
170 int nfonts, fontsize;
171 config_item *cfg;
172 int cfg_which, cfgret;
173 GtkWidget *cfgbox;
174 void *paste_data;
175 int paste_data_len;
176 int pw, ph; /* pixmap size (w, h are area size) */
177 int ox, oy; /* offset of pixmap in drawing area */
178#ifdef OLD_FILESEL
179 char *filesel_name;
180#endif
181 GSList *preset_radio;
182 int n_preset_menu_items;
183 int preset_threaded;
184 GtkWidget *preset_custom;
185 GtkWidget *copy_menu_item;
186#if !GTK_CHECK_VERSION(3,0,0)
187 int drawing_area_shrink_pending;
188 int menubar_is_local;
189#endif
190};
191
192struct blitter {
193#ifdef USE_CAIRO
194 cairo_surface_t *image;
195#else
196 GdkPixmap *pixmap;
197#endif
198 int w, h, x, y;
199};
200
201void get_random_seed(void **randseed, int *randseedsize)
202{
203 struct timeval *tvp = snew(struct timeval);
204 gettimeofday(tvp, NULL);
205 *randseed = (void *)tvp;
206 *randseedsize = sizeof(struct timeval);
207}
208
209void frontend_default_colour(frontend *fe, float *output)
210{
211#if !GTK_CHECK_VERSION(3,0,0)
212 /*
213 * Use the widget style's default background colour as the
214 * background for the puzzle drawing area.
215 */
216 GdkColor col = gtk_widget_get_style(fe->window)->bg[GTK_STATE_NORMAL];
217 output[0] = col.red / 65535.0;
218 output[1] = col.green / 65535.0;
219 output[2] = col.blue / 65535.0;
220#else
221 /*
222 * GTK 3 has decided that there's no such thing as a 'default
223 * background colour' any more, because widget styles might set
224 * the background to something more complicated like a background
225 * image. We don't want to get into overlaying our entire puzzle
226 * on an arbitrary background image, so we'll just make up a
227 * reasonable shade of grey.
228 */
229 output[0] = output[1] = output[2] = 0.9F;
230#endif
231}
232
233void gtk_status_bar(void *handle, char *text)
234{
235 frontend *fe = (frontend *)handle;
236
237 assert(fe->statusbar);
238
239 gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx);
240 gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text);
241}
242
243/* ----------------------------------------------------------------------
244 * Cairo drawing functions.
245 */
246
247#ifdef USE_CAIRO
248
249static void setup_drawing(frontend *fe)
250{
251 fe->cr = cairo_create(fe->image);
252 cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_GRAY);
253 cairo_set_line_width(fe->cr, 1.0);
254 cairo_set_line_cap(fe->cr, CAIRO_LINE_CAP_SQUARE);
255 cairo_set_line_join(fe->cr, CAIRO_LINE_JOIN_ROUND);
256}
257
258static void teardown_drawing(frontend *fe)
259{
260 cairo_destroy(fe->cr);
261 fe->cr = NULL;
262
263#ifndef USE_CAIRO_WITHOUT_PIXMAP
264 {
265 cairo_t *cr = gdk_cairo_create(fe->pixmap);
266 cairo_set_source_surface(cr, fe->image, 0, 0);
267 cairo_rectangle(cr,
268 fe->bbox_l - 1,
269 fe->bbox_u - 1,
270 fe->bbox_r - fe->bbox_l + 2,
271 fe->bbox_d - fe->bbox_u + 2);
272 cairo_fill(cr);
273 cairo_destroy(cr);
274 }
275#endif
276}
277
278static void snaffle_colours(frontend *fe)
279{
280 fe->colours = midend_colours(fe->me, &fe->ncolours);
281}
282
283static void set_colour(frontend *fe, int colour)
284{
285 cairo_set_source_rgb(fe->cr,
286 fe->colours[3*colour + 0],
287 fe->colours[3*colour + 1],
288 fe->colours[3*colour + 2]);
289}
290
291static void set_window_background(frontend *fe, int colour)
292{
293#if GTK_CHECK_VERSION(3,0,0)
294 GdkRGBA rgba;
295 rgba.red = fe->colours[3*colour + 0];
296 rgba.green = fe->colours[3*colour + 1];
297 rgba.blue = fe->colours[3*colour + 2];
298 rgba.alpha = 1.0;
299 gdk_window_set_background_rgba(gtk_widget_get_window(fe->area), &rgba);
300 gdk_window_set_background_rgba(gtk_widget_get_window(fe->window), &rgba);
301#else
302 GdkColormap *colmap;
303
304 colmap = gdk_colormap_get_system();
305 fe->background.red = fe->colours[3*colour + 0] * 65535;
306 fe->background.green = fe->colours[3*colour + 1] * 65535;
307 fe->background.blue = fe->colours[3*colour + 2] * 65535;
308 if (!gdk_colormap_alloc_color(colmap, &fe->background, FALSE, FALSE)) {
309 g_error("couldn't allocate background (#%02x%02x%02x)\n",
310 fe->background.red >> 8, fe->background.green >> 8,
311 fe->background.blue >> 8);
312 }
313 gdk_window_set_background(gtk_widget_get_window(fe->area),
314 &fe->background);
315 gdk_window_set_background(gtk_widget_get_window(fe->window),
316 &fe->background);
317#endif
318}
319
320static PangoLayout *make_pango_layout(frontend *fe)
321{
322 return (pango_cairo_create_layout(fe->cr));
323}
324
325static void draw_pango_layout(frontend *fe, PangoLayout *layout,
326 int x, int y)
327{
328 cairo_move_to(fe->cr, x, y);
329 pango_cairo_show_layout(fe->cr, layout);
330}
331
332static void save_screenshot_png(frontend *fe, const char *screenshot_file)
333{
334 cairo_surface_write_to_png(fe->image, screenshot_file);
335}
336
337static void do_clip(frontend *fe, int x, int y, int w, int h)
338{
339 cairo_new_path(fe->cr);
340 cairo_rectangle(fe->cr, x, y, w, h);
341 cairo_clip(fe->cr);
342}
343
344static void do_unclip(frontend *fe)
345{
346 cairo_reset_clip(fe->cr);
347}
348
349static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
350{
351 cairo_save(fe->cr);
352 cairo_new_path(fe->cr);
353 cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE);
354 cairo_rectangle(fe->cr, x, y, w, h);
355 cairo_fill(fe->cr);
356 cairo_restore(fe->cr);
357}
358
359static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
360{
361 cairo_new_path(fe->cr);
362 cairo_move_to(fe->cr, x1 + 0.5, y1 + 0.5);
363 cairo_line_to(fe->cr, x2 + 0.5, y2 + 0.5);
364 cairo_stroke(fe->cr);
365}
366
367static void do_draw_thick_line(frontend *fe, float thickness,
368 float x1, float y1, float x2, float y2)
369{
370 cairo_save(fe->cr);
371 cairo_set_line_width(fe->cr, thickness);
372 cairo_new_path(fe->cr);
373 cairo_move_to(fe->cr, x1, y1);
374 cairo_line_to(fe->cr, x2, y2);
375 cairo_stroke(fe->cr);
376 cairo_restore(fe->cr);
377}
378
379static void do_draw_poly(frontend *fe, int *coords, int npoints,
380 int fillcolour, int outlinecolour)
381{
382 int i;
383
384 cairo_new_path(fe->cr);
385 for (i = 0; i < npoints; i++)
386 cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5);
387 cairo_close_path(fe->cr);
388 if (fillcolour >= 0) {
389 set_colour(fe, fillcolour);
390 cairo_fill_preserve(fe->cr);
391 }
392 assert(outlinecolour >= 0);
393 set_colour(fe, outlinecolour);
394 cairo_stroke(fe->cr);
395}
396
397static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
398 int fillcolour, int outlinecolour)
399{
400 cairo_new_path(fe->cr);
401 cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI);
402 cairo_close_path(fe->cr); /* Just in case... */
403 if (fillcolour >= 0) {
404 set_colour(fe, fillcolour);
405 cairo_fill_preserve(fe->cr);
406 }
407 assert(outlinecolour >= 0);
408 set_colour(fe, outlinecolour);
409 cairo_stroke(fe->cr);
410}
411
412static void setup_blitter(blitter *bl, int w, int h)
413{
414 bl->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
415}
416
417static void teardown_blitter(blitter *bl)
418{
419 cairo_surface_destroy(bl->image);
420}
421
422static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
423{
424 cairo_t *cr = cairo_create(bl->image);
425
426 cairo_set_source_surface(cr, fe->image, -x, -y);
427 cairo_paint(cr);
428 cairo_destroy(cr);
429}
430
431static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
432{
433 cairo_set_source_surface(fe->cr, bl->image, x, y);
434 cairo_paint(fe->cr);
435}
436
437static void clear_backing_store(frontend *fe)
438{
439 fe->image = NULL;
440}
441
442static void wipe_and_destroy_cairo(frontend *fe, cairo_t *cr)
443{
444 cairo_set_source_rgb(cr, fe->colours[0], fe->colours[1], fe->colours[2]);
445 cairo_paint(cr);
446 cairo_destroy(cr);
447}
448
449static void setup_backing_store(frontend *fe)
450{
451#ifndef USE_CAIRO_WITHOUT_PIXMAP
452 fe->pixmap = gdk_pixmap_new(gtk_widget_get_window(fe->area),
453 fe->pw, fe->ph, -1);
454#endif
455 fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
456 fe->pw, fe->ph);
457
458 wipe_and_destroy_cairo(fe, cairo_create(fe->image));
459#ifndef USE_CAIRO_WITHOUT_PIXMAP
460 wipe_and_destroy_cairo(fe, gdk_cairo_create(fe->pixmap));
461#endif
462 wipe_and_destroy_cairo(fe, gdk_cairo_create
463 (gtk_widget_get_window(fe->area)));
464}
465
466static int backing_store_ok(frontend *fe)
467{
468 return (!!fe->image);
469}
470
471static void teardown_backing_store(frontend *fe)
472{
473 cairo_surface_destroy(fe->image);
474#ifndef USE_CAIRO_WITHOUT_PIXMAP
475 gdk_pixmap_unref(fe->pixmap);
476#endif
477 fe->image = NULL;
478}
479
480#endif
481
482/* ----------------------------------------------------------------------
483 * GDK drawing functions.
484 */
485
486#ifndef USE_CAIRO
487
488static void setup_drawing(frontend *fe)
489{
490 fe->gc = gdk_gc_new(fe->area->window);
491}
492
493static void teardown_drawing(frontend *fe)
494{
495 gdk_gc_unref(fe->gc);
496 fe->gc = NULL;
497}
498
499static void snaffle_colours(frontend *fe)
500{
501 int i, ncolours;
502 float *colours;
503 gboolean *success;
504
505 fe->colmap = gdk_colormap_get_system();
506 colours = midend_colours(fe->me, &ncolours);
507 fe->ncolours = ncolours;
508 fe->colours = snewn(ncolours, GdkColor);
509 for (i = 0; i < ncolours; i++) {
510 fe->colours[i].red = colours[i*3] * 0xFFFF;
511 fe->colours[i].green = colours[i*3+1] * 0xFFFF;
512 fe->colours[i].blue = colours[i*3+2] * 0xFFFF;
513 }
514 success = snewn(ncolours, gboolean);
515 gdk_colormap_alloc_colors(fe->colmap, fe->colours, ncolours,
516 FALSE, FALSE, success);
517 for (i = 0; i < ncolours; i++) {
518 if (!success[i]) {
519 g_error("couldn't allocate colour %d (#%02x%02x%02x)\n",
520 i, fe->colours[i].red >> 8,
521 fe->colours[i].green >> 8,
522 fe->colours[i].blue >> 8);
523 }
524 }
525}
526
527static void set_window_background(frontend *fe, int colour)
528{
529 fe->backgroundindex = colour;
530 gdk_window_set_background(fe->area->window, &fe->colours[colour]);
531 gdk_window_set_background(fe->window->window, &fe->colours[colour]);
532}
533
534static void set_colour(frontend *fe, int colour)
535{
536 gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
537}
538
539#ifdef USE_PANGO
540static PangoLayout *make_pango_layout(frontend *fe)
541{
542 return (pango_layout_new(gtk_widget_get_pango_context(fe->area)));
543}
544
545static void draw_pango_layout(frontend *fe, PangoLayout *layout,
546 int x, int y)
547{
548 gdk_draw_layout(fe->pixmap, fe->gc, x, y, layout);
549}
550#endif
551
552static void save_screenshot_png(frontend *fe, const char *screenshot_file)
553{
554 GdkPixbuf *pb;
555 GError *gerror = NULL;
556
557 midend_redraw(fe->me);
558
559 pb = gdk_pixbuf_get_from_drawable(NULL, fe->pixmap,
560 NULL, 0, 0, 0, 0, -1, -1);
561 gdk_pixbuf_save(pb, screenshot_file, "png", &gerror, NULL);
562}
563
564static void do_clip(frontend *fe, int x, int y, int w, int h)
565{
566 GdkRectangle rect;
567
568 rect.x = x;
569 rect.y = y;
570 rect.width = w;
571 rect.height = h;
572 gdk_gc_set_clip_rectangle(fe->gc, &rect);
573}
574
575static void do_unclip(frontend *fe)
576{
577 GdkRectangle rect;
578
579 rect.x = 0;
580 rect.y = 0;
581 rect.width = fe->w;
582 rect.height = fe->h;
583 gdk_gc_set_clip_rectangle(fe->gc, &rect);
584}
585
586static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
587{
588 gdk_draw_rectangle(fe->pixmap, fe->gc, 1, x, y, w, h);
589}
590
591static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
592{
593 gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
594}
595
596static void do_draw_thick_line(frontend *fe, float thickness,
597 float x1, float y1, float x2, float y2)
598{
599 GdkGCValues save;
600
601 gdk_gc_get_values(fe->gc, &save);
602 gdk_gc_set_line_attributes(fe->gc,
603 thickness,
604 GDK_LINE_SOLID,
605 GDK_CAP_BUTT,
606 GDK_JOIN_BEVEL);
607 gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
608 gdk_gc_set_line_attributes(fe->gc,
609 save.line_width,
610 save.line_style,
611 save.cap_style,
612 save.join_style);
613}
614
615static void do_draw_poly(frontend *fe, int *coords, int npoints,
616 int fillcolour, int outlinecolour)
617{
618 GdkPoint *points = snewn(npoints, GdkPoint);
619 int i;
620
621 for (i = 0; i < npoints; i++) {
622 points[i].x = coords[i*2];
623 points[i].y = coords[i*2+1];
624 }
625
626 if (fillcolour >= 0) {
627 set_colour(fe, fillcolour);
628 gdk_draw_polygon(fe->pixmap, fe->gc, TRUE, points, npoints);
629 }
630 assert(outlinecolour >= 0);
631 set_colour(fe, outlinecolour);
632
633 /*
634 * In principle we ought to be able to use gdk_draw_polygon for
635 * the outline as well. In fact, it turns out to interact badly
636 * with a clipping region, for no terribly obvious reason, so I
637 * draw the outline as a sequence of lines instead.
638 */
639 for (i = 0; i < npoints; i++)
640 gdk_draw_line(fe->pixmap, fe->gc,
641 points[i].x, points[i].y,
642 points[(i+1)%npoints].x, points[(i+1)%npoints].y);
643
644 sfree(points);
645}
646
647static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
648 int fillcolour, int outlinecolour)
649{
650 if (fillcolour >= 0) {
651 set_colour(fe, fillcolour);
652 gdk_draw_arc(fe->pixmap, fe->gc, TRUE,
653 cx - radius, cy - radius,
654 2 * radius, 2 * radius, 0, 360 * 64);
655 }
656
657 assert(outlinecolour >= 0);
658 set_colour(fe, outlinecolour);
659 gdk_draw_arc(fe->pixmap, fe->gc, FALSE,
660 cx - radius, cy - radius,
661 2 * radius, 2 * radius, 0, 360 * 64);
662}
663
664static void setup_blitter(blitter *bl, int w, int h)
665{
666 /*
667 * We can't create the pixmap right now, because fe->window
668 * might not yet exist. So we just cache w and h and create it
669 * during the firs call to blitter_save.
670 */
671 bl->pixmap = NULL;
672}
673
674static void teardown_blitter(blitter *bl)
675{
676 if (bl->pixmap)
677 gdk_pixmap_unref(bl->pixmap);
678}
679
680static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
681{
682 if (!bl->pixmap)
683 bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1);
684 gdk_draw_pixmap(bl->pixmap,
685 fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
686 fe->pixmap,
687 x, y, 0, 0, bl->w, bl->h);
688}
689
690static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
691{
692 assert(bl->pixmap);
693 gdk_draw_pixmap(fe->pixmap,
694 fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
695 bl->pixmap,
696 0, 0, x, y, bl->w, bl->h);
697}
698
699static void clear_backing_store(frontend *fe)
700{
701 fe->pixmap = NULL;
702}
703
704static void setup_backing_store(frontend *fe)
705{
706 GdkGC *gc;
707
708 fe->pixmap = gdk_pixmap_new(fe->area->window, fe->pw, fe->ph, -1);
709
710 gc = gdk_gc_new(fe->area->window);
711 gdk_gc_set_foreground(gc, &fe->colours[0]);
712 gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->pw, fe->ph);
713 gdk_draw_rectangle(fe->area->window, gc, 1, 0, 0, fe->w, fe->h);
714 gdk_gc_unref(gc);
715}
716
717static int backing_store_ok(frontend *fe)
718{
719 return (!!fe->pixmap);
720}
721
722static void teardown_backing_store(frontend *fe)
723{
724 gdk_pixmap_unref(fe->pixmap);
725 fe->pixmap = NULL;
726}
727
728#endif
729
730#ifndef USE_CAIRO_WITHOUT_PIXMAP
731static void repaint_rectangle(frontend *fe, GtkWidget *widget,
732 int x, int y, int w, int h)
733{
734 GdkGC *gc = gdk_gc_new(gtk_widget_get_window(widget));
735#ifdef USE_CAIRO
736 gdk_gc_set_foreground(gc, &fe->background);
737#else
738 gdk_gc_set_foreground(gc, &fe->colours[fe->backgroundindex]);
739#endif
740 if (x < fe->ox) {
741 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
742 TRUE, x, y, fe->ox - x, h);
743 w -= (fe->ox - x);
744 x = fe->ox;
745 }
746 if (y < fe->oy) {
747 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
748 TRUE, x, y, w, fe->oy - y);
749 h -= (fe->oy - y);
750 y = fe->oy;
751 }
752 if (w > fe->pw) {
753 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
754 TRUE, x + fe->pw, y, w - fe->pw, h);
755 w = fe->pw;
756 }
757 if (h > fe->ph) {
758 gdk_draw_rectangle(gtk_widget_get_window(widget), gc,
759 TRUE, x, y + fe->ph, w, h - fe->ph);
760 h = fe->ph;
761 }
762 gdk_draw_pixmap(gtk_widget_get_window(widget), gc, fe->pixmap,
763 x - fe->ox, y - fe->oy, x, y, w, h);
764 gdk_gc_unref(gc);
765}
766#endif
767
768/* ----------------------------------------------------------------------
769 * Pango font functions.
770 */
771
772#ifdef USE_PANGO
773
774static void add_font(frontend *fe, int index, int fonttype, int fontsize)
775{
776 /*
777 * Use Pango to find the closest match to the requested
778 * font.
779 */
780 PangoFontDescription *fd;
781
782 fd = pango_font_description_new();
783 /* `Monospace' and `Sans' are meta-families guaranteed to exist */
784 pango_font_description_set_family(fd, fonttype == FONT_FIXED ?
785 "Monospace" : "Sans");
786 pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD);
787 /*
788 * I found some online Pango documentation which
789 * described a function called
790 * pango_font_description_set_absolute_size(), which is
791 * _exactly_ what I want here. Unfortunately, none of
792 * my local Pango installations have it (presumably
793 * they're too old), so I'm going to have to hack round
794 * it by figuring out the point size myself. This
795 * limits me to X and probably also breaks in later
796 * Pango installations, so ideally I should add another
797 * CHECK_VERSION type ifdef and use set_absolute_size
798 * where available. All very annoying.
799 */
800#ifdef HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
801 pango_font_description_set_absolute_size(fd, PANGO_SCALE*fontsize);
802#else
803 {
804 Display *d = GDK_DISPLAY();
805 int s = DefaultScreen(d);
806 double resolution =
807 (PANGO_SCALE * 72.27 / 25.4) *
808 ((double) DisplayWidthMM(d, s) / DisplayWidth (d, s));
809 pango_font_description_set_size(fd, resolution * fontsize);
810 }
811#endif
812 fe->fonts[index].desc = fd;
813}
814
815static void align_and_draw_text(frontend *fe,
816 int index, int align, int x, int y,
817 const char *text)
818{
819 PangoLayout *layout;
820 PangoRectangle rect;
821
822 layout = make_pango_layout(fe);
823
824 /*
825 * Create a layout.
826 */
827 pango_layout_set_font_description(layout, fe->fonts[index].desc);
828 pango_layout_set_text(layout, text, strlen(text));
829 pango_layout_get_pixel_extents(layout, NULL, &rect);
830
831 if (align & ALIGN_VCENTRE)
832 rect.y -= rect.height / 2;
833 else
834 rect.y -= rect.height;
835
836 if (align & ALIGN_HCENTRE)
837 rect.x -= rect.width / 2;
838 else if (align & ALIGN_HRIGHT)
839 rect.x -= rect.width;
840
841 draw_pango_layout(fe, layout, rect.x + x, rect.y + y);
842
843 g_object_unref(layout);
844}
845
846#endif
847
848/* ----------------------------------------------------------------------
849 * Old-fashioned font functions.
850 */
851
852#ifndef USE_PANGO
853
854static void add_font(int index, int fonttype, int fontsize)
855{
856 /*
857 * In GTK 1.2, I don't know of any plausible way to
858 * pick a suitable font, so I'm just going to be
859 * tedious.
860 */
861 fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ?
862 "fixed" : "variable");
863}
864
865static void align_and_draw_text(int index, int align, int x, int y,
866 const char *text)
867{
868 int lb, rb, wid, asc, desc;
869
870 /*
871 * Measure vertical string extents with respect to the same
872 * string always...
873 */
874 gdk_string_extents(fe->fonts[i].font,
875 "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
876 &lb, &rb, &wid, &asc, &desc);
877 if (align & ALIGN_VCENTRE)
878 y += asc - (asc+desc)/2;
879 else
880 y += asc;
881
882 /*
883 * ... but horizontal extents with respect to the provided
884 * string. This means that multiple pieces of text centred
885 * on the same y-coordinate don't have different baselines.
886 */
887 gdk_string_extents(fe->fonts[i].font, text,
888 &lb, &rb, &wid, &asc, &desc);
889
890 if (align & ALIGN_HCENTRE)
891 x -= wid / 2;
892 else if (align & ALIGN_HRIGHT)
893 x -= wid;
894
895 /*
896 * Actually draw the text.
897 */
898 gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text);
899}
900
901#endif
902
903/* ----------------------------------------------------------------------
904 * The exported drawing functions.
905 */
906
907void gtk_start_draw(void *handle)
908{
909 frontend *fe = (frontend *)handle;
910 fe->bbox_l = fe->w;
911 fe->bbox_r = 0;
912 fe->bbox_u = fe->h;
913 fe->bbox_d = 0;
914 setup_drawing(fe);
915}
916
917void gtk_clip(void *handle, int x, int y, int w, int h)
918{
919 frontend *fe = (frontend *)handle;
920 do_clip(fe, x, y, w, h);
921}
922
923void gtk_unclip(void *handle)
924{
925 frontend *fe = (frontend *)handle;
926 do_unclip(fe);
927}
928
929void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
930 int align, int colour, char *text)
931{
932 frontend *fe = (frontend *)handle;
933 int i;
934
935 /*
936 * Find or create the font.
937 */
938 for (i = 0; i < fe->nfonts; i++)
939 if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
940 break;
941
942 if (i == fe->nfonts) {
943 if (fe->fontsize <= fe->nfonts) {
944 fe->fontsize = fe->nfonts + 10;
945 fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
946 }
947
948 fe->nfonts++;
949
950 fe->fonts[i].type = fonttype;
951 fe->fonts[i].size = fontsize;
952 add_font(fe, i, fonttype, fontsize);
953 }
954
955 /*
956 * Do the job.
957 */
958 set_colour(fe, colour);
959 align_and_draw_text(fe, i, align, x, y, text);
960}
961
962void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour)
963{
964 frontend *fe = (frontend *)handle;
965 set_colour(fe, colour);
966 do_draw_rect(fe, x, y, w, h);
967}
968
969void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
970{
971 frontend *fe = (frontend *)handle;
972 set_colour(fe, colour);
973 do_draw_line(fe, x1, y1, x2, y2);
974}
975
976void gtk_draw_thick_line(void *handle, float thickness,
977 float x1, float y1, float x2, float y2, int colour)
978{
979 frontend *fe = (frontend *)handle;
980 set_colour(fe, colour);
981 do_draw_thick_line(fe, thickness, x1, y1, x2, y2);
982}
983
984void gtk_draw_poly(void *handle, int *coords, int npoints,
985 int fillcolour, int outlinecolour)
986{
987 frontend *fe = (frontend *)handle;
988 do_draw_poly(fe, coords, npoints, fillcolour, outlinecolour);
989}
990
991void gtk_draw_circle(void *handle, int cx, int cy, int radius,
992 int fillcolour, int outlinecolour)
993{
994 frontend *fe = (frontend *)handle;
995 do_draw_circle(fe, cx, cy, radius, fillcolour, outlinecolour);
996}
997
998blitter *gtk_blitter_new(void *handle, int w, int h)
999{
1000 blitter *bl = snew(blitter);
1001 setup_blitter(bl, w, h);
1002 bl->w = w;
1003 bl->h = h;
1004 return bl;
1005}
1006
1007void gtk_blitter_free(void *handle, blitter *bl)
1008{
1009 teardown_blitter(bl);
1010 sfree(bl);
1011}
1012
1013void gtk_blitter_save(void *handle, blitter *bl, int x, int y)
1014{
1015 frontend *fe = (frontend *)handle;
1016 do_blitter_save(fe, bl, x, y);
1017 bl->x = x;
1018 bl->y = y;
1019}
1020
1021void gtk_blitter_load(void *handle, blitter *bl, int x, int y)
1022{
1023 frontend *fe = (frontend *)handle;
1024 if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
1025 x = bl->x;
1026 y = bl->y;
1027 }
1028 do_blitter_load(fe, bl, x, y);
1029}
1030
1031void gtk_draw_update(void *handle, int x, int y, int w, int h)
1032{
1033 frontend *fe = (frontend *)handle;
1034 if (fe->bbox_l > x ) fe->bbox_l = x ;
1035 if (fe->bbox_r < x+w) fe->bbox_r = x+w;
1036 if (fe->bbox_u > y ) fe->bbox_u = y ;
1037 if (fe->bbox_d < y+h) fe->bbox_d = y+h;
1038}
1039
1040void gtk_end_draw(void *handle)
1041{
1042 frontend *fe = (frontend *)handle;
1043
1044 teardown_drawing(fe);
1045
1046 if (fe->bbox_l < fe->bbox_r && fe->bbox_u < fe->bbox_d) {
1047#ifdef USE_CAIRO_WITHOUT_PIXMAP
1048 gtk_widget_queue_draw_area(fe->area,
1049 fe->bbox_l - 1 + fe->ox,
1050 fe->bbox_u - 1 + fe->oy,
1051 fe->bbox_r - fe->bbox_l + 2,
1052 fe->bbox_d - fe->bbox_u + 2);
1053#else
1054 repaint_rectangle(fe, fe->area,
1055 fe->bbox_l - 1 + fe->ox,
1056 fe->bbox_u - 1 + fe->oy,
1057 fe->bbox_r - fe->bbox_l + 2,
1058 fe->bbox_d - fe->bbox_u + 2);
1059#endif
1060 }
1061}
1062
1063#ifdef USE_PANGO
1064char *gtk_text_fallback(void *handle, const char *const *strings, int nstrings)
1065{
1066 /*
1067 * We assume Pango can cope with any UTF-8 likely to be emitted
1068 * by a puzzle.
1069 */
1070 return dupstr(strings[0]);
1071}
1072#endif
1073
1074const struct drawing_api gtk_drawing = {
1075 gtk_draw_text,
1076 gtk_draw_rect,
1077 gtk_draw_line,
1078 gtk_draw_poly,
1079 gtk_draw_circle,
1080 gtk_draw_update,
1081 gtk_clip,
1082 gtk_unclip,
1083 gtk_start_draw,
1084 gtk_end_draw,
1085 gtk_status_bar,
1086 gtk_blitter_new,
1087 gtk_blitter_free,
1088 gtk_blitter_save,
1089 gtk_blitter_load,
1090 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
1091 NULL, NULL, /* line_width, line_dotted */
1092#ifdef USE_PANGO
1093 gtk_text_fallback,
1094#else
1095 NULL,
1096#endif
1097#ifdef NO_THICK_LINE
1098 NULL,
1099#else
1100 gtk_draw_thick_line,
1101#endif
1102};
1103
1104static void destroy(GtkWidget *widget, gpointer data)
1105{
1106 frontend *fe = (frontend *)data;
1107 deactivate_timer(fe);
1108 midend_free(fe->me);
1109 gtk_main_quit();
1110}
1111
1112static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
1113{
1114 frontend *fe = (frontend *)data;
1115 int keyval;
1116 int shift = (event->state & GDK_SHIFT_MASK) ? MOD_SHFT : 0;
1117 int ctrl = (event->state & GDK_CONTROL_MASK) ? MOD_CTRL : 0;
1118
1119 if (!backing_store_ok(fe))
1120 return TRUE;
1121
1122#if !GTK_CHECK_VERSION(2,0,0)
1123 /* Gtk 1.2 passes a key event to this function even if it's also
1124 * defined as an accelerator.
1125 * Gtk 2 doesn't do this, and this function appears not to exist there. */
1126 if (fe->accelgroup &&
1127 gtk_accel_group_get_entry(fe->accelgroup,
1128 event->keyval, event->state))
1129 return TRUE;
1130#endif
1131
1132 /* Handle mnemonics. */
1133 if (gtk_window_activate_key(GTK_WINDOW(fe->window), event))
1134 return TRUE;
1135
1136 if (event->keyval == GDK_KEY_Up)
1137 keyval = shift | ctrl | CURSOR_UP;
1138 else if (event->keyval == GDK_KEY_KP_Up ||
1139 event->keyval == GDK_KEY_KP_8)
1140 keyval = MOD_NUM_KEYPAD | '8';
1141 else if (event->keyval == GDK_KEY_Down)
1142 keyval = shift | ctrl | CURSOR_DOWN;
1143 else if (event->keyval == GDK_KEY_KP_Down ||
1144 event->keyval == GDK_KEY_KP_2)
1145 keyval = MOD_NUM_KEYPAD | '2';
1146 else if (event->keyval == GDK_KEY_Left)
1147 keyval = shift | ctrl | CURSOR_LEFT;
1148 else if (event->keyval == GDK_KEY_KP_Left ||
1149 event->keyval == GDK_KEY_KP_4)
1150 keyval = MOD_NUM_KEYPAD | '4';
1151 else if (event->keyval == GDK_KEY_Right)
1152 keyval = shift | ctrl | CURSOR_RIGHT;
1153 else if (event->keyval == GDK_KEY_KP_Right ||
1154 event->keyval == GDK_KEY_KP_6)
1155 keyval = MOD_NUM_KEYPAD | '6';
1156 else if (event->keyval == GDK_KEY_KP_Home ||
1157 event->keyval == GDK_KEY_KP_7)
1158 keyval = MOD_NUM_KEYPAD | '7';
1159 else if (event->keyval == GDK_KEY_KP_End ||
1160 event->keyval == GDK_KEY_KP_1)
1161 keyval = MOD_NUM_KEYPAD | '1';
1162 else if (event->keyval == GDK_KEY_KP_Page_Up ||
1163 event->keyval == GDK_KEY_KP_9)
1164 keyval = MOD_NUM_KEYPAD | '9';
1165 else if (event->keyval == GDK_KEY_KP_Page_Down ||
1166 event->keyval == GDK_KEY_KP_3)
1167 keyval = MOD_NUM_KEYPAD | '3';
1168 else if (event->keyval == GDK_KEY_KP_Insert ||
1169 event->keyval == GDK_KEY_KP_0)
1170 keyval = MOD_NUM_KEYPAD | '0';
1171 else if (event->keyval == GDK_KEY_KP_Begin ||
1172 event->keyval == GDK_KEY_KP_5)
1173 keyval = MOD_NUM_KEYPAD | '5';
1174 else if (event->keyval == GDK_KEY_BackSpace ||
1175 event->keyval == GDK_KEY_Delete ||
1176 event->keyval == GDK_KEY_KP_Delete)
1177 keyval = '\177';
1178 else if (event->string[0] && !event->string[1])
1179 keyval = (unsigned char)event->string[0];
1180 else
1181 keyval = -1;
1182
1183 if (keyval >= 0 &&
1184 !midend_process_key(fe->me, 0, 0, keyval))
1185 gtk_widget_destroy(fe->window);
1186
1187 return TRUE;
1188}
1189
1190static gint button_event(GtkWidget *widget, GdkEventButton *event,
1191 gpointer data)
1192{
1193 frontend *fe = (frontend *)data;
1194 int button;
1195
1196 if (!backing_store_ok(fe))
1197 return TRUE;
1198
1199 if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
1200 return TRUE;
1201
1202 if (event->button == 2 || (event->state & GDK_SHIFT_MASK))
1203 button = MIDDLE_BUTTON;
1204 else if (event->button == 3 || (event->state & GDK_MOD1_MASK))
1205 button = RIGHT_BUTTON;
1206 else if (event->button == 1)
1207 button = LEFT_BUTTON;
1208 else if (event->button == 8 && event->type == GDK_BUTTON_PRESS)
1209 button = 'u';
1210 else if (event->button == 9 && event->type == GDK_BUTTON_PRESS)
1211 button = 'r';
1212 else
1213 return FALSE; /* don't even know what button! */
1214
1215 if (event->type == GDK_BUTTON_RELEASE && button >= LEFT_BUTTON)
1216 button += LEFT_RELEASE - LEFT_BUTTON;
1217
1218 if (!midend_process_key(fe->me, event->x - fe->ox,
1219 event->y - fe->oy, button))
1220 gtk_widget_destroy(fe->window);
1221
1222 return TRUE;
1223}
1224
1225static gint motion_event(GtkWidget *widget, GdkEventMotion *event,
1226 gpointer data)
1227{
1228 frontend *fe = (frontend *)data;
1229 int button;
1230
1231 if (!backing_store_ok(fe))
1232 return TRUE;
1233
1234 if (event->state & (GDK_BUTTON2_MASK | GDK_SHIFT_MASK))
1235 button = MIDDLE_DRAG;
1236 else if (event->state & GDK_BUTTON1_MASK)
1237 button = LEFT_DRAG;
1238 else if (event->state & GDK_BUTTON3_MASK)
1239 button = RIGHT_DRAG;
1240 else
1241 return FALSE; /* don't even know what button! */
1242
1243 if (!midend_process_key(fe->me, event->x - fe->ox,
1244 event->y - fe->oy, button))
1245 gtk_widget_destroy(fe->window);
1246#if GTK_CHECK_VERSION(2,12,0)
1247 gdk_event_request_motions(event);
1248#else
1249 gdk_window_get_pointer(gtk_widget_get_window(widget), NULL, NULL, NULL);
1250#endif
1251
1252 return TRUE;
1253}
1254
1255#if GTK_CHECK_VERSION(3,0,0)
1256static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
1257{
1258 frontend *fe = (frontend *)data;
1259 GdkRectangle dirtyrect;
1260
1261 gdk_cairo_get_clip_rectangle(cr, &dirtyrect);
1262 cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy);
1263 cairo_rectangle(cr, dirtyrect.x, dirtyrect.y,
1264 dirtyrect.width, dirtyrect.height);
1265 cairo_fill(cr);
1266
1267 return TRUE;
1268}
1269#else
1270static gint expose_area(GtkWidget *widget, GdkEventExpose *event,
1271 gpointer data)
1272{
1273 frontend *fe = (frontend *)data;
1274
1275 if (backing_store_ok(fe)) {
1276#ifdef USE_CAIRO_WITHOUT_PIXMAP
1277 cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
1278 cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy);
1279 cairo_rectangle(cr, event->area.x, event->area.y,
1280 event->area.width, event->area.height);
1281 cairo_fill(cr);
1282 cairo_destroy(cr);
1283#else
1284 repaint_rectangle(fe, widget,
1285 event->area.x, event->area.y,
1286 event->area.width, event->area.height);
1287#endif
1288 }
1289 return TRUE;
1290}
1291#endif
1292
1293static gint map_window(GtkWidget *widget, GdkEvent *event,
1294 gpointer data)
1295{
1296 frontend *fe = (frontend *)data;
1297
1298 /*
1299 * Apparently we need to do this because otherwise the status
1300 * bar will fail to update immediately. Annoying, but there we
1301 * go.
1302 */
1303 gtk_widget_queue_draw(fe->window);
1304
1305 return TRUE;
1306}
1307
1308static gint configure_area(GtkWidget *widget,
1309 GdkEventConfigure *event, gpointer data)
1310{
1311 frontend *fe = (frontend *)data;
1312 int x, y;
1313 int oldw = fe->w, oldpw = fe->pw, oldh = fe->h, oldph = fe->ph;
1314
1315 x = event->width;
1316 y = event->height;
1317 fe->w = x;
1318 fe->h = y;
1319 midend_size(fe->me, &x, &y, TRUE);
1320 fe->pw = x;
1321 fe->ph = y;
1322 fe->ox = (fe->w - fe->pw) / 2;
1323 fe->oy = (fe->h - fe->ph) / 2;
1324
1325 if (oldw != fe->w || oldpw != fe->pw ||
1326 oldh != fe->h || oldph != fe->ph || !backing_store_ok(fe)) {
1327 if (backing_store_ok(fe))
1328 teardown_backing_store(fe);
1329 setup_backing_store(fe);
1330 }
1331
1332 midend_force_redraw(fe->me);
1333
1334 return TRUE;
1335}
1336
1337static gint timer_func(gpointer data)
1338{
1339 frontend *fe = (frontend *)data;
1340
1341 if (fe->timer_active) {
1342 struct timeval now;
1343 float elapsed;
1344 gettimeofday(&now, NULL);
1345 elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
1346 (now.tv_sec - fe->last_time.tv_sec));
1347 midend_timer(fe->me, elapsed); /* may clear timer_active */
1348 fe->last_time = now;
1349 }
1350
1351 return fe->timer_active;
1352}
1353
1354void deactivate_timer(frontend *fe)
1355{
1356 if (!fe)
1357 return; /* can happen due to --generate */
1358 if (fe->timer_active)
1359 g_source_remove(fe->timer_id);
1360 fe->timer_active = FALSE;
1361}
1362
1363void activate_timer(frontend *fe)
1364{
1365 if (!fe)
1366 return; /* can happen due to --generate */
1367 if (!fe->timer_active) {
1368 fe->timer_id = g_timeout_add(20, timer_func, fe);
1369 gettimeofday(&fe->last_time, NULL);
1370 }
1371 fe->timer_active = TRUE;
1372}
1373
1374static void window_destroy(GtkWidget *widget, gpointer data)
1375{
1376 gtk_main_quit();
1377}
1378
1379static int win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
1380{
1381 GObject *cancelbutton = G_OBJECT(data);
1382
1383 /*
1384 * `Escape' effectively clicks the cancel button
1385 */
1386 if (event->keyval == GDK_KEY_Escape) {
1387 g_signal_emit_by_name(cancelbutton, "clicked");
1388 return TRUE;
1389 }
1390
1391 return FALSE;
1392}
1393
1394enum { MB_OK, MB_YESNO };
1395
1396static void align_label(GtkLabel *label, double x, double y)
1397{
1398#if GTK_CHECK_VERSION(3,16,0)
1399 gtk_label_set_xalign(label, x);
1400 gtk_label_set_yalign(label, y);
1401#elif GTK_CHECK_VERSION(3,14,0)
1402 gtk_widget_set_halign(GTK_WIDGET(label),
1403 x == 0 ? GTK_ALIGN_START :
1404 x == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER);
1405 gtk_widget_set_valign(GTK_WIDGET(label),
1406 y == 0 ? GTK_ALIGN_START :
1407 y == 1 ? GTK_ALIGN_END : GTK_ALIGN_CENTER);
1408#else
1409 gtk_misc_set_alignment(GTK_MISC(label), x, y);
1410#endif
1411}
1412
1413#if GTK_CHECK_VERSION(3,0,0)
1414int message_box(GtkWidget *parent, char *title, char *msg, int centre,
1415 int type)
1416{
1417 GtkWidget *window;
1418 gint ret;
1419
1420 window = gtk_message_dialog_new
1421 (GTK_WINDOW(parent),
1422 (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1423 (type == MB_OK ? GTK_MESSAGE_INFO : GTK_MESSAGE_QUESTION),
1424 (type == MB_OK ? GTK_BUTTONS_OK : GTK_BUTTONS_YES_NO),
1425 "%s", msg);
1426 gtk_window_set_title(GTK_WINDOW(window), title);
1427 ret = gtk_dialog_run(GTK_DIALOG(window));
1428 gtk_widget_destroy(window);
1429 return (type == MB_OK ? TRUE : (ret == GTK_RESPONSE_YES));
1430}
1431#else /* GTK_CHECK_VERSION(3,0,0) */
1432static void msgbox_button_clicked(GtkButton *button, gpointer data)
1433{
1434 GtkWidget *window = GTK_WIDGET(data);
1435 int v, *ip;
1436
1437 ip = (int *)g_object_get_data(G_OBJECT(window), "user-data");
1438 v = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "user-data"));
1439 *ip = v;
1440
1441 gtk_widget_destroy(GTK_WIDGET(data));
1442}
1443
1444int message_box(GtkWidget *parent, char *title, char *msg, int centre,
1445 int type)
1446{
1447 GtkWidget *window, *hbox, *text, *button;
1448 char *titles;
1449 int i, def, cancel;
1450
1451 window = gtk_dialog_new();
1452 text = gtk_label_new(msg);
1453 align_label(GTK_LABEL(text), 0.0, 0.0);
1454 hbox = gtk_hbox_new(FALSE, 0);
1455 gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 20);
1456 gtk_box_pack_start
1457 (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
1458 hbox, FALSE, FALSE, 20);
1459 gtk_widget_show(text);
1460 gtk_widget_show(hbox);
1461 gtk_window_set_title(GTK_WINDOW(window), title);
1462 gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
1463
1464 if (type == MB_OK) {
1465 titles = LABEL_OK "\0";
1466 def = cancel = 0;
1467 } else {
1468 assert(type == MB_YESNO);
1469 titles = LABEL_NO "\0" LABEL_YES "\0";
1470 def = 1;
1471 cancel = 0;
1472 }
1473 i = 0;
1474
1475 while (*titles) {
1476 button = gtk_button_new_with_our_label(titles);
1477 gtk_box_pack_end
1478 (GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(window))),
1479 button, FALSE, FALSE, 0);
1480 gtk_widget_show(button);
1481 if (i == def) {
1482 gtk_widget_set_can_default(button, TRUE);
1483 gtk_window_set_default(GTK_WINDOW(window), button);
1484 }
1485 if (i == cancel) {
1486 g_signal_connect(G_OBJECT(window), "key_press_event",
1487 G_CALLBACK(win_key_press), button);
1488 }
1489 g_signal_connect(G_OBJECT(button), "clicked",
1490 G_CALLBACK(msgbox_button_clicked), window);
1491 g_object_set_data(G_OBJECT(button), "user-data",
1492 GINT_TO_POINTER(i));
1493 titles += strlen(titles)+1;
1494 i++;
1495 }
1496 g_object_set_data(G_OBJECT(window), "user-data", &i);
1497 g_signal_connect(G_OBJECT(window), "destroy",
1498 G_CALLBACK(window_destroy), NULL);
1499 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1500 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent));
1501 /* set_transient_window_pos(parent, window); */
1502 gtk_widget_show(window);
1503 i = -1;
1504 gtk_main();
1505 return (type == MB_YESNO ? i == 1 : TRUE);
1506}
1507#endif /* GTK_CHECK_VERSION(3,0,0) */
1508
1509void error_box(GtkWidget *parent, char *msg)
1510{
1511 message_box(parent, "Error", msg, FALSE, MB_OK);
1512}
1513
1514static void config_ok_button_clicked(GtkButton *button, gpointer data)
1515{
1516 frontend *fe = (frontend *)data;
1517 char *err;
1518
1519 err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
1520
1521 if (err)
1522 error_box(fe->cfgbox, err);
1523 else {
1524 fe->cfgret = TRUE;
1525 gtk_widget_destroy(fe->cfgbox);
1526 changed_preset(fe);
1527 }
1528}
1529
1530static void config_cancel_button_clicked(GtkButton *button, gpointer data)
1531{
1532 frontend *fe = (frontend *)data;
1533
1534 gtk_widget_destroy(fe->cfgbox);
1535}
1536
1537static int editbox_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1538{
1539 /*
1540 * GtkEntry has a nasty habit of eating the Return key, which
1541 * is unhelpful since it doesn't actually _do_ anything with it
1542 * (it calls gtk_widget_activate, but our edit boxes never need
1543 * activating). So I catch Return before GtkEntry sees it, and
1544 * pass it straight on to the parent widget. Effect: hitting
1545 * Return in an edit box will now activate the default button
1546 * in the dialog just like it will everywhere else.
1547 */
1548 if (event->keyval == GDK_KEY_Return &&
1549 gtk_widget_get_parent(widget) != NULL) {
1550 gint return_val;
1551 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1552 g_signal_emit_by_name(G_OBJECT(gtk_widget_get_parent(widget)),
1553 "key_press_event", event, &return_val);
1554 return return_val;
1555 }
1556 return FALSE;
1557}
1558
1559static void editbox_changed(GtkEditable *ed, gpointer data)
1560{
1561 config_item *i = (config_item *)data;
1562
1563 sfree(i->sval);
1564 i->sval = dupstr(gtk_entry_get_text(GTK_ENTRY(ed)));
1565}
1566
1567static void button_toggled(GtkToggleButton *tb, gpointer data)
1568{
1569 config_item *i = (config_item *)data;
1570
1571 i->ival = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb));
1572}
1573
1574static void droplist_sel(GtkComboBox *combo, gpointer data)
1575{
1576 config_item *i = (config_item *)data;
1577
1578 i->ival = gtk_combo_box_get_active(combo);
1579}
1580
1581static int get_config(frontend *fe, int which)
1582{
1583 GtkWidget *w, *table, *cancel;
1584 GtkBox *content_box, *button_box;
1585 char *title;
1586 config_item *i;
1587 int y;
1588
1589 fe->cfg = midend_get_config(fe->me, which, &title);
1590 fe->cfg_which = which;
1591 fe->cfgret = FALSE;
1592
1593#if GTK_CHECK_VERSION(3,0,0)
1594 /* GtkDialog isn't quite flexible enough */
1595 fe->cfgbox = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1596 content_box = GTK_BOX(gtk_vbox_new(FALSE, 8));
1597 g_object_set(G_OBJECT(content_box), "margin", 8, (const char *)NULL);
1598 gtk_widget_show(GTK_WIDGET(content_box));
1599 gtk_container_add(GTK_CONTAINER(fe->cfgbox), GTK_WIDGET(content_box));
1600 button_box = GTK_BOX(gtk_hbox_new(FALSE, 8));
1601 gtk_widget_show(GTK_WIDGET(button_box));
1602 gtk_box_pack_end(content_box, GTK_WIDGET(button_box), FALSE, FALSE, 0);
1603 {
1604 GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1605 gtk_widget_show(sep);
1606 gtk_box_pack_end(content_box, sep, FALSE, FALSE, 0);
1607 }
1608#else
1609 fe->cfgbox = gtk_dialog_new();
1610 content_box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(fe->cfgbox)));
1611 button_box = GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(fe->cfgbox)));
1612#endif
1613 gtk_window_set_title(GTK_WINDOW(fe->cfgbox), title);
1614 sfree(title);
1615
1616 w = gtk_button_new_with_our_label(LABEL_CANCEL);
1617 gtk_box_pack_end(button_box, w, FALSE, FALSE, 0);
1618 gtk_widget_show(w);
1619 g_signal_connect(G_OBJECT(w), "clicked",
1620 G_CALLBACK(config_cancel_button_clicked), fe);
1621 cancel = w;
1622
1623 w = gtk_button_new_with_our_label(LABEL_OK);
1624 gtk_box_pack_end(button_box, w, FALSE, FALSE, 0);
1625 gtk_widget_show(w);
1626 gtk_widget_set_can_default(w, TRUE);
1627 gtk_window_set_default(GTK_WINDOW(fe->cfgbox), w);
1628 g_signal_connect(G_OBJECT(w), "clicked",
1629 G_CALLBACK(config_ok_button_clicked), fe);
1630
1631#if GTK_CHECK_VERSION(3,0,0)
1632 table = gtk_grid_new();
1633#else
1634 table = gtk_table_new(1, 2, FALSE);
1635#endif
1636 y = 0;
1637 gtk_box_pack_start(content_box, table, FALSE, FALSE, 0);
1638 gtk_widget_show(table);
1639
1640 for (i = fe->cfg; i->type != C_END; i++) {
1641#if !GTK_CHECK_VERSION(3,0,0)
1642 gtk_table_resize(GTK_TABLE(table), y+1, 2);
1643#endif
1644
1645 switch (i->type) {
1646 case C_STRING:
1647 /*
1648 * Edit box with a label beside it.
1649 */
1650
1651 w = gtk_label_new(i->name);
1652 align_label(GTK_LABEL(w), 0.0, 0.5);
1653#if GTK_CHECK_VERSION(3,0,0)
1654 gtk_grid_attach(GTK_GRID(table), w, 0, y, 1, 1);
1655#else
1656 gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
1657 GTK_SHRINK | GTK_FILL,
1658 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1659 3, 3);
1660#endif
1661 gtk_widget_show(w);
1662
1663 w = gtk_entry_new();
1664#if GTK_CHECK_VERSION(3,0,0)
1665 gtk_grid_attach(GTK_GRID(table), w, 1, y, 1, 1);
1666 g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
1667#else
1668 gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
1669 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1670 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1671 3, 3);
1672#endif
1673 gtk_entry_set_text(GTK_ENTRY(w), i->sval);
1674 g_signal_connect(G_OBJECT(w), "changed",
1675 G_CALLBACK(editbox_changed), i);
1676 g_signal_connect(G_OBJECT(w), "key_press_event",
1677 G_CALLBACK(editbox_key), NULL);
1678 gtk_widget_show(w);
1679
1680 break;
1681
1682 case C_BOOLEAN:
1683 /*
1684 * Simple checkbox.
1685 */
1686 w = gtk_check_button_new_with_label(i->name);
1687 g_signal_connect(G_OBJECT(w), "toggled",
1688 G_CALLBACK(button_toggled), i);
1689#if GTK_CHECK_VERSION(3,0,0)
1690 gtk_grid_attach(GTK_GRID(table), w, 0, y, 2, 1);
1691 g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
1692#else
1693 gtk_table_attach(GTK_TABLE(table), w, 0, 2, y, y+1,
1694 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1695 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1696 3, 3);
1697#endif
1698 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), i->ival);
1699 gtk_widget_show(w);
1700 break;
1701
1702 case C_CHOICES:
1703 /*
1704 * Drop-down list (GtkComboBox).
1705 */
1706
1707 w = gtk_label_new(i->name);
1708 align_label(GTK_LABEL(w), 0.0, 0.5);
1709#if GTK_CHECK_VERSION(3,0,0)
1710 gtk_grid_attach(GTK_GRID(table), w, 0, y, 1, 1);
1711#else
1712 gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
1713 GTK_SHRINK | GTK_FILL,
1714 GTK_EXPAND | GTK_SHRINK | GTK_FILL ,
1715 3, 3);
1716#endif
1717 gtk_widget_show(w);
1718
1719 {
1720 int c;
1721 char *p, *q, *name;
1722 GtkListStore *model;
1723 GtkCellRenderer *cr;
1724 GtkTreeIter iter;
1725
1726 model = gtk_list_store_new(1, G_TYPE_STRING);
1727
1728 c = *i->sval;
1729 p = i->sval+1;
1730
1731 while (*p) {
1732 q = p;
1733 while (*q && *q != c)
1734 q++;
1735
1736 name = snewn(q-p+1, char);
1737 strncpy(name, p, q-p);
1738 name[q-p] = '\0';
1739
1740 if (*q) q++; /* eat delimiter */
1741
1742 gtk_list_store_append(model, &iter);
1743 gtk_list_store_set(model, &iter, 0, name, -1);
1744
1745 p = q;
1746 }
1747
1748 w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
1749
1750 gtk_combo_box_set_active(GTK_COMBO_BOX(w), i->ival);
1751
1752 cr = gtk_cell_renderer_text_new();
1753 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), cr, TRUE);
1754 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), cr,
1755 "text", 0, NULL);
1756
1757 g_signal_connect(G_OBJECT(w), "changed",
1758 G_CALLBACK(droplist_sel), i);
1759 }
1760
1761#if GTK_CHECK_VERSION(3,0,0)
1762 gtk_grid_attach(GTK_GRID(table), w, 1, y, 1, 1);
1763 g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
1764#else
1765 gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
1766 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1767 GTK_EXPAND | GTK_SHRINK | GTK_FILL,
1768 3, 3);
1769#endif
1770 gtk_widget_show(w);
1771 break;
1772 }
1773
1774 y++;
1775 }
1776
1777 g_signal_connect(G_OBJECT(fe->cfgbox), "destroy",
1778 G_CALLBACK(window_destroy), NULL);
1779 g_signal_connect(G_OBJECT(fe->cfgbox), "key_press_event",
1780 G_CALLBACK(win_key_press), cancel);
1781 gtk_window_set_modal(GTK_WINDOW(fe->cfgbox), TRUE);
1782 gtk_window_set_transient_for(GTK_WINDOW(fe->cfgbox),
1783 GTK_WINDOW(fe->window));
1784 /* set_transient_window_pos(fe->window, fe->cfgbox); */
1785 gtk_widget_show(fe->cfgbox);
1786 gtk_main();
1787
1788 free_cfg(fe->cfg);
1789
1790 return fe->cfgret;
1791}
1792
1793static void menu_key_event(GtkMenuItem *menuitem, gpointer data)
1794{
1795 frontend *fe = (frontend *)data;
1796 int key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
1797 "user-data"));
1798 if (!midend_process_key(fe->me, 0, 0, key))
1799 gtk_widget_destroy(fe->window);
1800}
1801
1802static void get_size(frontend *fe, int *px, int *py)
1803{
1804 int x, y;
1805
1806 /*
1807 * Currently I don't want to make the GTK port scale large
1808 * puzzles to fit on the screen. This is because X does permit
1809 * extremely large windows and many window managers provide a
1810 * means of navigating round them, and the users I consulted
1811 * before deciding said that they'd rather have enormous puzzle
1812 * windows spanning multiple screen pages than have them
1813 * shrunk. I could change my mind later or introduce
1814 * configurability; this would be the place to do so, by
1815 * replacing the initial values of x and y with the screen
1816 * dimensions.
1817 */
1818 x = INT_MAX;
1819 y = INT_MAX;
1820 midend_size(fe->me, &x, &y, FALSE);
1821 *px = x;
1822 *py = y;
1823}
1824
1825#if !GTK_CHECK_VERSION(2,0,0)
1826#define gtk_window_resize(win, x, y) \
1827 gdk_window_resize(GTK_WIDGET(win)->window, x, y)
1828#endif
1829
1830/*
1831 * Called when any other code in this file has changed the
1832 * selected game parameters.
1833 */
1834static void changed_preset(frontend *fe)
1835{
1836 int n = midend_which_preset(fe->me);
1837
1838 fe->preset_threaded = TRUE;
1839 if (n < 0 && fe->preset_custom) {
1840 gtk_check_menu_item_set_active(
1841 GTK_CHECK_MENU_ITEM(fe->preset_custom),
1842 TRUE);
1843 } else {
1844 GSList *gs = fe->preset_radio;
1845 int i = fe->n_preset_menu_items - 1 - n;
1846 if (fe->preset_custom)
1847 gs = gs->next;
1848 while (i && gs) {
1849 i--;
1850 gs = gs->next;
1851 }
1852 if (gs) {
1853 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data),
1854 TRUE);
1855 } else for (gs = fe->preset_radio; gs; gs = gs->next) {
1856 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data),
1857 FALSE);
1858 }
1859 }
1860 fe->preset_threaded = FALSE;
1861
1862 /*
1863 * Update the greying on the Copy menu option.
1864 */
1865 if (fe->copy_menu_item) {
1866 int enabled = midend_can_format_as_text_now(fe->me);
1867 gtk_widget_set_sensitive(fe->copy_menu_item, enabled);
1868 }
1869}
1870
1871#if !GTK_CHECK_VERSION(3,0,0)
1872static gboolean not_size_allocated_yet(GtkWidget *w)
1873{
1874 /*
1875 * This function tests whether a widget has not yet taken up space
1876 * on the screen which it will occupy in future. (Therefore, it
1877 * returns true only if the widget does exist but does not have a
1878 * size allocation. A null widget is already taking up all the
1879 * space it ever will.)
1880 */
1881 if (!w)
1882 return FALSE; /* nonexistent widgets aren't a problem */
1883
1884#if GTK_CHECK_VERSION(2,18,0) /* skip if no gtk_widget_get_allocation */
1885 {
1886 GtkAllocation a;
1887 gtk_widget_get_allocation(w, &a);
1888 if (a.height == 0 || a.width == 0)
1889 return TRUE; /* widget exists but has no size yet */
1890 }
1891#endif
1892
1893 return FALSE;
1894}
1895
1896static void try_shrink_drawing_area(frontend *fe)
1897{
1898 if (fe->drawing_area_shrink_pending &&
1899 (!fe->menubar_is_local || !not_size_allocated_yet(fe->menubar)) &&
1900 !not_size_allocated_yet(fe->statusbar)) {
1901 /*
1902 * In order to permit the user to resize the window smaller as
1903 * well as bigger, we call this function after the window size
1904 * has ended up where we want it. This shouldn't shrink the
1905 * window immediately; it just arranges that the next time the
1906 * user tries to shrink it, they can.
1907 *
1908 * However, at puzzle creation time, we defer the first of
1909 * these operations until after the menu bar and status bar
1910 * are actually visible. On Ubuntu 12.04 I've found that these
1911 * can take a while to be displayed, and that it's a mistake
1912 * to reduce the drawing area's size allocation before they've
1913 * turned up or else the drawing area makes room for them by
1914 * shrinking to less than the size we intended.
1915 */
1916 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1);
1917 fe->drawing_area_shrink_pending = FALSE;
1918 }
1919}
1920#endif /* !GTK_CHECK_VERSION(3,0,0) */
1921
1922static gint configure_window(GtkWidget *widget,
1923 GdkEventConfigure *event, gpointer data)
1924{
1925#if !GTK_CHECK_VERSION(3,0,0)
1926 /*
1927 * When the main puzzle window changes size, it might be because
1928 * the menu bar or status bar has turned up after starting off
1929 * absent, in which case we should have another go at enacting a
1930 * pending shrink of the drawing area.
1931 */
1932 frontend *fe = (frontend *)data;
1933 try_shrink_drawing_area(fe);
1934#endif
1935 return FALSE;
1936}
1937
1938static void resize_fe(frontend *fe)
1939{
1940 int x, y;
1941
1942 get_size(fe, &x, &y);
1943
1944#if GTK_CHECK_VERSION(3,0,0)
1945 gtk_window_resize_to_geometry(GTK_WINDOW(fe->window), x, y);
1946#else
1947 fe->drawing_area_shrink_pending = FALSE;
1948 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
1949 {
1950 GtkRequisition req;
1951 gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
1952 gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
1953 }
1954 fe->drawing_area_shrink_pending = TRUE;
1955 try_shrink_drawing_area(fe);
1956#endif
1957}
1958
1959static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
1960{
1961 frontend *fe = (frontend *)data;
1962 game_params *params =
1963 (game_params *)g_object_get_data(G_OBJECT(menuitem), "user-data");
1964
1965 if (fe->preset_threaded ||
1966 (GTK_IS_CHECK_MENU_ITEM(menuitem) &&
1967 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))))
1968 return;
1969 midend_set_params(fe->me, params);
1970 midend_new_game(fe->me);
1971 changed_preset(fe);
1972 resize_fe(fe);
1973}
1974
1975GdkAtom compound_text_atom, utf8_string_atom;
1976int paste_initialised = FALSE;
1977
1978static void set_selection(frontend *fe, GdkAtom selection)
1979{
1980 if (!paste_initialised) {
1981 compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
1982 utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
1983 paste_initialised = TRUE;
1984 }
1985
1986 /*
1987 * For this simple application we can safely assume that the
1988 * data passed to this function is pure ASCII, which means we
1989 * can return precisely the same stuff for types STRING,
1990 * COMPOUND_TEXT or UTF8_STRING.
1991 */
1992
1993 if (gtk_selection_owner_set(fe->area, selection, CurrentTime)) {
1994 gtk_selection_clear_targets(fe->area, selection);
1995 gtk_selection_add_target(fe->area, selection,
1996 GDK_SELECTION_TYPE_STRING, 1);
1997 gtk_selection_add_target(fe->area, selection, compound_text_atom, 1);
1998 gtk_selection_add_target(fe->area, selection, utf8_string_atom, 1);
1999 }
2000}
2001
2002void write_clip(frontend *fe, char *data)
2003{
2004 if (fe->paste_data)
2005 sfree(fe->paste_data);
2006
2007 fe->paste_data = data;
2008 fe->paste_data_len = strlen(data);
2009
2010 set_selection(fe, GDK_SELECTION_PRIMARY);
2011 set_selection(fe, GDK_SELECTION_CLIPBOARD);
2012}
2013
2014void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
2015 guint info, guint time_stamp, gpointer data)
2016{
2017 frontend *fe = (frontend *)data;
2018 gtk_selection_data_set(seldata, gtk_selection_data_get_target(seldata), 8,
2019 fe->paste_data, fe->paste_data_len);
2020}
2021
2022gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
2023 gpointer data)
2024{
2025 frontend *fe = (frontend *)data;
2026
2027 if (fe->paste_data)
2028 sfree(fe->paste_data);
2029 fe->paste_data = NULL;
2030 fe->paste_data_len = 0;
2031 return TRUE;
2032}
2033
2034static void menu_copy_event(GtkMenuItem *menuitem, gpointer data)
2035{
2036 frontend *fe = (frontend *)data;
2037 char *text;
2038
2039 text = midend_text_format(fe->me);
2040
2041 if (text) {
2042 write_clip(fe, text);
2043 } else {
2044 gdk_beep();
2045 }
2046}
2047
2048#ifdef OLD_FILESEL
2049
2050static void filesel_ok(GtkButton *button, gpointer data)
2051{
2052 frontend *fe = (frontend *)data;
2053
2054 gpointer filesel = g_object_get_data(G_OBJECT(button), "user-data");
2055
2056 const char *name =
2057 gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
2058
2059 fe->filesel_name = dupstr(name);
2060}
2061
2062static char *file_selector(frontend *fe, char *title, int save)
2063{
2064 GtkWidget *filesel =
2065 gtk_file_selection_new(title);
2066
2067 fe->filesel_name = NULL;
2068
2069 gtk_window_set_modal(GTK_WINDOW(filesel), TRUE);
2070 g_object_set_data
2071 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",
2072 (gpointer)filesel);
2073 g_signal_connect
2074 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
2075 G_CALLBACK(filesel_ok), fe);
2076 g_signal_connect_swapped
2077 (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
2078 G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
2079 g_signal_connect_object
2080 (G_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",
2081 G_CALLBACK(gtk_widget_destroy), (gpointer)filesel);
2082 g_signal_connect(G_OBJECT(filesel), "destroy",
2083 G_CALLBACK(window_destroy), NULL);
2084 gtk_widget_show(filesel);
2085 gtk_window_set_transient_for(GTK_WINDOW(filesel), GTK_WINDOW(fe->window));
2086 gtk_main();
2087
2088 return fe->filesel_name;
2089}
2090
2091#else
2092
2093static char *file_selector(frontend *fe, char *title, int save)
2094{
2095 char *filesel_name = NULL;
2096
2097 GtkWidget *filesel =
2098 gtk_file_chooser_dialog_new(title,
2099 GTK_WINDOW(fe->window),
2100 save ? GTK_FILE_CHOOSER_ACTION_SAVE :
2101 GTK_FILE_CHOOSER_ACTION_OPEN,
2102 LABEL_CANCEL, GTK_RESPONSE_CANCEL,
2103 save ? LABEL_SAVE : LABEL_OPEN,
2104 GTK_RESPONSE_ACCEPT,
2105 NULL);
2106
2107 if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
2108 char *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
2109 filesel_name = dupstr(name);
2110 g_free(name);
2111 }
2112
2113 gtk_widget_destroy(filesel);
2114
2115 return filesel_name;
2116}
2117
2118#endif
2119
2120struct savefile_write_ctx {
2121 FILE *fp;
2122 int error;
2123};
2124
2125static void savefile_write(void *wctx, void *buf, int len)
2126{
2127 struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)wctx;
2128 if (fwrite(buf, 1, len, ctx->fp) < len)
2129 ctx->error = errno;
2130}
2131
2132static int savefile_read(void *wctx, void *buf, int len)
2133{
2134 FILE *fp = (FILE *)wctx;
2135 int ret;
2136
2137 ret = fread(buf, 1, len, fp);
2138 return (ret == len);
2139}
2140
2141static void menu_save_event(GtkMenuItem *menuitem, gpointer data)
2142{
2143 frontend *fe = (frontend *)data;
2144 char *name;
2145
2146 name = file_selector(fe, "Enter name of game file to save", TRUE);
2147
2148 if (name) {
2149 FILE *fp;
2150
2151 if ((fp = fopen(name, "r")) != NULL) {
2152 char buf[256 + FILENAME_MAX];
2153 fclose(fp);
2154 /* file exists */
2155
2156 sprintf(buf, "Are you sure you want to overwrite the"
2157 " file \"%.*s\"?",
2158 FILENAME_MAX, name);
2159 if (!message_box(fe->window, "Question", buf, TRUE, MB_YESNO))
2160 goto free_and_return;
2161 }
2162
2163 fp = fopen(name, "w");
2164
2165 if (!fp) {
2166 error_box(fe->window, "Unable to open save file");
2167 goto free_and_return;
2168 }
2169
2170 {
2171 struct savefile_write_ctx ctx;
2172 ctx.fp = fp;
2173 ctx.error = 0;
2174 midend_serialise(fe->me, savefile_write, &ctx);
2175 fclose(fp);
2176 if (ctx.error) {
2177 char boxmsg[512];
2178 sprintf(boxmsg, "Error writing save file: %.400s",
2179 strerror(errno));
2180 error_box(fe->window, boxmsg);
2181 goto free_and_return;
2182 }
2183 }
2184 free_and_return:
2185 sfree(name);
2186 }
2187}
2188
2189static void menu_load_event(GtkMenuItem *menuitem, gpointer data)
2190{
2191 frontend *fe = (frontend *)data;
2192 char *name, *err;
2193
2194 name = file_selector(fe, "Enter name of saved game file to load", FALSE);
2195
2196 if (name) {
2197 FILE *fp = fopen(name, "r");
2198 sfree(name);
2199
2200 if (!fp) {
2201 error_box(fe->window, "Unable to open saved game file");
2202 return;
2203 }
2204
2205 err = midend_deserialise(fe->me, savefile_read, fp);
2206
2207 fclose(fp);
2208
2209 if (err) {
2210 error_box(fe->window, err);
2211 return;
2212 }
2213
2214 changed_preset(fe);
2215 resize_fe(fe);
2216 }
2217}
2218
2219static void menu_solve_event(GtkMenuItem *menuitem, gpointer data)
2220{
2221 frontend *fe = (frontend *)data;
2222 char *msg;
2223
2224 msg = midend_solve(fe->me);
2225
2226 if (msg)
2227 error_box(fe->window, msg);
2228}
2229
2230static void menu_restart_event(GtkMenuItem *menuitem, gpointer data)
2231{
2232 frontend *fe = (frontend *)data;
2233
2234 midend_restart_game(fe->me);
2235}
2236
2237static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
2238{
2239 frontend *fe = (frontend *)data;
2240 int which = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
2241 "user-data"));
2242
2243 if (fe->preset_threaded ||
2244 (GTK_IS_CHECK_MENU_ITEM(menuitem) &&
2245 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))))
2246 return;
2247 changed_preset(fe); /* Put the old preset back! */
2248 if (!get_config(fe, which))
2249 return;
2250
2251 midend_new_game(fe->me);
2252 resize_fe(fe);
2253}
2254
2255static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
2256{
2257 frontend *fe = (frontend *)data;
2258
2259#if GTK_CHECK_VERSION(3,0,0)
2260 extern char *const *const xpm_icons[];
2261 extern const int n_xpm_icons;
2262 GdkPixbuf *icon = gdk_pixbuf_new_from_xpm_data
2263 ((const gchar **)xpm_icons[n_xpm_icons-1]);
2264 gtk_show_about_dialog
2265 (GTK_WINDOW(fe->window),
2266 "program-name", thegame.name,
2267 "version", ver,
2268 "comments", "Part of Simon Tatham's Portable Puzzle Collection",
2269 "logo", icon,
2270 (const gchar *)NULL);
2271 g_object_unref(G_OBJECT(icon));
2272#else
2273 char titlebuf[256];
2274 char textbuf[1024];
2275
2276 sprintf(titlebuf, "About %.200s", thegame.name);
2277 sprintf(textbuf,
2278 "%.200s\n\n"
2279 "from Simon Tatham's Portable Puzzle Collection\n\n"
2280 "%.500s", thegame.name, ver);
2281
2282 message_box(fe->window, titlebuf, textbuf, TRUE, MB_OK);
2283#endif
2284}
2285
2286static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont,
2287 char *text, int key)
2288{
2289 GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
2290 int keyqual;
2291 gtk_container_add(cont, menuitem);
2292 g_object_set_data(G_OBJECT(menuitem), "user-data", GINT_TO_POINTER(key));
2293 g_signal_connect(G_OBJECT(menuitem), "activate",
2294 G_CALLBACK(menu_key_event), fe);
2295 switch (key & ~0x1F) {
2296 case 0x00:
2297 key += 0x60;
2298 keyqual = GDK_CONTROL_MASK;
2299 break;
2300 case 0x40:
2301 key += 0x20;
2302 keyqual = GDK_SHIFT_MASK;
2303 break;
2304 default:
2305 keyqual = 0;
2306 break;
2307 }
2308 gtk_widget_add_accelerator(menuitem,
2309 "activate", fe->accelgroup,
2310 key, keyqual,
2311 GTK_ACCEL_VISIBLE);
2312 gtk_widget_show(menuitem);
2313 return menuitem;
2314}
2315
2316static void add_menu_separator(GtkContainer *cont)
2317{
2318 GtkWidget *menuitem = gtk_menu_item_new();
2319 gtk_container_add(cont, menuitem);
2320 gtk_widget_show(menuitem);
2321}
2322
2323enum { ARG_EITHER, ARG_SAVE, ARG_ID }; /* for argtype */
2324
2325static frontend *new_window(char *arg, int argtype, char **error)
2326{
2327 frontend *fe;
2328 GtkBox *vbox, *hbox;
2329 GtkWidget *menu, *menuitem;
2330 GList *iconlist;
2331 int x, y, n;
2332 char errbuf[1024];
2333 extern char *const *const xpm_icons[];
2334 extern const int n_xpm_icons;
2335
2336 fe = snew(frontend);
2337
2338 fe->timer_active = FALSE;
2339 fe->timer_id = -1;
2340
2341 fe->me = midend_new(fe, &thegame, &gtk_drawing, fe);
2342
2343 if (arg) {
2344 char *err;
2345 FILE *fp;
2346
2347 errbuf[0] = '\0';
2348
2349 switch (argtype) {
2350 case ARG_ID:
2351 err = midend_game_id(fe->me, arg);
2352 if (!err)
2353 midend_new_game(fe->me);
2354 else
2355 sprintf(errbuf, "Invalid game ID: %.800s", err);
2356 break;
2357 case ARG_SAVE:
2358 fp = fopen(arg, "r");
2359 if (!fp) {
2360 sprintf(errbuf, "Error opening file: %.800s", strerror(errno));
2361 } else {
2362 err = midend_deserialise(fe->me, savefile_read, fp);
2363 if (err)
2364 sprintf(errbuf, "Invalid save file: %.800s", err);
2365 fclose(fp);
2366 }
2367 break;
2368 default /*case ARG_EITHER*/:
2369 /*
2370 * First try treating the argument as a game ID.
2371 */
2372 err = midend_game_id(fe->me, arg);
2373 if (!err) {
2374 /*
2375 * It's a valid game ID.
2376 */
2377 midend_new_game(fe->me);
2378 } else {
2379 FILE *fp = fopen(arg, "r");
2380 if (!fp) {
2381 sprintf(errbuf, "Supplied argument is neither a game ID (%.400s)"
2382 " nor a save file (%.400s)", err, strerror(errno));
2383 } else {
2384 err = midend_deserialise(fe->me, savefile_read, fp);
2385 if (err)
2386 sprintf(errbuf, "%.800s", err);
2387 fclose(fp);
2388 }
2389 }
2390 break;
2391 }
2392 if (*errbuf) {
2393 *error = dupstr(errbuf);
2394 midend_free(fe->me);
2395 sfree(fe);
2396 return NULL;
2397 }
2398
2399 } else {
2400 midend_new_game(fe->me);
2401 }
2402
2403#if !GTK_CHECK_VERSION(3,0,0)
2404 {
2405 /*
2406 * try_shrink_drawing_area() will do some fiddling with the
2407 * window size request (see comment in that function) after
2408 * all the bits and pieces such as the menu bar and status bar
2409 * have appeared in the puzzle window.
2410 *
2411 * However, on Unity systems, the menu bar _doesn't_ appear in
2412 * the puzzle window, because the Unity shell hijacks it into
2413 * the menu bar at the very top of the screen. We therefore
2414 * try to detect that situation here, so that we don't sit
2415 * here forever waiting for a menu bar.
2416 */
2417 const char prop[] = "gtk-shell-shows-menubar";
2418 GtkSettings *settings = gtk_settings_get_default();
2419 if (!g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
2420 prop)) {
2421 fe->menubar_is_local = TRUE;
2422 } else {
2423 int unity_mode;
2424 g_object_get(gtk_settings_get_default(),
2425 prop, &unity_mode,
2426 (const gchar *)NULL);
2427 fe->menubar_is_local = !unity_mode;
2428 }
2429 }
2430#endif
2431
2432 fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2433 gtk_window_set_title(GTK_WINDOW(fe->window), thegame.name);
2434
2435 vbox = GTK_BOX(gtk_vbox_new(FALSE, 0));
2436 gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox));
2437 gtk_widget_show(GTK_WIDGET(vbox));
2438
2439 fe->accelgroup = gtk_accel_group_new();
2440 gtk_window_add_accel_group(GTK_WINDOW(fe->window), fe->accelgroup);
2441
2442 hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
2443 gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0);
2444 gtk_widget_show(GTK_WIDGET(hbox));
2445
2446 fe->menubar = gtk_menu_bar_new();
2447 gtk_box_pack_start(hbox, fe->menubar, TRUE, TRUE, 0);
2448 gtk_widget_show(fe->menubar);
2449
2450 menuitem = gtk_menu_item_new_with_mnemonic("_Game");
2451 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
2452 gtk_widget_show(menuitem);
2453
2454 menu = gtk_menu_new();
2455 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2456
2457 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n');
2458
2459 menuitem = gtk_menu_item_new_with_label("Restart");
2460 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2461 g_signal_connect(G_OBJECT(menuitem), "activate",
2462 G_CALLBACK(menu_restart_event), fe);
2463 gtk_widget_show(menuitem);
2464
2465 menuitem = gtk_menu_item_new_with_label("Specific...");
2466 g_object_set_data(G_OBJECT(menuitem), "user-data",
2467 GINT_TO_POINTER(CFG_DESC));
2468 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2469 g_signal_connect(G_OBJECT(menuitem), "activate",
2470 G_CALLBACK(menu_config_event), fe);
2471 gtk_widget_show(menuitem);
2472
2473 menuitem = gtk_menu_item_new_with_label("Random Seed...");
2474 g_object_set_data(G_OBJECT(menuitem), "user-data",
2475 GINT_TO_POINTER(CFG_SEED));
2476 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2477 g_signal_connect(G_OBJECT(menuitem), "activate",
2478 G_CALLBACK(menu_config_event), fe);
2479 gtk_widget_show(menuitem);
2480
2481 fe->preset_radio = NULL;
2482 fe->preset_custom = NULL;
2483 fe->n_preset_menu_items = 0;
2484 fe->preset_threaded = FALSE;
2485 if ((n = midend_num_presets(fe->me)) > 0 || thegame.can_configure) {
2486 GtkWidget *submenu;
2487 int i;
2488
2489 menuitem = gtk_menu_item_new_with_mnemonic("_Type");
2490 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
2491 gtk_widget_show(menuitem);
2492
2493 submenu = gtk_menu_new();
2494 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
2495
2496 for (i = 0; i < n; i++) {
2497 char *name;
2498 game_params *params;
2499
2500 midend_fetch_preset(fe->me, i, &name, &params);
2501
2502 menuitem =
2503 gtk_radio_menu_item_new_with_label(fe->preset_radio, name);
2504 fe->preset_radio =
2505 gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
2506 fe->n_preset_menu_items++;
2507 gtk_container_add(GTK_CONTAINER(submenu), menuitem);
2508 g_object_set_data(G_OBJECT(menuitem), "user-data", params);
2509 g_signal_connect(G_OBJECT(menuitem), "activate",
2510 G_CALLBACK(menu_preset_event), fe);
2511 gtk_widget_show(menuitem);
2512 }
2513
2514 if (thegame.can_configure) {
2515 menuitem = fe->preset_custom =
2516 gtk_radio_menu_item_new_with_label(fe->preset_radio,
2517 "Custom...");
2518 fe->preset_radio =
2519 gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
2520 gtk_container_add(GTK_CONTAINER(submenu), menuitem);
2521 g_object_set_data(G_OBJECT(menuitem), "user-data",
2522 GINT_TO_POINTER(CFG_SETTINGS));
2523 g_signal_connect(G_OBJECT(menuitem), "activate",
2524 G_CALLBACK(menu_config_event), fe);
2525 gtk_widget_show(menuitem);
2526 }
2527
2528 }
2529
2530 add_menu_separator(GTK_CONTAINER(menu));
2531 menuitem = gtk_menu_item_new_with_label("Load...");
2532 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2533 g_signal_connect(G_OBJECT(menuitem), "activate",
2534 G_CALLBACK(menu_load_event), fe);
2535 gtk_widget_show(menuitem);
2536 menuitem = gtk_menu_item_new_with_label("Save...");
2537 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2538 g_signal_connect(G_OBJECT(menuitem), "activate",
2539 G_CALLBACK(menu_save_event), fe);
2540 gtk_widget_show(menuitem);
2541#ifndef STYLUS_BASED
2542 add_menu_separator(GTK_CONTAINER(menu));
2543 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u');
2544 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", 'r');
2545#endif
2546 if (thegame.can_format_as_text_ever) {
2547 add_menu_separator(GTK_CONTAINER(menu));
2548 menuitem = gtk_menu_item_new_with_label("Copy");
2549 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2550 g_signal_connect(G_OBJECT(menuitem), "activate",
2551 G_CALLBACK(menu_copy_event), fe);
2552 gtk_widget_show(menuitem);
2553 fe->copy_menu_item = menuitem;
2554 } else {
2555 fe->copy_menu_item = NULL;
2556 }
2557 if (thegame.can_solve) {
2558 add_menu_separator(GTK_CONTAINER(menu));
2559 menuitem = gtk_menu_item_new_with_label("Solve");
2560 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2561 g_signal_connect(G_OBJECT(menuitem), "activate",
2562 G_CALLBACK(menu_solve_event), fe);
2563 gtk_widget_show(menuitem);
2564 }
2565 add_menu_separator(GTK_CONTAINER(menu));
2566 add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
2567
2568 menuitem = gtk_menu_item_new_with_mnemonic("_Help");
2569 gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
2570 gtk_widget_show(menuitem);
2571
2572 menu = gtk_menu_new();
2573 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2574
2575 menuitem = gtk_menu_item_new_with_label("About");
2576 gtk_container_add(GTK_CONTAINER(menu), menuitem);
2577 g_signal_connect(G_OBJECT(menuitem), "activate",
2578 G_CALLBACK(menu_about_event), fe);
2579 gtk_widget_show(menuitem);
2580
2581#ifdef STYLUS_BASED
2582 menuitem=gtk_button_new_with_mnemonic("_Redo");
2583 g_object_set_data(G_OBJECT(menuitem), "user-data",
2584 GINT_TO_POINTER((int)('r')));
2585 g_signal_connect(G_OBJECT(menuitem), "clicked",
2586 G_CALLBACK(menu_key_event), fe);
2587 gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0);
2588 gtk_widget_show(menuitem);
2589
2590 menuitem=gtk_button_new_with_mnemonic("_Undo");
2591 g_object_set_data(G_OBJECT(menuitem), "user-data",
2592 GINT_TO_POINTER((int)('u')));
2593 g_signal_connect(G_OBJECT(menuitem), "clicked",
2594 G_CALLBACK(menu_key_event), fe);
2595 gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0);
2596 gtk_widget_show(menuitem);
2597
2598 if (thegame.flags & REQUIRE_NUMPAD) {
2599 hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
2600 gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0);
2601 gtk_widget_show(GTK_WIDGET(hbox));
2602
2603 *((int*)errbuf)=0;
2604 errbuf[1]='\0';
2605 for(errbuf[0]='0';errbuf[0]<='9';errbuf[0]++) {
2606 menuitem=gtk_button_new_with_label(errbuf);
2607 g_object_set_data(G_OBJECT(menuitem), "user-data",
2608 GINT_TO_POINTER((int)(errbuf[0])));
2609 g_signal_connect(G_OBJECT(menuitem), "clicked",
2610 G_CALLBACK(menu_key_event), fe);
2611 gtk_box_pack_start(hbox, menuitem, TRUE, TRUE, 0);
2612 gtk_widget_show(menuitem);
2613 }
2614 }
2615#endif /* STYLUS_BASED */
2616
2617 changed_preset(fe);
2618
2619 snaffle_colours(fe);
2620
2621 if (midend_wants_statusbar(fe->me)) {
2622 GtkWidget *viewport;
2623 GtkRequisition req;
2624
2625 viewport = gtk_viewport_new(NULL, NULL);
2626 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
2627 fe->statusbar = gtk_statusbar_new();
2628 gtk_container_add(GTK_CONTAINER(viewport), fe->statusbar);
2629 gtk_widget_show(viewport);
2630 gtk_box_pack_end(vbox, viewport, FALSE, FALSE, 0);
2631 gtk_widget_show(fe->statusbar);
2632 fe->statusctx = gtk_statusbar_get_context_id
2633 (GTK_STATUSBAR(fe->statusbar), "game");
2634 gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx,
2635 DEFAULT_STATUSBAR_TEXT);
2636#if GTK_CHECK_VERSION(3,0,0)
2637 gtk_widget_get_preferred_size(fe->statusbar, &req, NULL);
2638#else
2639 gtk_widget_size_request(fe->statusbar, &req);
2640#endif
2641 gtk_widget_set_size_request(viewport, -1, req.height);
2642 } else
2643 fe->statusbar = NULL;
2644
2645 fe->area = gtk_drawing_area_new();
2646#if GTK_CHECK_VERSION(2,0,0) && !GTK_CHECK_VERSION(3,0,0)
2647 gtk_widget_set_double_buffered(fe->area, FALSE);
2648#endif
2649 {
2650 GdkGeometry geom;
2651 geom.base_width = geom.base_height = 0;
2652 gtk_window_set_geometry_hints(GTK_WINDOW(fe->window), fe->area,
2653 &geom, GDK_HINT_BASE_SIZE);
2654 }
2655 fe->w = -1;
2656 fe->h = -1;
2657 get_size(fe, &x, &y);
2658#if GTK_CHECK_VERSION(3,0,0)
2659 gtk_window_set_default_geometry(GTK_WINDOW(fe->window), x, y);
2660#else
2661 fe->drawing_area_shrink_pending = FALSE;
2662 gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
2663#endif
2664
2665 gtk_box_pack_end(vbox, fe->area, TRUE, TRUE, 0);
2666
2667 clear_backing_store(fe);
2668 fe->fonts = NULL;
2669 fe->nfonts = fe->fontsize = 0;
2670
2671 fe->paste_data = NULL;
2672 fe->paste_data_len = 0;
2673
2674 g_signal_connect(G_OBJECT(fe->window), "destroy",
2675 G_CALLBACK(destroy), fe);
2676 g_signal_connect(G_OBJECT(fe->window), "key_press_event",
2677 G_CALLBACK(key_event), fe);
2678 g_signal_connect(G_OBJECT(fe->area), "button_press_event",
2679 G_CALLBACK(button_event), fe);
2680 g_signal_connect(G_OBJECT(fe->area), "button_release_event",
2681 G_CALLBACK(button_event), fe);
2682 g_signal_connect(G_OBJECT(fe->area), "motion_notify_event",
2683 G_CALLBACK(motion_event), fe);
2684 g_signal_connect(G_OBJECT(fe->area), "selection_get",
2685 G_CALLBACK(selection_get), fe);
2686 g_signal_connect(G_OBJECT(fe->area), "selection_clear_event",
2687 G_CALLBACK(selection_clear), fe);
2688#if GTK_CHECK_VERSION(3,0,0)
2689 g_signal_connect(G_OBJECT(fe->area), "draw",
2690 G_CALLBACK(draw_area), fe);
2691#else
2692 g_signal_connect(G_OBJECT(fe->area), "expose_event",
2693 G_CALLBACK(expose_area), fe);
2694#endif
2695 g_signal_connect(G_OBJECT(fe->window), "map_event",
2696 G_CALLBACK(map_window), fe);
2697 g_signal_connect(G_OBJECT(fe->area), "configure_event",
2698 G_CALLBACK(configure_area), fe);
2699 g_signal_connect(G_OBJECT(fe->window), "configure_event",
2700 G_CALLBACK(configure_window), fe);
2701
2702 gtk_widget_add_events(GTK_WIDGET(fe->area),
2703 GDK_BUTTON_PRESS_MASK |
2704 GDK_BUTTON_RELEASE_MASK |
2705 GDK_BUTTON_MOTION_MASK |
2706 GDK_POINTER_MOTION_HINT_MASK);
2707
2708 if (n_xpm_icons) {
2709 gtk_window_set_icon(GTK_WINDOW(fe->window),
2710 gdk_pixbuf_new_from_xpm_data
2711 ((const gchar **)xpm_icons[0]));
2712
2713 iconlist = NULL;
2714 for (n = 0; n < n_xpm_icons; n++) {
2715 iconlist =
2716 g_list_append(iconlist,
2717 gdk_pixbuf_new_from_xpm_data((const gchar **)
2718 xpm_icons[n]));
2719 }
2720 gtk_window_set_icon_list(GTK_WINDOW(fe->window), iconlist);
2721 }
2722
2723 gtk_widget_show(fe->area);
2724 gtk_widget_show(fe->window);
2725
2726#if !GTK_CHECK_VERSION(3,0,0)
2727 fe->drawing_area_shrink_pending = TRUE;
2728 try_shrink_drawing_area(fe);
2729#endif
2730
2731 set_window_background(fe, 0);
2732
2733 return fe;
2734}
2735
2736char *fgetline(FILE *fp)
2737{
2738 char *ret = snewn(512, char);
2739 int size = 512, len = 0;
2740 while (fgets(ret + len, size - len, fp)) {
2741 len += strlen(ret + len);
2742 if (ret[len-1] == '\n')
2743 break; /* got a newline, we're done */
2744 size = len + 512;
2745 ret = sresize(ret, size, char);
2746 }
2747 if (len == 0) { /* first fgets returned NULL */
2748 sfree(ret);
2749 return NULL;
2750 }
2751 ret[len] = '\0';
2752 return ret;
2753}
2754
2755int main(int argc, char **argv)
2756{
2757 char *pname = argv[0];
2758 char *error;
2759 int ngenerate = 0, print = FALSE, px = 1, py = 1;
2760 int time_generation = FALSE, test_solve = FALSE, list_presets = FALSE;
2761 int soln = FALSE, colour = FALSE;
2762 float scale = 1.0F;
2763 float redo_proportion = 0.0F;
2764 char *savefile = NULL, *savesuffix = NULL;
2765 char *arg = NULL;
2766 int argtype = ARG_EITHER;
2767 char *screenshot_file = NULL;
2768 int doing_opts = TRUE;
2769 int ac = argc;
2770 char **av = argv;
2771 char errbuf[500];
2772
2773 /*
2774 * Command line parsing in this function is rather fiddly,
2775 * because GTK wants to have a go at argc/argv _first_ - and
2776 * yet we can't let it, because gtk_init() will bomb out if it
2777 * can't open an X display, whereas in fact we want to permit
2778 * our --generate and --print modes to run without an X
2779 * display.
2780 *
2781 * So what we do is:
2782 * - we parse the command line ourselves, without modifying
2783 * argc/argv
2784 * - if we encounter an error which might plausibly be the
2785 * result of a GTK command line (i.e. not detailed errors in
2786 * particular options of ours) we store the error message
2787 * and terminate parsing.
2788 * - if we got enough out of the command line to know it
2789 * specifies a non-X mode of operation, we either display
2790 * the stored error and return failure, or if there is no
2791 * stored error we do the non-X operation and return
2792 * success.
2793 * - otherwise, we go straight to gtk_init().
2794 */
2795
2796 errbuf[0] = '\0';
2797 while (--ac > 0) {
2798 char *p = *++av;
2799 if (doing_opts && !strcmp(p, "--version")) {
2800 printf("%s, from Simon Tatham's Portable Puzzle Collection\n%s\n",
2801 thegame.name, ver);
2802 return 0;
2803 } else if (doing_opts && !strcmp(p, "--generate")) {
2804 if (--ac > 0) {
2805 ngenerate = atoi(*++av);
2806 if (!ngenerate) {
2807 fprintf(stderr, "%s: '--generate' expected a number\n",
2808 pname);
2809 return 1;
2810 }
2811 } else
2812 ngenerate = 1;
2813 } else if (doing_opts && !strcmp(p, "--time-generation")) {
2814 time_generation = TRUE;
2815 } else if (doing_opts && !strcmp(p, "--test-solve")) {
2816 test_solve = TRUE;
2817 } else if (doing_opts && !strcmp(p, "--list-presets")) {
2818 list_presets = TRUE;
2819 } else if (doing_opts && !strcmp(p, "--save")) {
2820 if (--ac > 0) {
2821 savefile = *++av;
2822 } else {
2823 fprintf(stderr, "%s: '--save' expected a filename\n",
2824 pname);
2825 return 1;
2826 }
2827 } else if (doing_opts && (!strcmp(p, "--save-suffix") ||
2828 !strcmp(p, "--savesuffix"))) {
2829 if (--ac > 0) {
2830 savesuffix = *++av;
2831 } else {
2832 fprintf(stderr, "%s: '--save-suffix' expected a filename\n",
2833 pname);
2834 return 1;
2835 }
2836 } else if (doing_opts && !strcmp(p, "--print")) {
2837 if (!thegame.can_print) {
2838 fprintf(stderr, "%s: this game does not support printing\n",
2839 pname);
2840 return 1;
2841 }
2842 print = TRUE;
2843 if (--ac > 0) {
2844 char *dim = *++av;
2845 if (sscanf(dim, "%dx%d", &px, &py) != 2) {
2846 fprintf(stderr, "%s: unable to parse argument '%s' to "
2847 "'--print'\n", pname, dim);
2848 return 1;
2849 }
2850 } else {
2851 px = py = 1;
2852 }
2853 } else if (doing_opts && !strcmp(p, "--scale")) {
2854 if (--ac > 0) {
2855 scale = atof(*++av);
2856 } else {
2857 fprintf(stderr, "%s: no argument supplied to '--scale'\n",
2858 pname);
2859 return 1;
2860 }
2861 } else if (doing_opts && !strcmp(p, "--redo")) {
2862 /*
2863 * This is an internal option which I don't expect
2864 * users to have any particular use for. The effect of
2865 * --redo is that once the game has been loaded and
2866 * initialised, the next move in the redo chain is
2867 * replayed, and the game screen is redrawn part way
2868 * through the making of the move. This is only
2869 * meaningful if there _is_ a next move in the redo
2870 * chain, which means in turn that this option is only
2871 * useful if you're also passing a save file on the
2872 * command line.
2873 *
2874 * This option is used by the script which generates
2875 * the puzzle icons and website screenshots, and I
2876 * don't imagine it's useful for anything else.
2877 * (Unless, I suppose, users don't like my screenshots
2878 * and want to generate their own in the same way for
2879 * some repackaged version of the puzzles.)
2880 */
2881 if (--ac > 0) {
2882 redo_proportion = atof(*++av);
2883 } else {
2884 fprintf(stderr, "%s: no argument supplied to '--redo'\n",
2885 pname);
2886 return 1;
2887 }
2888 } else if (doing_opts && !strcmp(p, "--screenshot")) {
2889 /*
2890 * Another internal option for the icon building
2891 * script. This causes a screenshot of the central
2892 * drawing area (i.e. not including the menu bar or
2893 * status bar) to be saved to a PNG file once the
2894 * window has been drawn, and then the application
2895 * quits immediately.
2896 */
2897 if (--ac > 0) {
2898 screenshot_file = *++av;
2899 } else {
2900 fprintf(stderr, "%s: no argument supplied to '--screenshot'\n",
2901 pname);
2902 return 1;
2903 }
2904 } else if (doing_opts && (!strcmp(p, "--with-solutions") ||
2905 !strcmp(p, "--with-solution") ||
2906 !strcmp(p, "--with-solns") ||
2907 !strcmp(p, "--with-soln") ||
2908 !strcmp(p, "--solutions") ||
2909 !strcmp(p, "--solution") ||
2910 !strcmp(p, "--solns") ||
2911 !strcmp(p, "--soln"))) {
2912 soln = TRUE;
2913 } else if (doing_opts && !strcmp(p, "--colour")) {
2914 if (!thegame.can_print_in_colour) {
2915 fprintf(stderr, "%s: this game does not support colour"
2916 " printing\n", pname);
2917 return 1;
2918 }
2919 colour = TRUE;
2920 } else if (doing_opts && !strcmp(p, "--load")) {
2921 argtype = ARG_SAVE;
2922 } else if (doing_opts && !strcmp(p, "--game")) {
2923 argtype = ARG_ID;
2924 } else if (doing_opts && !strcmp(p, "--")) {
2925 doing_opts = FALSE;
2926 } else if (!doing_opts || p[0] != '-') {
2927 if (arg) {
2928 fprintf(stderr, "%s: more than one argument supplied\n",
2929 pname);
2930 return 1;
2931 }
2932 arg = p;
2933 } else {
2934 sprintf(errbuf, "%.100s: unrecognised option '%.100s'\n",
2935 pname, p);
2936 break;
2937 }
2938 }
2939
2940 /*
2941 * Special standalone mode for generating puzzle IDs on the
2942 * command line. Useful for generating puzzles to be printed
2943 * out and solved offline (for puzzles where that even makes
2944 * sense - Solo, for example, is a lot more pencil-and-paper
2945 * friendly than Twiddle!)
2946 *
2947 * Usage:
2948 *
2949 * <puzzle-name> --generate [<n> [<params>]]
2950 *
2951 * <n>, if present, is the number of puzzle IDs to generate.
2952 * <params>, if present, is the same type of parameter string
2953 * you would pass to the puzzle when running it in GUI mode,
2954 * including optional extras such as the expansion factor in
2955 * Rectangles and the difficulty level in Solo.
2956 *
2957 * If you specify <params>, you must also specify <n> (although
2958 * you may specify it to be 1). Sorry; that was the
2959 * simplest-to-parse command-line syntax I came up with.
2960 */
2961 if (ngenerate > 0 || print || savefile || savesuffix) {
2962 int i, n = 1;
2963 midend *me;
2964 char *id;
2965 document *doc = NULL;
2966
2967 /*
2968 * If we're in this branch, we should display any pending
2969 * error message from the command line, since GTK isn't going
2970 * to take another crack at making sense of it.
2971 */
2972 if (*errbuf) {
2973 fputs(errbuf, stderr);
2974 return 1;
2975 }
2976
2977 n = ngenerate;
2978
2979 me = midend_new(NULL, &thegame, NULL, NULL);
2980 i = 0;
2981
2982 if (savefile && !savesuffix)
2983 savesuffix = "";
2984 if (!savefile && savesuffix)
2985 savefile = "";
2986
2987 if (print)
2988 doc = document_new(px, py, scale);
2989
2990 /*
2991 * In this loop, we either generate a game ID or read one
2992 * from stdin depending on whether we're in generate mode;
2993 * then we either write it to stdout or print it, depending
2994 * on whether we're in print mode. Thus, this loop handles
2995 * generate-to-stdout, print-from-stdin and generate-and-
2996 * immediately-print modes.
2997 *
2998 * (It could also handle a copy-stdin-to-stdout mode,
2999 * although there's currently no combination of options
3000 * which will cause this loop to be activated in that mode.
3001 * It wouldn't be _entirely_ pointless, though, because
3002 * stdin could contain bare params strings or random-seed
3003 * IDs, and stdout would contain nothing but fully
3004 * generated descriptive game IDs.)
3005 */
3006 while (ngenerate == 0 || i < n) {
3007 char *pstr, *err, *seed;
3008 struct rusage before, after;
3009
3010 if (ngenerate == 0) {
3011 pstr = fgetline(stdin);
3012 if (!pstr)
3013 break;
3014 pstr[strcspn(pstr, "\r\n")] = '\0';
3015 } else {
3016 if (arg) {
3017 pstr = snewn(strlen(arg) + 40, char);
3018
3019 strcpy(pstr, arg);
3020 if (i > 0 && strchr(arg, '#'))
3021 sprintf(pstr + strlen(pstr), "-%d", i);
3022 } else
3023 pstr = NULL;
3024 }
3025
3026 if (pstr) {
3027 err = midend_game_id(me, pstr);
3028 if (err) {
3029 fprintf(stderr, "%s: error parsing '%s': %s\n",
3030 pname, pstr, err);
3031 return 1;
3032 }
3033 }
3034
3035 if (time_generation)
3036 getrusage(RUSAGE_SELF, &before);
3037
3038 midend_new_game(me);
3039
3040 seed = midend_get_random_seed(me);
3041
3042 if (time_generation) {
3043 double elapsed;
3044
3045 getrusage(RUSAGE_SELF, &after);
3046
3047 elapsed = (after.ru_utime.tv_sec -
3048 before.ru_utime.tv_sec);
3049 elapsed += (after.ru_utime.tv_usec -
3050 before.ru_utime.tv_usec) / 1000000.0;
3051
3052 printf("%s %s: %.6f\n", thegame.name, seed, elapsed);
3053 }
3054
3055 if (test_solve && thegame.can_solve) {
3056 /*
3057 * Now destroy the aux_info in the midend, by means of
3058 * re-entering the same game id, and then try to solve
3059 * it.
3060 */
3061 char *game_id, *err;
3062
3063 game_id = midend_get_game_id(me);
3064 err = midend_game_id(me, game_id);
3065 if (err) {
3066 fprintf(stderr, "%s %s: game id re-entry error: %s\n",
3067 thegame.name, seed, err);
3068 return 1;
3069 }
3070 midend_new_game(me);
3071 sfree(game_id);
3072
3073 err = midend_solve(me);
3074 /*
3075 * If the solve operation returned the error "Solution
3076 * not known for this puzzle", that's OK, because that
3077 * just means it's a puzzle for which we don't have an
3078 * algorithmic solver and hence can't solve it without
3079 * the aux_info, e.g. Netslide. Any other error is a
3080 * problem, though.
3081 */
3082 if (err && strcmp(err, "Solution not known for this puzzle")) {
3083 fprintf(stderr, "%s %s: solve error: %s\n",
3084 thegame.name, seed, err);
3085 return 1;
3086 }
3087 }
3088
3089 sfree(pstr);
3090 sfree(seed);
3091
3092 if (doc) {
3093 err = midend_print_puzzle(me, doc, soln);
3094 if (err) {
3095 fprintf(stderr, "%s: error in printing: %s\n", pname, err);
3096 return 1;
3097 }
3098 }
3099 if (savefile) {
3100 struct savefile_write_ctx ctx;
3101 char *realname = snewn(40 + strlen(savefile) +
3102 strlen(savesuffix), char);
3103 sprintf(realname, "%s%d%s", savefile, i, savesuffix);
3104
3105 if (soln) {
3106 char *err = midend_solve(me);
3107 if (err) {
3108 fprintf(stderr, "%s: unable to show solution: %s\n",
3109 realname, err);
3110 return 1;
3111 }
3112 }
3113
3114 ctx.fp = fopen(realname, "w");
3115 if (!ctx.fp) {
3116 fprintf(stderr, "%s: open: %s\n", realname,
3117 strerror(errno));
3118 return 1;
3119 }
3120 ctx.error = 0;
3121 midend_serialise(me, savefile_write, &ctx);
3122 if (ctx.error) {
3123 fprintf(stderr, "%s: write: %s\n", realname,
3124 strerror(ctx.error));
3125 return 1;
3126 }
3127 if (fclose(ctx.fp)) {
3128 fprintf(stderr, "%s: close: %s\n", realname,
3129 strerror(errno));
3130 return 1;
3131 }
3132 sfree(realname);
3133 }
3134 if (!doc && !savefile && !time_generation) {
3135 id = midend_get_game_id(me);
3136 puts(id);
3137 sfree(id);
3138 }
3139
3140 i++;
3141 }
3142
3143 if (doc) {
3144 psdata *ps = ps_init(stdout, colour);
3145 document_print(doc, ps_drawing_api(ps));
3146 document_free(doc);
3147 ps_free(ps);
3148 }
3149
3150 midend_free(me);
3151
3152 return 0;
3153 } else if (list_presets) {
3154 /*
3155 * Another specialist mode which causes the puzzle to list the
3156 * game_params strings for all its preset configurations.
3157 */
3158 int i, npresets;
3159 midend *me;
3160
3161 me = midend_new(NULL, &thegame, NULL, NULL);
3162 npresets = midend_num_presets(me);
3163
3164 for (i = 0; i < npresets; i++) {
3165 game_params *params;
3166 char *name, *paramstr;
3167
3168 midend_fetch_preset(me, i, &name, &params);
3169 paramstr = thegame.encode_params(params, TRUE);
3170
3171 printf("%s %s\n", paramstr, name);
3172 sfree(paramstr);
3173 }
3174
3175 midend_free(me);
3176 return 0;
3177 } else {
3178 frontend *fe;
3179
3180 gtk_init(&argc, &argv);
3181
3182 fe = new_window(arg, argtype, &error);
3183
3184 if (!fe) {
3185 fprintf(stderr, "%s: %s\n", pname, error);
3186 return 1;
3187 }
3188
3189 if (screenshot_file) {
3190 /*
3191 * Some puzzles will not redraw their entire area if
3192 * given a partially completed animation, which means
3193 * we must redraw now and _then_ redraw again after
3194 * freezing the move timer.
3195 */
3196 midend_force_redraw(fe->me);
3197 }
3198
3199 if (redo_proportion) {
3200 /* Start a redo. */
3201 midend_process_key(fe->me, 0, 0, 'r');
3202 /* And freeze the timer at the specified position. */
3203 midend_freeze_timer(fe->me, redo_proportion);
3204 }
3205
3206 if (screenshot_file) {
3207 save_screenshot_png(fe, screenshot_file);
3208 exit(0);
3209 }
3210
3211 gtk_main();
3212 }
3213
3214 return 0;
3215}
diff --git a/apps/plugins/puzzles/guess.R b/apps/plugins/puzzles/guess.R
new file mode 100644
index 0000000000..0e1a00c073
--- /dev/null
+++ b/apps/plugins/puzzles/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/guess.c b/apps/plugins/puzzles/guess.c
new file mode 100644
index 0000000000..d1187f3e3c
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/html/blackbox.html b/apps/plugins/puzzles/html/blackbox.html
new file mode 100644
index 0000000000..f98604f12f
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/bridges.html b/apps/plugins/puzzles/html/bridges.html
new file mode 100644
index 0000000000..06ec2d4d4b
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/cube.html b/apps/plugins/puzzles/html/cube.html
new file mode 100644
index 0000000000..f08e16c38d
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/dominosa.html b/apps/plugins/puzzles/html/dominosa.html
new file mode 100644
index 0000000000..d2f672806a
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/fifteen.html b/apps/plugins/puzzles/html/fifteen.html
new file mode 100644
index 0000000000..53053b440d
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/filling.html b/apps/plugins/puzzles/html/filling.html
new file mode 100644
index 0000000000..70ce16d4a9
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/flip.html b/apps/plugins/puzzles/html/flip.html
new file mode 100644
index 0000000000..404aae6b9a
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/flood.html b/apps/plugins/puzzles/html/flood.html
new file mode 100644
index 0000000000..cf09eac766
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/galaxies.html b/apps/plugins/puzzles/html/galaxies.html
new file mode 100644
index 0000000000..8041a95cee
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/group.html b/apps/plugins/puzzles/html/group.html
new file mode 100644
index 0000000000..c0f52de629
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/guess.html b/apps/plugins/puzzles/html/guess.html
new file mode 100644
index 0000000000..1e4fc1ed04
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/inertia.html b/apps/plugins/puzzles/html/inertia.html
new file mode 100644
index 0000000000..20077c0048
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/javapage.pl b/apps/plugins/puzzles/html/javapage.pl
new file mode 100755
index 0000000000..cd5e6a1669
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/jspage.pl b/apps/plugins/puzzles/html/jspage.pl
new file mode 100755
index 0000000000..19868bd948
--- /dev/null
+++ b/apps/plugins/puzzles/html/jspage.pl
@@ -0,0 +1,120 @@
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</head>
67<body onLoad="initPuzzle();">
68<h1 align=center>${puzzlename}</h1>
69${unfinishedheading}
70<h2 align=center>from Simon Tatham's Portable Puzzle Collection</h2>
71
72${unfinishedpara}
73
74<hr>
75<div id="puzzle" style="display: none">
76<p align=center>
77 <input type="button" id="new" value="New game">
78 <input type="button" id="restart" value="Restart game">
79 <input type="button" id="undo" value="Undo move">
80 <input type="button" id="redo" value="Redo move">
81 <input type="button" id="solve" value="Solve game">
82 <input type="button" id="specific" value="Enter game ID">
83 <input type="button" id="random" value="Enter random seed">
84 <select id="gametype"></select>
85</p>
86<div align=center>
87 <div id="resizable" style="position:relative; left:0; top:0">
88 <canvas style="display: block" id="puzzlecanvas" width="1px" height="1px" tabindex="1">
89 </canvas>
90 <div id="statusbarholder" style="display: block">
91 </div>
92 </div>
93 <p>
94 Link to this puzzle:
95 <a id="permalink-desc">by game ID</a>
96 <a id="permalink-seed">by random seed</a>
97 </p>
98</div>
99</div>
100<div id="apology">
101Sorry, this Javascript puzzle doesn't seem to work in your web
102browser. Perhaps you have Javascript disabled, or perhaps your browser
103doesn't provide a feature that the puzzle code requires (such as
104<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Typed_arrays">typed arrays</a>).
105These puzzles have been successfully run in Firefox 19, Chrome 26,
106Internet Explorer 10 and Safari 6.
107</div>
108<hr>
109
110${instructions}
111
112${links}
113
114${footer}
115</body>
116</html>
117EOF
118
119 close $outpage;
120}
diff --git a/apps/plugins/puzzles/html/keen.html b/apps/plugins/puzzles/html/keen.html
new file mode 100644
index 0000000000..bd0eb3644d
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/lightup.html b/apps/plugins/puzzles/html/lightup.html
new file mode 100644
index 0000000000..2de2f91bb9
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/loopy.html b/apps/plugins/puzzles/html/loopy.html
new file mode 100644
index 0000000000..96f3a9d908
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/magnets.html b/apps/plugins/puzzles/html/magnets.html
new file mode 100644
index 0000000000..2807569a6c
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/map.html b/apps/plugins/puzzles/html/map.html
new file mode 100644
index 0000000000..5f81793054
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/mines.html b/apps/plugins/puzzles/html/mines.html
new file mode 100644
index 0000000000..d17d6ffa80
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/net.html b/apps/plugins/puzzles/html/net.html
new file mode 100644
index 0000000000..08bffbac3e
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/netslide.html b/apps/plugins/puzzles/html/netslide.html
new file mode 100644
index 0000000000..f1877417d4
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/palisade.html b/apps/plugins/puzzles/html/palisade.html
new file mode 100644
index 0000000000..5b6b933104
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/pattern.html b/apps/plugins/puzzles/html/pattern.html
new file mode 100644
index 0000000000..54e05d6416
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/pearl.html b/apps/plugins/puzzles/html/pearl.html
new file mode 100644
index 0000000000..2ca25a5ee0
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/pegs.html b/apps/plugins/puzzles/html/pegs.html
new file mode 100644
index 0000000000..4a2378873e
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/range.html b/apps/plugins/puzzles/html/range.html
new file mode 100644
index 0000000000..bb5b59c4d2
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/rect.html b/apps/plugins/puzzles/html/rect.html
new file mode 100644
index 0000000000..d23d827663
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/samegame.html b/apps/plugins/puzzles/html/samegame.html
new file mode 100644
index 0000000000..e6de095210
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/signpost.html b/apps/plugins/puzzles/html/signpost.html
new file mode 100644
index 0000000000..fa23e99de0
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/singles.html b/apps/plugins/puzzles/html/singles.html
new file mode 100644
index 0000000000..252bffb380
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/sixteen.html b/apps/plugins/puzzles/html/sixteen.html
new file mode 100644
index 0000000000..4530469fe6
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/slant.html b/apps/plugins/puzzles/html/slant.html
new file mode 100644
index 0000000000..d6d31aa302
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/solo.html b/apps/plugins/puzzles/html/solo.html
new file mode 100644
index 0000000000..88ebd5cb29
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/tents.html b/apps/plugins/puzzles/html/tents.html
new file mode 100644
index 0000000000..e3f6d5f0ea
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/towers.html b/apps/plugins/puzzles/html/towers.html
new file mode 100644
index 0000000000..a710e0ab6e
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/tracks.html b/apps/plugins/puzzles/html/tracks.html
new file mode 100644
index 0000000000..afabed37ac
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/twiddle.html b/apps/plugins/puzzles/html/twiddle.html
new file mode 100644
index 0000000000..5f94e4e120
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/undead.html b/apps/plugins/puzzles/html/undead.html
new file mode 100644
index 0000000000..c21374f6dc
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/unequal.html b/apps/plugins/puzzles/html/unequal.html
new file mode 100644
index 0000000000..085f82effc
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/unruly.html b/apps/plugins/puzzles/html/unruly.html
new file mode 100644
index 0000000000..2cd3fb25f5
--- /dev/null
+++ b/apps/plugins/puzzles/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/html/untangle.html b/apps/plugins/puzzles/html/untangle.html
new file mode 100644
index 0000000000..7171a3d55d
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/Makefile b/apps/plugins/puzzles/icons/Makefile
new file mode 100644
index 0000000000..00dae1f841
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/blackbox.sav b/apps/plugins/puzzles/icons/blackbox.sav
new file mode 100644
index 0000000000..4483f3c81a
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/bridges.sav b/apps/plugins/puzzles/icons/bridges.sav
new file mode 100644
index 0000000000..ddae7085cd
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/cicon.pl b/apps/plugins/puzzles/icons/cicon.pl
new file mode 100755
index 0000000000..3578bd33fe
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/crop.sh b/apps/plugins/puzzles/icons/crop.sh
new file mode 100755
index 0000000000..0d15d3c9b9
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/cube.sav b/apps/plugins/puzzles/icons/cube.sav
new file mode 100644
index 0000000000..bb123f4e74
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/dominosa.sav b/apps/plugins/puzzles/icons/dominosa.sav
new file mode 100644
index 0000000000..5991f3e57e
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/fifteen.sav b/apps/plugins/puzzles/icons/fifteen.sav
new file mode 100644
index 0000000000..d81345a7d8
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/filling.sav b/apps/plugins/puzzles/icons/filling.sav
new file mode 100644
index 0000000000..caf0bb2d4c
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/flip.sav b/apps/plugins/puzzles/icons/flip.sav
new file mode 100644
index 0000000000..82b4c49357
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/flood.sav b/apps/plugins/puzzles/icons/flood.sav
new file mode 100644
index 0000000000..ac4adf7020
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/galaxies.sav b/apps/plugins/puzzles/icons/galaxies.sav
new file mode 100644
index 0000000000..42d18bc335
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/guess.sav b/apps/plugins/puzzles/icons/guess.sav
new file mode 100644
index 0000000000..69852bf769
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/icon.pl b/apps/plugins/puzzles/icons/icon.pl
new file mode 100755
index 0000000000..fcb1aa3e2c
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/inertia.sav b/apps/plugins/puzzles/icons/inertia.sav
new file mode 100644
index 0000000000..a6d6faed1b
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/keen.sav b/apps/plugins/puzzles/icons/keen.sav
new file mode 100644
index 0000000000..4adbd42d17
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/lightup.sav b/apps/plugins/puzzles/icons/lightup.sav
new file mode 100644
index 0000000000..21b59bdec4
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/loopy.sav b/apps/plugins/puzzles/icons/loopy.sav
new file mode 100644
index 0000000000..11611818af
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/magnets.sav b/apps/plugins/puzzles/icons/magnets.sav
new file mode 100644
index 0000000000..3c317b70ce
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/map.sav b/apps/plugins/puzzles/icons/map.sav
new file mode 100644
index 0000000000..33863e1eeb
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/mines.sav b/apps/plugins/puzzles/icons/mines.sav
new file mode 100644
index 0000000000..a827541163
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/net.sav b/apps/plugins/puzzles/icons/net.sav
new file mode 100644
index 0000000000..06a5426280
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/netslide.sav b/apps/plugins/puzzles/icons/netslide.sav
new file mode 100644
index 0000000000..f37178ee0c
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/palisade.sav b/apps/plugins/puzzles/icons/palisade.sav
new file mode 100644
index 0000000000..a935e890bb
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/pattern.sav b/apps/plugins/puzzles/icons/pattern.sav
new file mode 100644
index 0000000000..97c2396052
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/pearl.sav b/apps/plugins/puzzles/icons/pearl.sav
new file mode 100644
index 0000000000..730ca85149
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/pegs.sav b/apps/plugins/puzzles/icons/pegs.sav
new file mode 100644
index 0000000000..22b8a0d82f
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/range.sav b/apps/plugins/puzzles/icons/range.sav
new file mode 100644
index 0000000000..708e7db248
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/rect.sav b/apps/plugins/puzzles/icons/rect.sav
new file mode 100644
index 0000000000..17264daeeb
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/samegame.sav b/apps/plugins/puzzles/icons/samegame.sav
new file mode 100644
index 0000000000..f92b52d1b6
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/screenshot.sh b/apps/plugins/puzzles/icons/screenshot.sh
new file mode 100755
index 0000000000..0e2a06eea7
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/signpost.sav b/apps/plugins/puzzles/icons/signpost.sav
new file mode 100644
index 0000000000..9ad1958ddf
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/singles.sav b/apps/plugins/puzzles/icons/singles.sav
new file mode 100644
index 0000000000..260fd1f2b3
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/sixteen.sav b/apps/plugins/puzzles/icons/sixteen.sav
new file mode 100644
index 0000000000..076b1fbd4d
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/slant.sav b/apps/plugins/puzzles/icons/slant.sav
new file mode 100644
index 0000000000..02017e5d48
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/solo.sav b/apps/plugins/puzzles/icons/solo.sav
new file mode 100644
index 0000000000..385cc68fe5
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/square.pl b/apps/plugins/puzzles/icons/square.pl
new file mode 100755
index 0000000000..815b94b532
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/tents.sav b/apps/plugins/puzzles/icons/tents.sav
new file mode 100644
index 0000000000..292c2d2d75
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/towers.sav b/apps/plugins/puzzles/icons/towers.sav
new file mode 100644
index 0000000000..351d473a63
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/tracks.sav b/apps/plugins/puzzles/icons/tracks.sav
new file mode 100644
index 0000000000..ca30644506
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/twiddle.sav b/apps/plugins/puzzles/icons/twiddle.sav
new file mode 100644
index 0000000000..2863033f99
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/undead.sav b/apps/plugins/puzzles/icons/undead.sav
new file mode 100644
index 0000000000..3c314dde8a
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/unequal.sav b/apps/plugins/puzzles/icons/unequal.sav
new file mode 100644
index 0000000000..c414513a22
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/unruly.sav b/apps/plugins/puzzles/icons/unruly.sav
new file mode 100644
index 0000000000..0f7ca1b9dd
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/untangle.sav b/apps/plugins/puzzles/icons/untangle.sav
new file mode 100644
index 0000000000..016318a521
--- /dev/null
+++ b/apps/plugins/puzzles/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/icons/win16pal.xpm b/apps/plugins/puzzles/icons/win16pal.xpm
new file mode 100644
index 0000000000..66fd60a480
--- /dev/null
+++ b/apps/plugins/puzzles/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/inertia.R b/apps/plugins/puzzles/inertia.R
new file mode 100644
index 0000000000..e6e86beaec
--- /dev/null
+++ b/apps/plugins/puzzles/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/inertia.c b/apps/plugins/puzzles/inertia.c
new file mode 100644
index 0000000000..a0e1c45fb1
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/keen.R b/apps/plugins/puzzles/keen.R
new file mode 100644
index 0000000000..77609bc7fa
--- /dev/null
+++ b/apps/plugins/puzzles/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/keen.c b/apps/plugins/puzzles/keen.c
new file mode 100644
index 0000000000..32d6288f88
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/keymaps.h b/apps/plugins/puzzles/keymaps.h
new file mode 100644
index 0000000000..651ecca250
--- /dev/null
+++ b/apps/plugins/puzzles/keymaps.h
@@ -0,0 +1,206 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22#ifndef _XWORLD_KEYMAPS_H
23#define _XWORLD_KEYMAPS_H
24
25/* Handle the "nice" targets that have directional buttons with normal names */
26#if (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) || \
27 (CONFIG_KEYPAD == PHILIPS_HDD6330_PAD) || \
28 (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) || \
29 (CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD) || \
30 (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD) || \
31 (CONFIG_KEYPAD == SANSA_CONNECT_PAD) || \
32 (CONFIG_KEYPAD == SANSA_C200_PAD) || \
33 (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
34 (CONFIG_KEYPAD == SANSA_E200_PAD) || \
35 (CONFIG_KEYPAD == SANSA_FUZE_PAD) || \
36 (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD) || \
37 (CONFIG_KEYPAD == GIGABEAT_PAD) || \
38 (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
39 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) || \
40 (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
41 (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) || \
42 (CONFIG_KEYPAD == CREATIVE_ZEN_PAD) || \
43 (CONFIG_KEYPAD == SONY_NWZ_PAD) || \
44 (CONFIG_KEYPAD == CREATIVEZVM_PAD) || \
45 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD) || \
46 (CONFIG_KEYPAD == IRIVER_H300_PAD) || \
47 (CONFIG_KEYPAD == HM801_PAD) || \
48 (CONFIG_KEYPAD == HM60X_PAD)
49#define BTN_UP BUTTON_UP
50#define BTN_DOWN BUTTON_DOWN
51#define BTN_LEFT BUTTON_LEFT
52#define BTN_RIGHT BUTTON_RIGHT
53
54#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
55#define BTN_UP_LEFT BUTTON_BACK
56#define BTN_UP_RIGHT BUTTON_PLAYPAUSE
57#define BTN_DOWN_LEFT BUTTON_BOTTOMLEFT
58#define BTN_DOWN_RIGHT BUTTON_BOTTOMRIGHT
59#endif
60
61#if (CONFIG_KEYPAD == HM60X_PAD)
62#define BTN_FIRE BUTTON_POWER
63#define BTN_PAUSE BUTTON_SELECT
64#endif
65
66#if (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) || \
67 (CONFIG_KEYPAD == PHILIPS_HDD6330_PAD) || \
68 (CONFIG_KEYPAD == PHILIPS_SA9200_PAD) || \
69 (CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD) || \
70 (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD) || \
71 (CONFIG_KEYPAD == SANSA_CONNECT_PAD) || \
72 (CONFIG_KEYPAD == SANSA_C200_PAD) || \
73 (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD) || \
74 (CONFIG_KEYPAD == ONDAVX747_PAD)
75#define BTN_FIRE BUTTON_VOL_UP
76#define BTN_PAUSE BUTTON_VOL_DOWN
77
78#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
79#define BTN_FIRE BUTTON_HOME
80#define BTN_PAUSE BUTTON_SELECT
81
82#elif (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
83#define BTN_FIRE BUTTON_FFWD
84#define BTN_PAUSE BUTTON_REW
85
86#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
87#define BTN_FIRE BUTTON_REC
88#define BTN_PAUSE BUTTON_POWER
89
90#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
91#define BTN_FIRE BUTTON_SELECT
92#define BTN_PAUSE BUTTON_POWER
93
94#elif (CONFIG_KEYPAD == CREATIVE_ZEN_PAD)
95#define BTN_FIRE BUTTON_SELECT
96#define BTN_PAUSE BUTTON_BACK
97
98#elif (CONFIG_KEYPAD == CREATIVEZVM_PAD)
99#define BTN_FIRE BUTTON_PLAY
100#define BTN_PAUSE BUTTON_MENU
101
102#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
103#define BTN_FIRE BUTTON_USER
104#define BTN_PAUSE BUTTON_MENU
105
106#elif (CONFIG_KEYPAD == SONY_NWZ_PAD)
107#define BTN_FIRE BUTTON_PLAY
108#define BTN_PAUSE BUTTON_BACK
109
110#elif (CONFIG_KEYPAD == IRIVER_H300_PAD)
111#define BTN_FIRE BUTTON_REC
112#define BTN_PAUSE BUTTON_MODE
113
114#elif (CONFIG_KEYPAD == HM801_PAD)
115#define BTN_FIRE BUTTON_PREV
116#define BTN_PAUSE BUTTON_NEXT
117
118#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
119 (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
120#define BTN_FIRE BUTTON_REC
121#define BTN_PAUSE BUTTON_PLAY
122
123#elif (CONFIG_KEYPAD == GIGABEAT_PAD) || \
124 (CONFIG_KEYPAD == GIGABEAT_S_PAD)
125#define BTN_FIRE BUTTON_VOL_UP
126#define BTN_PAUSE BUTTON_MENU
127/* #if CONFIG_KEYPAD == PHILIPS_HDD1630_PAD */
128#endif
129
130/* ... and now for the bad ones that don't have
131 * standard names for the directional buttons */
132#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
133#define BTN_UP BUTTON_OK
134#define BTN_DOWN BUTTON_CANCEL
135#define BTN_LEFT BUTTON_MENU
136#define BTN_RIGHT BUTTON_PLAY
137#define BTN_FIRE BUTTON_POWER
138#define BTN_PAUSE BUTTON_REC
139
140#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
141#define BTN_UP BUTTON_SCROLL_UP
142#define BTN_DOWN BUTTON_SCROLL_DOWN
143#define BTN_LEFT BUTTON_LEFT
144#define BTN_RIGHT BUTTON_RIGHT
145#define BTN_FIRE BUTTON_REW
146#define BTN_PAUSE BUTTON_PLAY
147
148#elif (CONFIG_KEYPAD == MROBE500_PAD)
149#define BTN_FIRE BUTTON_POWER
150
151#elif (CONFIG_KEYPAD == MROBE_REMOTE)
152#define BTN_UP BUTTON_RC_PLAY
153#define BTN_DOWN BUTTON_RC_DOWN
154#define BTN_LEFT BUTTON_RC_REW
155#define BTN_RIGHT BUTTON_RC_FF
156
157#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
158 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
159 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
160#define BTN_UP BUTTON_MENU
161#define BTN_DOWN BUTTON_PLAY
162#define BTN_LEFT BUTTON_LEFT
163#define BTN_RIGHT BUTTON_RIGHT
164#define BTN_FIRE BUTTON_SELECT
165#define BTN_PAUSE (BUTTON_MENU | BUTTON_SELECT)
166
167#elif (CONFIG_KEYPAD == ONDAVX777_PAD)
168#define BTN_FIRE BUTTON_POWER
169
170#elif (CONFIG_KEYPAD == COWON_D2_PAD)
171#define BTN_FIRE BUTTON_PLUS
172#define BTN_PAUSE BUTTON_MINUS
173
174#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
175 (CONFIG_KEYPAD == DX50_PAD)
176#define BTN_LEFT BUTTON_LEFT
177#define BTN_RIGHT BUTTON_RIGHT
178#define BTN_FIRE BUTTON_BOTTOMLEFT
179#define BTN_PAUSE BUTTON_TOPLEFT
180
181#else
182#error Unsupported keypad
183#endif
184
185#ifdef HAVE_TOUCHSCREEN
186#define BTN_UP BUTTON_TOPMIDDLE
187#define BTN_DOWN BUTTON_BOTTOMMIDDLE
188#define BTN_LEFT BUTTON_LEFT
189#define BTN_RIGHT BUTTON_RIGHT
190
191#if (CONFIG_KEYPAD == MROBE500_PAD) || \
192 (CONFIG_KEYPAD == ONDAVX777_PAD)
193#define BTN_PAUSE BUTTON_BOTTOMLEFT
194
195#elif (CONFIG_KEYPAD != COWON_D2_PAD) && \
196 (CONFIG_KEYPAD != DX50_PAD) && \
197 (CONFIG_KEYPAD != ONDAVX777_PAD)
198#define BTN_FIRE BUTTON_BOTTOMLEFT
199#define BTN_PAUSE BUTTON_TOPLEFT
200#endif
201
202/* HAVE_TOUCHSCREEN */
203#endif
204
205/* _XWORLD_KEYMAPS_H */
206#endif
diff --git a/apps/plugins/puzzles/latin.c b/apps/plugins/puzzles/latin.c
new file mode 100644
index 0000000000..f7e97a7fa6
--- /dev/null
+++ b/apps/plugins/puzzles/latin.c
@@ -0,0 +1,1436 @@
1#include "rbassert.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/latin.h b/apps/plugins/puzzles/latin.h
new file mode 100644
index 0000000000..4b09f16ce1
--- /dev/null
+++ b/apps/plugins/puzzles/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/laydomino.c b/apps/plugins/puzzles/laydomino.c
new file mode 100644
index 0000000000..458027020b
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/lightup.R b/apps/plugins/puzzles/lightup.R
new file mode 100644
index 0000000000..a474de815d
--- /dev/null
+++ b/apps/plugins/puzzles/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/lightup.c b/apps/plugins/puzzles/lightup.c
new file mode 100644
index 0000000000..9ca37b19d2
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/list.c b/apps/plugins/puzzles/list.c
new file mode 100644
index 0000000000..ec019c31b2
--- /dev/null
+++ b/apps/plugins/puzzles/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/loopgen.c b/apps/plugins/puzzles/loopgen.c
new file mode 100644
index 0000000000..25b63906fa
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/loopgen.h b/apps/plugins/puzzles/loopgen.h
new file mode 100644
index 0000000000..079c87c576
--- /dev/null
+++ b/apps/plugins/puzzles/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/loopy.R b/apps/plugins/puzzles/loopy.R
new file mode 100644
index 0000000000..f44560095d
--- /dev/null
+++ b/apps/plugins/puzzles/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/loopy.c b/apps/plugins/puzzles/loopy.c
new file mode 100644
index 0000000000..a931b31c37
--- /dev/null
+++ b/apps/plugins/puzzles/loopy.c
@@ -0,0 +1,3688 @@
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 "rbassert.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/* ------- List of grid generators ------- */
247#define GRIDLIST(A) \
248 A(Squares,GRID_SQUARE,3,3) \
249 A(Triangular,GRID_TRIANGULAR,3,3) \
250 A(Honeycomb,GRID_HONEYCOMB,3,3) \
251 A(Snub-Square,GRID_SNUBSQUARE,3,3) \
252 A(Cairo,GRID_CAIRO,3,4) \
253 A(Great-Hexagonal,GRID_GREATHEXAGONAL,3,3) \
254 A(Octagonal,GRID_OCTAGONAL,3,3) \
255 A(Kites,GRID_KITE,3,3) \
256 A(Floret,GRID_FLORET,1,2) \
257 A(Dodecagonal,GRID_DODECAGONAL,2,2) \
258 A(Great-Dodecagonal,GRID_GREATDODECAGONAL,2,2) \
259 A(Penrose (kite/dart),GRID_PENROSE_P2,3,3) \
260 A(Penrose (rhombs),GRID_PENROSE_P3,3,3)
261
262#define GRID_NAME(title,type,amin,omin) #title,
263#define GRID_CONFIG(title,type,amin,omin) ":" #title
264#define GRID_TYPE(title,type,amin,omin) type,
265#define GRID_SIZES(title,type,amin,omin) \
266 {amin, omin, \
267 "Width and height for this grid type must both be at least " #amin, \
268 "At least one of width and height for this grid type must be at least " #omin,},
269static char const *const gridnames[] = { GRIDLIST(GRID_NAME) };
270#define GRID_CONFIGS GRIDLIST(GRID_CONFIG)
271static grid_type grid_types[] = { GRIDLIST(GRID_TYPE) };
272#define NUM_GRID_TYPES (sizeof(grid_types) / sizeof(grid_types[0]))
273static const struct {
274 int amin, omin;
275 char *aerr, *oerr;
276} grid_size_limits[] = { GRIDLIST(GRID_SIZES) };
277
278/* Generates a (dynamically allocated) new grid, according to the
279 * type and size requested in params. Does nothing if the grid is already
280 * generated. */
281static grid *loopy_generate_grid(const game_params *params,
282 const char *grid_desc)
283{
284 return grid_new(grid_types[params->type], params->w, params->h, grid_desc);
285}
286
287/* ----------------------------------------------------------------------
288 * Preprocessor magic
289 */
290
291/* General constants */
292#define PREFERRED_TILE_SIZE 32
293#define BORDER(tilesize) ((tilesize) / 2)
294#define FLASH_TIME 0.5F
295
296#define BIT_SET(field, bit) ((field) & (1<<(bit)))
297
298#define SET_BIT(field, bit) (BIT_SET(field, bit) ? FALSE : \
299 ((field) |= (1<<(bit)), TRUE))
300
301#define CLEAR_BIT(field, bit) (BIT_SET(field, bit) ? \
302 ((field) &= ~(1<<(bit)), TRUE) : FALSE)
303
304#define CLUE2CHAR(c) \
305 ((c < 0) ? ' ' : c < 10 ? c + '0' : c - 10 + 'A')
306
307/* ----------------------------------------------------------------------
308 * General struct manipulation and other straightforward code
309 */
310
311static game_state *dup_game(const game_state *state)
312{
313 game_state *ret = snew(game_state);
314
315 ret->game_grid = state->game_grid;
316 ret->game_grid->refcount++;
317
318 ret->solved = state->solved;
319 ret->cheated = state->cheated;
320
321 ret->clues = snewn(state->game_grid->num_faces, signed char);
322 memcpy(ret->clues, state->clues, state->game_grid->num_faces);
323
324 ret->lines = snewn(state->game_grid->num_edges, char);
325 memcpy(ret->lines, state->lines, state->game_grid->num_edges);
326
327 ret->line_errors = snewn(state->game_grid->num_edges, unsigned char);
328 memcpy(ret->line_errors, state->line_errors, state->game_grid->num_edges);
329 ret->exactly_one_loop = state->exactly_one_loop;
330
331 ret->grid_type = state->grid_type;
332 return ret;
333}
334
335static void free_game(game_state *state)
336{
337 if (state) {
338 grid_free(state->game_grid);
339 sfree(state->clues);
340 sfree(state->lines);
341 sfree(state->line_errors);
342 sfree(state);
343 }
344}
345
346static solver_state *new_solver_state(const game_state *state, int diff) {
347 int i;
348 int num_dots = state->game_grid->num_dots;
349 int num_faces = state->game_grid->num_faces;
350 int num_edges = state->game_grid->num_edges;
351 solver_state *ret = snew(solver_state);
352
353 ret->state = dup_game(state);
354
355 ret->solver_status = SOLVER_INCOMPLETE;
356 ret->diff = diff;
357
358 ret->dotdsf = snew_dsf(num_dots);
359 ret->looplen = snewn(num_dots, int);
360
361 for (i = 0; i < num_dots; i++) {
362 ret->looplen[i] = 1;
363 }
364
365 ret->dot_solved = snewn(num_dots, char);
366 ret->face_solved = snewn(num_faces, char);
367 memset(ret->dot_solved, FALSE, num_dots);
368 memset(ret->face_solved, FALSE, num_faces);
369
370 ret->dot_yes_count = snewn(num_dots, char);
371 memset(ret->dot_yes_count, 0, num_dots);
372 ret->dot_no_count = snewn(num_dots, char);
373 memset(ret->dot_no_count, 0, num_dots);
374 ret->face_yes_count = snewn(num_faces, char);
375 memset(ret->face_yes_count, 0, num_faces);
376 ret->face_no_count = snewn(num_faces, char);
377 memset(ret->face_no_count, 0, num_faces);
378
379 if (diff < DIFF_NORMAL) {
380 ret->dlines = NULL;
381 } else {
382 ret->dlines = snewn(2*num_edges, char);
383 memset(ret->dlines, 0, 2*num_edges);
384 }
385
386 if (diff < DIFF_HARD) {
387 ret->linedsf = NULL;
388 } else {
389 ret->linedsf = snew_dsf(state->game_grid->num_edges);
390 }
391
392 return ret;
393}
394
395static void free_solver_state(solver_state *sstate) {
396 if (sstate) {
397 free_game(sstate->state);
398 sfree(sstate->dotdsf);
399 sfree(sstate->looplen);
400 sfree(sstate->dot_solved);
401 sfree(sstate->face_solved);
402 sfree(sstate->dot_yes_count);
403 sfree(sstate->dot_no_count);
404 sfree(sstate->face_yes_count);
405 sfree(sstate->face_no_count);
406
407 /* OK, because sfree(NULL) is a no-op */
408 sfree(sstate->dlines);
409 sfree(sstate->linedsf);
410
411 sfree(sstate);
412 }
413}
414
415static solver_state *dup_solver_state(const solver_state *sstate) {
416 game_state *state = sstate->state;
417 int num_dots = state->game_grid->num_dots;
418 int num_faces = state->game_grid->num_faces;
419 int num_edges = state->game_grid->num_edges;
420 solver_state *ret = snew(solver_state);
421
422 ret->state = state = dup_game(sstate->state);
423
424 ret->solver_status = sstate->solver_status;
425 ret->diff = sstate->diff;
426
427 ret->dotdsf = snewn(num_dots, int);
428 ret->looplen = snewn(num_dots, int);
429 memcpy(ret->dotdsf, sstate->dotdsf,
430 num_dots * sizeof(int));
431 memcpy(ret->looplen, sstate->looplen,
432 num_dots * sizeof(int));
433
434 ret->dot_solved = snewn(num_dots, char);
435 ret->face_solved = snewn(num_faces, char);
436 memcpy(ret->dot_solved, sstate->dot_solved, num_dots);
437 memcpy(ret->face_solved, sstate->face_solved, num_faces);
438
439 ret->dot_yes_count = snewn(num_dots, char);
440 memcpy(ret->dot_yes_count, sstate->dot_yes_count, num_dots);
441 ret->dot_no_count = snewn(num_dots, char);
442 memcpy(ret->dot_no_count, sstate->dot_no_count, num_dots);
443
444 ret->face_yes_count = snewn(num_faces, char);
445 memcpy(ret->face_yes_count, sstate->face_yes_count, num_faces);
446 ret->face_no_count = snewn(num_faces, char);
447 memcpy(ret->face_no_count, sstate->face_no_count, num_faces);
448
449 if (sstate->dlines) {
450 ret->dlines = snewn(2*num_edges, char);
451 memcpy(ret->dlines, sstate->dlines,
452 2*num_edges);
453 } else {
454 ret->dlines = NULL;
455 }
456
457 if (sstate->linedsf) {
458 ret->linedsf = snewn(num_edges, int);
459 memcpy(ret->linedsf, sstate->linedsf,
460 num_edges * sizeof(int));
461 } else {
462 ret->linedsf = NULL;
463 }
464
465 return ret;
466}
467
468static game_params *default_params(void)
469{
470 game_params *ret = snew(game_params);
471
472#ifdef SLOW_SYSTEM
473 ret->h = 7;
474 ret->w = 7;
475#else
476 ret->h = 10;
477 ret->w = 10;
478#endif
479 ret->diff = DIFF_EASY;
480 ret->type = 0;
481
482 return ret;
483}
484
485static game_params *dup_params(const game_params *params)
486{
487 game_params *ret = snew(game_params);
488
489 *ret = *params; /* structure copy */
490 return ret;
491}
492
493static const game_params presets[] = {
494#ifdef SMALL_SCREEN
495 { 7, 7, DIFF_EASY, 0 },
496 { 7, 7, DIFF_NORMAL, 0 },
497 { 7, 7, DIFF_HARD, 0 },
498 { 7, 7, DIFF_HARD, 1 },
499 { 7, 7, DIFF_HARD, 2 },
500 { 5, 5, DIFF_HARD, 3 },
501 { 7, 7, DIFF_HARD, 4 },
502 { 5, 4, DIFF_HARD, 5 },
503 { 5, 5, DIFF_HARD, 6 },
504 { 5, 5, DIFF_HARD, 7 },
505 { 3, 3, DIFF_HARD, 8 },
506 { 3, 3, DIFF_HARD, 9 },
507 { 3, 3, DIFF_HARD, 10 },
508 { 6, 6, DIFF_HARD, 11 },
509 { 6, 6, DIFF_HARD, 12 },
510#else
511 { 7, 7, DIFF_EASY, 0 },
512 { 10, 10, DIFF_EASY, 0 },
513 { 7, 7, DIFF_NORMAL, 0 },
514 { 10, 10, DIFF_NORMAL, 0 },
515 { 7, 7, DIFF_HARD, 0 },
516 { 10, 10, DIFF_HARD, 0 },
517 { 10, 10, DIFF_HARD, 1 },
518 { 12, 10, DIFF_HARD, 2 },
519 { 7, 7, DIFF_HARD, 3 },
520 { 9, 9, DIFF_HARD, 4 },
521 { 5, 4, DIFF_HARD, 5 },
522 { 7, 7, DIFF_HARD, 6 },
523 { 5, 5, DIFF_HARD, 7 },
524 { 5, 5, DIFF_HARD, 8 },
525 { 5, 4, DIFF_HARD, 9 },
526 { 5, 4, DIFF_HARD, 10 },
527 { 10, 10, DIFF_HARD, 11 },
528 { 10, 10, DIFF_HARD, 12 }
529#endif
530};
531
532static int game_fetch_preset(int i, char **name, game_params **params)
533{
534 game_params *tmppar;
535 char buf[80];
536
537 if (i < 0 || i >= lenof(presets))
538 return FALSE;
539
540 tmppar = snew(game_params);
541 *tmppar = presets[i];
542 *params = tmppar;
543 sprintf(buf, "%dx%d %s - %s", tmppar->h, tmppar->w,
544 gridnames[tmppar->type], diffnames[tmppar->diff]);
545 *name = dupstr(buf);
546
547 return TRUE;
548}
549
550static void free_params(game_params *params)
551{
552 sfree(params);
553}
554
555static void decode_params(game_params *params, char const *string)
556{
557 params->h = params->w = atoi(string);
558 params->diff = DIFF_EASY;
559 while (*string && isdigit((unsigned char)*string)) string++;
560 if (*string == 'x') {
561 string++;
562 params->h = atoi(string);
563 while (*string && isdigit((unsigned char)*string)) string++;
564 }
565 if (*string == 't') {
566 string++;
567 params->type = atoi(string);
568 while (*string && isdigit((unsigned char)*string)) string++;
569 }
570 if (*string == 'd') {
571 int i;
572 string++;
573 for (i = 0; i < DIFF_MAX; i++)
574 if (*string == diffchars[i])
575 params->diff = i;
576 if (*string) string++;
577 }
578}
579
580static char *encode_params(const game_params *params, int full)
581{
582 char str[80];
583 sprintf(str, "%dx%dt%d", params->w, params->h, params->type);
584 if (full)
585 sprintf(str + strlen(str), "d%c", diffchars[params->diff]);
586 return dupstr(str);
587}
588
589static config_item *game_configure(const game_params *params)
590{
591 config_item *ret;
592 char buf[80];
593
594 ret = snewn(5, config_item);
595
596 ret[0].name = "Width";
597 ret[0].type = C_STRING;
598 sprintf(buf, "%d", params->w);
599 ret[0].sval = dupstr(buf);
600 ret[0].ival = 0;
601
602 ret[1].name = "Height";
603 ret[1].type = C_STRING;
604 sprintf(buf, "%d", params->h);
605 ret[1].sval = dupstr(buf);
606 ret[1].ival = 0;
607
608 ret[2].name = "Grid type";
609 ret[2].type = C_CHOICES;
610 ret[2].sval = GRID_CONFIGS;
611 ret[2].ival = params->type;
612
613 ret[3].name = "Difficulty";
614 ret[3].type = C_CHOICES;
615 ret[3].sval = DIFFCONFIG;
616 ret[3].ival = params->diff;
617
618 ret[4].name = NULL;
619 ret[4].type = C_END;
620 ret[4].sval = NULL;
621 ret[4].ival = 0;
622
623 return ret;
624}
625
626static game_params *custom_params(const config_item *cfg)
627{
628 game_params *ret = snew(game_params);
629
630 ret->w = atoi(cfg[0].sval);
631 ret->h = atoi(cfg[1].sval);
632 ret->type = cfg[2].ival;
633 ret->diff = cfg[3].ival;
634
635 return ret;
636}
637
638static char *validate_params(const game_params *params, int full)
639{
640 if (params->type < 0 || params->type >= NUM_GRID_TYPES)
641 return "Illegal grid type";
642 if (params->w < grid_size_limits[params->type].amin ||
643 params->h < grid_size_limits[params->type].amin)
644 return grid_size_limits[params->type].aerr;
645 if (params->w < grid_size_limits[params->type].omin &&
646 params->h < grid_size_limits[params->type].omin)
647 return grid_size_limits[params->type].oerr;
648
649 /*
650 * This shouldn't be able to happen at all, since decode_params
651 * and custom_params will never generate anything that isn't
652 * within range.
653 */
654 assert(params->diff < DIFF_MAX);
655
656 return NULL;
657}
658
659/* Returns a newly allocated string describing the current puzzle */
660static char *state_to_text(const game_state *state)
661{
662 grid *g = state->game_grid;
663 char *retval;
664 int num_faces = g->num_faces;
665 char *description = snewn(num_faces + 1, char);
666 char *dp = description;
667 int empty_count = 0;
668 int i;
669
670 for (i = 0; i < num_faces; i++) {
671 if (state->clues[i] < 0) {
672 if (empty_count > 25) {
673 dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1));
674 empty_count = 0;
675 }
676 empty_count++;
677 } else {
678 if (empty_count) {
679 dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1));
680 empty_count = 0;
681 }
682 dp += sprintf(dp, "%c", (int)CLUE2CHAR(state->clues[i]));
683 }
684 }
685
686 if (empty_count)
687 dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1));
688
689 retval = dupstr(description);
690 sfree(description);
691
692 return retval;
693}
694
695#define GRID_DESC_SEP '_'
696
697/* Splits up a (optional) grid_desc from the game desc. Returns the
698 * grid_desc (which needs freeing) and updates the desc pointer to
699 * start of real desc, or returns NULL if no desc. */
700static char *extract_grid_desc(const char **desc)
701{
702 char *sep = strchr(*desc, GRID_DESC_SEP), *gd;
703 int gd_len;
704
705 if (!sep) return NULL;
706
707 gd_len = sep - (*desc);
708 gd = snewn(gd_len+1, char);
709 memcpy(gd, *desc, gd_len);
710 gd[gd_len] = '\0';
711
712 *desc = sep+1;
713
714 return gd;
715}
716
717/* We require that the params pass the test in validate_params and that the
718 * description fills the entire game area */
719static char *validate_desc(const game_params *params, const char *desc)
720{
721 int count = 0;
722 grid *g;
723 char *grid_desc, *ret;
724
725 /* It's pretty inefficient to do this just for validation. All we need to
726 * know is the precise number of faces. */
727 grid_desc = extract_grid_desc(&desc);
728 ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc);
729 if (ret) return ret;
730
731 g = loopy_generate_grid(params, grid_desc);
732 if (grid_desc) sfree(grid_desc);
733
734 for (; *desc; ++desc) {
735 if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) {
736 count++;
737 continue;
738 }
739 if (*desc >= 'a') {
740 count += *desc - 'a' + 1;
741 continue;
742 }
743 return "Unknown character in description";
744 }
745
746 if (count < g->num_faces)
747 return "Description too short for board size";
748 if (count > g->num_faces)
749 return "Description too long for board size";
750
751 grid_free(g);
752
753 return NULL;
754}
755
756/* Sums the lengths of the numbers in range [0,n) */
757/* See equivalent function in solo.c for justification of this. */
758static int len_0_to_n(int n)
759{
760 int len = 1; /* Counting 0 as a bit of a special case */
761 int i;
762
763 for (i = 1; i < n; i *= 10) {
764 len += max(n - i, 0);
765 }
766
767 return len;
768}
769
770static char *encode_solve_move(const game_state *state)
771{
772 int len;
773 char *ret, *p;
774 int i;
775 int num_edges = state->game_grid->num_edges;
776
777 /* This is going to return a string representing the moves needed to set
778 * every line in a grid to be the same as the ones in 'state'. The exact
779 * length of this string is predictable. */
780
781 len = 1; /* Count the 'S' prefix */
782 /* Numbers in all lines */
783 len += len_0_to_n(num_edges);
784 /* For each line we also have a letter */
785 len += num_edges;
786
787 ret = snewn(len + 1, char);
788 p = ret;
789
790 p += sprintf(p, "S");
791
792 for (i = 0; i < num_edges; i++) {
793 switch (state->lines[i]) {
794 case LINE_YES:
795 p += sprintf(p, "%dy", i);
796 break;
797 case LINE_NO:
798 p += sprintf(p, "%dn", i);
799 break;
800 }
801 }
802
803 /* No point in doing sums like that if they're going to be wrong */
804 assert(strlen(ret) <= (size_t)len);
805 return ret;
806}
807
808static game_ui *new_ui(const game_state *state)
809{
810 return NULL;
811}
812
813static void free_ui(game_ui *ui)
814{
815}
816
817static char *encode_ui(const game_ui *ui)
818{
819 return NULL;
820}
821
822static void decode_ui(game_ui *ui, const char *encoding)
823{
824}
825
826static void game_changed_state(game_ui *ui, const game_state *oldstate,
827 const game_state *newstate)
828{
829}
830
831static void game_compute_size(const game_params *params, int tilesize,
832 int *x, int *y)
833{
834 int grid_width, grid_height, rendered_width, rendered_height;
835 int g_tilesize;
836
837 grid_compute_size(grid_types[params->type], params->w, params->h,
838 &g_tilesize, &grid_width, &grid_height);
839
840 /* multiply first to minimise rounding error on integer division */
841 rendered_width = grid_width * tilesize / g_tilesize;
842 rendered_height = grid_height * tilesize / g_tilesize;
843 *x = rendered_width + 2 * BORDER(tilesize) + 1;
844 *y = rendered_height + 2 * BORDER(tilesize) + 1;
845}
846
847static void game_set_size(drawing *dr, game_drawstate *ds,
848 const game_params *params, int tilesize)
849{
850 ds->tilesize = tilesize;
851}
852
853static float *game_colours(frontend *fe, int *ncolours)
854{
855 float *ret = snewn(3 * NCOLOURS, float);
856
857 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
858
859 ret[COL_FOREGROUND * 3 + 0] = 0.0F;
860 ret[COL_FOREGROUND * 3 + 1] = 0.0F;
861 ret[COL_FOREGROUND * 3 + 2] = 0.0F;
862
863 /*
864 * We want COL_LINEUNKNOWN to be a yellow which is a bit darker
865 * than the background. (I previously set it to 0.8,0.8,0, but
866 * found that this went badly with the 0.8,0.8,0.8 favoured as a
867 * background by the Java frontend.)
868 */
869 ret[COL_LINEUNKNOWN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
870 ret[COL_LINEUNKNOWN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F;
871 ret[COL_LINEUNKNOWN * 3 + 2] = 0.0F;
872
873 ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
874 ret[COL_HIGHLIGHT * 3 + 1] = 1.0F;
875 ret[COL_HIGHLIGHT * 3 + 2] = 1.0F;
876
877 ret[COL_MISTAKE * 3 + 0] = 1.0F;
878 ret[COL_MISTAKE * 3 + 1] = 0.0F;
879 ret[COL_MISTAKE * 3 + 2] = 0.0F;
880
881 ret[COL_SATISFIED * 3 + 0] = 0.0F;
882 ret[COL_SATISFIED * 3 + 1] = 0.0F;
883 ret[COL_SATISFIED * 3 + 2] = 0.0F;
884
885 /* We want the faint lines to be a bit darker than the background.
886 * Except if the background is pretty dark already; then it ought to be a
887 * bit lighter. Oy vey.
888 */
889 ret[COL_FAINT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
890 ret[COL_FAINT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F;
891 ret[COL_FAINT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.9F;
892
893 *ncolours = NCOLOURS;
894 return ret;
895}
896
897static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
898{
899 struct game_drawstate *ds = snew(struct game_drawstate);
900 int num_faces = state->game_grid->num_faces;
901 int num_edges = state->game_grid->num_edges;
902 int i;
903
904 ds->tilesize = 0;
905 ds->started = 0;
906 ds->lines = snewn(num_edges, char);
907 ds->clue_error = snewn(num_faces, char);
908 ds->clue_satisfied = snewn(num_faces, char);
909 ds->textx = snewn(num_faces, int);
910 ds->texty = snewn(num_faces, int);
911 ds->flashing = 0;
912
913 memset(ds->lines, LINE_UNKNOWN, num_edges);
914 memset(ds->clue_error, 0, num_faces);
915 memset(ds->clue_satisfied, 0, num_faces);
916 for (i = 0; i < num_faces; i++)
917 ds->textx[i] = ds->texty[i] = -1;
918
919 return ds;
920}
921
922static void game_free_drawstate(drawing *dr, game_drawstate *ds)
923{
924 sfree(ds->textx);
925 sfree(ds->texty);
926 sfree(ds->clue_error);
927 sfree(ds->clue_satisfied);
928 sfree(ds->lines);
929 sfree(ds);
930}
931
932static int game_timing_state(const game_state *state, game_ui *ui)
933{
934 return TRUE;
935}
936
937static float game_anim_length(const game_state *oldstate,
938 const game_state *newstate, int dir, game_ui *ui)
939{
940 return 0.0F;
941}
942
943static int game_can_format_as_text_now(const game_params *params)
944{
945 if (params->type != 0)
946 return FALSE;
947 return TRUE;
948}
949
950static char *game_text_format(const game_state *state)
951{
952 int w, h, W, H;
953 int x, y, i;
954 int cell_size;
955 char *ret;
956 grid *g = state->game_grid;
957 grid_face *f;
958
959 assert(state->grid_type == 0);
960
961 /* Work out the basic size unit */
962 f = g->faces; /* first face */
963 assert(f->order == 4);
964 /* The dots are ordered clockwise, so the two opposite
965 * corners are guaranteed to span the square */
966 cell_size = abs(f->dots[0]->x - f->dots[2]->x);
967
968 w = (g->highest_x - g->lowest_x) / cell_size;
969 h = (g->highest_y - g->lowest_y) / cell_size;
970
971 /* Create a blank "canvas" to "draw" on */
972 W = 2 * w + 2;
973 H = 2 * h + 1;
974 ret = snewn(W * H + 1, char);
975 for (y = 0; y < H; y++) {
976 for (x = 0; x < W-1; x++) {
977 ret[y*W + x] = ' ';
978 }
979 ret[y*W + W-1] = '\n';
980 }
981 ret[H*W] = '\0';
982
983 /* Fill in edge info */
984 for (i = 0; i < g->num_edges; i++) {
985 grid_edge *e = g->edges + i;
986 /* Cell coordinates, from (0,0) to (w-1,h-1) */
987 int x1 = (e->dot1->x - g->lowest_x) / cell_size;
988 int x2 = (e->dot2->x - g->lowest_x) / cell_size;
989 int y1 = (e->dot1->y - g->lowest_y) / cell_size;
990 int y2 = (e->dot2->y - g->lowest_y) / cell_size;
991 /* Midpoint, in canvas coordinates (canvas coordinates are just twice
992 * cell coordinates) */
993 x = x1 + x2;
994 y = y1 + y2;
995 switch (state->lines[i]) {
996 case LINE_YES:
997 ret[y*W + x] = (y1 == y2) ? '-' : '|';
998 break;
999 case LINE_NO:
1000 ret[y*W + x] = 'x';
1001 break;
1002 case LINE_UNKNOWN:
1003 break; /* already a space */
1004 default:
1005 assert(!"Illegal line state");
1006 }
1007 }
1008
1009 /* Fill in clues */
1010 for (i = 0; i < g->num_faces; i++) {
1011 int x1, x2, y1, y2;
1012
1013 f = g->faces + i;
1014 assert(f->order == 4);
1015 /* Cell coordinates, from (0,0) to (w-1,h-1) */
1016 x1 = (f->dots[0]->x - g->lowest_x) / cell_size;
1017 x2 = (f->dots[2]->x - g->lowest_x) / cell_size;
1018 y1 = (f->dots[0]->y - g->lowest_y) / cell_size;
1019 y2 = (f->dots[2]->y - g->lowest_y) / cell_size;
1020 /* Midpoint, in canvas coordinates */
1021 x = x1 + x2;
1022 y = y1 + y2;
1023 ret[y*W + x] = CLUE2CHAR(state->clues[i]);
1024 }
1025 return ret;
1026}
1027
1028/* ----------------------------------------------------------------------
1029 * Debug code
1030 */
1031
1032#ifdef DEBUG_CACHES
1033static void check_caches(const solver_state* sstate)
1034{
1035 int i;
1036 const game_state *state = sstate->state;
1037 const grid *g = state->game_grid;
1038
1039 for (i = 0; i < g->num_dots; i++) {
1040 assert(dot_order(state, i, LINE_YES) == sstate->dot_yes_count[i]);
1041 assert(dot_order(state, i, LINE_NO) == sstate->dot_no_count[i]);
1042 }
1043
1044 for (i = 0; i < g->num_faces; i++) {
1045 assert(face_order(state, i, LINE_YES) == sstate->face_yes_count[i]);
1046 assert(face_order(state, i, LINE_NO) == sstate->face_no_count[i]);
1047 }
1048}
1049
1050#if 0
1051#define check_caches(s) \
1052 do { \
1053 fprintf(stderr, "check_caches at line %d\n", __LINE__); \
1054 check_caches(s); \
1055 } while (0)
1056#endif
1057#endif /* DEBUG_CACHES */
1058
1059/* ----------------------------------------------------------------------
1060 * Solver utility functions
1061 */
1062
1063/* Sets the line (with index i) to the new state 'line_new', and updates
1064 * the cached counts of any affected faces and dots.
1065 * Returns TRUE if this actually changed the line's state. */
1066static int solver_set_line(solver_state *sstate, int i,
1067 enum line_state line_new
1068#ifdef SHOW_WORKING
1069 , const char *reason
1070#endif
1071 )
1072{
1073 game_state *state = sstate->state;
1074 grid *g;
1075 grid_edge *e;
1076
1077 assert(line_new != LINE_UNKNOWN);
1078
1079 check_caches(sstate);
1080
1081 if (state->lines[i] == line_new) {
1082 return FALSE; /* nothing changed */
1083 }
1084 state->lines[i] = line_new;
1085
1086#ifdef SHOW_WORKING
1087 fprintf(stderr, "solver: set line [%d] to %s (%s)\n",
1088 i, line_new == LINE_YES ? "YES" : "NO",
1089 reason);
1090#endif
1091
1092 g = state->game_grid;
1093 e = g->edges + i;
1094
1095 /* Update the cache for both dots and both faces affected by this. */
1096 if (line_new == LINE_YES) {
1097 sstate->dot_yes_count[e->dot1 - g->dots]++;
1098 sstate->dot_yes_count[e->dot2 - g->dots]++;
1099 if (e->face1) {
1100 sstate->face_yes_count[e->face1 - g->faces]++;
1101 }
1102 if (e->face2) {
1103 sstate->face_yes_count[e->face2 - g->faces]++;
1104 }
1105 } else {
1106 sstate->dot_no_count[e->dot1 - g->dots]++;
1107 sstate->dot_no_count[e->dot2 - g->dots]++;
1108 if (e->face1) {
1109 sstate->face_no_count[e->face1 - g->faces]++;
1110 }
1111 if (e->face2) {
1112 sstate->face_no_count[e->face2 - g->faces]++;
1113 }
1114 }
1115
1116 check_caches(sstate);
1117 return TRUE;
1118}
1119
1120#ifdef SHOW_WORKING
1121#define solver_set_line(a, b, c) \
1122 solver_set_line(a, b, c, __FUNCTION__)
1123#endif
1124
1125/*
1126 * Merge two dots due to the existence of an edge between them.
1127 * Updates the dsf tracking equivalence classes, and keeps track of
1128 * the length of path each dot is currently a part of.
1129 * Returns TRUE if the dots were already linked, ie if they are part of a
1130 * closed loop, and false otherwise.
1131 */
1132static int merge_dots(solver_state *sstate, int edge_index)
1133{
1134 int i, j, len;
1135 grid *g = sstate->state->game_grid;
1136 grid_edge *e = g->edges + edge_index;
1137
1138 i = e->dot1 - g->dots;
1139 j = e->dot2 - g->dots;
1140
1141 i = dsf_canonify(sstate->dotdsf, i);
1142 j = dsf_canonify(sstate->dotdsf, j);
1143
1144 if (i == j) {
1145 return TRUE;
1146 } else {
1147 len = sstate->looplen[i] + sstate->looplen[j];
1148 dsf_merge(sstate->dotdsf, i, j);
1149 i = dsf_canonify(sstate->dotdsf, i);
1150 sstate->looplen[i] = len;
1151 return FALSE;
1152 }
1153}
1154
1155/* Merge two lines because the solver has deduced that they must be either
1156 * identical or opposite. Returns TRUE if this is new information, otherwise
1157 * FALSE. */
1158static int merge_lines(solver_state *sstate, int i, int j, int inverse
1159#ifdef SHOW_WORKING
1160 , const char *reason
1161#endif
1162 )
1163{
1164 int inv_tmp;
1165
1166 assert(i < sstate->state->game_grid->num_edges);
1167 assert(j < sstate->state->game_grid->num_edges);
1168
1169 i = edsf_canonify(sstate->linedsf, i, &inv_tmp);
1170 inverse ^= inv_tmp;
1171 j = edsf_canonify(sstate->linedsf, j, &inv_tmp);
1172 inverse ^= inv_tmp;
1173
1174 edsf_merge(sstate->linedsf, i, j, inverse);
1175
1176#ifdef SHOW_WORKING
1177 if (i != j) {
1178 fprintf(stderr, "%s [%d] [%d] %s(%s)\n",
1179 __FUNCTION__, i, j,
1180 inverse ? "inverse " : "", reason);
1181 }
1182#endif
1183 return (i != j);
1184}
1185
1186#ifdef SHOW_WORKING
1187#define merge_lines(a, b, c, d) \
1188 merge_lines(a, b, c, d, __FUNCTION__)
1189#endif
1190
1191/* Count the number of lines of a particular type currently going into the
1192 * given dot. */
1193static int dot_order(const game_state* state, int dot, char line_type)
1194{
1195 int n = 0;
1196 grid *g = state->game_grid;
1197 grid_dot *d = g->dots + dot;
1198 int i;
1199
1200 for (i = 0; i < d->order; i++) {
1201 grid_edge *e = d->edges[i];
1202 if (state->lines[e - g->edges] == line_type)
1203 ++n;
1204 }
1205 return n;
1206}
1207
1208/* Count the number of lines of a particular type currently surrounding the
1209 * given face */
1210static int face_order(const game_state* state, int face, char line_type)
1211{
1212 int n = 0;
1213 grid *g = state->game_grid;
1214 grid_face *f = g->faces + face;
1215 int i;
1216
1217 for (i = 0; i < f->order; i++) {
1218 grid_edge *e = f->edges[i];
1219 if (state->lines[e - g->edges] == line_type)
1220 ++n;
1221 }
1222 return n;
1223}
1224
1225/* Set all lines bordering a dot of type old_type to type new_type
1226 * Return value tells caller whether this function actually did anything */
1227static int dot_setall(solver_state *sstate, int dot,
1228 char old_type, char new_type)
1229{
1230 int retval = FALSE, r;
1231 game_state *state = sstate->state;
1232 grid *g;
1233 grid_dot *d;
1234 int i;
1235
1236 if (old_type == new_type)
1237 return FALSE;
1238
1239 g = state->game_grid;
1240 d = g->dots + dot;
1241
1242 for (i = 0; i < d->order; i++) {
1243 int line_index = d->edges[i] - g->edges;
1244 if (state->lines[line_index] == old_type) {
1245 r = solver_set_line(sstate, line_index, new_type);
1246 assert(r == TRUE);
1247 retval = TRUE;
1248 }
1249 }
1250 return retval;
1251}
1252
1253/* Set all lines bordering a face of type old_type to type new_type */
1254static int face_setall(solver_state *sstate, int face,
1255 char old_type, char new_type)
1256{
1257 int retval = FALSE, r;
1258 game_state *state = sstate->state;
1259 grid *g;
1260 grid_face *f;
1261 int i;
1262
1263 if (old_type == new_type)
1264 return FALSE;
1265
1266 g = state->game_grid;
1267 f = g->faces + face;
1268
1269 for (i = 0; i < f->order; i++) {
1270 int line_index = f->edges[i] - g->edges;
1271 if (state->lines[line_index] == old_type) {
1272 r = solver_set_line(sstate, line_index, new_type);
1273 assert(r == TRUE);
1274 retval = TRUE;
1275 }
1276 }
1277 return retval;
1278}
1279
1280/* ----------------------------------------------------------------------
1281 * Loop generation and clue removal
1282 */
1283
1284static void add_full_clues(game_state *state, random_state *rs)
1285{
1286 signed char *clues = state->clues;
1287 grid *g = state->game_grid;
1288 char *board = snewn(g->num_faces, char);
1289 int i;
1290
1291 generate_loop(g, board, rs, NULL, NULL);
1292
1293 /* Fill out all the clues by initialising to 0, then iterating over
1294 * all edges and incrementing each clue as we find edges that border
1295 * between BLACK/WHITE faces. While we're at it, we verify that the
1296 * algorithm does work, and there aren't any GREY faces still there. */
1297 memset(clues, 0, g->num_faces);
1298 for (i = 0; i < g->num_edges; i++) {
1299 grid_edge *e = g->edges + i;
1300 grid_face *f1 = e->face1;
1301 grid_face *f2 = e->face2;
1302 enum face_colour c1 = FACE_COLOUR(f1);
1303 enum face_colour c2 = FACE_COLOUR(f2);
1304 assert(c1 != FACE_GREY);
1305 assert(c2 != FACE_GREY);
1306 if (c1 != c2) {
1307 if (f1) clues[f1 - g->faces]++;
1308 if (f2) clues[f2 - g->faces]++;
1309 }
1310 }
1311 sfree(board);
1312}
1313
1314
1315static int game_has_unique_soln(const game_state *state, int diff)
1316{
1317 int ret;
1318 solver_state *sstate_new;
1319 solver_state *sstate = new_solver_state((game_state *)state, diff);
1320
1321 sstate_new = solve_game_rec(sstate);
1322
1323 assert(sstate_new->solver_status != SOLVER_MISTAKE);
1324 ret = (sstate_new->solver_status == SOLVER_SOLVED);
1325
1326 free_solver_state(sstate_new);
1327 free_solver_state(sstate);
1328
1329 return ret;
1330}
1331
1332
1333/* Remove clues one at a time at random. */
1334static game_state *remove_clues(game_state *state, random_state *rs,
1335 int diff)
1336{
1337 int *face_list;
1338 int num_faces = state->game_grid->num_faces;
1339 game_state *ret = dup_game(state), *saved_ret;
1340 int n;
1341
1342 /* We need to remove some clues. We'll do this by forming a list of all
1343 * available clues, shuffling it, then going along one at a
1344 * time clearing each clue in turn for which doing so doesn't render the
1345 * board unsolvable. */
1346 face_list = snewn(num_faces, int);
1347 for (n = 0; n < num_faces; ++n) {
1348 face_list[n] = n;
1349 }
1350
1351 shuffle(face_list, num_faces, sizeof(int), rs);
1352
1353 for (n = 0; n < num_faces; ++n) {
1354 saved_ret = dup_game(ret);
1355 ret->clues[face_list[n]] = -1;
1356
1357 if (game_has_unique_soln(ret, diff)) {
1358 free_game(saved_ret);
1359 } else {
1360 free_game(ret);
1361 ret = saved_ret;
1362 }
1363 }
1364 sfree(face_list);
1365
1366 return ret;
1367}
1368
1369
1370static char *new_game_desc(const game_params *params, random_state *rs,
1371 char **aux, int interactive)
1372{
1373 /* solution and description both use run-length encoding in obvious ways */
1374 char *retval, *game_desc, *grid_desc;
1375 grid *g;
1376 game_state *state = snew(game_state);
1377 game_state *state_new;
1378
1379 grid_desc = grid_new_desc(grid_types[params->type], params->w, params->h, rs);
1380 state->game_grid = g = loopy_generate_grid(params, grid_desc);
1381
1382 state->clues = snewn(g->num_faces, signed char);
1383 state->lines = snewn(g->num_edges, char);
1384 state->line_errors = snewn(g->num_edges, unsigned char);
1385 state->exactly_one_loop = FALSE;
1386
1387 state->grid_type = params->type;
1388
1389 newboard_please:
1390
1391 memset(state->lines, LINE_UNKNOWN, g->num_edges);
1392 memset(state->line_errors, 0, g->num_edges);
1393
1394 state->solved = state->cheated = FALSE;
1395
1396 /* Get a new random solvable board with all its clues filled in. Yes, this
1397 * can loop for ever if the params are suitably unfavourable, but
1398 * preventing games smaller than 4x4 seems to stop this happening */
1399 do {
1400 add_full_clues(state, rs);
1401 } while (!game_has_unique_soln(state, params->diff));
1402
1403 state_new = remove_clues(state, rs, params->diff);
1404 free_game(state);
1405 state = state_new;
1406
1407
1408 if (params->diff > 0 && game_has_unique_soln(state, params->diff-1)) {
1409#ifdef SHOW_WORKING
1410 fprintf(stderr, "Rejecting board, it is too easy\n");
1411#endif
1412 goto newboard_please;
1413 }
1414
1415 game_desc = state_to_text(state);
1416
1417 free_game(state);
1418
1419 if (grid_desc) {
1420 retval = snewn(strlen(grid_desc) + 1 + strlen(game_desc) + 1, char);
1421 sprintf(retval, "%s%c%s", grid_desc, (int)GRID_DESC_SEP, game_desc);
1422 sfree(grid_desc);
1423 sfree(game_desc);
1424 } else {
1425 retval = game_desc;
1426 }
1427
1428 assert(!validate_desc(params, retval));
1429
1430 return retval;
1431}
1432
1433static game_state *new_game(midend *me, const game_params *params,
1434 const char *desc)
1435{
1436 int i;
1437 game_state *state = snew(game_state);
1438 int empties_to_make = 0;
1439 int n,n2;
1440 const char *dp;
1441 char *grid_desc;
1442 grid *g;
1443 int num_faces, num_edges;
1444
1445 grid_desc = extract_grid_desc(&desc);
1446 state->game_grid = g = loopy_generate_grid(params, grid_desc);
1447 if (grid_desc) sfree(grid_desc);
1448
1449 dp = desc;
1450
1451 num_faces = g->num_faces;
1452 num_edges = g->num_edges;
1453
1454 state->clues = snewn(num_faces, signed char);
1455 state->lines = snewn(num_edges, char);
1456 state->line_errors = snewn(num_edges, unsigned char);
1457 state->exactly_one_loop = FALSE;
1458
1459 state->solved = state->cheated = FALSE;
1460
1461 state->grid_type = params->type;
1462
1463 for (i = 0; i < num_faces; i++) {
1464 if (empties_to_make) {
1465 empties_to_make--;
1466 state->clues[i] = -1;
1467 continue;
1468 }
1469
1470 assert(*dp);
1471 n = *dp - '0';
1472 n2 = *dp - 'A' + 10;
1473 if (n >= 0 && n < 10) {
1474 state->clues[i] = n;
1475 } else if (n2 >= 10 && n2 < 36) {
1476 state->clues[i] = n2;
1477 } else {
1478 n = *dp - 'a' + 1;
1479 assert(n > 0);
1480 state->clues[i] = -1;
1481 empties_to_make = n - 1;
1482 }
1483 ++dp;
1484 }
1485
1486 memset(state->lines, LINE_UNKNOWN, num_edges);
1487 memset(state->line_errors, 0, num_edges);
1488 return state;
1489}
1490
1491/* Calculates the line_errors data, and checks if the current state is a
1492 * solution */
1493static int check_completion(game_state *state)
1494{
1495 grid *g = state->game_grid;
1496 int i, ret;
1497 int *dsf, *component_state;
1498 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
1499 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
1500
1501 memset(state->line_errors, 0, g->num_edges);
1502
1503 /*
1504 * Find loops in the grid, and determine whether the puzzle is
1505 * solved.
1506 *
1507 * Loopy is a bit more complicated than most puzzles that care
1508 * about loop detection. In most of them, loops are simply
1509 * _forbidden_; so the obviously right way to do
1510 * error-highlighting during play is to light up a graph edge red
1511 * iff it is part of a loop, which is exactly what the centralised
1512 * findloop.c makes easy.
1513 *
1514 * But Loopy is unusual in that you're _supposed_ to be making a
1515 * loop - and yet _some_ loops are not the right loop. So we need
1516 * to be more discriminating, by identifying loops one by one and
1517 * then thinking about which ones to highlight, and so findloop.c
1518 * isn't quite the right tool for the job in this case.
1519 *
1520 * Worse still, consider situations in which the grid contains a
1521 * loop and also some non-loop edges: there are some cases like
1522 * this in which the user's intuitive expectation would be to
1523 * highlight the loop (if you're only about half way through the
1524 * puzzle and have accidentally made a little loop in some corner
1525 * of the grid), and others in which they'd be more likely to
1526 * expect you to highlight the non-loop edges (if you've just
1527 * closed off a whole loop that you thought was the entire
1528 * solution, but forgot some disconnected edges in a corner
1529 * somewhere). So while it's easy enough to check whether the
1530 * solution is _right_, highlighting the wrong parts is a tricky
1531 * problem for this puzzle!
1532 *
1533 * I'd quite like, in some situations, to identify the largest
1534 * loop among the player's YES edges, and then light up everything
1535 * other than that. But finding the longest cycle in a graph is an
1536 * NP-complete problem (because, in particular, it must return a
1537 * Hamilton cycle if one exists).
1538 *
1539 * However, I think we can make the problem tractable by
1540 * exercising the Puzzles principle that it isn't absolutely
1541 * necessary to highlight _all_ errors: the key point is that by
1542 * the time the user has filled in the whole grid, they should
1543 * either have seen a completion flash, or have _some_ error
1544 * highlight showing them why the solution isn't right. So in
1545 * principle it would be *just about* good enough to highlight
1546 * just one error in the whole grid, if there was really no better
1547 * way. But we'd like to highlight as many errors as possible.
1548 *
1549 * In this case, I think the simple approach is to make use of the
1550 * fact that no vertex may have degree > 2, and that's really
1551 * simple to detect. So the plan goes like this:
1552 *
1553 * - Form the dsf of connected components of the graph vertices.
1554 *
1555 * - Highlight an error at any vertex with degree > 2. (It so
1556 * happens that we do this by lighting up all the edges
1557 * incident to that vertex, but that's an output detail.)
1558 *
1559 * - Any component that contains such a vertex is now excluded
1560 * from further consideration, because it already has a
1561 * highlight.
1562 *
1563 * - The remaining components have no vertex with degree > 2, and
1564 * hence they all consist of either a simple loop, or a simple
1565 * path with two endpoints.
1566 *
1567 * - For these purposes, group together all the paths and imagine
1568 * them to be a single component (because in most normal
1569 * situations the player will gradually build up the solution
1570 * _not_ all in one connected segment, but as lots of separate
1571 * little path pieces that gradually connect to each other).
1572 *
1573 * - After doing that, if there is exactly one (sensible)
1574 * component - be it a collection of paths or a loop - then
1575 * highlight no further edge errors. (The former case is normal
1576 * during play, and the latter is a potentially solved puzzle.)
1577 *
1578 * - Otherwise, find the largest of the sensible components,
1579 * leave that one unhighlighted, and light the rest up in red.
1580 */
1581
1582 dsf = snew_dsf(g->num_dots);
1583
1584 /* Build the dsf. */
1585 for (i = 0; i < g->num_edges; i++) {
1586 if (state->lines[i] == LINE_YES) {
1587 grid_edge *e = g->edges + i;
1588 int d1 = e->dot1 - g->dots, d2 = e->dot2 - g->dots;
1589 dsf_merge(dsf, d1, d2);
1590 }
1591 }
1592
1593 /* Initialise a state variable for each connected component. */
1594 component_state = snewn(g->num_dots, int);
1595 for (i = 0; i < g->num_dots; i++) {
1596 if (dsf_canonify(dsf, i) == i)
1597 component_state[i] = COMP_LOOP;
1598 else
1599 component_state[i] = COMP_NONE;
1600 }
1601
1602 /* Check for dots with degree > 3. Here we also spot dots of
1603 * degree 1 in which the user has marked all the non-edges as
1604 * LINE_NO, because those are also clear vertex-level errors, so
1605 * we give them the same treatment of excluding their connected
1606 * component from the subsequent loop analysis. */
1607 for (i = 0; i < g->num_dots; i++) {
1608 int comp = dsf_canonify(dsf, i);
1609 int yes = dot_order(state, i, LINE_YES);
1610 int unknown = dot_order(state, i, LINE_UNKNOWN);
1611 if ((yes == 1 && unknown == 0) || (yes >= 3)) {
1612 /* violation, so mark all YES edges as errors */
1613 grid_dot *d = g->dots + i;
1614 int j;
1615 for (j = 0; j < d->order; j++) {
1616 int e = d->edges[j] - g->edges;
1617 if (state->lines[e] == LINE_YES)
1618 state->line_errors[e] = TRUE;
1619 }
1620 /* And mark this component as not worthy of further
1621 * consideration. */
1622 component_state[comp] = COMP_SILLY;
1623
1624 } else if (yes == 0) {
1625 /* A completely isolated dot must also be excluded it from
1626 * the subsequent loop highlighting pass, but we tag it
1627 * with a different enum value to avoid it counting
1628 * towards the components that inhibit returning a win
1629 * status. */
1630 component_state[comp] = COMP_EMPTY;
1631 } else if (yes == 1) {
1632 /* A dot with degree 1 that didn't fall into the 'clearly
1633 * erroneous' case above indicates that this connected
1634 * component will be a path rather than a loop - unless
1635 * something worse elsewhere in the component has
1636 * classified it as silly. */
1637 if (component_state[comp] != COMP_SILLY)
1638 component_state[comp] = COMP_PATH;
1639 }
1640 }
1641
1642 /* Count up the components. Also, find the largest sensible
1643 * component. (Tie-breaking condition is derived from the order of
1644 * vertices in the grid data structure, which is fairly arbitrary
1645 * but at least stays stable throughout the game.) */
1646 nsilly = nloop = npath = 0;
1647 total_pathsize = 0;
1648 largest_comp = largest_size = -1;
1649 for (i = 0; i < g->num_dots; i++) {
1650 if (component_state[i] == COMP_SILLY) {
1651 nsilly++;
1652 } else if (component_state[i] == COMP_PATH) {
1653 total_pathsize += dsf_size(dsf, i);
1654 npath = 1;
1655 } else if (component_state[i] == COMP_LOOP) {
1656 int this_size;
1657
1658 nloop++;
1659
1660 if ((this_size = dsf_size(dsf, i)) > largest_size) {
1661 largest_comp = i;
1662 largest_size = this_size;
1663 }
1664 }
1665 }
1666 if (largest_size < total_pathsize) {
1667 largest_comp = -1; /* means the paths */
1668 largest_size = total_pathsize;
1669 }
1670
1671 if (nloop > 0 && nloop + npath > 1) {
1672 /*
1673 * If there are at least two sensible components including at
1674 * least one loop, highlight all edges in every sensible
1675 * component that is not the largest one.
1676 */
1677 for (i = 0; i < g->num_edges; i++) {
1678 if (state->lines[i] == LINE_YES) {
1679 grid_edge *e = g->edges + i;
1680 int d1 = e->dot1 - g->dots; /* either endpoint is good enough */
1681 int comp = dsf_canonify(dsf, d1);
1682 if ((component_state[comp] == COMP_PATH &&
1683 -1 != largest_comp) ||
1684 (component_state[comp] == COMP_LOOP &&
1685 comp != largest_comp))
1686 state->line_errors[i] = TRUE;
1687 }
1688 }
1689 }
1690
1691 if (nloop == 1 && npath == 0 && nsilly == 0) {
1692 /*
1693 * If there is exactly one component and it is a loop, then
1694 * the puzzle is potentially complete, so check the clues.
1695 */
1696 ret = TRUE;
1697
1698 for (i = 0; i < g->num_faces; i++) {
1699 int c = state->clues[i];
1700 if (c >= 0 && face_order(state, i, LINE_YES) != c) {
1701 ret = FALSE;
1702 break;
1703 }
1704 }
1705
1706 /*
1707 * Also, whether or not the puzzle is actually complete, set
1708 * the flag that says this game_state has exactly one loop and
1709 * nothing else, which will be used to vary the semantics of
1710 * clue highlighting at display time.
1711 */
1712 state->exactly_one_loop = TRUE;
1713 } else {
1714 ret = FALSE;
1715 state->exactly_one_loop = FALSE;
1716 }
1717
1718 sfree(component_state);
1719 sfree(dsf);
1720
1721 return ret;
1722}
1723
1724/* ----------------------------------------------------------------------
1725 * Solver logic
1726 *
1727 * Our solver modes operate as follows. Each mode also uses the modes above it.
1728 *
1729 * Easy Mode
1730 * Just implement the rules of the game.
1731 *
1732 * Normal and Tricky Modes
1733 * For each (adjacent) pair of lines through each dot we store a bit for
1734 * whether at least one of them is on and whether at most one is on. (If we
1735 * know both or neither is on that's already stored more directly.)
1736 *
1737 * Advanced Mode
1738 * Use edsf data structure to make equivalence classes of lines that are
1739 * known identical to or opposite to one another.
1740 */
1741
1742
1743/* DLines:
1744 * For general grids, we consider "dlines" to be pairs of lines joined
1745 * at a dot. The lines must be adjacent around the dot, so we can think of
1746 * a dline as being a dot+face combination. Or, a dot+edge combination where
1747 * the second edge is taken to be the next clockwise edge from the dot.
1748 * Original loopy code didn't have this extra restriction of the lines being
1749 * adjacent. From my tests with square grids, this extra restriction seems to
1750 * take little, if anything, away from the quality of the puzzles.
1751 * A dline can be uniquely identified by an edge/dot combination, given that
1752 * a dline-pair always goes clockwise around its common dot. The edge/dot
1753 * combination can be represented by an edge/bool combination - if bool is
1754 * TRUE, use edge->dot1 else use edge->dot2. So the total number of dlines is
1755 * exactly twice the number of edges in the grid - although the dlines
1756 * spanning the infinite face are not all that useful to the solver.
1757 * Note that, by convention, a dline goes clockwise around its common dot,
1758 * which means the dline goes anti-clockwise around its common face.
1759 */
1760
1761/* Helper functions for obtaining an index into an array of dlines, given
1762 * various information. We assume the grid layout conventions about how
1763 * the various lists are interleaved - see grid_make_consistent() for
1764 * details. */
1765
1766/* i points to the first edge of the dline pair, reading clockwise around
1767 * the dot. */
1768static int dline_index_from_dot(grid *g, grid_dot *d, int i)
1769{
1770 grid_edge *e = d->edges[i];
1771 int ret;
1772#ifdef DEBUG_DLINES
1773 grid_edge *e2;
1774 int i2 = i+1;
1775 if (i2 == d->order) i2 = 0;
1776 e2 = d->edges[i2];
1777#endif
1778 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0);
1779#ifdef DEBUG_DLINES
1780 printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n",
1781 (int)(d - g->dots), i, (int)(e - g->edges),
1782 (int)(e2 - g->edges), ret);
1783#endif
1784 return ret;
1785}
1786/* i points to the second edge of the dline pair, reading clockwise around
1787 * the face. That is, the edges of the dline, starting at edge{i}, read
1788 * anti-clockwise around the face. By layout conventions, the common dot
1789 * of the dline will be f->dots[i] */
1790static int dline_index_from_face(grid *g, grid_face *f, int i)
1791{
1792 grid_edge *e = f->edges[i];
1793 grid_dot *d = f->dots[i];
1794 int ret;
1795#ifdef DEBUG_DLINES
1796 grid_edge *e2;
1797 int i2 = i - 1;
1798 if (i2 < 0) i2 += f->order;
1799 e2 = f->edges[i2];
1800#endif
1801 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0);
1802#ifdef DEBUG_DLINES
1803 printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n",
1804 (int)(f - g->faces), i, (int)(e - g->edges),
1805 (int)(e2 - g->edges), ret);
1806#endif
1807 return ret;
1808}
1809static int is_atleastone(const char *dline_array, int index)
1810{
1811 return BIT_SET(dline_array[index], 0);
1812}
1813static int set_atleastone(char *dline_array, int index)
1814{
1815 return SET_BIT(dline_array[index], 0);
1816}
1817static int is_atmostone(const char *dline_array, int index)
1818{
1819 return BIT_SET(dline_array[index], 1);
1820}
1821static int set_atmostone(char *dline_array, int index)
1822{
1823 return SET_BIT(dline_array[index], 1);
1824}
1825
1826static void array_setall(char *array, char from, char to, int len)
1827{
1828 char *p = array, *p_old = p;
1829 int len_remaining = len;
1830
1831 while ((p = memchr(p, from, len_remaining))) {
1832 *p = to;
1833 len_remaining -= p - p_old;
1834 p_old = p;
1835 }
1836}
1837
1838/* Helper, called when doing dline dot deductions, in the case where we
1839 * have 4 UNKNOWNs, and two of them (adjacent) have *exactly* one YES between
1840 * them (because of dline atmostone/atleastone).
1841 * On entry, edge points to the first of these two UNKNOWNs. This function
1842 * will find the opposite UNKNOWNS (if they are adjacent to one another)
1843 * and set their corresponding dline to atleastone. (Setting atmostone
1844 * already happens in earlier dline deductions) */
1845static int dline_set_opp_atleastone(solver_state *sstate,
1846 grid_dot *d, int edge)
1847{
1848 game_state *state = sstate->state;
1849 grid *g = state->game_grid;
1850 int N = d->order;
1851 int opp, opp2;
1852 for (opp = 0; opp < N; opp++) {
1853 int opp_dline_index;
1854 if (opp == edge || opp == edge+1 || opp == edge-1)
1855 continue;
1856 if (opp == 0 && edge == N-1)
1857 continue;
1858 if (opp == N-1 && edge == 0)
1859 continue;
1860 opp2 = opp + 1;
1861 if (opp2 == N) opp2 = 0;
1862 /* Check if opp, opp2 point to LINE_UNKNOWNs */
1863 if (state->lines[d->edges[opp] - g->edges] != LINE_UNKNOWN)
1864 continue;
1865 if (state->lines[d->edges[opp2] - g->edges] != LINE_UNKNOWN)
1866 continue;
1867 /* Found opposite UNKNOWNS and they're next to each other */
1868 opp_dline_index = dline_index_from_dot(g, d, opp);
1869 return set_atleastone(sstate->dlines, opp_dline_index);
1870 }
1871 return FALSE;
1872}
1873
1874
1875/* Set pairs of lines around this face which are known to be identical, to
1876 * the given line_state */
1877static int face_setall_identical(solver_state *sstate, int face_index,
1878 enum line_state line_new)
1879{
1880 /* can[dir] contains the canonical line associated with the line in
1881 * direction dir from the square in question. Similarly inv[dir] is
1882 * whether or not the line in question is inverse to its canonical
1883 * element. */
1884 int retval = FALSE;
1885 game_state *state = sstate->state;
1886 grid *g = state->game_grid;
1887 grid_face *f = g->faces + face_index;
1888 int N = f->order;
1889 int i, j;
1890 int can1, can2, inv1, inv2;
1891
1892 for (i = 0; i < N; i++) {
1893 int line1_index = f->edges[i] - g->edges;
1894 if (state->lines[line1_index] != LINE_UNKNOWN)
1895 continue;
1896 for (j = i + 1; j < N; j++) {
1897 int line2_index = f->edges[j] - g->edges;
1898 if (state->lines[line2_index] != LINE_UNKNOWN)
1899 continue;
1900
1901 /* Found two UNKNOWNS */
1902 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1);
1903 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2);
1904 if (can1 == can2 && inv1 == inv2) {
1905 solver_set_line(sstate, line1_index, line_new);
1906 solver_set_line(sstate, line2_index, line_new);
1907 }
1908 }
1909 }
1910 return retval;
1911}
1912
1913/* Given a dot or face, and a count of LINE_UNKNOWNs, find them and
1914 * return the edge indices into e. */
1915static void find_unknowns(game_state *state,
1916 grid_edge **edge_list, /* Edge list to search (from a face or a dot) */
1917 int expected_count, /* Number of UNKNOWNs (comes from solver's cache) */
1918 int *e /* Returned edge indices */)
1919{
1920 int c = 0;
1921 grid *g = state->game_grid;
1922 while (c < expected_count) {
1923 int line_index = *edge_list - g->edges;
1924 if (state->lines[line_index] == LINE_UNKNOWN) {
1925 e[c] = line_index;
1926 c++;
1927 }
1928 ++edge_list;
1929 }
1930}
1931
1932/* If we have a list of edges, and we know whether the number of YESs should
1933 * be odd or even, and there are only a few UNKNOWNs, we can do some simple
1934 * linedsf deductions. This can be used for both face and dot deductions.
1935 * Returns the difficulty level of the next solver that should be used,
1936 * or DIFF_MAX if no progress was made. */
1937static int parity_deductions(solver_state *sstate,
1938 grid_edge **edge_list, /* Edge list (from a face or a dot) */
1939 int total_parity, /* Expected number of YESs modulo 2 (either 0 or 1) */
1940 int unknown_count)
1941{
1942 game_state *state = sstate->state;
1943 int diff = DIFF_MAX;
1944 int *linedsf = sstate->linedsf;
1945
1946 if (unknown_count == 2) {
1947 /* Lines are known alike/opposite, depending on inv. */
1948 int e[2];
1949 find_unknowns(state, edge_list, 2, e);
1950 if (merge_lines(sstate, e[0], e[1], total_parity))
1951 diff = min(diff, DIFF_HARD);
1952 } else if (unknown_count == 3) {
1953 int e[3];
1954 int can[3]; /* canonical edges */
1955 int inv[3]; /* whether can[x] is inverse to e[x] */
1956 find_unknowns(state, edge_list, 3, e);
1957 can[0] = edsf_canonify(linedsf, e[0], inv);
1958 can[1] = edsf_canonify(linedsf, e[1], inv+1);
1959 can[2] = edsf_canonify(linedsf, e[2], inv+2);
1960 if (can[0] == can[1]) {
1961 if (solver_set_line(sstate, e[2], (total_parity^inv[0]^inv[1]) ?
1962 LINE_YES : LINE_NO))
1963 diff = min(diff, DIFF_EASY);
1964 }
1965 if (can[0] == can[2]) {
1966 if (solver_set_line(sstate, e[1], (total_parity^inv[0]^inv[2]) ?
1967 LINE_YES : LINE_NO))
1968 diff = min(diff, DIFF_EASY);
1969 }
1970 if (can[1] == can[2]) {
1971 if (solver_set_line(sstate, e[0], (total_parity^inv[1]^inv[2]) ?
1972 LINE_YES : LINE_NO))
1973 diff = min(diff, DIFF_EASY);
1974 }
1975 } else if (unknown_count == 4) {
1976 int e[4];
1977 int can[4]; /* canonical edges */
1978 int inv[4]; /* whether can[x] is inverse to e[x] */
1979 find_unknowns(state, edge_list, 4, e);
1980 can[0] = edsf_canonify(linedsf, e[0], inv);
1981 can[1] = edsf_canonify(linedsf, e[1], inv+1);
1982 can[2] = edsf_canonify(linedsf, e[2], inv+2);
1983 can[3] = edsf_canonify(linedsf, e[3], inv+3);
1984 if (can[0] == can[1]) {
1985 if (merge_lines(sstate, e[2], e[3], total_parity^inv[0]^inv[1]))
1986 diff = min(diff, DIFF_HARD);
1987 } else if (can[0] == can[2]) {
1988 if (merge_lines(sstate, e[1], e[3], total_parity^inv[0]^inv[2]))
1989 diff = min(diff, DIFF_HARD);
1990 } else if (can[0] == can[3]) {
1991 if (merge_lines(sstate, e[1], e[2], total_parity^inv[0]^inv[3]))
1992 diff = min(diff, DIFF_HARD);
1993 } else if (can[1] == can[2]) {
1994 if (merge_lines(sstate, e[0], e[3], total_parity^inv[1]^inv[2]))
1995 diff = min(diff, DIFF_HARD);
1996 } else if (can[1] == can[3]) {
1997 if (merge_lines(sstate, e[0], e[2], total_parity^inv[1]^inv[3]))
1998 diff = min(diff, DIFF_HARD);
1999 } else if (can[2] == can[3]) {
2000 if (merge_lines(sstate, e[0], e[1], total_parity^inv[2]^inv[3]))
2001 diff = min(diff, DIFF_HARD);
2002 }
2003 }
2004 return diff;
2005}
2006
2007
2008/*
2009 * These are the main solver functions.
2010 *
2011 * Their return values are diff values corresponding to the lowest mode solver
2012 * that would notice the work that they have done. For example if the normal
2013 * mode solver adds actual lines or crosses, it will return DIFF_EASY as the
2014 * easy mode solver might be able to make progress using that. It doesn't make
2015 * sense for one of them to return a diff value higher than that of the
2016 * function itself.
2017 *
2018 * Each function returns the lowest value it can, as early as possible, in
2019 * order to try and pass as much work as possible back to the lower level
2020 * solvers which progress more quickly.
2021 */
2022
2023/* PROPOSED NEW DESIGN:
2024 * We have a work queue consisting of 'events' notifying us that something has
2025 * happened that a particular solver mode might be interested in. For example
2026 * the hard mode solver might do something that helps the normal mode solver at
2027 * dot [x,y] in which case it will enqueue an event recording this fact. Then
2028 * we pull events off the work queue, and hand each in turn to the solver that
2029 * is interested in them. If a solver reports that it failed we pass the same
2030 * event on to progressively more advanced solvers and the loop detector. Once
2031 * we've exhausted an event, or it has helped us progress, we drop it and
2032 * continue to the next one. The events are sorted first in order of solver
2033 * complexity (easy first) then order of insertion (oldest first).
2034 * Once we run out of events we loop over each permitted solver in turn
2035 * (easiest first) until either a deduction is made (and an event therefore
2036 * emerges) or no further deductions can be made (in which case we've failed).
2037 *
2038 * QUESTIONS:
2039 * * How do we 'loop over' a solver when both dots and squares are concerned.
2040 * Answer: first all squares then all dots.
2041 */
2042
2043static int trivial_deductions(solver_state *sstate)
2044{
2045 int i, current_yes, current_no;
2046 game_state *state = sstate->state;
2047 grid *g = state->game_grid;
2048 int diff = DIFF_MAX;
2049
2050 /* Per-face deductions */
2051 for (i = 0; i < g->num_faces; i++) {
2052 grid_face *f = g->faces + i;
2053
2054 if (sstate->face_solved[i])
2055 continue;
2056
2057 current_yes = sstate->face_yes_count[i];
2058 current_no = sstate->face_no_count[i];
2059
2060 if (current_yes + current_no == f->order) {
2061 sstate->face_solved[i] = TRUE;
2062 continue;
2063 }
2064
2065 if (state->clues[i] < 0)
2066 continue;
2067
2068 /*
2069 * This code checks whether the numeric clue on a face is so
2070 * large as to permit all its remaining LINE_UNKNOWNs to be
2071 * filled in as LINE_YES, or alternatively so small as to
2072 * permit them all to be filled in as LINE_NO.
2073 */
2074
2075 if (state->clues[i] < current_yes) {
2076 sstate->solver_status = SOLVER_MISTAKE;
2077 return DIFF_EASY;
2078 }
2079 if (state->clues[i] == current_yes) {
2080 if (face_setall(sstate, i, LINE_UNKNOWN, LINE_NO))
2081 diff = min(diff, DIFF_EASY);
2082 sstate->face_solved[i] = TRUE;
2083 continue;
2084 }
2085
2086 if (f->order - state->clues[i] < current_no) {
2087 sstate->solver_status = SOLVER_MISTAKE;
2088 return DIFF_EASY;
2089 }
2090 if (f->order - state->clues[i] == current_no) {
2091 if (face_setall(sstate, i, LINE_UNKNOWN, LINE_YES))
2092 diff = min(diff, DIFF_EASY);
2093 sstate->face_solved[i] = TRUE;
2094 continue;
2095 }
2096
2097 if (f->order - state->clues[i] == current_no + 1 &&
2098 f->order - current_yes - current_no > 2) {
2099 /*
2100 * One small refinement to the above: we also look for any
2101 * adjacent pair of LINE_UNKNOWNs around the face with
2102 * some LINE_YES incident on it from elsewhere. If we find
2103 * one, then we know that pair of LINE_UNKNOWNs can't
2104 * _both_ be LINE_YES, and hence that pushes us one line
2105 * closer to being able to determine all the rest.
2106 */
2107 int j, k, e1, e2, e, d;
2108
2109 for (j = 0; j < f->order; j++) {
2110 e1 = f->edges[j] - g->edges;
2111 e2 = f->edges[j+1 < f->order ? j+1 : 0] - g->edges;
2112
2113 if (g->edges[e1].dot1 == g->edges[e2].dot1 ||
2114 g->edges[e1].dot1 == g->edges[e2].dot2) {
2115 d = g->edges[e1].dot1 - g->dots;
2116 } else {
2117 assert(g->edges[e1].dot2 == g->edges[e2].dot1 ||
2118 g->edges[e1].dot2 == g->edges[e2].dot2);
2119 d = g->edges[e1].dot2 - g->dots;
2120 }
2121
2122 if (state->lines[e1] == LINE_UNKNOWN &&
2123 state->lines[e2] == LINE_UNKNOWN) {
2124 for (k = 0; k < g->dots[d].order; k++) {
2125 int e = g->dots[d].edges[k] - g->edges;
2126 if (state->lines[e] == LINE_YES)
2127 goto found; /* multi-level break */
2128 }
2129 }
2130 }
2131 continue;
2132
2133 found:
2134 /*
2135 * If we get here, we've found such a pair of edges, and
2136 * they're e1 and e2.
2137 */
2138 for (j = 0; j < f->order; j++) {
2139 e = f->edges[j] - g->edges;
2140 if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) {
2141 int r = solver_set_line(sstate, e, LINE_YES);
2142 assert(r);
2143 diff = min(diff, DIFF_EASY);
2144 }
2145 }
2146 }
2147 }
2148
2149 check_caches(sstate);
2150
2151 /* Per-dot deductions */
2152 for (i = 0; i < g->num_dots; i++) {
2153 grid_dot *d = g->dots + i;
2154 int yes, no, unknown;
2155
2156 if (sstate->dot_solved[i])
2157 continue;
2158
2159 yes = sstate->dot_yes_count[i];
2160 no = sstate->dot_no_count[i];
2161 unknown = d->order - yes - no;
2162
2163 if (yes == 0) {
2164 if (unknown == 0) {
2165 sstate->dot_solved[i] = TRUE;
2166 } else if (unknown == 1) {
2167 dot_setall(sstate, i, LINE_UNKNOWN, LINE_NO);
2168 diff = min(diff, DIFF_EASY);
2169 sstate->dot_solved[i] = TRUE;
2170 }
2171 } else if (yes == 1) {
2172 if (unknown == 0) {
2173 sstate->solver_status = SOLVER_MISTAKE;
2174 return DIFF_EASY;
2175 } else if (unknown == 1) {
2176 dot_setall(sstate, i, LINE_UNKNOWN, LINE_YES);
2177 diff = min(diff, DIFF_EASY);
2178 }
2179 } else if (yes == 2) {
2180 if (unknown > 0) {
2181 dot_setall(sstate, i, LINE_UNKNOWN, LINE_NO);
2182 diff = min(diff, DIFF_EASY);
2183 }
2184 sstate->dot_solved[i] = TRUE;
2185 } else {
2186 sstate->solver_status = SOLVER_MISTAKE;
2187 return DIFF_EASY;
2188 }
2189 }
2190
2191 check_caches(sstate);
2192
2193 return diff;
2194}
2195
2196static int dline_deductions(solver_state *sstate)
2197{
2198 game_state *state = sstate->state;
2199 grid *g = state->game_grid;
2200 char *dlines = sstate->dlines;
2201 int i;
2202 int diff = DIFF_MAX;
2203
2204 /* ------ Face deductions ------ */
2205
2206 /* Given a set of dline atmostone/atleastone constraints, need to figure
2207 * out if we can deduce any further info. For more general faces than
2208 * squares, this turns out to be a tricky problem.
2209 * The approach taken here is to define (per face) NxN matrices:
2210 * "maxs" and "mins".
2211 * The entries maxs(j,k) and mins(j,k) define the upper and lower limits
2212 * for the possible number of edges that are YES between positions j and k
2213 * going clockwise around the face. Can think of j and k as marking dots
2214 * around the face (recall the labelling scheme: edge0 joins dot0 to dot1,
2215 * edge1 joins dot1 to dot2 etc).
2216 * Trivially, mins(j,j) = maxs(j,j) = 0, and we don't even bother storing
2217 * these. mins(j,j+1) and maxs(j,j+1) are determined by whether edge{j}
2218 * is YES, NO or UNKNOWN. mins(j,j+2) and maxs(j,j+2) are related to
2219 * the dline atmostone/atleastone status for edges j and j+1.
2220 *
2221 * Then we calculate the remaining entries recursively. We definitely
2222 * know that
2223 * mins(j,k) >= { mins(j,u) + mins(u,k) } for any u between j and k.
2224 * This is because any valid placement of YESs between j and k must give
2225 * a valid placement between j and u, and also between u and k.
2226 * I believe it's sufficient to use just the two values of u:
2227 * j+1 and j+2. Seems to work well in practice - the bounds we compute
2228 * are rigorous, even if they might not be best-possible.
2229 *
2230 * Once we have maxs and mins calculated, we can make inferences about
2231 * each dline{j,j+1} by looking at the possible complementary edge-counts
2232 * mins(j+2,j) and maxs(j+2,j) and comparing these with the face clue.
2233 * As well as dlines, we can make similar inferences about single edges.
2234 * For example, consider a pentagon with clue 3, and we know at most one
2235 * of (edge0, edge1) is YES, and at most one of (edge2, edge3) is YES.
2236 * We could then deduce edge4 is YES, because maxs(0,4) would be 2, so
2237 * that final edge would have to be YES to make the count up to 3.
2238 */
2239
2240 /* Much quicker to allocate arrays on the stack than the heap, so
2241 * define the largest possible face size, and base our array allocations
2242 * on that. We check this with an assertion, in case someone decides to
2243 * make a grid which has larger faces than this. Note, this algorithm
2244 * could get quite expensive if there are many large faces. */
2245#define MAX_FACE_SIZE 12
2246
2247 for (i = 0; i < g->num_faces; i++) {
2248 int maxs[MAX_FACE_SIZE][MAX_FACE_SIZE];
2249 int mins[MAX_FACE_SIZE][MAX_FACE_SIZE];
2250 grid_face *f = g->faces + i;
2251 int N = f->order;
2252 int j,m;
2253 int clue = state->clues[i];
2254 assert(N <= MAX_FACE_SIZE);
2255 if (sstate->face_solved[i])
2256 continue;
2257 if (clue < 0) continue;
2258
2259 /* Calculate the (j,j+1) entries */
2260 for (j = 0; j < N; j++) {
2261 int edge_index = f->edges[j] - g->edges;
2262 int dline_index;
2263 enum line_state line1 = state->lines[edge_index];
2264 enum line_state line2;
2265 int tmp;
2266 int k = j + 1;
2267 if (k >= N) k = 0;
2268 maxs[j][k] = (line1 == LINE_NO) ? 0 : 1;
2269 mins[j][k] = (line1 == LINE_YES) ? 1 : 0;
2270 /* Calculate the (j,j+2) entries */
2271 dline_index = dline_index_from_face(g, f, k);
2272 edge_index = f->edges[k] - g->edges;
2273 line2 = state->lines[edge_index];
2274 k++;
2275 if (k >= N) k = 0;
2276
2277 /* max */
2278 tmp = 2;
2279 if (line1 == LINE_NO) tmp--;
2280 if (line2 == LINE_NO) tmp--;
2281 if (tmp == 2 && is_atmostone(dlines, dline_index))
2282 tmp = 1;
2283 maxs[j][k] = tmp;
2284
2285 /* min */
2286 tmp = 0;
2287 if (line1 == LINE_YES) tmp++;
2288 if (line2 == LINE_YES) tmp++;
2289 if (tmp == 0 && is_atleastone(dlines, dline_index))
2290 tmp = 1;
2291 mins[j][k] = tmp;
2292 }
2293
2294 /* Calculate the (j,j+m) entries for m between 3 and N-1 */
2295 for (m = 3; m < N; m++) {
2296 for (j = 0; j < N; j++) {
2297 int k = j + m;
2298 int u = j + 1;
2299 int v = j + 2;
2300 int tmp;
2301 if (k >= N) k -= N;
2302 if (u >= N) u -= N;
2303 if (v >= N) v -= N;
2304 maxs[j][k] = maxs[j][u] + maxs[u][k];
2305 mins[j][k] = mins[j][u] + mins[u][k];
2306 tmp = maxs[j][v] + maxs[v][k];
2307 maxs[j][k] = min(maxs[j][k], tmp);
2308 tmp = mins[j][v] + mins[v][k];
2309 mins[j][k] = max(mins[j][k], tmp);
2310 }
2311 }
2312
2313 /* See if we can make any deductions */
2314 for (j = 0; j < N; j++) {
2315 int k;
2316 grid_edge *e = f->edges[j];
2317 int line_index = e - g->edges;
2318 int dline_index;
2319
2320 if (state->lines[line_index] != LINE_UNKNOWN)
2321 continue;
2322 k = j + 1;
2323 if (k >= N) k = 0;
2324
2325 /* minimum YESs in the complement of this edge */
2326 if (mins[k][j] > clue) {
2327 sstate->solver_status = SOLVER_MISTAKE;
2328 return DIFF_EASY;
2329 }
2330 if (mins[k][j] == clue) {
2331 /* setting this edge to YES would make at least
2332 * (clue+1) edges - contradiction */
2333 solver_set_line(sstate, line_index, LINE_NO);
2334 diff = min(diff, DIFF_EASY);
2335 }
2336 if (maxs[k][j] < clue - 1) {
2337 sstate->solver_status = SOLVER_MISTAKE;
2338 return DIFF_EASY;
2339 }
2340 if (maxs[k][j] == clue - 1) {
2341 /* Only way to satisfy the clue is to set edge{j} as YES */
2342 solver_set_line(sstate, line_index, LINE_YES);
2343 diff = min(diff, DIFF_EASY);
2344 }
2345
2346 /* More advanced deduction that allows propagation along diagonal
2347 * chains of faces connected by dots, for example, 3-2-...-2-3
2348 * in square grids. */
2349 if (sstate->diff >= DIFF_TRICKY) {
2350 /* Now see if we can make dline deduction for edges{j,j+1} */
2351 e = f->edges[k];
2352 if (state->lines[e - g->edges] != LINE_UNKNOWN)
2353 /* Only worth doing this for an UNKNOWN,UNKNOWN pair.
2354 * Dlines where one of the edges is known, are handled in the
2355 * dot-deductions */
2356 continue;
2357
2358 dline_index = dline_index_from_face(g, f, k);
2359 k++;
2360 if (k >= N) k = 0;
2361
2362 /* minimum YESs in the complement of this dline */
2363 if (mins[k][j] > clue - 2) {
2364 /* Adding 2 YESs would break the clue */
2365 if (set_atmostone(dlines, dline_index))
2366 diff = min(diff, DIFF_NORMAL);
2367 }
2368 /* maximum YESs in the complement of this dline */
2369 if (maxs[k][j] < clue) {
2370 /* Adding 2 NOs would mean not enough YESs */
2371 if (set_atleastone(dlines, dline_index))
2372 diff = min(diff, DIFF_NORMAL);
2373 }
2374 }
2375 }
2376 }
2377
2378 if (diff < DIFF_NORMAL)
2379 return diff;
2380
2381 /* ------ Dot deductions ------ */
2382
2383 for (i = 0; i < g->num_dots; i++) {
2384 grid_dot *d = g->dots + i;
2385 int N = d->order;
2386 int yes, no, unknown;
2387 int j;
2388 if (sstate->dot_solved[i])
2389 continue;
2390 yes = sstate->dot_yes_count[i];
2391 no = sstate->dot_no_count[i];
2392 unknown = N - yes - no;
2393
2394 for (j = 0; j < N; j++) {
2395 int k;
2396 int dline_index;
2397 int line1_index, line2_index;
2398 enum line_state line1, line2;
2399 k = j + 1;
2400 if (k >= N) k = 0;
2401 dline_index = dline_index_from_dot(g, d, j);
2402 line1_index = d->edges[j] - g->edges;
2403 line2_index = d->edges[k] - g->edges;
2404 line1 = state->lines[line1_index];
2405 line2 = state->lines[line2_index];
2406
2407 /* Infer dline state from line state */
2408 if (line1 == LINE_NO || line2 == LINE_NO) {
2409 if (set_atmostone(dlines, dline_index))
2410 diff = min(diff, DIFF_NORMAL);
2411 }
2412 if (line1 == LINE_YES || line2 == LINE_YES) {
2413 if (set_atleastone(dlines, dline_index))
2414 diff = min(diff, DIFF_NORMAL);
2415 }
2416 /* Infer line state from dline state */
2417 if (is_atmostone(dlines, dline_index)) {
2418 if (line1 == LINE_YES && line2 == LINE_UNKNOWN) {
2419 solver_set_line(sstate, line2_index, LINE_NO);
2420 diff = min(diff, DIFF_EASY);
2421 }
2422 if (line2 == LINE_YES && line1 == LINE_UNKNOWN) {
2423 solver_set_line(sstate, line1_index, LINE_NO);
2424 diff = min(diff, DIFF_EASY);
2425 }
2426 }
2427 if (is_atleastone(dlines, dline_index)) {
2428 if (line1 == LINE_NO && line2 == LINE_UNKNOWN) {
2429 solver_set_line(sstate, line2_index, LINE_YES);
2430 diff = min(diff, DIFF_EASY);
2431 }
2432 if (line2 == LINE_NO && line1 == LINE_UNKNOWN) {
2433 solver_set_line(sstate, line1_index, LINE_YES);
2434 diff = min(diff, DIFF_EASY);
2435 }
2436 }
2437 /* Deductions that depend on the numbers of lines.
2438 * Only bother if both lines are UNKNOWN, otherwise the
2439 * easy-mode solver (or deductions above) would have taken
2440 * care of it. */
2441 if (line1 != LINE_UNKNOWN || line2 != LINE_UNKNOWN)
2442 continue;
2443
2444 if (yes == 0 && unknown == 2) {
2445 /* Both these unknowns must be identical. If we know
2446 * atmostone or atleastone, we can make progress. */
2447 if (is_atmostone(dlines, dline_index)) {
2448 solver_set_line(sstate, line1_index, LINE_NO);
2449 solver_set_line(sstate, line2_index, LINE_NO);
2450 diff = min(diff, DIFF_EASY);
2451 }
2452 if (is_atleastone(dlines, dline_index)) {
2453 solver_set_line(sstate, line1_index, LINE_YES);
2454 solver_set_line(sstate, line2_index, LINE_YES);
2455 diff = min(diff, DIFF_EASY);
2456 }
2457 }
2458 if (yes == 1) {
2459 if (set_atmostone(dlines, dline_index))
2460 diff = min(diff, DIFF_NORMAL);
2461 if (unknown == 2) {
2462 if (set_atleastone(dlines, dline_index))
2463 diff = min(diff, DIFF_NORMAL);
2464 }
2465 }
2466
2467 /* More advanced deduction that allows propagation along diagonal
2468 * chains of faces connected by dots, for example: 3-2-...-2-3
2469 * in square grids. */
2470 if (sstate->diff >= DIFF_TRICKY) {
2471 /* If we have atleastone set for this dline, infer
2472 * atmostone for each "opposite" dline (that is, each
2473 * dline without edges in common with this one).
2474 * Again, this test is only worth doing if both these
2475 * lines are UNKNOWN. For if one of these lines were YES,
2476 * the (yes == 1) test above would kick in instead. */
2477 if (is_atleastone(dlines, dline_index)) {
2478 int opp;
2479 for (opp = 0; opp < N; opp++) {
2480 int opp_dline_index;
2481 if (opp == j || opp == j+1 || opp == j-1)
2482 continue;
2483 if (j == 0 && opp == N-1)
2484 continue;
2485 if (j == N-1 && opp == 0)
2486 continue;
2487 opp_dline_index = dline_index_from_dot(g, d, opp);
2488 if (set_atmostone(dlines, opp_dline_index))
2489 diff = min(diff, DIFF_NORMAL);
2490 }
2491 if (yes == 0 && is_atmostone(dlines, dline_index)) {
2492 /* This dline has *exactly* one YES and there are no
2493 * other YESs. This allows more deductions. */
2494 if (unknown == 3) {
2495 /* Third unknown must be YES */
2496 for (opp = 0; opp < N; opp++) {
2497 int opp_index;
2498 if (opp == j || opp == k)
2499 continue;
2500 opp_index = d->edges[opp] - g->edges;
2501 if (state->lines[opp_index] == LINE_UNKNOWN) {
2502 solver_set_line(sstate, opp_index,
2503 LINE_YES);
2504 diff = min(diff, DIFF_EASY);
2505 }
2506 }
2507 } else if (unknown == 4) {
2508 /* Exactly one of opposite UNKNOWNS is YES. We've
2509 * already set atmostone, so set atleastone as
2510 * well.
2511 */
2512 if (dline_set_opp_atleastone(sstate, d, j))
2513 diff = min(diff, DIFF_NORMAL);
2514 }
2515 }
2516 }
2517 }
2518 }
2519 }
2520 return diff;
2521}
2522
2523static int linedsf_deductions(solver_state *sstate)
2524{
2525 game_state *state = sstate->state;
2526 grid *g = state->game_grid;
2527 char *dlines = sstate->dlines;
2528 int i;
2529 int diff = DIFF_MAX;
2530 int diff_tmp;
2531
2532 /* ------ Face deductions ------ */
2533
2534 /* A fully-general linedsf deduction seems overly complicated
2535 * (I suspect the problem is NP-complete, though in practice it might just
2536 * be doable because faces are limited in size).
2537 * For simplicity, we only consider *pairs* of LINE_UNKNOWNS that are
2538 * known to be identical. If setting them both to YES (or NO) would break
2539 * the clue, set them to NO (or YES). */
2540
2541 for (i = 0; i < g->num_faces; i++) {
2542 int N, yes, no, unknown;
2543 int clue;
2544
2545 if (sstate->face_solved[i])
2546 continue;
2547 clue = state->clues[i];
2548 if (clue < 0)
2549 continue;
2550
2551 N = g->faces[i].order;
2552 yes = sstate->face_yes_count[i];
2553 if (yes + 1 == clue) {
2554 if (face_setall_identical(sstate, i, LINE_NO))
2555 diff = min(diff, DIFF_EASY);
2556 }
2557 no = sstate->face_no_count[i];
2558 if (no + 1 == N - clue) {
2559 if (face_setall_identical(sstate, i, LINE_YES))
2560 diff = min(diff, DIFF_EASY);
2561 }
2562
2563 /* Reload YES count, it might have changed */
2564 yes = sstate->face_yes_count[i];
2565 unknown = N - no - yes;
2566
2567 /* Deductions with small number of LINE_UNKNOWNs, based on overall
2568 * parity of lines. */
2569 diff_tmp = parity_deductions(sstate, g->faces[i].edges,
2570 (clue - yes) % 2, unknown);
2571 diff = min(diff, diff_tmp);
2572 }
2573
2574 /* ------ Dot deductions ------ */
2575 for (i = 0; i < g->num_dots; i++) {
2576 grid_dot *d = g->dots + i;
2577 int N = d->order;
2578 int j;
2579 int yes, no, unknown;
2580 /* Go through dlines, and do any dline<->linedsf deductions wherever
2581 * we find two UNKNOWNS. */
2582 for (j = 0; j < N; j++) {
2583 int dline_index = dline_index_from_dot(g, d, j);
2584 int line1_index;
2585 int line2_index;
2586 int can1, can2, inv1, inv2;
2587 int j2;
2588 line1_index = d->edges[j] - g->edges;
2589 if (state->lines[line1_index] != LINE_UNKNOWN)
2590 continue;
2591 j2 = j + 1;
2592 if (j2 == N) j2 = 0;
2593 line2_index = d->edges[j2] - g->edges;
2594 if (state->lines[line2_index] != LINE_UNKNOWN)
2595 continue;
2596 /* Infer dline flags from linedsf */
2597 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1);
2598 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2);
2599 if (can1 == can2 && inv1 != inv2) {
2600 /* These are opposites, so set dline atmostone/atleastone */
2601 if (set_atmostone(dlines, dline_index))
2602 diff = min(diff, DIFF_NORMAL);
2603 if (set_atleastone(dlines, dline_index))
2604 diff = min(diff, DIFF_NORMAL);
2605 continue;
2606 }
2607 /* Infer linedsf from dline flags */
2608 if (is_atmostone(dlines, dline_index)
2609 && is_atleastone(dlines, dline_index)) {
2610 if (merge_lines(sstate, line1_index, line2_index, 1))
2611 diff = min(diff, DIFF_HARD);
2612 }
2613 }
2614
2615 /* Deductions with small number of LINE_UNKNOWNs, based on overall
2616 * parity of lines. */
2617 yes = sstate->dot_yes_count[i];
2618 no = sstate->dot_no_count[i];
2619 unknown = N - yes - no;
2620 diff_tmp = parity_deductions(sstate, d->edges,
2621 yes % 2, unknown);
2622 diff = min(diff, diff_tmp);
2623 }
2624
2625 /* ------ Edge dsf deductions ------ */
2626
2627 /* If the state of a line is known, deduce the state of its canonical line
2628 * too, and vice versa. */
2629 for (i = 0; i < g->num_edges; i++) {
2630 int can, inv;
2631 enum line_state s;
2632 can = edsf_canonify(sstate->linedsf, i, &inv);
2633 if (can == i)
2634 continue;
2635 s = sstate->state->lines[can];
2636 if (s != LINE_UNKNOWN) {
2637 if (solver_set_line(sstate, i, inv ? OPP(s) : s))
2638 diff = min(diff, DIFF_EASY);
2639 } else {
2640 s = sstate->state->lines[i];
2641 if (s != LINE_UNKNOWN) {
2642 if (solver_set_line(sstate, can, inv ? OPP(s) : s))
2643 diff = min(diff, DIFF_EASY);
2644 }
2645 }
2646 }
2647
2648 return diff;
2649}
2650
2651static int loop_deductions(solver_state *sstate)
2652{
2653 int edgecount = 0, clues = 0, satclues = 0, sm1clues = 0;
2654 game_state *state = sstate->state;
2655 grid *g = state->game_grid;
2656 int shortest_chainlen = g->num_dots;
2657 int loop_found = FALSE;
2658 int dots_connected;
2659 int progress = FALSE;
2660 int i;
2661
2662 /*
2663 * Go through the grid and update for all the new edges.
2664 * Since merge_dots() is idempotent, the simplest way to
2665 * do this is just to update for _all_ the edges.
2666 * Also, while we're here, we count the edges.
2667 */
2668 for (i = 0; i < g->num_edges; i++) {
2669 if (state->lines[i] == LINE_YES) {
2670 loop_found |= merge_dots(sstate, i);
2671 edgecount++;
2672 }
2673 }
2674
2675 /*
2676 * Count the clues, count the satisfied clues, and count the
2677 * satisfied-minus-one clues.
2678 */
2679 for (i = 0; i < g->num_faces; i++) {
2680 int c = state->clues[i];
2681 if (c >= 0) {
2682 int o = sstate->face_yes_count[i];
2683 if (o == c)
2684 satclues++;
2685 else if (o == c-1)
2686 sm1clues++;
2687 clues++;
2688 }
2689 }
2690
2691 for (i = 0; i < g->num_dots; ++i) {
2692 dots_connected =
2693 sstate->looplen[dsf_canonify(sstate->dotdsf, i)];
2694 if (dots_connected > 1)
2695 shortest_chainlen = min(shortest_chainlen, dots_connected);
2696 }
2697
2698 assert(sstate->solver_status == SOLVER_INCOMPLETE);
2699
2700 if (satclues == clues && shortest_chainlen == edgecount) {
2701 sstate->solver_status = SOLVER_SOLVED;
2702 /* This discovery clearly counts as progress, even if we haven't
2703 * just added any lines or anything */
2704 progress = TRUE;
2705 goto finished_loop_deductionsing;
2706 }
2707
2708 /*
2709 * Now go through looking for LINE_UNKNOWN edges which
2710 * connect two dots that are already in the same
2711 * equivalence class. If we find one, test to see if the
2712 * loop it would create is a solution.
2713 */
2714 for (i = 0; i < g->num_edges; i++) {
2715 grid_edge *e = g->edges + i;
2716 int d1 = e->dot1 - g->dots;
2717 int d2 = e->dot2 - g->dots;
2718 int eqclass, val;
2719 if (state->lines[i] != LINE_UNKNOWN)
2720 continue;
2721
2722 eqclass = dsf_canonify(sstate->dotdsf, d1);
2723 if (eqclass != dsf_canonify(sstate->dotdsf, d2))
2724 continue;
2725
2726 val = LINE_NO; /* loop is bad until proven otherwise */
2727
2728 /*
2729 * This edge would form a loop. Next
2730 * question: how long would the loop be?
2731 * Would it equal the total number of edges
2732 * (plus the one we'd be adding if we added
2733 * it)?
2734 */
2735 if (sstate->looplen[eqclass] == edgecount + 1) {
2736 int sm1_nearby;
2737
2738 /*
2739 * This edge would form a loop which
2740 * took in all the edges in the entire
2741 * grid. So now we need to work out
2742 * whether it would be a valid solution
2743 * to the puzzle, which means we have to
2744 * check if it satisfies all the clues.
2745 * This means that every clue must be
2746 * either satisfied or satisfied-minus-
2747 * 1, and also that the number of
2748 * satisfied-minus-1 clues must be at
2749 * most two and they must lie on either
2750 * side of this edge.
2751 */
2752 sm1_nearby = 0;
2753 if (e->face1) {
2754 int f = e->face1 - g->faces;
2755 int c = state->clues[f];
2756 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2757 sm1_nearby++;
2758 }
2759 if (e->face2) {
2760 int f = e->face2 - g->faces;
2761 int c = state->clues[f];
2762 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2763 sm1_nearby++;
2764 }
2765 if (sm1clues == sm1_nearby &&
2766 sm1clues + satclues == clues) {
2767 val = LINE_YES; /* loop is good! */
2768 }
2769 }
2770
2771 /*
2772 * Right. Now we know that adding this edge
2773 * would form a loop, and we know whether
2774 * that loop would be a viable solution or
2775 * not.
2776 *
2777 * If adding this edge produces a solution,
2778 * then we know we've found _a_ solution but
2779 * we don't know that it's _the_ solution -
2780 * if it were provably the solution then
2781 * we'd have deduced this edge some time ago
2782 * without the need to do loop detection. So
2783 * in this state we return SOLVER_AMBIGUOUS,
2784 * which has the effect that hitting Solve
2785 * on a user-provided puzzle will fill in a
2786 * solution but using the solver to
2787 * construct new puzzles won't consider this
2788 * a reasonable deduction for the user to
2789 * make.
2790 */
2791 progress = solver_set_line(sstate, i, val);
2792 assert(progress == TRUE);
2793 if (val == LINE_YES) {
2794 sstate->solver_status = SOLVER_AMBIGUOUS;
2795 goto finished_loop_deductionsing;
2796 }
2797 }
2798
2799 finished_loop_deductionsing:
2800 return progress ? DIFF_EASY : DIFF_MAX;
2801}
2802
2803/* This will return a dynamically allocated solver_state containing the (more)
2804 * solved grid */
2805static solver_state *solve_game_rec(const solver_state *sstate_start)
2806{
2807 solver_state *sstate;
2808
2809 /* Index of the solver we should call next. */
2810 int i = 0;
2811
2812 /* As a speed-optimisation, we avoid re-running solvers that we know
2813 * won't make any progress. This happens when a high-difficulty
2814 * solver makes a deduction that can only help other high-difficulty
2815 * solvers.
2816 * For example: if a new 'dline' flag is set by dline_deductions, the
2817 * trivial_deductions solver cannot do anything with this information.
2818 * If we've already run the trivial_deductions solver (because it's
2819 * earlier in the list), there's no point running it again.
2820 *
2821 * Therefore: if a solver is earlier in the list than "threshold_index",
2822 * we don't bother running it if it's difficulty level is less than
2823 * "threshold_diff".
2824 */
2825 int threshold_diff = 0;
2826 int threshold_index = 0;
2827
2828 sstate = dup_solver_state(sstate_start);
2829
2830 check_caches(sstate);
2831
2832 while (i < NUM_SOLVERS) {
2833 if (sstate->solver_status == SOLVER_MISTAKE)
2834 return sstate;
2835 if (sstate->solver_status == SOLVER_SOLVED ||
2836 sstate->solver_status == SOLVER_AMBIGUOUS) {
2837 /* solver finished */
2838 break;
2839 }
2840
2841 if ((solver_diffs[i] >= threshold_diff || i >= threshold_index)
2842 && solver_diffs[i] <= sstate->diff) {
2843 /* current_solver is eligible, so use it */
2844 int next_diff = solver_fns[i](sstate);
2845 if (next_diff != DIFF_MAX) {
2846 /* solver made progress, so use new thresholds and
2847 * start again at top of list. */
2848 threshold_diff = next_diff;
2849 threshold_index = i;
2850 i = 0;
2851 continue;
2852 }
2853 }
2854 /* current_solver is ineligible, or failed to make progress, so
2855 * go to the next solver in the list */
2856 i++;
2857 }
2858
2859 if (sstate->solver_status == SOLVER_SOLVED ||
2860 sstate->solver_status == SOLVER_AMBIGUOUS) {
2861 /* s/LINE_UNKNOWN/LINE_NO/g */
2862 array_setall(sstate->state->lines, LINE_UNKNOWN, LINE_NO,
2863 sstate->state->game_grid->num_edges);
2864 return sstate;
2865 }
2866
2867 return sstate;
2868}
2869
2870static char *solve_game(const game_state *state, const game_state *currstate,
2871 const char *aux, char **error)
2872{
2873 char *soln = NULL;
2874 solver_state *sstate, *new_sstate;
2875
2876 sstate = new_solver_state(state, DIFF_MAX);
2877 new_sstate = solve_game_rec(sstate);
2878
2879 if (new_sstate->solver_status == SOLVER_SOLVED) {
2880 soln = encode_solve_move(new_sstate->state);
2881 } else if (new_sstate->solver_status == SOLVER_AMBIGUOUS) {
2882 soln = encode_solve_move(new_sstate->state);
2883 /**error = "Solver found ambiguous solutions"; */
2884 } else {
2885 soln = encode_solve_move(new_sstate->state);
2886 /**error = "Solver failed"; */
2887 }
2888
2889 free_solver_state(new_sstate);
2890 free_solver_state(sstate);
2891
2892 return soln;
2893}
2894
2895/* ----------------------------------------------------------------------
2896 * Drawing and mouse-handling
2897 */
2898
2899static char *interpret_move(const game_state *state, game_ui *ui,
2900 const game_drawstate *ds,
2901 int x, int y, int button)
2902{
2903 grid *g = state->game_grid;
2904 grid_edge *e;
2905 int i;
2906 char *ret, buf[80];
2907 char button_char = ' ';
2908 enum line_state old_state;
2909
2910 button &= ~MOD_MASK;
2911
2912 /* Convert mouse-click (x,y) to grid coordinates */
2913 x -= BORDER(ds->tilesize);
2914 y -= BORDER(ds->tilesize);
2915 x = x * g->tilesize / ds->tilesize;
2916 y = y * g->tilesize / ds->tilesize;
2917 x += g->lowest_x;
2918 y += g->lowest_y;
2919
2920 e = grid_nearest_edge(g, x, y);
2921 if (e == NULL)
2922 return NULL;
2923
2924 i = e - g->edges;
2925
2926 /* I think it's only possible to play this game with mouse clicks, sorry */
2927 /* Maybe will add mouse drag support some time */
2928 old_state = state->lines[i];
2929
2930 switch (button) {
2931 case LEFT_BUTTON:
2932 switch (old_state) {
2933 case LINE_UNKNOWN:
2934 button_char = 'y';
2935 break;
2936 case LINE_YES:
2937#ifdef STYLUS_BASED
2938 button_char = 'n';
2939 break;
2940#endif
2941 case LINE_NO:
2942 button_char = 'u';
2943 break;
2944 }
2945 break;
2946 case MIDDLE_BUTTON:
2947 button_char = 'u';
2948 break;
2949 case RIGHT_BUTTON:
2950 switch (old_state) {
2951 case LINE_UNKNOWN:
2952 button_char = 'n';
2953 break;
2954 case LINE_NO:
2955#ifdef STYLUS_BASED
2956 button_char = 'y';
2957 break;
2958#endif
2959 case LINE_YES:
2960 button_char = 'u';
2961 break;
2962 }
2963 break;
2964 default:
2965 return NULL;
2966 }
2967
2968
2969 sprintf(buf, "%d%c", i, (int)button_char);
2970 ret = dupstr(buf);
2971
2972 return ret;
2973}
2974
2975static game_state *execute_move(const game_state *state, const char *move)
2976{
2977 int i;
2978 game_state *newstate = dup_game(state);
2979
2980 if (move[0] == 'S') {
2981 move++;
2982 newstate->cheated = TRUE;
2983 }
2984
2985 while (*move) {
2986 i = atoi(move);
2987 if (i < 0 || i >= newstate->game_grid->num_edges)
2988 goto fail;
2989 move += strspn(move, "1234567890");
2990 switch (*(move++)) {
2991 case 'y':
2992 newstate->lines[i] = LINE_YES;
2993 break;
2994 case 'n':
2995 newstate->lines[i] = LINE_NO;
2996 break;
2997 case 'u':
2998 newstate->lines[i] = LINE_UNKNOWN;
2999 break;
3000 default:
3001 goto fail;
3002 }
3003 }
3004
3005 /*
3006 * Check for completion.
3007 */
3008 if (check_completion(newstate))
3009 newstate->solved = TRUE;
3010
3011 return newstate;
3012
3013 fail:
3014 free_game(newstate);
3015 return NULL;
3016}
3017
3018/* ----------------------------------------------------------------------
3019 * Drawing routines.
3020 */
3021
3022/* Convert from grid coordinates to screen coordinates */
3023static void grid_to_screen(const game_drawstate *ds, const grid *g,
3024 int grid_x, int grid_y, int *x, int *y)
3025{
3026 *x = grid_x - g->lowest_x;
3027 *y = grid_y - g->lowest_y;
3028 *x = *x * ds->tilesize / g->tilesize;
3029 *y = *y * ds->tilesize / g->tilesize;
3030 *x += BORDER(ds->tilesize);
3031 *y += BORDER(ds->tilesize);
3032}
3033
3034/* Returns (into x,y) position of centre of face for rendering the text clue.
3035 */
3036static void face_text_pos(const game_drawstate *ds, const grid *g,
3037 grid_face *f, int *xret, int *yret)
3038{
3039 int faceindex = f - g->faces;
3040
3041 /*
3042 * Return the cached position for this face, if we've already
3043 * worked it out.
3044 */
3045 if (ds->textx[faceindex] >= 0) {
3046 *xret = ds->textx[faceindex];
3047 *yret = ds->texty[faceindex];
3048 return;
3049 }
3050
3051 /*
3052 * Otherwise, use the incentre computed by grid.c and convert it
3053 * to screen coordinates.
3054 */
3055 grid_find_incentre(f);
3056 grid_to_screen(ds, g, f->ix, f->iy,
3057 &ds->textx[faceindex], &ds->texty[faceindex]);
3058
3059 *xret = ds->textx[faceindex];
3060 *yret = ds->texty[faceindex];
3061}
3062
3063static void face_text_bbox(game_drawstate *ds, grid *g, grid_face *f,
3064 int *x, int *y, int *w, int *h)
3065{
3066 int xx, yy;
3067 face_text_pos(ds, g, f, &xx, &yy);
3068
3069 /* There seems to be a certain amount of trial-and-error involved
3070 * in working out the correct bounding-box for the text. */
3071
3072 *x = xx - ds->tilesize/4 - 1;
3073 *y = yy - ds->tilesize/4 - 3;
3074 *w = ds->tilesize/2 + 2;
3075 *h = ds->tilesize/2 + 5;
3076}
3077
3078static void game_redraw_clue(drawing *dr, game_drawstate *ds,
3079 const game_state *state, int i)
3080{
3081 grid *g = state->game_grid;
3082 grid_face *f = g->faces + i;
3083 int x, y;
3084 char c[20];
3085
3086 sprintf(c, "%d", state->clues[i]);
3087
3088 face_text_pos(ds, g, f, &x, &y);
3089 draw_text(dr, x, y,
3090 FONT_VARIABLE, ds->tilesize/2,
3091 ALIGN_VCENTRE | ALIGN_HCENTRE,
3092 ds->clue_error[i] ? COL_MISTAKE :
3093 ds->clue_satisfied[i] ? COL_SATISFIED : COL_FOREGROUND, c);
3094}
3095
3096static void edge_bbox(game_drawstate *ds, grid *g, grid_edge *e,
3097 int *x, int *y, int *w, int *h)
3098{
3099 int x1 = e->dot1->x;
3100 int y1 = e->dot1->y;
3101 int x2 = e->dot2->x;
3102 int y2 = e->dot2->y;
3103 int xmin, xmax, ymin, ymax;
3104
3105 grid_to_screen(ds, g, x1, y1, &x1, &y1);
3106 grid_to_screen(ds, g, x2, y2, &x2, &y2);
3107 /* Allow extra margin for dots, and thickness of lines */
3108 xmin = min(x1, x2) - 2;
3109 xmax = max(x1, x2) + 2;
3110 ymin = min(y1, y2) - 2;
3111 ymax = max(y1, y2) + 2;
3112
3113 *x = xmin;
3114 *y = ymin;
3115 *w = xmax - xmin + 1;
3116 *h = ymax - ymin + 1;
3117}
3118
3119static void dot_bbox(game_drawstate *ds, grid *g, grid_dot *d,
3120 int *x, int *y, int *w, int *h)
3121{
3122 int x1, y1;
3123
3124 grid_to_screen(ds, g, d->x, d->y, &x1, &y1);
3125
3126 *x = x1 - 2;
3127 *y = y1 - 2;
3128 *w = 5;
3129 *h = 5;
3130}
3131
3132static const int loopy_line_redraw_phases[] = {
3133 COL_FAINT, COL_LINEUNKNOWN, COL_FOREGROUND, COL_HIGHLIGHT, COL_MISTAKE
3134};
3135#define NPHASES lenof(loopy_line_redraw_phases)
3136
3137static void game_redraw_line(drawing *dr, game_drawstate *ds,
3138 const game_state *state, int i, int phase)
3139{
3140 grid *g = state->game_grid;
3141 grid_edge *e = g->edges + i;
3142 int x1, x2, y1, y2;
3143 int line_colour;
3144
3145 if (state->line_errors[i])
3146 line_colour = COL_MISTAKE;
3147 else if (state->lines[i] == LINE_UNKNOWN)
3148 line_colour = COL_LINEUNKNOWN;
3149 else if (state->lines[i] == LINE_NO)
3150 line_colour = COL_FAINT;
3151 else if (ds->flashing)
3152 line_colour = COL_HIGHLIGHT;
3153 else
3154 line_colour = COL_FOREGROUND;
3155 if (line_colour != loopy_line_redraw_phases[phase])
3156 return;
3157
3158 /* Convert from grid to screen coordinates */
3159 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1);
3160 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
3161
3162 if (line_colour == COL_FAINT) {
3163 static int draw_faint_lines = -1;
3164 if (draw_faint_lines < 0) {
3165 char *env = getenv("LOOPY_FAINT_LINES");
3166 draw_faint_lines = (!env || (env[0] == 'y' ||
3167 env[0] == 'Y'));
3168 }
3169 if (draw_faint_lines)
3170 draw_line(dr, x1, y1, x2, y2, line_colour);
3171 } else {
3172 draw_thick_line(dr, 3.0,
3173 x1 + 0.5, y1 + 0.5,
3174 x2 + 0.5, y2 + 0.5,
3175 line_colour);
3176 }
3177}
3178
3179static void game_redraw_dot(drawing *dr, game_drawstate *ds,
3180 const game_state *state, int i)
3181{
3182 grid *g = state->game_grid;
3183 grid_dot *d = g->dots + i;
3184 int x, y;
3185
3186 grid_to_screen(ds, g, d->x, d->y, &x, &y);
3187 draw_circle(dr, x, y, 2, COL_FOREGROUND, COL_FOREGROUND);
3188}
3189
3190static int boxes_intersect(int x0, int y0, int w0, int h0,
3191 int x1, int y1, int w1, int h1)
3192{
3193 /*
3194 * Two intervals intersect iff neither is wholly on one side of
3195 * the other. Two boxes intersect iff their horizontal and
3196 * vertical intervals both intersect.
3197 */
3198 return (x0 < x1+w1 && x1 < x0+w0 && y0 < y1+h1 && y1 < y0+h0);
3199}
3200
3201static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
3202 const game_state *state,
3203 int x, int y, int w, int h)
3204{
3205 grid *g = state->game_grid;
3206 int i, phase;
3207 int bx, by, bw, bh;
3208
3209 clip(dr, x, y, w, h);
3210 draw_rect(dr, x, y, w, h, COL_BACKGROUND);
3211
3212 for (i = 0; i < g->num_faces; i++) {
3213 if (state->clues[i] >= 0) {
3214 face_text_bbox(ds, g, &g->faces[i], &bx, &by, &bw, &bh);
3215 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3216 game_redraw_clue(dr, ds, state, i);
3217 }
3218 }
3219 for (phase = 0; phase < NPHASES; phase++) {
3220 for (i = 0; i < g->num_edges; i++) {
3221 edge_bbox(ds, g, &g->edges[i], &bx, &by, &bw, &bh);
3222 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3223 game_redraw_line(dr, ds, state, i, phase);
3224 }
3225 }
3226 for (i = 0; i < g->num_dots; i++) {
3227 dot_bbox(ds, g, &g->dots[i], &bx, &by, &bw, &bh);
3228 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3229 game_redraw_dot(dr, ds, state, i);
3230 }
3231
3232 unclip(dr);
3233 draw_update(dr, x, y, w, h);
3234}
3235
3236static void game_redraw(drawing *dr, game_drawstate *ds,
3237 const game_state *oldstate, const game_state *state,
3238 int dir, const game_ui *ui,
3239 float animtime, float flashtime)
3240{
3241#define REDRAW_OBJECTS_LIMIT 16 /* Somewhat arbitrary tradeoff */
3242
3243 grid *g = state->game_grid;
3244 int border = BORDER(ds->tilesize);
3245 int i;
3246 int flash_changed;
3247 int redraw_everything = FALSE;
3248
3249 int edges[REDRAW_OBJECTS_LIMIT], nedges = 0;
3250 int faces[REDRAW_OBJECTS_LIMIT], nfaces = 0;
3251
3252 /* Redrawing is somewhat involved.
3253 *
3254 * An update can theoretically affect an arbitrary number of edges
3255 * (consider, for example, completing or breaking a cycle which doesn't
3256 * satisfy all the clues -- we'll switch many edges between error and
3257 * normal states). On the other hand, redrawing the whole grid takes a
3258 * while, making the game feel sluggish, and many updates are actually
3259 * quite well localized.
3260 *
3261 * This redraw algorithm attempts to cope with both situations gracefully
3262 * and correctly. For localized changes, we set a clip rectangle, fill
3263 * it with background, and then redraw (a plausible but conservative
3264 * guess at) the objects which intersect the rectangle; if several
3265 * objects need redrawing, we'll do them individually. However, if lots
3266 * of objects are affected, we'll just redraw everything.
3267 *
3268 * The reason for all of this is that it's just not safe to do the redraw
3269 * piecemeal. If you try to draw an antialiased diagonal line over
3270 * itself, you get a slightly thicker antialiased diagonal line, which
3271 * looks rather ugly after a while.
3272 *
3273 * So, we take two passes over the grid. The first attempts to work out
3274 * what needs doing, and the second actually does it.
3275 */
3276
3277 if (!ds->started) {
3278 redraw_everything = TRUE;
3279 /*
3280 * But we must still go through the upcoming loops, so that we
3281 * set up stuff in ds correctly for the initial redraw.
3282 */
3283 }
3284
3285 /* First, trundle through the faces. */
3286 for (i = 0; i < g->num_faces; i++) {
3287 grid_face *f = g->faces + i;
3288 int sides = f->order;
3289 int yes_order, no_order;
3290 int clue_mistake;
3291 int clue_satisfied;
3292 int n = state->clues[i];
3293 if (n < 0)
3294 continue;
3295
3296 yes_order = face_order(state, i, LINE_YES);
3297 if (state->exactly_one_loop) {
3298 /*
3299 * Special case: if the set of LINE_YES edges in the grid
3300 * consists of exactly one loop and nothing else, then we
3301 * switch to treating LINE_UNKNOWN the same as LINE_NO for
3302 * purposes of clue checking.
3303 *
3304 * This is because some people like to play Loopy without
3305 * using the right-click, i.e. never setting anything to
3306 * LINE_NO. Without this special case, if a person playing
3307 * in that style fills in what they think is a correct
3308 * solution loop but in fact it has an underfilled clue,
3309 * then we will display no victory flash and also no error
3310 * highlight explaining why not. With this special case,
3311 * we light up underfilled clues at the instant the loop
3312 * is closed. (Of course, *overfilled* clues are fine
3313 * either way.)
3314 *
3315 * (It might still be considered unfortunate that we can't
3316 * warn this style of player any earlier, if they make a
3317 * mistake very near the beginning which doesn't show up
3318 * until they close the last edge of the loop. One other
3319 * thing we _could_ do here is to treat any LINE_UNKNOWN
3320 * as LINE_NO if either of its endpoints has yes-degree 2,
3321 * reflecting the fact that setting that line to YES would
3322 * be an obvious error. But I don't think even that could
3323 * catch _all_ clue errors in a timely manner; I think
3324 * there are some that won't be displayed until the loop
3325 * is filled in, even so, and there's no way to avoid that
3326 * with complete reliability except to switch to being a
3327 * player who sets things to LINE_NO.)
3328 */
3329 no_order = sides - yes_order;
3330 } else {
3331 no_order = face_order(state, i, LINE_NO);
3332 }
3333
3334 clue_mistake = (yes_order > n || no_order > (sides-n));
3335 clue_satisfied = (yes_order == n && no_order == (sides-n));
3336
3337 if (clue_mistake != ds->clue_error[i] ||
3338 clue_satisfied != ds->clue_satisfied[i]) {
3339 ds->clue_error[i] = clue_mistake;
3340 ds->clue_satisfied[i] = clue_satisfied;
3341 if (nfaces == REDRAW_OBJECTS_LIMIT)
3342 redraw_everything = TRUE;
3343 else
3344 faces[nfaces++] = i;
3345 }
3346 }
3347
3348 /* Work out what the flash state needs to be. */
3349 if (flashtime > 0 &&
3350 (flashtime <= FLASH_TIME/3 ||
3351 flashtime >= FLASH_TIME*2/3)) {
3352 flash_changed = !ds->flashing;
3353 ds->flashing = TRUE;
3354 } else {
3355 flash_changed = ds->flashing;
3356 ds->flashing = FALSE;
3357 }
3358
3359 /* Now, trundle through the edges. */
3360 for (i = 0; i < g->num_edges; i++) {
3361 char new_ds =
3362 state->line_errors[i] ? DS_LINE_ERROR : state->lines[i];
3363 if (new_ds != ds->lines[i] ||
3364 (flash_changed && state->lines[i] == LINE_YES)) {
3365 ds->lines[i] = new_ds;
3366 if (nedges == REDRAW_OBJECTS_LIMIT)
3367 redraw_everything = TRUE;
3368 else
3369 edges[nedges++] = i;
3370 }
3371 }
3372
3373 /* Pass one is now done. Now we do the actual drawing. */
3374 if (redraw_everything) {
3375 int grid_width = g->highest_x - g->lowest_x;
3376 int grid_height = g->highest_y - g->lowest_y;
3377 int w = grid_width * ds->tilesize / g->tilesize;
3378 int h = grid_height * ds->tilesize / g->tilesize;
3379
3380 game_redraw_in_rect(dr, ds, state,
3381 0, 0, w + 2*border + 1, h + 2*border + 1);
3382 } else {
3383
3384 /* Right. Now we roll up our sleeves. */
3385
3386 for (i = 0; i < nfaces; i++) {
3387 grid_face *f = g->faces + faces[i];
3388 int x, y, w, h;
3389
3390 face_text_bbox(ds, g, f, &x, &y, &w, &h);
3391 game_redraw_in_rect(dr, ds, state, x, y, w, h);
3392 }
3393
3394 for (i = 0; i < nedges; i++) {
3395 grid_edge *e = g->edges + edges[i];
3396 int x, y, w, h;
3397
3398 edge_bbox(ds, g, e, &x, &y, &w, &h);
3399 game_redraw_in_rect(dr, ds, state, x, y, w, h);
3400 }
3401 }
3402
3403 ds->started = TRUE;
3404}
3405
3406static float game_flash_length(const game_state *oldstate,
3407 const game_state *newstate, int dir, game_ui *ui)
3408{
3409 if (!oldstate->solved && newstate->solved &&
3410 !oldstate->cheated && !newstate->cheated) {
3411 return FLASH_TIME;
3412 }
3413
3414 return 0.0F;
3415}
3416
3417static int game_status(const game_state *state)
3418{
3419 return state->solved ? +1 : 0;
3420}
3421
3422static void game_print_size(const game_params *params, float *x, float *y)
3423{
3424 int pw, ph;
3425
3426 /*
3427 * I'll use 7mm "squares" by default.
3428 */
3429 game_compute_size(params, 700, &pw, &ph);
3430 *x = pw / 100.0F;
3431 *y = ph / 100.0F;
3432}
3433
3434static void game_print(drawing *dr, const game_state *state, int tilesize)
3435{
3436 int ink = print_mono_colour(dr, 0);
3437 int i;
3438 game_drawstate ads, *ds = &ads;
3439 grid *g = state->game_grid;
3440
3441 ds->tilesize = tilesize;
3442 ds->textx = snewn(g->num_faces, int);
3443 ds->texty = snewn(g->num_faces, int);
3444 for (i = 0; i < g->num_faces; i++)
3445 ds->textx[i] = ds->texty[i] = -1;
3446
3447 for (i = 0; i < g->num_dots; i++) {
3448 int x, y;
3449 grid_to_screen(ds, g, g->dots[i].x, g->dots[i].y, &x, &y);
3450 draw_circle(dr, x, y, ds->tilesize / 15, ink, ink);
3451 }
3452
3453 /*
3454 * Clues.
3455 */
3456 for (i = 0; i < g->num_faces; i++) {
3457 grid_face *f = g->faces + i;
3458 int clue = state->clues[i];
3459 if (clue >= 0) {
3460 char c[20];
3461 int x, y;
3462 sprintf(c, "%d", state->clues[i]);
3463 face_text_pos(ds, g, f, &x, &y);
3464 draw_text(dr, x, y,
3465 FONT_VARIABLE, ds->tilesize / 2,
3466 ALIGN_VCENTRE | ALIGN_HCENTRE, ink, c);
3467 }
3468 }
3469
3470 /*
3471 * Lines.
3472 */
3473 for (i = 0; i < g->num_edges; i++) {
3474 int thickness = (state->lines[i] == LINE_YES) ? 30 : 150;
3475 grid_edge *e = g->edges + i;
3476 int x1, y1, x2, y2;
3477 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1);
3478 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
3479 if (state->lines[i] == LINE_YES)
3480 {
3481 /* (dx, dy) points from (x1, y1) to (x2, y2).
3482 * The line is then "fattened" in a perpendicular
3483 * direction to create a thin rectangle. */
3484 double d = sqrt(SQ((double)x1 - x2) + SQ((double)y1 - y2));
3485 double dx = (x2 - x1) / d;
3486 double dy = (y2 - y1) / d;
3487 int points[8];
3488
3489 dx = (dx * ds->tilesize) / thickness;
3490 dy = (dy * ds->tilesize) / thickness;
3491 points[0] = x1 + (int)dy;
3492 points[1] = y1 - (int)dx;
3493 points[2] = x1 - (int)dy;
3494 points[3] = y1 + (int)dx;
3495 points[4] = x2 - (int)dy;
3496 points[5] = y2 + (int)dx;
3497 points[6] = x2 + (int)dy;
3498 points[7] = y2 - (int)dx;
3499 draw_polygon(dr, points, 4, ink, ink);
3500 }
3501 else
3502 {
3503 /* Draw a dotted line */
3504 int divisions = 6;
3505 int j;
3506 for (j = 1; j < divisions; j++) {
3507 /* Weighted average */
3508 int x = (x1 * (divisions -j) + x2 * j) / divisions;
3509 int y = (y1 * (divisions -j) + y2 * j) / divisions;
3510 draw_circle(dr, x, y, ds->tilesize / thickness, ink, ink);
3511 }
3512 }
3513 }
3514
3515 sfree(ds->textx);
3516 sfree(ds->texty);
3517}
3518
3519#ifdef COMBINED
3520#define thegame loopy
3521#endif
3522
3523const struct game thegame = {
3524 "Loopy", "games.loopy", "loopy",
3525 default_params,
3526 game_fetch_preset,
3527 decode_params,
3528 encode_params,
3529 free_params,
3530 dup_params,
3531 TRUE, game_configure, custom_params,
3532 validate_params,
3533 new_game_desc,
3534 validate_desc,
3535 new_game,
3536 dup_game,
3537 free_game,
3538 1, solve_game,
3539 TRUE, game_can_format_as_text_now, game_text_format,
3540 new_ui,
3541 free_ui,
3542 encode_ui,
3543 decode_ui,
3544 game_changed_state,
3545 interpret_move,
3546 execute_move,
3547 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3548 game_colours,
3549 game_new_drawstate,
3550 game_free_drawstate,
3551 game_redraw,
3552 game_anim_length,
3553 game_flash_length,
3554 game_status,
3555 TRUE, FALSE, game_print_size, game_print,
3556 FALSE /* wants_statusbar */,
3557 FALSE, game_timing_state,
3558 0, /* mouse_priorities */
3559};
3560
3561#ifdef STANDALONE_SOLVER
3562
3563/*
3564 * Half-hearted standalone solver. It can't output the solution to
3565 * anything but a square puzzle, and it can't log the deductions
3566 * it makes either. But it can solve square puzzles, and more
3567 * importantly it can use its solver to grade the difficulty of
3568 * any puzzle you give it.
3569 */
3570
3571#include <stdarg.h>
3572
3573int main(int argc, char **argv)
3574{
3575 game_params *p;
3576 game_state *s;
3577 char *id = NULL, *desc, *err;
3578 int grade = FALSE;
3579 int ret, diff;
3580#if 0 /* verbose solver not supported here (yet) */
3581 int really_verbose = FALSE;
3582#endif
3583
3584 while (--argc > 0) {
3585 char *p = *++argv;
3586#if 0 /* verbose solver not supported here (yet) */
3587 if (!strcmp(p, "-v")) {
3588 really_verbose = TRUE;
3589 } else
3590#endif
3591 if (!strcmp(p, "-g")) {
3592 grade = TRUE;
3593 } else if (*p == '-') {
3594 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
3595 return 1;
3596 } else {
3597 id = p;
3598 }
3599 }
3600
3601 if (!id) {
3602 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
3603 return 1;
3604 }
3605
3606 desc = strchr(id, ':');
3607 if (!desc) {
3608 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
3609 return 1;
3610 }
3611 *desc++ = '\0';
3612
3613 p = default_params();
3614 decode_params(p, id);
3615 err = validate_desc(p, desc);
3616 if (err) {
3617 fprintf(stderr, "%s: %s\n", argv[0], err);
3618 return 1;
3619 }
3620 s = new_game(NULL, p, desc);
3621
3622 /*
3623 * When solving an Easy puzzle, we don't want to bother the
3624 * user with Hard-level deductions. For this reason, we grade
3625 * the puzzle internally before doing anything else.
3626 */
3627 ret = -1; /* placate optimiser */
3628 for (diff = 0; diff < DIFF_MAX; diff++) {
3629 solver_state *sstate_new;
3630 solver_state *sstate = new_solver_state((game_state *)s, diff);
3631
3632 sstate_new = solve_game_rec(sstate);
3633
3634 if (sstate_new->solver_status == SOLVER_MISTAKE)
3635 ret = 0;
3636 else if (sstate_new->solver_status == SOLVER_SOLVED)
3637 ret = 1;
3638 else
3639 ret = 2;
3640
3641 free_solver_state(sstate_new);
3642 free_solver_state(sstate);
3643
3644 if (ret < 2)
3645 break;
3646 }
3647
3648 if (diff == DIFF_MAX) {
3649 if (grade)
3650 printf("Difficulty rating: harder than Hard, or ambiguous\n");
3651 else
3652 printf("Unable to find a unique solution\n");
3653 } else {
3654 if (grade) {
3655 if (ret == 0)
3656 printf("Difficulty rating: impossible (no solution exists)\n");
3657 else if (ret == 1)
3658 printf("Difficulty rating: %s\n", diffnames[diff]);
3659 } else {
3660 solver_state *sstate_new;
3661 solver_state *sstate = new_solver_state((game_state *)s, diff);
3662
3663 /* If we supported a verbose solver, we'd set verbosity here */
3664
3665 sstate_new = solve_game_rec(sstate);
3666
3667 if (sstate_new->solver_status == SOLVER_MISTAKE)
3668 printf("Puzzle is inconsistent\n");
3669 else {
3670 assert(sstate_new->solver_status == SOLVER_SOLVED);
3671 if (s->grid_type == 0) {
3672 fputs(game_text_format(sstate_new->state), stdout);
3673 } else {
3674 printf("Unable to output non-square grids\n");
3675 }
3676 }
3677
3678 free_solver_state(sstate_new);
3679 free_solver_state(sstate);
3680 }
3681 }
3682
3683 return 0;
3684}
3685
3686#endif
3687
3688/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/magnets.R b/apps/plugins/puzzles/magnets.R
new file mode 100644
index 0000000000..e55e4746ee
--- /dev/null
+++ b/apps/plugins/puzzles/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/magnets.c b/apps/plugins/puzzles/magnets.c
new file mode 100644
index 0000000000..e51ae103c5
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/makedist.sh b/apps/plugins/puzzles/makedist.sh
new file mode 100755
index 0000000000..22b4f5d0ae
--- /dev/null
+++ b/apps/plugins/puzzles/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/malloc.c b/apps/plugins/puzzles/malloc.c
new file mode 100644
index 0000000000..d9943c6b40
--- /dev/null
+++ b/apps/plugins/puzzles/malloc.c
@@ -0,0 +1,61 @@
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 */
13
14int allocs = 0;
15int frees = 0;
16
17void *smalloc(size_t size) {
18 void *p;
19 p = malloc(size);
20 LOGF("allocs: %d", ++allocs);
21 if (!p)
22 fatal("out of memory");
23 return p;
24}
25
26/*
27 * sfree should guaranteeably deal gracefully with freeing NULL
28 */
29void sfree(void *p) {
30 if (p) {
31 ++frees;
32 LOGF("frees: %d, total outstanding: %d", frees, allocs - frees);
33 free(p);
34 }
35}
36
37/*
38 * srealloc should guaranteeably be able to realloc NULL
39 */
40void *srealloc(void *p, size_t size) {
41 void *q;
42 if (p) {
43 q = realloc(p, size);
44 } else {
45 LOGF("allocs: %d", ++allocs);
46 q = malloc(size);
47 }
48 if (!q)
49 fatal("out of memory");
50 return q;
51}
52
53/*
54 * dupstr is like strdup, but with the never-return-NULL property
55 * of smalloc (and also reliably defined in all environments :-)
56 */
57char *dupstr(const char *s) {
58 char *r = smalloc(1+strlen(s));
59 strcpy(r,s);
60 return r;
61}
diff --git a/apps/plugins/puzzles/map.R b/apps/plugins/puzzles/map.R
new file mode 100644
index 0000000000..1e702aa19d
--- /dev/null
+++ b/apps/plugins/puzzles/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/map.c b/apps/plugins/puzzles/map.c
new file mode 100644
index 0000000000..6e9c12523a
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/maxflow.c b/apps/plugins/puzzles/maxflow.c
new file mode 100644
index 0000000000..3a25654ff0
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/maxflow.h b/apps/plugins/puzzles/maxflow.h
new file mode 100644
index 0000000000..d490f45421
--- /dev/null
+++ b/apps/plugins/puzzles/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/midend.c b/apps/plugins/puzzles/midend.c
new file mode 100644
index 0000000000..e2938a45bc
--- /dev/null
+++ b/apps/plugins/puzzles/midend.c
@@ -0,0 +1,2136 @@
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#include "puzzles.h"
8
9#include "rbcompat.h"
10#include "rbassert.h"
11
12enum { DEF_PARAMS, DEF_SEED, DEF_DESC }; /* for midend_game_id_int */
13
14enum { NEWGAME, MOVE, SOLVE, RESTART };/* for midend_state_entry.movetype */
15
16#define special(type) ( (type) != MOVE )
17
18struct midend_state_entry {
19 game_state *state;
20 char *movestr;
21 int movetype;
22};
23
24struct midend {
25 frontend *frontend;
26 random_state *random;
27 const game *ourgame;
28
29 game_params **presets;
30 char **preset_names, **preset_encodings;
31 int npresets, presetsize;
32
33 /*
34 * `desc' and `privdesc' deserve a comment.
35 *
36 * `desc' is the game description as presented to the user when
37 * they ask for Game -> Specific. `privdesc', if non-NULL, is a
38 * different game description used to reconstruct the initial
39 * game_state when de-serialising. If privdesc is NULL, `desc'
40 * is used for both.
41 *
42 * For almost all games, `privdesc' is NULL and never used. The
43 * exception (as usual) is Mines: the initial game state has no
44 * squares open at all, but after the first click `desc' is
45 * rewritten to describe a game state with an initial click and
46 * thus a bunch of squares open. If we used that desc to
47 * serialise and deserialise, then the initial game state after
48 * deserialisation would look unlike the initial game state
49 * beforehand, and worse still execute_move() might fail on the
50 * attempted first click. So `privdesc' is also used in this
51 * case, to provide a game description describing the same
52 * fixed mine layout _but_ no initial click. (These game IDs
53 * may also be typed directly into Mines if you like.)
54 */
55 char *desc, *privdesc, *seedstr;
56 char *aux_info;
57 enum { GOT_SEED, GOT_DESC, GOT_NOTHING } genmode;
58
59 int nstates, statesize, statepos;
60 struct midend_state_entry *states;
61
62 game_params *params, *curparams;
63 game_drawstate *drawstate;
64 game_ui *ui;
65
66 game_state *oldstate;
67 float anim_time, anim_pos;
68 float flash_time, flash_pos;
69 int dir;
70
71 int timing;
72 float elapsed;
73 char *laststatus;
74
75 drawing *drawing;
76
77 int pressed_mouse_button;
78
79 int preferred_tilesize, tilesize, winwidth, winheight;
80
81 void (*game_id_change_notify_function)(void *);
82 void *game_id_change_notify_ctx;
83};
84
85#define ensure(me) do { \
86 if ((me)->nstates >= (me)->statesize) { \
87 (me)->statesize = (me)->nstates + 128; \
88 (me)->states = sresize((me)->states, (me)->statesize, \
89 struct midend_state_entry); \
90 } \
91} while (0)
92
93void midend_reset_tilesize(midend *me)
94{
95 me->preferred_tilesize = me->ourgame->preferred_tilesize;
96 {
97 /*
98 * Allow an environment-based override for the default tile
99 * size by defining a variable along the lines of
100 * `NET_TILESIZE=15'.
101 */
102
103 char buf[80], *e;
104 int j, k, ts;
105
106 sprintf(buf, "%s_TILESIZE", me->ourgame->name);
107 for (j = k = 0; buf[j]; j++)
108 if (!isspace((unsigned char)buf[j]))
109 buf[k++] = toupper((unsigned char)buf[j]);
110 buf[k] = '\0';
111 if ((e = getenv(buf)) != NULL && sscanf(e, "%d", &ts) == 1 && ts > 0)
112 me->preferred_tilesize = ts;
113 }
114}
115
116midend *midend_new(frontend *fe, const game *ourgame,
117 const drawing_api *drapi, void *drhandle)
118{
119 midend *me = snew(midend);
120 void *randseed;
121 int randseedsize;
122
123 get_random_seed(&randseed, &randseedsize);
124
125 me->frontend = fe;
126 me->ourgame = ourgame;
127 me->random = random_new(randseed, randseedsize);
128 me->nstates = me->statesize = me->statepos = 0;
129 me->states = NULL;
130 me->params = ourgame->default_params();
131 me->game_id_change_notify_function = NULL;
132 me->game_id_change_notify_ctx = NULL;
133
134 /*
135 * Allow environment-based changing of the default settings by
136 * defining a variable along the lines of `NET_DEFAULT=25x25w'
137 * in which the value is an encoded parameter string.
138 */
139 {
140 char buf[80], *e;
141 int j, k;
142 sprintf(buf, "%s_DEFAULT", me->ourgame->name);
143 for (j = k = 0; buf[j]; j++)
144 if (!isspace((unsigned char)buf[j]))
145 buf[k++] = toupper((unsigned char)buf[j]);
146 buf[k] = '\0';
147 if ((e = getenv(buf)) != NULL)
148 me->ourgame->decode_params(me->params, e);
149 }
150 me->curparams = NULL;
151 me->desc = me->privdesc = NULL;
152 me->seedstr = NULL;
153 me->aux_info = NULL;
154 me->genmode = GOT_NOTHING;
155 me->drawstate = NULL;
156 me->oldstate = NULL;
157 me->presets = NULL;
158 me->preset_names = NULL;
159 me->preset_encodings = NULL;
160 me->npresets = me->presetsize = 0;
161 me->anim_time = me->anim_pos = 0.0F;
162 me->flash_time = me->flash_pos = 0.0F;
163 me->dir = 0;
164 me->ui = NULL;
165 me->pressed_mouse_button = 0;
166 me->laststatus = NULL;
167 me->timing = FALSE;
168 me->elapsed = 0.0F;
169 me->tilesize = me->winwidth = me->winheight = 0;
170 if (drapi)
171 me->drawing = drawing_new(drapi, me, drhandle);
172 else
173 me->drawing = NULL;
174
175 midend_reset_tilesize(me);
176
177 sfree(randseed);
178
179 return me;
180}
181
182const game *midend_which_game(midend *me)
183{
184 return me->ourgame;
185}
186
187static void midend_purge_states(midend *me)
188{
189 while (me->nstates > me->statepos) {
190 me->ourgame->free_game(me->states[--me->nstates].state);
191 if (me->states[me->nstates].movestr)
192 sfree(me->states[me->nstates].movestr);
193 }
194}
195
196static void midend_free_game(midend *me)
197{
198 while (me->nstates > 0) {
199 me->nstates--;
200 me->ourgame->free_game(me->states[me->nstates].state);
201 sfree(me->states[me->nstates].movestr);
202 }
203
204 if (me->drawstate)
205 me->ourgame->free_drawstate(me->drawing, me->drawstate);
206}
207
208void midend_free(midend *me)
209{
210 int i;
211
212 midend_free_game(me);
213
214 if (me->drawing)
215 drawing_free(me->drawing);
216 random_free(me->random);
217 sfree(me->states);
218 sfree(me->desc);
219 sfree(me->privdesc);
220 sfree(me->seedstr);
221 sfree(me->aux_info);
222 me->ourgame->free_params(me->params);
223 if (me->npresets) {
224 for (i = 0; i < me->npresets; i++) {
225 sfree(me->presets[i]);
226 sfree(me->preset_names[i]);
227 sfree(me->preset_encodings[i]);
228 }
229 sfree(me->presets);
230 sfree(me->preset_names);
231 sfree(me->preset_encodings);
232 }
233 if (me->ui)
234 me->ourgame->free_ui(me->ui);
235 if (me->curparams)
236 me->ourgame->free_params(me->curparams);
237 sfree(me->laststatus);
238 sfree(me);
239}
240
241static void midend_size_new_drawstate(midend *me)
242{
243 /*
244 * Don't even bother, if we haven't worked out our tile size
245 * anyway yet.
246 */
247 if (me->tilesize > 0) {
248 me->ourgame->compute_size(me->params, me->tilesize,
249 &me->winwidth, &me->winheight);
250 me->ourgame->set_size(me->drawing, me->drawstate,
251 me->params, me->tilesize);
252 }
253}
254
255void midend_size(midend *me, int *x, int *y, int user_size)
256{
257 int min, max;
258 int rx, ry;
259
260 /*
261 * We can't set the size on the same drawstate twice. So if
262 * we've already sized one drawstate, we must throw it away and
263 * create a new one.
264 */
265 if (me->drawstate && me->tilesize > 0) {
266 me->ourgame->free_drawstate(me->drawing, me->drawstate);
267 me->drawstate = me->ourgame->new_drawstate(me->drawing,
268 me->states[0].state);
269 }
270
271 /*
272 * Find the tile size that best fits within the given space. If
273 * `user_size' is TRUE, we must actually find the _largest_ such
274 * tile size, in order to get as close to the user's explicit
275 * request as possible; otherwise, we bound above at the game's
276 * preferred tile size, so that the game gets what it wants
277 * provided that this doesn't break the constraint from the
278 * front-end (which is likely to be a screen size or similar).
279 */
280 if (user_size) {
281 max = 1;
282 do {
283 max *= 2;
284 me->ourgame->compute_size(me->params, max, &rx, &ry);
285 } while (rx <= *x && ry <= *y);
286 } else
287 max = me->preferred_tilesize + 1;
288 min = 1;
289
290 /*
291 * Now binary-search between min and max. We're looking for a
292 * boundary rather than a value: the point at which tile sizes
293 * stop fitting within the given dimensions. Thus, we stop when
294 * max and min differ by exactly 1.
295 */
296 while (max - min > 1) {
297 int mid = (max + min) / 2;
298 me->ourgame->compute_size(me->params, mid, &rx, &ry);
299 if (rx <= *x && ry <= *y)
300 min = mid;
301 else
302 max = mid;
303 }
304
305 /*
306 * Now `min' is a valid size, and `max' isn't. So use `min'.
307 */
308
309 me->tilesize = min;
310 if (user_size)
311 /* If the user requested a change in size, make it permanent. */
312 me->preferred_tilesize = me->tilesize;
313 midend_size_new_drawstate(me);
314 *x = me->winwidth;
315 *y = me->winheight;
316}
317
318int midend_tilesize(midend *me) { return me->tilesize; }
319
320void midend_set_params(midend *me, game_params *params)
321{
322 me->ourgame->free_params(me->params);
323 me->params = me->ourgame->dup_params(params);
324}
325
326game_params *midend_get_params(midend *me)
327{
328 return me->ourgame->dup_params(me->params);
329}
330
331static void midend_set_timer(midend *me)
332{
333 me->timing = (me->ourgame->is_timed &&
334 me->ourgame->timing_state(me->states[me->statepos-1].state,
335 me->ui));
336 if (me->timing || me->flash_time || me->anim_time)
337 activate_timer(me->frontend);
338 else
339 deactivate_timer(me->frontend);
340}
341
342void midend_force_redraw(midend *me)
343{
344 if (me->drawstate)
345 me->ourgame->free_drawstate(me->drawing, me->drawstate);
346 me->drawstate = me->ourgame->new_drawstate(me->drawing,
347 me->states[0].state);
348 midend_size_new_drawstate(me);
349 midend_redraw(me);
350}
351
352void midend_new_game(midend *me)
353{
354 midend_stop_anim(me);
355 midend_free_game(me);
356
357 assert(me->nstates == 0);
358
359 if (me->genmode == GOT_DESC) {
360 me->genmode = GOT_NOTHING;
361 } else {
362 random_state *rs;
363
364 if (me->genmode == GOT_SEED) {
365 me->genmode = GOT_NOTHING;
366 } else {
367 /*
368 * Generate a new random seed. 15 digits comes to about
369 * 48 bits, which should be more than enough.
370 *
371 * I'll avoid putting a leading zero on the number,
372 * just in case it confuses anybody who thinks it's
373 * processed as an integer rather than a string.
374 */
375 char newseed[16];
376 int i;
377 newseed[15] = '\0';
378 newseed[0] = '1' + (char)random_upto(me->random, 9);
379 for (i = 1; i < 15; i++)
380 newseed[i] = '0' + (char)random_upto(me->random, 10);
381 sfree(me->seedstr);
382 me->seedstr = dupstr(newseed);
383
384 if (me->curparams)
385 me->ourgame->free_params(me->curparams);
386 me->curparams = me->ourgame->dup_params(me->params);
387 }
388
389 sfree(me->desc);
390 sfree(me->privdesc);
391 sfree(me->aux_info);
392 me->aux_info = NULL;
393
394 rs = random_new(me->seedstr, strlen(me->seedstr));
395 /*
396 * If this midend has been instantiated without providing a
397 * drawing API, it is non-interactive. This means that it's
398 * being used for bulk game generation, and hence we should
399 * pass the non-interactive flag to new_desc.
400 */
401 me->desc = me->ourgame->new_desc(me->curparams, rs,
402 &me->aux_info, (me->drawing != NULL));
403 me->privdesc = NULL;
404 random_free(rs);
405 }
406
407 ensure(me);
408
409 /*
410 * It might seem a bit odd that we're using me->params to
411 * create the initial game state, rather than me->curparams
412 * which is better tailored to this specific game and which we
413 * always know.
414 *
415 * It's supposed to be an invariant in the midend that
416 * me->params and me->curparams differ in no aspect that is
417 * important after generation (i.e. after new_desc()). By
418 * deliberately passing the _less_ specific of these two
419 * parameter sets, we provoke play-time misbehaviour in the
420 * case where a game has failed to encode a play-time parameter
421 * in the non-full version of encode_params().
422 */
423 me->states[me->nstates].state =
424 me->ourgame->new_game(me, me->params, me->desc);
425
426 /*
427 * As part of our commitment to self-testing, test the aux
428 * string to make sure nothing ghastly went wrong.
429 */
430 if (me->ourgame->can_solve && me->aux_info) {
431 game_state *s;
432 char *msg, *movestr;
433
434 msg = NULL;
435 movestr = me->ourgame->solve(me->states[0].state,
436 me->states[0].state,
437 me->aux_info, &msg);
438 assert(movestr && !msg);
439 s = me->ourgame->execute_move(me->states[0].state, movestr);
440 assert(s);
441 me->ourgame->free_game(s);
442 sfree(movestr);
443 }
444
445 me->states[me->nstates].movestr = NULL;
446 me->states[me->nstates].movetype = NEWGAME;
447 me->nstates++;
448 me->statepos = 1;
449 me->drawstate = me->ourgame->new_drawstate(me->drawing,
450 me->states[0].state);
451 midend_size_new_drawstate(me);
452 me->elapsed = 0.0F;
453 me->flash_pos = me->flash_time = 0.0F;
454 me->anim_pos = me->anim_time = 0.0F;
455 if (me->ui)
456 me->ourgame->free_ui(me->ui);
457 me->ui = me->ourgame->new_ui(me->states[0].state);
458 midend_set_timer(me);
459 me->pressed_mouse_button = 0;
460
461 if (me->game_id_change_notify_function)
462 me->game_id_change_notify_function(me->game_id_change_notify_ctx);
463}
464
465int midend_can_undo(midend *me)
466{
467 return (me->statepos > 1);
468}
469
470int midend_can_redo(midend *me)
471{
472 return (me->statepos < me->nstates);
473}
474
475static int midend_undo(midend *me)
476{
477 if (me->statepos > 1) {
478 if (me->ui)
479 me->ourgame->changed_state(me->ui,
480 me->states[me->statepos-1].state,
481 me->states[me->statepos-2].state);
482 me->statepos--;
483 me->dir = -1;
484 return 1;
485 } else
486 return 0;
487}
488
489static int midend_redo(midend *me)
490{
491 if (me->statepos < me->nstates) {
492 if (me->ui)
493 me->ourgame->changed_state(me->ui,
494 me->states[me->statepos-1].state,
495 me->states[me->statepos].state);
496 me->statepos++;
497 me->dir = +1;
498 return 1;
499 } else
500 return 0;
501}
502
503static void midend_finish_move(midend *me)
504{
505 float flashtime;
506
507 /*
508 * We do not flash if the later of the two states is special.
509 * This covers both forward Solve moves and backward (undone)
510 * Restart moves.
511 */
512 if ((me->oldstate || me->statepos > 1) &&
513 ((me->dir > 0 && !special(me->states[me->statepos-1].movetype)) ||
514 (me->dir < 0 && me->statepos < me->nstates &&
515 !special(me->states[me->statepos].movetype)))) {
516 flashtime = me->ourgame->flash_length(me->oldstate ? me->oldstate :
517 me->states[me->statepos-2].state,
518 me->states[me->statepos-1].state,
519 me->oldstate ? me->dir : +1,
520 me->ui);
521 if (flashtime > 0) {
522 me->flash_pos = 0.0F;
523 me->flash_time = flashtime;
524 }
525 }
526
527 if (me->oldstate)
528 me->ourgame->free_game(me->oldstate);
529 me->oldstate = NULL;
530 me->anim_pos = me->anim_time = 0;
531 me->dir = 0;
532
533 midend_set_timer(me);
534}
535
536void midend_stop_anim(midend *me)
537{
538 if (me->oldstate || me->anim_time != 0) {
539 midend_finish_move(me);
540 midend_redraw(me);
541 }
542}
543
544void midend_restart_game(midend *me)
545{
546 game_state *s;
547
548 assert(me->statepos >= 1);
549 if (me->statepos == 1)
550 return; /* no point doing anything at all! */
551
552 /*
553 * During restart, we reconstruct the game from the (public)
554 * game description rather than from states[0], because that
555 * way Mines gets slightly more sensible behaviour (restart
556 * goes to _after_ the first click so you don't have to
557 * remember where you clicked).
558 */
559 s = me->ourgame->new_game(me, me->params, me->desc);
560
561 /*
562 * Now enter the restarted state as the next move.
563 */
564 midend_stop_anim(me);
565 midend_purge_states(me);
566 ensure(me);
567 me->states[me->nstates].state = s;
568 me->states[me->nstates].movestr = dupstr(me->desc);
569 me->states[me->nstates].movetype = RESTART;
570 me->statepos = ++me->nstates;
571 if (me->ui)
572 me->ourgame->changed_state(me->ui,
573 me->states[me->statepos-2].state,
574 me->states[me->statepos-1].state);
575 me->flash_pos = me->flash_time = 0.0F;
576 midend_finish_move(me);
577 midend_redraw(me);
578 midend_set_timer(me);
579}
580
581static int midend_really_process_key(midend *me, int x, int y, int button)
582{
583 game_state *oldstate =
584 me->ourgame->dup_game(me->states[me->statepos - 1].state);
585 int type = MOVE, gottype = FALSE, ret = 1;
586 float anim_time;
587 game_state *s;
588 char *movestr;
589
590 movestr =
591 me->ourgame->interpret_move(me->states[me->statepos-1].state,
592 me->ui, me->drawstate, x, y, button);
593
594 if (!movestr) {
595 if (button == 'n' || button == 'N' || button == '\x0E') {
596 midend_new_game(me);
597 midend_redraw(me);
598 goto done; /* never animate */
599 } else if (button == 'u' || button == 'U' ||
600 button == '\x1A' || button == '\x1F') {
601 midend_stop_anim(me);
602 type = me->states[me->statepos-1].movetype;
603 gottype = TRUE;
604 if (!midend_undo(me))
605 goto done;
606 } else if (button == 'r' || button == 'R' ||
607 button == '\x12' || button == '\x19') {
608 midend_stop_anim(me);
609 if (!midend_redo(me))
610 goto done;
611 } else if (button == '\x13' && me->ourgame->can_solve) {
612 if (midend_solve(me))
613 goto done;
614 } else if (button == 'q' || button == 'Q' || button == '\x11') {
615 ret = 0;
616 goto done;
617 } else
618 goto done;
619 } else {
620 if (!*movestr)
621 s = me->states[me->statepos-1].state;
622 else {
623 s = me->ourgame->execute_move(me->states[me->statepos-1].state,
624 movestr);
625 assert(s != NULL);
626 }
627
628 if (s == me->states[me->statepos-1].state) {
629 /*
630 * make_move() is allowed to return its input state to
631 * indicate that although no move has been made, the UI
632 * state has been updated and a redraw is called for.
633 */
634 midend_redraw(me);
635 midend_set_timer(me);
636 goto done;
637 } else if (s) {
638 midend_stop_anim(me);
639 midend_purge_states(me);
640 ensure(me);
641 assert(movestr != NULL);
642 me->states[me->nstates].state = s;
643 me->states[me->nstates].movestr = movestr;
644 me->states[me->nstates].movetype = MOVE;
645 me->statepos = ++me->nstates;
646 me->dir = +1;
647 if (me->ui)
648 me->ourgame->changed_state(me->ui,
649 me->states[me->statepos-2].state,
650 me->states[me->statepos-1].state);
651 } else {
652 goto done;
653 }
654 }
655
656 if (!gottype)
657 type = me->states[me->statepos-1].movetype;
658
659 /*
660 * See if this move requires an animation.
661 */
662 if (special(type) && !(type == SOLVE &&
663 (me->ourgame->flags & SOLVE_ANIMATES))) {
664 anim_time = 0;
665 } else {
666 anim_time = me->ourgame->anim_length(oldstate,
667 me->states[me->statepos-1].state,
668 me->dir, me->ui);
669 }
670
671 me->oldstate = oldstate; oldstate = NULL;
672 if (anim_time > 0) {
673 me->anim_time = anim_time;
674 } else {
675 me->anim_time = 0.0;
676 midend_finish_move(me);
677 }
678 me->anim_pos = 0.0;
679
680 midend_redraw(me);
681
682 midend_set_timer(me);
683
684 done:
685 if (oldstate) me->ourgame->free_game(oldstate);
686 return ret;
687}
688
689int midend_process_key(midend *me, int x, int y, int button)
690{
691 int ret = 1;
692
693 /*
694 * Harmonise mouse drag and release messages.
695 *
696 * Some front ends might accidentally switch from sending, say,
697 * RIGHT_DRAG messages to sending LEFT_DRAG, half way through a
698 * drag. (This can happen on the Mac, for example, since
699 * RIGHT_DRAG is usually done using Command+drag, and if the
700 * user accidentally releases Command half way through the drag
701 * then there will be trouble.)
702 *
703 * It would be an O(number of front ends) annoyance to fix this
704 * in the front ends, but an O(number of back ends) annoyance
705 * to have each game capable of dealing with it. Therefore, we
706 * fix it _here_ in the common midend code so that it only has
707 * to be done once.
708 *
709 * The possible ways in which things can go screwy in the front
710 * end are:
711 *
712 * - in a system containing multiple physical buttons button
713 * presses can inadvertently overlap. We can see ABab (caps
714 * meaning button-down and lowercase meaning button-up) when
715 * the user had semantically intended AaBb.
716 *
717 * - in a system where one button is simulated by means of a
718 * modifier key and another button, buttons can mutate
719 * between press and release (possibly during drag). So we
720 * can see Ab instead of Aa.
721 *
722 * Definite requirements are:
723 *
724 * - button _presses_ must never be invented or destroyed. If
725 * the user presses two buttons in succession, the button
726 * presses must be transferred to the backend unchanged. So
727 * if we see AaBb , that's fine; if we see ABab (the button
728 * presses inadvertently overlapped) we must somehow
729 * `correct' it to AaBb.
730 *
731 * - every mouse action must end up looking like a press, zero
732 * or more drags, then a release. This allows back ends to
733 * make the _assumption_ that incoming mouse data will be
734 * sane in this regard, and not worry about the details.
735 *
736 * So my policy will be:
737 *
738 * - treat any button-up as a button-up for the currently
739 * pressed button, or ignore it if there is no currently
740 * pressed button.
741 *
742 * - treat any drag as a drag for the currently pressed
743 * button, or ignore it if there is no currently pressed
744 * button.
745 *
746 * - if we see a button-down while another button is currently
747 * pressed, invent a button-up for the first one and then
748 * pass the button-down through as before.
749 *
750 * 2005-05-31: An addendum to the above. Some games might want
751 * a `priority order' among buttons, such that if one button is
752 * pressed while another is down then a fixed one of the
753 * buttons takes priority no matter what order they're pressed
754 * in. Mines, in particular, wants to treat a left+right click
755 * like a left click for the benefit of users of other
756 * implementations. So the last of the above points is modified
757 * in the presence of an (optional) button priority order.
758 *
759 * A further addition: we translate certain keyboard presses to
760 * cursor key 'select' buttons, so that a) frontends don't have
761 * to translate these themselves (like they do for CURSOR_UP etc),
762 * and b) individual games don't have to hard-code button presses
763 * of '\n' etc for keyboard-based cursors. The choice of buttons
764 * here could eventually be controlled by a runtime configuration
765 * option.
766 */
767 if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
768 if (me->pressed_mouse_button) {
769 if (IS_MOUSE_DRAG(button)) {
770 button = me->pressed_mouse_button +
771 (LEFT_DRAG - LEFT_BUTTON);
772 } else {
773 button = me->pressed_mouse_button +
774 (LEFT_RELEASE - LEFT_BUTTON);
775 }
776 } else
777 return ret; /* ignore it */
778 } else if (IS_MOUSE_DOWN(button) && me->pressed_mouse_button) {
779 /*
780 * If the new button has lower priority than the old one,
781 * don't bother doing this.
782 */
783 if (me->ourgame->flags &
784 BUTTON_BEATS(me->pressed_mouse_button, button))
785 return ret; /* just ignore it */
786
787 /*
788 * Fabricate a button-up for the previously pressed button.
789 */
790 ret = ret && midend_really_process_key
791 (me, x, y, (me->pressed_mouse_button +
792 (LEFT_RELEASE - LEFT_BUTTON)));
793 }
794
795 /*
796 * Translate keyboard presses to cursor selection.
797 */
798 if (button == '\n' || button == '\r')
799 button = CURSOR_SELECT;
800 if (button == ' ')
801 button = CURSOR_SELECT2;
802
803 /*
804 * Normalise both backspace characters (8 and 127) to \b. Easier
805 * to do this once, here, than to require all front ends to
806 * carefully generate the same one - now each front end can
807 * generate whichever is easiest.
808 */
809 if (button == '\177')
810 button = '\b';
811
812 /*
813 * Now send on the event we originally received.
814 */
815 ret = ret && midend_really_process_key(me, x, y, button);
816
817 /*
818 * And update the currently pressed button.
819 */
820 if (IS_MOUSE_RELEASE(button))
821 me->pressed_mouse_button = 0;
822 else if (IS_MOUSE_DOWN(button))
823 me->pressed_mouse_button = button;
824
825 return ret;
826}
827
828void midend_redraw(midend *me)
829{
830 assert(me->drawing);
831
832 if (me->statepos > 0 && me->drawstate) {
833 start_draw(me->drawing);
834 if (me->oldstate && me->anim_time > 0 &&
835 me->anim_pos < me->anim_time) {
836 assert(me->dir != 0);
837 me->ourgame->redraw(me->drawing, me->drawstate, me->oldstate,
838 me->states[me->statepos-1].state, me->dir,
839 me->ui, me->anim_pos, me->flash_pos);
840 } else {
841 me->ourgame->redraw(me->drawing, me->drawstate, NULL,
842 me->states[me->statepos-1].state, +1 /*shrug*/,
843 me->ui, 0.0, me->flash_pos);
844 }
845 end_draw(me->drawing);
846 }
847}
848
849/*
850 * Nasty hacky function used to implement the --redo option in
851 * gtk.c. Only used for generating the puzzles' icons.
852 */
853void midend_freeze_timer(midend *me, float tprop)
854{
855 me->anim_pos = me->anim_time * tprop;
856 midend_redraw(me);
857 deactivate_timer(me->frontend);
858}
859
860void midend_timer(midend *me, float tplus)
861{
862 int need_redraw = (me->anim_time > 0 || me->flash_time > 0);
863
864 me->anim_pos += tplus;
865 if (me->anim_pos >= me->anim_time ||
866 me->anim_time == 0 || !me->oldstate) {
867 if (me->anim_time > 0)
868 midend_finish_move(me);
869 }
870
871 me->flash_pos += tplus;
872 if (me->flash_pos >= me->flash_time || me->flash_time == 0) {
873 me->flash_pos = me->flash_time = 0;
874 }
875
876 if (need_redraw)
877 midend_redraw(me);
878
879 if (me->timing) {
880 float oldelapsed = me->elapsed;
881 me->elapsed += tplus;
882 if ((int)oldelapsed != (int)me->elapsed)
883 status_bar(me->drawing, me->laststatus ? me->laststatus : "");
884 }
885
886 midend_set_timer(me);
887}
888
889float *midend_colours(midend *me, int *ncolours)
890{
891 float *ret;
892
893 ret = me->ourgame->colours(me->frontend, ncolours);
894
895 {
896 int i;
897
898 /*
899 * Allow environment-based overrides for the standard
900 * colours by defining variables along the lines of
901 * `NET_COLOUR_4=6000c0'.
902 */
903
904 for (i = 0; i < *ncolours; i++) {
905 char buf[80], *e;
906 unsigned int r, g, b;
907 int j, k;
908
909 sprintf(buf, "%s_COLOUR_%d", me->ourgame->name, i);
910 for (j = k = 0; buf[j]; j++)
911 if (!isspace((unsigned char)buf[j]))
912 buf[k++] = toupper((unsigned char)buf[j]);
913 buf[k] = '\0';
914 if ((e = getenv(buf)) != NULL &&
915 sscanf(e, "%2x%2x%2x", &r, &g, &b) == 3) {
916 ret[i*3 + 0] = r / 255.0F;
917 ret[i*3 + 1] = g / 255.0F;
918 ret[i*3 + 2] = b / 255.0F;
919 }
920 }
921 }
922
923 return ret;
924}
925
926int midend_num_presets(midend *me)
927{
928 if (!me->npresets) {
929 char *name;
930 game_params *preset;
931
932 while (me->ourgame->fetch_preset(me->npresets, &name, &preset)) {
933 if (me->presetsize <= me->npresets) {
934 me->presetsize = me->npresets + 10;
935 me->presets = sresize(me->presets, me->presetsize,
936 game_params *);
937 me->preset_names = sresize(me->preset_names, me->presetsize,
938 char *);
939 me->preset_encodings = sresize(me->preset_encodings,
940 me->presetsize, char *);
941 }
942
943 me->presets[me->npresets] = preset;
944 me->preset_names[me->npresets] = name;
945 me->preset_encodings[me->npresets] =
946 me->ourgame->encode_params(preset, TRUE);;
947 me->npresets++;
948 }
949 }
950
951 {
952 /*
953 * Allow environment-based extensions to the preset list by
954 * defining a variable along the lines of `SOLO_PRESETS=2x3
955 * Advanced:2x3da'. Colon-separated list of items,
956 * alternating between textual titles in the menu and
957 * encoded parameter strings.
958 */
959 char buf[80], *e, *p;
960 int j, k;
961
962 sprintf(buf, "%s_PRESETS", me->ourgame->name);
963 for (j = k = 0; buf[j]; j++)
964 if (!isspace((unsigned char)buf[j]))
965 buf[k++] = toupper((unsigned char)buf[j]);
966 buf[k] = '\0';
967
968 if ((e = getenv(buf)) != NULL) {
969 p = e = dupstr(e);
970
971 while (*p) {
972 char *name, *val;
973 game_params *preset;
974
975 name = p;
976 while (*p && *p != ':') p++;
977 if (*p) *p++ = '\0';
978 val = p;
979 while (*p && *p != ':') p++;
980 if (*p) *p++ = '\0';
981
982 preset = me->ourgame->default_params();
983 me->ourgame->decode_params(preset, val);
984
985 if (me->ourgame->validate_params(preset, TRUE)) {
986 /* Drop this one from the list. */
987 me->ourgame->free_params(preset);
988 continue;
989 }
990
991 if (me->presetsize <= me->npresets) {
992 me->presetsize = me->npresets + 10;
993 me->presets = sresize(me->presets, me->presetsize,
994 game_params *);
995 me->preset_names = sresize(me->preset_names,
996 me->presetsize, char *);
997 me->preset_encodings = sresize(me->preset_encodings,
998 me->presetsize, char *);
999 }
1000
1001 me->presets[me->npresets] = preset;
1002 me->preset_names[me->npresets] = dupstr(name);
1003 me->preset_encodings[me->npresets] =
1004 me->ourgame->encode_params(preset, TRUE);
1005 me->npresets++;
1006 }
1007 sfree(e);
1008 }
1009 }
1010
1011 return me->npresets;
1012}
1013
1014void midend_fetch_preset(midend *me, int n,
1015 char **name, game_params **params)
1016{
1017 assert(n >= 0 && n < me->npresets);
1018 *name = me->preset_names[n];
1019 *params = me->presets[n];
1020}
1021
1022int midend_which_preset(midend *me)
1023{
1024 char *encoding = me->ourgame->encode_params(me->params, TRUE);
1025 int i, ret;
1026
1027 ret = -1;
1028 for (i = 0; i < me->npresets; i++)
1029 if (!strcmp(encoding, me->preset_encodings[i])) {
1030 ret = i;
1031 break;
1032 }
1033
1034 sfree(encoding);
1035 return ret;
1036}
1037
1038int midend_wants_statusbar(midend *me)
1039{
1040 return me->ourgame->wants_statusbar;
1041}
1042
1043void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx)
1044{
1045 me->game_id_change_notify_function = notify;
1046 me->game_id_change_notify_ctx = ctx;
1047}
1048
1049void midend_supersede_game_desc(midend *me, char *desc, char *privdesc)
1050{
1051 sfree(me->desc);
1052 sfree(me->privdesc);
1053 me->desc = dupstr(desc);
1054 me->privdesc = privdesc ? dupstr(privdesc) : NULL;
1055 if (me->game_id_change_notify_function)
1056 me->game_id_change_notify_function(me->game_id_change_notify_ctx);
1057}
1058
1059config_item *midend_get_config(midend *me, int which, char **wintitle)
1060{
1061 char *titlebuf, *parstr, *rest;
1062 config_item *ret;
1063 char sep;
1064
1065 assert(wintitle);
1066 titlebuf = snewn(40 + strlen(me->ourgame->name), char);
1067
1068 switch (which) {
1069 case CFG_SETTINGS:
1070 sprintf(titlebuf, "%s configuration", me->ourgame->name);
1071 *wintitle = titlebuf;
1072 return me->ourgame->configure(me->params);
1073 case CFG_SEED:
1074 case CFG_DESC:
1075 if (!me->curparams) {
1076 sfree(titlebuf);
1077 return NULL;
1078 }
1079 sprintf(titlebuf, "%s %s selection", me->ourgame->name,
1080 which == CFG_SEED ? "random" : "game");
1081 *wintitle = titlebuf;
1082
1083 ret = snewn(2, config_item);
1084
1085 ret[0].type = C_STRING;
1086 if (which == CFG_SEED)
1087 ret[0].name = "Game random seed";
1088 else
1089 ret[0].name = "Game ID";
1090 ret[0].ival = 0;
1091 /*
1092 * For CFG_DESC the text going in here will be a string
1093 * encoding of the restricted parameters, plus a colon,
1094 * plus the game description. For CFG_SEED it will be the
1095 * full parameters, plus a hash, plus the random seed data.
1096 * Either of these is a valid full game ID (although only
1097 * the former is likely to persist across many code
1098 * changes).
1099 */
1100 parstr = me->ourgame->encode_params(me->curparams, which == CFG_SEED);
1101 assert(parstr);
1102 if (which == CFG_DESC) {
1103 rest = me->desc ? me->desc : "";
1104 sep = ':';
1105 } else {
1106 rest = me->seedstr ? me->seedstr : "";
1107 sep = '#';
1108 }
1109 ret[0].sval = snewn(strlen(parstr) + strlen(rest) + 2, char);
1110 sprintf(ret[0].sval, "%s%c%s", parstr, sep, rest);
1111 sfree(parstr);
1112
1113 ret[1].type = C_END;
1114 ret[1].name = ret[1].sval = NULL;
1115 ret[1].ival = 0;
1116
1117 return ret;
1118 }
1119
1120 assert(!"We shouldn't be here");
1121 return NULL;
1122}
1123
1124static char *midend_game_id_int(midend *me, char *id, int defmode)
1125{
1126 char *error, *par, *desc, *seed;
1127 game_params *newcurparams, *newparams, *oldparams1, *oldparams2;
1128 int free_params;
1129
1130 seed = strchr(id, '#');
1131 desc = strchr(id, ':');
1132
1133 if (desc && (!seed || desc < seed)) {
1134 /*
1135 * We have a colon separating parameters from game
1136 * description. So `par' now points to the parameters
1137 * string, and `desc' to the description string.
1138 */
1139 *desc++ = '\0';
1140 par = id;
1141 seed = NULL;
1142 } else if (seed && (!desc || seed < desc)) {
1143 /*
1144 * We have a hash separating parameters from random seed.
1145 * So `par' now points to the parameters string, and `seed'
1146 * to the seed string.
1147 */
1148 *seed++ = '\0';
1149 par = id;
1150 desc = NULL;
1151 } else {
1152 /*
1153 * We only have one string. Depending on `defmode', we take
1154 * it to be either parameters, seed or description.
1155 */
1156 if (defmode == DEF_SEED) {
1157 seed = id;
1158 par = desc = NULL;
1159 } else if (defmode == DEF_DESC) {
1160 desc = id;
1161 par = seed = NULL;
1162 } else {
1163 par = id;
1164 seed = desc = NULL;
1165 }
1166 }
1167
1168 /*
1169 * We must be reasonably careful here not to modify anything in
1170 * `me' until we have finished validating things. This function
1171 * must either return an error and do nothing to the midend, or
1172 * return success and do everything; nothing in between is
1173 * acceptable.
1174 */
1175 newcurparams = newparams = oldparams1 = oldparams2 = NULL;
1176
1177 if (par) {
1178 /*
1179 * The params string may underspecify the game parameters, so
1180 * we must first initialise newcurparams with a full set of
1181 * params from somewhere else before we decode_params the
1182 * input string over the top.
1183 *
1184 * But which set? It depends on what other data we have.
1185 *
1186 * If we've been given a _descriptive_ game id, then that may
1187 * well underspecify by design, e.g. Solo game descriptions
1188 * often start just '3x3:' without specifying one of Solo's
1189 * difficulty settings, because it isn't necessary once a game
1190 * has been generated (and you might not even know it, if
1191 * you're manually transcribing a game description). In that
1192 * situation, I've always felt that the best thing to set the
1193 * difficulty to (for use if the user hits 'New Game' after
1194 * pasting in that game id) is whatever it was previously set
1195 * to. That is, we use whatever is already in me->params as
1196 * the basis for our decoding of this input string.
1197 *
1198 * A random-seed based game id, however, should use the real,
1199 * built-in default params, and not even check the
1200 * <game>_DEFAULT environment setting, because when people
1201 * paste each other random seeds - whether it's two users
1202 * arranging to generate the same game at the same time to
1203 * race solving them, or a user sending a bug report upstream
1204 * - the whole point is for the random game id to always be
1205 * interpreted the same way, even if it does underspecify.
1206 *
1207 * A parameter string typed in on its own, with no seed _or_
1208 * description, gets treated the same way as a random seed,
1209 * because again I think the most likely reason for doing that
1210 * is to have a portable representation of a set of params.
1211 */
1212 if (desc) {
1213 newcurparams = me->ourgame->dup_params(me->params);
1214 } else {
1215 newcurparams = me->ourgame->default_params();
1216 }
1217 me->ourgame->decode_params(newcurparams, par);
1218 error = me->ourgame->validate_params(newcurparams, desc == NULL);
1219 if (error) {
1220 me->ourgame->free_params(newcurparams);
1221 return error;
1222 }
1223 oldparams1 = me->curparams;
1224
1225 /*
1226 * Now filter only the persistent parts of this state into
1227 * the long-term params structure, unless we've _only_
1228 * received a params string in which case the whole lot is
1229 * persistent.
1230 */
1231 oldparams2 = me->params;
1232 if (seed || desc) {
1233 char *tmpstr;
1234
1235 newparams = me->ourgame->dup_params(me->params);
1236
1237 tmpstr = me->ourgame->encode_params(newcurparams, FALSE);
1238 me->ourgame->decode_params(newparams, tmpstr);
1239
1240 sfree(tmpstr);
1241 } else {
1242 newparams = me->ourgame->dup_params(newcurparams);
1243 }
1244 free_params = TRUE;
1245 } else {
1246 newcurparams = me->curparams;
1247 newparams = me->params;
1248 free_params = FALSE;
1249 }
1250
1251 if (desc) {
1252 error = me->ourgame->validate_desc(newparams, desc);
1253 if (error) {
1254 if (free_params) {
1255 if (newcurparams)
1256 me->ourgame->free_params(newcurparams);
1257 if (newparams)
1258 me->ourgame->free_params(newparams);
1259 }
1260 return error;
1261 }
1262 }
1263
1264 /*
1265 * Now we've got past all possible error points. Update the
1266 * midend itself.
1267 */
1268 me->params = newparams;
1269 me->curparams = newcurparams;
1270 if (oldparams1)
1271 me->ourgame->free_params(oldparams1);
1272 if (oldparams2)
1273 me->ourgame->free_params(oldparams2);
1274
1275 sfree(me->desc);
1276 sfree(me->privdesc);
1277 me->desc = me->privdesc = NULL;
1278 sfree(me->seedstr);
1279 me->seedstr = NULL;
1280
1281 if (desc) {
1282 me->desc = dupstr(desc);
1283 me->genmode = GOT_DESC;
1284 sfree(me->aux_info);
1285 me->aux_info = NULL;
1286 }
1287
1288 if (seed) {
1289 me->seedstr = dupstr(seed);
1290 me->genmode = GOT_SEED;
1291 }
1292
1293 return NULL;
1294}
1295
1296char *midend_game_id(midend *me, char *id)
1297{
1298 return midend_game_id_int(me, id, DEF_PARAMS);
1299}
1300
1301char *midend_get_game_id(midend *me)
1302{
1303 char *parstr, *ret;
1304
1305 parstr = me->ourgame->encode_params(me->curparams, FALSE);
1306 assert(parstr);
1307 assert(me->desc);
1308 ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char);
1309 sprintf(ret, "%s:%s", parstr, me->desc);
1310 sfree(parstr);
1311 return ret;
1312}
1313
1314char *midend_get_random_seed(midend *me)
1315{
1316 char *parstr, *ret;
1317
1318 if (!me->seedstr)
1319 return NULL;
1320
1321 parstr = me->ourgame->encode_params(me->curparams, TRUE);
1322 assert(parstr);
1323 ret = snewn(strlen(parstr) + strlen(me->seedstr) + 2, char);
1324 sprintf(ret, "%s#%s", parstr, me->seedstr);
1325 sfree(parstr);
1326 return ret;
1327}
1328
1329char *midend_set_config(midend *me, int which, config_item *cfg)
1330{
1331 char *error;
1332 game_params *params;
1333
1334 switch (which) {
1335 case CFG_SETTINGS:
1336 params = me->ourgame->custom_params(cfg);
1337 error = me->ourgame->validate_params(params, TRUE);
1338
1339 if (error) {
1340 me->ourgame->free_params(params);
1341 return error;
1342 }
1343
1344 me->ourgame->free_params(me->params);
1345 me->params = params;
1346 break;
1347
1348 case CFG_SEED:
1349 case CFG_DESC:
1350 error = midend_game_id_int(me, cfg[0].sval,
1351 (which == CFG_SEED ? DEF_SEED : DEF_DESC));
1352 if (error)
1353 return error;
1354 break;
1355 }
1356
1357 return NULL;
1358}
1359
1360int midend_can_format_as_text_now(midend *me)
1361{
1362 if (me->ourgame->can_format_as_text_ever)
1363 return me->ourgame->can_format_as_text_now(me->params);
1364 else
1365 return FALSE;
1366}
1367
1368char *midend_text_format(midend *me)
1369{
1370 if (me->ourgame->can_format_as_text_ever && me->statepos > 0 &&
1371 me->ourgame->can_format_as_text_now(me->params))
1372 return me->ourgame->text_format(me->states[me->statepos-1].state);
1373 else
1374 return NULL;
1375}
1376
1377char *midend_solve(midend *me)
1378{
1379 game_state *s;
1380 char *msg, *movestr;
1381
1382 if (!me->ourgame->can_solve)
1383 return "This game does not support the Solve operation";
1384
1385 if (me->statepos < 1)
1386 return "No game set up to solve"; /* _shouldn't_ happen! */
1387
1388 msg = NULL;
1389 movestr = me->ourgame->solve(me->states[0].state,
1390 me->states[me->statepos-1].state,
1391 me->aux_info, &msg);
1392 if (!movestr) {
1393 if (!msg)
1394 msg = "Solve operation failed"; /* _shouldn't_ happen, but can */
1395 return msg;
1396 }
1397 s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr);
1398 assert(s);
1399
1400 /*
1401 * Now enter the solved state as the next move.
1402 */
1403 midend_stop_anim(me);
1404 midend_purge_states(me);
1405 ensure(me);
1406 me->states[me->nstates].state = s;
1407 me->states[me->nstates].movestr = movestr;
1408 me->states[me->nstates].movetype = SOLVE;
1409 me->statepos = ++me->nstates;
1410 if (me->ui)
1411 me->ourgame->changed_state(me->ui,
1412 me->states[me->statepos-2].state,
1413 me->states[me->statepos-1].state);
1414 me->dir = +1;
1415 if (me->ourgame->flags & SOLVE_ANIMATES) {
1416 me->oldstate = me->ourgame->dup_game(me->states[me->statepos-2].state);
1417 me->anim_time =
1418 me->ourgame->anim_length(me->states[me->statepos-2].state,
1419 me->states[me->statepos-1].state,
1420 +1, me->ui);
1421 me->anim_pos = 0.0;
1422 } else {
1423 me->anim_time = 0.0;
1424 midend_finish_move(me);
1425 }
1426 if (me->drawing)
1427 midend_redraw(me);
1428 midend_set_timer(me);
1429 return NULL;
1430}
1431
1432int midend_status(midend *me)
1433{
1434 /*
1435 * We should probably never be called when the state stack has no
1436 * states on it at all - ideally, midends should never be left in
1437 * that state for long enough to get put down and forgotten about.
1438 * But if we are, I think we return _true_ - pedantically speaking
1439 * a midend in that state is 'vacuously solved', and more
1440 * practically, a user whose midend has been left in that state
1441 * probably _does_ want the 'new game' option to be prominent.
1442 */
1443 if (me->statepos == 0)
1444 return +1;
1445
1446 return me->ourgame->status(me->states[me->statepos-1].state);
1447}
1448
1449char *midend_rewrite_statusbar(midend *me, char *text)
1450{
1451 /*
1452 * An important special case is that we are occasionally called
1453 * with our own laststatus, to update the timer.
1454 */
1455 if (me->laststatus != text) {
1456 sfree(me->laststatus);
1457 me->laststatus = dupstr(text);
1458 }
1459
1460 if (me->ourgame->is_timed) {
1461 char timebuf[100], *ret;
1462 int min, sec;
1463
1464 sec = (int)me->elapsed;
1465 min = sec / 60;
1466 sec %= 60;
1467 sprintf(timebuf, "[%d:%02d] ", min, sec);
1468
1469 ret = snewn(strlen(timebuf) + strlen(text) + 1, char);
1470 strcpy(ret, timebuf);
1471 strcat(ret, text);
1472 return ret;
1473
1474 } else {
1475 return dupstr(text);
1476 }
1477}
1478
1479#define SERIALISE_MAGIC "Simon Tatham's Portable Puzzle Collection"
1480#define SERIALISE_VERSION "1"
1481
1482/* rockbox kludge */
1483static void copy_left_justified(char *buf, size_t sz, const char *str)
1484{
1485 memset(buf, ' ', sz - 1);
1486 int len = strlen(str);
1487 if(len <= sz - 1)
1488 memcpy(buf, str, len);
1489 else
1490 fatal("overrun");
1491 buf[sz - 1] = 0;
1492}
1493
1494void midend_serialise(midend *me,
1495 void (*write)(void *ctx, void *buf, int len),
1496 void *wctx)
1497{
1498 int i;
1499
1500 /*
1501 * Each line of the save file contains three components. First
1502 * exactly 8 characters of header word indicating what type of
1503 * data is contained on the line; then a colon followed by a
1504 * decimal integer giving the length of the main string on the
1505 * line; then a colon followed by the string itself (exactly as
1506 * many bytes as previously specified, no matter what they
1507 * contain). Then a newline (of reasonably flexible form).
1508 */
1509#define wr(h,s) do { \
1510 char hbuf[80]; \
1511 char *str = (s); \
1512 char lbuf[9]; \
1513 copy_left_justified(lbuf, sizeof(lbuf), h); \
1514 sprintf(hbuf, "%s:%d:", lbuf, (int)strlen(str)); \
1515 write(wctx, hbuf, strlen(hbuf)); \
1516 write(wctx, str, strlen(str)); \
1517 write(wctx, "\n", 1); \
1518} while (0)
1519
1520 /*
1521 * Magic string identifying the file, and version number of the
1522 * file format.
1523 */
1524 wr("SAVEFILE", SERIALISE_MAGIC);
1525 wr("VERSION", SERIALISE_VERSION);
1526
1527 /*
1528 * The game name. (Copied locally to avoid const annoyance.)
1529 */
1530 {
1531 char *s = dupstr(me->ourgame->name);
1532 wr("GAME", s);
1533 sfree(s);
1534 }
1535
1536 /*
1537 * The current long-term parameters structure, in full.
1538 */
1539 if (me->params) {
1540 char *s = me->ourgame->encode_params(me->params, TRUE);
1541 wr("PARAMS", s);
1542 sfree(s);
1543 }
1544
1545 /*
1546 * The current short-term parameters structure, in full.
1547 */
1548 if (me->curparams) {
1549 char *s = me->ourgame->encode_params(me->curparams, TRUE);
1550 wr("CPARAMS", s);
1551 sfree(s);
1552 }
1553
1554 /*
1555 * The current game description, the privdesc, and the random seed.
1556 */
1557 if (me->seedstr)
1558 wr("SEED", me->seedstr);
1559 if (me->desc)
1560 wr("DESC", me->desc);
1561 if (me->privdesc)
1562 wr("PRIVDESC", me->privdesc);
1563
1564 /*
1565 * The game's aux_info. We obfuscate this to prevent spoilers
1566 * (people are likely to run `head' or similar on a saved game
1567 * file simply to find out what it is, and don't necessarily
1568 * want to be told the answer to the puzzle!)
1569 */
1570 if (me->aux_info) {
1571 unsigned char *s1;
1572 char *s2;
1573 int len;
1574
1575 len = strlen(me->aux_info);
1576 s1 = snewn(len, unsigned char);
1577 memcpy(s1, me->aux_info, len);
1578 obfuscate_bitmap(s1, len*8, FALSE);
1579 s2 = bin2hex(s1, len);
1580
1581 wr("AUXINFO", s2);
1582
1583 sfree(s2);
1584 sfree(s1);
1585 }
1586
1587 /*
1588 * Any required serialisation of the game_ui.
1589 */
1590 if (me->ui) {
1591 char *s = me->ourgame->encode_ui(me->ui);
1592 if (s) {
1593 wr("UI", s);
1594 sfree(s);
1595 }
1596 }
1597
1598 /*
1599 * The game time, if it's a timed game.
1600 */
1601 if (me->ourgame->is_timed) {
1602 char buf[80];
1603 sprintf(buf, "%g", me->elapsed);
1604 wr("TIME", buf);
1605 }
1606
1607 /*
1608 * The length of, and position in, the states list.
1609 */
1610 {
1611 char buf[80];
1612 sprintf(buf, "%d", me->nstates);
1613 wr("NSTATES", buf);
1614 sprintf(buf, "%d", me->statepos);
1615 wr("STATEPOS", buf);
1616 }
1617
1618 /*
1619 * For each state after the initial one (which we know is
1620 * constructed from either privdesc or desc), enough
1621 * information for execute_move() to reconstruct it from the
1622 * previous one.
1623 */
1624 for (i = 1; i < me->nstates; i++) {
1625 assert(me->states[i].movetype != NEWGAME); /* only state 0 */
1626 switch (me->states[i].movetype) {
1627 case MOVE:
1628 wr("MOVE", me->states[i].movestr);
1629 break;
1630 case SOLVE:
1631 wr("SOLVE", me->states[i].movestr);
1632 break;
1633 case RESTART:
1634 wr("RESTART", me->states[i].movestr);
1635 break;
1636 }
1637 }
1638
1639#undef wr
1640}
1641
1642/*
1643 * This function returns NULL on success, or an error message.
1644 */
1645char *midend_deserialise(midend *me,
1646 int (*read)(void *ctx, void *buf, int len),
1647 void *rctx)
1648{
1649 int nstates = 0, statepos = -1, gotstates = 0;
1650 int started = FALSE;
1651 int i;
1652
1653 char *val = NULL;
1654 /* Initially all errors give the same report */
1655 char *ret = "Data does not appear to be a saved game file";
1656
1657 /*
1658 * We construct all the new state in local variables while we
1659 * check its sanity. Only once we have finished reading the
1660 * serialised data and detected no errors at all do we start
1661 * modifying stuff in the midend passed in.
1662 */
1663 char *seed = NULL, *parstr = NULL, *desc = NULL, *privdesc = NULL;
1664 char *auxinfo = NULL, *uistr = NULL, *cparstr = NULL;
1665 float elapsed = 0.0F;
1666 game_params *params = NULL, *cparams = NULL;
1667 game_ui *ui = NULL;
1668 struct midend_state_entry *states = NULL;
1669
1670 /*
1671 * Loop round and round reading one key/value pair at a time
1672 * from the serialised stream, until we have enough game states
1673 * to finish.
1674 */
1675 while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) {
1676 char key[9], c;
1677 int len;
1678
1679 do {
1680 if (!read(rctx, key, 1)) {
1681 /* unexpected EOF */
1682 goto cleanup;
1683 }
1684 } while (key[0] == '\r' || key[0] == '\n');
1685
1686 if (!read(rctx, key+1, 8)) {
1687 /* unexpected EOF */
1688 goto cleanup;
1689 }
1690
1691 if (key[8] != ':') {
1692 if (started)
1693 ret = "Data was incorrectly formatted for a saved game file";
1694 goto cleanup;
1695 }
1696 len = strcspn(key, ": ");
1697 assert(len <= 8);
1698 key[len] = '\0';
1699
1700 len = 0;
1701 while (1) {
1702 if (!read(rctx, &c, 1)) {
1703 /* unexpected EOF */
1704 goto cleanup;
1705 }
1706
1707 if (c == ':') {
1708 break;
1709 } else if (c >= '0' && c <= '9') {
1710 len = (len * 10) + (c - '0');
1711 } else {
1712 if (started)
1713 ret = "Data was incorrectly formatted for a"
1714 " saved game file";
1715 goto cleanup;
1716 }
1717 }
1718
1719 val = snewn(len+1, char);
1720 if (!read(rctx, val, len)) {
1721 if (started)
1722 goto cleanup;
1723 }
1724 val[len] = '\0';
1725
1726 if (!started) {
1727 if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
1728 /* ret already has the right message in it */
1729 goto cleanup;
1730 }
1731 /* Now most errors are this one, unless otherwise specified */
1732 ret = "Saved data ended unexpectedly";
1733 started = TRUE;
1734 } else {
1735 if (!strcmp(key, "VERSION")) {
1736 if (strcmp(val, SERIALISE_VERSION)) {
1737 ret = "Cannot handle this version of the saved game"
1738 " file format";
1739 goto cleanup;
1740 }
1741 } else if (!strcmp(key, "GAME")) {
1742 if (strcmp(val, me->ourgame->name)) {
1743 ret = "Save file is from a different game";
1744 goto cleanup;
1745 }
1746 } else if (!strcmp(key, "PARAMS")) {
1747 sfree(parstr);
1748 parstr = val;
1749 val = NULL;
1750 } else if (!strcmp(key, "CPARAMS")) {
1751 sfree(cparstr);
1752 cparstr = val;
1753 val = NULL;
1754 } else if (!strcmp(key, "SEED")) {
1755 sfree(seed);
1756 seed = val;
1757 val = NULL;
1758 } else if (!strcmp(key, "DESC")) {
1759 sfree(desc);
1760 desc = val;
1761 val = NULL;
1762 } else if (!strcmp(key, "PRIVDESC")) {
1763 sfree(privdesc);
1764 privdesc = val;
1765 val = NULL;
1766 } else if (!strcmp(key, "AUXINFO")) {
1767 unsigned char *tmp;
1768 int len = strlen(val) / 2; /* length in bytes */
1769 tmp = hex2bin(val, len);
1770 obfuscate_bitmap(tmp, len*8, TRUE);
1771
1772 sfree(auxinfo);
1773 auxinfo = snewn(len + 1, char);
1774 memcpy(auxinfo, tmp, len);
1775 auxinfo[len] = '\0';
1776 sfree(tmp);
1777 } else if (!strcmp(key, "UI")) {
1778 sfree(uistr);
1779 uistr = val;
1780 val = NULL;
1781 } else if (!strcmp(key, "TIME")) {
1782 elapsed = (float)atof(val);
1783 } else if (!strcmp(key, "NSTATES")) {
1784 nstates = atoi(val);
1785 if (nstates <= 0) {
1786 ret = "Number of states in save file was negative";
1787 goto cleanup;
1788 }
1789 if (states) {
1790 ret = "Two state counts provided in save file";
1791 goto cleanup;
1792 }
1793 states = snewn(nstates, struct midend_state_entry);
1794 for (i = 0; i < nstates; i++) {
1795 states[i].state = NULL;
1796 states[i].movestr = NULL;
1797 states[i].movetype = NEWGAME;
1798 }
1799 } else if (!strcmp(key, "STATEPOS")) {
1800 statepos = atoi(val);
1801 } else if (!strcmp(key, "MOVE")) {
1802 gotstates++;
1803 states[gotstates].movetype = MOVE;
1804 states[gotstates].movestr = val;
1805 val = NULL;
1806 } else if (!strcmp(key, "SOLVE")) {
1807 gotstates++;
1808 states[gotstates].movetype = SOLVE;
1809 states[gotstates].movestr = val;
1810 val = NULL;
1811 } else if (!strcmp(key, "RESTART")) {
1812 gotstates++;
1813 states[gotstates].movetype = RESTART;
1814 states[gotstates].movestr = val;
1815 val = NULL;
1816 }
1817 }
1818
1819 sfree(val);
1820 val = NULL;
1821 }
1822
1823 params = me->ourgame->default_params();
1824 me->ourgame->decode_params(params, parstr);
1825 if (me->ourgame->validate_params(params, TRUE)) {
1826 ret = "Long-term parameters in save file are invalid";
1827 goto cleanup;
1828 }
1829 cparams = me->ourgame->default_params();
1830 me->ourgame->decode_params(cparams, cparstr);
1831 if (me->ourgame->validate_params(cparams, FALSE)) {
1832 ret = "Short-term parameters in save file are invalid";
1833 goto cleanup;
1834 }
1835 if (seed && me->ourgame->validate_params(cparams, TRUE)) {
1836 /*
1837 * The seed's no use with this version, but we can perfectly
1838 * well use the rest of the data.
1839 */
1840 sfree(seed);
1841 seed = NULL;
1842 }
1843 if (!desc) {
1844 ret = "Game description in save file is missing";
1845 goto cleanup;
1846 } else if (me->ourgame->validate_desc(params, desc)) {
1847 ret = "Game description in save file is invalid";
1848 goto cleanup;
1849 }
1850 if (privdesc && me->ourgame->validate_desc(params, privdesc)) {
1851 ret = "Game private description in save file is invalid";
1852 goto cleanup;
1853 }
1854 if (statepos < 0 || statepos >= nstates) {
1855 ret = "Game position in save file is out of range";
1856 }
1857
1858 states[0].state = me->ourgame->new_game(me, params,
1859 privdesc ? privdesc : desc);
1860 for (i = 1; i < nstates; i++) {
1861 assert(states[i].movetype != NEWGAME);
1862 switch (states[i].movetype) {
1863 case MOVE:
1864 case SOLVE:
1865 states[i].state = me->ourgame->execute_move(states[i-1].state,
1866 states[i].movestr);
1867 if (states[i].state == NULL) {
1868 ret = "Save file contained an invalid move";
1869 goto cleanup;
1870 }
1871 break;
1872 case RESTART:
1873 if (me->ourgame->validate_desc(params, states[i].movestr)) {
1874 ret = "Save file contained an invalid restart move";
1875 goto cleanup;
1876 }
1877 states[i].state = me->ourgame->new_game(me, params,
1878 states[i].movestr);
1879 break;
1880 }
1881 }
1882
1883 ui = me->ourgame->new_ui(states[0].state);
1884 me->ourgame->decode_ui(ui, uistr);
1885
1886 /*
1887 * Now we've run out of possible error conditions, so we're
1888 * ready to start overwriting the real data in the current
1889 * midend. We'll do this by swapping things with the local
1890 * variables, so that the same cleanup code will free the old
1891 * stuff.
1892 */
1893 {
1894 char *tmp;
1895
1896 tmp = me->desc;
1897 me->desc = desc;
1898 desc = tmp;
1899
1900 tmp = me->privdesc;
1901 me->privdesc = privdesc;
1902 privdesc = tmp;
1903
1904 tmp = me->seedstr;
1905 me->seedstr = seed;
1906 seed = tmp;
1907
1908 tmp = me->aux_info;
1909 me->aux_info = auxinfo;
1910 auxinfo = tmp;
1911 }
1912
1913 me->genmode = GOT_NOTHING;
1914
1915 me->statesize = nstates;
1916 nstates = me->nstates;
1917 me->nstates = me->statesize;
1918 {
1919 struct midend_state_entry *tmp;
1920 tmp = me->states;
1921 me->states = states;
1922 states = tmp;
1923 }
1924 me->statepos = statepos;
1925
1926 {
1927 game_params *tmp;
1928
1929 tmp = me->params;
1930 me->params = params;
1931 params = tmp;
1932
1933 tmp = me->curparams;
1934 me->curparams = cparams;
1935 cparams = tmp;
1936 }
1937
1938 me->oldstate = NULL;
1939 me->anim_time = me->anim_pos = me->flash_time = me->flash_pos = 0.0F;
1940 me->dir = 0;
1941
1942 {
1943 game_ui *tmp;
1944
1945 tmp = me->ui;
1946 me->ui = ui;
1947 ui = tmp;
1948 }
1949
1950 me->elapsed = elapsed;
1951 me->pressed_mouse_button = 0;
1952
1953 midend_set_timer(me);
1954
1955 if (me->drawstate)
1956 me->ourgame->free_drawstate(me->drawing, me->drawstate);
1957 me->drawstate =
1958 me->ourgame->new_drawstate(me->drawing,
1959 me->states[me->statepos-1].state);
1960 midend_size_new_drawstate(me);
1961
1962 ret = NULL; /* success! */
1963
1964 cleanup:
1965 sfree(val);
1966 sfree(seed);
1967 sfree(parstr);
1968 sfree(cparstr);
1969 sfree(desc);
1970 sfree(privdesc);
1971 sfree(auxinfo);
1972 sfree(uistr);
1973 if (params)
1974 me->ourgame->free_params(params);
1975 if (cparams)
1976 me->ourgame->free_params(cparams);
1977 if (ui)
1978 me->ourgame->free_ui(ui);
1979 if (states) {
1980 int i;
1981
1982 for (i = 0; i < nstates; i++) {
1983 if (states[i].state)
1984 me->ourgame->free_game(states[i].state);
1985 sfree(states[i].movestr);
1986 }
1987 sfree(states);
1988 }
1989
1990 return ret;
1991}
1992
1993/*
1994 * This function examines a saved game file just far enough to
1995 * determine which game type it contains. It returns NULL on success
1996 * and the game name string in 'name' (which will be dynamically
1997 * allocated and should be caller-freed), or an error message on
1998 * failure.
1999 */
2000char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len),
2001 void *rctx)
2002{
2003 int nstates = 0, statepos = -1, gotstates = 0;
2004 int started = FALSE;
2005
2006 char *val = NULL;
2007 /* Initially all errors give the same report */
2008 char *ret = "Data does not appear to be a saved game file";
2009
2010 *name = NULL;
2011
2012 /*
2013 * Loop round and round reading one key/value pair at a time from
2014 * the serialised stream, until we've found the game name.
2015 */
2016 while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) {
2017 char key[9], c;
2018 int len;
2019
2020 do {
2021 if (!read(rctx, key, 1)) {
2022 /* unexpected EOF */
2023 goto cleanup;
2024 }
2025 } while (key[0] == '\r' || key[0] == '\n');
2026
2027 if (!read(rctx, key+1, 8)) {
2028 /* unexpected EOF */
2029 goto cleanup;
2030 }
2031
2032 if (key[8] != ':') {
2033 if (started)
2034 ret = "Data was incorrectly formatted for a saved game file";
2035 goto cleanup;
2036 }
2037 len = strcspn(key, ": ");
2038 assert(len <= 8);
2039 key[len] = '\0';
2040
2041 len = 0;
2042 while (1) {
2043 if (!read(rctx, &c, 1)) {
2044 /* unexpected EOF */
2045 goto cleanup;
2046 }
2047
2048 if (c == ':') {
2049 break;
2050 } else if (c >= '0' && c <= '9') {
2051 len = (len * 10) + (c - '0');
2052 } else {
2053 if (started)
2054 ret = "Data was incorrectly formatted for a"
2055 " saved game file";
2056 goto cleanup;
2057 }
2058 }
2059
2060 val = snewn(len+1, char);
2061 if (!read(rctx, val, len)) {
2062 if (started)
2063 goto cleanup;
2064 }
2065 val[len] = '\0';
2066
2067 if (!started) {
2068 if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) {
2069 /* ret already has the right message in it */
2070 goto cleanup;
2071 }
2072 /* Now most errors are this one, unless otherwise specified */
2073 ret = "Saved data ended unexpectedly";
2074 started = TRUE;
2075 } else {
2076 if (!strcmp(key, "VERSION")) {
2077 if (strcmp(val, SERIALISE_VERSION)) {
2078 ret = "Cannot handle this version of the saved game"
2079 " file format";
2080 goto cleanup;
2081 }
2082 } else if (!strcmp(key, "GAME")) {
2083 *name = dupstr(val);
2084 ret = NULL;
2085 goto cleanup;
2086 }
2087 }
2088
2089 sfree(val);
2090 val = NULL;
2091 }
2092
2093 cleanup:
2094 sfree(val);
2095 return ret;
2096}
2097
2098char *midend_print_puzzle(midend *me, document *doc, int with_soln)
2099{
2100 game_state *soln = NULL;
2101
2102 if (me->statepos < 1)
2103 return "No game set up to print";/* _shouldn't_ happen! */
2104
2105 if (with_soln) {
2106 char *msg, *movestr;
2107
2108 if (!me->ourgame->can_solve)
2109 return "This game does not support the Solve operation";
2110
2111 msg = "Solve operation failed";/* game _should_ overwrite on error */
2112 movestr = me->ourgame->solve(me->states[0].state,
2113 me->states[me->statepos-1].state,
2114 me->aux_info, &msg);
2115 if (!movestr)
2116 return msg;
2117 soln = me->ourgame->execute_move(me->states[me->statepos-1].state,
2118 movestr);
2119 assert(soln);
2120
2121 sfree(movestr);
2122 } else
2123 soln = NULL;
2124
2125 /*
2126 * This call passes over ownership of the two game_states and
2127 * the game_params. Hence we duplicate the ones we want to
2128 * keep, and we don't have to bother freeing soln if it was
2129 * non-NULL.
2130 */
2131 document_add_puzzle(doc, me->ourgame,
2132 me->ourgame->dup_params(me->curparams),
2133 me->ourgame->dup_game(me->states[0].state), soln);
2134
2135 return NULL;
2136}
diff --git a/apps/plugins/puzzles/mines.R b/apps/plugins/puzzles/mines.R
new file mode 100644
index 0000000000..275c76c7ad
--- /dev/null
+++ b/apps/plugins/puzzles/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/mines.c b/apps/plugins/puzzles/mines.c
new file mode 100644
index 0000000000..deb71aed6b
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/misc.c b/apps/plugins/puzzles/misc.c
new file mode 100644
index 0000000000..caf52cd520
--- /dev/null
+++ b/apps/plugins/puzzles/misc.c
@@ -0,0 +1,363 @@
1/*
2 * misc.c: Miscellaneous helpful functions.
3 */
4
5#include "rbassert.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/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/mkauto.sh b/apps/plugins/puzzles/mkauto.sh
new file mode 100755
index 0000000000..297212ad4d
--- /dev/null
+++ b/apps/plugins/puzzles/mkauto.sh
@@ -0,0 +1,2 @@
1#! /bin/sh
2autoreconf -i && rm -rf autom4te.cache
diff --git a/apps/plugins/puzzles/mkfiles.pl b/apps/plugins/puzzles/mkfiles.pl
new file mode 100755
index 0000000000..c1623dfd12
--- /dev/null
+++ b/apps/plugins/puzzles/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/nestedvm.c b/apps/plugins/puzzles/nestedvm.c
new file mode 100644
index 0000000000..c61afecf6a
--- /dev/null
+++ b/apps/plugins/puzzles/nestedvm.c
@@ -0,0 +1,432 @@
1/*
2 * nestedvm.c: NestedVM front end for my puzzle collection.
3 */
4
5#include <stdio.h>
6#include "rbassert.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
385int main(int argc, char **argv)
386{
387 int i, n;
388 float* colours;
389
390 _fe = snew(frontend);
391 _fe->timer_active = FALSE;
392 _fe->me = midend_new(_fe, &thegame, &nestedvm_drawing, _fe);
393 if (argc > 1)
394 midend_game_id(_fe->me, argv[1]); /* ignore failure */
395 midend_new_game(_fe->me);
396
397 if ((n = midend_num_presets(_fe->me)) > 0) {
398 int i;
399 for (i = 0; i < n; i++) {
400 char *name;
401 game_params *params;
402 midend_fetch_preset(_fe->me, i, &name, &params);
403 _call_java(1, (int)name, (int)params, 0);
404 }
405 }
406
407 colours = midend_colours(_fe->me, &n);
408 _fe->ox = -1;
409
410 _call_java(0, (int)thegame.name,
411 (thegame.can_configure ? 1 : 0) |
412 (midend_wants_statusbar(_fe->me) ? 2 : 0) |
413 (thegame.can_solve ? 4 : 0), n);
414 for (i = 0; i < n; i++) {
415 _call_java(1024+ i,
416 (int)(colours[i*3] * 0xFF),
417 (int)(colours[i*3+1] * 0xFF),
418 (int)(colours[i*3+2] * 0xFF));
419 }
420 resize_fe(_fe);
421
422 _call_java(13, midend_which_preset(_fe->me), 0, 0);
423
424 // Now pause the vm. The VM will be call()ed when
425 // an input event occurs.
426 _pause();
427
428 // shut down when the VM is resumed.
429 deactivate_timer(_fe);
430 midend_free(_fe->me);
431 return 0;
432}
diff --git a/apps/plugins/puzzles/net.R b/apps/plugins/puzzles/net.R
new file mode 100644
index 0000000000..8e98216e03
--- /dev/null
+++ b/apps/plugins/puzzles/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/net.c b/apps/plugins/puzzles/net.c
new file mode 100644
index 0000000000..18ba7760d5
--- /dev/null
+++ b/apps/plugins/puzzles/net.c
@@ -0,0 +1,3210 @@
1/*
2 * net.c: Net game.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include "rbassert.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
2394 * is, because we can start from anywhere and correctly
2395 * determine whether the game is completed.
2396 */
2397 {
2398 unsigned char *active = compute_active(ret, 0, 0);
2399 int x1, y1;
2400 int complete = TRUE;
2401
2402 for (x1 = 0; x1 < ret->width; x1++)
2403 for (y1 = 0; y1 < ret->height; y1++)
2404 if ((tile(ret, x1, y1) & 0xF) && !index(ret, active, x1, y1)) {
2405 complete = FALSE;
2406 goto break_label; /* break out of two loops at once */
2407 }
2408 break_label:
2409
2410 sfree(active);
2411
2412 if (complete)
2413 ret->completed = TRUE;
2414 }
2415
2416 return ret;
2417}
2418
2419
2420/* ----------------------------------------------------------------------
2421 * Routines for drawing the game position on the screen.
2422 */
2423
2424static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2425{
2426 game_drawstate *ds = snew(game_drawstate);
2427 int i;
2428
2429 ds->started = FALSE;
2430 ds->width = state->width;
2431 ds->height = state->height;
2432 ds->org_x = ds->org_y = -1;
2433 ds->visible = snewn(state->width * state->height, int);
2434 ds->tilesize = 0; /* undecided yet */
2435 for (i = 0; i < state->width * state->height; i++)
2436 ds->visible[i] = -1;
2437
2438 return ds;
2439}
2440
2441static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2442{
2443 sfree(ds->visible);
2444 sfree(ds);
2445}
2446
2447static void game_compute_size(const game_params *params, int tilesize,
2448 int *x, int *y)
2449{
2450 *x = WINDOW_OFFSET * 2 + tilesize * params->width + TILE_BORDER;
2451 *y = WINDOW_OFFSET * 2 + tilesize * params->height + TILE_BORDER;
2452}
2453
2454static void game_set_size(drawing *dr, game_drawstate *ds,
2455 const game_params *params, int tilesize)
2456{
2457 ds->tilesize = tilesize;
2458}
2459
2460static float *game_colours(frontend *fe, int *ncolours)
2461{
2462 float *ret;
2463
2464 ret = snewn(NCOLOURS * 3, float);
2465 *ncolours = NCOLOURS;
2466
2467 /*
2468 * Basic background colour is whatever the front end thinks is
2469 * a sensible default.
2470 */
2471 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2472
2473 /*
2474 * Wires are black.
2475 */
2476 ret[COL_WIRE * 3 + 0] = 0.0F;
2477 ret[COL_WIRE * 3 + 1] = 0.0F;
2478 ret[COL_WIRE * 3 + 2] = 0.0F;
2479
2480 /*
2481 * Powered wires and powered endpoints are cyan.
2482 */
2483 ret[COL_POWERED * 3 + 0] = 0.0F;
2484 ret[COL_POWERED * 3 + 1] = 1.0F;
2485 ret[COL_POWERED * 3 + 2] = 1.0F;
2486
2487 /*
2488 * Barriers are red.
2489 */
2490 ret[COL_BARRIER * 3 + 0] = 1.0F;
2491 ret[COL_BARRIER * 3 + 1] = 0.0F;
2492 ret[COL_BARRIER * 3 + 2] = 0.0F;
2493
2494 /*
2495 * Highlighted loops are red as well.
2496 */
2497 ret[COL_LOOP * 3 + 0] = 1.0F;
2498 ret[COL_LOOP * 3 + 1] = 0.0F;
2499 ret[COL_LOOP * 3 + 2] = 0.0F;
2500
2501 /*
2502 * Unpowered endpoints are blue.
2503 */
2504 ret[COL_ENDPOINT * 3 + 0] = 0.0F;
2505 ret[COL_ENDPOINT * 3 + 1] = 0.0F;
2506 ret[COL_ENDPOINT * 3 + 2] = 1.0F;
2507
2508 /*
2509 * Tile borders are a darker grey than the background.
2510 */
2511 ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
2512 ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
2513 ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
2514
2515 /*
2516 * Locked tiles are a grey in between those two.
2517 */
2518 ret[COL_LOCKED * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
2519 ret[COL_LOCKED * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
2520 ret[COL_LOCKED * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
2521
2522 return ret;
2523}
2524
2525static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2,
2526 int colour)
2527{
2528 draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE);
2529 draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE);
2530 draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE);
2531 draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE);
2532 draw_line(dr, x1, y1, x2, y2, colour);
2533}
2534
2535static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2,
2536 int colour)
2537{
2538 int mx = (x1 < x2 ? x1 : x2);
2539 int my = (y1 < y2 ? y1 : y2);
2540 int dx = (x2 + x1 - 2*mx + 1);
2541 int dy = (y2 + y1 - 2*my + 1);
2542
2543 draw_rect(dr, mx, my, dx, dy, colour);
2544}
2545
2546/*
2547 * draw_barrier_corner() and draw_barrier() are passed physical coords
2548 */
2549static void draw_barrier_corner(drawing *dr, game_drawstate *ds,
2550 int x, int y, int dx, int dy, int phase)
2551{
2552 int bx = WINDOW_OFFSET + TILE_SIZE * x;
2553 int by = WINDOW_OFFSET + TILE_SIZE * y;
2554 int x1, y1;
2555
2556 x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
2557 y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
2558
2559 if (phase == 0) {
2560 draw_rect_coords(dr, bx+x1+dx, by+y1,
2561 bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy,
2562 COL_WIRE);
2563 draw_rect_coords(dr, bx+x1, by+y1+dy,
2564 bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
2565 COL_WIRE);
2566 } else {
2567 draw_rect_coords(dr, bx+x1, by+y1,
2568 bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy,
2569 COL_BARRIER);
2570 }
2571}
2572
2573static void draw_barrier(drawing *dr, game_drawstate *ds,
2574 int x, int y, int dir, int phase)
2575{
2576 int bx = WINDOW_OFFSET + TILE_SIZE * x;
2577 int by = WINDOW_OFFSET + TILE_SIZE * y;
2578 int x1, y1, w, h;
2579
2580 x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0);
2581 y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0);
2582 w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
2583 h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
2584
2585 if (phase == 0) {
2586 draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
2587 } else {
2588 draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER);
2589 }
2590}
2591
2592/*
2593 * draw_tile() is passed physical coordinates
2594 */
2595static void draw_tile(drawing *dr, const game_state *state, game_drawstate *ds,
2596 int x, int y, int tile, int src, float angle, int cursor)
2597{
2598 int bx = WINDOW_OFFSET + TILE_SIZE * x;
2599 int by = WINDOW_OFFSET + TILE_SIZE * y;
2600 float matrix[4];
2601 float cx, cy, ex, ey, tx, ty;
2602 int dir, col, phase;
2603
2604 /*
2605 * When we draw a single tile, we must draw everything up to
2606 * and including the borders around the tile. This means that
2607 * if the neighbouring tiles have connections to those borders,
2608 * we must draw those connections on the borders themselves.
2609 */
2610
2611 clip(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
2612
2613 /*
2614 * So. First blank the tile out completely: draw a big
2615 * rectangle in border colour, and a smaller rectangle in
2616 * background colour to fill it in.
2617 */
2618 draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER,
2619 COL_BORDER);
2620 draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER,
2621 TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER,
2622 tile & LOCKED ? COL_LOCKED : COL_BACKGROUND);
2623
2624 /*
2625 * Draw an inset outline rectangle as a cursor, in whichever of
2626 * COL_LOCKED and COL_BACKGROUND we aren't currently drawing
2627 * in.
2628 */
2629 if (cursor) {
2630 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8,
2631 bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2632 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2633 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8,
2634 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8,
2635 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2636 draw_line(dr, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8,
2637 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2638 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2639 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2640 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
2641 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
2642 }
2643
2644 /*
2645 * Set up the rotation matrix.
2646 */
2647 matrix[0] = (float)cos(angle * PI / 180.0);
2648 matrix[1] = (float)-sin(angle * PI / 180.0);
2649 matrix[2] = (float)sin(angle * PI / 180.0);
2650 matrix[3] = (float)cos(angle * PI / 180.0);
2651
2652 /*
2653 * Draw the wires.
2654 */
2655 cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F;
2656 col = (tile & ACTIVE ? COL_POWERED : COL_WIRE);
2657 for (dir = 1; dir < 0x10; dir <<= 1) {
2658 if (tile & dir) {
2659 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
2660 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
2661 MATMUL(tx, ty, matrix, ex, ey);
2662 draw_filled_line(dr, bx+(int)cx, by+(int)cy,
2663 bx+(int)(cx+tx), by+(int)(cy+ty),
2664 COL_WIRE);
2665 }
2666 }
2667 for (dir = 1; dir < 0x10; dir <<= 1) {
2668 if (tile & dir) {
2669 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
2670 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
2671 MATMUL(tx, ty, matrix, ex, ey);
2672 draw_line(dr, bx+(int)cx, by+(int)cy,
2673 bx+(int)(cx+tx), by+(int)(cy+ty),
2674 (tile & LOOP(dir)) ? COL_LOOP : col);
2675 }
2676 }
2677 /* If we've drawn any loop-highlighted arms, make sure the centre
2678 * point is loop-coloured rather than a later arm overwriting it. */
2679 if (tile & (RLOOP | ULOOP | LLOOP | DLOOP))
2680 draw_rect(dr, bx+(int)cx, by+(int)cy, 1, 1, COL_LOOP);
2681
2682 /*
2683 * Draw the box in the middle. We do this in blue if the tile
2684 * is an unpowered endpoint, in cyan if the tile is a powered
2685 * endpoint, in black if the tile is the centrepiece, and
2686 * otherwise not at all.
2687 */
2688 col = -1;
2689 if (src)
2690 col = COL_WIRE;
2691 else if (COUNT(tile) == 1) {
2692 col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT);
2693 }
2694 if (col >= 0) {
2695 int i, points[8];
2696
2697 points[0] = +1; points[1] = +1;
2698 points[2] = +1; points[3] = -1;
2699 points[4] = -1; points[5] = -1;
2700 points[6] = -1; points[7] = +1;
2701
2702 for (i = 0; i < 8; i += 2) {
2703 ex = (TILE_SIZE * 0.24F) * points[i];
2704 ey = (TILE_SIZE * 0.24F) * points[i+1];
2705 MATMUL(tx, ty, matrix, ex, ey);
2706 points[i] = bx+(int)(cx+tx);
2707 points[i+1] = by+(int)(cy+ty);
2708 }
2709
2710 draw_polygon(dr, points, 4, col, COL_WIRE);
2711 }
2712
2713 /*
2714 * Draw the points on the border if other tiles are connected
2715 * to us.
2716 */
2717 for (dir = 1; dir < 0x10; dir <<= 1) {
2718 int dx, dy, px, py, lx, ly, vx, vy, ox, oy;
2719
2720 dx = X(dir);
2721 dy = Y(dir);
2722
2723 ox = x + dx;
2724 oy = y + dy;
2725
2726 if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height)
2727 continue;
2728
2729 if (!(tile(state, GX(ox), GY(oy)) & F(dir)))
2730 continue;
2731
2732 px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx);
2733 py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy);
2734 lx = dx * (TILE_BORDER-1);
2735 ly = dy * (TILE_BORDER-1);
2736 vx = (dy ? 1 : 0);
2737 vy = (dx ? 1 : 0);
2738
2739 if (angle == 0.0 && (tile & dir)) {
2740 /*
2741 * If we are fully connected to the other tile, we must
2742 * draw right across the tile border. (We can use our
2743 * own ACTIVE state to determine what colour to do this
2744 * in: if we are fully connected to the other tile then
2745 * the two ACTIVE states will be the same.)
2746 */
2747 draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
2748 draw_rect_coords(dr, px, py, px+lx, py+ly,
2749 ((tile & LOOP(dir)) ? COL_LOOP :
2750 (tile & ACTIVE) ? COL_POWERED :
2751 COL_WIRE));
2752 } else {
2753 /*
2754 * The other tile extends into our border, but isn't
2755 * actually connected to us. Just draw a single black
2756 * dot.
2757 */
2758 draw_rect_coords(dr, px, py, px, py, COL_WIRE);
2759 }
2760 }
2761
2762 /*
2763 * Draw barrier corners, and then barriers.
2764 */
2765 for (phase = 0; phase < 2; phase++) {
2766 for (dir = 1; dir < 0x10; dir <<= 1) {
2767 int x1, y1, corner = FALSE;
2768 /*
2769 * If at least one barrier terminates at the corner
2770 * between dir and A(dir), draw a barrier corner.
2771 */
2772 if (barrier(state, GX(x), GY(y)) & (dir | A(dir))) {
2773 corner = TRUE;
2774 } else {
2775 /*
2776 * Only count barriers terminating at this corner
2777 * if they're physically next to the corner. (That
2778 * is, if they've wrapped round from the far side
2779 * of the screen, they don't count.)
2780 */
2781 x1 = x + X(dir);
2782 y1 = y + Y(dir);
2783 if (x1 >= 0 && x1 < state->width &&
2784 y1 >= 0 && y1 < state->height &&
2785 (barrier(state, GX(x1), GY(y1)) & A(dir))) {
2786 corner = TRUE;
2787 } else {
2788 x1 = x + X(A(dir));
2789 y1 = y + Y(A(dir));
2790 if (x1 >= 0 && x1 < state->width &&
2791 y1 >= 0 && y1 < state->height &&
2792 (barrier(state, GX(x1), GY(y1)) & dir))
2793 corner = TRUE;
2794 }
2795 }
2796
2797 if (corner) {
2798 /*
2799 * At least one barrier terminates here. Draw a
2800 * corner.
2801 */
2802 draw_barrier_corner(dr, ds, x, y,
2803 X(dir)+X(A(dir)), Y(dir)+Y(A(dir)),
2804 phase);
2805 }
2806 }
2807
2808 for (dir = 1; dir < 0x10; dir <<= 1)
2809 if (barrier(state, GX(x), GY(y)) & dir)
2810 draw_barrier(dr, ds, x, y, dir, phase);
2811 }
2812
2813 unclip(dr);
2814
2815 draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
2816}
2817
2818static void game_redraw(drawing *dr, game_drawstate *ds,
2819 const game_state *oldstate, const game_state *state,
2820 int dir, const game_ui *ui,
2821 float t, float ft)
2822{
2823 int x, y, tx, ty, frame, last_rotate_dir, moved_origin = FALSE;
2824 unsigned char *active;
2825 int *loops;
2826 float angle = 0.0;
2827
2828 /*
2829 * Clear the screen, and draw the exterior barrier lines, if
2830 * this is our first call or if the origin has changed.
2831 */
2832 if (!ds->started || ui->org_x != ds->org_x || ui->org_y != ds->org_y) {
2833 int phase;
2834
2835 ds->started = TRUE;
2836
2837 draw_rect(dr, 0, 0,
2838 WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
2839 WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
2840 COL_BACKGROUND);
2841
2842 ds->org_x = ui->org_x;
2843 ds->org_y = ui->org_y;
2844 moved_origin = TRUE;
2845
2846 draw_update(dr, 0, 0,
2847 WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
2848 WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
2849
2850 for (phase = 0; phase < 2; phase++) {
2851
2852 for (x = 0; x < ds->width; x++) {
2853 if (x+1 < ds->width) {
2854 if (barrier(state, GX(x), GY(0)) & R)
2855 draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
2856 if (barrier(state, GX(x), GY(ds->height-1)) & R)
2857 draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
2858 }
2859 if (barrier(state, GX(x), GY(0)) & U) {
2860 draw_barrier_corner(dr, ds, x, -1, -1, +1, phase);
2861 draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
2862 draw_barrier(dr, ds, x, -1, D, phase);
2863 }
2864 if (barrier(state, GX(x), GY(ds->height-1)) & D) {
2865 draw_barrier_corner(dr, ds, x, ds->height, -1, -1, phase);
2866 draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
2867 draw_barrier(dr, ds, x, ds->height, U, phase);
2868 }
2869 }
2870
2871 for (y = 0; y < ds->height; y++) {
2872 if (y+1 < ds->height) {
2873 if (barrier(state, GX(0), GY(y)) & D)
2874 draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
2875 if (barrier(state, GX(ds->width-1), GY(y)) & D)
2876 draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
2877 }
2878 if (barrier(state, GX(0), GY(y)) & L) {
2879 draw_barrier_corner(dr, ds, -1, y, +1, -1, phase);
2880 draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
2881 draw_barrier(dr, ds, -1, y, R, phase);
2882 }
2883 if (barrier(state, GX(ds->width-1), GY(y)) & R) {
2884 draw_barrier_corner(dr, ds, ds->width, y, -1, -1, phase);
2885 draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
2886 draw_barrier(dr, ds, ds->width, y, L, phase);
2887 }
2888 }
2889 }
2890 }
2891
2892 tx = ty = -1;
2893 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
2894 state->last_rotate_dir;
2895 if (oldstate && (t < ROTATE_TIME) && last_rotate_dir) {
2896 /*
2897 * We're animating a single tile rotation. Find the turning
2898 * tile.
2899 */
2900 tx = (dir==-1 ? oldstate->last_rotate_x : state->last_rotate_x);
2901 ty = (dir==-1 ? oldstate->last_rotate_y : state->last_rotate_y);
2902 angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME);
2903 state = oldstate;
2904 }
2905
2906 frame = -1;
2907 if (ft > 0) {
2908 /*
2909 * We're animating a completion flash. Find which frame
2910 * we're at.
2911 */
2912 frame = (int)(ft / FLASH_FRAME);
2913 }
2914
2915 /*
2916 * Draw any tile which differs from the way it was last drawn.
2917 */
2918 active = compute_active(state, ui->cx, ui->cy);
2919 loops = compute_loops(state);
2920
2921 for (x = 0; x < ds->width; x++)
2922 for (y = 0; y < ds->height; y++) {
2923 int c = tile(state, GX(x), GY(y)) |
2924 index(state, active, GX(x), GY(y)) |
2925 index(state, loops, GX(x), GY(y));
2926 int is_src = GX(x) == ui->cx && GY(y) == ui->cy;
2927 int is_anim = GX(x) == tx && GY(y) == ty;
2928 int is_cursor = ui->cur_visible &&
2929 GX(x) == ui->cur_x && GY(y) == ui->cur_y;
2930
2931 /*
2932 * In a completion flash, we adjust the LOCKED bit
2933 * depending on our distance from the centre point and
2934 * the frame number.
2935 */
2936 if (frame >= 0) {
2937 int rcx = RX(ui->cx), rcy = RY(ui->cy);
2938 int xdist, ydist, dist;
2939 xdist = (x < rcx ? rcx - x : x - rcx);
2940 ydist = (y < rcy ? rcy - y : y - rcy);
2941 dist = (xdist > ydist ? xdist : ydist);
2942
2943 if (frame >= dist && frame < dist+4) {
2944 int lock = (frame - dist) & 1;
2945 lock = lock ? LOCKED : 0;
2946 c = (c &~ LOCKED) | lock;
2947 }
2948 }
2949
2950 if (moved_origin ||
2951 index(state, ds->visible, x, y) != c ||
2952 index(state, ds->visible, x, y) == -1 ||
2953 is_src || is_anim || is_cursor) {
2954 draw_tile(dr, state, ds, x, y, c,
2955 is_src, (is_anim ? angle : 0.0F), is_cursor);
2956 if (is_src || is_anim || is_cursor)
2957 index(state, ds->visible, x, y) = -1;
2958 else
2959 index(state, ds->visible, x, y) = c;
2960 }
2961 }
2962
2963 /*
2964 * Update the status bar.
2965 */
2966 {
2967 char statusbuf[256];
2968 int i, n, n2, a;
2969
2970 n = state->width * state->height;
2971 for (i = a = n2 = 0; i < n; i++) {
2972 if (active[i])
2973 a++;
2974 if (state->tiles[i] & 0xF)
2975 n2++;
2976 }
2977
2978 sprintf(statusbuf, "%sActive: %d/%d",
2979 (state->used_solve ? "Auto-solved. " :
2980 state->completed ? "COMPLETED! " : ""), a, n2);
2981
2982 status_bar(dr, statusbuf);
2983 }
2984
2985 sfree(active);
2986 sfree(loops);
2987}
2988
2989static float game_anim_length(const game_state *oldstate,
2990 const game_state *newstate, int dir, game_ui *ui)
2991{
2992 int last_rotate_dir;
2993
2994 /*
2995 * Don't animate if last_rotate_dir is zero.
2996 */
2997 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
2998 newstate->last_rotate_dir;
2999 if (last_rotate_dir)
3000 return ROTATE_TIME;
3001
3002 return 0.0F;
3003}
3004
3005static float game_flash_length(const game_state *oldstate,
3006 const game_state *newstate, int dir, game_ui *ui)
3007{
3008 /*
3009 * If the game has just been completed, we display a completion
3010 * flash.
3011 */
3012 if (!oldstate->completed && newstate->completed &&
3013 !oldstate->used_solve && !newstate->used_solve) {
3014 int size = 0;
3015 if (size < newstate->width)
3016 size = newstate->width;
3017 if (size < newstate->height)
3018 size = newstate->height;
3019 return FLASH_FRAME * (size+4);
3020 }
3021
3022 return 0.0F;
3023}
3024
3025static int game_status(const game_state *state)
3026{
3027 return state->completed ? +1 : 0;
3028}
3029
3030static int game_timing_state(const game_state *state, game_ui *ui)
3031{
3032 return TRUE;
3033}
3034
3035static void game_print_size(const game_params *params, float *x, float *y)
3036{
3037 int pw, ph;
3038
3039 /*
3040 * I'll use 8mm squares by default.
3041 */
3042 game_compute_size(params, 800, &pw, &ph);
3043 *x = pw / 100.0F;
3044 *y = ph / 100.0F;
3045}
3046
3047static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y,
3048 int topleft, int v, int drawlines, int ink)
3049{
3050 int tx, ty, cx, cy, r, br, k, thick;
3051
3052 tx = WINDOW_OFFSET + TILE_SIZE * x;
3053 ty = WINDOW_OFFSET + TILE_SIZE * y;
3054
3055 /*
3056 * Find our centre point.
3057 */
3058 if (topleft) {
3059 cx = tx + (v & L ? TILE_SIZE / 4 : TILE_SIZE / 6);
3060 cy = ty + (v & U ? TILE_SIZE / 4 : TILE_SIZE / 6);
3061 r = TILE_SIZE / 8;
3062 br = TILE_SIZE / 32;
3063 } else {
3064 cx = tx + TILE_SIZE / 2;
3065 cy = ty + TILE_SIZE / 2;
3066 r = TILE_SIZE / 2;
3067 br = TILE_SIZE / 8;
3068 }
3069 thick = r / 20;
3070
3071 /*
3072 * Draw the square block if we have an endpoint.
3073 */
3074 if (v == 1 || v == 2 || v == 4 || v == 8)
3075 draw_rect(dr, cx - br, cy - br, br*2, br*2, ink);
3076
3077 /*
3078 * Draw each radial line.
3079 */
3080 if (drawlines) {
3081 for (k = 1; k < 16; k *= 2)
3082 if (v & k) {
3083 int x1 = min(cx, cx + (r-thick) * X(k));
3084 int x2 = max(cx, cx + (r-thick) * X(k));
3085 int y1 = min(cy, cy + (r-thick) * Y(k));
3086 int y2 = max(cy, cy + (r-thick) * Y(k));
3087 draw_rect(dr, x1 - thick, y1 - thick,
3088 (x2 - x1) + 2*thick, (y2 - y1) + 2*thick, ink);
3089 }
3090 }
3091}
3092
3093static void game_print(drawing *dr, const game_state *state, int tilesize)
3094{
3095 int w = state->width, h = state->height;
3096 int ink = print_mono_colour(dr, 0);
3097 int x, y;
3098
3099 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3100 game_drawstate ads, *ds = &ads;
3101 game_set_size(dr, ds, NULL, tilesize);
3102
3103 /*
3104 * Border.
3105 */
3106 print_line_width(dr, TILE_SIZE / (state->wrapping ? 128 : 12));
3107 draw_rect_outline(dr, WINDOW_OFFSET, WINDOW_OFFSET,
3108 TILE_SIZE * w, TILE_SIZE * h, ink);
3109
3110 /*
3111 * Grid.
3112 */
3113 print_line_width(dr, TILE_SIZE / 128);
3114 for (x = 1; x < w; x++)
3115 draw_line(dr, WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET,
3116 WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET + TILE_SIZE * h,
3117 ink);
3118 for (y = 1; y < h; y++)
3119 draw_line(dr, WINDOW_OFFSET, WINDOW_OFFSET + TILE_SIZE * y,
3120 WINDOW_OFFSET + TILE_SIZE * w, WINDOW_OFFSET + TILE_SIZE * y,
3121 ink);
3122
3123 /*
3124 * Barriers.
3125 */
3126 for (y = 0; y <= h; y++)
3127 for (x = 0; x <= w; x++) {
3128 int b = barrier(state, x % w, y % h);
3129 if (x < w && (b & U))
3130 draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
3131 WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
3132 TILE_SIZE + TILE_SIZE/24 * 2, TILE_SIZE/24 * 2, ink);
3133 if (y < h && (b & L))
3134 draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
3135 WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
3136 TILE_SIZE/24 * 2, TILE_SIZE + TILE_SIZE/24 * 2, ink);
3137 }
3138
3139 /*
3140 * Grid contents.
3141 */
3142 for (y = 0; y < h; y++)
3143 for (x = 0; x < w; x++) {
3144 int vx, v = tile(state, x, y);
3145 int locked = v & LOCKED;
3146
3147 v &= 0xF;
3148
3149 /*
3150 * Rotate into a standard orientation for the top left
3151 * corner diagram.
3152 */
3153 vx = v;
3154 while (vx != 0 && vx != 15 && vx != 1 && vx != 9 && vx != 13 &&
3155 vx != 5)
3156 vx = A(vx);
3157
3158 /*
3159 * Draw the top left corner diagram.
3160 */
3161 draw_diagram(dr, ds, x, y, TRUE, vx, TRUE, ink);
3162
3163 /*
3164 * Draw the real solution diagram, if we're doing so.
3165 */
3166 draw_diagram(dr, ds, x, y, FALSE, v, locked, ink);
3167 }
3168}
3169
3170#ifdef COMBINED
3171#define thegame net
3172#endif
3173
3174const struct game thegame = {
3175 "Net", "games.net", "net",
3176 default_params,
3177 game_fetch_preset,
3178 decode_params,
3179 encode_params,
3180 free_params,
3181 dup_params,
3182 TRUE, game_configure, custom_params,
3183 validate_params,
3184 new_game_desc,
3185 validate_desc,
3186 new_game,
3187 dup_game,
3188 free_game,
3189 TRUE, solve_game,
3190 FALSE, game_can_format_as_text_now, game_text_format,
3191 new_ui,
3192 free_ui,
3193 encode_ui,
3194 decode_ui,
3195 game_changed_state,
3196 interpret_move,
3197 execute_move,
3198 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3199 game_colours,
3200 game_new_drawstate,
3201 game_free_drawstate,
3202 game_redraw,
3203 game_anim_length,
3204 game_flash_length,
3205 game_status,
3206 TRUE, FALSE, game_print_size, game_print,
3207 TRUE, /* wants_statusbar */
3208 FALSE, game_timing_state,
3209 0, /* flags */
3210};
diff --git a/apps/plugins/puzzles/netslide.R b/apps/plugins/puzzles/netslide.R
new file mode 100644
index 0000000000..ecfe7c3df8
--- /dev/null
+++ b/apps/plugins/puzzles/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/netslide.c b/apps/plugins/puzzles/netslide.c
new file mode 100644
index 0000000000..e299b96eb3
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/no-icon.c b/apps/plugins/puzzles/no-icon.c
new file mode 100644
index 0000000000..114b2c57c7
--- /dev/null
+++ b/apps/plugins/puzzles/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/noicon.rc b/apps/plugins/puzzles/noicon.rc
new file mode 100644
index 0000000000..1de605d605
--- /dev/null
+++ b/apps/plugins/puzzles/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/nullfe.c b/apps/plugins/puzzles/nullfe.c
new file mode 100644
index 0000000000..4c9975b90e
--- /dev/null
+++ b/apps/plugins/puzzles/nullfe.c
@@ -0,0 +1,69 @@
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) {}
46
47void fatal(char *fmt, ...)
48{
49 va_list ap;
50
51 fprintf(stderr, "fatal error: ");
52
53 va_start(ap, fmt);
54 vfprintf(stderr, fmt, ap);
55 va_end(ap);
56
57 fprintf(stderr, "\n");
58 exit(1);
59}
60
61#ifdef DEBUGGING
62void debug_printf(char *fmt, ...)
63{
64 va_list ap;
65 va_start(ap, fmt);
66 vfprintf(stdout, fmt, ap);
67 va_end(ap);
68}
69#endif
diff --git a/apps/plugins/puzzles/nullgame.R b/apps/plugins/puzzles/nullgame.R
new file mode 100644
index 0000000000..41bdb85d57
--- /dev/null
+++ b/apps/plugins/puzzles/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/nullgame.c b/apps/plugins/puzzles/nullgame.c
new file mode 100644
index 0000000000..6e14c3237c
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/obfusc.c b/apps/plugins/puzzles/obfusc.c
new file mode 100644
index 0000000000..e95fa3f397
--- /dev/null
+++ b/apps/plugins/puzzles/obfusc.c
@@ -0,0 +1,126 @@
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 return 1;
67 }
68 p++;
69 }
70 } else {
71 if (!inhex) {
72 inhex = p;
73 } else {
74 return 1;
75 }
76 }
77 }
78
79 if (decode < 0) {
80 return 0;
81 }
82
83 if (outputmode == DEFAULT)
84 outputmode = (decode ? BINARY : HEX);
85
86 if (inhex) {
87 datalen = strlen(inhex) / 2;
88 data = hex2bin(inhex, datalen);
89 } else {
90 int datasize = 4096;
91 datalen = 0;
92 data = snewn(datasize, unsigned char);
93 while (1) {
94 int ret = fread(data + datalen, 1, datasize - datalen, stdin);
95 if (ret < 0) {
96 fprintf(stderr, "obfusc: read: %s\n", strerror(errno));
97 return 1;
98 } else if (ret == 0) {
99 break;
100 } else {
101 datalen += ret;
102 if (datasize - datalen < 4096) {
103 datasize = datalen * 5 / 4 + 4096;
104 data = sresize(data, datasize, unsigned char);
105 }
106 }
107 }
108 }
109
110 obfuscate_bitmap(data, datalen * 8, decode);
111
112 if (outputmode == BINARY) {
113 int ret = fwrite(data, 1, datalen, stdout);
114 if (ret < 0) {
115 fprintf(stderr, "obfusc: write: %s\n", strerror(errno));
116 return 1;
117 }
118 } else {
119 int i;
120 for (i = 0; i < datalen; i++)
121 printf("%02x", data[i]);
122 printf("\n");
123 }
124
125 return 0;
126}
diff --git a/apps/plugins/puzzles/osx-help.but b/apps/plugins/puzzles/osx-help.but
new file mode 100644
index 0000000000..fa45996aee
--- /dev/null
+++ b/apps/plugins/puzzles/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/osx-info.plist b/apps/plugins/puzzles/osx-info.plist
new file mode 100644
index 0000000000..9f4aef8e53
--- /dev/null
+++ b/apps/plugins/puzzles/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/osx.icns b/apps/plugins/puzzles/osx.icns
new file mode 100644
index 0000000000..b4346a0da1
--- /dev/null
+++ b/apps/plugins/puzzles/osx.icns
Binary files differ
diff --git a/apps/plugins/puzzles/osx.m b/apps/plugins/puzzles/osx.m
new file mode 100644
index 0000000000..4740124175
--- /dev/null
+++ b/apps/plugins/puzzles/osx.m
@@ -0,0 +1,1724 @@
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}
430- (id)initWithGame:(const game *)g;
431- (void)dealloc;
432- (void)processButton:(int)b x:(int)x y:(int)y;
433- (void)processKey:(int)b;
434- (void)keyDown:(NSEvent *)ev;
435- (void)activateTimer;
436- (void)deactivateTimer;
437- (void)setStatusLine:(char *)text;
438- (void)resizeForNewGameParams;
439- (void)updateTypeMenuTick;
440@end
441
442@implementation MyImageView
443
444- (void)setWindow:(GameWindow *)win
445{
446 ourwin = win;
447}
448
449- (void)mouseEvent:(NSEvent *)ev button:(int)b
450{
451 NSPoint point = [self convertPoint:[ev locationInWindow] fromView:nil];
452 [ourwin processButton:b x:point.x y:point.y];
453}
454
455- (void)mouseDown:(NSEvent *)ev
456{
457 unsigned mod = [ev modifierFlags];
458 [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_BUTTON :
459 (mod & NSShiftKeyMask) ? MIDDLE_BUTTON :
460 LEFT_BUTTON)];
461}
462- (void)mouseDragged:(NSEvent *)ev
463{
464 unsigned mod = [ev modifierFlags];
465 [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_DRAG :
466 (mod & NSShiftKeyMask) ? MIDDLE_DRAG :
467 LEFT_DRAG)];
468}
469- (void)mouseUp:(NSEvent *)ev
470{
471 unsigned mod = [ev modifierFlags];
472 [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_RELEASE :
473 (mod & NSShiftKeyMask) ? MIDDLE_RELEASE :
474 LEFT_RELEASE)];
475}
476- (void)rightMouseDown:(NSEvent *)ev
477{
478 unsigned mod = [ev modifierFlags];
479 [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_BUTTON :
480 RIGHT_BUTTON)];
481}
482- (void)rightMouseDragged:(NSEvent *)ev
483{
484 unsigned mod = [ev modifierFlags];
485 [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_DRAG :
486 RIGHT_DRAG)];
487}
488- (void)rightMouseUp:(NSEvent *)ev
489{
490 unsigned mod = [ev modifierFlags];
491 [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_RELEASE :
492 RIGHT_RELEASE)];
493}
494- (void)otherMouseDown:(NSEvent *)ev
495{
496 [self mouseEvent:ev button:MIDDLE_BUTTON];
497}
498- (void)otherMouseDragged:(NSEvent *)ev
499{
500 [self mouseEvent:ev button:MIDDLE_DRAG];
501}
502- (void)otherMouseUp:(NSEvent *)ev
503{
504 [self mouseEvent:ev button:MIDDLE_RELEASE];
505}
506@end
507
508@implementation GameWindow
509- (void)setupContentView
510{
511 NSRect frame;
512 int w, h;
513
514 if (status) {
515 frame = [status frame];
516 frame.origin.y = frame.size.height;
517 } else
518 frame.origin.y = 0;
519 frame.origin.x = 0;
520
521 w = h = INT_MAX;
522 midend_size(me, &w, &h, FALSE);
523 frame.size.width = w;
524 frame.size.height = h;
525 fe.w = w;
526 fe.h = h;
527
528 fe.image = [[NSImage alloc] initWithSize:frame.size];
529 fe.view = [[MyImageView alloc] initWithFrame:frame];
530 [fe.view setImage:fe.image];
531 [fe.view setWindow:self];
532
533 midend_redraw(me);
534
535 [[self contentView] addSubview:fe.view];
536}
537- (id)initWithGame:(const game *)g
538{
539 NSRect rect = { {0,0}, {0,0} }, rect2;
540 int w, h;
541
542 ourgame = g;
543
544 fe.window = self;
545
546 me = midend_new(&fe, ourgame, &osx_drawing, &fe);
547 /*
548 * If we ever need to open a fresh window using a provided game
549 * ID, I think the right thing is to move most of this method
550 * into a new initWithGame:gameID: method, and have
551 * initWithGame: simply call that one and pass it NULL.
552 */
553 midend_new_game(me);
554 w = h = INT_MAX;
555 midend_size(me, &w, &h, FALSE);
556 rect.size.width = w;
557 rect.size.height = h;
558 fe.w = w;
559 fe.h = h;
560
561 /*
562 * Create the status bar, which will just be an NSTextField.
563 */
564 if (midend_wants_statusbar(me)) {
565 status = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,100,50)];
566 [status setEditable:NO];
567 [status setSelectable:NO];
568 [status setBordered:YES];
569 [status setBezeled:YES];
570 [status setBezelStyle:NSTextFieldSquareBezel];
571 [status setDrawsBackground:YES];
572 [[status cell] setTitle:@DEFAULT_STATUSBAR_TEXT];
573 [status sizeToFit];
574 rect2 = [status frame];
575 rect.size.height += rect2.size.height;
576 rect2.size.width = rect.size.width;
577 rect2.origin.x = rect2.origin.y = 0;
578 [status setFrame:rect2];
579 } else
580 status = nil;
581
582 self = [super initWithContentRect:rect
583 styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |
584 NSClosableWindowMask)
585 backing:NSBackingStoreBuffered
586 defer:YES];
587 [self setTitle:[NSString stringWithUTF8String:ourgame->name]];
588
589 {
590 float *colours;
591 int i, ncolours;
592
593 colours = midend_colours(me, &ncolours);
594 fe.ncolours = ncolours;
595 fe.colours = snewn(ncolours, NSColor *);
596
597 for (i = 0; i < ncolours; i++) {
598 fe.colours[i] = [[NSColor colorWithDeviceRed:colours[i*3]
599 green:colours[i*3+1] blue:colours[i*3+2]
600 alpha:1.0] retain];
601 }
602 }
603
604 [self setupContentView];
605 if (status)
606 [[self contentView] addSubview:status];
607 [self setIgnoresMouseEvents:NO];
608
609 [self center]; /* :-) */
610
611 return self;
612}
613
614- (void)dealloc
615{
616 int i;
617 for (i = 0; i < fe.ncolours; i++) {
618 [fe.colours[i] release];
619 }
620 sfree(fe.colours);
621 midend_free(me);
622 [super dealloc];
623}
624
625- (void)processButton:(int)b x:(int)x y:(int)y
626{
627 if (!midend_process_key(me, x, fe.h - 1 - y, b))
628 [self close];
629}
630
631- (void)processKey:(int)b
632{
633 if (!midend_process_key(me, -1, -1, b))
634 [self close];
635}
636
637- (void)keyDown:(NSEvent *)ev
638{
639 NSString *s = [ev characters];
640 int i, n = [s length];
641
642 for (i = 0; i < n; i++) {
643 int c = [s characterAtIndex:i];
644
645 /*
646 * ASCII gets passed straight to midend_process_key.
647 * Anything above that has to be translated to our own
648 * function key codes.
649 */
650 if (c >= 0x80) {
651 int mods = FALSE;
652 switch (c) {
653 case NSUpArrowFunctionKey:
654 c = CURSOR_UP;
655 mods = TRUE;
656 break;
657 case NSDownArrowFunctionKey:
658 c = CURSOR_DOWN;
659 mods = TRUE;
660 break;
661 case NSLeftArrowFunctionKey:
662 c = CURSOR_LEFT;
663 mods = TRUE;
664 break;
665 case NSRightArrowFunctionKey:
666 c = CURSOR_RIGHT;
667 mods = TRUE;
668 break;
669 default:
670 continue;
671 }
672
673 if (mods) {
674 if ([ev modifierFlags] & NSShiftKeyMask)
675 c |= MOD_SHFT;
676 if ([ev modifierFlags] & NSControlKeyMask)
677 c |= MOD_CTRL;
678 }
679 }
680
681 if (c >= '0' && c <= '9' && ([ev modifierFlags] & NSNumericPadKeyMask))
682 c |= MOD_NUM_KEYPAD;
683
684 [self processKey:c];
685 }
686}
687
688- (void)activateTimer
689{
690 if (timer != nil)
691 return;
692
693 timer = [NSTimer scheduledTimerWithTimeInterval:0.02
694 target:self selector:@selector(timerTick:)
695 userInfo:nil repeats:YES];
696 gettimeofday(&last_time, NULL);
697}
698
699- (void)deactivateTimer
700{
701 if (timer == nil)
702 return;
703
704 [timer invalidate];
705 timer = nil;
706}
707
708- (void)timerTick:(id)sender
709{
710 struct timeval now;
711 float elapsed;
712 gettimeofday(&now, NULL);
713 elapsed = ((now.tv_usec - last_time.tv_usec) * 0.000001F +
714 (now.tv_sec - last_time.tv_sec));
715 midend_timer(me, elapsed);
716 last_time = now;
717}
718
719- (void)showError:(char *)message
720{
721 NSAlert *alert;
722
723 alert = [[[NSAlert alloc] init] autorelease];
724 [alert addButtonWithTitle:@"Bah"];
725 [alert setInformativeText:[NSString stringWithUTF8String:message]];
726 [alert beginSheetModalForWindow:self modalDelegate:nil
727 didEndSelector:NULL contextInfo:nil];
728}
729
730- (void)newGame:(id)sender
731{
732 [self processKey:'n'];
733}
734- (void)restartGame:(id)sender
735{
736 midend_restart_game(me);
737}
738- (void)saveGame:(id)sender
739{
740 NSSavePanel *sp = [NSSavePanel savePanel];
741
742 if ([sp runModal] == NSFileHandlingPanelOKButton) {
743 const char *name = [[sp filename] UTF8String];
744
745 FILE *fp = fopen(name, "w");
746
747 if (!fp) {
748 [self showError:"Unable to open save file"];
749 return;
750 }
751
752 midend_serialise(me, savefile_write, fp);
753
754 fclose(fp);
755 }
756}
757- (void)loadSavedGame:(id)sender
758{
759 NSOpenPanel *op = [NSOpenPanel openPanel];
760
761 [op setAllowsMultipleSelection:NO];
762
763 if ([op runModalForTypes:nil] == NSOKButton) {
764 /*
765 * This used to be
766 *
767 * [[[op filenames] objectAtIndex:0] cString]
768 *
769 * but the plain cString method became deprecated and Xcode 7
770 * started complaining about it. Since OS X 10.9 we can
771 * apparently use the more modern API
772 *
773 * [[[op URLs] objectAtIndex:0] fileSystemRepresentation]
774 *
775 * but the alternative below still compiles with Xcode 7 and
776 * is a bit more backwards compatible, so I'll try it for the
777 * moment.
778 */
779 const char *name = [[[op filenames] objectAtIndex:0]
780 cStringUsingEncoding:
781 [NSString defaultCStringEncoding]];
782 char *err;
783
784 FILE *fp = fopen(name, "r");
785
786 if (!fp) {
787 [self showError:"Unable to open saved game file"];
788 return;
789 }
790
791 err = midend_deserialise(me, savefile_read, fp);
792
793 fclose(fp);
794
795 if (err) {
796 [self showError:err];
797 return;
798 }
799
800 [self resizeForNewGameParams];
801 [self updateTypeMenuTick];
802 }
803}
804- (void)undoMove:(id)sender
805{
806 [self processKey:'u'];
807}
808- (void)redoMove:(id)sender
809{
810 [self processKey:'r'&0x1F];
811}
812
813- (void)copy:(id)sender
814{
815 char *text;
816
817 if ((text = midend_text_format(me)) != NULL) {
818 NSPasteboard *pb = [NSPasteboard generalPasteboard];
819 NSArray *a = [NSArray arrayWithObject:NSStringPboardType];
820 [pb declareTypes:a owner:nil];
821 [pb setString:[NSString stringWithUTF8String:text]
822 forType:NSStringPboardType];
823 } else
824 NSBeep();
825}
826
827- (void)solveGame:(id)sender
828{
829 char *msg;
830
831 msg = midend_solve(me);
832
833 if (msg)
834 [self showError:msg];
835}
836
837- (BOOL)validateMenuItem:(NSMenuItem *)item
838{
839 if ([item action] == @selector(copy:))
840 return (ourgame->can_format_as_text_ever &&
841 midend_can_format_as_text_now(me) ? YES : NO);
842 else if ([item action] == @selector(solveGame:))
843 return (ourgame->can_solve ? YES : NO);
844 else
845 return [super validateMenuItem:item];
846}
847
848- (void)clearTypeMenu
849{
850 while ([typemenu numberOfItems] > 1)
851 [typemenu removeItemAtIndex:0];
852 [[typemenu itemAtIndex:0] setState:NSOffState];
853}
854
855- (void)updateTypeMenuTick
856{
857 int i, total, n;
858
859 total = [typemenu numberOfItems];
860 n = midend_which_preset(me);
861 if (n < 0)
862 n = total - 1; /* that's always where "Custom" lives */
863 for (i = 0; i < total; i++)
864 [[typemenu itemAtIndex:i] setState:(i == n ? NSOnState : NSOffState)];
865}
866
867- (void)becomeKeyWindow
868{
869 int n;
870
871 [self clearTypeMenu];
872
873 [super becomeKeyWindow];
874
875 n = midend_num_presets(me);
876
877 if (n > 0) {
878 [typemenu insertItem:[NSMenuItem separatorItem] atIndex:0];
879 while (n--) {
880 char *name;
881 game_params *params;
882 DataMenuItem *item;
883
884 midend_fetch_preset(me, n, &name, &params);
885
886 item = [[[DataMenuItem alloc]
887 initWithTitle:[NSString stringWithUTF8String:name]
888 action:NULL keyEquivalent:@""]
889 autorelease];
890
891 [item setEnabled:YES];
892 [item setTarget:self];
893 [item setAction:@selector(presetGame:)];
894 [item setPayload:params];
895
896 [typemenu insertItem:item atIndex:0];
897 }
898 }
899
900 [self updateTypeMenuTick];
901}
902
903- (void)resignKeyWindow
904{
905 [self clearTypeMenu];
906 [super resignKeyWindow];
907}
908
909- (void)close
910{
911 [self clearTypeMenu];
912 [super close];
913}
914
915- (void)resizeForNewGameParams
916{
917 NSSize size = {0,0};
918 int w, h;
919
920 w = h = INT_MAX;
921 midend_size(me, &w, &h, FALSE);
922 size.width = w;
923 size.height = h;
924 fe.w = w;
925 fe.h = h;
926
927 if (status) {
928 NSRect frame = [status frame];
929 size.height += frame.size.height;
930 frame.size.width = size.width;
931 [status setFrame:frame];
932 }
933
934#ifndef GNUSTEP
935 NSDisableScreenUpdates();
936#endif
937 [self setContentSize:size];
938 [self setupContentView];
939#ifndef GNUSTEP
940 NSEnableScreenUpdates();
941#endif
942}
943
944- (void)presetGame:(id)sender
945{
946 game_params *params = [sender getPayload];
947
948 midend_set_params(me, params);
949 midend_new_game(me);
950
951 [self resizeForNewGameParams];
952 [self updateTypeMenuTick];
953}
954
955- (void)startConfigureSheet:(int)which
956{
957 NSButton *ok, *cancel;
958 int actw, acth, leftw, rightw, totalw, h, thish, y;
959 int k;
960 NSRect rect, tmprect;
961 const int SPACING = 16;
962 char *title;
963 config_item *i;
964 int cfg_controlsize;
965 NSTextField *tf;
966 NSButton *b;
967 NSPopUpButton *pb;
968
969 assert(sheet == NULL);
970
971 /*
972 * Every control we create here is going to have this size
973 * until we tell it to calculate a better one.
974 */
975 tmprect = NSMakeRect(0, 0, 100, 50);
976
977 /*
978 * Set up OK and Cancel buttons. (Actually, MacOS doesn't seem
979 * to be fond of generic OK and Cancel wording, so I'm going to
980 * rename them to something nicer.)
981 */
982 actw = acth = 0;
983
984 cancel = [[NSButton alloc] initWithFrame:tmprect];
985 [cancel setBezelStyle:NSRoundedBezelStyle];
986 [cancel setTitle:@"Abandon"];
987 [cancel setTarget:self];
988 [cancel setKeyEquivalent:@"\033"];
989 [cancel setAction:@selector(sheetCancelButton:)];
990 [cancel sizeToFit];
991 rect = [cancel frame];
992 if (actw < rect.size.width) actw = rect.size.width;
993 if (acth < rect.size.height) acth = rect.size.height;
994
995 ok = [[NSButton alloc] initWithFrame:tmprect];
996 [ok setBezelStyle:NSRoundedBezelStyle];
997 [ok setTitle:@"Accept"];
998 [ok setTarget:self];
999 [ok setKeyEquivalent:@"\r"];
1000 [ok setAction:@selector(sheetOKButton:)];
1001 [ok sizeToFit];
1002 rect = [ok frame];
1003 if (actw < rect.size.width) actw = rect.size.width;
1004 if (acth < rect.size.height) acth = rect.size.height;
1005
1006 totalw = SPACING + 2 * actw;
1007 h = 2 * SPACING + acth;
1008
1009 /*
1010 * Now fetch the midend config data and go through it creating
1011 * controls.
1012 */
1013 cfg = midend_get_config(me, which, &title);
1014 sfree(title); /* FIXME: should we use this somehow? */
1015 cfg_which = which;
1016
1017 cfg_ncontrols = cfg_controlsize = 0;
1018 cfg_controls = NULL;
1019 leftw = rightw = 0;
1020 for (i = cfg; i->type != C_END; i++) {
1021 if (cfg_controlsize < cfg_ncontrols + 5) {
1022 cfg_controlsize = cfg_ncontrols + 32;
1023 cfg_controls = sresize(cfg_controls, cfg_controlsize, NSView *);
1024 }
1025
1026 thish = 0;
1027
1028 switch (i->type) {
1029 case C_STRING:
1030 /*
1031 * Two NSTextFields, one being a label and the other
1032 * being an edit box.
1033 */
1034
1035 tf = [[NSTextField alloc] initWithFrame:tmprect];
1036 [tf setEditable:NO];
1037 [tf setSelectable:NO];
1038 [tf setBordered:NO];
1039 [tf setDrawsBackground:NO];
1040 [[tf cell] setTitle:[NSString stringWithUTF8String:i->name]];
1041 [tf sizeToFit];
1042 rect = [tf frame];
1043 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1044 if (leftw < rect.size.width + 1) leftw = rect.size.width + 1;
1045 cfg_controls[cfg_ncontrols++] = tf;
1046
1047 tf = [[NSTextField alloc] initWithFrame:tmprect];
1048 [tf setEditable:YES];
1049 [tf setSelectable:YES];
1050 [tf setBordered:YES];
1051 [[tf cell] setTitle:[NSString stringWithUTF8String:i->sval]];
1052 [tf sizeToFit];
1053 rect = [tf frame];
1054 /*
1055 * We impose a minimum and maximum width on editable
1056 * NSTextFields. If we allow them to size themselves to
1057 * the contents of the text within them, then they will
1058 * look very silly if that text is only one or two
1059 * characters, and equally silly if it's an absolutely
1060 * enormous Rectangles or Pattern game ID!
1061 */
1062 if (rect.size.width < 75) rect.size.width = 75;
1063 if (rect.size.width > 400) rect.size.width = 400;
1064
1065 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1066 if (rightw < rect.size.width + 1) rightw = rect.size.width + 1;
1067 cfg_controls[cfg_ncontrols++] = tf;
1068 break;
1069
1070 case C_BOOLEAN:
1071 /*
1072 * A checkbox is an NSButton with a type of
1073 * NSSwitchButton.
1074 */
1075 b = [[NSButton alloc] initWithFrame:tmprect];
1076 [b setBezelStyle:NSRoundedBezelStyle];
1077 [b setButtonType:NSSwitchButton];
1078 [b setTitle:[NSString stringWithUTF8String:i->name]];
1079 [b sizeToFit];
1080 [b setState:(i->ival ? NSOnState : NSOffState)];
1081 rect = [b frame];
1082 if (totalw < rect.size.width + 1) totalw = rect.size.width + 1;
1083 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1084 cfg_controls[cfg_ncontrols++] = b;
1085 break;
1086
1087 case C_CHOICES:
1088 /*
1089 * A pop-up menu control is an NSPopUpButton, which
1090 * takes an embedded NSMenu. We also need an
1091 * NSTextField to act as a label.
1092 */
1093
1094 tf = [[NSTextField alloc] initWithFrame:tmprect];
1095 [tf setEditable:NO];
1096 [tf setSelectable:NO];
1097 [tf setBordered:NO];
1098 [tf setDrawsBackground:NO];
1099 [[tf cell] setTitle:[NSString stringWithUTF8String:i->name]];
1100 [tf sizeToFit];
1101 rect = [tf frame];
1102 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1103 if (leftw < rect.size.width + 1) leftw = rect.size.width + 1;
1104 cfg_controls[cfg_ncontrols++] = tf;
1105
1106 pb = [[NSPopUpButton alloc] initWithFrame:tmprect pullsDown:NO];
1107 [pb setBezelStyle:NSRoundedBezelStyle];
1108 {
1109 char c, *p;
1110
1111 p = i->sval;
1112 c = *p++;
1113 while (*p) {
1114 char *q, *copy;
1115
1116 q = p;
1117 while (*p && *p != c) p++;
1118
1119 copy = snewn((p-q) + 1, char);
1120 memcpy(copy, q, p-q);
1121 copy[p-q] = '\0';
1122 [pb addItemWithTitle:[NSString stringWithUTF8String:copy]];
1123 sfree(copy);
1124
1125 if (*p) p++;
1126 }
1127 }
1128 [pb selectItemAtIndex:i->ival];
1129 [pb sizeToFit];
1130
1131 rect = [pb frame];
1132 if (rightw < rect.size.width + 1) rightw = rect.size.width + 1;
1133 if (thish < rect.size.height + 1) thish = rect.size.height + 1;
1134 cfg_controls[cfg_ncontrols++] = pb;
1135 break;
1136 }
1137
1138 h += SPACING + thish;
1139 }
1140
1141 if (totalw < leftw + SPACING + rightw)
1142 totalw = leftw + SPACING + rightw;
1143 if (totalw > leftw + SPACING + rightw) {
1144 int excess = totalw - (leftw + SPACING + rightw);
1145 int leftexcess = leftw * excess / (leftw + rightw);
1146 int rightexcess = excess - leftexcess;
1147 leftw += leftexcess;
1148 rightw += rightexcess;
1149 }
1150
1151 /*
1152 * Now go through the list again, setting the final position
1153 * for each control.
1154 */
1155 k = 0;
1156 y = h;
1157 for (i = cfg; i->type != C_END; i++) {
1158 y -= SPACING;
1159 thish = 0;
1160 switch (i->type) {
1161 case C_STRING:
1162 case C_CHOICES:
1163 /*
1164 * These two are treated identically, since both expect
1165 * a control on the left and another on the right.
1166 */
1167 rect = [cfg_controls[k] frame];
1168 if (thish < rect.size.height + 1)
1169 thish = rect.size.height + 1;
1170 rect = [cfg_controls[k+1] frame];
1171 if (thish < rect.size.height + 1)
1172 thish = rect.size.height + 1;
1173 rect = [cfg_controls[k] frame];
1174 rect.origin.y = y - thish/2 - rect.size.height/2;
1175 rect.origin.x = SPACING;
1176 rect.size.width = leftw;
1177 [cfg_controls[k] setFrame:rect];
1178 rect = [cfg_controls[k+1] frame];
1179 rect.origin.y = y - thish/2 - rect.size.height/2;
1180 rect.origin.x = 2 * SPACING + leftw;
1181 rect.size.width = rightw;
1182 [cfg_controls[k+1] setFrame:rect];
1183 k += 2;
1184 break;
1185
1186 case C_BOOLEAN:
1187 rect = [cfg_controls[k] frame];
1188 if (thish < rect.size.height + 1)
1189 thish = rect.size.height + 1;
1190 rect.origin.y = y - thish/2 - rect.size.height/2;
1191 rect.origin.x = SPACING;
1192 rect.size.width = totalw;
1193 [cfg_controls[k] setFrame:rect];
1194 k++;
1195 break;
1196 }
1197 y -= thish;
1198 }
1199
1200 assert(k == cfg_ncontrols);
1201
1202 [cancel setFrame:NSMakeRect(SPACING+totalw/4-actw/2, SPACING, actw, acth)];
1203 [ok setFrame:NSMakeRect(SPACING+3*totalw/4-actw/2, SPACING, actw, acth)];
1204
1205 sheet = [[NSWindow alloc]
1206 initWithContentRect:NSMakeRect(0,0,totalw + 2*SPACING,h)
1207 styleMask:NSTitledWindowMask | NSClosableWindowMask
1208 backing:NSBackingStoreBuffered
1209 defer:YES];
1210
1211 [[sheet contentView] addSubview:cancel];
1212 [[sheet contentView] addSubview:ok];
1213
1214 for (k = 0; k < cfg_ncontrols; k++)
1215 [[sheet contentView] addSubview:cfg_controls[k]];
1216
1217 [app beginSheet:sheet modalForWindow:self
1218 modalDelegate:nil didEndSelector:NULL contextInfo:nil];
1219}
1220
1221- (void)specificGame:(id)sender
1222{
1223 [self startConfigureSheet:CFG_DESC];
1224}
1225
1226- (void)specificRandomGame:(id)sender
1227{
1228 [self startConfigureSheet:CFG_SEED];
1229}
1230
1231- (void)customGameType:(id)sender
1232{
1233 [self startConfigureSheet:CFG_SETTINGS];
1234}
1235
1236- (void)sheetEndWithStatus:(BOOL)update
1237{
1238 assert(sheet != NULL);
1239 [app endSheet:sheet];
1240 [sheet orderOut:self];
1241 sheet = NULL;
1242 if (update) {
1243 int k;
1244 config_item *i;
1245 char *error;
1246
1247 k = 0;
1248 for (i = cfg; i->type != C_END; i++) {
1249 switch (i->type) {
1250 case C_STRING:
1251 sfree(i->sval);
1252 i->sval = dupstr([[[(id)cfg_controls[k+1] cell]
1253 title] UTF8String]);
1254 k += 2;
1255 break;
1256 case C_BOOLEAN:
1257 i->ival = [(id)cfg_controls[k] state] == NSOnState;
1258 k++;
1259 break;
1260 case C_CHOICES:
1261 i->ival = [(id)cfg_controls[k+1] indexOfSelectedItem];
1262 k += 2;
1263 break;
1264 }
1265 }
1266
1267 error = midend_set_config(me, cfg_which, cfg);
1268 if (error) {
1269 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
1270 [alert addButtonWithTitle:@"Bah"];
1271 [alert setInformativeText:[NSString stringWithUTF8String:error]];
1272 [alert beginSheetModalForWindow:self modalDelegate:nil
1273 didEndSelector:NULL contextInfo:nil];
1274 } else {
1275 midend_new_game(me);
1276 [self resizeForNewGameParams];
1277 [self updateTypeMenuTick];
1278 }
1279 }
1280 sfree(cfg_controls);
1281 cfg_controls = NULL;
1282}
1283- (void)sheetOKButton:(id)sender
1284{
1285 [self sheetEndWithStatus:YES];
1286}
1287- (void)sheetCancelButton:(id)sender
1288{
1289 [self sheetEndWithStatus:NO];
1290}
1291
1292- (void)setStatusLine:(char *)text
1293{
1294 [[status cell] setTitle:[NSString stringWithUTF8String:text]];
1295}
1296
1297@end
1298
1299/*
1300 * Drawing routines called by the midend.
1301 */
1302static void osx_draw_polygon(void *handle, int *coords, int npoints,
1303 int fillcolour, int outlinecolour)
1304{
1305 frontend *fe = (frontend *)handle;
1306 NSBezierPath *path = [NSBezierPath bezierPath];
1307 int i;
1308
1309 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
1310
1311 for (i = 0; i < npoints; i++) {
1312 NSPoint p = { coords[i*2] + 0.5, fe->h - coords[i*2+1] - 0.5 };
1313 if (i == 0)
1314 [path moveToPoint:p];
1315 else
1316 [path lineToPoint:p];
1317 }
1318
1319 [path closePath];
1320
1321 if (fillcolour >= 0) {
1322 assert(fillcolour >= 0 && fillcolour < fe->ncolours);
1323 [fe->colours[fillcolour] set];
1324 [path fill];
1325 }
1326
1327 assert(outlinecolour >= 0 && outlinecolour < fe->ncolours);
1328 [fe->colours[outlinecolour] set];
1329 [path stroke];
1330}
1331static void osx_draw_circle(void *handle, int cx, int cy, int radius,
1332 int fillcolour, int outlinecolour)
1333{
1334 frontend *fe = (frontend *)handle;
1335 NSBezierPath *path = [NSBezierPath bezierPath];
1336
1337 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
1338
1339 [path appendBezierPathWithArcWithCenter:NSMakePoint(cx+0.5, fe->h-cy-0.5)
1340 radius:radius startAngle:0.0 endAngle:360.0];
1341
1342 [path closePath];
1343
1344 if (fillcolour >= 0) {
1345 assert(fillcolour >= 0 && fillcolour < fe->ncolours);
1346 [fe->colours[fillcolour] set];
1347 [path fill];
1348 }
1349
1350 assert(outlinecolour >= 0 && outlinecolour < fe->ncolours);
1351 [fe->colours[outlinecolour] set];
1352 [path stroke];
1353}
1354static void osx_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
1355{
1356 frontend *fe = (frontend *)handle;
1357 NSBezierPath *path = [NSBezierPath bezierPath];
1358 NSPoint p1 = { x1 + 0.5, fe->h - y1 - 0.5 };
1359 NSPoint p2 = { x2 + 0.5, fe->h - y2 - 0.5 };
1360
1361 [[NSGraphicsContext currentContext] setShouldAntialias:NO];
1362
1363 assert(colour >= 0 && colour < fe->ncolours);
1364 [fe->colours[colour] set];
1365
1366 [path moveToPoint:p1];
1367 [path lineToPoint:p2];
1368 [path stroke];
1369 NSRectFill(NSMakeRect(x1, fe->h-y1-1, 1, 1));
1370 NSRectFill(NSMakeRect(x2, fe->h-y2-1, 1, 1));
1371}
1372
1373static void osx_draw_thick_line(
1374 void *handle, float thickness,
1375 float x1, float y1,
1376 float x2, float y2,
1377 int colour)
1378{
1379 frontend *fe = (frontend *)handle;
1380 NSBezierPath *path = [NSBezierPath bezierPath];
1381
1382 assert(colour >= 0 && colour < fe->ncolours);
1383 [fe->colours[colour] set];
1384 [[NSGraphicsContext currentContext] setShouldAntialias: YES];
1385 [path setLineWidth: thickness];
1386 [path setLineCapStyle: NSButtLineCapStyle];
1387 [path moveToPoint: NSMakePoint(x1, fe->h-y1)];
1388 [path lineToPoint: NSMakePoint(x2, fe->h-y2)];
1389 [path stroke];
1390}
1391
1392static void osx_draw_rect(void *handle, int x, int y, int w, int h, int colour)
1393{
1394 frontend *fe = (frontend *)handle;
1395 NSRect r = { {x, fe->h - y - h}, {w,h} };
1396
1397 [[NSGraphicsContext currentContext] setShouldAntialias:NO];
1398
1399 assert(colour >= 0 && colour < fe->ncolours);
1400 [fe->colours[colour] set];
1401
1402 NSRectFill(r);
1403}
1404static void osx_draw_text(void *handle, int x, int y, int fonttype,
1405 int fontsize, int align, int colour, char *text)
1406{
1407 frontend *fe = (frontend *)handle;
1408 NSString *string = [NSString stringWithUTF8String:text];
1409 NSDictionary *attr;
1410 NSFont *font;
1411 NSSize size;
1412 NSPoint point;
1413
1414 [[NSGraphicsContext currentContext] setShouldAntialias:YES];
1415
1416 assert(colour >= 0 && colour < fe->ncolours);
1417
1418 if (fonttype == FONT_FIXED)
1419 font = [NSFont userFixedPitchFontOfSize:fontsize];
1420 else
1421 font = [NSFont userFontOfSize:fontsize];
1422
1423 attr = [NSDictionary dictionaryWithObjectsAndKeys:
1424 fe->colours[colour], NSForegroundColorAttributeName,
1425 font, NSFontAttributeName, nil];
1426
1427 point.x = x;
1428 point.y = fe->h - y;
1429
1430 size = [string sizeWithAttributes:attr];
1431 if (align & ALIGN_HRIGHT)
1432 point.x -= size.width;
1433 else if (align & ALIGN_HCENTRE)
1434 point.x -= size.width / 2;
1435 if (align & ALIGN_VCENTRE)
1436 point.y -= size.height / 2;
1437
1438 [string drawAtPoint:point withAttributes:attr];
1439}
1440static char *osx_text_fallback(void *handle, const char *const *strings,
1441 int nstrings)
1442{
1443 /*
1444 * We assume OS X can cope with any UTF-8 likely to be emitted
1445 * by a puzzle.
1446 */
1447 return dupstr(strings[0]);
1448}
1449struct blitter {
1450 int w, h;
1451 int x, y;
1452 NSImage *img;
1453};
1454static blitter *osx_blitter_new(void *handle, int w, int h)
1455{
1456 blitter *bl = snew(blitter);
1457 bl->x = bl->y = -1;
1458 bl->w = w;
1459 bl->h = h;
1460 bl->img = [[NSImage alloc] initWithSize:NSMakeSize(w, h)];
1461 return bl;
1462}
1463static void osx_blitter_free(void *handle, blitter *bl)
1464{
1465 [bl->img release];
1466 sfree(bl);
1467}
1468static void osx_blitter_save(void *handle, blitter *bl, int x, int y)
1469{
1470 frontend *fe = (frontend *)handle;
1471 int sx, sy, sX, sY, dx, dy, dX, dY;
1472 [fe->image unlockFocus];
1473 [bl->img lockFocus];
1474
1475 /*
1476 * Find the intersection of the source and destination rectangles,
1477 * so as to avoid trying to copy from outside the source image,
1478 * which GNUstep dislikes.
1479 *
1480 * Lower-case x,y coordinates are bottom left box corners;
1481 * upper-case X,Y are the top right.
1482 */
1483 sx = x; sy = fe->h - y - bl->h;
1484 sX = sx + bl->w; sY = sy + bl->h;
1485 dx = dy = 0;
1486 dX = bl->w; dY = bl->h;
1487 if (sx < 0) {
1488 dx += -sx;
1489 sx = 0;
1490 }
1491 if (sy < 0) {
1492 dy += -sy;
1493 sy = 0;
1494 }
1495 if (sX > fe->w) {
1496 dX -= (sX - fe->w);
1497 sX = fe->w;
1498 }
1499 if (sY > fe->h) {
1500 dY -= (sY - fe->h);
1501 sY = fe->h;
1502 }
1503
1504 [fe->image drawInRect:NSMakeRect(dx, dy, dX-dx, dY-dy)
1505 fromRect:NSMakeRect(sx, sy, sX-sx, sY-sy)
1506 operation:NSCompositeCopy fraction:1.0];
1507 [bl->img unlockFocus];
1508 [fe->image lockFocus];
1509 bl->x = x;
1510 bl->y = y;
1511}
1512static void osx_blitter_load(void *handle, blitter *bl, int x, int y)
1513{
1514 frontend *fe = (frontend *)handle;
1515 if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
1516 x = bl->x;
1517 y = bl->y;
1518 }
1519 [bl->img drawInRect:NSMakeRect(x, fe->h - y - bl->h, bl->w, bl->h)
1520 fromRect:NSMakeRect(0, 0, bl->w, bl->h)
1521 operation:NSCompositeCopy fraction:1.0];
1522}
1523static void osx_draw_update(void *handle, int x, int y, int w, int h)
1524{
1525 frontend *fe = (frontend *)handle;
1526 [fe->view setNeedsDisplayInRect:NSMakeRect(x, fe->h - y - h, w, h)];
1527}
1528static void osx_clip(void *handle, int x, int y, int w, int h)
1529{
1530 frontend *fe = (frontend *)handle;
1531 NSRect r = { {x, fe->h - y - h}, {w, h} };
1532
1533 if (!fe->clipped)
1534 [[NSGraphicsContext currentContext] saveGraphicsState];
1535 [NSBezierPath clipRect:r];
1536 fe->clipped = TRUE;
1537}
1538static void osx_unclip(void *handle)
1539{
1540 frontend *fe = (frontend *)handle;
1541 if (fe->clipped)
1542 [[NSGraphicsContext currentContext] restoreGraphicsState];
1543 fe->clipped = FALSE;
1544}
1545static void osx_start_draw(void *handle)
1546{
1547 frontend *fe = (frontend *)handle;
1548 [fe->image lockFocus];
1549 fe->clipped = FALSE;
1550}
1551static void osx_end_draw(void *handle)
1552{
1553 frontend *fe = (frontend *)handle;
1554 [fe->image unlockFocus];
1555}
1556static void osx_status_bar(void *handle, char *text)
1557{
1558 frontend *fe = (frontend *)handle;
1559 [fe->window setStatusLine:text];
1560}
1561
1562const struct drawing_api osx_drawing = {
1563 osx_draw_text,
1564 osx_draw_rect,
1565 osx_draw_line,
1566 osx_draw_polygon,
1567 osx_draw_circle,
1568 osx_draw_update,
1569 osx_clip,
1570 osx_unclip,
1571 osx_start_draw,
1572 osx_end_draw,
1573 osx_status_bar,
1574 osx_blitter_new,
1575 osx_blitter_free,
1576 osx_blitter_save,
1577 osx_blitter_load,
1578 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
1579 NULL, NULL, /* line_width, line_dotted */
1580 osx_text_fallback,
1581 osx_draw_thick_line,
1582};
1583
1584void deactivate_timer(frontend *fe)
1585{
1586 [fe->window deactivateTimer];
1587}
1588void activate_timer(frontend *fe)
1589{
1590 [fe->window activateTimer];
1591}
1592
1593/* ----------------------------------------------------------------------
1594 * AppController: the object which receives the messages from all
1595 * menu selections that aren't standard OS X functions.
1596 */
1597@interface AppController : NSObject <NSApplicationDelegate>
1598{
1599}
1600- (void)newGameWindow:(id)sender;
1601- (void)about:(id)sender;
1602@end
1603
1604@implementation AppController
1605
1606- (void)newGameWindow:(id)sender
1607{
1608 const game *g = [sender getPayload];
1609 id win;
1610
1611 win = [[GameWindow alloc] initWithGame:g];
1612 [win makeKeyAndOrderFront:self];
1613}
1614
1615- (void)about:(id)sender
1616{
1617 id win;
1618
1619 win = [[AboutBox alloc] init];
1620 [win makeKeyAndOrderFront:self];
1621}
1622
1623- (NSMenu *)applicationDockMenu:(NSApplication *)sender
1624{
1625 NSMenu *menu = newmenu("Dock Menu");
1626 {
1627 int i;
1628
1629 for (i = 0; i < gamecount; i++) {
1630 id item =
1631 initnewitem([DataMenuItem allocWithZone:[NSMenu menuZone]],
1632 menu, gamelist[i]->name, "", self,
1633 @selector(newGameWindow:));
1634 [item setPayload:(void *)gamelist[i]];
1635 }
1636 }
1637 return menu;
1638}
1639
1640@end
1641
1642/* ----------------------------------------------------------------------
1643 * Main program. Constructs the menus and runs the application.
1644 */
1645int main(int argc, char **argv)
1646{
1647 NSAutoreleasePool *pool;
1648 NSMenu *menu;
1649 AppController *controller;
1650 NSImage *icon;
1651
1652 pool = [[NSAutoreleasePool alloc] init];
1653
1654 icon = [NSImage imageNamed:@"NSApplicationIcon"];
1655 app = [NSApplication sharedApplication];
1656 [app setApplicationIconImage:icon];
1657
1658 controller = [[[AppController alloc] init] autorelease];
1659 [app setDelegate:controller];
1660
1661 [app setMainMenu: newmenu("Main Menu")];
1662
1663 menu = newsubmenu([app mainMenu], "Apple Menu");
1664 newitem(menu, "About Puzzles", "", NULL, @selector(about:));
1665 [menu addItem:[NSMenuItem separatorItem]];
1666 [app setServicesMenu:newsubmenu(menu, "Services")];
1667 [menu addItem:[NSMenuItem separatorItem]];
1668 newitem(menu, "Hide Puzzles", "h", app, @selector(hide:));
1669 newitem(menu, "Hide Others", "o-h", app, @selector(hideOtherApplications:));
1670 newitem(menu, "Show All", "", app, @selector(unhideAllApplications:));
1671 [menu addItem:[NSMenuItem separatorItem]];
1672 newitem(menu, "Quit", "q", app, @selector(terminate:));
1673 [app setAppleMenu: menu];
1674
1675 menu = newsubmenu([app mainMenu], "File");
1676 newitem(menu, "Open", "o", NULL, @selector(loadSavedGame:));
1677 newitem(menu, "Save As", "s", NULL, @selector(saveGame:));
1678 newitem(menu, "New Game", "n", NULL, @selector(newGame:));
1679 newitem(menu, "Restart Game", "r", NULL, @selector(restartGame:));
1680 newitem(menu, "Specific Game", "", NULL, @selector(specificGame:));
1681 newitem(menu, "Specific Random Seed", "", NULL,
1682 @selector(specificRandomGame:));
1683 [menu addItem:[NSMenuItem separatorItem]];
1684 {
1685 NSMenu *submenu = newsubmenu(menu, "New Window");
1686 int i;
1687
1688 for (i = 0; i < gamecount; i++) {
1689 id item =
1690 initnewitem([DataMenuItem allocWithZone:[NSMenu menuZone]],
1691 submenu, gamelist[i]->name, "", controller,
1692 @selector(newGameWindow:));
1693 [item setPayload:(void *)gamelist[i]];
1694 }
1695 }
1696 [menu addItem:[NSMenuItem separatorItem]];
1697 newitem(menu, "Close", "w", NULL, @selector(performClose:));
1698
1699 menu = newsubmenu([app mainMenu], "Edit");
1700 newitem(menu, "Undo", "z", NULL, @selector(undoMove:));
1701 newitem(menu, "Redo", "S-z", NULL, @selector(redoMove:));
1702 [menu addItem:[NSMenuItem separatorItem]];
1703 newitem(menu, "Cut", "x", NULL, @selector(cut:));
1704 newitem(menu, "Copy", "c", NULL, @selector(copy:));
1705 newitem(menu, "Paste", "v", NULL, @selector(paste:));
1706 [menu addItem:[NSMenuItem separatorItem]];
1707 newitem(menu, "Solve", "S-s", NULL, @selector(solveGame:));
1708
1709 menu = newsubmenu([app mainMenu], "Type");
1710 typemenu = menu;
1711 newitem(menu, "Custom", "", NULL, @selector(customGameType:));
1712
1713 menu = newsubmenu([app mainMenu], "Window");
1714 [app setWindowsMenu: menu];
1715 newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:));
1716
1717 menu = newsubmenu([app mainMenu], "Help");
1718 newitem(menu, "Puzzles Help", "?", app, @selector(showHelp:));
1719
1720 [app run];
1721 [pool release];
1722
1723 return 0;
1724}
diff --git a/apps/plugins/puzzles/padtoolbar.bmp b/apps/plugins/puzzles/padtoolbar.bmp
new file mode 100644
index 0000000000..46c3e0dcae
--- /dev/null
+++ b/apps/plugins/puzzles/padtoolbar.bmp
Binary files differ
diff --git a/apps/plugins/puzzles/palisade.R b/apps/plugins/puzzles/palisade.R
new file mode 100644
index 0000000000..de4bd83126
--- /dev/null
+++ b/apps/plugins/puzzles/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/palisade.c b/apps/plugins/puzzles/palisade.c
new file mode 100644
index 0000000000..c5f1b62022
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/pattern.R b/apps/plugins/puzzles/pattern.R
new file mode 100644
index 0000000000..d6695715f9
--- /dev/null
+++ b/apps/plugins/puzzles/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/pattern.c b/apps/plugins/puzzles/pattern.c
new file mode 100644
index 0000000000..c741a2e3a4
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/pearl.R b/apps/plugins/puzzles/pearl.R
new file mode 100644
index 0000000000..79bc7325c7
--- /dev/null
+++ b/apps/plugins/puzzles/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/pearl.c b/apps/plugins/puzzles/pearl.c
new file mode 100644
index 0000000000..59effeda40
--- /dev/null
+++ b/apps/plugins/puzzles/pearl.c
@@ -0,0 +1,2772 @@
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 "rbassert.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 comp = -1; /* part of the 'all paths' quasi-component */
1615 if ((component_state[comp] == COMP_PATH &&
1616 -1 != largest_comp) ||
1617 (component_state[comp] == COMP_LOOP &&
1618 comp != largest_comp))
1619 ERROR(i%w, i/w, state->lines[i]);
1620 }
1621 }
1622
1623 /* Now we've finished with the dsf and component states. The only
1624 * thing we'll need to remember later on is whether all edges were
1625 * part of a single loop, for which our counter variables
1626 * nsilly,nloop,npath are enough. */
1627 sfree(component_state);
1628 sfree(dsf);
1629
1630 /*
1631 * Check that no clues are contradicted. This code is similar to
1632 * the code that sets up the maximal clue array for any given
1633 * loop.
1634 */
1635 for (x = 0; x < w; x++) {
1636 for (y = 0; y < h; y++) {
1637 int type = state->lines[y*w+x];
1638 if (state->shared->clues[y*w+x] == CORNER) {
1639 /* Supposed to be a corner: will find a contradiction if
1640 * it actually contains a straight line, or if it touches any
1641 * corners. */
1642 if ((bLR|bUD) & (1 << type)) {
1643 ERROR(x,y,ERROR_CLUE); /* actually straight */
1644 }
1645 for (d = 1; d <= 8; d += d) if (type & d) {
1646 int xx = x + DX(d), yy = y + DY(d);
1647 if (!INGRID(state, xx, yy)) {
1648 ERROR(x,y,d); /* leads off grid */
1649 } else {
1650 if ((bLU|bLD|bRU|bRD) & (1 << state->lines[yy*w+xx])) {
1651 ERROR(x,y,ERROR_CLUE); /* touches corner */
1652 }
1653 }
1654 }
1655 } else if (state->shared->clues[y*w+x] == STRAIGHT) {
1656 /* Supposed to be straight: will find a contradiction if
1657 * it actually contains a corner, or if it only touches
1658 * straight lines. */
1659 if ((bLU|bLD|bRU|bRD) & (1 << type)) {
1660 ERROR(x,y,ERROR_CLUE); /* actually a corner */
1661 }
1662 i = 0;
1663 for (d = 1; d <= 8; d += d) if (type & d) {
1664 int xx = x + DX(d), yy = y + DY(d);
1665 if (!INGRID(state, xx, yy)) {
1666 ERROR(x,y,d); /* leads off grid */
1667 } else {
1668 if ((bLR|bUD) & (1 << state->lines[yy*w+xx]))
1669 i++; /* a straight */
1670 }
1671 }
1672 if (i >= 2 && NBITS(type) >= 2) {
1673 ERROR(x,y,ERROR_CLUE); /* everything touched is straight */
1674 }
1675 }
1676 }
1677 }
1678
1679 if (nloop == 1 && nsilly == 0 && npath == 0) {
1680 /*
1681 * If there's exactly one loop (so that the puzzle is at least
1682 * potentially complete), we need to ensure it hasn't left any
1683 * clue out completely.
1684 */
1685 for (x = 0; x < w; x++) {
1686 for (y = 0; y < h; y++) {
1687 if (state->lines[y*w+x] == BLANK) {
1688 if (state->shared->clues[y*w+x] != NOCLUE) {
1689 /* the loop doesn't include this clue square! */
1690 ERROR(x, y, ERROR_CLUE);
1691 }
1692 }
1693 }
1694 }
1695
1696 /*
1697 * But if not, then we're done!
1698 */
1699 if (!had_error)
1700 state->completed = TRUE;
1701 }
1702}
1703
1704/* completion check:
1705 *
1706 * - no clues must be contradicted (highlight clue itself in error if so)
1707 * - if there is a closed loop it must include every line segment laid
1708 * - if there's a smaller closed loop then highlight whole loop as error
1709 * - no square must have more than 2 lines radiating from centre point
1710 * (highlight all lines in that square as error if so)
1711 */
1712
1713static char *solve_for_diff(game_state *state, char *old_lines, char *new_lines)
1714{
1715 int w = state->shared->w, h = state->shared->h, i;
1716 char *move = snewn(w*h*40, char), *p = move;
1717
1718 *p++ = 'S';
1719 for (i = 0; i < w*h; i++) {
1720 if (old_lines[i] != new_lines[i]) {
1721 p += sprintf(p, ";R%d,%d,%d", new_lines[i], i%w, i/w);
1722 }
1723 }
1724 *p++ = '\0';
1725 move = sresize(move, p - move, char);
1726
1727 return move;
1728}
1729
1730static char *solve_game(const game_state *state, const game_state *currstate,
1731 const char *aux, char **error)
1732{
1733 game_state *solved = dup_game(state);
1734 int i, ret, sz = state->shared->sz;
1735 char *move;
1736
1737 if (aux) {
1738 for (i = 0; i < sz; i++) {
1739 if (aux[i] >= '0' && aux[i] <= '9')
1740 solved->lines[i] = aux[i] - '0';
1741 else if (aux[i] >= 'A' && aux[i] <= 'F')
1742 solved->lines[i] = aux[i] - 'A' + 10;
1743 else {
1744 *error = "invalid char in aux";
1745 move = NULL;
1746 goto done;
1747 }
1748 }
1749 ret = 1;
1750 } else {
1751 /* Try to solve with present (half-solved) state first: if there's no
1752 * solution from there go back to original state. */
1753 ret = pearl_solve(currstate->shared->w, currstate->shared->h,
1754 currstate->shared->clues, solved->lines,
1755 DIFFCOUNT, FALSE);
1756 if (ret < 1)
1757 ret = pearl_solve(state->shared->w, state->shared->h,
1758 state->shared->clues, solved->lines,
1759 DIFFCOUNT, FALSE);
1760
1761 }
1762
1763 if (ret < 1) {
1764 *error = "Unable to find solution";
1765 move = NULL;
1766 } else {
1767 move = solve_for_diff(solved, currstate->lines, solved->lines);
1768 }
1769
1770done:
1771 free_game(solved);
1772 return move;
1773}
1774
1775static int game_can_format_as_text_now(const game_params *params)
1776{
1777 return TRUE;
1778}
1779
1780static char *game_text_format(const game_state *state)
1781{
1782 int w = state->shared->w, h = state->shared->h, cw = 4, ch = 2;
1783 int gw = cw*(w-1) + 2, gh = ch*(h-1) + 1, len = gw * gh, r, c, j;
1784 char *board = snewn(len + 1, char);
1785
1786 assert(board);
1787 memset(board, ' ', len);
1788
1789 for (r = 0; r < h; ++r) {
1790 for (c = 0; c < w; ++c) {
1791 int i = r*w + c, cell = r*ch*gw + c*cw;
1792 board[cell] = "+BW"[(unsigned char)state->shared->clues[i]];
1793 if (c < w - 1 && (state->lines[i] & R || state->lines[i+1] & L))
1794 memset(board + cell + 1, '-', cw - 1);
1795 if (r < h - 1 && (state->lines[i] & D || state->lines[i+w] & U))
1796 for (j = 1; j < ch; ++j) board[cell + j*gw] = '|';
1797 if (c < w - 1 && (state->marks[i] & R || state->marks[i+1] & L))
1798 board[cell + cw/2] = 'x';
1799 if (r < h - 1 && (state->marks[i] & D || state->marks[i+w] & U))
1800 board[cell + (ch/2 * gw)] = 'x';
1801 }
1802
1803 for (j = 0; j < (r == h - 1 ? 1 : ch); ++j)
1804 board[r*ch*gw + (gw - 1) + j*gw] = '\n';
1805 }
1806
1807 board[len] = '\0';
1808 return board;
1809}
1810
1811struct game_ui {
1812 int *dragcoords; /* list of (y*w+x) coords in drag so far */
1813 int ndragcoords; /* number of entries in dragcoords.
1814 * 0 = click but no drag yet. -1 = no drag at all */
1815 int clickx, clicky; /* pixel position of initial click */
1816
1817 int curx, cury; /* grid position of keyboard cursor */
1818 int cursor_active; /* TRUE iff cursor is shown */
1819};
1820
1821static game_ui *new_ui(const game_state *state)
1822{
1823 game_ui *ui = snew(game_ui);
1824 int sz = state->shared->sz;
1825
1826 ui->ndragcoords = -1;
1827 ui->dragcoords = snewn(sz, int);
1828 ui->cursor_active = FALSE;
1829 ui->curx = ui->cury = 0;
1830
1831 return ui;
1832}
1833
1834static void free_ui(game_ui *ui)
1835{
1836 sfree(ui->dragcoords);
1837 sfree(ui);
1838}
1839
1840static char *encode_ui(const game_ui *ui)
1841{
1842 return NULL;
1843}
1844
1845static void decode_ui(game_ui *ui, const char *encoding)
1846{
1847}
1848
1849static void game_changed_state(game_ui *ui, const game_state *oldstate,
1850 const game_state *newstate)
1851{
1852}
1853
1854#define PREFERRED_TILE_SIZE 31
1855#define HALFSZ (ds->halfsz)
1856#define TILE_SIZE (ds->halfsz*2 + 1)
1857
1858#define BORDER ((get_gui_style() == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2))
1859
1860#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
1861
1862#define COORD(x) ( (x) * TILE_SIZE + BORDER )
1863#define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 )
1864#define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) )
1865
1866#define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */
1867#define DS_DSHIFT 8 /* R/U/L/D shift, for drag-in-progress flags */
1868#define DS_MSHIFT 12 /* shift for no-line mark */
1869
1870#define DS_ERROR_CLUE (1 << 20)
1871#define DS_FLASH (1 << 21)
1872#define DS_CURSOR (1 << 22)
1873
1874enum { GUI_MASYU, GUI_LOOPY };
1875
1876static int get_gui_style(void)
1877{
1878 static int gui_style = -1;
1879
1880 if (gui_style == -1) {
1881 char *env = getenv("PEARL_GUI_LOOPY");
1882 if (env && (env[0] == 'y' || env[0] == 'Y'))
1883 gui_style = GUI_LOOPY;
1884 else
1885 gui_style = GUI_MASYU;
1886 }
1887 return gui_style;
1888}
1889
1890struct game_drawstate {
1891 int halfsz;
1892 int started;
1893
1894 int w, h, sz;
1895 unsigned int *lflags; /* size w*h */
1896
1897 char *draglines; /* size w*h; lines flipped by current drag */
1898};
1899
1900static void update_ui_drag(const game_state *state, game_ui *ui,
1901 int gx, int gy)
1902{
1903 int /* sz = state->shared->sz, */ w = state->shared->w;
1904 int i, ox, oy, pos;
1905 int lastpos;
1906
1907 if (!INGRID(state, gx, gy))
1908 return; /* square is outside grid */
1909
1910 if (ui->ndragcoords < 0)
1911 return; /* drag not in progress anyway */
1912
1913 pos = gy * w + gx;
1914
1915 lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0];
1916 if (pos == lastpos)
1917 return; /* same square as last visited one */
1918
1919 /* Drag confirmed, if it wasn't already. */
1920 if (ui->ndragcoords == 0)
1921 ui->ndragcoords = 1;
1922
1923 /*
1924 * Dragging the mouse into a square that's already been visited by
1925 * the drag path so far has the effect of truncating the path back
1926 * to that square, so a player can back out part of an uncommitted
1927 * drag without having to let go of the mouse.
1928 */
1929 for (i = 0; i < ui->ndragcoords; i++)
1930 if (pos == ui->dragcoords[i]) {
1931 ui->ndragcoords = i+1;
1932 return;
1933 }
1934
1935 /*
1936 * Otherwise, dragging the mouse into a square that's a rook-move
1937 * away from the last one on the path extends the path.
1938 */
1939 oy = ui->dragcoords[ui->ndragcoords-1] / w;
1940 ox = ui->dragcoords[ui->ndragcoords-1] % w;
1941 if (ox == gx || oy == gy) {
1942 int dx = (gx < ox ? -1 : gx > ox ? +1 : 0);
1943 int dy = (gy < oy ? -1 : gy > oy ? +1 : 0);
1944 int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L);
1945 while (ox != gx || oy != gy) {
1946 /*
1947 * If the drag attempts to cross a 'no line here' mark,
1948 * stop there. We physically don't allow the user to drag
1949 * over those marks.
1950 */
1951 if (state->marks[oy*w+ox] & dir)
1952 break;
1953 ox += dx;
1954 oy += dy;
1955 ui->dragcoords[ui->ndragcoords++] = oy * w + ox;
1956 }
1957 }
1958
1959 /*
1960 * Failing that, we do nothing at all: if the user has dragged
1961 * diagonally across the board, they'll just have to return the
1962 * mouse to the last known position and do whatever they meant to
1963 * do again, more slowly and clearly.
1964 */
1965}
1966
1967/*
1968 * Routine shared between interpret_move and game_redraw to work out
1969 * the intended effect of a drag path on the grid.
1970 *
1971 * Call it in a loop, like this:
1972 *
1973 * int clearing = TRUE;
1974 * for (i = 0; i < ui->ndragcoords - 1; i++) {
1975 * int sx, sy, dx, dy, dir, oldstate, newstate;
1976 * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
1977 * &dir, &oldstate, &newstate);
1978 *
1979 * [do whatever is needed to handle the fact that the drag
1980 * wants the edge from sx,sy to dx,dy (heading in direction
1981 * 'dir' at the sx,sy end) to be changed from state oldstate
1982 * to state newstate, each of which equals either 0 or dir]
1983 * }
1984 */
1985static void interpret_ui_drag(const game_state *state, const game_ui *ui,
1986 int *clearing, int i, int *sx, int *sy,
1987 int *dx, int *dy, int *dir,
1988 int *oldstate, int *newstate)
1989{
1990 int w = state->shared->w;
1991 int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1];
1992 *sy = sp/w;
1993 *sx = sp%w;
1994 *dy = dp/w;
1995 *dx = dp%w;
1996 *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L);
1997 *oldstate = state->lines[sp] & *dir;
1998 if (*oldstate) {
1999 /*
2000 * The edge we've dragged over was previously
2001 * present. Set it to absent, unless we've already
2002 * stopped doing that.
2003 */
2004 *newstate = *clearing ? 0 : *dir;
2005 } else {
2006 /*
2007 * The edge we've dragged over was previously
2008 * absent. Set it to present, and cancel the
2009 * 'clearing' flag so that all subsequent edges in
2010 * the drag are set rather than cleared.
2011 */
2012 *newstate = *dir;
2013 *clearing = FALSE;
2014 }
2015}
2016
2017static char *mark_in_direction(const game_state *state, int x, int y, int dir,
2018 int primary, char *buf)
2019{
2020 int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */;
2021 int x2 = x + DX(dir);
2022 int y2 = y + DY(dir);
2023 int dir2 = F(dir);
2024
2025 char ch = primary ? 'F' : 'M', *other;
2026
2027 if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return "";
2028
2029 /* disallow laying a mark over a line, or vice versa. */
2030 other = primary ? state->marks : state->lines;
2031 if (other[y*w+x] & dir || other[y2*w+x2] & dir2) return "";
2032
2033 sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2);
2034 return dupstr(buf);
2035}
2036
2037#define KEY_DIRECTION(btn) (\
2038 (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\
2039 (btn) == CURSOR_LEFT ? L : R)
2040
2041static char *interpret_move(const game_state *state, game_ui *ui,
2042 const game_drawstate *ds,
2043 int x, int y, int button)
2044{
2045 int w = state->shared->w, h = state->shared->h /*, sz = state->shared->sz */;
2046 int gx = FROMCOORD(x), gy = FROMCOORD(y), i;
2047 int release = FALSE;
2048 char tmpbuf[80];
2049
2050 int shift = button & MOD_SHFT, control = button & MOD_CTRL;
2051 button &= ~MOD_MASK;
2052
2053 if (IS_MOUSE_DOWN(button)) {
2054 ui->cursor_active = FALSE;
2055
2056 if (!INGRID(state, gx, gy)) {
2057 ui->ndragcoords = -1;
2058 return NULL;
2059 }
2060
2061 ui->clickx = x; ui->clicky = y;
2062 ui->dragcoords[0] = gy * w + gx;
2063 ui->ndragcoords = 0; /* will be 1 once drag is confirmed */
2064
2065 return "";
2066 }
2067
2068 if (button == LEFT_DRAG && ui->ndragcoords >= 0) {
2069 update_ui_drag(state, ui, gx, gy);
2070 return "";
2071 }
2072
2073 if (IS_MOUSE_RELEASE(button)) release = TRUE;
2074
2075 if (IS_CURSOR_MOVE(button)) {
2076 if (!ui->cursor_active) {
2077 ui->cursor_active = TRUE;
2078 } else if (control | shift) {
2079 char *move;
2080 if (ui->ndragcoords > 0) return NULL;
2081 ui->ndragcoords = -1;
2082 move = mark_in_direction(state, ui->curx, ui->cury,
2083 KEY_DIRECTION(button), control, tmpbuf);
2084 if (control && !shift && *move)
2085 move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE);
2086 return move;
2087 } else {
2088 move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE);
2089 if (ui->ndragcoords >= 0)
2090 update_ui_drag(state, ui, ui->curx, ui->cury);
2091 }
2092 return "";
2093 }
2094
2095 if (IS_CURSOR_SELECT(button)) {
2096 if (!ui->cursor_active) {
2097 ui->cursor_active = TRUE;
2098 return "";
2099 } else if (button == CURSOR_SELECT) {
2100 if (ui->ndragcoords == -1) {
2101 ui->ndragcoords = 0;
2102 ui->dragcoords[0] = ui->cury * w + ui->curx;
2103 ui->clickx = CENTERED_COORD(ui->curx);
2104 ui->clicky = CENTERED_COORD(ui->cury);
2105 return "";
2106 } else release = TRUE;
2107 } else if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) {
2108 ui->ndragcoords = -1;
2109 return "";
2110 }
2111 }
2112
2113 if (button == 27 || button == '\b') {
2114 ui->ndragcoords = -1;
2115 return "";
2116 }
2117
2118 if (release) {
2119 if (ui->ndragcoords > 0) {
2120 /* End of a drag: process the cached line data. */
2121 int buflen = 0, bufsize = 256, tmplen;
2122 char *buf = NULL;
2123 const char *sep = "";
2124 int clearing = TRUE;
2125
2126 for (i = 0; i < ui->ndragcoords - 1; i++) {
2127 int sx, sy, dx, dy, dir, oldstate, newstate;
2128 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2129 &dir, &oldstate, &newstate);
2130
2131 if (oldstate != newstate) {
2132 if (!buf) buf = snewn(bufsize, char);
2133 tmplen = sprintf(tmpbuf, "%sF%d,%d,%d;F%d,%d,%d", sep,
2134 dir, sx, sy, F(dir), dx, dy);
2135 if (buflen + tmplen >= bufsize) {
2136 bufsize = (buflen + tmplen) * 5 / 4 + 256;
2137 buf = sresize(buf, bufsize, char);
2138 }
2139 strcpy(buf + buflen, tmpbuf);
2140 buflen += tmplen;
2141 sep = ";";
2142 }
2143 }
2144
2145 ui->ndragcoords = -1;
2146
2147 return buf ? buf : "";
2148 } else if (ui->ndragcoords == 0) {
2149 /* Click (or tiny drag). Work out which edge we were
2150 * closest to. */
2151 int cx, cy;
2152
2153 ui->ndragcoords = -1;
2154
2155 /*
2156 * We process clicks based on the mouse-down location,
2157 * because that's more natural for a user to carefully
2158 * control than the mouse-up.
2159 */
2160 x = ui->clickx;
2161 y = ui->clicky;
2162
2163 gx = FROMCOORD(x);
2164 gy = FROMCOORD(y);
2165 cx = CENTERED_COORD(gx);
2166 cy = CENTERED_COORD(gy);
2167
2168 if (!INGRID(state, gx, gy)) return "";
2169
2170 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
2171 /* TODO closer to centre of grid: process as a cell click not an edge click. */
2172
2173 return "";
2174 } else {
2175 int direction;
2176 if (abs(x-cx) < abs(y-cy)) {
2177 /* Closest to top/bottom edge. */
2178 direction = (y < cy) ? U : D;
2179 } else {
2180 /* Closest to left/right edge. */
2181 direction = (x < cx) ? L : R;
2182 }
2183 return mark_in_direction(state, gx, gy, direction,
2184 (button == LEFT_RELEASE), tmpbuf);
2185 }
2186 }
2187 }
2188
2189 if (button == 'H' || button == 'h')
2190 return dupstr("H");
2191
2192 return NULL;
2193}
2194
2195static game_state *execute_move(const game_state *state, const char *move)
2196{
2197 int w = state->shared->w, h = state->shared->h;
2198 char c;
2199 int x, y, l, n;
2200 game_state *ret = dup_game(state);
2201
2202 debug(("move: %s\n", move));
2203
2204 while (*move) {
2205 c = *move;
2206 if (c == 'S') {
2207 ret->used_solve = TRUE;
2208 move++;
2209 } else if (c == 'L' || c == 'N' || c == 'R' || c == 'F' || c == 'M') {
2210 /* 'line' or 'noline' or 'replace' or 'flip' or 'mark' */
2211 move++;
2212 if (sscanf(move, "%d,%d,%d%n", &l, &x, &y, &n) != 3)
2213 goto badmove;
2214 if (!INGRID(state, x, y)) goto badmove;
2215 if (l < 0 || l > 15) goto badmove;
2216
2217 if (c == 'L')
2218 ret->lines[y*w + x] |= (char)l;
2219 else if (c == 'N')
2220 ret->lines[y*w + x] &= ~((char)l);
2221 else if (c == 'R') {
2222 ret->lines[y*w + x] = (char)l;
2223 ret->marks[y*w + x] &= ~((char)l); /* erase marks too */
2224 } else if (c == 'F')
2225 ret->lines[y*w + x] ^= (char)l;
2226 else if (c == 'M')
2227 ret->marks[y*w + x] ^= (char)l;
2228
2229 /*
2230 * If we ended up trying to lay a line _over_ a mark,
2231 * that's a failed move: interpret_move() should have
2232 * ensured we never received a move string like that in
2233 * the first place.
2234 */
2235 if ((ret->lines[y*w + x] & (char)l) &&
2236 (ret->marks[y*w + x] & (char)l))
2237 goto badmove;
2238
2239 move += n;
2240 } else if (strcmp(move, "H") == 0) {
2241 pearl_solve(ret->shared->w, ret->shared->h,
2242 ret->shared->clues, ret->lines, DIFFCOUNT, TRUE);
2243 for (n = 0; n < w*h; n++)
2244 ret->marks[n] &= ~ret->lines[n]; /* erase marks too */
2245 move++;
2246 } else {
2247 goto badmove;
2248 }
2249 if (*move == ';')
2250 move++;
2251 else if (*move)
2252 goto badmove;
2253 }
2254
2255 check_completion(ret, TRUE);
2256
2257 return ret;
2258
2259badmove:
2260 free_game(ret);
2261 return NULL;
2262}
2263
2264/* ----------------------------------------------------------------------
2265 * Drawing routines.
2266 */
2267
2268#define FLASH_TIME 0.5F
2269
2270static void game_compute_size(const game_params *params, int tilesize,
2271 int *x, int *y)
2272{
2273 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2274 struct { int halfsz; } ads, *ds = &ads;
2275 ads.halfsz = (tilesize-1)/2;
2276
2277 *x = (params->w) * TILE_SIZE + 2 * BORDER;
2278 *y = (params->h) * TILE_SIZE + 2 * BORDER;
2279}
2280
2281static void game_set_size(drawing *dr, game_drawstate *ds,
2282 const game_params *params, int tilesize)
2283{
2284 ds->halfsz = (tilesize-1)/2;
2285}
2286
2287static float *game_colours(frontend *fe, int *ncolours)
2288{
2289 float *ret = snewn(3 * NCOLOURS, float);
2290 int i;
2291
2292 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
2293
2294 for (i = 0; i < 3; i++) {
2295 ret[COL_BLACK * 3 + i] = 0.0F;
2296 ret[COL_WHITE * 3 + i] = 1.0F;
2297 ret[COL_GRID * 3 + i] = 0.4F;
2298 }
2299
2300 ret[COL_ERROR * 3 + 0] = 1.0F;
2301 ret[COL_ERROR * 3 + 1] = 0.0F;
2302 ret[COL_ERROR * 3 + 2] = 0.0F;
2303
2304 ret[COL_DRAGON * 3 + 0] = 0.0F;
2305 ret[COL_DRAGON * 3 + 1] = 0.0F;
2306 ret[COL_DRAGON * 3 + 2] = 1.0F;
2307
2308 ret[COL_DRAGOFF * 3 + 0] = 0.8F;
2309 ret[COL_DRAGOFF * 3 + 1] = 0.8F;
2310 ret[COL_DRAGOFF * 3 + 2] = 1.0F;
2311
2312 ret[COL_FLASH * 3 + 0] = 1.0F;
2313 ret[COL_FLASH * 3 + 1] = 1.0F;
2314 ret[COL_FLASH * 3 + 2] = 1.0F;
2315
2316 *ncolours = NCOLOURS;
2317
2318 return ret;
2319}
2320
2321static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2322{
2323 struct game_drawstate *ds = snew(struct game_drawstate);
2324 int i;
2325
2326 ds->halfsz = 0;
2327 ds->started = FALSE;
2328
2329 ds->w = state->shared->w;
2330 ds->h = state->shared->h;
2331 ds->sz = state->shared->sz;
2332 ds->lflags = snewn(ds->sz, unsigned int);
2333 for (i = 0; i < ds->sz; i++)
2334 ds->lflags[i] = 0;
2335
2336 ds->draglines = snewn(ds->sz, char);
2337
2338 return ds;
2339}
2340
2341static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2342{
2343 sfree(ds->draglines);
2344 sfree(ds->lflags);
2345 sfree(ds);
2346}
2347
2348static void draw_lines_specific(drawing *dr, game_drawstate *ds,
2349 int x, int y, unsigned int lflags,
2350 unsigned int shift, int c)
2351{
2352 int ox = COORD(x), oy = COORD(y);
2353 int t2 = HALFSZ, t16 = HALFSZ/4;
2354 int cx = ox + t2, cy = oy + t2;
2355 int d;
2356
2357 /* Draw each of the four directions, where laid (or error, or drag, etc.) */
2358 for (d = 1; d < 16; d *= 2) {
2359 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2360 int xnudge = abs(t16 * DX(C(d))), ynudge = abs(t16 * DY(C(d)));
2361
2362 if ((lflags >> shift) & d) {
2363 int lx = cx + ((xoff < 0) ? xoff : 0) - xnudge;
2364 int ly = cy + ((yoff < 0) ? yoff : 0) - ynudge;
2365
2366 if (c == COL_DRAGOFF && !(lflags & d))
2367 continue;
2368 if (c == COL_DRAGON && (lflags & d))
2369 continue;
2370
2371 draw_rect(dr, lx, ly,
2372 abs(xoff)+2*xnudge+1,
2373 abs(yoff)+2*ynudge+1, c);
2374 /* end cap */
2375 draw_rect(dr, cx - t16, cy - t16, 2*t16+1, 2*t16+1, c);
2376 }
2377 }
2378}
2379
2380static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
2381 int x, int y, unsigned int lflags, char clue)
2382{
2383 int ox = COORD(x), oy = COORD(y);
2384 int t2 = HALFSZ, t16 = HALFSZ/4;
2385 int cx = ox + t2, cy = oy + t2;
2386 int d;
2387
2388 assert(dr);
2389
2390 /* Clip to the grid square. */
2391 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2392
2393 /* Clear the square. */
2394 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE,
2395 (lflags & DS_CURSOR) ?
2396 COL_CURSOR_BACKGROUND : COL_BACKGROUND);
2397
2398
2399 if (get_gui_style() == GUI_LOOPY) {
2400 /* Draw small dot, underneath any lines. */
2401 draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID);
2402 } else {
2403 /* Draw outline of grid square */
2404 draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID);
2405 draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID);
2406 }
2407
2408 /* Draw grid: either thin gridlines, or no-line marks.
2409 * We draw these first because the thick laid lines should be on top. */
2410 for (d = 1; d < 16; d *= 2) {
2411 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2412
2413 if ((x == 0 && d == L) ||
2414 (y == 0 && d == U) ||
2415 (x == ds->w-1 && d == R) ||
2416 (y == ds->h-1 && d == D))
2417 continue; /* no gridlines out to the border. */
2418
2419 if ((lflags >> DS_MSHIFT) & d) {
2420 /* either a no-line mark ... */
2421 int mx = cx + xoff, my = cy + yoff, msz = t16;
2422
2423 draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK);
2424 draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK);
2425 } else {
2426 if (get_gui_style() == GUI_LOOPY) {
2427 /* draw grid lines connecting centre of cells */
2428 draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID);
2429 }
2430 }
2431 }
2432
2433 /* Draw each of the four directions, where laid (or error, or drag, etc.)
2434 * Order is important here, specifically for the eventual colours of the
2435 * exposed end caps. */
2436 draw_lines_specific(dr, ds, x, y, lflags, 0,
2437 (lflags & DS_FLASH ? COL_FLASH : COL_BLACK));
2438 draw_lines_specific(dr, ds, x, y, lflags, DS_ESHIFT, COL_ERROR);
2439 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGOFF);
2440 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGON);
2441
2442 /* Draw a clue, if present */
2443 if (clue != NOCLUE) {
2444 int c = (lflags & DS_FLASH) ? COL_FLASH :
2445 (clue == STRAIGHT) ? COL_WHITE : COL_BLACK;
2446
2447 if (lflags & DS_ERROR_CLUE) /* draw a bigger 'error' clue circle. */
2448 draw_circle(dr, cx, cy, TILE_SIZE*3/8, COL_ERROR, COL_ERROR);
2449
2450 draw_circle(dr, cx, cy, TILE_SIZE/4, c, COL_BLACK);
2451 }
2452
2453 unclip(dr);
2454 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2455}
2456
2457static void game_redraw(drawing *dr, game_drawstate *ds,
2458 const game_state *oldstate, const game_state *state,
2459 int dir, const game_ui *ui,
2460 float animtime, float flashtime)
2461{
2462 int w = state->shared->w, h = state->shared->h, sz = state->shared->sz;
2463 int x, y, force = 0, flashing = 0;
2464
2465 if (!ds->started) {
2466 /*
2467 * The initial contents of the window are not guaranteed and
2468 * can vary with front ends. To be on the safe side, all games
2469 * should start by drawing a big background-colour rectangle
2470 * covering the whole window.
2471 */
2472 draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER,
2473 COL_BACKGROUND);
2474
2475 if (get_gui_style() == GUI_MASYU) {
2476 /*
2477 * Smaller black rectangle which is the main grid.
2478 */
2479 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2480 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
2481 h*TILE_SIZE + 2*BORDER_WIDTH + 1,
2482 COL_GRID);
2483 }
2484
2485 draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER);
2486
2487 ds->started = TRUE;
2488 force = 1;
2489 }
2490
2491 if (flashtime > 0 &&
2492 (flashtime <= FLASH_TIME/3 ||
2493 flashtime >= FLASH_TIME*2/3))
2494 flashing = DS_FLASH;
2495
2496 memset(ds->draglines, 0, sz);
2497 if (ui->ndragcoords > 0) {
2498 int i, clearing = TRUE;
2499 for (i = 0; i < ui->ndragcoords - 1; i++) {
2500 int sx, sy, dx, dy, dir, oldstate, newstate;
2501 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2502 &dir, &oldstate, &newstate);
2503 ds->draglines[sy*w+sx] ^= (oldstate ^ newstate);
2504 ds->draglines[dy*w+dx] ^= (F(oldstate) ^ F(newstate));
2505 }
2506 }
2507
2508 for (x = 0; x < w; x++) {
2509 for (y = 0; y < h; y++) {
2510 unsigned int f = (unsigned int)state->lines[y*w+x];
2511 unsigned int eline = (unsigned int)(state->errors[y*w+x] & (R|U|L|D));
2512
2513 f |= eline << DS_ESHIFT;
2514 f |= ((unsigned int)ds->draglines[y*w+x]) << DS_DSHIFT;
2515 f |= ((unsigned int)state->marks[y*w+x]) << DS_MSHIFT;
2516
2517 if (state->errors[y*w+x] & ERROR_CLUE)
2518 f |= DS_ERROR_CLUE;
2519
2520 f |= flashing;
2521
2522 if (ui->cursor_active && x == ui->curx && y == ui->cury)
2523 f |= DS_CURSOR;
2524
2525 if (f != ds->lflags[y*w+x] || force) {
2526 ds->lflags[y*w+x] = f;
2527 draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]);
2528 }
2529 }
2530 }
2531}
2532
2533static float game_anim_length(const game_state *oldstate,
2534 const game_state *newstate, int dir, game_ui *ui)
2535{
2536 return 0.0F;
2537}
2538
2539static float game_flash_length(const game_state *oldstate,
2540 const game_state *newstate, int dir, game_ui *ui)
2541{
2542 if (!oldstate->completed && newstate->completed &&
2543 !oldstate->used_solve && !newstate->used_solve)
2544 return FLASH_TIME;
2545 else
2546 return 0.0F;
2547}
2548
2549static int game_status(const game_state *state)
2550{
2551 return state->completed ? +1 : 0;
2552}
2553
2554static int game_timing_state(const game_state *state, game_ui *ui)
2555{
2556 return TRUE;
2557}
2558
2559static void game_print_size(const game_params *params, float *x, float *y)
2560{
2561 int pw, ph;
2562
2563 /*
2564 * I'll use 6mm squares by default.
2565 */
2566 game_compute_size(params, 600, &pw, &ph);
2567 *x = pw / 100.0F;
2568 *y = ph / 100.0F;
2569}
2570
2571static void game_print(drawing *dr, const game_state *state, int tilesize)
2572{
2573 int w = state->shared->w, h = state->shared->h, x, y;
2574 int black = print_mono_colour(dr, 0);
2575 int white = print_mono_colour(dr, 1);
2576
2577 /* No GUI_LOOPY here: only use the familiar masyu style. */
2578
2579 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2580 game_drawstate *ds = game_new_drawstate(dr, state);
2581 game_set_size(dr, ds, NULL, tilesize);
2582
2583 /* Draw grid outlines (black). */
2584 for (x = 0; x <= w; x++)
2585 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
2586 for (y = 0; y <= h; y++)
2587 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
2588
2589 for (x = 0; x < w; x++) {
2590 for (y = 0; y < h; y++) {
2591 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ;
2592 int clue = state->shared->clues[y*w+x];
2593
2594 draw_lines_specific(dr, ds, x, y, state->lines[y*w+x], 0, black);
2595
2596 if (clue != NOCLUE) {
2597 int c = (clue == CORNER) ? black : white;
2598 draw_circle(dr, cx, cy, TILE_SIZE/4, c, black);
2599 }
2600 }
2601 }
2602
2603 game_free_drawstate(dr, ds);
2604}
2605
2606#ifdef COMBINED
2607#define thegame pearl
2608#endif
2609
2610const struct game thegame = {
2611 "Pearl", "games.pearl", "pearl",
2612 default_params,
2613 game_fetch_preset,
2614 decode_params,
2615 encode_params,
2616 free_params,
2617 dup_params,
2618 TRUE, game_configure, custom_params,
2619 validate_params,
2620 new_game_desc,
2621 validate_desc,
2622 new_game,
2623 dup_game,
2624 free_game,
2625 TRUE, solve_game,
2626 TRUE, game_can_format_as_text_now, game_text_format,
2627 new_ui,
2628 free_ui,
2629 encode_ui,
2630 decode_ui,
2631 game_changed_state,
2632 interpret_move,
2633 execute_move,
2634 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2635 game_colours,
2636 game_new_drawstate,
2637 game_free_drawstate,
2638 game_redraw,
2639 game_anim_length,
2640 game_flash_length,
2641 game_status,
2642 TRUE, FALSE, game_print_size, game_print,
2643 FALSE, /* wants_statusbar */
2644 FALSE, game_timing_state,
2645 0, /* flags */
2646};
2647
2648#ifdef STANDALONE_SOLVER
2649
2650#include <time.h>
2651#include <stdarg.h>
2652
2653const char *quis = NULL;
2654
2655static void usage(FILE *out) {
2656 fprintf(out, "usage: %s <params>\n", quis);
2657}
2658
2659static void pnum(int n, int ntot, const char *desc)
2660{
2661 printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc);
2662}
2663
2664static void start_soak(game_params *p, random_state *rs, int nsecs)
2665{
2666 time_t tt_start, tt_now, tt_last;
2667 int n = 0, nsolved = 0, nimpossible = 0, ret;
2668 char *grid, *clues;
2669
2670 tt_start = tt_last = time(NULL);
2671
2672 /* Currently this generates puzzles of any difficulty (trying to solve it
2673 * on the maximum difficulty level and not checking it's not too easy). */
2674 printf("Soak-testing a %dx%d grid (any difficulty)", p->w, p->h);
2675 if (nsecs > 0) printf(" for %d seconds", nsecs);
2676 printf(".\n");
2677
2678 p->nosolve = TRUE;
2679
2680 grid = snewn(p->w*p->h, char);
2681 clues = snewn(p->w*p->h, char);
2682
2683 while (1) {
2684 n += new_clues(p, rs, clues, grid); /* should be 1, with nosolve */
2685
2686 ret = pearl_solve(p->w, p->h, clues, grid, DIFF_TRICKY, FALSE);
2687 if (ret <= 0) nimpossible++;
2688 if (ret == 1) nsolved++;
2689
2690 tt_now = time(NULL);
2691 if (tt_now > tt_last) {
2692 tt_last = tt_now;
2693
2694 printf("%d total, %3.1f/s, ",
2695 n, (double)n / ((double)tt_now - tt_start));
2696 pnum(nsolved, n, "solved"); printf(", ");
2697 printf("%3.1f/s", (double)nsolved / ((double)tt_now - tt_start));
2698 if (nimpossible > 0)
2699 pnum(nimpossible, n, "impossible");
2700 printf("\n");
2701 }
2702 if (nsecs > 0 && (tt_now - tt_start) > nsecs) {
2703 printf("\n");
2704 break;
2705 }
2706 }
2707
2708 sfree(grid);
2709 sfree(clues);
2710}
2711
2712int main(int argc, const char *argv[])
2713{
2714 game_params *p = NULL;
2715 random_state *rs = NULL;
2716 time_t seed = time(NULL);
2717 char *id = NULL, *err;
2718
2719 setvbuf(stdout, NULL, _IONBF, 0);
2720
2721 quis = argv[0];
2722
2723 while (--argc > 0) {
2724 char *p = (char*)(*++argv);
2725 if (!strcmp(p, "-e") || !strcmp(p, "--seed")) {
2726 seed = atoi(*++argv);
2727 argc--;
2728 } else if (*p == '-') {
2729 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2730 usage(stderr);
2731 exit(1);
2732 } else {
2733 id = p;
2734 }
2735 }
2736
2737 rs = random_new((void*)&seed, sizeof(time_t));
2738 p = default_params();
2739
2740 if (id) {
2741 if (strchr(id, ':')) {
2742 fprintf(stderr, "soak takes params only.\n");
2743 goto done;
2744 }
2745
2746 decode_params(p, id);
2747 err = validate_params(p, 1);
2748 if (err) {
2749 fprintf(stderr, "%s: %s", argv[0], err);
2750 goto done;
2751 }
2752
2753 start_soak(p, rs, 0); /* run forever */
2754 } else {
2755 int i;
2756
2757 for (i = 5; i <= 12; i++) {
2758 p->w = p->h = i;
2759 start_soak(p, rs, 5);
2760 }
2761 }
2762
2763done:
2764 free_params(p);
2765 random_free(rs);
2766
2767 return 0;
2768}
2769
2770#endif
2771
2772/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/pegs.R b/apps/plugins/puzzles/pegs.R
new file mode 100644
index 0000000000..1e79e99ca0
--- /dev/null
+++ b/apps/plugins/puzzles/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/pegs.c b/apps/plugins/puzzles/pegs.c
new file mode 100644
index 0000000000..f02e25cafa
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/penrose.c b/apps/plugins/puzzles/penrose.c
new file mode 100644
index 0000000000..839b50a853
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/penrose.h b/apps/plugins/puzzles/penrose.h
new file mode 100644
index 0000000000..ba5ae16f2c
--- /dev/null
+++ b/apps/plugins/puzzles/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/printing.c b/apps/plugins/puzzles/printing.c
new file mode 100644
index 0000000000..e921a4d545
--- /dev/null
+++ b/apps/plugins/puzzles/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/ps.c b/apps/plugins/puzzles/ps.c
new file mode 100644
index 0000000000..67d76b4daa
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/puzzles.but b/apps/plugins/puzzles/puzzles.but
new file mode 100644
index 0000000000..2508fe3337
--- /dev/null
+++ b/apps/plugins/puzzles/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/puzzles.h b/apps/plugins/puzzles/puzzles.h
new file mode 100644
index 0000000000..c70b33ccbf
--- /dev/null
+++ b/apps/plugins/puzzles/puzzles.h
@@ -0,0 +1,636 @@
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 "plugin.h"
9#include <tlsf.h>
10#include "rbcompat.h"
11#include "lib/pluginlib_exit.h"
12
13#ifdef ROCKBOX
14#if LCD_WIDTH < LCD_HEIGHT
15#define PORTRAIT_SCREEN
16#endif
17#endif
18
19#ifndef TRUE
20#define TRUE 1
21#endif
22#ifndef FALSE
23#define FALSE 0
24#endif
25
26#define PI 3.141592653589793238462643383279502884197169399
27
28#define lenof(array) ( sizeof(array) / sizeof(*(array)) )
29
30#undef STR
31#define STR_INT(x) #x
32#define STR(x) STR_INT(x)
33
34/* NB not perfect because they evaluate arguments multiple times. */
35#ifndef max
36#define max(x,y) ( (x)>(y) ? (x) : (y) )
37#endif /* max */
38#ifndef min
39#define min(x,y) ( (x)<(y) ? (x) : (y) )
40#endif /* min */
41
42enum {
43 LEFT_BUTTON = 0x0200,
44 MIDDLE_BUTTON,
45 RIGHT_BUTTON,
46 LEFT_DRAG,
47 MIDDLE_DRAG,
48 RIGHT_DRAG,
49 LEFT_RELEASE,
50 MIDDLE_RELEASE,
51 RIGHT_RELEASE,
52 CURSOR_UP,
53 CURSOR_DOWN,
54 CURSOR_LEFT,
55 CURSOR_RIGHT,
56 CURSOR_SELECT,
57 CURSOR_SELECT2,
58
59 /* made smaller because of 'limited range of datatype' errors. */
60 MOD_CTRL = 0x1000,
61 MOD_SHFT = 0x2000,
62 MOD_NUM_KEYPAD = 0x4000,
63 MOD_MASK = 0x7000 /* mask for all modifiers */
64};
65
66#define IS_MOUSE_DOWN(m) ( (unsigned)((m) - LEFT_BUTTON) <= \
67 (unsigned)(RIGHT_BUTTON - LEFT_BUTTON))
68#define IS_MOUSE_DRAG(m) ( (unsigned)((m) - LEFT_DRAG) <= \
69 (unsigned)(RIGHT_DRAG - LEFT_DRAG))
70#define IS_MOUSE_RELEASE(m) ( (unsigned)((m) - LEFT_RELEASE) <= \
71 (unsigned)(RIGHT_RELEASE - LEFT_RELEASE))
72#define IS_CURSOR_MOVE(m) ( (m) == CURSOR_UP || (m) == CURSOR_DOWN || \
73 (m) == CURSOR_RIGHT || (m) == CURSOR_LEFT )
74#define IS_CURSOR_SELECT(m) ( (m) == CURSOR_SELECT || (m) == CURSOR_SELECT2)
75
76/*
77 * Flags in the back end's `flags' word.
78 */
79/* Bit flags indicating mouse button priorities */
80#define BUTTON_BEATS(x,y) ( 1 << (((x)-LEFT_BUTTON)*3+(y)-LEFT_BUTTON) )
81/* Flag indicating that Solve operations should be animated */
82#define SOLVE_ANIMATES ( 1 << 9 )
83/* Pocket PC: Game requires right mouse button emulation */
84#define REQUIRE_RBUTTON ( 1 << 10 )
85/* Pocket PC: Game requires numeric input */
86#define REQUIRE_NUMPAD ( 1 << 11 )
87/* end of `flags' word definitions */
88
89#ifdef _WIN32_WCE
90 /* Pocket PC devices have small, portrait screen that requires more vivid colours */
91 #define SMALL_SCREEN
92 #define PORTRAIT_SCREEN
93 #define VIVID_COLOURS
94 #define STYLUS_BASED
95#endif
96
97#define IGNOREARG(x) ( (x) = (x) )
98
99typedef struct frontend frontend;
100typedef struct config_item config_item;
101typedef struct midend midend;
102typedef struct random_state random_state;
103typedef struct game_params game_params;
104typedef struct game_state game_state;
105typedef struct game_ui game_ui;
106typedef struct game_drawstate game_drawstate;
107typedef struct game game;
108typedef struct blitter blitter;
109typedef struct document document;
110typedef struct drawing_api drawing_api;
111typedef struct drawing drawing;
112typedef struct psdata psdata;
113
114#define ALIGN_VNORMAL 0x000
115#define ALIGN_VCENTRE 0x100
116
117#define ALIGN_HLEFT 0x000
118#define ALIGN_HCENTRE 0x001
119#define ALIGN_HRIGHT 0x002
120
121#define FONT_FIXED 0
122#define FONT_VARIABLE 1
123
124/* For printing colours */
125#define HATCH_SLASH 1
126#define HATCH_BACKSLASH 2
127#define HATCH_HORIZ 3
128#define HATCH_VERT 4
129#define HATCH_PLUS 5
130#define HATCH_X 6
131
132/*
133 * Structure used to pass configuration data between frontend and
134 * game
135 */
136enum { C_STRING, C_CHOICES, C_BOOLEAN, C_END };
137struct config_item {
138 /*
139 * `name' is never dynamically allocated.
140 */
141 char *name;
142 /*
143 * `type' contains one of the above values.
144 */
145 int type;
146 /*
147 * For C_STRING, `sval' is always dynamically allocated and
148 * non-NULL. For C_BOOLEAN and C_END, `sval' is always NULL.
149 * For C_CHOICES, `sval' is non-NULL, _not_ dynamically
150 * allocated, and contains a set of option strings separated by
151 * a delimiter. The delimeter is also the first character in
152 * the string, so for example ":Foo:Bar:Baz" gives three
153 * options `Foo', `Bar' and `Baz'.
154 */
155 char *sval;
156 /*
157 * For C_BOOLEAN, this is TRUE or FALSE. For C_CHOICES, it
158 * indicates the chosen index from the `sval' list. In the
159 * above example, 0==Foo, 1==Bar and 2==Baz.
160 */
161 int ival;
162};
163
164/*
165 * Platform routines
166 */
167
168/* We can't use #ifdef DEBUG, because Cygwin defines it by default. */
169#ifdef DEBUGGING
170#define debug(x) (debug_printf x)
171void debug_printf(char *fmt, ...);
172#else
173#define debug(x)
174#endif
175
176void fatal(char *fmt, ...);
177void frontend_default_colour(frontend *fe, float *output);
178void deactivate_timer(frontend *fe);
179void activate_timer(frontend *fe);
180void get_random_seed(void **randseed, int *randseedsize);
181
182/*
183 * drawing.c
184 */
185drawing *drawing_new(const drawing_api *api, midend *me, void *handle);
186void drawing_free(drawing *dr);
187void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize,
188 int align, int colour, char *text);
189void draw_rect(drawing *dr, int x, int y, int w, int h, int colour);
190void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour);
191void draw_polygon(drawing *dr, int *coords, int npoints,
192 int fillcolour, int outlinecolour);
193void draw_circle(drawing *dr, int cx, int cy, int radius,
194 int fillcolour, int outlinecolour);
195void draw_thick_line(drawing *dr, float thickness,
196 float x1, float y1, float x2, float y2, int colour);
197void clip(drawing *dr, int x, int y, int w, int h);
198void unclip(drawing *dr);
199void start_draw(drawing *dr);
200void draw_update(drawing *dr, int x, int y, int w, int h);
201void end_draw(drawing *dr);
202char *text_fallback(drawing *dr, const char *const *strings, int nstrings);
203void status_bar(drawing *dr, char *text);
204blitter *blitter_new(drawing *dr, int w, int h);
205void blitter_free(drawing *dr, blitter *bl);
206/* save puts the portion of the current display with top-left corner
207 * (x,y) to the blitter. load puts it back again to the specified
208 * coords, or else wherever it was saved from
209 * (if x = y = BLITTER_FROMSAVED). */
210void blitter_save(drawing *dr, blitter *bl, int x, int y);
211#define BLITTER_FROMSAVED (-1)
212void blitter_load(drawing *dr, blitter *bl, int x, int y);
213void print_begin_doc(drawing *dr, int pages);
214void print_begin_page(drawing *dr, int number);
215void print_begin_puzzle(drawing *dr, float xm, float xc,
216 float ym, float yc, int pw, int ph, float wmm,
217 float scale);
218void print_end_puzzle(drawing *dr);
219void print_end_page(drawing *dr, int number);
220void print_end_doc(drawing *dr);
221void print_get_colour(drawing *dr, int colour, int printing_in_colour,
222 int *hatch, float *r, float *g, float *b);
223int print_mono_colour(drawing *dr, int grey); /* 0==black, 1==white */
224int print_grey_colour(drawing *dr, float grey);
225int print_hatched_colour(drawing *dr, int hatch);
226int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int mono);
227int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey);
228int print_rgb_hatched_colour(drawing *dr, float r, float g, float b,
229 int hatch);
230void print_line_width(drawing *dr, int width);
231void print_line_dotted(drawing *dr, int dotted);
232
233/*
234 * midend.c
235 */
236midend *midend_new(frontend *fe, const game *ourgame,
237 const drawing_api *drapi, void *drhandle);
238void midend_free(midend *me);
239const game *midend_which_game(midend *me);
240void midend_set_params(midend *me, game_params *params);
241game_params *midend_get_params(midend *me);
242void midend_size(midend *me, int *x, int *y, int user_size);
243void midend_reset_tilesize(midend *me);
244void midend_new_game(midend *me);
245void midend_restart_game(midend *me);
246void midend_stop_anim(midend *me);
247int midend_process_key(midend *me, int x, int y, int button);
248void midend_force_redraw(midend *me);
249void midend_redraw(midend *me);
250float *midend_colours(midend *me, int *ncolours);
251void midend_freeze_timer(midend *me, float tprop);
252void midend_timer(midend *me, float tplus);
253int midend_num_presets(midend *me);
254void midend_fetch_preset(midend *me, int n,
255 char **name, game_params **params);
256int midend_which_preset(midend *me);
257int midend_wants_statusbar(midend *me);
258enum { CFG_SETTINGS, CFG_SEED, CFG_DESC, CFG_FRONTEND_SPECIFIC };
259config_item *midend_get_config(midend *me, int which, char **wintitle);
260char *midend_set_config(midend *me, int which, config_item *cfg);
261char *midend_game_id(midend *me, char *id);
262char *midend_get_game_id(midend *me);
263char *midend_get_random_seed(midend *me);
264int midend_can_format_as_text_now(midend *me);
265char *midend_text_format(midend *me);
266char *midend_solve(midend *me);
267int midend_status(midend *me);
268int midend_can_undo(midend *me);
269int midend_can_redo(midend *me);
270void midend_supersede_game_desc(midend *me, char *desc, char *privdesc);
271char *midend_rewrite_statusbar(midend *me, char *text);
272void midend_serialise(midend *me,
273 void (*write)(void *ctx, void *buf, int len),
274 void *wctx);
275char *midend_deserialise(midend *me,
276 int (*read)(void *ctx, void *buf, int len),
277 void *rctx);
278char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len),
279 void *rctx);
280void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx);
281/* Printing functions supplied by the mid-end */
282char *midend_print_puzzle(midend *me, document *doc, int with_soln);
283int midend_tilesize(midend *me);
284
285/*
286 * malloc.c
287 */
288void *smalloc(size_t size);
289void *srealloc(void *p, size_t size);
290void sfree(void *p);
291char *dupstr(const char *s);
292#define snew(type) \
293 ( (type *) smalloc (sizeof (type)) )
294#define snewn(number, type) \
295 ( (type *) smalloc ((number) * sizeof (type)) )
296#define sresize(array, number, type) \
297 ( (type *) srealloc ((array), (number) * sizeof (type)) )
298
299/*
300 * misc.c
301 */
302void free_cfg(config_item *cfg);
303void obfuscate_bitmap(unsigned char *bmp, int bits, int decode);
304
305/* allocates output each time. len is always in bytes of binary data.
306 * May assert (or just go wrong) if lengths are unchecked. */
307char *bin2hex(const unsigned char *in, int inlen);
308unsigned char *hex2bin(const char *in, int outlen);
309
310/* Sets (and possibly dims) background from frontend default colour,
311 * and auto-generates highlight and lowlight colours too. */
312void game_mkhighlight(frontend *fe, float *ret,
313 int background, int highlight, int lowlight);
314/* As above, but starts from a provided background colour rather
315 * than the frontend default. */
316void game_mkhighlight_specific(frontend *fe, float *ret,
317 int background, int highlight, int lowlight);
318
319/* Randomly shuffles an array of items. */
320void shuffle(void *array, int nelts, int eltsize, random_state *rs);
321
322/* Draw a rectangle outline, using the drawing API's draw_line. */
323void draw_rect_outline(drawing *dr, int x, int y, int w, int h,
324 int colour);
325
326/* Draw a set of rectangle corners (e.g. for a cursor display). */
327void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col);
328
329void move_cursor(int button, int *x, int *y, int maxw, int maxh, int wrap);
330
331/* Used in netslide.c and sixteen.c for cursor movement around edge. */
332int c2pos(int w, int h, int cx, int cy);
333int c2diff(int w, int h, int cx, int cy, int button);
334void pos2c(int w, int h, int pos, int *cx, int *cy);
335
336/* Draws text with an 'outline' formed by offsetting the text
337 * by one pixel; useful for highlighting. Outline is omitted if -1. */
338void draw_text_outline(drawing *dr, int x, int y, int fonttype,
339 int fontsize, int align,
340 int text_colour, int outline_colour, char *text);
341/*
342 * dsf.c
343 */
344int *snew_dsf(int size);
345
346void print_dsf(int *dsf, int size);
347
348/* Return the canonical element of the equivalence class containing element
349 * val. If 'inverse' is non-NULL, this function will put into it a flag
350 * indicating whether the canonical element is inverse to val. */
351int edsf_canonify(int *dsf, int val, int *inverse);
352int dsf_canonify(int *dsf, int val);
353int dsf_size(int *dsf, int val);
354
355/* Allow the caller to specify that two elements should be in the same
356 * equivalence class. If 'inverse' is TRUE, the elements are actually opposite
357 * to one another in some sense. This function will fail an assertion if the
358 * caller gives it self-contradictory data, ie if two elements are claimed to
359 * be both opposite and non-opposite. */
360void edsf_merge(int *dsf, int v1, int v2, int inverse);
361void dsf_merge(int *dsf, int v1, int v2);
362void dsf_init(int *dsf, int len);
363
364/*
365 * tdq.c
366 */
367
368/*
369 * Data structure implementing a 'to-do queue', a simple
370 * de-duplicating to-do list mechanism.
371 *
372 * Specification: a tdq is a queue which can hold integers from 0 to
373 * n-1, where n was some constant specified at tdq creation time. No
374 * integer may appear in the queue's current contents more than once;
375 * an attempt to add an already-present integer again will do nothing,
376 * so that that integer is removed from the queue at the position
377 * where it was _first_ inserted. The add and remove operations take
378 * constant time.
379 *
380 * The idea is that you might use this in applications like solvers:
381 * keep a tdq listing the indices of grid squares that you currently
382 * need to process in some way. Whenever you modify a square in a way
383 * that will require you to re-scan its neighbours, add them to the
384 * list with tdq_add; meanwhile you're constantly taking elements off
385 * the list when you need another square to process. In solvers where
386 * deductions are mostly localised, this should prevent repeated
387 * O(N^2) loops over the whole grid looking for something to do. (But
388 * if only _most_ of the deductions are localised, then you should
389 * respond to an empty to-do list by re-adding everything using
390 * tdq_fill, so _then_ you rescan the whole grid looking for newly
391 * enabled non-local deductions. Only if you've done that and emptied
392 * the list again finding nothing new to do are you actually done.)
393 */
394typedef struct tdq tdq;
395tdq *tdq_new(int n);
396void tdq_free(tdq *tdq);
397void tdq_add(tdq *tdq, int k);
398int tdq_remove(tdq *tdq); /* returns -1 if nothing available */
399void tdq_fill(tdq *tdq); /* add everything to the tdq at once */
400
401/*
402 * laydomino.c
403 */
404int *domino_layout(int w, int h, random_state *rs);
405void domino_layout_prealloc(int w, int h, random_state *rs,
406 int *grid, int *grid2, int *list);
407/*
408 * version.c
409 */
410extern char ver[];
411
412/*
413 * random.c
414 */
415random_state *random_new(const char *seed, int len);
416random_state *random_copy(random_state *tocopy);
417unsigned long random_bits(random_state *state, int bits);
418unsigned long random_upto(random_state *state, unsigned long limit);
419void random_free(random_state *state);
420char *random_state_encode(random_state *state);
421random_state *random_state_decode(const char *input);
422/* random.c also exports SHA, which occasionally comes in useful. */
423#if __STDC_VERSION__ >= 199901L
424#include <stdint.h>
425typedef uint32_t uint32;
426#elif UINT_MAX >= 4294967295L
427typedef unsigned int uint32;
428#else
429typedef unsigned long uint32;
430#endif
431typedef struct {
432 uint32 h[5];
433 unsigned char block[64];
434 int blkused;
435 uint32 lenhi, lenlo;
436} SHA_State;
437void SHA_Init(SHA_State *s);
438void SHA_Bytes(SHA_State *s, const void *p, int len);
439void SHA_Final(SHA_State *s, unsigned char *output);
440void SHA_Simple(const void *p, int len, unsigned char *output);
441
442/*
443 * printing.c
444 */
445document *document_new(int pw, int ph, float userscale);
446void document_free(document *doc);
447void document_add_puzzle(document *doc, const game *game, game_params *par,
448 game_state *st, game_state *st2);
449void document_print(document *doc, drawing *dr);
450
451/*
452 * ps.c
453 */
454//psdata *ps_init(FILE *outfile, int colour);
455//void ps_free(psdata *ps);
456//drawing *ps_drawing_api(psdata *ps);
457
458/*
459 * combi.c: provides a structure and functions for iterating over
460 * combinations (i.e. choosing r things out of n).
461 */
462typedef struct _combi_ctx {
463 int r, n, nleft, total;
464 int *a;
465} combi_ctx;
466
467combi_ctx *new_combi(int r, int n);
468void reset_combi(combi_ctx *combi);
469combi_ctx *next_combi(combi_ctx *combi); /* returns NULL for end */
470void free_combi(combi_ctx *combi);
471
472/*
473 * divvy.c
474 */
475/* divides w*h rectangle into pieces of size k. Returns w*h dsf. */
476int *divvy_rectangle(int w, int h, int k, random_state *rs);
477
478/*
479 * findloop.c
480 */
481struct findloopstate;
482struct findloopstate *findloop_new_state(int nvertices);
483void findloop_free_state(struct findloopstate *);
484/*
485 * Callback provided by the client code to enumerate the graph
486 * vertices joined directly to a given vertex.
487 *
488 * Semantics: if vertex >= 0, return one of its neighbours; if vertex
489 * < 0, return a previously unmentioned neighbour of whatever vertex
490 * was last passed as input. Write to 'ctx' as necessary to store
491 * state. In either case, return < 0 if no such vertex can be found.
492 */
493typedef int (*neighbour_fn_t)(int vertex, void *ctx);
494/*
495 * Actual function to find loops. 'ctx' will be passed unchanged to
496 * the 'neighbour' function to query graph edges. Returns FALSE if no
497 * loop was found, or TRUE if one was.
498 */
499int findloop_run(struct findloopstate *state, int nvertices,
500 neighbour_fn_t neighbour, void *ctx);
501/*
502 * Query whether an edge is part of a loop, in the output of
503 * find_loops.
504 *
505 * Due to the internal storage format, if you pass u,v which are not
506 * connected at all, the output will be TRUE. (The algorithm actually
507 * stores an exhaustive list of *non*-loop edges, because there are
508 * fewer of those, so really it's querying whether the edge is on that
509 * list.)
510 */
511int findloop_is_loop_edge(struct findloopstate *state, int u, int v);
512
513/*
514 * Data structure containing the function calls and data specific
515 * to a particular game. This is enclosed in a data structure so
516 * that a particular platform can choose, if it wishes, to compile
517 * all the games into a single combined executable rather than
518 * having lots of little ones.
519 */
520struct game {
521 const char *name;
522 const char *winhelp_topic, *htmlhelp_topic;
523 game_params *(*default_params)(void);
524 int (*fetch_preset)(int i, char **name, game_params **params);
525 void (*decode_params)(game_params *, char const *string);
526 char *(*encode_params)(const game_params *, int full);
527 void (*free_params)(game_params *params);
528 game_params *(*dup_params)(const game_params *params);
529 int can_configure;
530 config_item *(*configure)(const game_params *params);
531 game_params *(*custom_params)(const config_item *cfg);
532 char *(*validate_params)(const game_params *params, int full);
533 char *(*new_desc)(const game_params *params, random_state *rs,
534 char **aux, int interactive);
535 char *(*validate_desc)(const game_params *params, const char *desc);
536 game_state *(*new_game)(midend *me, const game_params *params,
537 const char *desc);
538 game_state *(*dup_game)(const game_state *state);
539 void (*free_game)(game_state *state);
540 int can_solve;
541 char *(*solve)(const game_state *orig, const game_state *curr,
542 const char *aux, char **error);
543 int can_format_as_text_ever;
544 int (*can_format_as_text_now)(const game_params *params);
545 char *(*text_format)(const game_state *state);
546 game_ui *(*new_ui)(const game_state *state);
547 void (*free_ui)(game_ui *ui);
548 char *(*encode_ui)(const game_ui *ui);
549 void (*decode_ui)(game_ui *ui, const char *encoding);
550 void (*changed_state)(game_ui *ui, const game_state *oldstate,
551 const game_state *newstate);
552 char *(*interpret_move)(const game_state *state, game_ui *ui,
553 const game_drawstate *ds, int x, int y, int button);
554 game_state *(*execute_move)(const game_state *state, const char *move);
555 int preferred_tilesize;
556 void (*compute_size)(const game_params *params, int tilesize,
557 int *x, int *y);
558 void (*set_size)(drawing *dr, game_drawstate *ds,
559 const game_params *params, int tilesize);
560 float *(*colours)(frontend *fe, int *ncolours);
561 game_drawstate *(*new_drawstate)(drawing *dr, const game_state *state);
562 void (*free_drawstate)(drawing *dr, game_drawstate *ds);
563 void (*redraw)(drawing *dr, game_drawstate *ds, const game_state *oldstate,
564 const game_state *newstate, int dir, const game_ui *ui,
565 float anim_time, float flash_time);
566 float (*anim_length)(const game_state *oldstate,
567 const game_state *newstate, int dir, game_ui *ui);
568 float (*flash_length)(const game_state *oldstate,
569 const game_state *newstate, int dir, game_ui *ui);
570 int (*status)(const game_state *state);
571 int can_print, can_print_in_colour;
572 void (*print_size)(const game_params *params, float *x, float *y);
573 void (*print)(drawing *dr, const game_state *state, int tilesize);
574 int wants_statusbar;
575 int is_timed;
576 int (*timing_state)(const game_state *state, game_ui *ui);
577 int flags;
578};
579
580/*
581 * Data structure containing the drawing API implemented by the
582 * front end and also by cross-platform printing modules such as
583 * PostScript.
584 */
585struct drawing_api {
586 void (*draw_text)(void *handle, int x, int y, int fonttype, int fontsize,
587 int align, int colour, char *text);
588 void (*draw_rect)(void *handle, int x, int y, int w, int h, int colour);
589 void (*draw_line)(void *handle, int x1, int y1, int x2, int y2,
590 int colour);
591 void (*draw_polygon)(void *handle, int *coords, int npoints,
592 int fillcolour, int outlinecolour);
593 void (*draw_circle)(void *handle, int cx, int cy, int radius,
594 int fillcolour, int outlinecolour);
595 void (*draw_update)(void *handle, int x, int y, int w, int h);
596 void (*clip)(void *handle, int x, int y, int w, int h);
597 void (*unclip)(void *handle);
598 void (*start_draw)(void *handle);
599 void (*end_draw)(void *handle);
600 void (*status_bar)(void *handle, char *text);
601 blitter *(*blitter_new)(void *handle, int w, int h);
602 void (*blitter_free)(void *handle, blitter *bl);
603 void (*blitter_save)(void *handle, blitter *bl, int x, int y);
604 void (*blitter_load)(void *handle, blitter *bl, int x, int y);
605 void (*begin_doc)(void *handle, int pages);
606 void (*begin_page)(void *handle, int number);
607 void (*begin_puzzle)(void *handle, float xm, float xc,
608 float ym, float yc, int pw, int ph, float wmm);
609 void (*end_puzzle)(void *handle);
610 void (*end_page)(void *handle, int number);
611 void (*end_doc)(void *handle);
612 void (*line_width)(void *handle, float width);
613 void (*line_dotted)(void *handle, int dotted);
614 char *(*text_fallback)(void *handle, const char *const *strings,
615 int nstrings);
616 void (*draw_thick_line)(void *handle, float thickness,
617 float x1, float y1, float x2, float y2,
618 int colour);
619};
620
621/*
622 * For one-game-at-a-time platforms, there's a single structure
623 * like the above, under a fixed name. For all-at-once platforms,
624 * there's a list of all available puzzles in array form.
625 */
626#ifdef COMBINED
627extern const game *gamelist[];
628extern const int gamecount;
629#else
630extern const game thegame;
631#endif
632
633/* A little bit of help to lazy developers */
634#define DEFAULT_STATUSBAR_TEXT "Use status_bar() to fill this in."
635
636#endif /* PUZZLES_PUZZLES_H */
diff --git a/apps/plugins/puzzles/puzzles.make b/apps/plugins/puzzles/puzzles.make
new file mode 100644
index 0000000000..054e53eeb1
--- /dev/null
+++ b/apps/plugins/puzzles/puzzles.make
@@ -0,0 +1,113 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10PUZZLES_SRCDIR = $(APPSDIR)/plugins/puzzles
11PUZZLES_OBJDIR = $(BUILDDIR)/apps/plugins/puzzles
12
13# define this as "yes" to do a monolithic build -- remember to also
14# uncomment the sgt-puzzles loader in apps/plugins/SOURCES
15PUZZLES_COMBINED =
16
17ifdef PUZZLES_COMBINED
18PUZZLES_SRC := $(call preprocess, $(PUZZLES_SRCDIR)/SOURCES, -DCOMBINED)
19else
20PUZZLES_SRC := $(call preprocess, $(PUZZLES_SRCDIR)/SOURCES)
21endif
22
23ifndef PUZZLES_COMBINED
24PUZZLES_SHARED_OBJ := $(call c2obj, $(PUZZLES_SRC))
25endif
26
27PUZZLES_GAMES_SRC := $(call preprocess, $(PUZZLES_SRCDIR)/SOURCES.games)
28PUZZLES_SRC += $(PUZZLES_GAMES_SRC)
29PUZZLES_OBJ := $(call c2obj, $(PUZZLES_SRC))
30OTHER_SRC += $(PUZZLES_SRC)
31
32ifdef PUZZLES_COMBINED
33ifndef APP_TYPE
34ROCKS += $(PUZZLES_OBJDIR)/puzzles.ovl
35PUZZLES_OUTLDS = $(PUZZLES_OBJDIR)/puzzles.link
36PUZZLES_OVLFLAGS = -T$(PUZZLES_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map
37else
38ROCKS += $(PUZZLES_OBJDIR)/puzzles.rock
39endif
40else
41PUZZLES_ROCKS := $(addprefix $(PUZZLES_OBJDIR)/sgt-, $(notdir $(PUZZLES_GAMES_SRC:.c=.rock)))
42ROCKS += $(PUZZLES_ROCKS)
43endif
44
45PUZZLESFLAGS = $(filter-out -O%,$(PLUGINFLAGS)) -Os \
46 -Wno-unused-parameter -Wno-sign-compare -Wno-strict-aliasing -w \
47 -DFOR_REAL
48ifdef PUZZLES_COMBINED
49PUZZLESFLAGS += -DCOMBINED
50endif
51
52ifdef PUZZLES_COMBINED
53$(PUZZLES_OBJDIR)/puzzles.rock: $(PUZZLES_OBJ) $(TLSFLIB)
54
55$(PUZZLES_OBJDIR)/puzzles.refmap: $(PUZZLES_OBJ) $(TLSFLIB)
56
57$(PUZZLES_OUTLDS): $(PLUGIN_LDS) $(PUZZLES_OBJDIR)/puzzles.refmap
58 $(call PRINTS,PP $(@F))$(call preprocess2file,$<,$@,-DOVERLAY_OFFSET=$(shell \
59 $(TOOLSDIR)/ovl_offset.pl $(PUZZLES_OBJDIR)/puzzles.refmap))
60
61$(PUZZLES_OBJDIR)/puzzles.ovl: $(PUZZLES_OBJ) $(PUZZLES_OUTLDS) $(TLSFLIB)
62 $(SILENT)$(CC) $(PLUGINFLAGS) -o $(basename $@).elf \
63 $(filter %.o, $^) \
64 $(filter %.a, $+) \
65 -lgcc $(PUZZLES_OVLFLAGS)
66 $(call PRINTS,LD $(@F))$(call objcopy,$(basename $@).elf,$@)
67else
68# todo: figure out how to do this with wildcards
69$(PUZZLES_OBJDIR)/sgt-blackbox.rock: $(PUZZLES_OBJDIR)/blackbox.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
70$(PUZZLES_OBJDIR)/sgt-bridges.rock: $(PUZZLES_OBJDIR)/bridges.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
71$(PUZZLES_OBJDIR)/sgt-cube.rock: $(PUZZLES_OBJDIR)/cube.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
72$(PUZZLES_OBJDIR)/sgt-dominosa.rock: $(PUZZLES_OBJDIR)/dominosa.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
73$(PUZZLES_OBJDIR)/sgt-fifteen.rock: $(PUZZLES_OBJDIR)/fifteen.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
74$(PUZZLES_OBJDIR)/sgt-filling.rock: $(PUZZLES_OBJDIR)/filling.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
75$(PUZZLES_OBJDIR)/sgt-flip.rock: $(PUZZLES_OBJDIR)/flip.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
76$(PUZZLES_OBJDIR)/sgt-flood.rock: $(PUZZLES_OBJDIR)/flood.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
77$(PUZZLES_OBJDIR)/sgt-galaxies.rock: $(PUZZLES_OBJDIR)/galaxies.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
78$(PUZZLES_OBJDIR)/sgt-guess.rock: $(PUZZLES_OBJDIR)/guess.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
79$(PUZZLES_OBJDIR)/sgt-inertia.rock: $(PUZZLES_OBJDIR)/inertia.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
80$(PUZZLES_OBJDIR)/sgt-keen.rock: $(PUZZLES_OBJDIR)/keen.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
81$(PUZZLES_OBJDIR)/sgt-lightup.rock: $(PUZZLES_OBJDIR)/lightup.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
82$(PUZZLES_OBJDIR)/sgt-loopy.rock: $(PUZZLES_OBJDIR)/loopy.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
83$(PUZZLES_OBJDIR)/sgt-magnets.rock: $(PUZZLES_OBJDIR)/magnets.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
84$(PUZZLES_OBJDIR)/sgt-map.rock: $(PUZZLES_OBJDIR)/map.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
85$(PUZZLES_OBJDIR)/sgt-mines.rock: $(PUZZLES_OBJDIR)/mines.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
86$(PUZZLES_OBJDIR)/sgt-net.rock: $(PUZZLES_OBJDIR)/net.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
87$(PUZZLES_OBJDIR)/sgt-netslide.rock: $(PUZZLES_OBJDIR)/netslide.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
88$(PUZZLES_OBJDIR)/sgt-palisade.rock: $(PUZZLES_OBJDIR)/palisade.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
89$(PUZZLES_OBJDIR)/sgt-pattern.rock: $(PUZZLES_OBJDIR)/pattern.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
90$(PUZZLES_OBJDIR)/sgt-pearl.rock: $(PUZZLES_OBJDIR)/pearl.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
91$(PUZZLES_OBJDIR)/sgt-pegs.rock: $(PUZZLES_OBJDIR)/pegs.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
92$(PUZZLES_OBJDIR)/sgt-range.rock: $(PUZZLES_OBJDIR)/range.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
93$(PUZZLES_OBJDIR)/sgt-rect.rock: $(PUZZLES_OBJDIR)/rect.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
94$(PUZZLES_OBJDIR)/sgt-samegame.rock: $(PUZZLES_OBJDIR)/samegame.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
95$(PUZZLES_OBJDIR)/sgt-signpost.rock: $(PUZZLES_OBJDIR)/signpost.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
96$(PUZZLES_OBJDIR)/sgt-singles.rock: $(PUZZLES_OBJDIR)/singles.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
97$(PUZZLES_OBJDIR)/sgt-sixteen.rock: $(PUZZLES_OBJDIR)/sixteen.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
98$(PUZZLES_OBJDIR)/sgt-slant.rock: $(PUZZLES_OBJDIR)/slant.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
99$(PUZZLES_OBJDIR)/sgt-solo.rock: $(PUZZLES_OBJDIR)/solo.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
100$(PUZZLES_OBJDIR)/sgt-tents.rock: $(PUZZLES_OBJDIR)/tents.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
101$(PUZZLES_OBJDIR)/sgt-towers.rock: $(PUZZLES_OBJDIR)/towers.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
102$(PUZZLES_OBJDIR)/sgt-tracks.rock: $(PUZZLES_OBJDIR)/tracks.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
103$(PUZZLES_OBJDIR)/sgt-twiddle.rock: $(PUZZLES_OBJDIR)/twiddle.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
104$(PUZZLES_OBJDIR)/sgt-undead.rock: $(PUZZLES_OBJDIR)/undead.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
105$(PUZZLES_OBJDIR)/sgt-unequal.rock: $(PUZZLES_OBJDIR)/unequal.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
106$(PUZZLES_OBJDIR)/sgt-unruly.rock: $(PUZZLES_OBJDIR)/unruly.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
107$(PUZZLES_OBJDIR)/sgt-untangle.rock: $(PUZZLES_OBJDIR)/untangle.o $(PUZZLES_SHARED_OBJ) $(TLSFLIB)
108endif
109
110# special pattern rule for compiling puzzles with extra flags
111$(PUZZLES_OBJDIR)/%.o: $(PUZZLES_SRCDIR)/%.c $(PUZZLES_SRCDIR)/puzzles.make
112 $(SILENT)mkdir -p $(dir $@)
113 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PUZZLESFLAGS) -c $< -o $@
diff --git a/apps/plugins/puzzles/puzzles.rc2 b/apps/plugins/puzzles/puzzles.rc2
new file mode 100644
index 0000000000..4442a9b138
--- /dev/null
+++ b/apps/plugins/puzzles/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/random.c b/apps/plugins/puzzles/random.c
new file mode 100644
index 0000000000..c129a8d2ba
--- /dev/null
+++ b/apps/plugins/puzzles/random.c
@@ -0,0 +1,350 @@
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 <string.h>
14#include <stdio.h>
15
16#include "puzzles.h"
17
18/* ----------------------------------------------------------------------
19 * Core SHA algorithm: processes 16-word blocks into a message digest.
20 */
21
22#define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) )
23
24static void SHA_Core_Init(uint32 h[5])
25{
26 h[0] = 0x67452301;
27 h[1] = 0xefcdab89;
28 h[2] = 0x98badcfe;
29 h[3] = 0x10325476;
30 h[4] = 0xc3d2e1f0;
31}
32
33static void SHATransform(uint32 * digest, uint32 * block)
34{
35 uint32 w[80];
36 uint32 a, b, c, d, e;
37 int t;
38
39 for (t = 0; t < 16; t++)
40 w[t] = block[t];
41
42 for (t = 16; t < 80; t++) {
43 uint32 tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
44 w[t] = rol(tmp, 1);
45 }
46
47 a = digest[0];
48 b = digest[1];
49 c = digest[2];
50 d = digest[3];
51 e = digest[4];
52
53 for (t = 0; t < 20; t++) {
54 uint32 tmp =
55 rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999;
56 e = d;
57 d = c;
58 c = rol(b, 30);
59 b = a;
60 a = tmp;
61 }
62 for (t = 20; t < 40; t++) {
63 uint32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0x6ed9eba1;
64 e = d;
65 d = c;
66 c = rol(b, 30);
67 b = a;
68 a = tmp;
69 }
70 for (t = 40; t < 60; t++) {
71 uint32 tmp = rol(a,
72 5) + ((b & c) | (b & d) | (c & d)) + e + w[t] +
73 0x8f1bbcdc;
74 e = d;
75 d = c;
76 c = rol(b, 30);
77 b = a;
78 a = tmp;
79 }
80 for (t = 60; t < 80; t++) {
81 uint32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0xca62c1d6;
82 e = d;
83 d = c;
84 c = rol(b, 30);
85 b = a;
86 a = tmp;
87 }
88
89 digest[0] += a;
90 digest[1] += b;
91 digest[2] += c;
92 digest[3] += d;
93 digest[4] += e;
94}
95
96/* ----------------------------------------------------------------------
97 * Outer SHA algorithm: take an arbitrary length byte string,
98 * convert it into 16-word blocks with the prescribed padding at
99 * the end, and pass those blocks to the core SHA algorithm.
100 */
101
102void SHA_Init(SHA_State * s)
103{
104 SHA_Core_Init(s->h);
105 s->blkused = 0;
106 s->lenhi = s->lenlo = 0;
107}
108
109void SHA_Bytes(SHA_State * s, const void *p, int len)
110{
111 unsigned char *q = (unsigned char *) p;
112 uint32 wordblock[16];
113 uint32 lenw = len;
114 int i;
115
116 /*
117 * Update the length field.
118 */
119 s->lenlo += lenw;
120 s->lenhi += (s->lenlo < lenw);
121
122 if (s->blkused && s->blkused + len < 64) {
123 /*
124 * Trivial case: just add to the block.
125 */
126 memcpy(s->block + s->blkused, q, len);
127 s->blkused += len;
128 } else {
129 /*
130 * We must complete and process at least one block.
131 */
132 while (s->blkused + len >= 64) {
133 memcpy(s->block + s->blkused, q, 64 - s->blkused);
134 q += 64 - s->blkused;
135 len -= 64 - s->blkused;
136 /* Now process the block. Gather bytes big-endian into words */
137 for (i = 0; i < 16; i++) {
138 wordblock[i] =
139 (((uint32) s->block[i * 4 + 0]) << 24) |
140 (((uint32) s->block[i * 4 + 1]) << 16) |
141 (((uint32) s->block[i * 4 + 2]) << 8) |
142 (((uint32) s->block[i * 4 + 3]) << 0);
143 }
144 SHATransform(s->h, wordblock);
145 s->blkused = 0;
146 }
147 memcpy(s->block, q, len);
148 s->blkused = len;
149 }
150}
151
152void SHA_Final(SHA_State * s, unsigned char *output)
153{
154 int i;
155 int pad;
156 unsigned char c[64];
157 uint32 lenhi, lenlo;
158
159 if (s->blkused >= 56)
160 pad = 56 + 64 - s->blkused;
161 else
162 pad = 56 - s->blkused;
163
164 lenhi = (s->lenhi << 3) | (s->lenlo >> (32 - 3));
165 lenlo = (s->lenlo << 3);
166
167 memset(c, 0, pad);
168 c[0] = 0x80;
169 SHA_Bytes(s, &c, pad);
170
171 c[0] = (unsigned char)((lenhi >> 24) & 0xFF);
172 c[1] = (unsigned char)((lenhi >> 16) & 0xFF);
173 c[2] = (unsigned char)((lenhi >> 8) & 0xFF);
174 c[3] = (unsigned char)((lenhi >> 0) & 0xFF);
175 c[4] = (unsigned char)((lenlo >> 24) & 0xFF);
176 c[5] = (unsigned char)((lenlo >> 16) & 0xFF);
177 c[6] = (unsigned char)((lenlo >> 8) & 0xFF);
178 c[7] = (unsigned char)((lenlo >> 0) & 0xFF);
179
180 SHA_Bytes(s, &c, 8);
181
182 for (i = 0; i < 5; i++) {
183 output[i * 4] = (unsigned char)((s->h[i] >> 24) & 0xFF);
184 output[i * 4 + 1] = (unsigned char)((s->h[i] >> 16) & 0xFF);
185 output[i * 4 + 2] = (unsigned char)((s->h[i] >> 8) & 0xFF);
186 output[i * 4 + 3] = (unsigned char)((s->h[i]) & 0xFF);
187 }
188}
189
190void SHA_Simple(const void *p, int len, unsigned char *output)
191{
192 SHA_State s;
193
194 SHA_Init(&s);
195 SHA_Bytes(&s, p, len);
196 SHA_Final(&s, output);
197}
198
199/* ----------------------------------------------------------------------
200 * The random number generator.
201 */
202
203struct random_state {
204 unsigned char seedbuf[40];
205 unsigned char databuf[20];
206 int pos;
207};
208
209random_state *random_new(const char *seed, int len)
210{
211 random_state *state;
212
213 state = snew(random_state);
214
215 SHA_Simple(seed, len, state->seedbuf);
216 SHA_Simple(state->seedbuf, 20, state->seedbuf + 20);
217 SHA_Simple(state->seedbuf, 40, state->databuf);
218 state->pos = 0;
219
220 return state;
221}
222
223random_state *random_copy(random_state *tocopy)
224{
225 random_state *result;
226 result = snew(random_state);
227 memcpy(result->seedbuf, tocopy->seedbuf, sizeof(result->seedbuf));
228 memcpy(result->databuf, tocopy->databuf, sizeof(result->databuf));
229 result->pos = tocopy->pos;
230 return result;
231}
232
233unsigned long random_bits(random_state *state, int bits)
234{
235 unsigned long ret = 0;
236 int n;
237
238 for (n = 0; n < bits; n += 8) {
239 if (state->pos >= 20) {
240 int i;
241
242 for (i = 0; i < 20; i++) {
243 if (state->seedbuf[i] != 0xFF) {
244 state->seedbuf[i]++;
245 break;
246 } else
247 state->seedbuf[i] = 0;
248 }
249 SHA_Simple(state->seedbuf, 40, state->databuf);
250 state->pos = 0;
251 }
252 ret = (ret << 8) | state->databuf[state->pos++];
253 }
254
255 /*
256 * `(1 << bits) - 1' is not good enough, since if bits==32 on a
257 * 32-bit machine, behaviour is undefined and Intel has a nasty
258 * habit of shifting left by zero instead. We'll shift by
259 * bits-1 and then separately shift by one.
260 */
261 ret &= (1 << (bits-1)) * 2 - 1;
262 return ret;
263}
264
265unsigned long random_upto(random_state *state, unsigned long limit)
266{
267 int bits = 0;
268 unsigned long max, divisor, data;
269
270 while ((limit >> bits) != 0)
271 bits++;
272
273 bits += 3;
274 //assert(bits < 32);
275
276 max = 1L << bits;
277 divisor = max / limit;
278 max = limit * divisor;
279
280 do {
281 data = random_bits(state, bits);
282 } while (data >= max);
283
284 return data / divisor;
285}
286
287void random_free(random_state *state)
288{
289 sfree(state);
290}
291
292char *random_state_encode(random_state *state)
293{
294 char retbuf[256];
295 int len = 0, i;
296
297 for (i = 0; i < lenof(state->seedbuf); i++)
298 len += sprintf(retbuf+len, "%02x", state->seedbuf[i]);
299 for (i = 0; i < lenof(state->databuf); i++)
300 len += sprintf(retbuf+len, "%02x", state->databuf[i]);
301 len += sprintf(retbuf+len, "%02x", state->pos);
302
303 return dupstr(retbuf);
304}
305
306random_state *random_state_decode(const char *input)
307{
308 random_state *state;
309 int pos, byte, digits;
310
311 state = snew(random_state);
312
313 memset(state->seedbuf, 0, sizeof(state->seedbuf));
314 memset(state->databuf, 0, sizeof(state->databuf));
315 state->pos = 0;
316
317 byte = digits = 0;
318 pos = 0;
319 while (*input) {
320 int v = *input++;
321
322 if (v >= '0' && v <= '9')
323 v = v - '0';
324 else if (v >= 'A' && v <= 'F')
325 v = v - 'A' + 10;
326 else if (v >= 'a' && v <= 'f')
327 v = v - 'a' + 10;
328 else
329 v = 0;
330
331 byte = (byte << 4) | v;
332 digits++;
333
334 if (digits == 2) {
335 /*
336 * We have a byte. Put it somewhere.
337 */
338 if (pos < lenof(state->seedbuf))
339 state->seedbuf[pos++] = byte;
340 else if (pos < lenof(state->seedbuf) + lenof(state->databuf))
341 state->databuf[pos++ - lenof(state->seedbuf)] = byte;
342 else if (pos == lenof(state->seedbuf) + lenof(state->databuf) &&
343 byte <= lenof(state->databuf))
344 state->pos = byte;
345 byte = digits = 0;
346 }
347 }
348
349 return state;
350}
diff --git a/apps/plugins/puzzles/range.R b/apps/plugins/puzzles/range.R
new file mode 100644
index 0000000000..f1256efd1e
--- /dev/null
+++ b/apps/plugins/puzzles/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/range.c b/apps/plugins/puzzles/range.c
new file mode 100644
index 0000000000..e1067cbcc5
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/rbassert.h b/apps/plugins/puzzles/rbassert.h
new file mode 100644
index 0000000000..f3fef84351
--- /dev/null
+++ b/apps/plugins/puzzles/rbassert.h
@@ -0,0 +1,13 @@
1/*
2 copied from firmware/assert.h
3*/
4
5#undef assert
6
7#ifdef NDEBUG /* required by ANSI standard */
8#define assert(p) ((void)0)
9#else
10
11#define assert(e) ((e) ? (void)0 : fatal("assertion failed %s:%d", __FILE__, __LINE__))
12
13#endif /* NDEBUG */
diff --git a/apps/plugins/puzzles/rbcompat.h b/apps/plugins/puzzles/rbcompat.h
new file mode 100644
index 0000000000..148aaef073
--- /dev/null
+++ b/apps/plugins/puzzles/rbcompat.h
@@ -0,0 +1,62 @@
1#include "plugin.h"
2#include "rbassert.h"
3
4int sprintf_wrapper(char *str, const char *fmt, ...);
5char *getenv_wrapper(const char *c);
6int puts_wrapper(const char *s);
7double sin_wrapper(double rads);
8double cos_wrapper(double rads);
9int vsprintf_wrapper(char *s, const char *fmt, va_list ap);
10float fabs_wrapper(float n);
11float floor_wrapper(float n);
12
13float atan_wrapper(float x);
14float atan2_wrapper(float y, float x);
15float sqrt_wrapper(float x);
16long strtol_wrapper(const char *nptr, char **endptr, int base);
17int64_t strtoq_wrapper(const char *nptr, char **endptr, int base);
18uint64_t strtouq_wrapper(const char *nptr, char **endptr, int base);
19float pow_wrapper(float x, float y);
20float ceil_wrapper(float x);
21
22size_t strspn_wrapper(const char *s1, const char *s2);
23size_t strcspn_wrapper(const char *s1, const char *s2);
24int sscanf_wrapper(const char *ibuf, const char *fmt, ...);
25double atof_wrapper(const char *s);
26double acos_wrapper(double x);
27
28#define acos acos_wrapper
29#define atan atan_wrapper
30#define atan2 atan2_wrapper
31#define atof atof_wrapper
32#define atoi rb->atoi
33#define atol atoi
34#define calloc tlsf_calloc
35#define ceil ceil_wrapper
36#define cos cos_wrapper
37#define fabs fabs_wrapper
38#define floor floor_wrapper
39#define free tlsf_free
40#define getenv getenv_wrapper
41#define malloc tlsf_malloc
42#define memchr rb->memchr
43#define pow pow_wrapper
44#define printf LOGF
45#define puts puts_wrapper
46#define qsort rb->qsort
47#define realloc tlsf_realloc
48#define sin sin_wrapper
49#define sprintf sprintf_wrapper
50#define sqrt sqrt_wrapper
51#define sscanf sscanf_wrapper
52#define strcat rb->strcat
53#define strchr rb->strchr
54#define strcmp rb->strcmp
55#define strcpy rb->strcpy
56#define strcspn strcspn_wrapper
57#define strlen rb->strlen
58#define strspn strspn_wrapper
59#define strtol strtol_wrapper
60#define strtoq strtoq_wrapper
61#define strtouq strtouq_wrapper
62#define vsprintf vsprintf_wrapper
diff --git a/apps/plugins/puzzles/rbwrappers.c b/apps/plugins/puzzles/rbwrappers.c
new file mode 100644
index 0000000000..8eca1909c3
--- /dev/null
+++ b/apps/plugins/puzzles/rbwrappers.c
@@ -0,0 +1,2378 @@
1#include <puzzles.h>
2#include "fixedpoint.h"
3
4int sprintf_wrapper(char *str, const char *fmt, ...)
5{
6 va_list ap;
7 va_start(ap, fmt);
8 int ret = rb->vsnprintf(str, 9999, fmt, ap);
9 va_end(ap);
10 return ret;
11}
12
13char *getenv_wrapper(const char *c)
14{
15 return NULL;
16}
17
18int puts_wrapper(const char *s)
19{
20 LOGF("%s", s);
21 return 0;
22}
23
24/* fixed-point wrappers */
25double sin_wrapper(double rads)
26{
27 int degs = rads * 180/PI;
28 long fixed = fp14_sin(degs);
29 return fixed / (16384.0);
30}
31
32double cos_wrapper(double rads)
33{
34 int degs = rads * 180/PI;
35 long fixed = fp14_cos(degs);
36 return fixed / (16384.0);
37}
38
39int vsprintf_wrapper(char *s, const char *fmt, va_list ap)
40{
41 return rb->vsnprintf(s, 9999, fmt, ap);
42
43}
44
45/* Absolute value, simple calculus */
46float fabs_wrapper(float x)
47{
48 return (x < 0.0f) ? -x : x;
49}
50
51float floor_wrapper(float n)
52{
53 if(n < 0.0f)
54 return ((int)n - 1);
55 else
56 return (int)n;
57}
58
59/* Natural logarithm.
60 Taken from glibc-2.8 */
61static const float
62ln2_hi = 6.9313812256e-01, /* 0x3f317180 */
63ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */
64two25 = 3.355443200e+07, /* 0x4c000000 */
65Lg1 = 6.6666668653e-01, /* 3F2AAAAB */
66Lg2 = 4.0000000596e-01, /* 3ECCCCCD */
67Lg3 = 2.8571429849e-01, /* 3E924925 */
68Lg4 = 2.2222198546e-01, /* 3E638E29 */
69Lg5 = 1.8183572590e-01, /* 3E3A3325 */
70Lg6 = 1.5313838422e-01, /* 3E1CD04F */
71Lg7 = 1.4798198640e-01; /* 3E178897 */
72
73static const float zero = 0.0;
74
75/* A union which permits us to convert between a float and a 32 bit
76 int. */
77
78typedef union
79{
80 float value;
81 uint32_t word;
82} ieee_float_shape_type;
83
84/* Get a 32 bit int from a float. */
85
86#define GET_FLOAT_WORD(i,d) \
87do { \
88 ieee_float_shape_type gf_u; \
89 gf_u.value = (d); \
90 (i) = gf_u.word; \
91} while (0)
92
93/* Set a float from a 32 bit int. */
94
95#define SET_FLOAT_WORD(d,i) \
96do { \
97 ieee_float_shape_type sf_u; \
98 sf_u.word = (i); \
99 (d) = sf_u.value; \
100} while (0)
101
102#ifdef ROCKBOX_LITTLE_ENDIAN
103#define __HI(x) *(1+(int*)&x)
104#define __LO(x) *(int*)&x
105#define __HIp(x) *(1+(int*)x)
106#define __LOp(x) *(int*)x
107#else
108#define __HI(x) *(int*)&x
109#define __LO(x) *(1+(int*)&x)
110#define __HIp(x) *(int*)x
111#define __LOp(x) *(1+(int*)x)
112#endif
113
114static float rb_log(float x)
115{
116 float hfsq, f, s, z, R, w, t1, t2, dk;
117 int32_t k, ix, i, j;
118
119 GET_FLOAT_WORD(ix,x);
120
121 k=0;
122 if (ix < 0x00800000) { /* x < 2**-126 */
123 if ((ix&0x7fffffff)==0)
124 return -two25/(x-x); /* log(+-0)=-inf */
125 if (ix<0) return (x-x)/(x-x); /* log(-#) = NaN */
126 k -= 25; x *= two25; /* subnormal number, scale up x */
127 GET_FLOAT_WORD(ix,x);
128 }
129 if (ix >= 0x7f800000) return x+x;
130 k += (ix>>23)-127;
131 ix &= 0x007fffff;
132 i = (ix+(0x95f64<<3))&0x800000;
133 SET_FLOAT_WORD(x,ix|(i^0x3f800000)); /* normalize x or x/2 */
134 k += (i>>23);
135 f = x-(float)1.0;
136 if((0x007fffff&(15+ix))<16) { /* |f| < 2**-20 */
137 if(f==zero) {
138 if(k==0)
139 return zero;
140 else
141 {
142 dk=(float)k;
143 return dk*ln2_hi+dk*ln2_lo;
144 }
145 }
146 R = f*f*((float)0.5-(float)0.33333333333333333*f);
147 if(k==0)
148 return f-R;
149 else
150 {
151 dk=(float)k;
152 return dk*ln2_hi-((R-dk*ln2_lo)-f);
153 }
154 }
155 s = f/((float)2.0+f);
156 dk = (float)k;
157 z = s*s;
158 i = ix-(0x6147a<<3);
159 w = z*z;
160 j = (0x6b851<<3)-ix;
161 t1= w*(Lg2+w*(Lg4+w*Lg6));
162 t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7)));
163 i |= j;
164 R = t2+t1;
165 if(i>0) {
166 hfsq=(float)0.5*f*f;
167 if(k==0)
168 return f-(hfsq-s*(hfsq+R));
169 else
170 return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f);
171 } else {
172 if(k==0)
173 return f-s*(f-R);
174 else
175 return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f);
176 }
177}
178
179union ieee754_double
180 {
181 double d;
182
183 /* This is the IEEE 754 double-precision format. */
184 struct
185 {
186#ifdef ROCKBOX_BIG_ENDIAN
187 unsigned int negative:1;
188 unsigned int exponent:11;
189 /* Together these comprise the mantissa. */
190 unsigned int mantissa0:20;
191 unsigned int mantissa1:32;
192#else /* ROCKBOX_LITTLE_ENDIAN */
193 /* Together these comprise the mantissa. */
194 unsigned int mantissa1:32;
195 unsigned int mantissa0:20;
196 unsigned int exponent:11;
197 unsigned int negative:1;
198#endif /* ROCKBOX_LITTLE_ENDIAN */
199 } ieee;
200
201 /* This format makes it easier to see if a NaN is a signalling NaN. */
202 struct
203 {
204#ifdef ROCKBOX_BIG_ENDIAN
205 unsigned int negative:1;
206 unsigned int exponent:11;
207 unsigned int quiet_nan:1;
208 /* Together these comprise the mantissa. */
209 unsigned int mantissa0:19;
210 unsigned int mantissa1:32;
211#else /* ROCKBOX_LITTLE_ENDIAN */
212 /* Together these comprise the mantissa. */
213 unsigned int mantissa1:32;
214 unsigned int mantissa0:19;
215 unsigned int quiet_nan:1;
216 unsigned int exponent:11;
217 unsigned int negative:1;
218#endif /* ROCKBOX_LITTLE_ENDIAN */
219 } ieee_nan;
220 };
221
222static const volatile float TWOM100 = 7.88860905e-31;
223static const volatile float TWO127 = 1.7014118346e+38;
224
225/* Exponential function,
226 taken from glibc-2.8
227 As it uses double values and udefines some symbols,
228 it was moved to the end of the source code */
229
230#define W52 (2.22044605e-16)
231#define W55 (2.77555756e-17)
232#define W58 (3.46944695e-18)
233#define W59 (1.73472348e-18)
234#define W60 (8.67361738e-19)
235const float __exp_deltatable[178] = {
236 0*W60, 16558714*W60, -10672149*W59, 1441652*W60,
237 -15787963*W55, 462888*W60, 7291806*W60, 1698880*W60,
238 -14375103*W58, -2021016*W60, 728829*W60, -3759654*W60,
239 3202123*W60, -10916019*W58, -251570*W60, -1043086*W60,
240 8207536*W60, -409964*W60, -5993931*W60, -475500*W60,
241 2237522*W60, 324170*W60, -244117*W60, 32077*W60,
242 123907*W60, -1019734*W60, -143*W60, 813077*W60,
243 743345*W60, 462461*W60, 629794*W60, 2125066*W60,
244 -2339121*W60, -337951*W60, 9922067*W60, -648704*W60,
245 149407*W60, -2687209*W60, -631608*W60, 2128280*W60,
246 -4882082*W60, 2001360*W60, 175074*W60, 2923216*W60,
247 -538947*W60, -1212193*W60, -1920926*W60, -1080577*W60,
248 3690196*W60, 2643367*W60, 2911937*W60, 671455*W60,
249 -1128674*W60, 593282*W60, -5219347*W60, -1941490*W60,
250 11007953*W60, 239609*W60, -2969658*W60, -1183650*W60,
251 942998*W60, 699063*W60, 450569*W60, -329250*W60,
252 -7257875*W60, -312436*W60, 51626*W60, 555877*W60,
253 -641761*W60, 1565666*W60, 884327*W60, -10960035*W60,
254 -2004679*W60, -995793*W60, -2229051*W60, -146179*W60,
255 -510327*W60, 1453482*W60, -3778852*W60, -2238056*W60,
256 -4895983*W60, 3398883*W60, -252738*W60, 1230155*W60,
257 346918*W60, 1109352*W60, 268941*W60, -2930483*W60,
258 -1036263*W60, -1159280*W60, 1328176*W60, 2937642*W60,
259 -9371420*W60, -6902650*W60, -1419134*W60, 1442904*W60,
260 -1319056*W60, -16369*W60, 696555*W60, -279987*W60,
261 -7919763*W60, 252741*W60, 459711*W60, -1709645*W60,
262 354913*W60, 6025867*W60, -421460*W60, -853103*W60,
263 -338649*W60, 962151*W60, 955965*W60, 784419*W60,
264 -3633653*W60, 2277133*W60, -8847927*W52, 1223028*W60,
265 5907079*W60, 623167*W60, 5142888*W60, 2599099*W60,
266 1214280*W60, 4870359*W60, 593349*W60, -57705*W60,
267 7761209*W60, -5564097*W60, 2051261*W60, 6216869*W60,
268 4692163*W60, 601691*W60, -5264906*W60, 1077872*W60,
269 -3205949*W60, 1833082*W60, 2081746*W60, -987363*W60,
270 -1049535*W60, 2015244*W60, 874230*W60, 2168259*W60,
271 -1740124*W60, -10068269*W60, -18242*W60, -3013583*W60,
272 580601*W60, -2547161*W60, -535689*W60, 2220815*W60,
273 1285067*W60, 2806933*W60, -983086*W60, -1729097*W60,
274 -1162985*W60, -2561904*W60, 801988*W60, 244351*W60,
275 1441893*W60, -7517981*W60, 271781*W60, -15021588*W60,
276 -2341588*W60, -919198*W60, 1642232*W60, 4771771*W60,
277 -1220099*W60, -3062372*W60, 628624*W60, 1278114*W60,
278 13083513*W60, -10521925*W60, 3180310*W60, -1659307*W60,
279 3543773*W60, 2501203*W60, 4151*W60, -340748*W60,
280 -2285625*W60, 2495202*W60
281};
282
283const double __exp_atable[355] /* __attribute__((mode(DF))) */ = {
284 0.707722561055888932371, /* 0x0.b52d4e46605c27ffd */
285 0.709106182438804188967, /* 0x0.b587fb96f75097ffb */
286 0.710492508843861281234, /* 0x0.b5e2d649899167ffd */
287 0.711881545564593931623, /* 0x0.b63dde74d36bdfffe */
288 0.713273297897442870573, /* 0x0.b699142f945f87ffc */
289 0.714667771153751463236, /* 0x0.b6f477909c4ea0001 */
290 0.716064970655995725059, /* 0x0.b75008aec758f8004 */
291 0.717464901723956938193, /* 0x0.b7abc7a0eea7e0002 */
292 0.718867569715736398602, /* 0x0.b807b47e1586c7ff8 */
293 0.720272979947266023271, /* 0x0.b863cf5d10e380003 */
294 0.721681137825144314297, /* 0x0.b8c01855195c37ffb */
295 0.723092048691992950199, /* 0x0.b91c8f7d213740004 */
296 0.724505717938892290800, /* 0x0.b97934ec5002d0007 */
297 0.725922150953176470431, /* 0x0.b9d608b9c92ea7ffc */
298 0.727341353138962865022, /* 0x0.ba330afcc29e98003 */
299 0.728763329918453162104, /* 0x0.ba903bcc8618b7ffc */
300 0.730188086709957051568, /* 0x0.baed9b40591ba0000 */
301 0.731615628948127705309, /* 0x0.bb4b296f931e30002 */
302 0.733045962086486091436, /* 0x0.bba8e671a05617ff9 */
303 0.734479091556371366251, /* 0x0.bc06d25dd49568001 */
304 0.735915022857225542529, /* 0x0.bc64ed4bce8f6fff9 */
305 0.737353761441304711410, /* 0x0.bcc33752f915d7ff9 */
306 0.738795312814142124419, /* 0x0.bd21b08af98e78005 */
307 0.740239682467211168593, /* 0x0.bd80590b65e9a8000 */
308 0.741686875913991849885, /* 0x0.bddf30ebec4a10000 */
309 0.743136898669507939299, /* 0x0.be3e38443c84e0007 */
310 0.744589756269486091620, /* 0x0.be9d6f2c1d32a0002 */
311 0.746045454254026796384, /* 0x0.befcd5bb59baf8004 */
312 0.747503998175051087583, /* 0x0.bf5c6c09ca84c0003 */
313 0.748965393601880857739, /* 0x0.bfbc322f5b18b7ff8 */
314 0.750429646104262104698, /* 0x0.c01c2843f776fffff */
315 0.751896761271877989160, /* 0x0.c07c4e5fa18b88002 */
316 0.753366744698445112140, /* 0x0.c0dca49a5fb18fffd */
317 0.754839601988627206827, /* 0x0.c13d2b0c444db0005 */
318 0.756315338768691947122, /* 0x0.c19de1cd798578006 */
319 0.757793960659406629066, /* 0x0.c1fec8f623723fffd */
320 0.759275473314173443536, /* 0x0.c25fe09e8a0f47ff8 */
321 0.760759882363831851927, /* 0x0.c2c128dedc88f8000 */
322 0.762247193485956486805, /* 0x0.c322a1cf7d6e7fffa */
323 0.763737412354726363781, /* 0x0.c3844b88cb9347ffc */
324 0.765230544649828092739, /* 0x0.c3e626232bd8f7ffc */
325 0.766726596071518051729, /* 0x0.c44831b719bf18002 */
326 0.768225572321911687194, /* 0x0.c4aa6e5d12d078001 */
327 0.769727479119219348810, /* 0x0.c50cdc2da64a37ffb */
328 0.771232322196981678892, /* 0x0.c56f7b41744490001 */
329 0.772740107296721268087, /* 0x0.c5d24bb1259e70004 */
330 0.774250840160724651565, /* 0x0.c6354d95640dd0007 */
331 0.775764526565368872643, /* 0x0.c6988106fec447fff */
332 0.777281172269557396602, /* 0x0.c6fbe61eb1bd0ffff */
333 0.778800783068235302750, /* 0x0.c75f7cf560942fffc */
334 0.780323364758801041312, /* 0x0.c7c345a3f1983fffe */
335 0.781848923151573727006, /* 0x0.c8274043594cb0002 */
336 0.783377464064598849602, /* 0x0.c88b6cec94b3b7ff9 */
337 0.784908993312207869935, /* 0x0.c8efcbb89cba27ffe */
338 0.786443516765346961618, /* 0x0.c9545cc0a88c70003 */
339 0.787981040257604625744, /* 0x0.c9b9201dc643bfffa */
340 0.789521569657452682047, /* 0x0.ca1e15e92a5410007 */
341 0.791065110849462849192, /* 0x0.ca833e3c1ae510005 */
342 0.792611669712891875319, /* 0x0.cae8992fd84667ffd */
343 0.794161252150049179450, /* 0x0.cb4e26ddbc207fff8 */
344 0.795713864077794763584, /* 0x0.cbb3e75f301b60003 */
345 0.797269511407239561694, /* 0x0.cc19dacd978cd8002 */
346 0.798828200086368567220, /* 0x0.cc8001427e55d7ffb */
347 0.800389937624300440456, /* 0x0.cce65ade24d360006 */
348 0.801954725261124767840, /* 0x0.cd4ce7a5de839fffb */
349 0.803522573691593189330, /* 0x0.cdb3a7c79a678fffd */
350 0.805093487311204114563, /* 0x0.ce1a9b563965ffffc */
351 0.806667472122675088819, /* 0x0.ce81c26b838db8000 */
352 0.808244534127439906441, /* 0x0.cee91d213f8428002 */
353 0.809824679342317166307, /* 0x0.cf50ab9144d92fff9 */
354 0.811407913793616542005, /* 0x0.cfb86dd5758c2ffff */
355 0.812994243520784198882, /* 0x0.d0206407c20e20005 */
356 0.814583674571603966162, /* 0x0.d0888e4223facfff9 */
357 0.816176213022088536960, /* 0x0.d0f0ec9eb3f7c8002 */
358 0.817771864936188586101, /* 0x0.d1597f377d6768002 */
359 0.819370636400374108252, /* 0x0.d1c24626a46eafff8 */
360 0.820972533518165570298, /* 0x0.d22b41865ff1e7ff9 */
361 0.822577562404315121269, /* 0x0.d2947170f32ec7ff9 */
362 0.824185729164559344159, /* 0x0.d2fdd60097795fff8 */
363 0.825797039949601741075, /* 0x0.d3676f4fb796d0001 */
364 0.827411500902565544264, /* 0x0.d3d13d78b5f68fffb */
365 0.829029118181348834154, /* 0x0.d43b40960546d8001 */
366 0.830649897953322891022, /* 0x0.d4a578c222a058000 */
367 0.832273846408250750368, /* 0x0.d50fe617a3ba78005 */
368 0.833900969738858188772, /* 0x0.d57a88b1218e90002 */
369 0.835531274148056613016, /* 0x0.d5e560a94048f8006 */
370 0.837164765846411529371, /* 0x0.d6506e1aac8078003 */
371 0.838801451086016225394, /* 0x0.d6bbb1204074e0001 */
372 0.840441336100884561780, /* 0x0.d72729d4c28518004 */
373 0.842084427144139224814, /* 0x0.d792d8530e12b0001 */
374 0.843730730487052604790, /* 0x0.d7febcb61273e7fff */
375 0.845380252404570153833, /* 0x0.d86ad718c308dfff9 */
376 0.847032999194574087728, /* 0x0.d8d727962c69d7fff */
377 0.848688977161248581090, /* 0x0.d943ae49621ce7ffb */
378 0.850348192619261200615, /* 0x0.d9b06b4d832ef8005 */
379 0.852010651900976245816, /* 0x0.da1d5ebdc22220005 */
380 0.853676361342631029337, /* 0x0.da8a88b555baa0006 */
381 0.855345327311054837175, /* 0x0.daf7e94f965f98004 */
382 0.857017556155879489641, /* 0x0.db6580a7c98f7fff8 */
383 0.858693054267390953857, /* 0x0.dbd34ed9617befff8 */
384 0.860371828028939855647, /* 0x0.dc4153ffc8b65fff9 */
385 0.862053883854957292436, /* 0x0.dcaf90368bfca8004 */
386 0.863739228154875360306, /* 0x0.dd1e0399328d87ffe */
387 0.865427867361348468455, /* 0x0.dd8cae435d303fff9 */
388 0.867119807911702289458, /* 0x0.ddfb9050b1cee8006 */
389 0.868815056264353846599, /* 0x0.de6aa9dced8448001 */
390 0.870513618890481399881, /* 0x0.ded9fb03db7320006 */
391 0.872215502247877139094, /* 0x0.df4983e1380657ff8 */
392 0.873920712852848668986, /* 0x0.dfb94490ffff77ffd */
393 0.875629257204025623884, /* 0x0.e0293d2f1cb01fff9 */
394 0.877341141814212965880, /* 0x0.e0996dd786fff0007 */
395 0.879056373217612985183, /* 0x0.e109d6a64f5d57ffc */
396 0.880774957955916648615, /* 0x0.e17a77b78e72a7ffe */
397 0.882496902590150900078, /* 0x0.e1eb5127722cc7ff8 */
398 0.884222213673356738383, /* 0x0.e25c63121fb0c8006 */
399 0.885950897802399772740, /* 0x0.e2cdad93ec5340003 */
400 0.887682961567391237685, /* 0x0.e33f30c925fb97ffb */
401 0.889418411575228162725, /* 0x0.e3b0ecce2d05ffff9 */
402 0.891157254447957902797, /* 0x0.e422e1bf727718006 */
403 0.892899496816652704641, /* 0x0.e4950fb9713fc7ffe */
404 0.894645145323828439008, /* 0x0.e50776d8b0e60fff8 */
405 0.896394206626591749641, /* 0x0.e57a1739c8fadfffc */
406 0.898146687421414902124, /* 0x0.e5ecf0f97c5798007 */
407 0.899902594367530173098, /* 0x0.e660043464e378005 */
408 0.901661934163603406867, /* 0x0.e6d3510747e150006 */
409 0.903424713533971135418, /* 0x0.e746d78f06cd97ffd */
410 0.905190939194458810123, /* 0x0.e7ba97e879c91fffc */
411 0.906960617885092856864, /* 0x0.e82e92309390b0007 */
412 0.908733756358986566306, /* 0x0.e8a2c6845544afffa */
413 0.910510361377119825629, /* 0x0.e9173500c8abc7ff8 */
414 0.912290439722343249336, /* 0x0.e98bddc30f98b0002 */
415 0.914073998177417412765, /* 0x0.ea00c0e84bc4c7fff */
416 0.915861043547953501680, /* 0x0.ea75de8db8094fffe */
417 0.917651582652244779397, /* 0x0.eaeb36d09d3137ffe */
418 0.919445622318405764159, /* 0x0.eb60c9ce4ed3dffff */
419 0.921243169397334638073, /* 0x0.ebd697a43995b0007 */
420 0.923044230737526172328, /* 0x0.ec4ca06fc7768fffa */
421 0.924848813220121135342, /* 0x0.ecc2e44e865b6fffb */
422 0.926656923710931002014, /* 0x0.ed39635df34e70006 */
423 0.928468569126343790092, /* 0x0.edb01dbbc2f5b7ffa */
424 0.930283756368834757725, /* 0x0.ee2713859aab57ffa */
425 0.932102492359406786818, /* 0x0.ee9e44d9342870004 */
426 0.933924784042873379360, /* 0x0.ef15b1d4635438005 */
427 0.935750638358567643520, /* 0x0.ef8d5a94f60f50007 */
428 0.937580062297704630580, /* 0x0.f0053f38f345cffff */
429 0.939413062815381727516, /* 0x0.f07d5fde3a2d98001 */
430 0.941249646905368053689, /* 0x0.f0f5bca2d481a8004 */
431 0.943089821583810716806, /* 0x0.f16e55a4e497d7ffe */
432 0.944933593864477061592, /* 0x0.f1e72b028a2827ffb */
433 0.946780970781518460559, /* 0x0.f2603cd9fb5430001 */
434 0.948631959382661205081, /* 0x0.f2d98b497d2a87ff9 */
435 0.950486566729423554277, /* 0x0.f353166f63e3dffff */
436 0.952344799896018723290, /* 0x0.f3ccde6a11ae37ffe */
437 0.954206665969085765512, /* 0x0.f446e357f66120000 */
438 0.956072172053890279009, /* 0x0.f4c12557964f0fff9 */
439 0.957941325265908139014, /* 0x0.f53ba48781046fffb */
440 0.959814132734539637840, /* 0x0.f5b66106555d07ffa */
441 0.961690601603558903308, /* 0x0.f6315af2c2027fffc */
442 0.963570739036113010927, /* 0x0.f6ac926b8aeb80004 */
443 0.965454552202857141381, /* 0x0.f728078f7c5008002 */
444 0.967342048278315158608, /* 0x0.f7a3ba7d66a908001 */
445 0.969233234469444204768, /* 0x0.f81fab543e1897ffb */
446 0.971128118008140250896, /* 0x0.f89bda33122c78007 */
447 0.973026706099345495256, /* 0x0.f9184738d4cf97ff8 */
448 0.974929006031422851235, /* 0x0.f994f284d3a5c0008 */
449 0.976835024947348973265, /* 0x0.fa11dc35bc7820002 */
450 0.978744770239899142285, /* 0x0.fa8f046b4fb7f8007 */
451 0.980658249138918636210, /* 0x0.fb0c6b449ab1cfff9 */
452 0.982575468959622777535, /* 0x0.fb8a10e1088fb7ffa */
453 0.984496437054508843888, /* 0x0.fc07f5602d79afffc */
454 0.986421160608523028820, /* 0x0.fc8618e0e55e47ffb */
455 0.988349647107594098099, /* 0x0.fd047b83571b1fffa */
456 0.990281903873210800357, /* 0x0.fd831d66f4c018002 */
457 0.992217938695037382475, /* 0x0.fe01fead3320bfff8 */
458 0.994157757657894713987, /* 0x0.fe811f703491e8006 */
459 0.996101369488558541238, /* 0x0.ff007fd5744490005 */
460 0.998048781093141101932, /* 0x0.ff801ffa9b9280007 */
461 1.000000000000000000000, /* 0x1.00000000000000000 */
462 1.001955033605393285965, /* 0x1.0080200565d29ffff */
463 1.003913889319761887310, /* 0x1.0100802aa0e80fff0 */
464 1.005876574715736104818, /* 0x1.01812090377240007 */
465 1.007843096764807100351, /* 0x1.020201541aad7fff6 */
466 1.009813464316352327214, /* 0x1.0283229c4c9820007 */
467 1.011787683565730677817, /* 0x1.030484836910a000e */
468 1.013765762469146736174, /* 0x1.0386272b9c077fffe */
469 1.015747708536026694351, /* 0x1.04080ab526304fff0 */
470 1.017733529475172815584, /* 0x1.048a2f412375ffff0 */
471 1.019723232714418781378, /* 0x1.050c94ef7ad5e000a */
472 1.021716825883923762690, /* 0x1.058f3be0f1c2d0004 */
473 1.023714316605201180057, /* 0x1.06122436442e2000e */
474 1.025715712440059545995, /* 0x1.06954e0fec63afff2 */
475 1.027721021151397406936, /* 0x1.0718b98f41c92fff6 */
476 1.029730250269221158939, /* 0x1.079c66d49bb2ffff1 */
477 1.031743407506447551857, /* 0x1.082056011a9230009 */
478 1.033760500517691527387, /* 0x1.08a487359ebd50002 */
479 1.035781537016238873464, /* 0x1.0928fa93490d4fff3 */
480 1.037806524719013578963, /* 0x1.09adb03b3e5b3000d */
481 1.039835471338248051878, /* 0x1.0a32a84e9e5760004 */
482 1.041868384612101516848, /* 0x1.0ab7e2eea5340ffff */
483 1.043905272300907460835, /* 0x1.0b3d603ca784f0009 */
484 1.045946142174331239262, /* 0x1.0bc3205a042060000 */
485 1.047991002016745332165, /* 0x1.0c4923682a086fffe */
486 1.050039859627715177527, /* 0x1.0ccf698898f3a000d */
487 1.052092722826109660856, /* 0x1.0d55f2dce5d1dfffb */
488 1.054149599440827866881, /* 0x1.0ddcbf86b09a5fff6 */
489 1.056210497317612961855, /* 0x1.0e63cfa7abc97fffd */
490 1.058275424318780855142, /* 0x1.0eeb23619c146fffb */
491 1.060344388322010722446, /* 0x1.0f72bad65714bffff */
492 1.062417397220589476718, /* 0x1.0ffa9627c38d30004 */
493 1.064494458915699715017, /* 0x1.1082b577d0eef0003 */
494 1.066575581342167566880, /* 0x1.110b18e893a90000a */
495 1.068660772440545025953, /* 0x1.1193c09c267610006 */
496 1.070750040138235936705, /* 0x1.121cacb4959befff6 */
497 1.072843392435016474095, /* 0x1.12a5dd543cf36ffff */
498 1.074940837302467588937, /* 0x1.132f529d59552000b */
499 1.077042382749654914030, /* 0x1.13b90cb250d08fff5 */
500 1.079148036789447484528, /* 0x1.14430bb58da3dfff9 */
501 1.081257807444460983297, /* 0x1.14cd4fc984c4a000e */
502 1.083371702785017154417, /* 0x1.1557d910df9c7000e */
503 1.085489730853784307038, /* 0x1.15e2a7ae292d30002 */
504 1.087611899742884524772, /* 0x1.166dbbc422d8c0004 */
505 1.089738217537583819804, /* 0x1.16f9157586772ffff */
506 1.091868692357631731528, /* 0x1.1784b4e533cacfff0 */
507 1.094003332327482702577, /* 0x1.18109a360fc23fff2 */
508 1.096142145591650907149, /* 0x1.189cc58b155a70008 */
509 1.098285140311341168136, /* 0x1.1929370751ea50002 */
510 1.100432324652149906842, /* 0x1.19b5eecdd79cefff0 */
511 1.102583706811727015711, /* 0x1.1a42ed01dbdba000e */
512 1.104739294993289488947, /* 0x1.1ad031c69a2eafff0 */
513 1.106899097422573863281, /* 0x1.1b5dbd3f66e120003 */
514 1.109063122341542140286, /* 0x1.1beb8f8fa8150000b */
515 1.111231377994659874592, /* 0x1.1c79a8dac6ad0fff4 */
516 1.113403872669181282605, /* 0x1.1d0809445a97ffffc */
517 1.115580614653132185460, /* 0x1.1d96b0effc9db000e */
518 1.117761612217810673898, /* 0x1.1e25a001332190000 */
519 1.119946873713312474002, /* 0x1.1eb4d69bdb2a9fff1 */
520 1.122136407473298902480, /* 0x1.1f4454e3bfae00006 */
521 1.124330221845670330058, /* 0x1.1fd41afcbb48bfff8 */
522 1.126528325196519908506, /* 0x1.2064290abc98c0001 */
523 1.128730725913251964394, /* 0x1.20f47f31c9aa7000f */
524 1.130937432396844410880, /* 0x1.21851d95f776dfff0 */
525 1.133148453059692917203, /* 0x1.2216045b6784efffa */
526 1.135363796355857157764, /* 0x1.22a733a6692ae0004 */
527 1.137583470716100553249, /* 0x1.2338ab9b3221a0004 */
528 1.139807484614418608939, /* 0x1.23ca6c5e27aadfff7 */
529 1.142035846532929888057, /* 0x1.245c7613b7f6c0004 */
530 1.144268564977221958089, /* 0x1.24eec8e06b035000c */
531 1.146505648458203463465, /* 0x1.258164e8cea85fff8 */
532 1.148747105501412235671, /* 0x1.26144a5180d380009 */
533 1.150992944689175123667, /* 0x1.26a7793f5de2efffa */
534 1.153243174560058870217, /* 0x1.273af1d712179000d */
535 1.155497803703682491111, /* 0x1.27ceb43d81d42fff1 */
536 1.157756840726344771440, /* 0x1.2862c097a3d29000c */
537 1.160020294239811677834, /* 0x1.28f7170a74cf4fff1 */
538 1.162288172883275239058, /* 0x1.298bb7bb0faed0004 */
539 1.164560485298402170388, /* 0x1.2a20a2ce920dffff4 */
540 1.166837240167474476460, /* 0x1.2ab5d86a4631ffff6 */
541 1.169118446164539637555, /* 0x1.2b4b58b36d5220009 */
542 1.171404112007080167155, /* 0x1.2be123cf786790002 */
543 1.173694246390975415341, /* 0x1.2c7739e3c0aac000d */
544 1.175988858069749065617, /* 0x1.2d0d9b15deb58fff6 */
545 1.178287955789017793514, /* 0x1.2da4478b627040002 */
546 1.180591548323240091978, /* 0x1.2e3b3f69fb794fffc */
547 1.182899644456603782686, /* 0x1.2ed282d76421d0004 */
548 1.185212252993012693694, /* 0x1.2f6a11f96c685fff3 */
549 1.187529382762033236513, /* 0x1.3001ecf60082ffffa */
550 1.189851042595508889847, /* 0x1.309a13f30f28a0004 */
551 1.192177241354644978669, /* 0x1.31328716a758cfff7 */
552 1.194507987909589896687, /* 0x1.31cb4686e1e85fffb */
553 1.196843291137896336843, /* 0x1.32645269dfd04000a */
554 1.199183159977805113226, /* 0x1.32fdaae604c39000f */
555 1.201527603343041317132, /* 0x1.339750219980dfff3 */
556 1.203876630171082595692, /* 0x1.3431424300e480007 */
557 1.206230249419600664189, /* 0x1.34cb8170b3fee000e */
558 1.208588470077065268869, /* 0x1.35660dd14dbd4fffc */
559 1.210951301134513435915, /* 0x1.3600e78b6bdfc0005 */
560 1.213318751604272271958, /* 0x1.369c0ec5c38ebfff2 */
561 1.215690830512196507537, /* 0x1.373783a718d29000f */
562 1.218067546930756250870, /* 0x1.37d3465662f480007 */
563 1.220448909901335365929, /* 0x1.386f56fa770fe0008 */
564 1.222834928513994334780, /* 0x1.390bb5ba5fc540004 */
565 1.225225611877684750397, /* 0x1.39a862bd3c7a8fff3 */
566 1.227620969111500981433, /* 0x1.3a455e2a37bcafffd */
567 1.230021009336254911271, /* 0x1.3ae2a8287dfbefff6 */
568 1.232425741726685064472, /* 0x1.3b8040df76f39fffa */
569 1.234835175450728295084, /* 0x1.3c1e287682e48fff1 */
570 1.237249319699482263931, /* 0x1.3cbc5f151b86bfff8 */
571 1.239668183679933477545, /* 0x1.3d5ae4e2cc0a8000f */
572 1.242091776620540377629, /* 0x1.3df9ba07373bf0006 */
573 1.244520107762172811399, /* 0x1.3e98deaa0d8cafffe */
574 1.246953186383919165383, /* 0x1.3f3852f32973efff0 */
575 1.249391019292643401078, /* 0x1.3fd816ffc72b90001 */
576 1.251833623164381181797, /* 0x1.40782b17863250005 */
577 1.254280999953110153911, /* 0x1.41188f42caf400000 */
578 1.256733161434815393410, /* 0x1.41b943b42945bfffd */
579 1.259190116985283935980, /* 0x1.425a4893e5f10000a */
580 1.261651875958665236542, /* 0x1.42fb9e0a2df4c0009 */
581 1.264118447754797758244, /* 0x1.439d443f608c4fff9 */
582 1.266589841787181258708, /* 0x1.443f3b5bebf850008 */
583 1.269066067469190262045, /* 0x1.44e183883e561fff7 */
584 1.271547134259576328224, /* 0x1.45841cecf7a7a0001 */
585 1.274033051628237434048, /* 0x1.462707b2c43020009 */
586 1.276523829025464573684, /* 0x1.46ca44023aa410007 */
587 1.279019475999373156531, /* 0x1.476dd2045d46ffff0 */
588 1.281520002043128991825, /* 0x1.4811b1e1f1f19000b */
589 1.284025416692967214122, /* 0x1.48b5e3c3edd74fff4 */
590 1.286535729509738823464, /* 0x1.495a67d3613c8fff7 */
591 1.289050950070396384145, /* 0x1.49ff3e396e19d000b */
592 1.291571087985403654081, /* 0x1.4aa4671f5b401fff1 */
593 1.294096152842774794011, /* 0x1.4b49e2ae56d19000d */
594 1.296626154297237043484, /* 0x1.4befb10fd84a3fff4 */
595 1.299161101984141142272, /* 0x1.4c95d26d41d84fff8 */
596 1.301701005575179204100, /* 0x1.4d3c46f01d9f0fff3 */
597 1.304245874766450485904, /* 0x1.4de30ec21097d0003 */
598 1.306795719266019562007, /* 0x1.4e8a2a0ccce3d0002 */
599 1.309350548792467483458, /* 0x1.4f3198fa10346fff5 */
600 1.311910373099227200545, /* 0x1.4fd95bb3be8cffffd */
601 1.314475201942565174546, /* 0x1.50817263bf0e5fffb */
602 1.317045045107389400535, /* 0x1.5129dd3418575000e */
603 1.319619912422941299109, /* 0x1.51d29c4f01c54ffff */
604 1.322199813675649204855, /* 0x1.527bafde83a310009 */
605 1.324784758729532718739, /* 0x1.5325180cfb8b3fffd */
606 1.327374757430096474625, /* 0x1.53ced504b2bd0fff4 */
607 1.329969819671041886272, /* 0x1.5478e6f02775e0001 */
608 1.332569955346704748651, /* 0x1.55234df9d8a59fff8 */
609 1.335175174370685002822, /* 0x1.55ce0a4c5a6a9fff6 */
610 1.337785486688218616860, /* 0x1.56791c1263abefff7 */
611 1.340400902247843806217, /* 0x1.57248376aef21fffa */
612 1.343021431036279800211, /* 0x1.57d040a420c0bfff3 */
613 1.345647083048053138662, /* 0x1.587c53c5a630f0002 */
614 1.348277868295411074918, /* 0x1.5928bd063fd7bfff9 */
615 1.350913796821875845231, /* 0x1.59d57c9110ad60006 */
616 1.353554878672557082439, /* 0x1.5a8292913d68cfffc */
617 1.356201123929036356254, /* 0x1.5b2fff3212db00007 */
618 1.358852542671913132777, /* 0x1.5bddc29edcc06fff3 */
619 1.361509145047255398051, /* 0x1.5c8bdd032ed16000f */
620 1.364170941142184734180, /* 0x1.5d3a4e8a5bf61fff4 */
621 1.366837941171020309735, /* 0x1.5de9176042f1effff */
622 1.369510155261156381121, /* 0x1.5e9837b062f4e0005 */
623 1.372187593620959988833, /* 0x1.5f47afa69436cfff1 */
624 1.374870266463378287715, /* 0x1.5ff77f6eb3f8cfffd */
625 1.377558184010425845733, /* 0x1.60a7a734a9742fff9 */
626 1.380251356531521533853, /* 0x1.6158272490016000c */
627 1.382949794301995272203, /* 0x1.6208ff6a8978a000f */
628 1.385653507605306700170, /* 0x1.62ba3032c0a280004 */
629 1.388362506772382154503, /* 0x1.636bb9a994784000f */
630 1.391076802081129493127, /* 0x1.641d9bfb29a7bfff6 */
631 1.393796403973427855412, /* 0x1.64cfd7545928b0002 */
632 1.396521322756352656542, /* 0x1.65826be167badfff8 */
633 1.399251568859207761660, /* 0x1.663559cf20826000c */
634 1.401987152677323100733, /* 0x1.66e8a14a29486fffc */
635 1.404728084651919228815, /* 0x1.679c427f5a4b6000b */
636 1.407474375243217723560, /* 0x1.68503d9ba0add000f */
637 1.410226034922914983815, /* 0x1.690492cbf6303fff9 */
638 1.412983074197955213304, /* 0x1.69b9423d7b548fff6 */
639};
640
641/* All floating-point numbers can be put in one of these categories. */
642enum
643 {
644 FP_NAN,
645# define FP_NAN FP_NAN
646 FP_INFINITE,
647# define FP_INFINITE FP_INFINITE
648 FP_ZERO,
649# define FP_ZERO FP_ZERO
650 FP_SUBNORMAL,
651# define FP_SUBNORMAL FP_SUBNORMAL
652 FP_NORMAL
653# define FP_NORMAL FP_NORMAL
654 };
655
656
657int
658__fpclassifyf (float x)
659{
660 uint32_t wx;
661 int retval = FP_NORMAL;
662
663 GET_FLOAT_WORD (wx, x);
664 wx &= 0x7fffffff;
665 if (wx == 0)
666 retval = FP_ZERO;
667 else if (wx < 0x800000)
668 retval = FP_SUBNORMAL;
669 else if (wx >= 0x7f800000)
670 retval = wx > 0x7f800000 ? FP_NAN : FP_INFINITE;
671
672 return retval;
673}
674
675
676int
677__isinff (float x)
678{
679 int32_t ix,t;
680 GET_FLOAT_WORD(ix,x);
681 t = ix & 0x7fffffff;
682 t ^= 0x7f800000;
683 t |= -t;
684 return ~(t >> 31) & (ix >> 30);
685}
686
687/* Return nonzero value if arguments are unordered. */
688#define fpclassify(x) \
689 (sizeof (x) == sizeof (float) ? __fpclassifyf (x) : __fpclassifyf (x))
690
691#ifndef isunordered
692#define isunordered(u, v) \
693 (__extension__ \
694 ({ __typeof__(u) __u = (u); __typeof__(v) __v = (v); \
695 fpclassify (__u) == FP_NAN || fpclassify (__v) == FP_NAN; }))
696#endif
697
698/* Return nonzero value if X is less than Y. */
699#ifndef isless
700#define isless(x, y) \
701 (__extension__ \
702 ({ __typeof__(x) __x = (x); __typeof__(y) __y = (y); \
703 !isunordered (__x, __y) && __x < __y; }))
704#endif
705
706/* Return nonzero value if X is greater than Y. */
707#ifndef isgreater
708#define isgreater(x, y) \
709 (__extension__ \
710 ({ __typeof__(x) __x = (x); __typeof__(y) __y = (y); \
711 !isunordered (__x, __y) && __x > __y; }))
712#endif
713
714float rb_exp(float x)
715{
716 static const float himark = 88.72283935546875;
717 static const float lomark = -103.972084045410;
718 /* Check for usual case. */
719 if (isless (x, himark) && isgreater (x, lomark))
720 {
721 static const float THREEp42 = 13194139533312.0;
722 static const float THREEp22 = 12582912.0;
723 /* 1/ln(2). */
724#undef M_1_LN2
725 static const float M_1_LN2 = 1.44269502163f;
726 /* ln(2) */
727#undef M_LN2
728 static const double M_LN2 = .6931471805599452862;
729
730 int tval;
731 double x22, t, result, dx;
732 float n, delta;
733 union ieee754_double ex2_u;
734#ifndef ROCKBOX
735 fenv_t oldenv;
736
737 feholdexcept (&oldenv);
738#endif
739
740#ifdef FE_TONEAREST
741 fesetround (FE_TONEAREST);
742#endif
743
744 /* Calculate n. */
745 n = x * M_1_LN2 + THREEp22;
746 n -= THREEp22;
747 dx = x - n*M_LN2;
748
749 /* Calculate t/512. */
750 t = dx + THREEp42;
751 t -= THREEp42;
752 dx -= t;
753
754 /* Compute tval = t. */
755 tval = (int) (t * 512.0);
756
757 if (t >= 0)
758 delta = - __exp_deltatable[tval];
759 else
760 delta = __exp_deltatable[-tval];
761
762 /* Compute ex2 = 2^n e^(t/512+delta[t]). */
763 ex2_u.d = __exp_atable[tval+177];
764 ex2_u.ieee.exponent += (int) n;
765
766 /* Approximate e^(dx+delta) - 1, using a second-degree polynomial,
767 with maximum error in [-2^-10-2^-28,2^-10+2^-28]
768 less than 5e-11. */
769 x22 = (0.5000000496709180453 * dx + 1.0000001192102037084) * dx + delta;
770
771 /* Return result. */
772#ifndef ROCKBOX
773 fesetenv (&oldenv);
774#endif
775
776 result = x22 * ex2_u.d + ex2_u.d;
777 return (float) result;
778 }
779 /* Exceptional cases: */
780 else if (isless (x, himark))
781 {
782 if (__isinff (x))
783 /* e^-inf == 0, with no error. */
784 return 0;
785 else
786 /* Underflow */
787 return TWOM100 * TWOM100;
788 }
789 else
790 /* Return x, if x is a NaN or Inf; or overflow, otherwise. */
791 return TWO127*x;
792}
793
794/* Arc tangent,
795 taken from glibc-2.8. */
796
797static const float atanhi[] = {
798 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
799 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
800 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
801 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
802};
803
804static const float atanlo[] = {
805 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
806 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
807 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
808 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
809};
810
811static const float aT[] = {
812 3.3333334327e-01, /* 0x3eaaaaaa */
813 -2.0000000298e-01, /* 0xbe4ccccd */
814 1.4285714924e-01, /* 0x3e124925 */
815 -1.1111110449e-01, /* 0xbde38e38 */
816 9.0908870101e-02, /* 0x3dba2e6e */
817 -7.6918758452e-02, /* 0xbd9d8795 */
818 6.6610731184e-02, /* 0x3d886b35 */
819 -5.8335702866e-02, /* 0xbd6ef16b */
820 4.9768779427e-02, /* 0x3d4bda59 */
821 -3.6531571299e-02, /* 0xbd15a221 */
822 1.6285819933e-02, /* 0x3c8569d7 */
823};
824
825static const float
826huge = 1.0e+30,
827tiny = 1.0e-30,
828one = 1.0f;
829
830float atan_wrapper(float x)
831{
832 float w,s1,s2,z;
833 int32_t ix,hx,id;
834
835 GET_FLOAT_WORD(hx,x);
836 ix = hx&0x7fffffff;
837 if(ix>=0x50800000) { /* if |x| >= 2^34 */
838 if(ix>0x7f800000)
839 return x+x; /* NaN */
840 if(hx>0) return atanhi[3]+atanlo[3];
841 else return -atanhi[3]-atanlo[3];
842 } if (ix < 0x3ee00000) { /* |x| < 0.4375 */
843 if (ix < 0x31000000) { /* |x| < 2^-29 */
844 if(huge+x>one) return x; /* raise inexact */
845 }
846 id = -1;
847 } else {
848 x = fabs_wrapper(x);
849 if (ix < 0x3f980000) { /* |x| < 1.1875 */
850 if (ix < 0x3f300000) { /* 7/16 <=|x|<11/16 */
851 id = 0; x = ((float)2.0*x-one)/((float)2.0+x);
852 } else { /* 11/16<=|x|< 19/16 */
853 id = 1; x = (x-one)/(x+one);
854 }
855 } else {
856 if (ix < 0x401c0000) { /* |x| < 2.4375 */
857 id = 2; x = (x-(float)1.5)/(one+(float)1.5*x);
858 } else { /* 2.4375 <= |x| < 2^66 */
859 id = 3; x = -(float)1.0/x;
860 }
861 }}
862 /* end of argument reduction */
863 z = x*x;
864 w = z*z;
865 /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
866 s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10])))));
867 s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9]))));
868 if (id<0) return x - x*(s1+s2);
869 else {
870 z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x);
871 return (hx<0)? -z:z;
872 }
873}
874
875/* Arc tangent from two variables, original. */
876
877static const float
878pi_o_4 = 7.8539818525e-01, /* 0x3f490fdb */
879pi_o_2 = 1.5707963705e+00, /* 0x3fc90fdb */
880pi = 3.1415927410e+00, /* 0x40490fdb */
881pi_lo = -8.7422776573e-08; /* 0xb3bbbd2e */
882
883float atan2_wrapper(float y, float x)
884{
885 float z;
886 int32_t k,m,hx,hy,ix,iy;
887
888 GET_FLOAT_WORD(hx,x);
889 ix = hx&0x7fffffff;
890 GET_FLOAT_WORD(hy,y);
891 iy = hy&0x7fffffff;
892 if((ix>0x7f800000)||
893 (iy>0x7f800000)) /* x or y is NaN */
894 return x+y;
895 if(hx==0x3f800000) return atan_wrapper(y); /* x=1.0 */
896 m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */
897
898 /* when y = 0 */
899 if(iy==0) {
900 switch(m) {
901 case 0:
902 case 1: return y; /* atan(+-0,+anything)=+-0 */
903 case 2: return pi+tiny;/* atan(+0,-anything) = pi */
904 case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */
905 }
906 }
907 /* when x = 0 */
908 if(ix==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny;
909
910 /* when x is INF */
911 if(ix==0x7f800000) {
912 if(iy==0x7f800000) {
913 switch(m) {
914 case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */
915 case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */
916 case 2: return (float)3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/
917 case 3: return (float)-3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/
918 }
919 } else {
920 switch(m) {
921 case 0: return zero ; /* atan(+...,+INF) */
922 case 1: return -zero ; /* atan(-...,+INF) */
923 case 2: return pi+tiny ; /* atan(+...,-INF) */
924 case 3: return -pi-tiny ; /* atan(-...,-INF) */
925 }
926 }
927 }
928 /* when y is INF */
929 if(iy==0x7f800000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny;
930
931 /* compute y/x */
932 k = (iy-ix)>>23;
933 if(k > 60) z=pi_o_2+(float)0.5*pi_lo; /* |y/x| > 2**60 */
934 else if(hx<0&&k<-60) z=0.0; /* |y|/x < -2**60 */
935 else z=atan_wrapper(fabs_wrapper(y/x)); /* safe to do y/x */
936 switch (m) {
937 case 0: return z ; /* atan(+,+) */
938 case 1: {
939 uint32_t zh;
940 GET_FLOAT_WORD(zh,z);
941 SET_FLOAT_WORD(z,zh ^ 0x80000000);
942 }
943 return z ; /* atan(-,+) */
944 case 2: return pi-(z-pi_lo);/* atan(+,-) */
945 default: /* case 3 */
946 return (z-pi_lo)-pi;/* atan(-,-) */
947 }
948}
949
950/* Square root function, original. */
951float sqrt_wrapper(float x)
952{
953 float z;
954 int32_t sign = (int)0x80000000;
955 int32_t ix,s,q,m,t,i;
956 uint32_t r;
957
958 GET_FLOAT_WORD(ix,x);
959
960 /* take care of Inf and NaN */
961 if((ix&0x7f800000)==0x7f800000) {
962 return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf
963 sqrt(-inf)=sNaN */
964 }
965 /* take care of zero */
966 if(ix<=0) {
967 if((ix&(~sign))==0) return x;/* sqrt(+-0) = +-0 */
968 else if(ix<0)
969 return (x-x)/(x-x); /* sqrt(-ve) = sNaN */
970 }
971 /* normalize x */
972 m = (ix>>23);
973 if(m==0) { /* subnormal x */
974 for(i=0;(ix&0x00800000)==0;i++) ix<<=1;
975 m -= i-1;
976 }
977 m -= 127; /* unbias exponent */
978 ix = (ix&0x007fffff)|0x00800000;
979 if(m&1) /* odd m, double x to make it even */
980 ix += ix;
981 m >>= 1; /* m = [m/2] */
982
983 /* generate sqrt(x) bit by bit */
984 ix += ix;
985 q = s = 0; /* q = sqrt(x) */
986 r = 0x01000000; /* r = moving bit from right to left */
987
988 while(r!=0) {
989 t = s+r;
990 if(t<=ix) {
991 s = t+r;
992 ix -= t;
993 q += r;
994 }
995 ix += ix;
996 r>>=1;
997 }
998
999 /* use floating add to find out rounding direction */
1000 if(ix!=0) {
1001 z = one-tiny; /* trigger inexact flag */
1002 if (z>=one) {
1003 z = one+tiny;
1004 if (z>one)
1005 q += 2;
1006 else
1007 q += (q&1);
1008 }
1009 }
1010 ix = (q>>1)+0x3f000000;
1011 ix += (m <<23);
1012 SET_FLOAT_WORD(z,ix);
1013 return z;
1014}
1015
1016/* @(#)e_acos.c 1.3 95/01/18 */
1017/*
1018 * ====================================================
1019 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
1020 *
1021 * Developed at SunSoft, a Sun Microsystems, Inc. business.
1022 * Permission to use, copy, modify, and distribute this
1023 * software is freely granted, provided that this notice
1024 * is preserved.
1025 * ====================================================
1026 */
1027
1028/* __ieee754_acos(x)
1029 * Method :
1030 * acos(x) = pi/2 - asin(x)
1031 * acos(-x) = pi/2 + asin(x)
1032 * For |x|<=0.5
1033 * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c)
1034 * For x>0.5
1035 * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2)))
1036 * = 2asin(sqrt((1-x)/2))
1037 * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z)
1038 * = 2f + (2c + 2s*z*R(z))
1039 * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term
1040 * for f so that f+c ~ sqrt(z).
1041 * For x<-0.5
1042 * acos(x) = pi - 2asin(sqrt((1-|x|)/2))
1043 * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z)
1044 *
1045 * Special cases:
1046 * if x is NaN, return x itself;
1047 * if |x|>1, return NaN with invalid signal.
1048 *
1049 * Function needed: sqrt
1050 */
1051
1052#ifdef __STDC__
1053static const double
1054#else
1055static double
1056#endif
1057pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */
1058pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */
1059pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */
1060pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */
1061pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */
1062pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */
1063pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */
1064pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */
1065qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */
1066qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */
1067qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */
1068qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
1069
1070double acos_wrapper(double x)
1071{
1072 double z,p,q,r,w,s,c,df;
1073 int hx,ix;
1074 hx = __HI(x);
1075 ix = hx&0x7fffffff;
1076 if(ix>=0x3ff00000) { /* |x| >= 1 */
1077 if(((ix-0x3ff00000)|__LO(x))==0) { /* |x|==1 */
1078 if(hx>0) return 0.0; /* acos(1) = 0 */
1079 else return pi+2.0*pio2_lo; /* acos(-1)= pi */
1080 }
1081 return (x-x)/(x-x); /* acos(|x|>1) is NaN */
1082 }
1083 if(ix<0x3fe00000) { /* |x| < 0.5 */
1084 if(ix<=0x3c600000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/
1085 z = x*x;
1086 p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
1087 q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
1088 r = p/q;
1089 return pio2_hi - (x - (pio2_lo-x*r));
1090 } else if (hx<0) { /* x < -0.5 */
1091 z = (one+x)*0.5;
1092 p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
1093 q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
1094 s = sqrt_wrapper(z);
1095 r = p/q;
1096 w = r*s-pio2_lo;
1097 return pi - 2.0*(s+w);
1098 } else { /* x > 0.5 */
1099 z = (one-x)*0.5;
1100 s = sqrt_wrapper(z);
1101 df = s;
1102 __LO(df) = 0;
1103 c = (z-df*df)/(s+df);
1104 p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
1105 q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
1106 r = p/q;
1107 w = r*s+c;
1108 return 2.0*(df+w);
1109 }
1110
1111}
1112
1113/*
1114 * Copyright (C) 2004 by egnite Software GmbH. All rights reserved.
1115 *
1116 * Redistribution and use in source and binary forms, with or without
1117 * modification, are permitted provided that the following conditions
1118 * are met:
1119 *
1120 * 1. Redistributions of source code must retain the above copyright
1121 * notice, this list of conditions and the following disclaimer.
1122 * 2. Redistributions in binary form must reproduce the above copyright
1123 * notice, this list of conditions and the following disclaimer in the
1124 * documentation and/or other materials provided with the distribution.
1125 * 3. Neither the name of the copyright holders nor the names of
1126 * contributors may be used to endorse or promote products derived
1127 * from this software without specific prior written permission.
1128 *
1129 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
1130 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1131 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
1132 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
1133 * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1134 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1135 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1136 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
1137 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1138 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1139 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1140 * SUCH DAMAGE.
1141 *
1142 * For additional information see http://www.ethernut.de/
1143 *
1144 *-
1145 * Copyright (c) 1990 The Regents of the University of California.
1146 * All rights reserved.
1147 *
1148 * Redistribution and use in source and binary forms, with or without
1149 * modification, are permitted provided that the following conditions
1150 * are met:
1151 * 1. Redistributions of source code must retain the above copyright
1152 * notice, this list of conditions and the following disclaimer.
1153 * 2. Redistributions in binary form must reproduce the above copyright
1154 * notice, this list of conditions and the following disclaimer in the
1155 * documentation and/or other materials provided with the distribution.
1156 * 3. Neither the name of the University nor the names of its contributors
1157 * may be used to endorse or promote products derived from this software
1158 * without specific prior written permission.
1159 *
1160 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1161 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1162 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1163 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1164 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1165 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1166 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1167 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1168 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1169 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1170 * SUCH DAMAGE.
1171 */
1172
1173/*
1174 * $Log$
1175 * Revision 1.4 2008/02/15 17:13:01 haraldkipp
1176 * Use configurable constant attribute.
1177 *
1178 * Revision 1.3 2006/10/08 16:48:08 haraldkipp
1179 * Documentation fixed
1180 *
1181 * Revision 1.2 2005/08/02 17:46:47 haraldkipp
1182 * Major API documentation update.
1183 *
1184 * Revision 1.1 2004/09/08 10:23:35 haraldkipp
1185 * Generic C stdlib added
1186 *
1187 */
1188
1189#define CONST const
1190long strtol_wrapper(CONST char *nptr, char **endptr, int base)
1191{
1192 register CONST char *s;
1193 register long acc, cutoff;
1194 register int c;
1195 register int neg, any, cutlim;
1196
1197 /*
1198 * Skip white space and pick up leading +/- sign if any.
1199 * If base is 0, allow 0x for hex and 0 for octal, else
1200 * assume decimal; if base is already 16, allow 0x.
1201 */
1202 s = nptr;
1203 do {
1204 c = (unsigned char) *s++;
1205 } while (isspace(c));
1206 if (c == '-') {
1207 neg = 1;
1208 c = *s++;
1209 } else {
1210 neg = 0;
1211 if (c == '+')
1212 c = *s++;
1213 }
1214 if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
1215 c = s[1];
1216 s += 2;
1217 base = 16;
1218 }
1219 if (base == 0)
1220 base = c == '0' ? 8 : 10;
1221
1222 /*
1223 * Compute the cutoff value between legal numbers and illegal
1224 * numbers. That is the largest legal value, divided by the
1225 * base. An input number that is greater than this value, if
1226 * followed by a legal input character, is too big. One that
1227 * is equal to this value may be valid or not; the limit
1228 * between valid and invalid numbers is then based on the last
1229 * digit. For instance, if the range for longs is
1230 * [-2147483648..2147483647] and the input base is 10,
1231 * cutoff will be set to 214748364 and cutlim to either
1232 * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
1233 * a value > 214748364, or equal but the next digit is > 7 (or 8),
1234 * the number is too big, and we will return a range error.
1235 *
1236 * Set any if any `digits' consumed; make it negative to indicate
1237 * overflow.
1238 */
1239 cutoff = neg ? LONG_MIN : LONG_MAX;
1240 cutlim = cutoff % base;
1241 cutoff /= base;
1242 if (neg) {
1243 if (cutlim > 0) {
1244 cutlim -= base;
1245 cutoff += 1;
1246 }
1247 cutlim = -cutlim;
1248 }
1249 for (acc = 0, any = 0;; c = (unsigned char) *s++) {
1250 if (isdigit(c))
1251 c -= '0';
1252 else if (isalpha(c))
1253 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
1254 else
1255 break;
1256 if (c >= base)
1257 break;
1258 if (any < 0)
1259 continue;
1260 if (neg) {
1261 if ((acc < cutoff || acc == cutoff) && c > cutlim) {
1262 any = -1;
1263 acc = LONG_MIN;
1264 } else {
1265 any = 1;
1266 acc *= base;
1267 acc -= c;
1268 }
1269 } else {
1270 if ((acc > cutoff || acc == cutoff) && c > cutlim) {
1271 any = -1;
1272 acc = LONG_MAX;
1273 } else {
1274 any = 1;
1275 acc *= base;
1276 acc += c;
1277 }
1278 }
1279 }
1280 if (endptr != 0)
1281 *endptr = (char *) (any ? s - 1 : nptr);
1282 return (acc);
1283}
1284
1285int64_t strtoq_wrapper(CONST char *nptr, char **endptr, int base)
1286{
1287 return strtol(nptr, endptr, base);
1288}
1289
1290uint64_t strtouq_wrapper(CONST char *nptr, char **endptr, int base)
1291{
1292 return strtol(nptr, endptr, base);
1293}
1294
1295/* Power function, taken from glibc-2.8 and dietlibc-0.32 */
1296float pow_wrapper(float x, float y)
1297{
1298 unsigned int e;
1299 float result;
1300
1301 /* Special cases 0^x */
1302 if(x == 0.0f)
1303 {
1304 if(y > 0.0f)
1305 return 0.0f;
1306 else if(y == 0.0f)
1307 return 1.0f;
1308 else
1309 return 1.0f / x;
1310 }
1311
1312 /* Special case x^n where n is integer */
1313 if(y == (int) (e = (int) y))
1314 {
1315 if((int) e < 0)
1316 {
1317 e = -e;
1318 x = 1.0f / x;
1319 }
1320
1321 result = 1.0f;
1322
1323 while(1)
1324 {
1325 if(e & 1)
1326 result *= x;
1327
1328 if((e >>= 1) == 0)
1329 break;
1330
1331 x *= x;
1332 }
1333
1334 return result;
1335 }
1336
1337 /* Normal case */
1338 return rb_exp(rb_log(x) * y);
1339}
1340
1341/* @(#)s_copysign.c 1.3 95/01/18 */
1342/*
1343 * ====================================================
1344 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
1345 *
1346 * Developed at SunSoft, a Sun Microsystems, Inc. business.
1347 * Permission to use, copy, modify, and distribute this
1348 * software is freely granted, provided that this notice
1349 * is preserved.
1350 * ====================================================
1351 */
1352
1353/*
1354 * copysign(double x, double y)
1355 * copysign(x,y) returns a value with the magnitude of x and
1356 * with the sign bit of y.
1357 */
1358
1359double copysign_wrapper(double x, double y)
1360{
1361 __HI(x) = (__HI(x)&0x7fffffff)|(__HI(y)&0x80000000);
1362 return x;
1363}
1364
1365/* @(#)s_scalbn.c 1.3 95/01/18 */
1366/*
1367 * ====================================================
1368 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
1369 *
1370 * Developed at SunSoft, a Sun Microsystems, Inc. business.
1371 * Permission to use, copy, modify, and distribute this
1372 * software is freely granted, provided that this notice
1373 * is preserved.
1374 * ====================================================
1375 */
1376
1377/*
1378 * scalbn (double x, int n)
1379 * scalbn(x,n) returns x* 2**n computed by exponent
1380 * manipulation rather than by actually performing an
1381 * exponentiation or a multiplication.
1382 */
1383
1384#ifdef __STDC__
1385static const double
1386#else
1387static double
1388#endif
1389two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */
1390 twom54 = 5.55111512312578270212e-17; /* 0x3C900000, 0x00000000 */
1391
1392double scalbn_wrapper (double x, int n)
1393{
1394 int k,hx,lx;
1395 hx = __HI(x);
1396 lx = __LO(x);
1397 k = (hx&0x7ff00000)>>20; /* extract exponent */
1398 if (k==0) { /* 0 or subnormal x */
1399 if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */
1400 x *= two54;
1401 hx = __HI(x);
1402 k = ((hx&0x7ff00000)>>20) - 54;
1403 if (n< -50000) return tiny*x; /*underflow*/
1404 }
1405 if (k==0x7ff) return x+x; /* NaN or Inf */
1406 k = k+n;
1407 if (k > 0x7fe) return huge*copysign_wrapper(huge,x); /* overflow */
1408 if (k > 0) /* normal result */
1409 {__HI(x) = (hx&0x800fffff)|(k<<20); return x;}
1410 if (k <= -54)
1411 if (n > 50000) /* in case integer overflow in n+k */
1412 return huge*copysign_wrapper(huge,x); /*overflow*/
1413 else return tiny*copysign_wrapper(tiny,x); /*underflow*/
1414 k += 54; /* subnormal result */
1415 __HI(x) = (hx&0x800fffff)|(k<<20);
1416 return x*twom54;
1417}
1418
1419/* horrible hack */
1420float ceil_wrapper(float x)
1421{
1422 return floor_wrapper(x) + 1.0;
1423}
1424
1425/* Implementation of strtod() and atof(),
1426 taken from SanOS (http://www.jbox.dk/sanos/). */
1427static int rb_errno = 0;
1428
1429static double rb_strtod(const char *str, char **endptr)
1430{
1431 double number;
1432 int exponent;
1433 int negative;
1434 char *p = (char *) str;
1435 double p10;
1436 int n;
1437 int num_digits;
1438 int num_decimals;
1439
1440 /* Reset Rockbox errno -- W.B. */
1441#ifdef ROCKBOX
1442 rb_errno = 0;
1443#endif
1444
1445 // Skip leading whitespace
1446 while (isspace(*p)) p++;
1447
1448 // Handle optional sign
1449 negative = 0;
1450 switch (*p)
1451 {
1452 case '-': negative = 1; // Fall through to increment position
1453 case '+': p++;
1454 }
1455
1456 number = 0.;
1457 exponent = 0;
1458 num_digits = 0;
1459 num_decimals = 0;
1460
1461 // Process string of digits
1462 while (isdigit(*p))
1463 {
1464 number = number * 10. + (*p - '0');
1465 p++;
1466 num_digits++;
1467 }
1468
1469 // Process decimal part
1470 if (*p == '.')
1471 {
1472 p++;
1473
1474 while (isdigit(*p))
1475 {
1476 number = number * 10. + (*p - '0');
1477 p++;
1478 num_digits++;
1479 num_decimals++;
1480 }
1481
1482 exponent -= num_decimals;
1483 }
1484
1485 if (num_digits == 0)
1486 {
1487#ifdef ROCKBOX
1488 rb_errno = 1;
1489#else
1490 errno = ERANGE;
1491#endif
1492 return 0.0;
1493 }
1494
1495 // Correct for sign
1496 if (negative) number = -number;
1497
1498 // Process an exponent string
1499 if (*p == 'e' || *p == 'E')
1500 {
1501 // Handle optional sign
1502 negative = 0;
1503 switch(*++p)
1504 {
1505 case '-': negative = 1; // Fall through to increment pos
1506 case '+': p++;
1507 }
1508
1509 // Process string of digits
1510 n = 0;
1511 while (isdigit(*p))
1512 {
1513 n = n * 10 + (*p - '0');
1514 p++;
1515 }
1516
1517 if (negative)
1518 exponent -= n;
1519 else
1520 exponent += n;
1521 }
1522
1523#ifndef ROCKBOX
1524 if (exponent < DBL_MIN_EXP || exponent > DBL_MAX_EXP)
1525 {
1526 errno = ERANGE;
1527 return HUGE_VAL;
1528 }
1529#endif
1530
1531 // Scale the result
1532 p10 = 10.;
1533 n = exponent;
1534 if (n < 0) n = -n;
1535 while (n)
1536 {
1537 if (n & 1)
1538 {
1539 if (exponent < 0)
1540 number /= p10;
1541 else
1542 number *= p10;
1543 }
1544 n >>= 1;
1545 p10 *= p10;
1546 }
1547
1548#ifndef ROCKBOX
1549 if (number == HUGE_VAL) errno = ERANGE;
1550#endif
1551 if (endptr) *endptr = p;
1552
1553 return number;
1554}
1555
1556double atof_wrapper(const char *str)
1557{
1558 return rb_strtod(str, NULL);
1559}
1560
1561/*
1562 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
1563 *
1564 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1565 *
1566 * This file contains Original Code and/or Modifications of Original Code
1567 * as defined in and that are subject to the Apple Public Source License
1568 * Version 2.0 (the 'License'). You may not use this file except in
1569 * compliance with the License. The rights granted to you under the License
1570 * may not be used to create, or enable the creation or redistribution of,
1571 * unlawful or unlicensed copies of an Apple operating system, or to
1572 * circumvent, violate, or enable the circumvention or violation of, any
1573 * terms of an Apple operating system software license agreement.
1574 *
1575 * Please obtain a copy of the License at
1576 * http://www.opensource.apple.com/apsl/ and read it before using this file.
1577 *
1578 * The Original Code and all software distributed under the License are
1579 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1580 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1581 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1582 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1583 * Please see the License for the specific language governing rights and
1584 * limitations under the License.
1585 *
1586 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1587 */
1588/*-
1589 * Copyright (c) 1990, 1993
1590 * The Regents of the University of California. All rights reserved.
1591 *
1592 * This code is derived from software contributed to Berkeley by
1593 * Chris Torek.
1594 *
1595 * Redistribution and use in source and binary forms, with or without
1596 * modification, are permitted provided that the following conditions
1597 * are met:
1598 * 1. Redistributions of source code must retain the above copyright
1599 * notice, this list of conditions and the following disclaimer.
1600 * 2. Redistributions in binary form must reproduce the above copyright
1601 * notice, this list of conditions and the following disclaimer in the
1602 * documentation and/or other materials provided with the distribution.
1603 * 3. All advertising materials mentioning features or use of this software
1604 * must display the following acknowledgement:
1605 * This product includes software developed by the University of
1606 * California, Berkeley and its contributors.
1607 * 4. Neither the name of the University nor the names of its contributors
1608 * may be used to endorse or promote products derived from this software
1609 * without specific prior written permission.
1610 *
1611 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1612 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1613 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1614 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1615 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1616 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1617 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1618 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1619 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1620 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1621 * SUCH DAMAGE.
1622 */
1623
1624#include "rbcompat.h"
1625
1626#define BUF 32 /* Maximum length of numeric string. */
1627
1628/*
1629 * Flags used during conversion.
1630 */
1631#define LONG 0x01 /* l: long or double */
1632#define SHORT 0x04 /* h: short */
1633#define SUPPRESS 0x08 /* *: suppress assignment */
1634#define POINTER 0x10 /* p: void * (as hex) */
1635#define NOSKIP 0x20 /* [ or c: do not skip blanks */
1636#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
1637#define SHORTSHORT 0x4000 /* hh: char */
1638#define UNSIGNED 0x8000 /* %[oupxX] conversions */
1639
1640/*
1641 * The following are used in numeric conversions only:
1642 * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
1643 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
1644 */
1645#define SIGNOK 0x40 /* +/- is (still) legal */
1646#define NDIGITS 0x80 /* no digits detected */
1647
1648#define DPTOK 0x100 /* (float) decimal point is still legal */
1649#define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */
1650
1651#define PFXOK 0x100 /* 0x prefix is (still) legal */
1652#define NZDIGITS 0x200 /* no zero digits detected */
1653
1654/*
1655 * Conversion types.
1656 */
1657#define CT_CHAR 0 /* %c conversion */
1658#define CT_CCL 1 /* %[...] conversion */
1659#define CT_STRING 2 /* %s conversion */
1660#define CT_INT 3 /* %[dioupxX] conversion */
1661
1662typedef unsigned char u_char;
1663typedef uint64_t u_quad_t;
1664
1665static const u_char *__sccl(char *, const u_char *);
1666
1667void bcopy(const void *src, void *dst, size_t n)
1668{
1669 memmove(dst, src, n);
1670}
1671
1672int
1673sscanf_wrapper(const char *ibuf, const char *fmt, ...)
1674{
1675 va_list ap;
1676 int ret;
1677
1678 va_start(ap, fmt);
1679 ret = vsscanf(ibuf, fmt, ap);
1680 va_end(ap);
1681 return(ret);
1682}
1683
1684int
1685vsscanf(const char *inp, char const *fmt0, va_list ap)
1686{
1687 int inr;
1688 const u_char *fmt = (const u_char *)fmt0;
1689 int c; /* character from format, or conversion */
1690 size_t width; /* field width, or 0 */
1691 char *p; /* points into all kinds of strings */
1692 int n; /* handy integer */
1693 int flags; /* flags as defined above */
1694 char *p0; /* saves original value of p when necessary */
1695 int nassigned; /* number of fields assigned */
1696 int nconversions; /* number of conversions */
1697 int nread; /* number of characters consumed from fp */
1698 int base; /* base argument to conversion function */
1699 char ccltab[256]; /* character class table for %[...] */
1700 char buf[BUF]; /* buffer for numeric conversions */
1701
1702 /* `basefix' is used to avoid `if' tests in the integer scanner */
1703 static short basefix[17] =
1704 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
1705
1706 inr = strlen(inp);
1707
1708 nassigned = 0;
1709 nconversions = 0;
1710 nread = 0;
1711 base = 0; /* XXX just to keep gcc happy */
1712 for (;;) {
1713 c = *fmt++;
1714 if (c == 0)
1715 return (nassigned);
1716 if (isspace(c)) {
1717 while (inr > 0 && isspace(*inp))
1718 nread++, inr--, inp++;
1719 continue;
1720 }
1721 if (c != '%')
1722 goto literal;
1723 width = 0;
1724 flags = 0;
1725 /*
1726 * switch on the format. continue if done;
1727 * break once format type is derived.
1728 */
1729again: c = *fmt++;
1730 switch (c) {
1731 case '%':
1732literal:
1733 if (inr <= 0)
1734 goto input_failure;
1735 if (*inp != c)
1736 goto match_failure;
1737 inr--, inp++;
1738 nread++;
1739 continue;
1740
1741 case '*':
1742 flags |= SUPPRESS;
1743 goto again;
1744 case 'l':
1745 if (flags & LONG) {
1746 flags &= ~LONG;
1747 flags |= LONGLONG;
1748 } else
1749 flags |= LONG;
1750 goto again;
1751 case 'q':
1752 flags |= LONGLONG; /* not quite */
1753 goto again;
1754 case 'h':
1755 if (flags & SHORT) {
1756 flags &= ~SHORT;
1757 flags |= SHORTSHORT;
1758 } else
1759 flags |= SHORT;
1760 goto again;
1761
1762 case '0': case '1': case '2': case '3': case '4':
1763 case '5': case '6': case '7': case '8': case '9':
1764 width = width * 10 + c - '0';
1765 goto again;
1766
1767 /*
1768 * Conversions.
1769 */
1770 case 'd':
1771 c = CT_INT;
1772 base = 10;
1773 break;
1774
1775 case 'i':
1776 c = CT_INT;
1777 base = 0;
1778 break;
1779
1780 case 'o':
1781 c = CT_INT;
1782 flags |= UNSIGNED;
1783 base = 8;
1784 break;
1785
1786 case 'u':
1787 c = CT_INT;
1788 flags |= UNSIGNED;
1789 base = 10;
1790 break;
1791
1792 case 'X':
1793 case 'x':
1794 flags |= PFXOK; /* enable 0x prefixing */
1795 c = CT_INT;
1796 flags |= UNSIGNED;
1797 base = 16;
1798 break;
1799
1800 case 's':
1801 c = CT_STRING;
1802 break;
1803
1804 case '[':
1805 fmt = __sccl(ccltab, fmt);
1806 flags |= NOSKIP;
1807 c = CT_CCL;
1808 break;
1809
1810 case 'c':
1811 flags |= NOSKIP;
1812 c = CT_CHAR;
1813 break;
1814
1815 case 'p': /* pointer format is like hex */
1816 flags |= POINTER | PFXOK;
1817 c = CT_INT;
1818 flags |= UNSIGNED;
1819 base = 16;
1820 break;
1821
1822 case 'n':
1823 nconversions++;
1824 if (flags & SUPPRESS) /* ??? */
1825 continue;
1826 if (flags & SHORTSHORT)
1827 *va_arg(ap, char *) = nread;
1828 else if (flags & SHORT)
1829 *va_arg(ap, short *) = nread;
1830 else if (flags & LONG)
1831 *va_arg(ap, long *) = nread;
1832 else if (flags & LONGLONG)
1833 *va_arg(ap, long long *) = nread;
1834 else
1835 *va_arg(ap, int *) = nread;
1836 continue;
1837 }
1838
1839 /*
1840 * We have a conversion that requires input.
1841 */
1842 if (inr <= 0)
1843 goto input_failure;
1844
1845 /*
1846 * Consume leading white space, except for formats
1847 * that suppress this.
1848 */
1849 if ((flags & NOSKIP) == 0) {
1850 while (isspace(*inp)) {
1851 nread++;
1852 if (--inr > 0)
1853 inp++;
1854 else
1855 goto input_failure;
1856 }
1857 /*
1858 * Note that there is at least one character in
1859 * the buffer, so conversions that do not set NOSKIP
1860 * can no longer result in an input failure.
1861 */
1862 }
1863
1864 /*
1865 * Do the conversion.
1866 */
1867 switch (c) {
1868
1869 case CT_CHAR:
1870 /* scan arbitrary characters (sets NOSKIP) */
1871 if (width == 0)
1872 width = 1;
1873 if (flags & SUPPRESS) {
1874 size_t sum = 0;
1875 for (;;) {
1876 if ((n = inr) < (int)width) {
1877 sum += n;
1878 width -= n;
1879 inp += n;
1880 if (sum == 0)
1881 goto input_failure;
1882 break;
1883 } else {
1884 sum += width;
1885 inr -= width;
1886 inp += width;
1887 break;
1888 }
1889 }
1890 nread += sum;
1891 } else {
1892 bcopy(inp, va_arg(ap, char *), width);
1893 inr -= width;
1894 inp += width;
1895 nread += width;
1896 nassigned++;
1897 }
1898 nconversions++;
1899 break;
1900
1901 case CT_CCL:
1902 /* scan a (nonempty) character class (sets NOSKIP) */
1903 if (width == 0)
1904 width = (size_t)~0; /* `infinity' */
1905 /* take only those things in the class */
1906 if (flags & SUPPRESS) {
1907 n = 0;
1908 while (ccltab[(unsigned char)*inp]) {
1909 n++, inr--, inp++;
1910 if (--width == 0)
1911 break;
1912 if (inr <= 0) {
1913 if (n == 0)
1914 goto input_failure;
1915 break;
1916 }
1917 }
1918 if (n == 0)
1919 goto match_failure;
1920 } else {
1921 p0 = p = va_arg(ap, char *);
1922 while (ccltab[(unsigned char)*inp]) {
1923 inr--;
1924 *p++ = *inp++;
1925 if (--width == 0)
1926 break;
1927 if (inr <= 0) {
1928 if (p == p0)
1929 goto input_failure;
1930 break;
1931 }
1932 }
1933 n = p - p0;
1934 if (n == 0)
1935 goto match_failure;
1936 *p = 0;
1937 nassigned++;
1938 }
1939 nread += n;
1940 nconversions++;
1941 break;
1942
1943 case CT_STRING:
1944 /* like CCL, but zero-length string OK, & no NOSKIP */
1945 if (width == 0)
1946 width = (size_t)~0;
1947 if (flags & SUPPRESS) {
1948 n = 0;
1949 while (!isspace(*inp)) {
1950 n++, inr--, inp++;
1951 if (--width == 0)
1952 break;
1953 if (inr <= 0)
1954 break;
1955 }
1956 nread += n;
1957 } else {
1958 p0 = p = va_arg(ap, char *);
1959 while (!isspace(*inp)) {
1960 inr--;
1961 *p++ = *inp++;
1962 if (--width == 0)
1963 break;
1964 if (inr <= 0)
1965 break;
1966 }
1967 *p = 0;
1968 nread += p - p0;
1969 nassigned++;
1970 }
1971 nconversions++;
1972 continue;
1973
1974 case CT_INT:
1975 /* scan an integer as if by the conversion function */
1976#ifdef hardway
1977 if (width == 0 || width > sizeof(buf) - 1)
1978 width = sizeof(buf) - 1;
1979#else
1980 /* size_t is unsigned, hence this optimisation */
1981 if (--width > sizeof(buf) - 2)
1982 width = sizeof(buf) - 2;
1983 width++;
1984#endif
1985 flags |= SIGNOK | NDIGITS | NZDIGITS;
1986 for (p = buf; width; width--) {
1987 c = *inp;
1988 /*
1989 * Switch on the character; `goto ok'
1990 * if we accept it as a part of number.
1991 */
1992 switch (c) {
1993
1994 /*
1995 * The digit 0 is always legal, but is
1996 * special. For %i conversions, if no
1997 * digits (zero or nonzero) have been
1998 * scanned (only signs), we will have
1999 * base==0. In that case, we should set
2000 * it to 8 and enable 0x prefixing.
2001 * Also, if we have not scanned zero digits
2002 * before this, do not turn off prefixing
2003 * (someone else will turn it off if we
2004 * have scanned any nonzero digits).
2005 */
2006 case '0':
2007 if (base == 0) {
2008 base = 8;
2009 flags |= PFXOK;
2010 }
2011 if (flags & NZDIGITS)
2012 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
2013 else
2014 flags &= ~(SIGNOK|PFXOK|NDIGITS);
2015 goto ok;
2016
2017 /* 1 through 7 always legal */
2018 case '1': case '2': case '3':
2019 case '4': case '5': case '6': case '7':
2020 base = basefix[base];
2021 flags &= ~(SIGNOK | PFXOK | NDIGITS);
2022 goto ok;
2023
2024 /* digits 8 and 9 ok iff decimal or hex */
2025 case '8': case '9':
2026 base = basefix[base];
2027 if (base <= 8)
2028 break; /* not legal here */
2029 flags &= ~(SIGNOK | PFXOK | NDIGITS);
2030 goto ok;
2031
2032 /* letters ok iff hex */
2033 case 'A': case 'B': case 'C':
2034 case 'D': case 'E': case 'F':
2035 case 'a': case 'b': case 'c':
2036 case 'd': case 'e': case 'f':
2037 /* no need to fix base here */
2038 if (base <= 10)
2039 break; /* not legal here */
2040 flags &= ~(SIGNOK | PFXOK | NDIGITS);
2041 goto ok;
2042
2043 /* sign ok only as first character */
2044 case '+': case '-':
2045 if (flags & SIGNOK) {
2046 flags &= ~SIGNOK;
2047 goto ok;
2048 }
2049 break;
2050
2051 /* x ok iff flag still set & 2nd char */
2052 case 'x': case 'X':
2053 if (flags & PFXOK && p == buf + 1) {
2054 base = 16; /* if %i */
2055 flags &= ~PFXOK;
2056 goto ok;
2057 }
2058 break;
2059 }
2060
2061 /*
2062 * If we got here, c is not a legal character
2063 * for a number. Stop accumulating digits.
2064 */
2065 break;
2066 ok:
2067 /*
2068 * c is legal: store it and look at the next.
2069 */
2070 *p++ = c;
2071 if (--inr > 0)
2072 inp++;
2073 else
2074 break; /* end of input */
2075 }
2076 /*
2077 * If we had only a sign, it is no good; push
2078 * back the sign. If the number ends in `x',
2079 * it was [sign] '0' 'x', so push back the x
2080 * and treat it as [sign] '0'.
2081 */
2082 if (flags & NDIGITS) {
2083 if (p > buf) {
2084 inp--;
2085 inr++;
2086 }
2087 goto match_failure;
2088 }
2089 c = ((u_char *)p)[-1];
2090 if (c == 'x' || c == 'X') {
2091 --p;
2092 inp--;
2093 inr++;
2094 }
2095 if ((flags & SUPPRESS) == 0) {
2096 u_quad_t res;
2097
2098 *p = 0;
2099 if ((flags & UNSIGNED) == 0)
2100 res = strtoq(buf, (char **)NULL, base);
2101 else
2102 res = strtouq(buf, (char **)NULL, base);
2103 if (flags & POINTER)
2104 *va_arg(ap, void **) =
2105 (void *)(uintptr_t)res;
2106 else if (flags & SHORTSHORT)
2107 *va_arg(ap, char *) = res;
2108 else if (flags & SHORT)
2109 *va_arg(ap, short *) = res;
2110 else if (flags & LONG)
2111 *va_arg(ap, long *) = res;
2112 else if (flags & LONGLONG)
2113 *va_arg(ap, long long *) = res;
2114 else
2115 *va_arg(ap, int *) = res;
2116 nassigned++;
2117 }
2118 nread += p - buf;
2119 nconversions++;
2120 break;
2121
2122 }
2123 }
2124input_failure:
2125 return (nconversions != 0 ? nassigned : -1);
2126match_failure:
2127 return (nassigned);
2128}
2129
2130/*
2131 * Fill in the given table from the scanset at the given format
2132 * (just after `['). Return a pointer to the character past the
2133 * closing `]'. The table has a 1 wherever characters should be
2134 * considered part of the scanset.
2135 */
2136static const u_char *
2137__sccl(char *tab, const u_char *fmt)
2138{
2139 int c, n, v;
2140
2141 /* first `clear' the whole table */
2142 c = *fmt++; /* first char hat => negated scanset */
2143 if (c == '^') {
2144 v = 1; /* default => accept */
2145 c = *fmt++; /* get new first char */
2146 } else
2147 v = 0; /* default => reject */
2148
2149 /* XXX: Will not work if sizeof(tab*) > sizeof(char) */
2150 (void) memset(tab, v, 256);
2151
2152 if (c == 0)
2153 return (fmt - 1);/* format ended before closing ] */
2154
2155 /*
2156 * Now set the entries corresponding to the actual scanset
2157 * to the opposite of the above.
2158 *
2159 * The first character may be ']' (or '-') without being special;
2160 * the last character may be '-'.
2161 */
2162 v = 1 - v;
2163 for (;;) {
2164 tab[c] = v; /* take character c */
2165doswitch:
2166 n = *fmt++; /* and examine the next */
2167 switch (n) {
2168
2169 case 0: /* format ended too soon */
2170 return (fmt - 1);
2171
2172 case '-':
2173 /*
2174 * A scanset of the form
2175 * [01+-]
2176 * is defined as `the digit 0, the digit 1,
2177 * the character +, the character -', but
2178 * the effect of a scanset such as
2179 * [a-zA-Z0-9]
2180 * is implementation defined. The V7 Unix
2181 * scanf treats `a-z' as `the letters a through
2182 * z', but treats `a-a' as `the letter a, the
2183 * character -, and the letter a'.
2184 *
2185 * For compatibility, the `-' is not considerd
2186 * to define a range if the character following
2187 * it is either a close bracket (required by ANSI)
2188 * or is not numerically greater than the character
2189 * we just stored in the table (c).
2190 */
2191 n = *fmt;
2192 if (n == ']' || n < c) {
2193 c = '-';
2194 break; /* resume the for(;;) */
2195 }
2196 fmt++;
2197 /* fill in the range */
2198 do {
2199 tab[++c] = v;
2200 } while (c < n);
2201 c = n;
2202 /*
2203 * Alas, the V7 Unix scanf also treats formats
2204 * such as [a-c-e] as `the letters a through e'.
2205 * This too is permitted by the standard....
2206 */
2207 goto doswitch;
2208 break;
2209
2210 case ']': /* end of scanset */
2211 return (fmt);
2212
2213 default: /* just another character */
2214 c = n;
2215 break;
2216 }
2217 }
2218 /* NOTREACHED */
2219}
2220
2221/*
2222 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
2223 *
2224 * @APPLE_LICENSE_HEADER_START@
2225 *
2226 * The contents of this file constitute Original Code as defined in and
2227 * are subject to the Apple Public Source License Version 1.1 (the
2228 * "License"). You may not use this file except in compliance with the
2229 * License. Please obtain a copy of the License at
2230 * http://www.apple.com/publicsource and read it before using this file.
2231 *
2232 * This Original Code and all software distributed under the License are
2233 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
2234 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
2235 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2236 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
2237 * License for the specific language governing rights and limitations
2238 * under the License.
2239 *
2240 * @APPLE_LICENSE_HEADER_END@
2241 */
2242/*
2243 * Copyright (c) 1990, 1993
2244 * The Regents of the University of California. All rights reserved.
2245 *
2246 * This code is derived from software contributed to Berkeley by
2247 * Chris Torek.
2248 *
2249 * Redistribution and use in source and binary forms, with or without
2250 * modification, are permitted provided that the following conditions
2251 * are met:
2252 * 1. Redistributions of source code must retain the above copyright
2253 * notice, this list of conditions and the following disclaimer.
2254 * 2. Redistributions in binary form must reproduce the above copyright
2255 * notice, this list of conditions and the following disclaimer in the
2256 * documentation and/or other materials provided with the distribution.
2257 * 3. All advertising materials mentioning features or use of this software
2258 * must display the following acknowledgement:
2259 * This product includes software developed by the University of
2260 * California, Berkeley and its contributors.
2261 * 4. Neither the name of the University nor the names of its contributors
2262 * may be used to endorse or promote products derived from this software
2263 * without specific prior written permission.
2264 *
2265 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2266 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2267 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2268 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2269 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2270 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2271 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2272 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2273 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2274 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2275 * SUCH DAMAGE.
2276 */
2277
2278#include "plugin.h"
2279
2280/*
2281 * Span the complement of string s2.
2282 */
2283size_t
2284strcspn_wrapper(const char *s1, const char *s2)
2285{
2286 register const char *p, *spanp;
2287 register char c, sc;
2288
2289 /*
2290 * Stop as soon as we find any character from s2. Note that there
2291 * must be a NUL in s2; it suffices to stop when we find that, too.
2292 */
2293 for (p = s1;;) {
2294 c = *p++;
2295 spanp = s2;
2296 do {
2297 if ((sc = *spanp++) == c)
2298 return (p - 1 - s1);
2299 } while (sc != 0);
2300 }
2301 /* NOTREACHED */
2302}
2303
2304/*
2305 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
2306 *
2307 * @APPLE_LICENSE_HEADER_START@
2308 *
2309 * The contents of this file constitute Original Code as defined in and
2310 * are subject to the Apple Public Source License Version 1.1 (the
2311 * "License"). You may not use this file except in compliance with the
2312 * License. Please obtain a copy of the License at
2313 * http://www.apple.com/publicsource and read it before using this file.
2314 *
2315 * This Original Code and all software distributed under the License are
2316 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
2317 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
2318 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2319 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
2320 * License for the specific language governing rights and limitations
2321 * under the License.
2322 *
2323 * @APPLE_LICENSE_HEADER_END@
2324 */
2325/*
2326 * Copyright (c) 1989, 1993
2327 * The Regents of the University of California. All rights reserved.
2328 *
2329 * Redistribution and use in source and binary forms, with or without
2330 * modification, are permitted provided that the following conditions
2331 * are met:
2332 * 1. Redistributions of source code must retain the above copyright
2333 * notice, this list of conditions and the following disclaimer.
2334 * 2. Redistributions in binary form must reproduce the above copyright
2335 * notice, this list of conditions and the following disclaimer in the
2336 * documentation and/or other materials provided with the distribution.
2337 * 3. All advertising materials mentioning features or use of this software
2338 * must display the following acknowledgement:
2339 * This product includes software developed by the University of
2340 * California, Berkeley and its contributors.
2341 * 4. Neither the name of the University nor the names of its contributors
2342 * may be used to endorse or promote products derived from this software
2343 * without specific prior written permission.
2344 *
2345 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2346 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2347 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2348 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2349 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2350 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2351 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2352 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2353 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2354 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2355 * SUCH DAMAGE.
2356 */
2357
2358#include "rbcompat.h"
2359
2360/*
2361 * Span the string s2 (skip characters that are in s2).
2362 */
2363size_t
2364strspn_wrapper(const char *s1, const char *s2)
2365{
2366 register const char *p = s1, *spanp;
2367 register char c, sc;
2368
2369 /*
2370 * Skip any characters in s2, excluding the terminating \0.
2371 */
2372cont:
2373 c = *p++;
2374 for (spanp = s2; (sc = *spanp++) != 0;)
2375 if (sc == c)
2376 goto cont;
2377 return (p - 1 - s1);
2378}
diff --git a/apps/plugins/puzzles/rect.R b/apps/plugins/puzzles/rect.R
new file mode 100644
index 0000000000..1448c0fa63
--- /dev/null
+++ b/apps/plugins/puzzles/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/rect.c b/apps/plugins/puzzles/rect.c
new file mode 100644
index 0000000000..ca3cb984ea
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/resource.h b/apps/plugins/puzzles/resource.h
new file mode 100644
index 0000000000..f0bfa16d6d
--- /dev/null
+++ b/apps/plugins/puzzles/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/rockbox.c b/apps/plugins/puzzles/rockbox.c
new file mode 100644
index 0000000000..3a1c00e615
--- /dev/null
+++ b/apps/plugins/puzzles/rockbox.c
@@ -0,0 +1,1682 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2016 Franklin Wei
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ***************************************************************************/
21
22/* rockbox frontend for puzzles */
23
24#include "plugin.h"
25
26#include "puzzles.h"
27#include "keymaps.h"
28
29#ifndef COMBINED
30#include "lib/playback_control.h"
31#endif
32#include "lib/xlcd.h"
33
34/* how many ticks between timer callbacks */
35#define TIMER_INTERVAL (HZ / 50)
36#define BG_R .9f /* very light gray */
37#define BG_G .9f
38#define BG_B .9f
39
40#ifdef COMBINED
41#define SAVE_FILE PLUGIN_GAMES_DATA_DIR "/puzzles.sav"
42#else
43static char save_file_path[MAX_PATH];
44#define SAVE_FILE ((const char*)save_file_path)
45#endif
46
47#define BG_COLOR LCD_RGBPACK((int)(255*BG_R), (int)(255*BG_G), (int)(255*BG_B))
48
49#define MURICA
50
51#ifdef MURICA
52#define midend_serialize midend_serialise
53#define midend_deserialize midend_deserialise
54#define frontend_default_color frontend_default_colour
55#define midend_colors midend_colours
56#endif
57
58static midend *me = NULL;
59static unsigned *colors = NULL;
60static int ncolors = 0;
61static long last_keystate = 0;
62
63static void fix_size(void);
64
65static void rb_start_draw(void *handle)
66{
67 (void) handle;
68}
69
70static struct viewport clip_rect;
71static bool clipped = false;
72
73static struct settings_t {
74 int slowmo_factor;
75 bool bulk, timerflash;
76} settings;
77
78/* clipping is implemented through viewports and offsetting
79 * coordinates */
80static void rb_clip(void *handle, int x, int y, int w, int h)
81{
82 LOGF("rb_clip(%d %d %d %d)", x, y, w, h);
83 clip_rect.x = x;
84 clip_rect.y = y;
85 clip_rect.width = w;
86 clip_rect.height = h;
87 clip_rect.font = FONT_UI;
88 clip_rect.drawmode = DRMODE_SOLID;
89#if LCD_DEPTH > 1
90 clip_rect.fg_pattern = LCD_DEFAULT_FG;
91 clip_rect.bg_pattern = LCD_DEFAULT_BG;
92#endif
93 rb->lcd_set_viewport(&clip_rect);
94 clipped = true;
95}
96
97static void rb_unclip(void *handle)
98{
99 LOGF("rb_unclip");
100 rb->lcd_set_viewport(NULL);
101 clipped = false;
102}
103
104static void offset_coords(int *x, int *y)
105{
106 if(clipped)
107 {
108 *x -= clip_rect.x;
109 *y -= clip_rect.y;
110 }
111}
112
113static void rb_color(int n)
114{
115 if(n < 0)
116 {
117 fatal("bad color %d", n);
118 return;
119 }
120 rb->lcd_set_foreground(colors[n]);
121}
122
123static void rb_draw_text(void *handle, int x, int y, int fonttype,
124 int fontsize, int align, int color, char *text)
125{
126 (void) fontsize;
127 LOGF("rb_draw_text(%d %d %s)", x, y, text);
128
129 offset_coords(&x, &y);
130
131 /* TODO: variable font size */
132 switch(fonttype)
133 {
134 case FONT_FIXED:
135 rb->lcd_setfont(FONT_SYSFIXED);
136 break;
137 case FONT_VARIABLE:
138 rb->lcd_setfont(FONT_UI);
139 break;
140 default:
141 fatal("bad font");
142 break;
143 }
144
145 int w, h;
146 rb->lcd_getstringsize(text, &w, &h);
147
148 static int cap_h = -1;
149 if(cap_h < 0)
150 rb->lcd_getstringsize("X", NULL, &cap_h);
151
152 if(align & ALIGN_VNORMAL)
153 y -= h;
154 else if(align & ALIGN_VCENTRE)
155 y -= cap_h / 2;
156
157 if(align & ALIGN_HCENTRE)
158 x -= w / 2;
159 else if(align & ALIGN_HRIGHT)
160 x -= w;
161
162 rb_color(color);
163 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
164 rb->lcd_putsxy(x, y, text);
165 rb->lcd_set_drawmode(DRMODE_SOLID);
166}
167
168static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color)
169{
170 LOGF("rb_draw_rect(%d, %d, %d, %d, %d)", x, y, w, h, color);
171 rb_color(color);
172 offset_coords(&x, &y);
173 rb->lcd_fillrect(x, y, w, h);
174}
175
176static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2,
177 int color)
178{
179 LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color);
180 offset_coords(&x1, &y1);
181 offset_coords(&x2, &y2);
182 rb_color(color);
183 rb->lcd_drawline(x1, y1, x2, y2);
184}
185
186/*
187 * draw filled polygon
188 * originally by Sebastian Leonhardt (ulmutul)
189 * 'count' : number of coordinate pairs
190 * 'pxy': array of coordinates. pxy[0]=x0,pxy[1]=y0,...
191 * note: provide space for one extra coordinate, because the starting point
192 * will automatically be inserted as end point.
193 */
194
195/*
196 * helper function:
197 * find points of intersection between polygon and scanline
198 */
199
200#define MAX_INTERSECTION 32
201
202static void fill_poly_line(int scanline, int count, int *pxy)
203{
204 int i;
205 int j;
206 int num_of_intersects;
207 int direct, old_direct;
208 //intersections of every line with scanline (y-coord)
209 int intersection[MAX_INTERSECTION];
210 /* add starting point as ending point */
211 pxy[count*2] = pxy[0];
212 pxy[count*2+1] = pxy[1];
213
214 old_direct=0;
215 num_of_intersects=0;
216 for (i=0; i<count*2; i+=2) {
217 int x1=pxy[i];
218 int y1=pxy[i+1];
219 int x2=pxy[i+2];
220 int y2=pxy[i+3];
221 // skip if line is outside of scanline
222 if (y1 < y2) {
223 if (scanline < y1 || scanline > y2)
224 continue;
225 }
226 else {
227 if (scanline < y2 || scanline > y1)
228 continue;
229 }
230 // calculate x-coord of intersection
231 if (y1==y2) {
232 direct=0;
233 }
234 else {
235 direct = y1>y2 ? 1 : -1;
236 // omit double intersections, if both lines lead in the same direction
237 intersection[num_of_intersects] =
238 x1+((scanline-y1)*(x2-x1))/(y2-y1);
239 if ( (direct!=old_direct)
240 || (intersection[num_of_intersects] != intersection[num_of_intersects-1])
241 )
242 ++num_of_intersects;
243 }
244 old_direct = direct;
245 }
246
247 // sort points of intersection
248 for (i=0; i<num_of_intersects-1; ++i) {
249 for (j=i+1; j<num_of_intersects; ++j) {
250 if (intersection[j]<intersection[i]) {
251 int temp=intersection[i];
252 intersection[i]=intersection[j];
253 intersection[j]=temp;
254 }
255 }
256 }
257 // draw
258 for (i=0; i<num_of_intersects; i+=2) {
259 rb->lcd_hline(intersection[i], intersection[i+1], scanline);
260 }
261}
262
263/* two extra elements at end of pxy needed */
264static void v_fillarea(int count, int *pxy)
265{
266 int i;
267 int y1, y2;
268
269 // find min and max y coords
270 y1=y2=pxy[1];
271 for (i=3; i<count*2; i+=2) {
272 if (pxy[i] < y1) y1 = pxy[i];
273 else if (pxy[i] > y2) y2 = pxy[i];
274 }
275
276 for (i=y1; i<=y2; ++i) {
277 fill_poly_line(i, count, pxy);
278 }
279}
280
281static void rb_draw_poly(void *handle, int *coords, int npoints,
282 int fillcolor, int outlinecolor)
283{
284 LOGF("rb_draw_poly");
285
286 if(fillcolor >= 0)
287 {
288 rb_color(fillcolor);
289#if 1
290 /* serious hack: draw a bunch of triangles between adjacent points */
291 /* this generally works, even with some concave polygons */
292 for(int i = 2; i < npoints; ++i)
293 {
294 int x1, y1, x2, y2, x3, y3;
295 x1 = coords[0];
296 y1 = coords[1];
297 x2 = coords[(i - 1) * 2];
298 y2 = coords[(i - 1) * 2 + 1];
299 x3 = coords[i * 2];
300 y3 = coords[i * 2 + 1];
301 offset_coords(&x1, &y1);
302 offset_coords(&x2, &y2);
303 offset_coords(&x3, &y3);
304 xlcd_filltriangle(x1, y1,
305 x2, y2,
306 x3, y3);
307
308#if 0
309 /* debug code */
310 rb->lcd_set_foreground(LCD_RGBPACK(255,0,0));
311 rb->lcd_drawpixel(x1, y1);
312 rb->lcd_drawpixel(x2, y2);
313 rb->lcd_drawpixel(x3, y3);
314 rb->lcd_update();
315 rb->sleep(HZ);
316 rb_color(fillcolor);
317 rb->lcd_drawpixel(x1, y1);
318 rb->lcd_drawpixel(x2, y2);
319 rb->lcd_drawpixel(x3, y3);
320 rb->lcd_update();
321#endif
322 }
323#else
324 int *pxy = smalloc(sizeof(int) * 2 * npoints + 2);
325 /* copy points, offsetted */
326 for(int i = 0; i < npoints; ++i)
327 {
328 pxy[2 * i + 0] = coords[2 * i + 0];
329 pxy[2 * i + 1] = coords[2 * i + 1];
330 offset_coords(&pxy[2*i+0], &pxy[2*i+1]);
331 }
332 v_fillarea(npoints, pxy);
333 sfree(pxy);
334#endif
335 }
336
337 /* draw outlines last so they're not covered by the fill */
338 assert(outlinecolor >= 0);
339 rb_color(outlinecolor);
340
341 for(int i = 1; i < npoints; ++i)
342 {
343 int x1, y1, x2, y2;
344 x1 = coords[2 * (i - 1)];
345 y1 = coords[2 * (i - 1) + 1];
346 x2 = coords[2 * i];
347 y2 = coords[2 * i + 1];
348 offset_coords(&x1, &y1);
349 offset_coords(&x2, &y2);
350 rb->lcd_drawline(x1, y1,
351 x2, y2);
352 //rb->lcd_update();
353 //rb->sleep(HZ/2);
354 }
355
356 int x1, y1, x2, y2;
357 x1 = coords[0];
358 y1 = coords[1];
359 x2 = coords[2 * (npoints - 1)];
360 y2 = coords[2 * (npoints - 1) + 1];
361 offset_coords(&x1, &y1);
362 offset_coords(&x2, &y2);
363
364 rb->lcd_drawline(x1, y1,
365 x2, y2);
366}
367
368static void rb_draw_circle(void *handle, int cx, int cy, int radius,
369 int fillcolor, int outlinecolor)
370{
371 LOGF("rb_draw_circle(%d, %d, %d)", cx, cy, radius);
372 offset_coords(&cx, &cy);
373
374 if(fillcolor >= 0)
375 {
376 rb_color(fillcolor);
377 xlcd_fillcircle(cx, cy, radius - 1);
378 }
379
380 assert(outlinecolor >= 0);
381 rb_color(outlinecolor);
382 xlcd_drawcircle(cx, cy, radius - 1);
383}
384
385struct blitter {
386 bool have_data;
387 int x, y;
388 struct bitmap bmp;
389};
390
391static blitter *rb_blitter_new(void *handle, int w, int h)
392{
393 LOGF("rb_blitter_new");
394 blitter *b = snew(blitter);
395 b->bmp.width = w;
396 b->bmp.height = h;
397 b->bmp.data = smalloc(w * h * sizeof(fb_data));
398 b->have_data = false;
399 return b;
400}
401
402static void rb_blitter_free(void *handle, blitter *bl)
403{
404 LOGF("rb_blitter_free");
405 sfree(bl->bmp.data);
406 sfree(bl);
407 return;
408}
409
410static void trim_rect(int *x, int *y, int *w, int *h)
411{
412 int x0, x1, y0, y1;
413
414 /*
415 * Reduce the size of the copied rectangle to stop it going
416 * outside the bounds of the canvas.
417 */
418
419 /* Transform from x,y,w,h form into coordinates of all edges */
420 x0 = *x;
421 y0 = *y;
422 x1 = *x + *w;
423 y1 = *y + *h;
424
425 /* Clip each coordinate at both extremes of the canvas */
426 x0 = (x0 < 0 ? 0 : x0 > LCD_WIDTH ? LCD_WIDTH : x0);
427 x1 = (x1 < 0 ? 0 : x1 > LCD_WIDTH ? LCD_WIDTH : x1);
428 y0 = (y0 < 0 ? 0 : y0 > LCD_HEIGHT ? LCD_HEIGHT : y0);
429 y1 = (y1 < 0 ? 0 : y1 > LCD_HEIGHT ? LCD_HEIGHT : y1);
430
431 /* Transform back into x,y,w,h to return */
432 *x = x0;
433 *y = y0;
434 *w = x1 - x0;
435 *h = y1 - y0;
436}
437
438/* copy a section of the framebuffer */
439static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
440{
441 /* no viewport offset */
442#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
443#error no vertical stride
444#else
445 if(bl->bmp.data)
446 {
447 int w = bl->bmp.width, h = bl->bmp.height;
448 trim_rect(&x, &y, &w, &h);
449 LOGF("rb_blitter_save(%d, %d, %d, %d)", x, y, w, h);
450 for(int i = 0; i < h; ++i)
451 {
452 /* copy line-by-line */
453 rb->memcpy(bl->bmp.data + sizeof(fb_data) * i * w,
454 rb->lcd_framebuffer + (y + i) * LCD_WIDTH + x,
455 w * sizeof(fb_data));
456 }
457 bl->x = x;
458 bl->y = y;
459 bl->have_data = true;
460 }
461#endif
462}
463
464static void rb_blitter_load(void *handle, blitter *bl, int x, int y)
465{
466 LOGF("rb_blitter_load");
467 if(!bl->have_data)
468 return;
469 int w = bl->bmp.width, h = bl->bmp.height;
470
471 if(x == BLITTER_FROMSAVED) x = bl->x;
472 if(y == BLITTER_FROMSAVED) y = bl->y;
473
474 offset_coords(&x, &y);
475 trim_rect(&x, &y, &w, &h);
476 rb->lcd_bitmap((fb_data*)bl->bmp.data, x, y, w, h);
477}
478
479static void rb_draw_update(void *handle, int x, int y, int w, int h)
480{
481 LOGF("rb_draw_update(%d, %d, %d, %d)", x, y, w, h);
482 if(!settings.bulk)
483 rb->lcd_update_rect(x, y, w, h);
484 else
485 rb->lcd_update();
486}
487
488static void rb_end_draw(void *handle)
489{
490 LOGF("rb_end_draw");
491}
492
493static char *titlebar = NULL;
494
495static void rb_status_bar(void *handle, char *text)
496{
497 if(titlebar)
498 sfree(titlebar);
499 titlebar = dupstr(text);
500 LOGF("game title is %s\n", text);
501}
502
503static void draw_title(void)
504{
505 const char *str = NULL;
506 if(titlebar)
507 str = titlebar;
508 else
509 str = midend_which_game(me)->name;
510
511 /* quick hack */
512 bool orig_clipped = clipped;
513 if(orig_clipped)
514 rb_unclip(NULL);
515
516 int h;
517 rb->lcd_setfont(FONT_UI);
518 rb->lcd_getstringsize(str, NULL, &h);
519
520 rb->lcd_set_foreground(BG_COLOR);
521 rb->lcd_fillrect(0, LCD_HEIGHT - h, LCD_WIDTH, h);
522
523 rb->lcd_set_foreground(LCD_BLACK);
524 rb->lcd_putsxy(0, LCD_HEIGHT - h, str);
525 rb->lcd_update_rect(0, LCD_HEIGHT - h, LCD_WIDTH, h);
526
527 if(orig_clipped)
528 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
529}
530
531static char *rb_text_fallback(void *handle, const char *const *strings,
532 int nstrings)
533{
534 return dupstr(strings[0]);
535}
536
537const drawing_api rb_drawing = {
538 rb_draw_text,
539 rb_draw_rect,
540 rb_draw_line,
541 rb_draw_poly,
542 rb_draw_circle,
543 rb_draw_update,
544 rb_clip,
545 rb_unclip,
546 rb_start_draw,
547 rb_end_draw,
548 rb_status_bar,
549 rb_blitter_new,
550 rb_blitter_free,
551 rb_blitter_save,
552 rb_blitter_load,
553 NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
554 NULL, NULL, /* line_width, line_dotted */
555 rb_text_fallback,
556 NULL,
557};
558
559void frontend_default_color(frontend *fe, float *out)
560{
561 *out++ = BG_R;
562 *out++ = BG_G;
563 *out++ = BG_B;
564}
565
566void fatal(char *fmt, ...)
567{
568 va_list ap;
569
570 rb->splash(HZ, "FATAL ERROR");
571
572 va_start(ap, fmt);
573 char buf[80];
574 rb->vsnprintf(buf, 80, fmt, ap);
575 rb->splash(HZ * 2, buf);
576 va_end(ap);
577
578 exit(1);
579}
580
581void get_random_seed(void **randseed, int *randseedsize)
582{
583 *randseed = snew(long);
584 long seed = *rb->current_tick;
585 rb->memcpy(*randseed, &seed, sizeof(seed));
586 //*(long*)*randseed = 42; // debug
587 //rb->splash(HZ, "DEBUG SEED ON");
588 *randseedsize = sizeof(long);
589}
590
591const char *config_choices_formatter(int sel, void *data, char *buf, size_t len)
592{
593 /* we can't rely on being called in any particular order */
594 char *list = dupstr(data);
595 char delimbuf[2] = { *list, 0 };
596 char *save = NULL;
597 char *str = rb->strtok_r(list, delimbuf, &save);
598 for(int i = 0; i < sel; ++i)
599 str = rb->strtok_r(NULL, delimbuf, &save);
600 rb->snprintf(buf, len, "%s", str);
601 sfree(list);
602 return buf;
603}
604
605static int list_choose(const char *list_str, const char *title)
606{
607 char delim = *list_str;
608
609 const char *ptr = list_str + 1;
610 int n = 0;
611 while(ptr)
612 {
613 n++;
614 ptr = strchr(ptr + 1, delim);
615 }
616
617 struct gui_synclist list;
618
619 rb->gui_synclist_init(&list, &config_choices_formatter, (void*)list_str, false, 1, NULL);
620 rb->gui_synclist_set_icon_callback(&list, NULL);
621 rb->gui_synclist_set_nb_items(&list, n);
622 rb->gui_synclist_limit_scroll(&list, false);
623
624 rb->gui_synclist_select_item(&list, 0);
625
626 rb->gui_synclist_set_title(&list, (char*)title, NOICON);
627 while (1)
628 {
629 rb->gui_synclist_draw(&list);
630 int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
631 if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
632 continue;
633 switch(button)
634 {
635 case ACTION_STD_OK:
636 return rb->gui_synclist_get_sel_pos(&list);
637 case ACTION_STD_PREV:
638 case ACTION_STD_CANCEL:
639 return -1;
640 default:
641 break;
642 }
643 }
644}
645
646static void do_configure_item(config_item *cfg)
647{
648 switch(cfg->type)
649 {
650 case C_STRING:
651 {
652#define MAX_STRLEN 128
653 char *newstr = smalloc(MAX_STRLEN);
654 rb->strlcpy(newstr, cfg->sval, MAX_STRLEN);
655 rb->lcd_set_foreground(LCD_WHITE);
656 rb->lcd_set_background(LCD_BLACK);
657 if(rb->kbd_input(newstr, MAX_STRLEN) < 0)
658 {
659 sfree(newstr);
660 break;
661 }
662 sfree(cfg->sval);
663 cfg->sval = newstr;
664 break;
665 }
666 case C_BOOLEAN:
667 {
668 bool res = cfg->ival != 0;
669 rb->set_bool(cfg->name, &res);
670 cfg->ival = res;
671 break;
672 }
673 case C_CHOICES:
674 {
675 int sel = list_choose(cfg->sval, cfg->name);
676 if(sel >= 0)
677 cfg->ival = sel;
678 break;
679 }
680 default:
681 fatal("bad type");
682 break;
683 }
684}
685
686const char *config_formatter(int sel, void *data, char *buf, size_t len)
687{
688 config_item *cfg = data;
689 cfg += sel;
690 rb->snprintf(buf, len, "%s", cfg->name);
691 return buf;
692}
693
694static void config_menu(void)
695{
696 char *title;
697 config_item *config = midend_get_config(me, CFG_SETTINGS, &title);
698
699 if(!config)
700 {
701 rb->splash(HZ, "Nothing to configure.");
702 goto done;
703 }
704
705 /* count */
706 int n = 0;
707 config_item *ptr = config;
708 while(ptr->type != C_END)
709 {
710 n++;
711 ptr++;
712 }
713
714 /* display a list */
715 struct gui_synclist list;
716
717 rb->gui_synclist_init(&list, &config_formatter, config, false, 1, NULL);
718 rb->gui_synclist_set_icon_callback(&list, NULL);
719 rb->gui_synclist_set_nb_items(&list, n);
720 rb->gui_synclist_limit_scroll(&list, false);
721
722 rb->gui_synclist_select_item(&list, 0);
723
724 bool done = false;
725 rb->gui_synclist_set_title(&list, title, NOICON);
726 while (!done)
727 {
728 rb->gui_synclist_draw(&list);
729 int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
730 if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
731 continue;
732 switch(button)
733 {
734 case ACTION_STD_OK:
735 {
736 config_item old;
737 int pos = rb->gui_synclist_get_sel_pos(&list);
738 memcpy(&old, config + pos, sizeof(old));
739 do_configure_item(config + pos);
740 char *err = midend_set_config(me, CFG_SETTINGS, config);
741 if(err)
742 {
743 rb->splash(HZ, err);
744 memcpy(config + pos, &old, sizeof(old));
745 }
746 break;
747 }
748 case ACTION_STD_PREV:
749 case ACTION_STD_CANCEL:
750 done = true;
751 break;
752 default:
753 break;
754 }
755 }
756
757done:
758 sfree(title);
759 free_cfg(config);
760}
761
762const char *preset_formatter(int sel, void *data, char *buf, size_t len)
763{
764 char *name;
765 game_params *junk;
766 midend_fetch_preset(me, sel, &name, &junk);
767 rb->strlcpy(buf, name, len);
768 return buf;
769}
770
771static void presets_menu(void)
772{
773 if(!midend_num_presets(me))
774 {
775 rb->splash(HZ, "No presets!");
776 return;
777 }
778
779 /* display a list */
780 struct gui_synclist list;
781
782 rb->gui_synclist_init(&list, &preset_formatter, NULL, false, 1, NULL);
783 rb->gui_synclist_set_icon_callback(&list, NULL);
784 rb->gui_synclist_set_nb_items(&list, midend_num_presets(me));
785 rb->gui_synclist_limit_scroll(&list, false);
786
787 int current = midend_which_preset(me);
788 rb->gui_synclist_select_item(&list, current >= 0 ? current : 0);
789
790 bool done = false;
791 rb->gui_synclist_set_title(&list, "Game Type", NOICON);
792 while (!done)
793 {
794 rb->gui_synclist_draw(&list);
795 int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
796 if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
797 continue;
798 switch(button)
799 {
800 case ACTION_STD_OK:
801 {
802 int sel = rb->gui_synclist_get_sel_pos(&list);
803 char *junk;
804 game_params *params;
805 midend_fetch_preset(me, sel, &junk, &params);
806 midend_set_params(me, params);
807 done = true;
808 break;
809 }
810 case ACTION_STD_PREV:
811 case ACTION_STD_CANCEL:
812 done = true;
813 break;
814 default:
815 break;
816 }
817 }
818}
819
820static const struct {
821 const char *game, *help;
822} quick_help_text[] = {
823 { "Black Box", "Find the hidden balls in the box by bouncing laser beams off them." },
824 { "Bridges", "Connect all the islands with a network of bridges." },
825 { "Cube", "Pick up all the blue squares by rolling the cube over them." },
826 { "Dominosa", "Tile the rectangle with a full set of dominoes." },
827 { "Fifteen", "Slide the tiles around to arrange them into order." },
828 { "Filling", "Mark every square with the area of its containing region." },
829 { "Flip", "Flip groups of squares to light them all up at once." },
830 { "Flood", "Turn the grid the same colour in as few flood fills as possible." },
831 { "Galaxies", "Divide the grid into rotationally symmetric regions each centred on a dot." },
832 { "Guess", "Guess the hidden combination of colours." },
833 { "Inertia", "Collect all the gems without running into any of the mines." },
834 { "Keen", "Complete the latin square in accordance with the arithmetic clues." },
835 { "Light Up", "Place bulbs to light up all the squares." },
836 { "Loopy", "Draw a single closed loop, given clues about number of adjacent edges." },
837 { "Magnets", "Place magnets to satisfy the clues and avoid like poles touching." },
838 { "Map", "Colour the map so that adjacent regions are never the same colour." },
839 { "Mines", "Find all the mines without treading on any of them." },
840 { "Net", "Rotate each tile to reassemble the network." },
841 { "Netslide", "Slide a row at a time to reassemble the network." },
842 { "Palisade", "Divide the grid into equal-sized areas in accordance with the clues." },
843 { "Pattern", "Fill in the pattern in the grid, given only the lengths of runs of black squares." },
844 { "Pearl", "Draw a single closed loop, given clues about corner and straight squares." },
845 { "Pegs", "Jump pegs over each other to remove all but one." },
846 { "Range", "Place black squares to limit the visible distance from each numbered cell." },
847 { "Rectangles", "Divide the grid into rectangles with areas equal to the numbers." },
848 { "Same Game", "Clear the grid by removing touching groups of the same colour squares." },
849 { "Signpost", "Connect the squares into a path following the arrows." },
850 { "Singles", "Black out the right set of duplicate numbers." },
851 { "Sixteen", "Slide a row at a time to arrange the tiles into order." },
852 { "Slant", "Draw a maze of slanting lines that matches the clues." },
853 { "Solo", "Fill in the grid so that each row, column and square block contains one of every digit." },
854 { "Tents", "Place a tent next to each tree." },
855 { "Towers", "Complete the latin square of towers in accordance with the clues." },
856 { "Tracks", "Fill in the railway track according to the clues." },
857 { "Twiddle", "Rotate the tiles around themselves to arrange them into order." },
858 { "Undead", "Place ghosts, vampires and zombies so that the right numbers of them can be seen in mirrors." },
859 { "Unequal", "Complete the latin square in accordance with the > signs." },
860 { "Unruly", "Fill in the black and white grid to avoid runs of three." },
861 { "Untangle", "Reposition the points so that the lines do not cross." },
862};
863
864static void quick_help(void)
865{
866 for(int i = 0; i < ARRAYLEN(quick_help_text); ++i)
867 {
868 if(!strcmp(midend_which_game(me)->name, quick_help_text[i].game))
869 {
870 rb->splash(0, quick_help_text[i].help);
871 rb->button_get(true);
872 return;
873 }
874 }
875}
876
877static void full_help(void)
878{
879 /* TODO */
880}
881
882static void init_default_settings(void)
883{
884 settings.slowmo_factor = 1;
885 settings.bulk = false;
886 settings.timerflash = false;
887}
888
889static void debug_menu(void)
890{
891 MENUITEM_STRINGLIST(menu, "Debug Menu", NULL,
892 "Slowmo factor",
893 "Randomize colors",
894 "Toggle bulk update",
895 "Toggle flash pixel on timer",
896 "Back");
897 bool quit = false;
898 int sel = 0;
899 while(!quit)
900 {
901 switch(rb->do_menu(&menu, &sel, NULL, false))
902 {
903 case 0:
904 rb->set_int("Slowmo factor", "", UNIT_INT, &settings.slowmo_factor, NULL, 1, 1, 10, NULL);
905 break;
906 case 1:
907 {
908 unsigned *ptr = colors;
909 for(int i = 0; i < ncolors; ++i)
910 {
911 /* not seeded, who cares? */
912 *ptr++ = LCD_RGBPACK(rb->rand()%255, rb->rand()%255, rb->rand()%255);
913 }
914 break;
915 }
916 case 2:
917 settings.bulk = !settings.bulk;
918 break;
919 case 3:
920 settings.timerflash = !settings.timerflash;
921 break;
922 case 4:
923 default:
924 quit = true;
925 break;
926 }
927 }
928}
929
930static int pausemenu_cb(int action, const struct menu_item_ex *this_item)
931{
932 int i = (intptr_t) this_item;
933 if(action == ACTION_REQUEST_MENUITEM)
934 {
935 switch(i)
936 {
937 case 3:
938 if(!midend_can_undo(me))
939 return ACTION_EXIT_MENUITEM;
940 break;
941 case 4:
942 if(!midend_can_redo(me))
943 return ACTION_EXIT_MENUITEM;
944 break;
945 case 5:
946 if(!midend_which_game(me)->can_solve)
947 return ACTION_EXIT_MENUITEM;
948 break;
949 case 7:
950#ifdef FOR_REAL
951 return ACTION_EXIT_MENUITEM;
952#else
953 break;
954#endif
955 case 8:
956 if(!midend_num_presets(me))
957 return ACTION_EXIT_MENUITEM;
958 break;
959 case 9:
960#ifdef FOR_REAL
961 return ACTION_EXIT_MENUITEM;
962#else
963 break;
964#endif
965 case 10:
966 if(!midend_which_game(me)->can_configure)
967 return ACTION_EXIT_MENUITEM;
968 break;
969 default:
970 break;
971 }
972 }
973 return action;
974}
975
976static int pause_menu(void)
977{
978#define static auto
979#define const
980 MENUITEM_STRINGLIST(menu, NULL, pausemenu_cb,
981 "Resume Game",
982 "New Game",
983 "Restart Game",
984 "Undo",
985 "Redo",
986 "Solve",
987 "Quick Help",
988 "Extensive Help",
989 "Game Type",
990 "Debug Menu",
991 "Configure Game",
992#ifdef COMBINED
993 "Select Another Game",
994#endif
995 "Quit without Saving",
996 "Quit");
997#undef static
998#undef const
999 /* HACK ALERT */
1000 char title[32] = { 0 };
1001 rb->snprintf(title, sizeof(title), "%s Menu", midend_which_game(me)->name);
1002 menu__.desc = title;
1003
1004 bool quit = false;
1005 int sel = 0;
1006 while(!quit)
1007 {
1008 switch(rb->do_menu(&menu, &sel, NULL, false))
1009 {
1010 case 0:
1011 quit = true;
1012 break;
1013 case 1:
1014 midend_new_game(me);
1015 fix_size();
1016 quit = true;
1017 break;
1018 case 2:
1019 midend_restart_game(me);
1020 fix_size();
1021 quit = true;
1022 break;
1023 case 3:
1024 if(!midend_can_undo(me))
1025 rb->splash(HZ, "Cannot undo.");
1026 else
1027 midend_process_key(me, 0, 0, 'u');
1028 quit = true;
1029 break;
1030 case 4:
1031 if(!midend_can_redo(me))
1032 rb->splash(HZ, "Cannot redo.");
1033 else
1034 midend_process_key(me, 0, 0, 'r');
1035 quit = true;
1036 break;
1037 case 5:
1038 {
1039 char *msg = midend_solve(me);
1040 if(msg)
1041 rb->splash(HZ, msg);
1042 quit = true;
1043 break;
1044 }
1045 case 6:
1046 quick_help();
1047 break;
1048 case 7:
1049 full_help();
1050 break;
1051 case 8:
1052 presets_menu();
1053 midend_new_game(me);
1054 fix_size();
1055 quit = true;
1056 break;
1057 case 9:
1058 debug_menu();
1059 break;
1060 case 10:
1061 config_menu();
1062 midend_new_game(me);
1063 fix_size();
1064 quit = true;
1065 break;
1066#ifdef COMBINED
1067 case 11:
1068 return -1;
1069 case 12:
1070 return -2;
1071 case 13:
1072 return -3;
1073#else
1074 case 11:
1075 return -2;
1076 case 12:
1077 return -3;
1078#endif
1079 default:
1080 break;
1081 }
1082 }
1083 rb->lcd_set_background(BG_COLOR);
1084 rb->lcd_clear_display();
1085 rb->lcd_update();
1086 midend_force_redraw(me);
1087 return 0;
1088}
1089
1090static bool want_redraw = true;
1091static bool accept_input = true;
1092
1093/* ignore the excess of LOGFs below... */
1094#ifdef LOGF_ENABLE
1095#undef LOGF_ENABLE
1096#endif
1097static int process_input(int tmo)
1098{
1099 LOGF("process_input start");
1100 LOGF("------------------");
1101 int state = 0;
1102
1103#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1104 rb->cpu_boost(false); /* about to block for button input */
1105#endif
1106
1107 int button = rb->button_get_w_tmo(tmo);
1108
1109 /* weird stuff */
1110 exit_on_usb(button);
1111
1112 button = rb->button_status();
1113
1114#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1115 rb->cpu_boost(true);
1116#endif
1117
1118 if(button == BTN_PAUSE)
1119 {
1120 want_redraw = false;
1121 /* quick hack to preserve the clipping state */
1122 bool orig_clipped = clipped;
1123 if(orig_clipped)
1124 rb_unclip(NULL);
1125
1126 int rc = pause_menu();
1127
1128 if(orig_clipped)
1129 rb_clip(NULL, clip_rect.x, clip_rect.y, clip_rect.width, clip_rect.height);
1130
1131 last_keystate = 0;
1132 accept_input = true;
1133
1134 return rc;
1135 }
1136
1137 /* special case for inertia: moves occur after RELEASES */
1138 if(!strcmp("Inertia", midend_which_game(me)->name))
1139 {
1140 LOGF("received button 0x%08x", button);
1141
1142 unsigned released = ~button & last_keystate;
1143 last_keystate = button;
1144
1145 if(!button)
1146 {
1147 if(!accept_input)
1148 {
1149 LOGF("ignoring, all keys released but not accepting input before, can accept input later");
1150 accept_input = true;
1151 return 0;
1152 }
1153 }
1154
1155 if(!released || !accept_input)
1156 {
1157 LOGF("released keys detected: 0x%08x", released);
1158 LOGF("ignoring, either no keys released or not accepting input");
1159 return 0;
1160 }
1161
1162 button |= released;
1163 if(last_keystate)
1164 {
1165 LOGF("ignoring input from now until all released");
1166 accept_input = false;
1167 }
1168 LOGF("accepting event 0x%08x", button);
1169 }
1170 /* not inertia: events fire on presses */
1171 else
1172 {
1173 /* start accepting input again after a release */
1174 if(!button)
1175 {
1176 accept_input = true;
1177 return 0;
1178 }
1179 /* ignore repeats */
1180 if(!accept_input)
1181 return 0;
1182 accept_input = false;
1183 }
1184
1185 switch(button)
1186 {
1187 case BTN_UP:
1188 state = CURSOR_UP;
1189 break;
1190 case BTN_DOWN:
1191 state = CURSOR_DOWN;
1192 break;
1193 case BTN_LEFT:
1194 state = CURSOR_LEFT;
1195 break;
1196 case BTN_RIGHT:
1197 state = CURSOR_RIGHT;
1198 break;
1199
1200 /* handle diagonals (mainly for Inertia) */
1201 case BTN_DOWN | BTN_LEFT:
1202#ifdef BTN_DOWN_LEFT
1203 case BTN_DOWN_LEFT:
1204#endif
1205 state = '1' | MOD_NUM_KEYPAD;
1206 break;
1207 case BTN_DOWN | BTN_RIGHT:
1208#ifdef BTN_DOWN_RIGHT
1209 case BTN_DOWN_RIGHT:
1210#endif
1211 state = '3' | MOD_NUM_KEYPAD;
1212 break;
1213 case BTN_UP | BTN_LEFT:
1214#ifdef BTN_UP_LEFT
1215 case BTN_UP_LEFT:
1216#endif
1217 state = '7' | MOD_NUM_KEYPAD;
1218 break;
1219 case BTN_UP | BTN_RIGHT:
1220#ifdef BTN_UP_RIGHT
1221 case BTN_UP_RIGHT:
1222#endif
1223 state = '9' | MOD_NUM_KEYPAD;
1224 break;
1225
1226 case BTN_FIRE:
1227 state = CURSOR_SELECT;
1228 break;
1229 }
1230 LOGF("process_input done");
1231 LOGF("------------------");
1232 return state;
1233}
1234
1235static long last_tstamp;
1236
1237static void timer_cb(void)
1238{
1239#if LCD_DEPTH != 24
1240 if(settings.timerflash)
1241 {
1242 static bool what = false;
1243 what = !what;
1244 if(what)
1245 rb->lcd_framebuffer[0] = LCD_BLACK;
1246 else
1247 rb->lcd_framebuffer[0] = LCD_WHITE;
1248 rb->lcd_update();
1249 }
1250#endif
1251
1252 LOGF("timer callback");
1253 midend_timer(me, ((float)(*rb->current_tick - last_tstamp) / (float)HZ) / settings.slowmo_factor);
1254 last_tstamp = *rb->current_tick;
1255}
1256
1257static volatile bool timer_on = false;
1258
1259void activate_timer(frontend *fe)
1260{
1261 last_tstamp = *rb->current_tick;
1262 timer_on = true;
1263}
1264
1265void deactivate_timer(frontend *fe)
1266{
1267 timer_on = false;
1268}
1269
1270#ifdef COMBINED
1271/* can't use audio buffer */
1272static char giant_buffer[1024*1024*4];
1273#else
1274/* points to audiobuf */
1275static char *giant_buffer = NULL;
1276#endif
1277static size_t giant_buffer_len = 0; /* set on start */
1278
1279#ifdef COMBINED
1280const char *formatter(char *buf, size_t n, int i, const char *unit)
1281{
1282 rb->snprintf(buf, n, "%s", gamelist[i]->name);
1283 return buf;
1284}
1285#endif
1286
1287static void fix_size(void)
1288{
1289 int w = LCD_WIDTH, h = LCD_HEIGHT, h_x;
1290 rb->lcd_setfont(FONT_UI);
1291 rb->lcd_getstringsize("X", NULL, &h_x);
1292 h -= h_x;
1293 midend_size(me, &w, &h, TRUE);
1294}
1295
1296static void reset_tlsf(void)
1297{
1298 /* reset tlsf by nuking the signature */
1299 /* will make any already-allocated memory point to garbage */
1300 memset(giant_buffer, 0, 4);
1301 init_memory_pool(giant_buffer_len, giant_buffer);
1302}
1303
1304static int read_wrapper(void *ptr, void *buf, int len)
1305{
1306 int fd = (int) ptr;
1307 return rb->read(fd, buf, len);
1308}
1309
1310static void write_wrapper(void *ptr, void *buf, int len)
1311{
1312 int fd = (int) ptr;
1313 rb->write(fd, buf, len);
1314}
1315
1316static void clear_and_draw(void)
1317{
1318 rb->lcd_clear_display();
1319 rb->lcd_update();
1320
1321 midend_force_redraw(me);
1322 draw_title();
1323}
1324
1325static char *init_for_game(const game *gm, int load_fd, bool draw)
1326{
1327 /* if we are loading a game tlsf has already been initialized */
1328 if(load_fd < 0)
1329 reset_tlsf();
1330
1331 me = midend_new(NULL, gm, &rb_drawing, NULL);
1332
1333 if(load_fd < 0)
1334 midend_new_game(me);
1335 else
1336 {
1337 char *ret = midend_deserialize(me, read_wrapper, (void*) load_fd);
1338 if(ret)
1339 return ret;
1340 }
1341
1342 fix_size();
1343
1344 float *floatcolors = midend_colors(me, &ncolors);
1345
1346 /* convert them to packed RGB */
1347 colors = smalloc(ncolors * sizeof(unsigned));
1348 unsigned *ptr = colors;
1349 float *floatptr = floatcolors;
1350 for(int i = 0; i < ncolors; ++i)
1351 {
1352 int r = 255 * *(floatptr++);
1353 int g = 255 * *(floatptr++);
1354 int b = 255 * *(floatptr++);
1355 LOGF("color %d is %d %d %d", i, r, g, b);
1356 *ptr++ = LCD_RGBPACK(r, g, b);
1357 }
1358 sfree(floatcolors);
1359
1360 rb->lcd_set_viewport(NULL);
1361 rb->lcd_set_backdrop(NULL);
1362 rb->lcd_set_foreground(LCD_BLACK);
1363 rb->lcd_set_background(BG_COLOR);
1364
1365 if(draw)
1366 {
1367 clear_and_draw();
1368 }
1369 return NULL;
1370}
1371
1372static void exit_handler(void)
1373{
1374#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1375 rb->cpu_boost(false);
1376#endif
1377}
1378
1379/* expects a totally free me* pointer */
1380static bool load_game(void)
1381{
1382 reset_tlsf();
1383
1384 int fd = rb->open(SAVE_FILE, O_RDONLY);
1385 if(fd < 0)
1386 return false;
1387
1388 rb->splash(0, "Loading...");
1389
1390 LOGF("opening %s", SAVE_FILE);
1391
1392 char *game;
1393 char *ret = identify_game(&game, read_wrapper, (void*)fd);
1394
1395 if(!*game && ret)
1396 {
1397 sfree(game);
1398 rb->splash(HZ, ret);
1399 rb->close(fd);
1400 return false;
1401 }
1402 else
1403 {
1404 /* seek to beginning */
1405 rb->lseek(fd, 0, SEEK_SET);
1406
1407#ifdef COMBINED
1408 /* search for the game and initialize the midend */
1409 for(int i = 0; i < gamecount; ++i)
1410 {
1411 if(!strcmp(game, gamelist[i]->name))
1412 {
1413 sfree(ret);
1414 ret = init_for_game(gamelist[i], fd, true);
1415 if(ret)
1416 {
1417 rb->splash(HZ, ret);
1418 sfree(ret);
1419 rb->close(fd);
1420 return false;
1421 }
1422 rb->close(fd);
1423 /* success, we delete the save */
1424 rb->remove(SAVE_FILE);
1425 return true;
1426 }
1427 }
1428 rb->splashf(HZ, "Incompatible game %s reported as compatible!?!? REEEPORT MEEEE!!!!", game);
1429 rb->close(fd);
1430 return false;
1431#else
1432 if(!strcmp(game, thegame.name))
1433 {
1434 sfree(ret);
1435 ret = init_for_game(&thegame, fd, false);
1436 if(ret)
1437 {
1438 rb->splash(HZ, ret);
1439 sfree(ret);
1440 rb->close(fd);
1441 return false;
1442 }
1443 rb->close(fd);
1444 /* success, we delete the save */
1445 rb->remove(SAVE_FILE);
1446 return true;
1447 }
1448 rb->splashf(HZ, "Cannot load save game for %s!", game);
1449 return false;
1450#endif
1451 }
1452}
1453
1454static void save_game(void)
1455{
1456 rb->splash(0, "Saving...");
1457 int fd = rb->open(SAVE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0666);
1458 midend_serialize(me, write_wrapper, (void*) fd);
1459 rb->close(fd);
1460 rb->lcd_update();
1461}
1462
1463static bool load_success;
1464
1465static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
1466{
1467 int i = (intptr_t) this_item;
1468 if(action == ACTION_REQUEST_MENUITEM)
1469 {
1470 switch(i)
1471 {
1472 case 0:
1473 case 7:
1474 if(!load_success)
1475 return ACTION_EXIT_MENUITEM;
1476 break;
1477 case 3:
1478#ifdef FOR_REAL
1479 return ACTION_EXIT_MENUITEM;
1480#else
1481 break;
1482#endif
1483 case 5:
1484 if(!midend_num_presets(me))
1485 return ACTION_EXIT_MENUITEM;
1486 break;
1487 case 6:
1488 if(!midend_which_game(me)->can_configure)
1489 return ACTION_EXIT_MENUITEM;
1490 break;
1491 default:
1492 break;
1493 }
1494 }
1495 return action;
1496}
1497
1498enum plugin_status plugin_start(const void *param)
1499{
1500 (void) param;
1501
1502#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1503 /* boost for init */
1504 rb->cpu_boost(true);
1505#endif
1506
1507#ifdef COMBINED
1508 giant_buffer_len = sizeof(giant_buffer);
1509#else
1510 giant_buffer = rb->plugin_get_buffer(&giant_buffer_len);
1511#endif
1512
1513#ifndef COMBINED
1514 rb->snprintf(save_file_path, sizeof(save_file_path), "%s/sgt-%s.sav", PLUGIN_GAMES_DATA_DIR, thegame.htmlhelp_topic);
1515#endif
1516
1517 rb_atexit(exit_handler);
1518
1519 if(fabs(sqrt(3)/2 - sin(PI/3)) > .01)
1520 rb->splash(HZ, "WARNING: floating-point functions are being weird... report me!");
1521
1522 init_default_settings();
1523
1524 load_success = load_game();
1525
1526#ifndef COMBINED
1527 if(!load_success)
1528 {
1529 /* our main menu expects a ready-to-use midend */
1530 init_for_game(&thegame, -1, false);
1531 }
1532#endif
1533
1534#ifdef HAVE_ADJUSTABLE_CPU_FREQ
1535 /* about to go to menu or button block */
1536 rb->cpu_boost(false);
1537#endif
1538
1539#ifndef COMBINED
1540#define static auto
1541#define const
1542 MENUITEM_STRINGLIST(menu, NULL, mainmenu_cb,
1543 "Resume Game",
1544 "New Game",
1545 "Quick Help",
1546 "Extensive Help",
1547 "Playback Control",
1548 "Game Type",
1549 "Configure Game",
1550 "Quit without Saving",
1551 "Quit");
1552#undef static
1553#undef const
1554
1555 /* HACK ALERT */
1556 char title[32] = { 0 };
1557 rb->snprintf(title, sizeof(title), "%s Menu", midend_which_game(me)->name);
1558 menu__.desc = title;
1559
1560 bool quit = false;
1561 int sel = 0;
1562 while(!quit)
1563 {
1564 switch(rb->do_menu(&menu, &sel, NULL, false))
1565 {
1566 case 0:
1567 clear_and_draw();
1568 goto game_loop;
1569 case 1:
1570 if(!load_success)
1571 {
1572 clear_and_draw();
1573 goto game_loop;
1574 }
1575 quit = true;
1576 break;
1577 case 2:
1578 quick_help();
1579 break;
1580 case 3:
1581 full_help();
1582 break;
1583 case 4:
1584 playback_control(NULL);
1585 break;
1586 case 5:
1587 presets_menu();
1588 break;
1589 case 6:
1590 config_menu();
1591 break;
1592 case 8:
1593 if(load_success)
1594 save_game();
1595 /* fall through */
1596 case 7:
1597 /* we don't care about freeing anything because tlsf will
1598 * be wiped out the next time around */
1599 return PLUGIN_OK;
1600 default:
1601 break;
1602 }
1603 }
1604#else
1605 if(load_success)
1606 goto game_loop;
1607#endif
1608
1609#ifdef COMBINED
1610 int gm = 0;
1611#endif
1612 while(1)
1613 {
1614#ifdef COMBINED
1615 if(rb->set_int("Choose Game", "", UNIT_INT, &gm, NULL, 1, 0, gamecount - 1, formatter))
1616 return PLUGIN_OK;
1617
1618 init_for_game(gamelist[gm], -1, true);
1619#else
1620 init_for_game(&thegame, -1, true);
1621#endif
1622
1623 last_keystate = 0;
1624 accept_input = true;
1625
1626 game_loop:
1627 while(1)
1628 {
1629 want_redraw = true;
1630
1631 draw_title();
1632
1633 int button = process_input(timer_on ? TIMER_INTERVAL : -1);
1634
1635 if(button < 0)
1636 {
1637 rb_unclip(NULL);
1638 deactivate_timer(NULL);
1639
1640 if(titlebar)
1641 {
1642 sfree(titlebar);
1643 titlebar = NULL;
1644 }
1645
1646 if(button == -1)
1647 {
1648 /* new game */
1649 midend_free(me);
1650 break;
1651 }
1652 else if(button == -2)
1653 {
1654 /* quit without saving */
1655 midend_free(me);
1656 sfree(colors);
1657 exit(PLUGIN_OK);
1658 }
1659 else if(button == -3)
1660 {
1661 /* save and quit */
1662 save_game();
1663 midend_free(me);
1664 sfree(colors);
1665 exit(PLUGIN_OK);
1666 }
1667 }
1668
1669 if(button)
1670 midend_process_key(me, 0, 0, button);
1671
1672 if(want_redraw)
1673 midend_redraw(me);
1674
1675 if(timer_on)
1676 timer_cb();
1677
1678 rb->yield();
1679 }
1680 sfree(colors);
1681 }
1682}
diff --git a/apps/plugins/puzzles/samegame.R b/apps/plugins/puzzles/samegame.R
new file mode 100644
index 0000000000..cc0d350041
--- /dev/null
+++ b/apps/plugins/puzzles/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/samegame.c b/apps/plugins/puzzles/samegame.c
new file mode 100644
index 0000000000..ed5efd224e
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/signpost.R b/apps/plugins/puzzles/signpost.R
new file mode 100644
index 0000000000..09ea367d57
--- /dev/null
+++ b/apps/plugins/puzzles/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/signpost.c b/apps/plugins/puzzles/signpost.c
new file mode 100644
index 0000000000..a2e431e746
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/singles.R b/apps/plugins/puzzles/singles.R
new file mode 100644
index 0000000000..2d10c4b388
--- /dev/null
+++ b/apps/plugins/puzzles/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/singles.c b/apps/plugins/puzzles/singles.c
new file mode 100644
index 0000000000..119be29122
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/sixteen.R b/apps/plugins/puzzles/sixteen.R
new file mode 100644
index 0000000000..c63a27cef8
--- /dev/null
+++ b/apps/plugins/puzzles/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/sixteen.c b/apps/plugins/puzzles/sixteen.c
new file mode 100644
index 0000000000..1dd1d6b017
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/slant.R b/apps/plugins/puzzles/slant.R
new file mode 100644
index 0000000000..ff0d21f1eb
--- /dev/null
+++ b/apps/plugins/puzzles/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/slant.c b/apps/plugins/puzzles/slant.c
new file mode 100644
index 0000000000..3ab4d306ef
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/solo.R b/apps/plugins/puzzles/solo.R
new file mode 100644
index 0000000000..081a76147e
--- /dev/null
+++ b/apps/plugins/puzzles/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/solo.c b/apps/plugins/puzzles/solo.c
new file mode 100644
index 0000000000..26f0eddd1a
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/tdq.c b/apps/plugins/puzzles/tdq.c
new file mode 100644
index 0000000000..43c9c35de5
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/tents.R b/apps/plugins/puzzles/tents.R
new file mode 100644
index 0000000000..557f929840
--- /dev/null
+++ b/apps/plugins/puzzles/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/tents.c b/apps/plugins/puzzles/tents.c
new file mode 100644
index 0000000000..1bc7a85188
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/towers.R b/apps/plugins/puzzles/towers.R
new file mode 100644
index 0000000000..c060c697a7
--- /dev/null
+++ b/apps/plugins/puzzles/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/towers.c b/apps/plugins/puzzles/towers.c
new file mode 100644
index 0000000000..d8e087a4bc
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/tracks.R b/apps/plugins/puzzles/tracks.R
new file mode 100644
index 0000000000..f88dfb03eb
--- /dev/null
+++ b/apps/plugins/puzzles/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/tracks.c b/apps/plugins/puzzles/tracks.c
new file mode 100644
index 0000000000..dd00e32b9a
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/tree234.c b/apps/plugins/puzzles/tree234.c
new file mode 100644
index 0000000000..71c3be242a
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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/tree234.h b/apps/plugins/puzzles/tree234.h
new file mode 100644
index 0000000000..f75c8f7fb3
--- /dev/null
+++ b/apps/plugins/puzzles/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/twiddle.R b/apps/plugins/puzzles/twiddle.R
new file mode 100644
index 0000000000..1495c33181
--- /dev/null
+++ b/apps/plugins/puzzles/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/twiddle.c b/apps/plugins/puzzles/twiddle.c
new file mode 100644
index 0000000000..2a2ab668ca
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/undead.R b/apps/plugins/puzzles/undead.R
new file mode 100644
index 0000000000..5907ed6b74
--- /dev/null
+++ b/apps/plugins/puzzles/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/undead.c b/apps/plugins/puzzles/undead.c
new file mode 100644
index 0000000000..28fd0f35fe
--- /dev/null
+++ b/apps/plugins/puzzles/undead.c
@@ -0,0 +1,2738 @@
1/*
2 * undead: Implementation of Haunted Mirror Mazes
3 *
4 * http://www.janko.at/Raetsel/Spukschloss/index.htm
5 *
6 * Puzzle definition is the total number of each monster type, the
7 * grid definition, and the list of sightings (clockwise, starting
8 * from top left corner)
9 *
10 * Example: (Janko puzzle No. 1,
11 * http://www.janko.at/Raetsel/Spukschloss/001.a.htm )
12 *
13 * Ghosts: 0 Vampires: 2 Zombies: 6
14 *
15 * 2 1 1 1
16 * 1 \ \ . / 2
17 * 0 \ . / . 2
18 * 0 / . / . 2
19 * 3 . . . \ 2
20 * 3 3 2 2
21 *
22 * would be encoded into:
23 * 4x4:0,2,6,LLaRLaRaRaRdL,2,1,1,1,2,2,2,2,2,2,3,3,3,0,0,1
24 *
25 * Additionally, the game description can contain monsters fixed at a
26 * certain grid position. The internal generator does not (yet) use
27 * this feature, but this is needed to enter puzzles like Janko No.
28 * 14, which is encoded as:
29 * 8x5:12,12,0,LaRbLaRaLaRLbRaVaVaGRaRaRaLbLaRbRLb,0,2,0,2,2,1,2,1,3,1,0,1,8,4,3,0,0,2,3,2,7,2,1,6,2,1
30 *
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include "rbassert.h"
37#include <ctype.h>
38#include <math.h>
39
40#include "puzzles.h"
41
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,
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/unequal.R b/apps/plugins/puzzles/unequal.R
new file mode 100644
index 0000000000..a061582768
--- /dev/null
+++ b/apps/plugins/puzzles/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/unequal.c b/apps/plugins/puzzles/unequal.c
new file mode 100644
index 0000000000..457965b4ff
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/unfinished/README b/apps/plugins/puzzles/unfinished/README
new file mode 100644
index 0000000000..0f8bb41d7c
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/group.R b/apps/plugins/puzzles/unfinished/group.R
new file mode 100644
index 0000000000..a11d22e9b9
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/group.c b/apps/plugins/puzzles/unfinished/group.c
new file mode 100644
index 0000000000..bec826e367
--- /dev/null
+++ b/apps/plugins/puzzles/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,
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/unfinished/group.gap b/apps/plugins/puzzles/unfinished/group.gap
new file mode 100644
index 0000000000..280adf4664
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/numgame.c b/apps/plugins/puzzles/unfinished/numgame.c
new file mode 100644
index 0000000000..aed5c17347
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/path.c b/apps/plugins/puzzles/unfinished/path.c
new file mode 100644
index 0000000000..61d6c61c6a
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/separate.R b/apps/plugins/puzzles/unfinished/separate.R
new file mode 100644
index 0000000000..f861c8f4fe
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/separate.c b/apps/plugins/puzzles/unfinished/separate.c
new file mode 100644
index 0000000000..898304a0e6
--- /dev/null
+++ b/apps/plugins/puzzles/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,
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/unfinished/slide.R b/apps/plugins/puzzles/unfinished/slide.R
new file mode 100644
index 0000000000..189ed652d1
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/slide.c b/apps/plugins/puzzles/unfinished/slide.c
new file mode 100644
index 0000000000..b1aa04bc90
--- /dev/null
+++ b/apps/plugins/puzzles/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,
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/unfinished/sokoban.R b/apps/plugins/puzzles/unfinished/sokoban.R
new file mode 100644
index 0000000000..3b6dab56ba
--- /dev/null
+++ b/apps/plugins/puzzles/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/unfinished/sokoban.c b/apps/plugins/puzzles/unfinished/sokoban.c
new file mode 100644
index 0000000000..b5533c9034
--- /dev/null
+++ b/apps/plugins/puzzles/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,
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/unruly.R b/apps/plugins/puzzles/unruly.R
new file mode 100644
index 0000000000..064ccc35ba
--- /dev/null
+++ b/apps/plugins/puzzles/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/unruly.c b/apps/plugins/puzzles/unruly.c
new file mode 100644
index 0000000000..aea4cdf789
--- /dev/null
+++ b/apps/plugins/puzzles/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 "rbassert.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,
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/untangle.R b/apps/plugins/puzzles/untangle.R
new file mode 100644
index 0000000000..a57f1e56fd
--- /dev/null
+++ b/apps/plugins/puzzles/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/untangle.c b/apps/plugins/puzzles/untangle.c
new file mode 100644
index 0000000000..67da03d8c0
--- /dev/null
+++ b/apps/plugins/puzzles/untangle.c
@@ -0,0 +1,1491 @@
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 "rbassert.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
43#define FLASH_TIME 0.30F
44#define ANIM_TIME 0.13F
45#define SOLVEANIM_TIME 0.50F
46
47enum {
48 COL_SYSBACKGROUND,
49 COL_BACKGROUND,
50 COL_LINE,
51#ifdef SHOW_CROSSINGS
52 COL_CROSSEDLINE,
53#endif
54 COL_OUTLINE,
55 COL_POINT,
56 COL_DRAGPOINT,
57 COL_NEIGHBOUR,
58 COL_FLASH1,
59 COL_FLASH2,
60 NCOLOURS
61};
62
63typedef struct point {
64 /*
65 * Points are stored using rational coordinates, with the same
66 * denominator for both coordinates.
67 */
68 long x, y, d;
69} point;
70
71typedef struct edge {
72 /*
73 * This structure is implicitly associated with a particular
74 * point set, so all it has to do is to store two point
75 * indices. It is required to store them in the order (lower,
76 * higher), i.e. a < b always.
77 */
78 int a, b;
79} edge;
80
81struct game_params {
82 int n; /* number of points */
83};
84
85struct graph {
86 int refcount; /* for deallocation */
87 tree234 *edges; /* stores `edge' structures */
88};
89
90struct game_state {
91 game_params params;
92 int w, h; /* extent of coordinate system only */
93 point *pts;
94#ifdef SHOW_CROSSINGS
95 int *crosses; /* mark edges which are crossed */
96#endif
97 struct graph *graph;
98 int completed, cheated, just_solved;
99};
100
101static int edgecmpC(const void *av, const void *bv)
102{
103 const edge *a = (const edge *)av;
104 const edge *b = (const edge *)bv;
105
106 if (a->a < b->a)
107 return -1;
108 else if (a->a > b->a)
109 return +1;
110 else if (a->b < b->b)
111 return -1;
112 else if (a->b > b->b)
113 return +1;
114 return 0;
115}
116
117static int edgecmp(void *av, void *bv) { return edgecmpC(av, bv); }
118
119static game_params *default_params(void)
120{
121 game_params *ret = snew(game_params);
122
123 ret->n = 10;
124
125 return ret;
126}
127
128static int game_fetch_preset(int i, char **name, game_params **params)
129{
130 game_params *ret;
131 int n;
132 char buf[80];
133
134 switch (i) {
135 case 0: n = 6; break;
136 case 1: n = 10; break;
137 case 2: n = 15; break;
138 case 3: n = 20; break;
139 case 4: n = 25; break;
140 default: return FALSE;
141 }
142
143 sprintf(buf, "%d points", n);
144 *name = dupstr(buf);
145
146 *params = ret = snew(game_params);
147 ret->n = n;
148
149 return TRUE;
150}
151
152static void free_params(game_params *params)
153{
154 sfree(params);
155}
156
157static game_params *dup_params(const game_params *params)
158{
159 game_params *ret = snew(game_params);
160 *ret = *params; /* structure copy */
161 return ret;
162}
163
164static void decode_params(game_params *params, char const *string)
165{
166 params->n = atoi(string);
167}
168
169static char *encode_params(const game_params *params, int full)
170{
171 char buf[80];
172
173 sprintf(buf, "%d", params->n);
174
175 return dupstr(buf);
176}
177
178static config_item *game_configure(const game_params *params)
179{
180 config_item *ret;
181 char buf[80];
182
183 ret = snewn(3, config_item);
184
185 ret[0].name = "Number of points";
186 ret[0].type = C_STRING;
187 sprintf(buf, "%d", params->n);
188 ret[0].sval = dupstr(buf);
189 ret[0].ival = 0;
190
191 ret[1].name = NULL;
192 ret[1].type = C_END;
193 ret[1].sval = NULL;
194 ret[1].ival = 0;
195
196 return ret;
197}
198
199static game_params *custom_params(const config_item *cfg)
200{
201 game_params *ret = snew(game_params);
202
203 ret->n = atoi(cfg[0].sval);
204
205 return ret;
206}
207
208static char *validate_params(const game_params *params, int full)
209{
210 if (params->n < 4)
211 return "Number of points must be at least four";
212 return NULL;
213}
214
215/* ----------------------------------------------------------------------
216 * Small number of 64-bit integer arithmetic operations, to prevent
217 * integer overflow at the very core of cross().
218 */
219
220typedef struct {
221 long hi;
222 unsigned long lo;
223} int64;
224
225#define greater64(i,j) ( (i).hi>(j).hi || ((i).hi==(j).hi && (i).lo>(j).lo))
226#define sign64(i) ((i).hi < 0 ? -1 : (i).hi==0 && (i).lo==0 ? 0 : +1)
227
228static int64 mulu32to64(unsigned long x, unsigned long y)
229{
230 unsigned long a, b, c, d, t;
231 int64 ret;
232
233 a = (x & 0xFFFF) * (y & 0xFFFF);
234 b = (x & 0xFFFF) * (y >> 16);
235 c = (x >> 16) * (y & 0xFFFF);
236 d = (x >> 16) * (y >> 16);
237
238 ret.lo = a;
239 ret.hi = d + (b >> 16) + (c >> 16);
240 t = (b & 0xFFFF) << 16;
241 ret.lo += t;
242 if (ret.lo < t)
243 ret.hi++;
244 t = (c & 0xFFFF) << 16;
245 ret.lo += t;
246 if (ret.lo < t)
247 ret.hi++;
248
249#ifdef DIAGNOSTIC_VIA_LONGLONG
250 assert(((unsigned long long)ret.hi << 32) + ret.lo ==
251 (unsigned long long)x * y);
252#endif
253
254 return ret;
255}
256
257static int64 mul32to64(long x, long y)
258{
259 int sign = +1;
260 int64 ret;
261#ifdef DIAGNOSTIC_VIA_LONGLONG
262 long long realret = (long long)x * y;
263#endif
264
265 if (x < 0)
266 x = -x, sign = -sign;
267 if (y < 0)
268 y = -y, sign = -sign;
269
270 ret = mulu32to64(x, y);
271
272 if (sign < 0) {
273 ret.hi = -ret.hi;
274 ret.lo = -ret.lo;
275 if (ret.lo)
276 ret.hi--;
277 }
278
279#ifdef DIAGNOSTIC_VIA_LONGLONG
280 assert(((unsigned long long)ret.hi << 32) + ret.lo == realret);
281#endif
282
283 return ret;
284}
285
286static int64 dotprod64(long a, long b, long p, long q)
287{
288 int64 ab, pq;
289
290 ab = mul32to64(a, b);
291 pq = mul32to64(p, q);
292 ab.hi += pq.hi;
293 ab.lo += pq.lo;
294 if (ab.lo < pq.lo)
295 ab.hi++;
296 return ab;
297}
298
299/*
300 * Determine whether the line segments between a1 and a2, and
301 * between b1 and b2, intersect. We count it as an intersection if
302 * any of the endpoints lies _on_ the other line.
303 */
304static int cross(point a1, point a2, point b1, point b2)
305{
306 long b1x, b1y, b2x, b2y, px, py;
307 int64 d1, d2, d3;
308
309 /*
310 * The condition for crossing is that b1 and b2 are on opposite
311 * sides of the line a1-a2, and vice versa. We determine this
312 * by taking the dot product of b1-a1 with a vector
313 * perpendicular to a2-a1, and similarly with b2-a1, and seeing
314 * if they have different signs.
315 */
316
317 /*
318 * Construct the vector b1-a1. We don't have to worry too much
319 * about the denominator, because we're only going to check the
320 * sign of this vector; we just need to get the numerator
321 * right.
322 */
323 b1x = b1.x * a1.d - a1.x * b1.d;
324 b1y = b1.y * a1.d - a1.y * b1.d;
325 /* Now construct b2-a1, and a vector perpendicular to a2-a1,
326 * in the same way. */
327 b2x = b2.x * a1.d - a1.x * b2.d;
328 b2y = b2.y * a1.d - a1.y * b2.d;
329 px = a1.y * a2.d - a2.y * a1.d;
330 py = a2.x * a1.d - a1.x * a2.d;
331 /* Take the dot products. Here we resort to 64-bit arithmetic. */
332 d1 = dotprod64(b1x, px, b1y, py);
333 d2 = dotprod64(b2x, px, b2y, py);
334 /* If they have the same non-zero sign, the lines do not cross. */
335 if ((sign64(d1) > 0 && sign64(d2) > 0) ||
336 (sign64(d1) < 0 && sign64(d2) < 0))
337 return FALSE;
338
339 /*
340 * If the dot products are both exactly zero, then the two line
341 * segments are collinear. At this point the intersection
342 * condition becomes whether or not they overlap within their
343 * line.
344 */
345 if (sign64(d1) == 0 && sign64(d2) == 0) {
346 /* Construct the vector a2-a1. */
347 px = a2.x * a1.d - a1.x * a2.d;
348 py = a2.y * a1.d - a1.y * a2.d;
349 /* Determine the dot products of b1-a1 and b2-a1 with this. */
350 d1 = dotprod64(b1x, px, b1y, py);
351 d2 = dotprod64(b2x, px, b2y, py);
352 /* If they're both strictly negative, the lines do not cross. */
353 if (sign64(d1) < 0 && sign64(d2) < 0)
354 return FALSE;
355 /* Otherwise, take the dot product of a2-a1 with itself. If
356 * the other two dot products both exceed this, the lines do
357 * not cross. */
358 d3 = dotprod64(px, px, py, py);
359 if (greater64(d1, d3) && greater64(d2, d3))
360 return FALSE;
361 }
362
363 /*
364 * We've eliminated the only important special case, and we
365 * have determined that b1 and b2 are on opposite sides of the
366 * line a1-a2. Now do the same thing the other way round and
367 * we're done.
368 */
369 b1x = a1.x * b1.d - b1.x * a1.d;
370 b1y = a1.y * b1.d - b1.y * a1.d;
371 b2x = a2.x * b1.d - b1.x * a2.d;
372 b2y = a2.y * b1.d - b1.y * a2.d;
373 px = b1.y * b2.d - b2.y * b1.d;
374 py = b2.x * b1.d - b1.x * b2.d;
375 d1 = dotprod64(b1x, px, b1y, py);
376 d2 = dotprod64(b2x, px, b2y, py);
377 if ((sign64(d1) > 0 && sign64(d2) > 0) ||
378 (sign64(d1) < 0 && sign64(d2) < 0))
379 return FALSE;
380
381 /*
382 * The lines must cross.
383 */
384 return TRUE;
385}
386
387static unsigned long squarert(unsigned long n) {
388 unsigned long d, a, b, di;
389
390 d = n;
391 a = 0;
392 b = 1L << 30; /* largest available power of 4 */
393 do {
394 a >>= 1;
395 di = 2*a + b;
396 if (di <= d) {
397 d -= di;
398 a += b;
399 }
400 b >>= 2;
401 } while (b);
402
403 return a;
404}
405
406/*
407 * Our solutions are arranged on a square grid big enough that n
408 * points occupy about 1/POINTDENSITY of the grid.
409 */
410#define POINTDENSITY 3
411#define MAXDEGREE 4
412#define COORDLIMIT(n) squarert((n) * POINTDENSITY)
413
414static void addedge(tree234 *edges, int a, int b)
415{
416 edge *e = snew(edge);
417
418 assert(a != b);
419
420 e->a = min(a, b);
421 e->b = max(a, b);
422
423 add234(edges, e);
424}
425
426static int isedge(tree234 *edges, int a, int b)
427{
428 edge e;
429
430 assert(a != b);
431
432 e.a = min(a, b);
433 e.b = max(a, b);
434
435 return find234(edges, &e, NULL) != NULL;
436}
437
438typedef struct vertex {
439 int param;
440 int vindex;
441} vertex;
442
443static int vertcmpC(const void *av, const void *bv)
444{
445 const vertex *a = (vertex *)av;
446 const vertex *b = (vertex *)bv;
447
448 if (a->param < b->param)
449 return -1;
450 else if (a->param > b->param)
451 return +1;
452 else if (a->vindex < b->vindex)
453 return -1;
454 else if (a->vindex > b->vindex)
455 return +1;
456 return 0;
457}
458static int vertcmp(void *av, void *bv) { return vertcmpC(av, bv); }
459
460/*
461 * Construct point coordinates for n points arranged in a circle,
462 * within the bounding box (0,0) to (w,w).
463 */
464static void make_circle(point *pts, int n, int w)
465{
466 long d, r, c, i;
467
468 /*
469 * First, decide on a denominator. Although in principle it
470 * would be nice to set this really high so as to finely
471 * distinguish all the points on the circle, I'm going to set
472 * it at a fixed size to prevent integer overflow problems.
473 */
474 d = PREFERRED_TILESIZE;
475
476 /*
477 * Leave a little space outside the circle.
478 */
479 c = d * w / 2;
480 r = d * w * 3 / 7;
481
482 /*
483 * Place the points.
484 */
485 for (i = 0; i < n; i++) {
486 double angle = i * 2 * PI / n;
487 double x = r * sin(angle), y = - r * cos(angle);
488 pts[i].x = (long)(c + x + 0.5);
489 pts[i].y = (long)(c + y + 0.5);
490 pts[i].d = d;
491 }
492}
493
494static char *new_game_desc(const game_params *params, random_state *rs,
495 char **aux, int interactive)
496{
497 int n = params->n, i;
498 long w, h, j, k, m;
499 point *pts, *pts2;
500 long *tmp;
501 tree234 *edges, *vertices;
502 edge *e, *e2;
503 vertex *v, *vs, *vlist;
504 char *ret;
505
506 w = h = COORDLIMIT(n);
507
508 /*
509 * Choose n points from this grid.
510 */
511 pts = snewn(n, point);
512 tmp = snewn(w*h, long);
513 for (i = 0; i < w*h; i++)
514 tmp[i] = i;
515 shuffle(tmp, w*h, sizeof(*tmp), rs);
516 for (i = 0; i < n; i++) {
517 pts[i].x = tmp[i] % w;
518 pts[i].y = tmp[i] / w;
519 pts[i].d = 1;
520 }
521 sfree(tmp);
522
523 /*
524 * Now start adding edges between the points.
525 *
526 * At all times, we attempt to add an edge to the lowest-degree
527 * vertex we currently have, and we try the other vertices as
528 * candidate second endpoints in order of distance from this
529 * one. We stop as soon as we find an edge which
530 *
531 * (a) does not increase any vertex's degree beyond MAXDEGREE
532 * (b) does not cross any existing edges
533 * (c) does not intersect any actual point.
534 */
535 vs = snewn(n, vertex);
536 vertices = newtree234(vertcmp);
537 for (i = 0; i < n; i++) {
538 v = vs + i;
539 v->param = 0; /* in this tree, param is the degree */
540 v->vindex = i;
541 add234(vertices, v);
542 }
543 edges = newtree234(edgecmp);
544 vlist = snewn(n, vertex);
545 while (1) {
546 int added = FALSE;
547
548 for (i = 0; i < n; i++) {
549 v = index234(vertices, i);
550 j = v->vindex;
551
552 if (v->param >= MAXDEGREE)
553 break; /* nothing left to add! */
554
555 /*
556 * Sort the other vertices into order of their distance
557 * from this one. Don't bother looking below i, because
558 * we've already tried those edges the other way round.
559 * Also here we rule out target vertices with too high
560 * a degree, and (of course) ones to which we already
561 * have an edge.
562 */
563 m = 0;
564 for (k = i+1; k < n; k++) {
565 vertex *kv = index234(vertices, k);
566 int ki = kv->vindex;
567 int dx, dy;
568
569 if (kv->param >= MAXDEGREE || isedge(edges, ki, j))
570 continue;
571
572 vlist[m].vindex = ki;
573 dx = pts[ki].x - pts[j].x;
574 dy = pts[ki].y - pts[j].y;
575 vlist[m].param = dx*dx + dy*dy;
576 m++;
577 }
578
579 qsort(vlist, m, sizeof(*vlist), vertcmpC);
580
581 for (k = 0; k < m; k++) {
582 int p;
583 int ki = vlist[k].vindex;
584
585 /*
586 * Check to see whether this edge intersects any
587 * existing edge or point.
588 */
589 for (p = 0; p < n; p++)
590 if (p != ki && p != j && cross(pts[ki], pts[j],
591 pts[p], pts[p]))
592 break;
593 if (p < n)
594 continue;
595 for (p = 0; (e = index234(edges, p)) != NULL; p++)
596 if (e->a != ki && e->a != j &&
597 e->b != ki && e->b != j &&
598 cross(pts[ki], pts[j], pts[e->a], pts[e->b]))
599 break;
600 if (e)
601 continue;
602
603 /*
604 * We're done! Add this edge, modify the degrees of
605 * the two vertices involved, and break.
606 */
607 addedge(edges, j, ki);
608 added = TRUE;
609 del234(vertices, vs+j);
610 vs[j].param++;
611 add234(vertices, vs+j);
612 del234(vertices, vs+ki);
613 vs[ki].param++;
614 add234(vertices, vs+ki);
615 break;
616 }
617
618 if (k < m)
619 break;
620 }
621
622 if (!added)
623 break; /* we're done. */
624 }
625
626 /*
627 * That's our graph. Now shuffle the points, making sure that
628 * they come out with at least one crossed line when arranged
629 * in a circle (so that the puzzle isn't immediately solved!).
630 */
631 tmp = snewn(n, long);
632 for (i = 0; i < n; i++)
633 tmp[i] = i;
634 pts2 = snewn(n, point);
635 make_circle(pts2, n, w);
636 while (1) {
637 shuffle(tmp, n, sizeof(*tmp), rs);
638 for (i = 0; (e = index234(edges, i)) != NULL; i++) {
639 for (j = i+1; (e2 = index234(edges, j)) != NULL; j++) {
640 if (e2->a == e->a || e2->a == e->b ||
641 e2->b == e->a || e2->b == e->b)
642 continue;
643 if (cross(pts2[tmp[e2->a]], pts2[tmp[e2->b]],
644 pts2[tmp[e->a]], pts2[tmp[e->b]]))
645 break;
646 }
647 if (e2)
648 break;
649 }
650 if (e)
651 break; /* we've found a crossing */
652 }
653
654 /*
655 * We're done. Now encode the graph in a string format. Let's
656 * use a comma-separated list of dash-separated vertex number
657 * pairs, numbered from zero. We'll sort the list to prevent
658 * side channels.
659 */
660 ret = NULL;
661 {
662 char *sep;
663 char buf[80];
664 int retlen;
665 edge *ea;
666
667 retlen = 0;
668 m = count234(edges);
669 ea = snewn(m, edge);
670 for (i = 0; (e = index234(edges, i)) != NULL; i++) {
671 assert(i < m);
672 ea[i].a = min(tmp[e->a], tmp[e->b]);
673 ea[i].b = max(tmp[e->a], tmp[e->b]);
674 retlen += 1 + sprintf(buf, "%d-%d", ea[i].a, ea[i].b);
675 }
676 assert(i == m);
677 qsort(ea, m, sizeof(*ea), edgecmpC);
678
679 ret = snewn(retlen, char);
680 sep = "";
681 k = 0;
682
683 for (i = 0; i < m; i++) {
684 k += sprintf(ret + k, "%s%d-%d", sep, ea[i].a, ea[i].b);
685 sep = ",";
686 }
687 assert(k < retlen);
688
689 sfree(ea);
690 }
691
692 /*
693 * Encode the solution we started with as an aux_info string.
694 */
695 {
696 char buf[80];
697 char *auxstr;
698 int auxlen;
699
700 auxlen = 2; /* leading 'S' and trailing '\0' */
701 for (i = 0; i < n; i++) {
702 j = tmp[i];
703 pts2[j] = pts[i];
704 if (pts2[j].d & 1) {
705 pts2[j].x *= 2;
706 pts2[j].y *= 2;
707 pts2[j].d *= 2;
708 }
709 pts2[j].x += pts2[j].d / 2;
710 pts2[j].y += pts2[j].d / 2;
711 auxlen += sprintf(buf, ";P%d:%ld,%ld/%ld", i,
712 pts2[j].x, pts2[j].y, pts2[j].d);
713 }
714 k = 0;
715 auxstr = snewn(auxlen, char);
716 auxstr[k++] = 'S';
717 for (i = 0; i < n; i++)
718 k += sprintf(auxstr+k, ";P%d:%ld,%ld/%ld", i,
719 pts2[i].x, pts2[i].y, pts2[i].d);
720 assert(k < auxlen);
721 *aux = auxstr;
722 }
723 sfree(pts2);
724
725 sfree(tmp);
726 sfree(vlist);
727 freetree234(vertices);
728 sfree(vs);
729 while ((e = delpos234(edges, 0)) != NULL)
730 sfree(e);
731 freetree234(edges);
732 sfree(pts);
733
734 return ret;
735}
736
737static char *validate_desc(const game_params *params, const char *desc)
738{
739 int a, b;
740
741 while (*desc) {
742 a = atoi(desc);
743 if (a < 0 || a >= params->n)
744 return "Number out of range in game description";
745 while (*desc && isdigit((unsigned char)*desc)) desc++;
746 if (*desc != '-')
747 return "Expected '-' after number in game description";
748 desc++; /* eat dash */
749 b = atoi(desc);
750 if (b < 0 || b >= params->n)
751 return "Number out of range in game description";
752 while (*desc && isdigit((unsigned char)*desc)) desc++;
753 if (*desc) {
754 if (*desc != ',')
755 return "Expected ',' after number in game description";
756 desc++; /* eat comma */
757 }
758 }
759
760 return NULL;
761}
762
763static void mark_crossings(game_state *state)
764{
765 int ok = TRUE;
766 int i, j;
767 edge *e, *e2;
768
769#ifdef SHOW_CROSSINGS
770 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++)
771 state->crosses[i] = FALSE;
772#endif
773
774 /*
775 * Check correctness: for every pair of edges, see whether they
776 * cross.
777 */
778 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) {
779 for (j = i+1; (e2 = index234(state->graph->edges, j)) != NULL; j++) {
780 if (e2->a == e->a || e2->a == e->b ||
781 e2->b == e->a || e2->b == e->b)
782 continue;
783 if (cross(state->pts[e2->a], state->pts[e2->b],
784 state->pts[e->a], state->pts[e->b])) {
785 ok = FALSE;
786#ifdef SHOW_CROSSINGS
787 state->crosses[i] = state->crosses[j] = TRUE;
788#else
789 goto done; /* multi-level break - sorry */
790#endif
791 }
792 }
793 }
794
795 /*
796 * e == NULL if we've gone through all the edge pairs
797 * without finding a crossing.
798 */
799#ifndef SHOW_CROSSINGS
800 done:
801#endif
802 if (ok)
803 state->completed = TRUE;
804}
805
806static game_state *new_game(midend *me, const game_params *params,
807 const char *desc)
808{
809 int n = params->n;
810 game_state *state = snew(game_state);
811 int a, b;
812
813 state->params = *params;
814 state->w = state->h = COORDLIMIT(n);
815 state->pts = snewn(n, point);
816 make_circle(state->pts, n, state->w);
817 state->graph = snew(struct graph);
818 state->graph->refcount = 1;
819 state->graph->edges = newtree234(edgecmp);
820 state->completed = state->cheated = state->just_solved = FALSE;
821
822 while (*desc) {
823 a = atoi(desc);
824 assert(a >= 0 && a < params->n);
825 while (*desc && isdigit((unsigned char)*desc)) desc++;
826 assert(*desc == '-');
827 desc++; /* eat dash */
828 b = atoi(desc);
829 assert(b >= 0 && b < params->n);
830 while (*desc && isdigit((unsigned char)*desc)) desc++;
831 if (*desc) {
832 assert(*desc == ',');
833 desc++; /* eat comma */
834 }
835 addedge(state->graph->edges, a, b);
836 }
837
838#ifdef SHOW_CROSSINGS
839 state->crosses = snewn(count234(state->graph->edges), int);
840 mark_crossings(state); /* sets up `crosses' and `completed' */
841#endif
842
843 return state;
844}
845
846static game_state *dup_game(const game_state *state)
847{
848 int n = state->params.n;
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->pts = snewn(n, point);
855 memcpy(ret->pts, state->pts, n * sizeof(point));
856 ret->graph = state->graph;
857 ret->graph->refcount++;
858 ret->completed = state->completed;
859 ret->cheated = state->cheated;
860 ret->just_solved = state->just_solved;
861#ifdef SHOW_CROSSINGS
862 ret->crosses = snewn(count234(ret->graph->edges), int);
863 memcpy(ret->crosses, state->crosses,
864 count234(ret->graph->edges) * sizeof(int));
865#endif
866
867 return ret;
868}
869
870static void free_game(game_state *state)
871{
872 if (--state->graph->refcount <= 0) {
873 edge *e;
874 while ((e = delpos234(state->graph->edges, 0)) != NULL)
875 sfree(e);
876 freetree234(state->graph->edges);
877 sfree(state->graph);
878 }
879 sfree(state->pts);
880 sfree(state);
881}
882
883static char *solve_game(const game_state *state, const game_state *currstate,
884 const char *aux, char **error)
885{
886 int n = state->params.n;
887 int matrix[4];
888 point *pts;
889 int i, j, besti;
890 float bestd;
891 char buf[80], *ret;
892 int retlen, retsize;
893
894 if (!aux) {
895 *error = "Solution not known for this puzzle";
896 return NULL;
897 }
898
899 /*
900 * Decode the aux_info to get the original point positions.
901 */
902 pts = snewn(n, point);
903 aux++; /* eat 'S' */
904 for (i = 0; i < n; i++) {
905 int p, k;
906 long x, y, d;
907 int ret = sscanf(aux, ";P%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k);
908 if (ret != 4 || p != i) {
909 *error = "Internal error: aux_info badly formatted";
910 sfree(pts);
911 return NULL;
912 }
913 pts[i].x = x;
914 pts[i].y = y;
915 pts[i].d = d;
916 aux += k;
917 }
918
919 /*
920 * Now go through eight possible symmetries of the point set.
921 * For each one, work out the sum of the Euclidean distances
922 * between the points' current positions and their new ones.
923 *
924 * We're squaring distances here, which means we're at risk of
925 * integer overflow. Fortunately, there's no real need to be
926 * massively careful about rounding errors, since this is a
927 * non-essential bit of the code; so I'll just work in floats
928 * internally.
929 */
930 besti = -1;
931 bestd = 0.0F;
932
933 for (i = 0; i < 8; i++) {
934 float d;
935
936 matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0;
937 matrix[i & 1] = (i & 2) ? +1 : -1;
938 matrix[3-(i&1)] = (i & 4) ? +1 : -1;
939
940 d = 0.0F;
941 for (j = 0; j < n; j++) {
942 float px = (float)pts[j].x / pts[j].d;
943 float py = (float)pts[j].y / pts[j].d;
944 float sx = (float)currstate->pts[j].x / currstate->pts[j].d;
945 float sy = (float)currstate->pts[j].y / currstate->pts[j].d;
946 float cx = (float)currstate->w / 2;
947 float cy = (float)currstate->h / 2;
948 float ox, oy, dx, dy;
949
950 px -= cx;
951 py -= cy;
952
953 ox = matrix[0] * px + matrix[1] * py;
954 oy = matrix[2] * px + matrix[3] * py;
955
956 ox += cx;
957 oy += cy;
958
959 dx = ox - sx;
960 dy = oy - sy;
961
962 d += dx*dx + dy*dy;
963 }
964
965 if (besti < 0 || bestd > d) {
966 besti = i;
967 bestd = d;
968 }
969 }
970
971 assert(besti >= 0);
972
973 /*
974 * Now we know which symmetry is closest to the points' current
975 * positions. Use it.
976 */
977 matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0;
978 matrix[besti & 1] = (besti & 2) ? +1 : -1;
979 matrix[3-(besti&1)] = (besti & 4) ? +1 : -1;
980
981 retsize = 256;
982 ret = snewn(retsize, char);
983 retlen = 0;
984 ret[retlen++] = 'S';
985 ret[retlen] = '\0';
986
987 for (i = 0; i < n; i++) {
988 float px = (float)pts[i].x / pts[i].d;
989 float py = (float)pts[i].y / pts[i].d;
990 float cx = (float)currstate->w / 2;
991 float cy = (float)currstate->h / 2;
992 float ox, oy;
993 int extra;
994
995 px -= cx;
996 py -= cy;
997
998 ox = matrix[0] * px + matrix[1] * py;
999 oy = matrix[2] * px + matrix[3] * py;
1000
1001 ox += cx;
1002 oy += cy;
1003
1004 /*
1005 * Use a fixed denominator of 2, because we know the
1006 * original points were on an integer grid offset by 1/2.
1007 */
1008 pts[i].d = 2;
1009 ox *= pts[i].d;
1010 oy *= pts[i].d;
1011 pts[i].x = (long)(ox + 0.5F);
1012 pts[i].y = (long)(oy + 0.5F);
1013
1014 extra = sprintf(buf, ";P%d:%ld,%ld/%ld", i,
1015 pts[i].x, pts[i].y, pts[i].d);
1016 if (retlen + extra >= retsize) {
1017 retsize = retlen + extra + 256;
1018 ret = sresize(ret, retsize, char);
1019 }
1020 strcpy(ret + retlen, buf);
1021 retlen += extra;
1022 }
1023
1024 sfree(pts);
1025
1026 return ret;
1027}
1028
1029static int game_can_format_as_text_now(const game_params *params)
1030{
1031 return TRUE;
1032}
1033
1034static char *game_text_format(const game_state *state)
1035{
1036 return NULL;
1037}
1038
1039struct game_ui {
1040 int dragpoint; /* point being dragged; -1 if none */
1041 point newpoint; /* where it's been dragged to so far */
1042 int just_dragged; /* reset in game_changed_state */
1043 int just_moved; /* _set_ in game_changed_state */
1044 float anim_length;
1045};
1046
1047static game_ui *new_ui(const game_state *state)
1048{
1049 game_ui *ui = snew(game_ui);
1050 ui->dragpoint = -1;
1051 ui->just_moved = ui->just_dragged = FALSE;
1052 return ui;
1053}
1054
1055static void free_ui(game_ui *ui)
1056{
1057 sfree(ui);
1058}
1059
1060static char *encode_ui(const game_ui *ui)
1061{
1062 return NULL;
1063}
1064
1065static void decode_ui(game_ui *ui, const char *encoding)
1066{
1067}
1068
1069static void game_changed_state(game_ui *ui, const game_state *oldstate,
1070 const game_state *newstate)
1071{
1072 ui->dragpoint = -1;
1073 ui->just_moved = ui->just_dragged;
1074 ui->just_dragged = FALSE;
1075}
1076
1077struct game_drawstate {
1078 long tilesize;
1079 int bg, dragpoint;
1080 long *x, *y;
1081};
1082
1083static char *interpret_move(const game_state *state, game_ui *ui,
1084 const game_drawstate *ds,
1085 int x, int y, int button)
1086{
1087 int n = state->params.n;
1088
1089 if (IS_MOUSE_DOWN(button)) {
1090 int i, best;
1091 long bestd;
1092
1093 /*
1094 * Begin drag. We drag the vertex _nearest_ to the pointer,
1095 * just in case one is nearly on top of another and we want
1096 * to drag the latter. However, we drag nothing at all if
1097 * the nearest vertex is outside DRAG_THRESHOLD.
1098 */
1099 best = -1;
1100 bestd = 0;
1101
1102 for (i = 0; i < n; i++) {
1103 long px = state->pts[i].x * ds->tilesize / state->pts[i].d;
1104 long py = state->pts[i].y * ds->tilesize / state->pts[i].d;
1105 long dx = px - x;
1106 long dy = py - y;
1107 long d = dx*dx + dy*dy;
1108
1109 if (best == -1 || bestd > d) {
1110 best = i;
1111 bestd = d;
1112 }
1113 }
1114
1115 if (bestd <= DRAG_THRESHOLD * DRAG_THRESHOLD) {
1116 ui->dragpoint = best;
1117 ui->newpoint.x = x;
1118 ui->newpoint.y = y;
1119 ui->newpoint.d = ds->tilesize;
1120 return "";
1121 }
1122
1123 } else if (IS_MOUSE_DRAG(button) && ui->dragpoint >= 0) {
1124 ui->newpoint.x = x;
1125 ui->newpoint.y = y;
1126 ui->newpoint.d = ds->tilesize;
1127 return "";
1128 } else if (IS_MOUSE_RELEASE(button) && ui->dragpoint >= 0) {
1129 int p = ui->dragpoint;
1130 char buf[80];
1131
1132 ui->dragpoint = -1; /* terminate drag, no matter what */
1133
1134 /*
1135 * First, see if we're within range. The user can cancel a
1136 * drag by dragging the point right off the window.
1137 */
1138 if (ui->newpoint.x < 0 ||
1139 ui->newpoint.x >= (long)state->w*ui->newpoint.d ||
1140 ui->newpoint.y < 0 ||
1141 ui->newpoint.y >= (long)state->h*ui->newpoint.d)
1142 return "";
1143
1144 /*
1145 * We aren't cancelling the drag. Construct a move string
1146 * indicating where this point is going to.
1147 */
1148 sprintf(buf, "P%d:%ld,%ld/%ld", p,
1149 ui->newpoint.x, ui->newpoint.y, ui->newpoint.d);
1150 ui->just_dragged = TRUE;
1151 return dupstr(buf);
1152 }
1153
1154 return NULL;
1155}
1156
1157static game_state *execute_move(const game_state *state, const char *move)
1158{
1159 int n = state->params.n;
1160 int p, k;
1161 long x, y, d;
1162 game_state *ret = dup_game(state);
1163
1164 ret->just_solved = FALSE;
1165
1166 while (*move) {
1167 if (*move == 'S') {
1168 move++;
1169 if (*move == ';') move++;
1170 ret->cheated = ret->just_solved = TRUE;
1171 }
1172 if (*move == 'P' &&
1173 sscanf(move+1, "%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k) == 4 &&
1174 p >= 0 && p < n && d > 0) {
1175 ret->pts[p].x = x;
1176 ret->pts[p].y = y;
1177 ret->pts[p].d = d;
1178
1179 move += k+1;
1180 if (*move == ';') move++;
1181 } else {
1182 free_game(ret);
1183 return NULL;
1184 }
1185 }
1186
1187 mark_crossings(ret);
1188
1189 return ret;
1190}
1191
1192/* ----------------------------------------------------------------------
1193 * Drawing routines.
1194 */
1195
1196static void game_compute_size(const game_params *params, int tilesize,
1197 int *x, int *y)
1198{
1199 *x = *y = COORDLIMIT(params->n) * tilesize;
1200}
1201
1202static void game_set_size(drawing *dr, game_drawstate *ds,
1203 const game_params *params, int tilesize)
1204{
1205 ds->tilesize = tilesize;
1206}
1207
1208static float *game_colours(frontend *fe, int *ncolours)
1209{
1210 float *ret = snewn(3 * NCOLOURS, float);
1211
1212 /*
1213 * COL_BACKGROUND is what we use as the normal background colour.
1214 * Unusually, though, it isn't colour #0: COL_SYSBACKGROUND, a bit
1215 * darker, takes that place. This means that if the user resizes
1216 * an Untangle window so as to change its aspect ratio, the
1217 * still-square playable area will be distinguished from the dead
1218 * space around it.
1219 */
1220 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_SYSBACKGROUND);
1221
1222 ret[COL_LINE * 3 + 0] = 0.0F;
1223 ret[COL_LINE * 3 + 1] = 0.0F;
1224 ret[COL_LINE * 3 + 2] = 0.0F;
1225
1226#ifdef SHOW_CROSSINGS
1227 ret[COL_CROSSEDLINE * 3 + 0] = 1.0F;
1228 ret[COL_CROSSEDLINE * 3 + 1] = 0.0F;
1229 ret[COL_CROSSEDLINE * 3 + 2] = 0.0F;
1230#endif
1231
1232 ret[COL_OUTLINE * 3 + 0] = 0.0F;
1233 ret[COL_OUTLINE * 3 + 1] = 0.0F;
1234 ret[COL_OUTLINE * 3 + 2] = 0.0F;
1235
1236 ret[COL_POINT * 3 + 0] = 0.0F;
1237 ret[COL_POINT * 3 + 1] = 0.0F;
1238 ret[COL_POINT * 3 + 2] = 1.0F;
1239
1240 ret[COL_DRAGPOINT * 3 + 0] = 1.0F;
1241 ret[COL_DRAGPOINT * 3 + 1] = 1.0F;
1242 ret[COL_DRAGPOINT * 3 + 2] = 1.0F;
1243
1244 ret[COL_NEIGHBOUR * 3 + 0] = 1.0F;
1245 ret[COL_NEIGHBOUR * 3 + 1] = 0.0F;
1246 ret[COL_NEIGHBOUR * 3 + 2] = 0.0F;
1247
1248 ret[COL_FLASH1 * 3 + 0] = 0.5F;
1249 ret[COL_FLASH1 * 3 + 1] = 0.5F;
1250 ret[COL_FLASH1 * 3 + 2] = 0.5F;
1251
1252 ret[COL_FLASH2 * 3 + 0] = 1.0F;
1253 ret[COL_FLASH2 * 3 + 1] = 1.0F;
1254 ret[COL_FLASH2 * 3 + 2] = 1.0F;
1255
1256 *ncolours = NCOLOURS;
1257 return ret;
1258}
1259
1260static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1261{
1262 struct game_drawstate *ds = snew(struct game_drawstate);
1263 int i;
1264
1265 ds->tilesize = 0;
1266 ds->x = snewn(state->params.n, long);
1267 ds->y = snewn(state->params.n, long);
1268 for (i = 0; i < state->params.n; i++)
1269 ds->x[i] = ds->y[i] = -1;
1270 ds->bg = -1;
1271 ds->dragpoint = -1;
1272
1273 return ds;
1274}
1275
1276static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1277{
1278 sfree(ds->y);
1279 sfree(ds->x);
1280 sfree(ds);
1281}
1282
1283static point mix(point a, point b, float distance)
1284{
1285 point ret;
1286
1287 ret.d = a.d * b.d;
1288 ret.x = (long)(a.x * b.d + distance * (b.x * a.d - a.x * b.d));
1289 ret.y = (long)(a.y * b.d + distance * (b.y * a.d - a.y * b.d));
1290
1291 return ret;
1292}
1293
1294static void game_redraw(drawing *dr, game_drawstate *ds,
1295 const game_state *oldstate, const game_state *state,
1296 int dir, const game_ui *ui,
1297 float animtime, float flashtime)
1298{
1299 int w, h;
1300 edge *e;
1301 int i, j;
1302 int bg, points_moved;
1303
1304 /*
1305 * There's no terribly sensible way to do partial redraws of
1306 * this game, so I'm going to have to resort to redrawing the
1307 * whole thing every time.
1308 */
1309
1310 if (flashtime == 0)
1311 bg = COL_BACKGROUND;
1312 else if ((int)(flashtime * 4 / FLASH_TIME) % 2 == 0)
1313 bg = COL_FLASH1;
1314 else
1315 bg = COL_FLASH2;
1316
1317 /*
1318 * To prevent excessive spinning on redraw during a completion
1319 * flash, we first check to see if _either_ the flash
1320 * background colour has changed _or_ at least one point has
1321 * moved _or_ a drag has begun or ended, and abandon the redraw
1322 * if neither is the case.
1323 *
1324 * Also in this loop we work out the coordinates of all the
1325 * points for this redraw.
1326 */
1327 points_moved = FALSE;
1328 for (i = 0; i < state->params.n; i++) {
1329 point p = state->pts[i];
1330 long x, y;
1331
1332 if (ui->dragpoint == i)
1333 p = ui->newpoint;
1334
1335 if (oldstate)
1336 p = mix(oldstate->pts[i], p, animtime / ui->anim_length);
1337
1338 x = p.x * ds->tilesize / p.d;
1339 y = p.y * ds->tilesize / p.d;
1340
1341 if (ds->x[i] != x || ds->y[i] != y)
1342 points_moved = TRUE;
1343
1344 ds->x[i] = x;
1345 ds->y[i] = y;
1346 }
1347
1348 if (ds->bg == bg && ds->dragpoint == ui->dragpoint && !points_moved)
1349 return; /* nothing to do */
1350
1351 ds->dragpoint = ui->dragpoint;
1352 ds->bg = bg;
1353
1354 game_compute_size(&state->params, ds->tilesize, &w, &h);
1355 draw_rect(dr, 0, 0, w, h, bg);
1356
1357 /*
1358 * Draw the edges.
1359 */
1360
1361 for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) {
1362 draw_line(dr, ds->x[e->a], ds->y[e->a], ds->x[e->b], ds->y[e->b],
1363#ifdef SHOW_CROSSINGS
1364 (oldstate?oldstate:state)->crosses[i] ?
1365 COL_CROSSEDLINE :
1366#endif
1367 COL_LINE);
1368 }
1369
1370 /*
1371 * Draw the points.
1372 *
1373 * When dragging, we should not only vary the colours, but
1374 * leave the point being dragged until last.
1375 */
1376 for (j = 0; j < 3; j++) {
1377 int thisc = (j == 0 ? COL_POINT :
1378 j == 1 ? COL_NEIGHBOUR : COL_DRAGPOINT);
1379 for (i = 0; i < state->params.n; i++) {
1380 int c;
1381
1382 if (ui->dragpoint == i) {
1383 c = COL_DRAGPOINT;
1384 } else if (ui->dragpoint >= 0 &&
1385 isedge(state->graph->edges, ui->dragpoint, i)) {
1386 c = COL_NEIGHBOUR;
1387 } else {
1388 c = COL_POINT;
1389 }
1390
1391 if (c == thisc) {
1392#ifdef VERTEX_NUMBERS
1393 draw_circle(dr, ds->x[i], ds->y[i], DRAG_THRESHOLD, bg, bg);
1394 {
1395 char buf[80];
1396 sprintf(buf, "%d", i);
1397 draw_text(dr, ds->x[i], ds->y[i], FONT_VARIABLE,
1398 DRAG_THRESHOLD*3/2,
1399 ALIGN_VCENTRE|ALIGN_HCENTRE, c, buf);
1400 }
1401#else
1402 draw_circle(dr, ds->x[i], ds->y[i], CIRCLE_RADIUS,
1403 c, COL_OUTLINE);
1404#endif
1405 }
1406 }
1407 }
1408
1409 draw_update(dr, 0, 0, w, h);
1410}
1411
1412static float game_anim_length(const game_state *oldstate,
1413 const game_state *newstate, int dir, game_ui *ui)
1414{
1415 if (ui->just_moved)
1416 return 0.0F;
1417 if ((dir < 0 ? oldstate : newstate)->just_solved)
1418 ui->anim_length = SOLVEANIM_TIME;
1419 else
1420 ui->anim_length = ANIM_TIME;
1421 return ui->anim_length;
1422}
1423
1424static float game_flash_length(const game_state *oldstate,
1425 const game_state *newstate, int dir, game_ui *ui)
1426{
1427 if (!oldstate->completed && newstate->completed &&
1428 !oldstate->cheated && !newstate->cheated)
1429 return FLASH_TIME;
1430 return 0.0F;
1431}
1432
1433static int game_status(const game_state *state)
1434{
1435 return state->completed ? +1 : 0;
1436}
1437
1438static int game_timing_state(const game_state *state, game_ui *ui)
1439{
1440 return TRUE;
1441}
1442
1443static void game_print_size(const game_params *params, float *x, float *y)
1444{
1445}
1446
1447static void game_print(drawing *dr, const game_state *state, int tilesize)
1448{
1449}
1450
1451#ifdef COMBINED
1452#define thegame untangle
1453#endif
1454
1455const struct game thegame = {
1456 "Untangle", "games.untangle", "untangle",
1457 default_params,
1458 game_fetch_preset,
1459 decode_params,
1460 encode_params,
1461 free_params,
1462 dup_params,
1463 TRUE, game_configure, custom_params,
1464 validate_params,
1465 new_game_desc,
1466 validate_desc,
1467 new_game,
1468 dup_game,
1469 free_game,
1470 TRUE, solve_game,
1471 FALSE, game_can_format_as_text_now, game_text_format,
1472 new_ui,
1473 free_ui,
1474 encode_ui,
1475 decode_ui,
1476 game_changed_state,
1477 interpret_move,
1478 execute_move,
1479 PREFERRED_TILESIZE, game_compute_size, game_set_size,
1480 game_colours,
1481 game_new_drawstate,
1482 game_free_drawstate,
1483 game_redraw,
1484 game_anim_length,
1485 game_flash_length,
1486 game_status,
1487 FALSE, FALSE, game_print_size, game_print,
1488 FALSE, /* wants_statusbar */
1489 FALSE, game_timing_state,
1490 SOLVE_ANIMATES, /* flags */
1491};
diff --git a/apps/plugins/puzzles/version.c b/apps/plugins/puzzles/version.c
new file mode 100644
index 0000000000..1cef29feb7
--- /dev/null
+++ b/apps/plugins/puzzles/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/version.h b/apps/plugins/puzzles/version.h
new file mode 100644
index 0000000000..997e00592b
--- /dev/null
+++ b/apps/plugins/puzzles/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/wceinf.pl b/apps/plugins/puzzles/wceinf.pl
new file mode 100644
index 0000000000..4756f3c2b8
--- /dev/null
+++ b/apps/plugins/puzzles/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/webpage.pl b/apps/plugins/puzzles/webpage.pl
new file mode 100755
index 0000000000..3a0779ef0a
--- /dev/null
+++ b/apps/plugins/puzzles/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/website.url b/apps/plugins/puzzles/website.url
new file mode 100644
index 0000000000..2ab37f6faf
--- /dev/null
+++ b/apps/plugins/puzzles/website.url
@@ -0,0 +1,2 @@
1[InternetShortcut]
2URL=http://www.chiark.greenend.org.uk/~sgtatham/puzzles/
diff --git a/apps/plugins/puzzles/windows.c b/apps/plugins/puzzles/windows.c
new file mode 100644
index 0000000000..492e37ceac
--- /dev/null
+++ b/apps/plugins/puzzles/windows.c
@@ -0,0 +1,3760 @@
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 "rbassert.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 frontend {
199 const game *game;
200 midend *me;
201 HWND hwnd, statusbar, cfgbox;
202#ifdef _WIN32_WCE
203 HWND numpad; /* window handle for the numeric pad */
204#endif
205 HINSTANCE inst;
206 HBITMAP bitmap, prevbm;
207 RECT bitmapPosition; /* game bitmap position within game window */
208 HDC hdc;
209 COLORREF *colours;
210 HBRUSH *brushes;
211 HPEN *pens;
212 HRGN clip;
213 HMENU gamemenu, typemenu;
214 UINT timer;
215 DWORD timer_last_tickcount;
216 int npresets;
217 game_params **presets;
218 struct font *fonts;
219 int nfonts, fontsize;
220 config_item *cfg;
221 struct cfg_aux *cfgaux;
222 int cfg_which, dlg_done;
223 HFONT cfgfont;
224 HBRUSH oldbr;
225 HPEN oldpen;
226 int help_running;
227 enum { DRAWING, PRINTING, NOTHING } drawstatus;
228 DOCINFO di;
229 int printcount, printw, printh, printsolns, printcurr, printcolour;
230 float printscale;
231 int printoffsetx, printoffsety;
232 float printpixelscale;
233 int fontstart;
234 int linewidth, linedotted;
235 drawing *dr;
236 int xmin, ymin;
237 float puzz_scale;
238};
239
240void frontend_free(frontend *fe)
241{
242 midend_free(fe->me);
243
244 sfree(fe->colours);
245 sfree(fe->brushes);
246 sfree(fe->pens);
247 sfree(fe->presets);
248 sfree(fe->fonts);
249
250 sfree(fe);
251}
252
253static void update_type_menu_tick(frontend *fe);
254static void update_copy_menu_greying(frontend *fe);
255
256void fatal(char *fmt, ...)
257{
258 char buf[2048];
259 va_list ap;
260
261 va_start(ap, fmt);
262 vsprintf(buf, fmt, ap);
263 va_end(ap);
264
265 MessageBox(NULL, buf, "Fatal error", MB_ICONEXCLAMATION | MB_OK);
266
267 exit(1);
268}
269
270char *geterrstr(void)
271{
272 LPVOID lpMsgBuf;
273 DWORD dw = GetLastError();
274 char *ret;
275
276 FormatMessage(
277 FORMAT_MESSAGE_ALLOCATE_BUFFER |
278 FORMAT_MESSAGE_FROM_SYSTEM,
279 NULL,
280 dw,
281 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
282 (LPTSTR) &lpMsgBuf,
283 0, NULL );
284
285 ret = dupstr(lpMsgBuf);
286
287 LocalFree(lpMsgBuf);
288
289 return ret;
290}
291
292void get_random_seed(void **randseed, int *randseedsize)
293{
294 SYSTEMTIME *st = snew(SYSTEMTIME);
295
296 GetLocalTime(st);
297
298 *randseed = (void *)st;
299 *randseedsize = sizeof(SYSTEMTIME);
300}
301
302static void win_status_bar(void *handle, char *text)
303{
304#ifdef _WIN32_WCE
305 TCHAR wText[255];
306#endif
307 frontend *fe = (frontend *)handle;
308
309#ifdef _WIN32_WCE
310 MultiByteToWideChar (CP_ACP, 0, text, -1, wText, 255);
311 SendMessage(fe->statusbar, SB_SETTEXT,
312 (WPARAM) 255 | SBT_NOBORDERS,
313 (LPARAM) wText);
314#else
315 SetWindowText(fe->statusbar, text);
316#endif
317}
318
319static blitter *win_blitter_new(void *handle, int w, int h)
320{
321 blitter *bl = snew(blitter);
322
323 memset(bl, 0, sizeof(blitter));
324 bl->w = w;
325 bl->h = h;
326 bl->bitmap = 0;
327
328 return bl;
329}
330
331static void win_blitter_free(void *handle, blitter *bl)
332{
333 if (bl->bitmap) DeleteObject(bl->bitmap);
334 sfree(bl);
335}
336
337static void blitter_mkbitmap(frontend *fe, blitter *bl)
338{
339 HDC hdc = GetDC(fe->hwnd);
340 bl->bitmap = CreateCompatibleBitmap(hdc, bl->w, bl->h);
341 ReleaseDC(fe->hwnd, hdc);
342}
343
344/* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */
345
346static void win_blitter_save(void *handle, blitter *bl, int x, int y)
347{
348 frontend *fe = (frontend *)handle;
349 HDC hdc_win, hdc_blit;
350 HBITMAP prev_blit;
351
352 assert(fe->drawstatus == DRAWING);
353
354 if (!bl->bitmap) blitter_mkbitmap(fe, bl);
355
356 bl->x = x; bl->y = y;
357
358 hdc_win = GetDC(fe->hwnd);
359 hdc_blit = CreateCompatibleDC(hdc_win);
360 if (!hdc_blit) fatal("hdc_blit failed: 0x%x", GetLastError());
361
362 prev_blit = SelectObject(hdc_blit, bl->bitmap);
363 if (prev_blit == NULL || prev_blit == HGDI_ERROR)
364 fatal("SelectObject for hdc_main failed: 0x%x", GetLastError());
365
366 if (!BitBlt(hdc_blit, 0, 0, bl->w, bl->h,
367 fe->hdc, x, y, SRCCOPY))
368 fatal("BitBlt failed: 0x%x", GetLastError());
369
370 SelectObject(hdc_blit, prev_blit);
371 DeleteDC(hdc_blit);
372 ReleaseDC(fe->hwnd, hdc_win);
373}
374
375static void win_blitter_load(void *handle, blitter *bl, int x, int y)
376{
377 frontend *fe = (frontend *)handle;
378 HDC hdc_win, hdc_blit;
379 HBITMAP prev_blit;
380
381 assert(fe->drawstatus == DRAWING);
382
383 assert(bl->bitmap); /* we should always have saved before loading */
384
385 if (x == BLITTER_FROMSAVED) x = bl->x;
386 if (y == BLITTER_FROMSAVED) y = bl->y;
387
388 hdc_win = GetDC(fe->hwnd);
389 hdc_blit = CreateCompatibleDC(hdc_win);
390
391 prev_blit = SelectObject(hdc_blit, bl->bitmap);
392
393 BitBlt(fe->hdc, x, y, bl->w, bl->h,
394 hdc_blit, 0, 0, SRCCOPY);
395
396 SelectObject(hdc_blit, prev_blit);
397 DeleteDC(hdc_blit);
398 ReleaseDC(fe->hwnd, hdc_win);
399}
400
401void frontend_default_colour(frontend *fe, float *output)
402{
403 DWORD c = GetSysColor(COLOR_MENU); /* ick */
404
405 output[0] = (float)(GetRValue(c) / 255.0);
406 output[1] = (float)(GetGValue(c) / 255.0);
407 output[2] = (float)(GetBValue(c) / 255.0);
408}
409
410static POINT win_transform_point(frontend *fe, int x, int y)
411{
412 POINT ret;
413
414 assert(fe->drawstatus != NOTHING);
415
416 if (fe->drawstatus == PRINTING) {
417 ret.x = (int)(fe->printoffsetx + fe->printpixelscale * x);
418 ret.y = (int)(fe->printoffsety + fe->printpixelscale * y);
419 } else {
420 ret.x = x;
421 ret.y = y;
422 }
423
424 return ret;
425}
426
427static void win_text_colour(frontend *fe, int colour)
428{
429 assert(fe->drawstatus != NOTHING);
430
431 if (fe->drawstatus == PRINTING) {
432 int hatch;
433 float r, g, b;
434 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
435
436 /*
437 * Displaying text in hatched colours is not permitted.
438 */
439 assert(hatch < 0);
440
441 SetTextColor(fe->hdc, RGB(r * 255, g * 255, b * 255));
442 } else {
443 SetTextColor(fe->hdc, fe->colours[colour]);
444 }
445}
446
447static void win_set_brush(frontend *fe, int colour)
448{
449 HBRUSH br;
450 assert(fe->drawstatus != NOTHING);
451
452 if (fe->drawstatus == PRINTING) {
453 int hatch;
454 float r, g, b;
455 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
456
457 if (hatch < 0) {
458 br = CreateSolidBrush(RGB(r * 255, g * 255, b * 255));
459 } else {
460#ifdef _WIN32_WCE
461 /*
462 * This is only ever required during printing, and the
463 * PocketPC port doesn't support printing.
464 */
465 fatal("CreateHatchBrush not supported");
466#else
467 br = CreateHatchBrush(hatch == HATCH_BACKSLASH ? HS_FDIAGONAL :
468 hatch == HATCH_SLASH ? HS_BDIAGONAL :
469 hatch == HATCH_HORIZ ? HS_HORIZONTAL :
470 hatch == HATCH_VERT ? HS_VERTICAL :
471 hatch == HATCH_PLUS ? HS_CROSS :
472 /* hatch == HATCH_X ? */ HS_DIAGCROSS,
473 RGB(0,0,0));
474#endif
475 }
476 } else {
477 br = fe->brushes[colour];
478 }
479 fe->oldbr = SelectObject(fe->hdc, br);
480}
481
482static void win_reset_brush(frontend *fe)
483{
484 HBRUSH br;
485
486 assert(fe->drawstatus != NOTHING);
487
488 br = SelectObject(fe->hdc, fe->oldbr);
489 if (fe->drawstatus == PRINTING)
490 DeleteObject(br);
491}
492
493static void win_set_pen(frontend *fe, int colour, int thin)
494{
495 HPEN pen;
496 assert(fe->drawstatus != NOTHING);
497
498 if (fe->drawstatus == PRINTING) {
499 int hatch;
500 float r, g, b;
501 int width = thin ? 0 : fe->linewidth;
502
503 if (fe->linedotted)
504 width = 0;
505
506 print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
507 /*
508 * Stroking in hatched colours is not permitted.
509 */
510 assert(hatch < 0);
511 pen = CreatePen(fe->linedotted ? PS_DOT : PS_SOLID,
512 width, RGB(r * 255, g * 255, b * 255));
513 } else {
514 pen = fe->pens[colour];
515 }
516 fe->oldpen = SelectObject(fe->hdc, pen);
517}
518
519static void win_reset_pen(frontend *fe)
520{
521 HPEN pen;
522
523 assert(fe->drawstatus != NOTHING);
524
525 pen = SelectObject(fe->hdc, fe->oldpen);
526 if (fe->drawstatus == PRINTING)
527 DeleteObject(pen);
528}
529
530static void win_clip(void *handle, int x, int y, int w, int h)
531{
532 frontend *fe = (frontend *)handle;
533 POINT p, q;
534
535 if (fe->drawstatus == NOTHING)
536 return;
537
538 p = win_transform_point(fe, x, y);
539 q = win_transform_point(fe, x+w, y+h);
540 IntersectClipRect(fe->hdc, p.x, p.y, q.x, q.y);
541}
542
543static void win_unclip(void *handle)
544{
545 frontend *fe = (frontend *)handle;
546
547 if (fe->drawstatus == NOTHING)
548 return;
549
550 SelectClipRgn(fe->hdc, NULL);
551}
552
553static void win_draw_text(void *handle, int x, int y, int fonttype,
554 int fontsize, int align, int colour, char *text)
555{
556 frontend *fe = (frontend *)handle;
557 POINT xy;
558 int i;
559 LOGFONT lf;
560
561 if (fe->drawstatus == NOTHING)
562 return;
563
564 if (fe->drawstatus == PRINTING)
565 fontsize = (int)(fontsize * fe->printpixelscale);
566
567 xy = win_transform_point(fe, x, y);
568
569 /*
570 * Find or create the font.
571 */
572 for (i = fe->fontstart; i < fe->nfonts; i++)
573 if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
574 break;
575
576 if (i == fe->nfonts) {
577 if (fe->fontsize <= fe->nfonts) {
578 fe->fontsize = fe->nfonts + 10;
579 fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
580 }
581
582 fe->nfonts++;
583
584 fe->fonts[i].type = fonttype;
585 fe->fonts[i].size = fontsize;
586
587 memset (&lf, 0, sizeof(LOGFONT));
588 lf.lfHeight = -fontsize;
589 lf.lfWeight = (fe->drawstatus == PRINTING ? 0 : FW_BOLD);
590 lf.lfCharSet = DEFAULT_CHARSET;
591 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
592 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
593 lf.lfQuality = DEFAULT_QUALITY;
594 lf.lfPitchAndFamily = (fonttype == FONT_FIXED ?
595 FIXED_PITCH | FF_DONTCARE :
596 VARIABLE_PITCH | FF_SWISS);
597#ifdef _WIN32_WCE
598 wcscpy(lf.lfFaceName, TEXT("Tahoma"));
599#endif
600
601 fe->fonts[i].font = CreateFontIndirect(&lf);
602 }
603
604 /*
605 * Position and draw the text.
606 */
607 {
608 HFONT oldfont;
609 TEXTMETRIC tm;
610 SIZE size;
611 WCHAR wText[256];
612 MultiByteToWideChar (CP_UTF8, 0, text, -1, wText, 256);
613
614 oldfont = SelectObject(fe->hdc, fe->fonts[i].font);
615 if (GetTextMetrics(fe->hdc, &tm)) {
616 if (align & ALIGN_VCENTRE)
617 xy.y -= (tm.tmAscent+tm.tmDescent)/2;
618 else
619 xy.y -= tm.tmAscent;
620 }
621 if (GetTextExtentPoint32W(fe->hdc, wText, wcslen(wText), &size))
622 {
623 if (align & ALIGN_HCENTRE)
624 xy.x -= size.cx / 2;
625 else if (align & ALIGN_HRIGHT)
626 xy.x -= size.cx;
627 }
628 SetBkMode(fe->hdc, TRANSPARENT);
629 win_text_colour(fe, colour);
630 ExtTextOutW(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL);
631 SelectObject(fe->hdc, oldfont);
632 }
633}
634
635static void win_draw_rect(void *handle, int x, int y, int w, int h, int colour)
636{
637 frontend *fe = (frontend *)handle;
638 POINT p, q;
639
640 if (fe->drawstatus == NOTHING)
641 return;
642
643 if (fe->drawstatus == DRAWING && w == 1 && h == 1) {
644 /*
645 * Rectangle() appears to get uppity if asked to draw a 1x1
646 * rectangle, presumably on the grounds that that's beneath
647 * its dignity and you ought to be using SetPixel instead.
648 * So I will.
649 */
650 SetPixel(fe->hdc, x, y, fe->colours[colour]);
651 } else {
652 win_set_brush(fe, colour);
653 win_set_pen(fe, colour, TRUE);
654 p = win_transform_point(fe, x, y);
655 q = win_transform_point(fe, x+w, y+h);
656 Rectangle(fe->hdc, p.x, p.y, q.x, q.y);
657 win_reset_brush(fe);
658 win_reset_pen(fe);
659 }
660}
661
662static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
663{
664 frontend *fe = (frontend *)handle;
665 POINT pp[2];
666
667 if (fe->drawstatus == NOTHING)
668 return;
669
670 win_set_pen(fe, colour, FALSE);
671 pp[0] = win_transform_point(fe, x1, y1);
672 pp[1] = win_transform_point(fe, x2, y2);
673 Polyline(fe->hdc, pp, 2);
674 if (fe->drawstatus == DRAWING)
675 SetPixel(fe->hdc, pp[1].x, pp[1].y, fe->colours[colour]);
676 win_reset_pen(fe);
677}
678
679static void win_draw_circle(void *handle, int cx, int cy, int radius,
680 int fillcolour, int outlinecolour)
681{
682 frontend *fe = (frontend *)handle;
683 POINT p, q;
684
685 assert(outlinecolour >= 0);
686
687 if (fe->drawstatus == NOTHING)
688 return;
689
690 if (fillcolour >= 0)
691 win_set_brush(fe, fillcolour);
692 else
693 fe->oldbr = SelectObject(fe->hdc, GetStockObject(NULL_BRUSH));
694
695 win_set_pen(fe, outlinecolour, FALSE);
696 p = win_transform_point(fe, cx - radius, cy - radius);
697 q = win_transform_point(fe, cx + radius, cy + radius);
698 Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1);
699 win_reset_brush(fe);
700 win_reset_pen(fe);
701}
702
703static void win_draw_polygon(void *handle, int *coords, int npoints,
704 int fillcolour, int outlinecolour)
705{
706 frontend *fe = (frontend *)handle;
707 POINT *pts;
708 int i;
709
710 if (fe->drawstatus == NOTHING)
711 return;
712
713 pts = snewn(npoints+1, POINT);
714
715 for (i = 0; i <= npoints; i++) {
716 int j = (i < npoints ? i : 0);
717 pts[i] = win_transform_point(fe, coords[j*2], coords[j*2+1]);
718 }
719
720 assert(outlinecolour >= 0);
721
722 if (fillcolour >= 0) {
723 win_set_brush(fe, fillcolour);
724 win_set_pen(fe, outlinecolour, FALSE);
725 Polygon(fe->hdc, pts, npoints);
726 win_reset_brush(fe);
727 win_reset_pen(fe);
728 } else {
729 win_set_pen(fe, outlinecolour, FALSE);
730 Polyline(fe->hdc, pts, npoints+1);
731 win_reset_pen(fe);
732 }
733
734 sfree(pts);
735}
736
737static void win_start_draw(void *handle)
738{
739 frontend *fe = (frontend *)handle;
740 HDC hdc_win;
741
742 assert(fe->drawstatus == NOTHING);
743
744 hdc_win = GetDC(fe->hwnd);
745 fe->hdc = CreateCompatibleDC(hdc_win);
746 fe->prevbm = SelectObject(fe->hdc, fe->bitmap);
747 ReleaseDC(fe->hwnd, hdc_win);
748 fe->clip = NULL;
749#ifndef _WIN32_WCE
750 SetMapMode(fe->hdc, MM_TEXT);
751#endif
752 fe->drawstatus = DRAWING;
753}
754
755static void win_draw_update(void *handle, int x, int y, int w, int h)
756{
757 frontend *fe = (frontend *)handle;
758 RECT r;
759
760 if (fe->drawstatus != DRAWING)
761 return;
762
763 r.left = x;
764 r.top = y;
765 r.right = x + w;
766 r.bottom = y + h;
767
768 OffsetRect(&r, fe->bitmapPosition.left, fe->bitmapPosition.top);
769 InvalidateRect(fe->hwnd, &r, FALSE);
770}
771
772static void win_end_draw(void *handle)
773{
774 frontend *fe = (frontend *)handle;
775 assert(fe->drawstatus == DRAWING);
776 SelectObject(fe->hdc, fe->prevbm);
777 DeleteDC(fe->hdc);
778 if (fe->clip) {
779 DeleteObject(fe->clip);
780 fe->clip = NULL;
781 }
782 fe->drawstatus = NOTHING;
783}
784
785static void win_line_width(void *handle, float width)
786{
787 frontend *fe = (frontend *)handle;
788
789 assert(fe->drawstatus != DRAWING);
790 if (fe->drawstatus == NOTHING)
791 return;
792
793 fe->linewidth = (int)(width * fe->printpixelscale);
794}
795
796static void win_line_dotted(void *handle, int dotted)
797{
798 frontend *fe = (frontend *)handle;
799
800 assert(fe->drawstatus != DRAWING);
801 if (fe->drawstatus == NOTHING)
802 return;
803
804 fe->linedotted = dotted;
805}
806
807static void win_begin_doc(void *handle, int pages)
808{
809 frontend *fe = (frontend *)handle;
810
811 assert(fe->drawstatus != DRAWING);
812 if (fe->drawstatus == NOTHING)
813 return;
814
815 if (StartDoc(fe->hdc, &fe->di) <= 0) {
816 char *e = geterrstr();
817 MessageBox(fe->hwnd, e, "Error starting to print",
818 MB_ICONERROR | MB_OK);
819 sfree(e);
820 fe->drawstatus = NOTHING;
821 }
822
823 /*
824 * Push a marker on the font stack so that we won't use the
825 * same fonts for printing and drawing. (This is because
826 * drawing seems to look generally better in bold, but printing
827 * is better not in bold.)
828 */
829 fe->fontstart = fe->nfonts;
830}
831
832static void win_begin_page(void *handle, int number)
833{
834 frontend *fe = (frontend *)handle;
835
836 assert(fe->drawstatus != DRAWING);
837 if (fe->drawstatus == NOTHING)
838 return;
839
840 if (StartPage(fe->hdc) <= 0) {
841 char *e = geterrstr();
842 MessageBox(fe->hwnd, e, "Error starting a page",
843 MB_ICONERROR | MB_OK);
844 sfree(e);
845 fe->drawstatus = NOTHING;
846 }
847}
848
849static void win_begin_puzzle(void *handle, float xm, float xc,
850 float ym, float yc, int pw, int ph, float wmm)
851{
852 frontend *fe = (frontend *)handle;
853 int ppw, pph, pox, poy;
854 float mmpw, mmph, mmox, mmoy;
855 float scale;
856
857 assert(fe->drawstatus != DRAWING);
858 if (fe->drawstatus == NOTHING)
859 return;
860
861 ppw = GetDeviceCaps(fe->hdc, HORZRES);
862 pph = GetDeviceCaps(fe->hdc, VERTRES);
863 mmpw = (float)GetDeviceCaps(fe->hdc, HORZSIZE);
864 mmph = (float)GetDeviceCaps(fe->hdc, VERTSIZE);
865
866 /*
867 * Compute the puzzle's position on the logical page.
868 */
869 mmox = xm * mmpw + xc;
870 mmoy = ym * mmph + yc;
871
872 /*
873 * Work out what that comes to in pixels.
874 */
875 pox = (int)(mmox * (float)ppw / mmpw);
876 poy = (int)(mmoy * (float)pph / mmph);
877
878 /*
879 * And determine the scale.
880 *
881 * I need a scale such that the maximum puzzle-coordinate
882 * extent of the rectangle (pw * scale) is equal to the pixel
883 * equivalent of the puzzle's millimetre width (wmm * ppw /
884 * mmpw).
885 */
886 scale = (wmm * ppw) / (mmpw * pw);
887
888 /*
889 * Now store pox, poy and scale for use in the main drawing
890 * functions.
891 */
892 fe->printoffsetx = pox;
893 fe->printoffsety = poy;
894 fe->printpixelscale = scale;
895
896 fe->linewidth = 1;
897 fe->linedotted = FALSE;
898}
899
900static void win_end_puzzle(void *handle)
901{
902 /* Nothing needs to be done here. */
903}
904
905static void win_end_page(void *handle, int number)
906{
907 frontend *fe = (frontend *)handle;
908
909 assert(fe->drawstatus != DRAWING);
910
911 if (fe->drawstatus == NOTHING)
912 return;
913
914 if (EndPage(fe->hdc) <= 0) {
915 char *e = geterrstr();
916 MessageBox(fe->hwnd, e, "Error finishing a page",
917 MB_ICONERROR | MB_OK);
918 sfree(e);
919 fe->drawstatus = NOTHING;
920 }
921}
922
923static void win_end_doc(void *handle)
924{
925 frontend *fe = (frontend *)handle;
926
927 assert(fe->drawstatus != DRAWING);
928
929 /*
930 * Free all the fonts created since we began printing.
931 */
932 while (fe->nfonts > fe->fontstart) {
933 fe->nfonts--;
934 DeleteObject(fe->fonts[fe->nfonts].font);
935 }
936 fe->fontstart = 0;
937
938 /*
939 * The MSDN web site sample code doesn't bother to call EndDoc
940 * if an error occurs half way through printing. I expect doing
941 * so would cause the erroneous document to actually be
942 * printed, or something equally undesirable.
943 */
944 if (fe->drawstatus == NOTHING)
945 return;
946
947 if (EndDoc(fe->hdc) <= 0) {
948 char *e = geterrstr();
949 MessageBox(fe->hwnd, e, "Error finishing printing",
950 MB_ICONERROR | MB_OK);
951 sfree(e);
952 fe->drawstatus = NOTHING;
953 }
954}
955
956char *win_text_fallback(void *handle, const char *const *strings, int nstrings)
957{
958 /*
959 * We assume Windows can cope with any UTF-8 likely to be
960 * emitted by a puzzle.
961 */
962 return dupstr(strings[0]);
963}
964
965const struct drawing_api win_drawing = {
966 win_draw_text,
967 win_draw_rect,
968 win_draw_line,
969 win_draw_polygon,
970 win_draw_circle,
971 win_draw_update,
972 win_clip,
973 win_unclip,
974 win_start_draw,
975 win_end_draw,
976 win_status_bar,
977 win_blitter_new,
978 win_blitter_free,
979 win_blitter_save,
980 win_blitter_load,
981 win_begin_doc,
982 win_begin_page,
983 win_begin_puzzle,
984 win_end_puzzle,
985 win_end_page,
986 win_end_doc,
987 win_line_width,
988 win_line_dotted,
989 win_text_fallback,
990};
991
992void print(frontend *fe)
993{
994#ifndef _WIN32_WCE
995 PRINTDLG pd;
996 char doctitle[256];
997 document *doc;
998 midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */
999 int i;
1000 char *err = NULL;
1001
1002 /*
1003 * Create our document structure and fill it up with puzzles.
1004 */
1005 doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F);
1006 for (i = 0; i < fe->printcount; i++) {
1007 if (i == 0 && fe->printcurr) {
1008 err = midend_print_puzzle(fe->me, doc, fe->printsolns);
1009 } else {
1010 if (!nme) {
1011 game_params *params;
1012
1013 nme = midend_new(NULL, fe->game, NULL, NULL);
1014
1015 /*
1016 * Set the non-interactive mid-end to have the same
1017 * parameters as the standard one.
1018 */
1019 params = midend_get_params(fe->me);
1020 midend_set_params(nme, params);
1021 fe->game->free_params(params);
1022 }
1023
1024 midend_new_game(nme);
1025 err = midend_print_puzzle(nme, doc, fe->printsolns);
1026 }
1027 if (err)
1028 break;
1029 }
1030 if (nme)
1031 midend_free(nme);
1032
1033 if (err) {
1034 MessageBox(fe->hwnd, err, "Error preparing puzzles for printing",
1035 MB_ICONERROR | MB_OK);
1036 document_free(doc);
1037 return;
1038 }
1039
1040 memset(&pd, 0, sizeof(pd));
1041 pd.lStructSize = sizeof(pd);
1042 pd.hwndOwner = fe->hwnd;
1043 pd.hDevMode = NULL;
1044 pd.hDevNames = NULL;
1045 pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC |
1046 PD_NOPAGENUMS | PD_NOSELECTION;
1047 pd.nCopies = 1;
1048 pd.nFromPage = pd.nToPage = 0xFFFF;
1049 pd.nMinPage = pd.nMaxPage = 1;
1050
1051 if (!PrintDlg(&pd)) {
1052 document_free(doc);
1053 return;
1054 }
1055
1056 /*
1057 * Now pd.hDC is a device context for the printer.
1058 */
1059
1060 /*
1061 * FIXME: IWBNI we put up an Abort box here.
1062 */
1063
1064 memset(&fe->di, 0, sizeof(fe->di));
1065 fe->di.cbSize = sizeof(fe->di);
1066 sprintf(doctitle, "Printed puzzles from %s (from Simon Tatham's"
1067 " Portable Puzzle Collection)", fe->game->name);
1068 fe->di.lpszDocName = doctitle;
1069 fe->di.lpszOutput = NULL;
1070 fe->di.lpszDatatype = NULL;
1071 fe->di.fwType = 0;
1072
1073 fe->drawstatus = PRINTING;
1074 fe->hdc = pd.hDC;
1075
1076 fe->dr = drawing_new(&win_drawing, NULL, fe);
1077 document_print(doc, fe->dr);
1078 drawing_free(fe->dr);
1079 fe->dr = NULL;
1080
1081 fe->drawstatus = NOTHING;
1082
1083 DeleteDC(pd.hDC);
1084 document_free(doc);
1085#endif
1086}
1087
1088void deactivate_timer(frontend *fe)
1089{
1090 if (!fe)
1091 return; /* for non-interactive midend */
1092 if (fe->hwnd) KillTimer(fe->hwnd, fe->timer);
1093 fe->timer = 0;
1094}
1095
1096void activate_timer(frontend *fe)
1097{
1098 if (!fe)
1099 return; /* for non-interactive midend */
1100 if (!fe->timer) {
1101 fe->timer = SetTimer(fe->hwnd, 1, 20, NULL);
1102 fe->timer_last_tickcount = GetTickCount();
1103 }
1104}
1105
1106void write_clip(HWND hwnd, char *data)
1107{
1108 HGLOBAL clipdata;
1109 int len, i, j;
1110 char *data2;
1111 void *lock;
1112
1113 /*
1114 * Windows expects CRLF in the clipboard, so we must convert
1115 * any \n that has come out of the puzzle backend.
1116 */
1117 len = 0;
1118 for (i = 0; data[i]; i++) {
1119 if (data[i] == '\n')
1120 len++;
1121 len++;
1122 }
1123 data2 = snewn(len+1, char);
1124 j = 0;
1125 for (i = 0; data[i]; i++) {
1126 if (data[i] == '\n')
1127 data2[j++] = '\r';
1128 data2[j++] = data[i];
1129 }
1130 assert(j == len);
1131 data2[j] = '\0';
1132
1133 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
1134 if (!clipdata) {
1135 sfree(data2);
1136 return;
1137 }
1138 lock = GlobalLock(clipdata);
1139 if (!lock) {
1140 GlobalFree(clipdata);
1141 sfree(data2);
1142 return;
1143 }
1144 memcpy(lock, data2, len);
1145 ((unsigned char *) lock)[len] = 0;
1146 GlobalUnlock(clipdata);
1147
1148 if (OpenClipboard(hwnd)) {
1149 EmptyClipboard();
1150 SetClipboardData(CF_TEXT, clipdata);
1151 CloseClipboard();
1152 } else
1153 GlobalFree(clipdata);
1154
1155 sfree(data2);
1156}
1157
1158/*
1159 * Set up Help and see if we can find a help file.
1160 */
1161static void init_help(void)
1162{
1163#ifndef _WIN32_WCE
1164 char b[2048], *p, *q, *r;
1165 FILE *fp;
1166
1167 /*
1168 * Find the executable file path, so we can look alongside
1169 * it for help files. Trim the filename off the end.
1170 */
1171 GetModuleFileName(NULL, b, sizeof(b) - 1);
1172 r = b;
1173 p = strrchr(b, '\\');
1174 if (p && p >= r) r = p+1;
1175 q = strrchr(b, ':');
1176 if (q && q >= r) r = q+1;
1177
1178#ifndef NO_HTMLHELP
1179 /*
1180 * Try HTML Help first.
1181 */
1182 strcpy(r, CHM_FILE_NAME);
1183 if ( (fp = fopen(b, "r")) != NULL) {
1184 fclose(fp);
1185
1186 /*
1187 * We have a .CHM. See if we can use it.
1188 */
1189 hh_dll = LoadLibrary("hhctrl.ocx");
1190 if (hh_dll) {
1191 htmlhelp = (htmlhelp_t)GetProcAddress(hh_dll, "HtmlHelpA");
1192 if (!htmlhelp)
1193 FreeLibrary(hh_dll);
1194 }
1195 if (htmlhelp) {
1196 help_path = dupstr(b);
1197 help_type = CHM;
1198 return;
1199 }
1200 }
1201#endif /* NO_HTMLHELP */
1202
1203 /*
1204 * Now try old-style .HLP.
1205 */
1206 strcpy(r, HELP_FILE_NAME);
1207 if ( (fp = fopen(b, "r")) != NULL) {
1208 fclose(fp);
1209
1210 help_path = dupstr(b);
1211 help_type = HLP;
1212
1213 /*
1214 * See if there's a .CNT file alongside it.
1215 */
1216 strcpy(r, HELP_CNT_NAME);
1217 if ( (fp = fopen(b, "r")) != NULL) {
1218 fclose(fp);
1219 help_has_contents = TRUE;
1220 } else
1221 help_has_contents = FALSE;
1222
1223 return;
1224 }
1225
1226 help_type = NONE; /* didn't find any */
1227#endif
1228}
1229
1230#ifndef _WIN32_WCE
1231
1232/*
1233 * Start Help.
1234 */
1235static void start_help(frontend *fe, const char *topic)
1236{
1237 char *str = NULL;
1238 int cmd;
1239
1240 switch (help_type) {
1241 case HLP:
1242 assert(help_path);
1243 if (topic) {
1244 str = snewn(10+strlen(topic), char);
1245 sprintf(str, "JI(`',`%s')", topic);
1246 cmd = HELP_COMMAND;
1247 } else if (help_has_contents) {
1248 cmd = HELP_FINDER;
1249 } else {
1250 cmd = HELP_CONTENTS;
1251 }
1252 WinHelp(fe->hwnd, help_path, cmd, (DWORD)str);
1253 fe->help_running = TRUE;
1254 break;
1255 case CHM:
1256#ifndef NO_HTMLHELP
1257 assert(help_path);
1258 assert(htmlhelp);
1259 if (topic) {
1260 str = snewn(20 + strlen(topic) + strlen(help_path), char);
1261 sprintf(str, "%s::/%s.html>main", help_path, topic);
1262 } else {
1263 str = dupstr(help_path);
1264 }
1265 htmlhelp(fe->hwnd, str, HH_DISPLAY_TOPIC, 0);
1266 fe->help_running = TRUE;
1267 break;
1268#endif /* NO_HTMLHELP */
1269 case NONE:
1270 assert(!"This shouldn't happen");
1271 break;
1272 }
1273
1274 sfree(str);
1275}
1276
1277/*
1278 * Stop Help on window cleanup.
1279 */
1280static void stop_help(frontend *fe)
1281{
1282 if (fe->help_running) {
1283 switch (help_type) {
1284 case HLP:
1285 WinHelp(fe->hwnd, help_path, HELP_QUIT, 0);
1286 break;
1287 case CHM:
1288#ifndef NO_HTMLHELP
1289 assert(htmlhelp);
1290 htmlhelp(NULL, NULL, HH_CLOSE_ALL, 0);
1291 break;
1292#endif /* NO_HTMLHELP */
1293 case NONE:
1294 assert(!"This shouldn't happen");
1295 break;
1296 }
1297 fe->help_running = FALSE;
1298 }
1299}
1300
1301#endif
1302
1303/*
1304 * Terminate Help on process exit.
1305 */
1306static void cleanup_help(void)
1307{
1308 /* Nothing to do currently.
1309 * (If we were running HTML Help single-threaded, this is where we'd
1310 * call HH_UNINITIALIZE.) */
1311}
1312
1313static int get_statusbar_height(frontend *fe)
1314{
1315 int sy;
1316 if (fe->statusbar) {
1317 RECT sr;
1318 GetWindowRect(fe->statusbar, &sr);
1319 sy = sr.bottom - sr.top;
1320 } else {
1321 sy = 0;
1322 }
1323 return sy;
1324}
1325
1326static void adjust_statusbar(frontend *fe, RECT *r)
1327{
1328 int sy;
1329
1330 if (!fe->statusbar) return;
1331
1332 sy = get_statusbar_height(fe);
1333#ifndef _WIN32_WCE
1334 SetWindowPos(fe->statusbar, NULL, 0, r->bottom-r->top-sy, r->right-r->left,
1335 sy, SWP_NOZORDER);
1336#endif
1337}
1338
1339static void get_menu_size(HWND wh, RECT *r)
1340{
1341 HMENU bar = GetMenu(wh);
1342 RECT rect;
1343 int i;
1344
1345 SetRect(r, 0, 0, 0, 0);
1346 for (i = 0; i < GetMenuItemCount(bar); i++) {
1347 GetMenuItemRect(wh, bar, i, &rect);
1348 UnionRect(r, r, &rect);
1349 }
1350}
1351
1352/*
1353 * Given a proposed new puzzle size (cx,cy), work out the actual
1354 * puzzle size that would be (px,py) and the window size including
1355 * furniture (wx,wy).
1356 */
1357
1358static int check_window_resize(frontend *fe, int cx, int cy,
1359 int *px, int *py,
1360 int *wx, int *wy)
1361{
1362 RECT r;
1363 int x, y, sy = get_statusbar_height(fe), changed = 0;
1364
1365 /* disallow making window thinner than menu bar */
1366 x = max(cx, fe->xmin);
1367 y = max(cy - sy, fe->ymin);
1368
1369 /*
1370 * See if we actually got the window size we wanted, and adjust
1371 * the puzzle size if not.
1372 */
1373 midend_size(fe->me, &x, &y, TRUE);
1374 if (x != cx || y != cy) {
1375 /*
1376 * Resize the window, now we know what size we _really_
1377 * want it to be.
1378 */
1379 r.left = r.top = 0;
1380 r.right = x;
1381 r.bottom = y + sy;
1382 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1383 *wx = r.right - r.left;
1384 *wy = r.bottom - r.top;
1385 changed = 1;
1386 }
1387
1388 *px = x;
1389 *py = y;
1390
1391 fe->puzz_scale =
1392 (float)midend_tilesize(fe->me) / (float)fe->game->preferred_tilesize;
1393
1394 return changed;
1395}
1396
1397/*
1398 * Given the current window size, make sure it's sane for the
1399 * current puzzle and resize if necessary.
1400 */
1401
1402static void check_window_size(frontend *fe, int *px, int *py)
1403{
1404 RECT r;
1405 int wx, wy, cx, cy;
1406
1407 GetClientRect(fe->hwnd, &r);
1408 cx = r.right - r.left;
1409 cy = r.bottom - r.top;
1410
1411 if (check_window_resize(fe, cx, cy, px, py, &wx, &wy)) {
1412#ifdef _WIN32_WCE
1413 SetWindowPos(fe->hwnd, NULL, 0, 0, wx, wy,
1414 SWP_NOMOVE | SWP_NOZORDER);
1415#endif
1416 ;
1417 }
1418
1419 GetClientRect(fe->hwnd, &r);
1420 adjust_statusbar(fe, &r);
1421}
1422
1423static void get_max_puzzle_size(frontend *fe, int *x, int *y)
1424{
1425 RECT r, sr;
1426
1427 if (SystemParametersInfo(SPI_GETWORKAREA, 0, &sr, FALSE)) {
1428 *x = sr.right - sr.left;
1429 *y = sr.bottom - sr.top;
1430 r.left = 100;
1431 r.right = 200;
1432 r.top = 100;
1433 r.bottom = 200;
1434 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1435 *x -= r.right - r.left - 100;
1436 *y -= r.bottom - r.top - 100;
1437 } else {
1438 *x = *y = INT_MAX;
1439 }
1440
1441 if (fe->statusbar != NULL) {
1442 GetWindowRect(fe->statusbar, &sr);
1443 *y -= sr.bottom - sr.top;
1444 }
1445}
1446
1447#ifdef _WIN32_WCE
1448/* Toolbar buttons on the numeric pad */
1449static TBBUTTON tbNumpadButtons[] =
1450{
1451 {0, IDM_KEYEMUL + '1', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1452 {1, IDM_KEYEMUL + '2', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1453 {2, IDM_KEYEMUL + '3', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1454 {3, IDM_KEYEMUL + '4', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1455 {4, IDM_KEYEMUL + '5', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1456 {5, IDM_KEYEMUL + '6', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1457 {6, IDM_KEYEMUL + '7', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1458 {7, IDM_KEYEMUL + '8', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1459 {8, IDM_KEYEMUL + '9', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1460 {9, IDM_KEYEMUL + ' ', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}
1461};
1462#endif
1463
1464/*
1465 * Allocate a new frontend structure and create its main window.
1466 */
1467static frontend *frontend_new(HINSTANCE inst)
1468{
1469 frontend *fe;
1470 const char *nogame = "Puzzles (no game selected)";
1471
1472 fe = snew(frontend);
1473
1474 fe->inst = inst;
1475
1476 fe->game = NULL;
1477 fe->me = NULL;
1478
1479 fe->timer = 0;
1480 fe->hwnd = NULL;
1481
1482 fe->help_running = FALSE;
1483
1484 fe->drawstatus = NOTHING;
1485 fe->dr = NULL;
1486 fe->fontstart = 0;
1487
1488 fe->fonts = NULL;
1489 fe->nfonts = fe->fontsize = 0;
1490
1491 fe->colours = NULL;
1492 fe->brushes = NULL;
1493 fe->pens = NULL;
1494
1495 fe->puzz_scale = 1.0;
1496
1497 #ifdef _WIN32_WCE
1498 MultiByteToWideChar (CP_ACP, 0, nogame, -1, wGameName, 256);
1499 fe->hwnd = CreateWindowEx(0, wClassName, wGameName,
1500 WS_VISIBLE,
1501 CW_USEDEFAULT, CW_USEDEFAULT,
1502 CW_USEDEFAULT, CW_USEDEFAULT,
1503 NULL, NULL, inst, NULL);
1504
1505 {
1506 SHMENUBARINFO mbi;
1507 RECT rc, rcBar, rcTB, rcClient;
1508
1509 memset (&mbi, 0, sizeof(SHMENUBARINFO));
1510 mbi.cbSize = sizeof(SHMENUBARINFO);
1511 mbi.hwndParent = fe->hwnd;
1512 mbi.nToolBarId = IDR_MENUBAR1;
1513 mbi.hInstRes = inst;
1514
1515 SHCreateMenuBar(&mbi);
1516
1517 GetWindowRect(fe->hwnd, &rc);
1518 GetWindowRect(mbi.hwndMB, &rcBar);
1519 rc.bottom -= rcBar.bottom - rcBar.top;
1520 MoveWindow(fe->hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
1521
1522 fe->numpad = NULL;
1523 }
1524#else
1525 fe->hwnd = CreateWindowEx(0, CLASSNAME, nogame,
1526 WS_OVERLAPPEDWINDOW &~
1527 (WS_MAXIMIZEBOX),
1528 CW_USEDEFAULT, CW_USEDEFAULT,
1529 CW_USEDEFAULT, CW_USEDEFAULT,
1530 NULL, NULL, inst, NULL);
1531 if (!fe->hwnd) {
1532 DWORD lerr = GetLastError();
1533 printf("no window: 0x%x\n", lerr);
1534 }
1535#endif
1536
1537 fe->gamemenu = NULL;
1538 fe->presets = NULL;
1539
1540 fe->statusbar = NULL;
1541 fe->bitmap = NULL;
1542
1543 SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe);
1544
1545 return fe;
1546}
1547
1548static void savefile_write(void *wctx, void *buf, int len)
1549{
1550 FILE *fp = (FILE *)wctx;
1551 fwrite(buf, 1, len, fp);
1552}
1553
1554static int savefile_read(void *wctx, void *buf, int len)
1555{
1556 FILE *fp = (FILE *)wctx;
1557 int ret;
1558
1559 ret = fread(buf, 1, len, fp);
1560 return (ret == len);
1561}
1562
1563/*
1564 * Create an appropriate midend structure to go in a puzzle window,
1565 * given a game type and/or a command-line argument.
1566 *
1567 * 'arg' can be either a game ID string (descriptive, random, or a
1568 * plain set of parameters) or the filename of a save file. The two
1569 * boolean flag arguments indicate which possibilities are
1570 * permissible.
1571 */
1572static midend *midend_for_new_game(frontend *fe, const game *cgame,
1573 char *arg, int maybe_game_id,
1574 int maybe_save_file, char **error)
1575{
1576 midend *me = NULL;
1577
1578 if (!arg) {
1579 if (me) midend_free(me);
1580 me = midend_new(fe, cgame, &win_drawing, fe);
1581 midend_new_game(me);
1582 } else {
1583 FILE *fp;
1584 char *err_param, *err_load;
1585
1586 /*
1587 * See if arg is a valid filename of a save game file.
1588 */
1589 err_load = NULL;
1590 if (maybe_save_file && (fp = fopen(arg, "r")) != NULL) {
1591 const game *loadgame;
1592
1593#ifdef COMBINED
1594 /*
1595 * Find out what kind of game is stored in the save
1596 * file; if we're going to end up loading that, it
1597 * will have to override our caller's judgment as to
1598 * what game to initialise our midend with.
1599 */
1600 char *id_name;
1601 err_load = identify_game(&id_name, savefile_read, fp);
1602 if (!err_load) {
1603 int i;
1604 for (i = 0; i < gamecount; i++)
1605 if (!strcmp(id_name, gamelist[i]->name))
1606 break;
1607 if (i == gamecount) {
1608 err_load = "Save file is for a game not supported by"
1609 " this program";
1610 } else {
1611 loadgame = gamelist[i];
1612 rewind(fp); /* go back to the start for actual load */
1613 }
1614 }
1615#else
1616 loadgame = cgame;
1617#endif
1618 if (!err_load) {
1619 if (me) midend_free(me);
1620 me = midend_new(fe, loadgame, &win_drawing, fe);
1621 err_load = midend_deserialise(me, savefile_read, fp);
1622 }
1623 } else {
1624 err_load = "Unable to open file";
1625 }
1626
1627 if (maybe_game_id && (!maybe_save_file || err_load)) {
1628 /*
1629 * See if arg is a game description.
1630 */
1631 if (me) midend_free(me);
1632 me = midend_new(fe, cgame, &win_drawing, fe);
1633 err_param = midend_game_id(me, arg);
1634 if (!err_param) {
1635 midend_new_game(me);
1636 } else {
1637 if (maybe_save_file) {
1638 *error = snewn(256 + strlen(arg) + strlen(err_param) +
1639 strlen(err_load), char);
1640 sprintf(*error, "Supplied argument \"%s\" is neither a"
1641 " game ID (%s) nor a save file (%s)",
1642 arg, err_param, err_load);
1643 } else {
1644 *error = dupstr(err_param);
1645 }
1646 midend_free(me);
1647 sfree(fe);
1648 return NULL;
1649 }
1650 } else if (err_load) {
1651 *error = dupstr(err_load);
1652 midend_free(me);
1653 sfree(fe);
1654 return NULL;
1655 }
1656 }
1657
1658 return me;
1659}
1660
1661/*
1662 * Populate a frontend structure with a new midend structure, and
1663 * create any window furniture that it needs.
1664 *
1665 * Previously-allocated memory and window furniture will be freed by
1666 * this function.
1667 *
1668 */
1669static int fe_set_midend(frontend *fe, midend *me)
1670{
1671 int x, y;
1672 RECT r;
1673
1674 if (fe->me) midend_free(fe->me);
1675 fe->me = me;
1676 fe->game = midend_which_game(fe->me);
1677
1678 {
1679 int i, ncolours;
1680 float *colours;
1681
1682 colours = midend_colours(fe->me, &ncolours);
1683
1684 if (fe->colours) sfree(fe->colours);
1685 if (fe->brushes) sfree(fe->brushes);
1686 if (fe->pens) sfree(fe->pens);
1687
1688 fe->colours = snewn(ncolours, COLORREF);
1689 fe->brushes = snewn(ncolours, HBRUSH);
1690 fe->pens = snewn(ncolours, HPEN);
1691
1692 for (i = 0; i < ncolours; i++) {
1693 fe->colours[i] = RGB(255 * colours[i*3+0],
1694 255 * colours[i*3+1],
1695 255 * colours[i*3+2]);
1696 fe->brushes[i] = CreateSolidBrush(fe->colours[i]);
1697 fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]);
1698 }
1699 sfree(colours);
1700 }
1701
1702 if (fe->statusbar)
1703 DestroyWindow(fe->statusbar);
1704 if (midend_wants_statusbar(fe->me)) {
1705 fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME,
1706 TEXT(DEFAULT_STATUSBAR_TEXT),
1707 WS_CHILD | WS_VISIBLE,
1708 0, 0, 0, 0, /* status bar does these */
1709 NULL, NULL, fe->inst, NULL);
1710 } else
1711 fe->statusbar = NULL;
1712
1713 get_max_puzzle_size(fe, &x, &y);
1714 midend_size(fe->me, &x, &y, FALSE);
1715
1716 r.left = r.top = 0;
1717 r.right = x;
1718 r.bottom = y;
1719 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1720
1721#ifdef _WIN32_WCE
1722 if (fe->numpad)
1723 DestroyWindow(fe->numpad);
1724 if (fe->game->flags & REQUIRE_NUMPAD)
1725 {
1726 fe->numpad = CreateToolbarEx (fe->hwnd,
1727 WS_VISIBLE | WS_CHILD | CCS_NOPARENTALIGN | TBSTYLE_FLAT,
1728 0, 10, fe->inst, IDR_PADTOOLBAR,
1729 tbNumpadButtons, sizeof (tbNumpadButtons) / sizeof (TBBUTTON),
1730 0, 0, 14, 15, sizeof (TBBUTTON));
1731 GetWindowRect(fe->numpad, &rcTB);
1732 GetClientRect(fe->hwnd, &rcClient);
1733 MoveWindow(fe->numpad,
1734 0,
1735 rcClient.bottom - (rcTB.bottom - rcTB.top) - 1,
1736 rcClient.right,
1737 rcTB.bottom - rcTB.top,
1738 FALSE);
1739 SendMessage(fe->numpad, TB_SETINDENT, (rcClient.right - (10 * 21)) / 2, 0);
1740 }
1741 else {
1742 fe->numpad = NULL;
1743 }
1744 MultiByteToWideChar (CP_ACP, 0, fe->game->name, -1, wGameName, 256);
1745 SetWindowText(fe->hwnd, wGameName);
1746#else
1747 SetWindowText(fe->hwnd, fe->game->name);
1748#endif
1749
1750 if (fe->statusbar)
1751 DestroyWindow(fe->statusbar);
1752 if (midend_wants_statusbar(fe->me)) {
1753 RECT sr;
1754 fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"),
1755 WS_CHILD | WS_VISIBLE,
1756 0, 0, 0, 0, /* status bar does these */
1757 fe->hwnd, NULL, fe->inst, NULL);
1758#ifdef _WIN32_WCE
1759 /* Flat status bar looks better on the Pocket PC */
1760 SendMessage(fe->statusbar, SB_SIMPLE, (WPARAM) TRUE, 0);
1761 SendMessage(fe->statusbar, SB_SETTEXT,
1762 (WPARAM) 255 | SBT_NOBORDERS,
1763 (LPARAM) L"");
1764#endif
1765
1766 /*
1767 * Now resize the window to take account of the status bar.
1768 */
1769 GetWindowRect(fe->statusbar, &sr);
1770 GetWindowRect(fe->hwnd, &r);
1771#ifndef _WIN32_WCE
1772 SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left,
1773 r.bottom - r.top + sr.bottom - sr.top,
1774 SWP_NOMOVE | SWP_NOZORDER);
1775#endif
1776 } else {
1777 fe->statusbar = NULL;
1778 }
1779
1780 {
1781 HMENU oldmenu = GetMenu(fe->hwnd);
1782
1783#ifndef _WIN32_WCE
1784 HMENU bar = CreateMenu();
1785 HMENU menu = CreateMenu();
1786 RECT menusize;
1787
1788 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "&Game");
1789#else
1790 HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME);
1791 DeleteMenu(menu, 0, MF_BYPOSITION);
1792#endif
1793 fe->gamemenu = menu;
1794 AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("&New"));
1795 AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("&Restart"));
1796#ifndef _WIN32_WCE
1797 /* ...here I run out of sensible accelerator characters. */
1798 AppendMenu(menu, MF_ENABLED, IDM_DESC, TEXT("Speci&fic..."));
1799 AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Rando&m Seed..."));
1800#endif
1801
1802 if (fe->presets)
1803 sfree(fe->presets);
1804 if ((fe->npresets = midend_num_presets(fe->me)) > 0 ||
1805 fe->game->can_configure) {
1806 int i;
1807#ifndef _WIN32_WCE
1808 HMENU sub = CreateMenu();
1809
1810 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "&Type");
1811#else
1812 HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE);
1813 DeleteMenu(sub, 0, MF_BYPOSITION);
1814#endif
1815 fe->presets = snewn(fe->npresets, game_params *);
1816
1817 for (i = 0; i < fe->npresets; i++) {
1818 char *name;
1819#ifdef _WIN32_WCE
1820 TCHAR wName[255];
1821#endif
1822
1823 midend_fetch_preset(fe->me, i, &name, &fe->presets[i]);
1824
1825 /*
1826 * FIXME: we ought to go through and do something
1827 * with ampersands here.
1828 */
1829
1830#ifndef _WIN32_WCE
1831 AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name);
1832#else
1833 MultiByteToWideChar (CP_ACP, 0, name, -1, wName, 255);
1834 AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, wName);
1835#endif
1836 }
1837 if (fe->game->can_configure) {
1838 AppendMenu(sub, MF_ENABLED, IDM_CONFIG, TEXT("&Custom..."));
1839 }
1840
1841 fe->typemenu = sub;
1842 } else {
1843 fe->typemenu = INVALID_HANDLE_VALUE;
1844 fe->presets = NULL;
1845 }
1846
1847#ifdef COMBINED
1848#ifdef _WIN32_WCE
1849#error Windows CE does not support COMBINED build.
1850#endif
1851 {
1852 HMENU games = CreateMenu();
1853 int i;
1854
1855 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1856 AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)games, "&Other");
1857 for (i = 0; i < gamecount; i++) {
1858 if (strcmp(gamelist[i]->name, fe->game->name) != 0) {
1859 /* only include those games that aren't the same as the
1860 * game we're currently playing. */
1861 AppendMenu(games, MF_ENABLED, IDM_GAMES + i, gamelist[i]->name);
1862 }
1863 }
1864 }
1865#endif
1866
1867 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1868#ifndef _WIN32_WCE
1869 AppendMenu(menu, MF_ENABLED, IDM_LOAD, TEXT("&Load..."));
1870 AppendMenu(menu, MF_ENABLED, IDM_SAVE, TEXT("&Save..."));
1871 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1872 if (fe->game->can_print) {
1873 AppendMenu(menu, MF_ENABLED, IDM_PRINT, TEXT("&Print..."));
1874 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1875 }
1876#endif
1877 AppendMenu(menu, MF_ENABLED, IDM_UNDO, TEXT("Undo"));
1878 AppendMenu(menu, MF_ENABLED, IDM_REDO, TEXT("Redo"));
1879#ifndef _WIN32_WCE
1880 if (fe->game->can_format_as_text_ever) {
1881 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1882 AppendMenu(menu, MF_ENABLED, IDM_COPY, TEXT("&Copy"));
1883 }
1884#endif
1885 if (fe->game->can_solve) {
1886 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1887 AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Sol&ve"));
1888 }
1889 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1890#ifndef _WIN32_WCE
1891 AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("E&xit"));
1892 menu = CreateMenu();
1893 AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, TEXT("&Help"));
1894#endif
1895 AppendMenu(menu, MF_ENABLED, IDM_ABOUT, TEXT("&About"));
1896#ifndef _WIN32_WCE
1897 if (help_type != NONE) {
1898 char *item;
1899 AppendMenu(menu, MF_SEPARATOR, 0, 0);
1900 AppendMenu(menu, MF_ENABLED, IDM_HELPC, TEXT("&Contents"));
1901 assert(fe->game->name);
1902 item = snewn(10+strlen(fe->game->name), char); /*ick*/
1903 sprintf(item, "&Help on %s", fe->game->name);
1904 AppendMenu(menu, MF_ENABLED, IDM_GAMEHELP, item);
1905 sfree(item);
1906 }
1907 DestroyMenu(oldmenu);
1908 SetMenu(fe->hwnd, bar);
1909 get_menu_size(fe->hwnd, &menusize);
1910 fe->xmin = (menusize.right - menusize.left) + 25;
1911#endif
1912 }
1913
1914 if (fe->bitmap) DeleteObject(fe->bitmap);
1915 fe->bitmap = NULL;
1916 new_game_size(fe, fe->puzz_scale); /* initialises fe->bitmap */
1917
1918 return 0;
1919}
1920
1921static void show_window(frontend *fe)
1922{
1923 ShowWindow(fe->hwnd, SW_SHOWNORMAL);
1924 SetForegroundWindow(fe->hwnd);
1925
1926 update_type_menu_tick(fe);
1927 update_copy_menu_greying(fe);
1928
1929 midend_redraw(fe->me);
1930}
1931
1932#ifdef _WIN32_WCE
1933static HFONT dialog_title_font()
1934{
1935 static HFONT hf = NULL;
1936 LOGFONT lf;
1937
1938 if (hf)
1939 return hf;
1940
1941 memset (&lf, 0, sizeof(LOGFONT));
1942 lf.lfHeight = -11; /* - ((8 * GetDeviceCaps(hdc, LOGPIXELSY)) / 72) */
1943 lf.lfWeight = FW_BOLD;
1944 wcscpy(lf.lfFaceName, TEXT("Tahoma"));
1945
1946 return hf = CreateFontIndirect(&lf);
1947}
1948
1949static void make_dialog_full_screen(HWND hwnd)
1950{
1951 SHINITDLGINFO shidi;
1952
1953 /* Make dialog full screen */
1954 shidi.dwMask = SHIDIM_FLAGS;
1955 shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN |
1956 SHIDIF_EMPTYMENU;
1957 shidi.hDlg = hwnd;
1958 SHInitDialog(&shidi);
1959}
1960#endif
1961
1962static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg,
1963 WPARAM wParam, LPARAM lParam)
1964{
1965 frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
1966
1967 switch (msg) {
1968 case WM_INITDIALOG:
1969#ifdef _WIN32_WCE
1970 {
1971 char title[256];
1972
1973 make_dialog_full_screen(hwnd);
1974
1975 sprintf(title, "About %.250s", fe->game->name);
1976 SetDlgItemTextA(hwnd, IDC_ABOUT_CAPTION, title);
1977
1978 SendDlgItemMessage(hwnd, IDC_ABOUT_CAPTION, WM_SETFONT,
1979 (WPARAM) dialog_title_font(), 0);
1980
1981 SetDlgItemTextA(hwnd, IDC_ABOUT_GAME, fe->game->name);
1982 SetDlgItemTextA(hwnd, IDC_ABOUT_VERSION, ver);
1983 }
1984#endif
1985 return TRUE;
1986
1987 case WM_COMMAND:
1988 if (LOWORD(wParam) == IDOK)
1989#ifdef _WIN32_WCE
1990 EndDialog(hwnd, 1);
1991#else
1992 fe->dlg_done = 1;
1993#endif
1994 return 0;
1995
1996 case WM_CLOSE:
1997#ifdef _WIN32_WCE
1998 EndDialog(hwnd, 1);
1999#else
2000 fe->dlg_done = 1;
2001#endif
2002 return 0;
2003 }
2004
2005 return 0;
2006}
2007
2008/*
2009 * Wrappers on midend_{get,set}_config, which extend the CFG_*
2010 * enumeration to add CFG_PRINT.
2011 */
2012static config_item *frontend_get_config(frontend *fe, int which,
2013 char **wintitle)
2014{
2015 if (which < CFG_FRONTEND_SPECIFIC) {
2016 return midend_get_config(fe->me, which, wintitle);
2017 } else if (which == CFG_PRINT) {
2018 config_item *ret;
2019 int i;
2020
2021 *wintitle = snewn(40 + strlen(fe->game->name), char);
2022 sprintf(*wintitle, "%s print setup", fe->game->name);
2023
2024 ret = snewn(8, config_item);
2025
2026 i = 0;
2027
2028 ret[i].name = "Number of puzzles to print";
2029 ret[i].type = C_STRING;
2030 ret[i].sval = dupstr("1");
2031 ret[i].ival = 0;
2032 i++;
2033
2034 ret[i].name = "Number of puzzles across the page";
2035 ret[i].type = C_STRING;
2036 ret[i].sval = dupstr("1");
2037 ret[i].ival = 0;
2038 i++;
2039
2040 ret[i].name = "Number of puzzles down the page";
2041 ret[i].type = C_STRING;
2042 ret[i].sval = dupstr("1");
2043 ret[i].ival = 0;
2044 i++;
2045
2046 ret[i].name = "Percentage of standard size";
2047 ret[i].type = C_STRING;
2048 ret[i].sval = dupstr("100.0");
2049 ret[i].ival = 0;
2050 i++;
2051
2052 ret[i].name = "Include currently shown puzzle";
2053 ret[i].type = C_BOOLEAN;
2054 ret[i].sval = NULL;
2055 ret[i].ival = TRUE;
2056 i++;
2057
2058 ret[i].name = "Print solutions";
2059 ret[i].type = C_BOOLEAN;
2060 ret[i].sval = NULL;
2061 ret[i].ival = FALSE;
2062 i++;
2063
2064 if (fe->game->can_print_in_colour) {
2065 ret[i].name = "Print in colour";
2066 ret[i].type = C_BOOLEAN;
2067 ret[i].sval = NULL;
2068 ret[i].ival = FALSE;
2069 i++;
2070 }
2071
2072 ret[i].name = NULL;
2073 ret[i].type = C_END;
2074 ret[i].sval = NULL;
2075 ret[i].ival = 0;
2076 i++;
2077
2078 return ret;
2079 } else {
2080 assert(!"We should never get here");
2081 return NULL;
2082 }
2083}
2084
2085static char *frontend_set_config(frontend *fe, int which, config_item *cfg)
2086{
2087 if (which < CFG_FRONTEND_SPECIFIC) {
2088 return midend_set_config(fe->me, which, cfg);
2089 } else if (which == CFG_PRINT) {
2090 if ((fe->printcount = atoi(cfg[0].sval)) <= 0)
2091 return "Number of puzzles to print should be at least one";
2092 if ((fe->printw = atoi(cfg[1].sval)) <= 0)
2093 return "Number of puzzles across the page should be at least one";
2094 if ((fe->printh = atoi(cfg[2].sval)) <= 0)
2095 return "Number of puzzles down the page should be at least one";
2096 if ((fe->printscale = (float)atof(cfg[3].sval)) <= 0)
2097 return "Print size should be positive";
2098 fe->printcurr = cfg[4].ival;
2099 fe->printsolns = cfg[5].ival;
2100 fe->printcolour = fe->game->can_print_in_colour && cfg[6].ival;
2101 return NULL;
2102 } else {
2103 assert(!"We should never get here");
2104 return "Internal error";
2105 }
2106}
2107
2108#ifdef _WIN32_WCE
2109/* Separate version of mkctrl function for the Pocket PC. */
2110/* Control coordinates should be specified in dialog units. */
2111HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
2112 LPCTSTR wclass, int wstyle,
2113 int exstyle, const char *wtext, int wid)
2114{
2115 RECT rc;
2116 TCHAR wwtext[256];
2117
2118 /* Convert dialog units into pixels */
2119 rc.left = x1; rc.right = x2;
2120 rc.top = y1; rc.bottom = y2;
2121 MapDialogRect(fe->cfgbox, &rc);
2122
2123 MultiByteToWideChar (CP_ACP, 0, wtext, -1, wwtext, 256);
2124
2125 return CreateWindowEx(exstyle, wclass, wwtext,
2126 wstyle | WS_CHILD | WS_VISIBLE,
2127 rc.left, rc.top,
2128 rc.right - rc.left, rc.bottom - rc.top,
2129 fe->cfgbox, (HMENU) wid, fe->inst, NULL);
2130}
2131
2132static void create_config_controls(frontend * fe)
2133{
2134 int id, nctrls;
2135 int col1l, col1r, col2l, col2r, y;
2136 config_item *i;
2137 struct cfg_aux *j;
2138 HWND ctl;
2139
2140 /* Control placement done in dialog units */
2141 col1l = 4; col1r = 96; /* Label column */
2142 col2l = 100; col2r = 154; /* Input column (edit boxes and combo boxes) */
2143
2144 /*
2145 * Count the controls so we can allocate cfgaux.
2146 */
2147 for (nctrls = 0, i = fe->cfg; i->type != C_END; i++)
2148 nctrls++;
2149 fe->cfgaux = snewn(nctrls, struct cfg_aux);
2150
2151 id = 1000;
2152 y = 22; /* Leave some room for the dialog title */
2153 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2154 switch (i->type) {
2155 case C_STRING:
2156 /*
2157 * Edit box with a label beside it.
2158 */
2159 mkctrl(fe, col1l, col1r, y + 1, y + 11,
2160 TEXT("Static"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
2161 mkctrl(fe, col2l, col2r, y, y + 12,
2162 TEXT("EDIT"), WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,
2163 0, "", (j->ctlid = id++));
2164 SetDlgItemTextA(fe->cfgbox, j->ctlid, i->sval);
2165 break;
2166
2167 case C_BOOLEAN:
2168 /*
2169 * Simple checkbox.
2170 */
2171 mkctrl(fe, col1l, col2r, y + 1, y + 11, TEXT("BUTTON"),
2172 BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
2173 0, i->name, (j->ctlid = id++));
2174 CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0));
2175 break;
2176
2177 case C_CHOICES:
2178 /*
2179 * Drop-down list with a label beside it.
2180 */
2181 mkctrl(fe, col1l, col1r, y + 1, y + 11,
2182 TEXT("STATIC"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
2183 ctl = mkctrl(fe, col2l, col2r, y, y + 48,
2184 TEXT("COMBOBOX"), WS_BORDER | WS_TABSTOP |
2185 CBS_DROPDOWNLIST | CBS_HASSTRINGS,
2186 0, "", (j->ctlid = id++));
2187 {
2188 char c, *p, *q, *str;
2189
2190 p = i->sval;
2191 c = *p++;
2192 while (*p) {
2193 q = p;
2194 while (*q && *q != c) q++;
2195 str = snewn(q-p+1, char);
2196 strncpy(str, p, q-p);
2197 str[q-p] = '\0';
2198 {
2199 TCHAR ws[50];
2200 MultiByteToWideChar (CP_ACP, 0, str, -1, ws, 50);
2201 SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)ws);
2202 }
2203
2204 sfree(str);
2205 if (*q) q++;
2206 p = q;
2207 }
2208 }
2209 SendMessage(ctl, CB_SETCURSEL, i->ival, 0);
2210 break;
2211 }
2212
2213 y += 15;
2214 }
2215
2216}
2217#endif
2218
2219static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg,
2220 WPARAM wParam, LPARAM lParam)
2221{
2222 frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
2223 config_item *i;
2224 struct cfg_aux *j;
2225
2226 switch (msg) {
2227 case WM_INITDIALOG:
2228#ifdef _WIN32_WCE
2229 {
2230 char *title;
2231
2232 fe = (frontend *) lParam;
2233 SetWindowLong(hwnd, GWL_USERDATA, lParam);
2234 fe->cfgbox = hwnd;
2235
2236 fe->cfg = frontend_get_config(fe, fe->cfg_which, &title);
2237
2238 make_dialog_full_screen(hwnd);
2239
2240 SetDlgItemTextA(hwnd, IDC_CONFIG_CAPTION, title);
2241 SendDlgItemMessage(hwnd, IDC_CONFIG_CAPTION, WM_SETFONT,
2242 (WPARAM) dialog_title_font(), 0);
2243
2244 create_config_controls(fe);
2245 }
2246#endif
2247 return TRUE;
2248
2249 case WM_COMMAND:
2250 /*
2251 * OK and Cancel are special cases.
2252 */
2253 if ((LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
2254 if (LOWORD(wParam) == IDOK) {
2255 char *err = frontend_set_config(fe, fe->cfg_which, fe->cfg);
2256
2257 if (err) {
2258 MessageBox(hwnd, err, "Validation error",
2259 MB_ICONERROR | MB_OK);
2260 } else {
2261#ifdef _WIN32_WCE
2262 EndDialog(hwnd, 2);
2263#else
2264 fe->dlg_done = 2;
2265#endif
2266 }
2267 } else {
2268#ifdef _WIN32_WCE
2269 EndDialog(hwnd, 1);
2270#else
2271 fe->dlg_done = 1;
2272#endif
2273 }
2274 return 0;
2275 }
2276
2277 /*
2278 * First find the control whose id this is.
2279 */
2280 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2281 if (j->ctlid == LOWORD(wParam))
2282 break;
2283 }
2284 if (i->type == C_END)
2285 return 0; /* not our problem */
2286
2287 if (i->type == C_STRING && HIWORD(wParam) == EN_CHANGE) {
2288 char buffer[4096];
2289#ifdef _WIN32_WCE
2290 TCHAR wBuffer[4096];
2291 GetDlgItemText(fe->cfgbox, j->ctlid, wBuffer, 4096);
2292 WideCharToMultiByte(CP_ACP, 0, wBuffer, -1, buffer, 4096, NULL, NULL);
2293#else
2294 GetDlgItemText(fe->cfgbox, j->ctlid, buffer, lenof(buffer));
2295#endif
2296 buffer[lenof(buffer)-1] = '\0';
2297 sfree(i->sval);
2298 i->sval = dupstr(buffer);
2299 } else if (i->type == C_BOOLEAN &&
2300 (HIWORD(wParam) == BN_CLICKED ||
2301 HIWORD(wParam) == BN_DBLCLK)) {
2302 i->ival = IsDlgButtonChecked(fe->cfgbox, j->ctlid);
2303 } else if (i->type == C_CHOICES &&
2304 HIWORD(wParam) == CBN_SELCHANGE) {
2305 i->ival = SendDlgItemMessage(fe->cfgbox, j->ctlid,
2306 CB_GETCURSEL, 0, 0);
2307 }
2308
2309 return 0;
2310
2311 case WM_CLOSE:
2312 fe->dlg_done = 1;
2313 return 0;
2314 }
2315
2316 return 0;
2317}
2318
2319#ifndef _WIN32_WCE
2320HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
2321 char *wclass, int wstyle,
2322 int exstyle, const char *wtext, int wid)
2323{
2324 HWND ret;
2325 ret = CreateWindowEx(exstyle, wclass, wtext,
2326 wstyle | WS_CHILD | WS_VISIBLE, x1, y1, x2-x1, y2-y1,
2327 fe->cfgbox, (HMENU) wid, fe->inst, NULL);
2328 SendMessage(ret, WM_SETFONT, (WPARAM)fe->cfgfont, MAKELPARAM(TRUE, 0));
2329 return ret;
2330}
2331#endif
2332
2333static void about(frontend *fe)
2334{
2335#ifdef _WIN32_WCE
2336 DialogBox(fe->inst, MAKEINTRESOURCE(IDD_ABOUT), fe->hwnd, AboutDlgProc);
2337#else
2338 int i;
2339 WNDCLASS wc;
2340 MSG msg;
2341 TEXTMETRIC tm;
2342 HDC hdc;
2343 HFONT oldfont;
2344 SIZE size;
2345 int gm, id;
2346 int winwidth, winheight, y;
2347 int height, width, maxwid;
2348 const char *strings[16];
2349 int lengths[16];
2350 int nstrings = 0;
2351 char titlebuf[512];
2352
2353 sprintf(titlebuf, "About %.250s", fe->game->name);
2354
2355 strings[nstrings++] = fe->game->name;
2356 strings[nstrings++] = "from Simon Tatham's Portable Puzzle Collection";
2357 strings[nstrings++] = ver;
2358
2359 wc.style = CS_DBLCLKS | CS_SAVEBITS;
2360 wc.lpfnWndProc = DefDlgProc;
2361 wc.cbClsExtra = 0;
2362 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
2363 wc.hInstance = fe->inst;
2364 wc.hIcon = NULL;
2365 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2366 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
2367 wc.lpszMenuName = NULL;
2368 wc.lpszClassName = "GameAboutBox";
2369 RegisterClass(&wc);
2370
2371 hdc = GetDC(fe->hwnd);
2372 SetMapMode(hdc, MM_TEXT);
2373
2374 fe->dlg_done = FALSE;
2375
2376 fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2377 0, 0, 0, 0,
2378 FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2379 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2380 DEFAULT_QUALITY,
2381 FF_SWISS,
2382 "MS Shell Dlg");
2383
2384 oldfont = SelectObject(hdc, fe->cfgfont);
2385 if (GetTextMetrics(hdc, &tm)) {
2386 height = tm.tmAscent + tm.tmDescent;
2387 width = tm.tmAveCharWidth;
2388 } else {
2389 height = width = 30;
2390 }
2391
2392 /*
2393 * Figure out the layout of the About box by measuring the
2394 * length of each piece of text.
2395 */
2396 maxwid = 0;
2397 winheight = height/2;
2398
2399 for (i = 0; i < nstrings; i++) {
2400 if (GetTextExtentPoint32(hdc, strings[i], strlen(strings[i]), &size))
2401 lengths[i] = size.cx;
2402 else
2403 lengths[i] = 0; /* *shrug* */
2404 if (maxwid < lengths[i])
2405 maxwid = lengths[i];
2406 winheight += height * 3 / 2 + (height / 2);
2407 }
2408
2409 winheight += height + height * 7 / 4; /* OK button */
2410 winwidth = maxwid + 4*width;
2411
2412 SelectObject(hdc, oldfont);
2413 ReleaseDC(fe->hwnd, hdc);
2414
2415 /*
2416 * Create the dialog, now that we know its size.
2417 */
2418 {
2419 RECT r, r2;
2420
2421 r.left = r.top = 0;
2422 r.right = winwidth;
2423 r.bottom = winheight;
2424
2425 AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*|
2426 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2427 WS_CAPTION | WS_SYSMENU*/) &~
2428 (WS_MAXIMIZEBOX | WS_OVERLAPPED),
2429 FALSE, 0);
2430
2431 /*
2432 * Centre the dialog on its parent window.
2433 */
2434 r.right -= r.left;
2435 r.bottom -= r.top;
2436 GetWindowRect(fe->hwnd, &r2);
2437 r.left = (r2.left + r2.right - r.right) / 2;
2438 r.top = (r2.top + r2.bottom - r.bottom) / 2;
2439 r.right += r.left;
2440 r.bottom += r.top;
2441
2442 fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, titlebuf,
2443 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2444 WS_CAPTION | WS_SYSMENU,
2445 r.left, r.top,
2446 r.right-r.left, r.bottom-r.top,
2447 fe->hwnd, NULL, fe->inst, NULL);
2448 }
2449
2450 SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE);
2451
2452 SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe);
2453 SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)AboutDlgProc);
2454
2455 id = 1000;
2456 y = height/2;
2457 for (i = 0; i < nstrings; i++) {
2458 int border = width*2 + (maxwid - lengths[i]) / 2;
2459 mkctrl(fe, border, border+lengths[i], y+height*1/8, y+height*9/8,
2460 "Static", 0, 0, strings[i], id++);
2461 y += height*3/2;
2462
2463 assert(y < winheight);
2464 y += height/2;
2465 }
2466
2467 y += height/2; /* extra space before OK */
2468 mkctrl(fe, width*2, maxwid+width*2, y, y+height*7/4, "BUTTON",
2469 BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
2470 "OK", IDOK);
2471
2472 SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0);
2473
2474 EnableWindow(fe->hwnd, FALSE);
2475 ShowWindow(fe->cfgbox, SW_SHOWNORMAL);
2476 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
2477 if (!IsDialogMessage(fe->cfgbox, &msg))
2478 DispatchMessage(&msg);
2479 if (fe->dlg_done)
2480 break;
2481 }
2482 EnableWindow(fe->hwnd, TRUE);
2483 SetForegroundWindow(fe->hwnd);
2484 DestroyWindow(fe->cfgbox);
2485 DeleteObject(fe->cfgfont);
2486#endif
2487}
2488
2489static int get_config(frontend *fe, int which)
2490{
2491#ifdef _WIN32_WCE
2492 fe->cfg_which = which;
2493
2494 return DialogBoxParam(fe->inst,
2495 MAKEINTRESOURCE(IDD_CONFIG),
2496 fe->hwnd, ConfigDlgProc,
2497 (LPARAM) fe) == 2;
2498#else
2499 config_item *i;
2500 struct cfg_aux *j;
2501 char *title;
2502 WNDCLASS wc;
2503 MSG msg;
2504 TEXTMETRIC tm;
2505 HDC hdc;
2506 HFONT oldfont;
2507 SIZE size;
2508 HWND ctl;
2509 int gm, id, nctrls;
2510 int winwidth, winheight, col1l, col1r, col2l, col2r, y;
2511 int height, width, maxlabel, maxcheckbox;
2512
2513 wc.style = CS_DBLCLKS | CS_SAVEBITS;
2514 wc.lpfnWndProc = DefDlgProc;
2515 wc.cbClsExtra = 0;
2516 wc.cbWndExtra = DLGWINDOWEXTRA + 8;
2517 wc.hInstance = fe->inst;
2518 wc.hIcon = NULL;
2519 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2520 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
2521 wc.lpszMenuName = NULL;
2522 wc.lpszClassName = "GameConfigBox";
2523 RegisterClass(&wc);
2524
2525 hdc = GetDC(fe->hwnd);
2526 SetMapMode(hdc, MM_TEXT);
2527
2528 fe->dlg_done = FALSE;
2529
2530 fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2531 0, 0, 0, 0,
2532 FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2533 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2534 DEFAULT_QUALITY,
2535 FF_SWISS,
2536 "MS Shell Dlg");
2537
2538 oldfont = SelectObject(hdc, fe->cfgfont);
2539 if (GetTextMetrics(hdc, &tm)) {
2540 height = tm.tmAscent + tm.tmDescent;
2541 width = tm.tmAveCharWidth;
2542 } else {
2543 height = width = 30;
2544 }
2545
2546 fe->cfg = frontend_get_config(fe, which, &title);
2547 fe->cfg_which = which;
2548
2549 /*
2550 * Figure out the layout of the config box by measuring the
2551 * length of each piece of text.
2552 */
2553 maxlabel = maxcheckbox = 0;
2554 winheight = height/2;
2555
2556 for (i = fe->cfg; i->type != C_END; i++) {
2557 switch (i->type) {
2558 case C_STRING:
2559 case C_CHOICES:
2560 /*
2561 * Both these control types have a label filling only
2562 * the left-hand column of the box.
2563 */
2564 if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) &&
2565 maxlabel < size.cx)
2566 maxlabel = size.cx;
2567 winheight += height * 3 / 2 + (height / 2);
2568 break;
2569
2570 case C_BOOLEAN:
2571 /*
2572 * Checkboxes take up the whole of the box width.
2573 */
2574 if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) &&
2575 maxcheckbox < size.cx)
2576 maxcheckbox = size.cx;
2577 winheight += height + (height / 2);
2578 break;
2579 }
2580 }
2581
2582 winheight += height + height * 7 / 4; /* OK / Cancel buttons */
2583
2584 col1l = 2*width;
2585 col1r = col1l + maxlabel;
2586 col2l = col1r + 2*width;
2587 col2r = col2l + 30*width;
2588 if (col2r < col1l+2*height+maxcheckbox)
2589 col2r = col1l+2*height+maxcheckbox;
2590 winwidth = col2r + 2*width;
2591
2592 SelectObject(hdc, oldfont);
2593 ReleaseDC(fe->hwnd, hdc);
2594
2595 /*
2596 * Create the dialog, now that we know its size.
2597 */
2598 {
2599 RECT r, r2;
2600
2601 r.left = r.top = 0;
2602 r.right = winwidth;
2603 r.bottom = winheight;
2604
2605 AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*|
2606 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2607 WS_CAPTION | WS_SYSMENU*/) &~
2608 (WS_MAXIMIZEBOX | WS_OVERLAPPED),
2609 FALSE, 0);
2610
2611 /*
2612 * Centre the dialog on its parent window.
2613 */
2614 r.right -= r.left;
2615 r.bottom -= r.top;
2616 GetWindowRect(fe->hwnd, &r2);
2617 r.left = (r2.left + r2.right - r.right) / 2;
2618 r.top = (r2.top + r2.bottom - r.bottom) / 2;
2619 r.right += r.left;
2620 r.bottom += r.top;
2621
2622 fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, title,
2623 DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
2624 WS_CAPTION | WS_SYSMENU,
2625 r.left, r.top,
2626 r.right-r.left, r.bottom-r.top,
2627 fe->hwnd, NULL, fe->inst, NULL);
2628 sfree(title);
2629 }
2630
2631 SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE);
2632
2633 SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe);
2634 SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)ConfigDlgProc);
2635
2636 /*
2637 * Count the controls so we can allocate cfgaux.
2638 */
2639 for (nctrls = 0, i = fe->cfg; i->type != C_END; i++)
2640 nctrls++;
2641 fe->cfgaux = snewn(nctrls, struct cfg_aux);
2642
2643 id = 1000;
2644 y = height/2;
2645 for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
2646 switch (i->type) {
2647 case C_STRING:
2648 /*
2649 * Edit box with a label beside it.
2650 */
2651 mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8,
2652 "Static", 0, 0, i->name, id++);
2653 ctl = mkctrl(fe, col2l, col2r, y, y+height*3/2,
2654 "EDIT", WS_TABSTOP | ES_AUTOHSCROLL,
2655 WS_EX_CLIENTEDGE, "", (j->ctlid = id++));
2656 SetWindowText(ctl, i->sval);
2657 y += height*3/2;
2658 break;
2659
2660 case C_BOOLEAN:
2661 /*
2662 * Simple checkbox.
2663 */
2664 mkctrl(fe, col1l, col2r, y, y+height, "BUTTON",
2665 BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
2666 0, i->name, (j->ctlid = id++));
2667 CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0));
2668 y += height;
2669 break;
2670
2671 case C_CHOICES:
2672 /*
2673 * Drop-down list with a label beside it.
2674 */
2675 mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8,
2676 "STATIC", 0, 0, i->name, id++);
2677 ctl = mkctrl(fe, col2l, col2r, y, y+height*41/2,
2678 "COMBOBOX", WS_TABSTOP |
2679 CBS_DROPDOWNLIST | CBS_HASSTRINGS,
2680 WS_EX_CLIENTEDGE, "", (j->ctlid = id++));
2681 {
2682 char c, *p, *q, *str;
2683
2684 SendMessage(ctl, CB_RESETCONTENT, 0, 0);
2685 p = i->sval;
2686 c = *p++;
2687 while (*p) {
2688 q = p;
2689 while (*q && *q != c) q++;
2690 str = snewn(q-p+1, char);
2691 strncpy(str, p, q-p);
2692 str[q-p] = '\0';
2693 SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)str);
2694 sfree(str);
2695 if (*q) q++;
2696 p = q;
2697 }
2698 }
2699
2700 SendMessage(ctl, CB_SETCURSEL, i->ival, 0);
2701
2702 y += height*3/2;
2703 break;
2704 }
2705
2706 assert(y < winheight);
2707 y += height/2;
2708 }
2709
2710 y += height/2; /* extra space before OK and Cancel */
2711 mkctrl(fe, col1l, (col1l+col2r)/2-width, y, y+height*7/4, "BUTTON",
2712 BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
2713 "OK", IDOK);
2714 mkctrl(fe, (col1l+col2r)/2+width, col2r, y, y+height*7/4, "BUTTON",
2715 BS_PUSHBUTTON | WS_TABSTOP, 0, "Cancel", IDCANCEL);
2716
2717 SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0);
2718
2719 EnableWindow(fe->hwnd, FALSE);
2720 ShowWindow(fe->cfgbox, SW_SHOWNORMAL);
2721 while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
2722 if (!IsDialogMessage(fe->cfgbox, &msg))
2723 DispatchMessage(&msg);
2724 if (fe->dlg_done)
2725 break;
2726 }
2727 EnableWindow(fe->hwnd, TRUE);
2728 SetForegroundWindow(fe->hwnd);
2729 DestroyWindow(fe->cfgbox);
2730 DeleteObject(fe->cfgfont);
2731
2732 free_cfg(fe->cfg);
2733 sfree(fe->cfgaux);
2734
2735 return (fe->dlg_done == 2);
2736#endif
2737}
2738
2739#ifdef _WIN32_WCE
2740static void calculate_bitmap_position(frontend *fe, int x, int y)
2741{
2742 /* Pocket PC - center the game in the full screen window */
2743 int yMargin;
2744 RECT rcClient;
2745
2746 GetClientRect(fe->hwnd, &rcClient);
2747 fe->bitmapPosition.left = (rcClient.right - x) / 2;
2748 yMargin = rcClient.bottom - y;
2749
2750 if (fe->numpad != NULL) {
2751 RECT rcPad;
2752 GetWindowRect(fe->numpad, &rcPad);
2753 yMargin -= rcPad.bottom - rcPad.top;
2754 }
2755
2756 if (fe->statusbar != NULL) {
2757 RECT rcStatus;
2758 GetWindowRect(fe->statusbar, &rcStatus);
2759 yMargin -= rcStatus.bottom - rcStatus.top;
2760 }
2761
2762 fe->bitmapPosition.top = yMargin / 2;
2763
2764 fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2765 fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2766}
2767#else
2768static void calculate_bitmap_position(frontend *fe, int x, int y)
2769{
2770 /* Plain Windows - position the game in the upper-left corner */
2771 fe->bitmapPosition.left = 0;
2772 fe->bitmapPosition.top = 0;
2773 fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2774 fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2775}
2776#endif
2777
2778static void new_bitmap(frontend *fe, int x, int y)
2779{
2780 HDC hdc;
2781
2782 if (fe->bitmap) DeleteObject(fe->bitmap);
2783
2784 hdc = GetDC(fe->hwnd);
2785 fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
2786 calculate_bitmap_position(fe, x, y);
2787 ReleaseDC(fe->hwnd, hdc);
2788}
2789
2790static void new_game_size(frontend *fe, float scale)
2791{
2792 RECT r, sr;
2793 int x, y;
2794
2795 get_max_puzzle_size(fe, &x, &y);
2796 midend_size(fe->me, &x, &y, FALSE);
2797
2798 if (scale != 1.0) {
2799 x = (int)((float)x * fe->puzz_scale);
2800 y = (int)((float)y * fe->puzz_scale);
2801 midend_size(fe->me, &x, &y, TRUE);
2802 }
2803 fe->ymin = (fe->xmin * y) / x;
2804
2805 r.left = r.top = 0;
2806 r.right = x;
2807 r.bottom = y;
2808 AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
2809
2810 if (fe->statusbar != NULL) {
2811 GetWindowRect(fe->statusbar, &sr);
2812 } else {
2813 sr.left = sr.right = sr.top = sr.bottom = 0;
2814 }
2815#ifndef _WIN32_WCE
2816 SetWindowPos(fe->hwnd, NULL, 0, 0,
2817 r.right - r.left,
2818 r.bottom - r.top + sr.bottom - sr.top,
2819 SWP_NOMOVE | SWP_NOZORDER);
2820#endif
2821
2822 check_window_size(fe, &x, &y);
2823
2824#ifndef _WIN32_WCE
2825 if (fe->statusbar != NULL)
2826 SetWindowPos(fe->statusbar, NULL, 0, y, x,
2827 sr.bottom - sr.top, SWP_NOZORDER);
2828#endif
2829
2830 new_bitmap(fe, x, y);
2831
2832#ifdef _WIN32_WCE
2833 InvalidateRect(fe->hwnd, NULL, TRUE);
2834#endif
2835 midend_redraw(fe->me);
2836}
2837
2838/*
2839 * Given a proposed new window rect, work out the resulting
2840 * difference in client size (from current), and use to try
2841 * and resize the puzzle, returning (wx,wy) as the actual
2842 * new window size.
2843 */
2844
2845static void adjust_game_size(frontend *fe, RECT *proposed, int isedge,
2846 int *wx_r, int *wy_r)
2847{
2848 RECT cr, wr;
2849 int nx, ny, xdiff, ydiff, wx, wy;
2850
2851 /* Work out the current window sizing, and thus the
2852 * difference in size we're asking for. */
2853 GetClientRect(fe->hwnd, &cr);
2854 wr = cr;
2855 AdjustWindowRectEx(&wr, WINFLAGS, TRUE, 0);
2856
2857 xdiff = (proposed->right - proposed->left) - (wr.right - wr.left);
2858 ydiff = (proposed->bottom - proposed->top) - (wr.bottom - wr.top);
2859
2860 if (isedge) {
2861 /* These next four lines work around the fact that midend_size
2862 * is happy to shrink _but not grow_ if you change one dimension
2863 * but not the other. */
2864 if (xdiff > 0 && ydiff == 0)
2865 ydiff = (xdiff * (wr.right - wr.left)) / (wr.bottom - wr.top);
2866 if (xdiff == 0 && ydiff > 0)
2867 xdiff = (ydiff * (wr.bottom - wr.top)) / (wr.right - wr.left);
2868 }
2869
2870 if (check_window_resize(fe,
2871 (cr.right - cr.left) + xdiff,
2872 (cr.bottom - cr.top) + ydiff,
2873 &nx, &ny, &wx, &wy)) {
2874 new_bitmap(fe, nx, ny);
2875 midend_force_redraw(fe->me);
2876 } else {
2877 /* reset size to current window size */
2878 wx = wr.right - wr.left;
2879 wy = wr.bottom - wr.top;
2880 }
2881 /* Re-fetch rectangle; size limits mean we might not have
2882 * taken it quite to the mouse drag positions. */
2883 GetClientRect(fe->hwnd, &cr);
2884 adjust_statusbar(fe, &cr);
2885
2886 *wx_r = wx; *wy_r = wy;
2887}
2888
2889static void update_type_menu_tick(frontend *fe)
2890{
2891 int total, n, i;
2892
2893 if (fe->typemenu == INVALID_HANDLE_VALUE)
2894 return;
2895
2896 total = GetMenuItemCount(fe->typemenu);
2897 n = midend_which_preset(fe->me);
2898 if (n < 0)
2899 n = total - 1; /* "Custom" item */
2900
2901 for (i = 0; i < total; i++) {
2902 int flag = (i == n ? MF_CHECKED : MF_UNCHECKED);
2903 CheckMenuItem(fe->typemenu, i, MF_BYPOSITION | flag);
2904 }
2905
2906 DrawMenuBar(fe->hwnd);
2907}
2908
2909static void update_copy_menu_greying(frontend *fe)
2910{
2911 UINT enable = (midend_can_format_as_text_now(fe->me) ?
2912 MF_ENABLED : MF_GRAYED);
2913 EnableMenuItem(fe->gamemenu, IDM_COPY, MF_BYCOMMAND | enable);
2914}
2915
2916static void new_game_type(frontend *fe)
2917{
2918 midend_new_game(fe->me);
2919 new_game_size(fe, 1.0);
2920 update_type_menu_tick(fe);
2921 update_copy_menu_greying(fe);
2922}
2923
2924static int is_alt_pressed(void)
2925{
2926 BYTE keystate[256];
2927 int r = GetKeyboardState(keystate);
2928 if (!r)
2929 return FALSE;
2930 if (keystate[VK_MENU] & 0x80)
2931 return TRUE;
2932 if (keystate[VK_RMENU] & 0x80)
2933 return TRUE;
2934 return FALSE;
2935}
2936
2937static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
2938 WPARAM wParam, LPARAM lParam)
2939{
2940 frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
2941 int cmd;
2942
2943 switch (message) {
2944 case WM_CLOSE:
2945 DestroyWindow(hwnd);
2946 return 0;
2947 case WM_COMMAND:
2948#ifdef _WIN32_WCE
2949 /* Numeric pad sends WM_COMMAND messages */
2950 if ((wParam >= IDM_KEYEMUL) && (wParam < IDM_KEYEMUL + 256))
2951 {
2952 midend_process_key(fe->me, 0, 0, wParam - IDM_KEYEMUL);
2953 }
2954#endif
2955 cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */
2956 switch (cmd) {
2957 case IDM_NEW:
2958 if (!midend_process_key(fe->me, 0, 0, 'n'))
2959 PostQuitMessage(0);
2960 break;
2961 case IDM_RESTART:
2962 midend_restart_game(fe->me);
2963 break;
2964 case IDM_UNDO:
2965 if (!midend_process_key(fe->me, 0, 0, 'u'))
2966 PostQuitMessage(0);
2967 break;
2968 case IDM_REDO:
2969 if (!midend_process_key(fe->me, 0, 0, '\x12'))
2970 PostQuitMessage(0);
2971 break;
2972 case IDM_COPY:
2973 {
2974 char *text = midend_text_format(fe->me);
2975 if (text)
2976 write_clip(hwnd, text);
2977 else
2978 MessageBeep(MB_ICONWARNING);
2979 sfree(text);
2980 }
2981 break;
2982 case IDM_SOLVE:
2983 {
2984 char *msg = midend_solve(fe->me);
2985 if (msg)
2986 MessageBox(hwnd, msg, "Unable to solve",
2987 MB_ICONERROR | MB_OK);
2988 }
2989 break;
2990 case IDM_QUIT:
2991 if (!midend_process_key(fe->me, 0, 0, 'q'))
2992 PostQuitMessage(0);
2993 break;
2994 case IDM_CONFIG:
2995 if (get_config(fe, CFG_SETTINGS))
2996 new_game_type(fe);
2997 break;
2998 case IDM_SEED:
2999 if (get_config(fe, CFG_SEED))
3000 new_game_type(fe);
3001 break;
3002 case IDM_DESC:
3003 if (get_config(fe, CFG_DESC))
3004 new_game_type(fe);
3005 break;
3006 case IDM_PRINT:
3007 if (get_config(fe, CFG_PRINT))
3008 print(fe);
3009 break;
3010 case IDM_ABOUT:
3011 about(fe);
3012 break;
3013 case IDM_LOAD:
3014 case IDM_SAVE:
3015 {
3016 OPENFILENAME of;
3017 char filename[FILENAME_MAX];
3018 int ret;
3019
3020 memset(&of, 0, sizeof(of));
3021 of.hwndOwner = hwnd;
3022 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
3023 of.lpstrCustomFilter = NULL;
3024 of.nFilterIndex = 1;
3025 of.lpstrFile = filename;
3026 filename[0] = '\0';
3027 of.nMaxFile = lenof(filename);
3028 of.lpstrFileTitle = NULL;
3029 of.lpstrTitle = (cmd == IDM_SAVE ?
3030 "Enter name of game file to save" :
3031 "Enter name of saved game file to load");
3032 of.Flags = 0;
3033#ifdef OPENFILENAME_SIZE_VERSION_400
3034 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
3035#else
3036 of.lStructSize = sizeof(of);
3037#endif
3038 of.lpstrInitialDir = NULL;
3039
3040 if (cmd == IDM_SAVE)
3041 ret = GetSaveFileName(&of);
3042 else
3043 ret = GetOpenFileName(&of);
3044
3045 if (ret) {
3046 if (cmd == IDM_SAVE) {
3047 FILE *fp;
3048
3049 if ((fp = fopen(filename, "r")) != NULL) {
3050 char buf[256 + FILENAME_MAX];
3051 fclose(fp);
3052 /* file exists */
3053
3054 sprintf(buf, "Are you sure you want to overwrite"
3055 " the file \"%.*s\"?",
3056 FILENAME_MAX, filename);
3057 if (MessageBox(hwnd, buf, "Question",
3058 MB_YESNO | MB_ICONQUESTION)
3059 != IDYES)
3060 break;
3061 }
3062
3063 fp = fopen(filename, "w");
3064
3065 if (!fp) {
3066 MessageBox(hwnd, "Unable to open save file",
3067 "Error", MB_ICONERROR | MB_OK);
3068 break;
3069 }
3070
3071 midend_serialise(fe->me, savefile_write, fp);
3072
3073 fclose(fp);
3074 } else {
3075 FILE *fp = fopen(filename, "r");
3076 char *err = NULL;
3077 midend *me = fe->me;
3078#ifdef COMBINED
3079 char *id_name;
3080#endif
3081
3082 if (!fp) {
3083 MessageBox(hwnd, "Unable to open saved game file",
3084 "Error", MB_ICONERROR | MB_OK);
3085 break;
3086 }
3087
3088#ifdef COMBINED
3089 /*
3090 * This save file might be from a different
3091 * game.
3092 */
3093 err = identify_game(&id_name, savefile_read, fp);
3094 if (!err) {
3095 int i;
3096 for (i = 0; i < gamecount; i++)
3097 if (!strcmp(id_name, gamelist[i]->name))
3098 break;
3099 if (i == gamecount) {
3100 err = "Save file is for a game not "
3101 "supported by this program";
3102 } else {
3103 me = midend_for_new_game(fe, gamelist[i], NULL,
3104 FALSE, FALSE, &err);
3105 rewind(fp); /* for the actual load */
3106 }
3107 sfree(id_name);
3108 }
3109#endif
3110 if (!err)
3111 err = midend_deserialise(me, savefile_read, fp);
3112
3113 fclose(fp);
3114
3115 if (err) {
3116 MessageBox(hwnd, err, "Error", MB_ICONERROR|MB_OK);
3117 break;
3118 }
3119
3120 if (fe->me != me)
3121 fe_set_midend(fe, me);
3122 new_game_size(fe, 1.0);
3123 }
3124 }
3125 }
3126
3127 break;
3128#ifndef _WIN32_WCE
3129 case IDM_HELPC:
3130 start_help(fe, NULL);
3131 break;
3132 case IDM_GAMEHELP:
3133 assert(help_type != NONE);
3134 start_help(fe, help_type == CHM ?
3135 fe->game->htmlhelp_topic : fe->game->winhelp_topic);
3136 break;
3137#endif
3138 default:
3139#ifdef COMBINED
3140 if (wParam >= IDM_GAMES && wParam < (IDM_GAMES + (WPARAM)gamecount)) {
3141 int p = wParam - IDM_GAMES;
3142 char *error = NULL;
3143 fe_set_midend(fe, midend_for_new_game(fe, gamelist[p], NULL,
3144 FALSE, FALSE, &error));
3145 sfree(error);
3146 } else
3147#endif
3148 {
3149 int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10;
3150
3151 if (p >= 0 && p < fe->npresets) {
3152 midend_set_params(fe->me, fe->presets[p]);
3153 new_game_type(fe);
3154 }
3155 }
3156 break;
3157 }
3158 break;
3159 case WM_DESTROY:
3160#ifndef _WIN32_WCE
3161 stop_help(fe);
3162#endif
3163 frontend_free(fe);
3164 PostQuitMessage(0);
3165 return 0;
3166 case WM_PAINT:
3167 {
3168 PAINTSTRUCT p;
3169 HDC hdc, hdc2;
3170 HBITMAP prevbm;
3171 RECT rcDest;
3172
3173 hdc = BeginPaint(hwnd, &p);
3174 hdc2 = CreateCompatibleDC(hdc);
3175 prevbm = SelectObject(hdc2, fe->bitmap);
3176#ifdef _WIN32_WCE
3177 FillRect(hdc, &(p.rcPaint), (HBRUSH) GetStockObject(WHITE_BRUSH));
3178#endif
3179 IntersectRect(&rcDest, &(fe->bitmapPosition), &(p.rcPaint));
3180 BitBlt(hdc,
3181 rcDest.left, rcDest.top,
3182 rcDest.right - rcDest.left,
3183 rcDest.bottom - rcDest.top,
3184 hdc2,
3185 rcDest.left - fe->bitmapPosition.left,
3186 rcDest.top - fe->bitmapPosition.top,
3187 SRCCOPY);
3188 SelectObject(hdc2, prevbm);
3189 DeleteDC(hdc2);
3190 EndPaint(hwnd, &p);
3191 }
3192 return 0;
3193 case WM_KEYDOWN:
3194 {
3195 int key = -1;
3196 BYTE keystate[256];
3197 int r = GetKeyboardState(keystate);
3198 int shift = (r && (keystate[VK_SHIFT] & 0x80)) ? MOD_SHFT : 0;
3199 int ctrl = (r && (keystate[VK_CONTROL] & 0x80)) ? MOD_CTRL : 0;
3200
3201 switch (wParam) {
3202 case VK_LEFT:
3203 if (!(lParam & 0x01000000))
3204 key = MOD_NUM_KEYPAD | '4';
3205 else
3206 key = shift | ctrl | CURSOR_LEFT;
3207 break;
3208 case VK_RIGHT:
3209 if (!(lParam & 0x01000000))
3210 key = MOD_NUM_KEYPAD | '6';
3211 else
3212 key = shift | ctrl | CURSOR_RIGHT;
3213 break;
3214 case VK_UP:
3215 if (!(lParam & 0x01000000))
3216 key = MOD_NUM_KEYPAD | '8';
3217 else
3218 key = shift | ctrl | CURSOR_UP;
3219 break;
3220 case VK_DOWN:
3221 if (!(lParam & 0x01000000))
3222 key = MOD_NUM_KEYPAD | '2';
3223 else
3224 key = shift | ctrl | CURSOR_DOWN;
3225 break;
3226 /*
3227 * Diagonal keys on the numeric keypad.
3228 */
3229 case VK_PRIOR:
3230 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '9';
3231 break;
3232 case VK_NEXT:
3233 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '3';
3234 break;
3235 case VK_HOME:
3236 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '7';
3237 break;
3238 case VK_END:
3239 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '1';
3240 break;
3241 case VK_INSERT:
3242 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '0';
3243 break;
3244 case VK_CLEAR:
3245 if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '5';
3246 break;
3247 /*
3248 * Numeric keypad keys with Num Lock on.
3249 */
3250 case VK_NUMPAD4: key = MOD_NUM_KEYPAD | '4'; break;
3251 case VK_NUMPAD6: key = MOD_NUM_KEYPAD | '6'; break;
3252 case VK_NUMPAD8: key = MOD_NUM_KEYPAD | '8'; break;
3253 case VK_NUMPAD2: key = MOD_NUM_KEYPAD | '2'; break;
3254 case VK_NUMPAD5: key = MOD_NUM_KEYPAD | '5'; break;
3255 case VK_NUMPAD9: key = MOD_NUM_KEYPAD | '9'; break;
3256 case VK_NUMPAD3: key = MOD_NUM_KEYPAD | '3'; break;
3257 case VK_NUMPAD7: key = MOD_NUM_KEYPAD | '7'; break;
3258 case VK_NUMPAD1: key = MOD_NUM_KEYPAD | '1'; break;
3259 case VK_NUMPAD0: key = MOD_NUM_KEYPAD | '0'; break;
3260 }
3261
3262 if (key != -1) {
3263 if (!midend_process_key(fe->me, 0, 0, key))
3264 PostQuitMessage(0);
3265 } else {
3266 MSG m;
3267 m.hwnd = hwnd;
3268 m.message = WM_KEYDOWN;
3269 m.wParam = wParam;
3270 m.lParam = lParam & 0xdfff;
3271 TranslateMessage(&m);
3272 }
3273 }
3274 break;
3275 case WM_LBUTTONDOWN:
3276 case WM_RBUTTONDOWN:
3277 case WM_MBUTTONDOWN:
3278 {
3279 int button;
3280
3281 /*
3282 * Shift-clicks count as middle-clicks, since otherwise
3283 * two-button Windows users won't have any kind of
3284 * middle click to use.
3285 */
3286 if (message == WM_MBUTTONDOWN || (wParam & MK_SHIFT))
3287 button = MIDDLE_BUTTON;
3288 else if (message == WM_RBUTTONDOWN || is_alt_pressed())
3289 button = RIGHT_BUTTON;
3290 else
3291#ifndef _WIN32_WCE
3292 button = LEFT_BUTTON;
3293#else
3294 if ((fe->game->flags & REQUIRE_RBUTTON) == 0)
3295 button = LEFT_BUTTON;
3296 else
3297 {
3298 SHRGINFO shrgi;
3299
3300 shrgi.cbSize = sizeof(SHRGINFO);
3301 shrgi.hwndClient = hwnd;
3302 shrgi.ptDown.x = (signed short)LOWORD(lParam);
3303 shrgi.ptDown.y = (signed short)HIWORD(lParam);
3304 shrgi.dwFlags = SHRG_RETURNCMD;
3305
3306 if (GN_CONTEXTMENU == SHRecognizeGesture(&shrgi))
3307 button = RIGHT_BUTTON;
3308 else
3309 button = LEFT_BUTTON;
3310 }
3311#endif
3312
3313 if (!midend_process_key(fe->me,
3314 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3315 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3316 button))
3317 PostQuitMessage(0);
3318
3319 SetCapture(hwnd);
3320 }
3321 break;
3322 case WM_LBUTTONUP:
3323 case WM_RBUTTONUP:
3324 case WM_MBUTTONUP:
3325 {
3326 int button;
3327
3328 /*
3329 * Shift-clicks count as middle-clicks, since otherwise
3330 * two-button Windows users won't have any kind of
3331 * middle click to use.
3332 */
3333 if (message == WM_MBUTTONUP || (wParam & MK_SHIFT))
3334 button = MIDDLE_RELEASE;
3335 else if (message == WM_RBUTTONUP || is_alt_pressed())
3336 button = RIGHT_RELEASE;
3337 else
3338 button = LEFT_RELEASE;
3339
3340 if (!midend_process_key(fe->me,
3341 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3342 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3343 button))
3344 PostQuitMessage(0);
3345
3346 ReleaseCapture();
3347 }
3348 break;
3349 case WM_MOUSEMOVE:
3350 {
3351 int button;
3352
3353 if (wParam & (MK_MBUTTON | MK_SHIFT))
3354 button = MIDDLE_DRAG;
3355 else if (wParam & MK_RBUTTON || is_alt_pressed())
3356 button = RIGHT_DRAG;
3357 else
3358 button = LEFT_DRAG;
3359
3360 if (!midend_process_key(fe->me,
3361 (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
3362 (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
3363 button))
3364 PostQuitMessage(0);
3365 }
3366 break;
3367 case WM_CHAR:
3368 if (!midend_process_key(fe->me, 0, 0, (unsigned char)wParam))
3369 PostQuitMessage(0);
3370 return 0;
3371 case WM_TIMER:
3372 if (fe->timer) {
3373 DWORD now = GetTickCount();
3374 float elapsed = (float) (now - fe->timer_last_tickcount) * 0.001F;
3375 midend_timer(fe->me, elapsed);
3376 fe->timer_last_tickcount = now;
3377 }
3378 return 0;
3379#ifndef _WIN32_WCE
3380 case WM_SIZING:
3381 {
3382 RECT *sr = (RECT *)lParam;
3383 int wx, wy, isedge = 0;
3384
3385 if (wParam == WMSZ_TOP ||
3386 wParam == WMSZ_RIGHT ||
3387 wParam == WMSZ_BOTTOM ||
3388 wParam == WMSZ_LEFT) isedge = 1;
3389 adjust_game_size(fe, sr, isedge, &wx, &wy);
3390
3391 /* Given the window size the puzzles constrain
3392 * us to, work out which edge we should be moving. */
3393 if (wParam == WMSZ_TOP ||
3394 wParam == WMSZ_TOPLEFT ||
3395 wParam == WMSZ_TOPRIGHT) {
3396 sr->top = sr->bottom - wy;
3397 } else {
3398 sr->bottom = sr->top + wy;
3399 }
3400 if (wParam == WMSZ_LEFT ||
3401 wParam == WMSZ_TOPLEFT ||
3402 wParam == WMSZ_BOTTOMLEFT) {
3403 sr->left = sr->right - wx;
3404 } else {
3405 sr->right = sr->left + wx;
3406 }
3407 return TRUE;
3408 }
3409 break;
3410#endif
3411 }
3412
3413 return DefWindowProc(hwnd, message, wParam, lParam);
3414}
3415
3416#ifdef _WIN32_WCE
3417static int FindPreviousInstance()
3418{
3419 /* Check if application is running. If it's running then focus on the window */
3420 HWND hOtherWnd = NULL;
3421
3422 hOtherWnd = FindWindow (wGameName, wGameName);
3423 if (hOtherWnd)
3424 {
3425 SetForegroundWindow (hOtherWnd);
3426 return TRUE;
3427 }
3428
3429 return FALSE;
3430}
3431#endif
3432
3433/*
3434 * Split a complete command line into argc/argv, attempting to do it
3435 * exactly the same way the Visual Studio C library would do it (so
3436 * that our console utilities, which receive argc and argv already
3437 * broken apart by the C library, will have their command lines
3438 * processed in the same way as the GUI utilities which get a whole
3439 * command line and must call this function).
3440 *
3441 * Does not modify the input command line.
3442 *
3443 * The final parameter (argstart) is used to return a second array
3444 * of char * pointers, the same length as argv, each one pointing
3445 * at the start of the corresponding element of argv in the
3446 * original command line. So if you get half way through processing
3447 * your command line in argc/argv form and then decide you want to
3448 * treat the rest as a raw string, you can. If you don't want to,
3449 * `argstart' can be safely left NULL.
3450 */
3451void split_into_argv(char *cmdline, int *argc, char ***argv,
3452 char ***argstart)
3453{
3454 char *p;
3455 char *outputline, *q;
3456 char **outputargv, **outputargstart;
3457 int outputargc;
3458
3459 /*
3460 * These argument-breaking rules apply to Visual Studio 7, which
3461 * is currently the compiler expected to be used for the Windows
3462 * port of my puzzles. Visual Studio 10 has different rules,
3463 * lacking the curious mod 3 behaviour of consecutive quotes
3464 * described below; I presume they fixed a bug. As and when we
3465 * migrate to a newer compiler, we'll have to adjust this to
3466 * match; however, for the moment we faithfully imitate in our GUI
3467 * utilities what our CLI utilities can't be prevented from doing.
3468 *
3469 * When I investigated this, at first glance the rules appeared to
3470 * be:
3471 *
3472 * - Single quotes are not special characters.
3473 *
3474 * - Double quotes are removed, but within them spaces cease
3475 * to be special.
3476 *
3477 * - Backslashes are _only_ special when a sequence of them
3478 * appear just before a double quote. In this situation,
3479 * they are treated like C backslashes: so \" just gives a
3480 * literal quote, \\" gives a literal backslash and then
3481 * opens or closes a double-quoted segment, \\\" gives a
3482 * literal backslash and then a literal quote, \\\\" gives
3483 * two literal backslashes and then opens/closes a
3484 * double-quoted segment, and so forth. Note that this
3485 * behaviour is identical inside and outside double quotes.
3486 *
3487 * - Two successive double quotes become one literal double
3488 * quote, but only _inside_ a double-quoted segment.
3489 * Outside, they just form an empty double-quoted segment
3490 * (which may cause an empty argument word).
3491 *
3492 * - That only leaves the interesting question of what happens
3493 * when one or more backslashes precedes two or more double
3494 * quotes, starting inside a double-quoted string. And the
3495 * answer to that appears somewhat bizarre. Here I tabulate
3496 * number of backslashes (across the top) against number of
3497 * quotes (down the left), and indicate how many backslashes
3498 * are output, how many quotes are output, and whether a
3499 * quoted segment is open at the end of the sequence:
3500 *
3501 * backslashes
3502 *
3503 * 0 1 2 3 4
3504 *
3505 * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y
3506 * --------+-----------------------------
3507 * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n
3508 * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n
3509 * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y
3510 * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n
3511 * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n
3512 * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y
3513 * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n
3514 * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n
3515 * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y
3516 * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n
3517 * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n
3518 *
3519 *
3520 * [Test fragment was of the form "a\\\"""b c" d.]
3521 *
3522 * There is very weird mod-3 behaviour going on here in the
3523 * number of quotes, and it even applies when there aren't any
3524 * backslashes! How ghastly.
3525 *
3526 * With a bit of thought, this extremely odd diagram suddenly
3527 * coalesced itself into a coherent, if still ghastly, model of
3528 * how things work:
3529 *
3530 * - As before, backslashes are only special when one or more
3531 * of them appear contiguously before at least one double
3532 * quote. In this situation the backslashes do exactly what
3533 * you'd expect: each one quotes the next thing in front of
3534 * it, so you end up with n/2 literal backslashes (if n is
3535 * even) or (n-1)/2 literal backslashes and a literal quote
3536 * (if n is odd). In the latter case the double quote
3537 * character right after the backslashes is used up.
3538 *
3539 * - After that, any remaining double quotes are processed. A
3540 * string of contiguous unescaped double quotes has a mod-3
3541 * behaviour:
3542 *
3543 * * inside a quoted segment, a quote ends the segment.
3544 * * _immediately_ after ending a quoted segment, a quote
3545 * simply produces a literal quote.
3546 * * otherwise, outside a quoted segment, a quote begins a
3547 * quoted segment.
3548 *
3549 * So, for example, if we started inside a quoted segment
3550 * then two contiguous quotes would close the segment and
3551 * produce a literal quote; three would close the segment,
3552 * produce a literal quote, and open a new segment. If we
3553 * started outside a quoted segment, then two contiguous
3554 * quotes would open and then close a segment, producing no
3555 * output (but potentially creating a zero-length argument);
3556 * but three quotes would open and close a segment and then
3557 * produce a literal quote.
3558 */
3559
3560 /*
3561 * First deal with the simplest of all special cases: if there
3562 * aren't any arguments, return 0,NULL,NULL.
3563 */
3564 while (*cmdline && isspace(*cmdline)) cmdline++;
3565 if (!*cmdline) {
3566 if (argc) *argc = 0;
3567 if (argv) *argv = NULL;
3568 if (argstart) *argstart = NULL;
3569 return;
3570 }
3571
3572 /*
3573 * This will guaranteeably be big enough; we can realloc it
3574 * down later.
3575 */
3576 outputline = snewn(1+strlen(cmdline), char);
3577 outputargv = snewn(strlen(cmdline)+1 / 2, char *);
3578 outputargstart = snewn(strlen(cmdline)+1 / 2, char *);
3579
3580 p = cmdline; q = outputline; outputargc = 0;
3581
3582 while (*p) {
3583 int quote;
3584
3585 /* Skip whitespace searching for start of argument. */
3586 while (*p && isspace(*p)) p++;
3587 if (!*p) break;
3588
3589 /* We have an argument; start it. */
3590 outputargv[outputargc] = q;
3591 outputargstart[outputargc] = p;
3592 outputargc++;
3593 quote = 0;
3594
3595 /* Copy data into the argument until it's finished. */
3596 while (*p) {
3597 if (!quote && isspace(*p))
3598 break; /* argument is finished */
3599
3600 if (*p == '"' || *p == '\\') {
3601 /*
3602 * We have a sequence of zero or more backslashes
3603 * followed by a sequence of zero or more quotes.
3604 * Count up how many of each, and then deal with
3605 * them as appropriate.
3606 */
3607 int i, slashes = 0, quotes = 0;
3608 while (*p == '\\') slashes++, p++;
3609 while (*p == '"') quotes++, p++;
3610
3611 if (!quotes) {
3612 /*
3613 * Special case: if there are no quotes,
3614 * slashes are not special at all, so just copy
3615 * n slashes to the output string.
3616 */
3617 while (slashes--) *q++ = '\\';
3618 } else {
3619 /* Slashes annihilate in pairs. */
3620 while (slashes >= 2) slashes -= 2, *q++ = '\\';
3621
3622 /* One remaining slash takes out the first quote. */
3623 if (slashes) quotes--, *q++ = '"';
3624
3625 if (quotes > 0) {
3626 /* Outside a quote segment, a quote starts one. */
3627 if (!quote) quotes--, quote = 1;
3628
3629 /* Now we produce (n+1)/3 literal quotes... */
3630 for (i = 3; i <= quotes+1; i += 3) *q++ = '"';
3631
3632 /* ... and end in a quote segment iff 3 divides n. */
3633 quote = (quotes % 3 == 0);
3634 }
3635 }
3636 } else {
3637 *q++ = *p++;
3638 }
3639 }
3640
3641 /* At the end of an argument, just append a trailing NUL. */
3642 *q++ = '\0';
3643 }
3644
3645 outputargv = sresize(outputargv, outputargc, char *);
3646 outputargstart = sresize(outputargstart, outputargc, char *);
3647
3648 if (argc) *argc = outputargc;
3649 if (argv) *argv = outputargv; else sfree(outputargv);
3650 if (argstart) *argstart = outputargstart; else sfree(outputargstart);
3651}
3652
3653int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
3654{
3655 MSG msg;
3656 char *error;
3657 const game *gg;
3658 frontend *fe;
3659 midend *me;
3660 int argc;
3661 char **argv;
3662
3663 split_into_argv(cmdline, &argc, &argv, NULL);
3664
3665#ifdef _WIN32_WCE
3666 MultiByteToWideChar (CP_ACP, 0, CLASSNAME, -1, wClassName, 256);
3667 if (FindPreviousInstance ())
3668 return 0;
3669#endif
3670
3671 InitCommonControls();
3672
3673 if (!prev) {
3674 WNDCLASS wndclass;
3675
3676 wndclass.style = 0;
3677 wndclass.lpfnWndProc = WndProc;
3678 wndclass.cbClsExtra = 0;
3679 wndclass.cbWndExtra = 0;
3680 wndclass.hInstance = inst;
3681 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(200));
3682#ifndef _WIN32_WCE
3683 if (!wndclass.hIcon) /* in case resource file is absent */
3684 wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION);
3685#endif
3686 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
3687 wndclass.hbrBackground = NULL;
3688 wndclass.lpszMenuName = NULL;
3689#ifdef _WIN32_WCE
3690 wndclass.lpszClassName = wClassName;
3691#else
3692 wndclass.lpszClassName = CLASSNAME;
3693#endif
3694
3695 RegisterClass(&wndclass);
3696 }
3697
3698 while (*cmdline && isspace((unsigned char)*cmdline))
3699 cmdline++;
3700
3701 init_help();
3702
3703#ifdef COMBINED
3704 gg = gamelist[0];
3705 if (argc > 0) {
3706 int i;
3707 for (i = 0; i < gamecount; i++) {
3708 const char *p = gamelist[i]->name;
3709 char *q = argv[0];
3710 while (*p && *q) {
3711 if (isspace((unsigned char)*p)) {
3712 while (*q && isspace((unsigned char)*q))
3713 q++;
3714 } else {
3715 if (tolower((unsigned char)*p) !=
3716 tolower((unsigned char)*q))
3717 break;
3718 q++;
3719 }
3720 p++;
3721 }
3722 if (!*p) {
3723 gg = gamelist[i];
3724 --argc;
3725 ++argv;
3726 break;
3727 }
3728 }
3729 }
3730#else
3731 gg = &thegame;
3732#endif
3733
3734 fe = frontend_new(inst);
3735 me = midend_for_new_game(fe, gg, argc > 0 ? argv[0] : NULL,
3736 TRUE, TRUE, &error);
3737 if (!me) {
3738 char buf[128];
3739#ifdef COMBINED
3740 sprintf(buf, "Puzzles Error");
3741#else
3742 sprintf(buf, "%.100s Error", gg->name);
3743#endif
3744 MessageBox(NULL, error, buf, MB_OK|MB_ICONERROR);
3745 sfree(error);
3746 return 1;
3747 }
3748 fe_set_midend(fe, me);
3749 show_window(fe);
3750
3751 while (GetMessage(&msg, NULL, 0, 0)) {
3752 DispatchMessage(&msg);
3753 }
3754
3755 DestroyWindow(fe->hwnd);
3756 cleanup_help();
3757
3758 return msg.wParam;
3759}
3760/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/apps/plugins/puzzles/winiss.pl b/apps/plugins/puzzles/winiss.pl
new file mode 100755
index 0000000000..eca02d3b15
--- /dev/null
+++ b/apps/plugins/puzzles/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/winwix.mc b/apps/plugins/puzzles/winwix.mc
new file mode 100644
index 0000000000..1a1e620b82
--- /dev/null
+++ b/apps/plugins/puzzles/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>
diff --git a/apps/plugins/sgt-puzzles.c b/apps/plugins/sgt-puzzles.c
new file mode 100644
index 0000000000..082e5b4cd1
--- /dev/null
+++ b/apps/plugins/sgt-puzzles.c
@@ -0,0 +1,33 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009 Franklin Wei
11 *
12 * Overlay loader stub plugin for puzzles
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#include "plugin.h"
24
25#include "lib/overlay.h"
26
27
28
29/* this is the plugin entry point */
30enum plugin_status plugin_start(const void* parameter)
31{
32 return run_overlay(parameter, PLUGIN_GAMES_DIR "/puzzles.ovl", "Puzzles");
33}