diff --git a/apps/plugins/puzzles/help/blackbox.c b/apps/plugins/puzzles/help/blackbox.c index 659d26cd7a..0121ee3f93 100644 --- a/apps/plugins/puzzles/help/blackbox.c +++ b/apps/plugins/puzzles/help/blackbox.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/bridges.c b/apps/plugins/puzzles/help/bridges.c index b0cdcedd34..be4469b5ac 100644 --- a/apps/plugins/puzzles/help/bridges.c +++ b/apps/plugins/puzzles/help/bridges.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/cube.c b/apps/plugins/puzzles/help/cube.c index 7939437658..6857cbf733 100644 --- a/apps/plugins/puzzles/help/cube.c +++ b/apps/plugins/puzzles/help/cube.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/dominosa.c b/apps/plugins/puzzles/help/dominosa.c index 20a2341c64..388abb16d4 100644 --- a/apps/plugins/puzzles/help/dominosa.c +++ b/apps/plugins/puzzles/help/dominosa.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/fifteen.c b/apps/plugins/puzzles/help/fifteen.c index dc3e5373e8..467118b7d5 100644 --- a/apps/plugins/puzzles/help/fifteen.c +++ b/apps/plugins/puzzles/help/fifteen.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/filling.c b/apps/plugins/puzzles/help/filling.c index 6602890bd8..19ffb97dee 100644 --- a/apps/plugins/puzzles/help/filling.c +++ b/apps/plugins/puzzles/help/filling.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/flip.c b/apps/plugins/puzzles/help/flip.c index 9553df8b77..e5ec994fdb 100644 --- a/apps/plugins/puzzles/help/flip.c +++ b/apps/plugins/puzzles/help/flip.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/flood.c b/apps/plugins/puzzles/help/flood.c index a800ca4444..80e4383903 100644 --- a/apps/plugins/puzzles/help/flood.c +++ b/apps/plugins/puzzles/help/flood.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/galaxies.c b/apps/plugins/puzzles/help/galaxies.c index d6bbf9360c..3942832c52 100644 --- a/apps/plugins/puzzles/help/galaxies.c +++ b/apps/plugins/puzzles/help/galaxies.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/guess.c b/apps/plugins/puzzles/help/guess.c index 8260b0c6f6..cdfc24e6b3 100644 --- a/apps/plugins/puzzles/help/guess.c +++ b/apps/plugins/puzzles/help/guess.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/inertia.c b/apps/plugins/puzzles/help/inertia.c index 184893af4a..956d133d44 100644 --- a/apps/plugins/puzzles/help/inertia.c +++ b/apps/plugins/puzzles/help/inertia.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/keen.c b/apps/plugins/puzzles/help/keen.c index 4539df4625..58a5f0b2af 100644 --- a/apps/plugins/puzzles/help/keen.c +++ b/apps/plugins/puzzles/help/keen.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/lightup.c b/apps/plugins/puzzles/help/lightup.c index 1dca0363c4..5562b954b7 100644 --- a/apps/plugins/puzzles/help/lightup.c +++ b/apps/plugins/puzzles/help/lightup.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/loopy.c b/apps/plugins/puzzles/help/loopy.c index 85ad8e27c4..425b157107 100644 --- a/apps/plugins/puzzles/help/loopy.c +++ b/apps/plugins/puzzles/help/loopy.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/magnets.c b/apps/plugins/puzzles/help/magnets.c index 2f18033675..8c53b2f127 100644 --- a/apps/plugins/puzzles/help/magnets.c +++ b/apps/plugins/puzzles/help/magnets.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/map.c b/apps/plugins/puzzles/help/map.c index 5608eb7619..2f577ae6b2 100644 --- a/apps/plugins/puzzles/help/map.c +++ b/apps/plugins/puzzles/help/map.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/mines.c b/apps/plugins/puzzles/help/mines.c index c37e9298ab..c2f73d4b54 100644 --- a/apps/plugins/puzzles/help/mines.c +++ b/apps/plugins/puzzles/help/mines.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/net.c b/apps/plugins/puzzles/help/net.c index 256012638e..b0224838cf 100644 --- a/apps/plugins/puzzles/help/net.c +++ b/apps/plugins/puzzles/help/net.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/netslide.c b/apps/plugins/puzzles/help/netslide.c index 5d6e282e04..0035c6e3e1 100644 --- a/apps/plugins/puzzles/help/netslide.c +++ b/apps/plugins/puzzles/help/netslide.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/palisade.c b/apps/plugins/puzzles/help/palisade.c index c3e1de1b51..1094502ea7 100644 --- a/apps/plugins/puzzles/help/palisade.c +++ b/apps/plugins/puzzles/help/palisade.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/pattern.c b/apps/plugins/puzzles/help/pattern.c index 83bfaeb5fe..2b43a878bd 100644 --- a/apps/plugins/puzzles/help/pattern.c +++ b/apps/plugins/puzzles/help/pattern.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/pearl.c b/apps/plugins/puzzles/help/pearl.c index fe3f3534fc..353c541d47 100644 --- a/apps/plugins/puzzles/help/pearl.c +++ b/apps/plugins/puzzles/help/pearl.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/pegs.c b/apps/plugins/puzzles/help/pegs.c index 24513761c8..14cbbf7f94 100644 --- a/apps/plugins/puzzles/help/pegs.c +++ b/apps/plugins/puzzles/help/pegs.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/range.c b/apps/plugins/puzzles/help/range.c index 34ae51a512..1ce92c05d1 100644 --- a/apps/plugins/puzzles/help/range.c +++ b/apps/plugins/puzzles/help/range.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/rect.c b/apps/plugins/puzzles/help/rect.c index 196dc077d9..e86a77091b 100644 --- a/apps/plugins/puzzles/help/rect.c +++ b/apps/plugins/puzzles/help/rect.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/samegame.c b/apps/plugins/puzzles/help/samegame.c index 02fc670b80..5efe0ff4c3 100644 --- a/apps/plugins/puzzles/help/samegame.c +++ b/apps/plugins/puzzles/help/samegame.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/signpost.c b/apps/plugins/puzzles/help/signpost.c index c0e4c44e9f..6f89cbdd62 100644 --- a/apps/plugins/puzzles/help/signpost.c +++ b/apps/plugins/puzzles/help/signpost.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/singles.c b/apps/plugins/puzzles/help/singles.c index 606b4f7b41..3f4ecd157b 100644 --- a/apps/plugins/puzzles/help/singles.c +++ b/apps/plugins/puzzles/help/singles.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/sixteen.c b/apps/plugins/puzzles/help/sixteen.c index d72226f8a1..c3d52b6220 100644 --- a/apps/plugins/puzzles/help/sixteen.c +++ b/apps/plugins/puzzles/help/sixteen.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/slant.c b/apps/plugins/puzzles/help/slant.c index b78c7c694c..ad4363825e 100644 --- a/apps/plugins/puzzles/help/slant.c +++ b/apps/plugins/puzzles/help/slant.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/solo.c b/apps/plugins/puzzles/help/solo.c index 449a352fe2..6ebeec86c0 100644 --- a/apps/plugins/puzzles/help/solo.c +++ b/apps/plugins/puzzles/help/solo.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/tents.c b/apps/plugins/puzzles/help/tents.c index d26afead48..f0617fefd4 100644 --- a/apps/plugins/puzzles/help/tents.c +++ b/apps/plugins/puzzles/help/tents.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/towers.c b/apps/plugins/puzzles/help/towers.c index b062c4f350..7b27064659 100644 --- a/apps/plugins/puzzles/help/towers.c +++ b/apps/plugins/puzzles/help/towers.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/tracks.c b/apps/plugins/puzzles/help/tracks.c index 6872ddbbab..dc1bd011fa 100644 --- a/apps/plugins/puzzles/help/tracks.c +++ b/apps/plugins/puzzles/help/tracks.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/twiddle.c b/apps/plugins/puzzles/help/twiddle.c index 69ce44e9b0..48efbc10ad 100644 --- a/apps/plugins/puzzles/help/twiddle.c +++ b/apps/plugins/puzzles/help/twiddle.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/undead.c b/apps/plugins/puzzles/help/undead.c index 11fb57cf48..c08801003a 100644 --- a/apps/plugins/puzzles/help/undead.c +++ b/apps/plugins/puzzles/help/undead.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/unequal.c b/apps/plugins/puzzles/help/unequal.c index f7a612c01e..8885d70c33 100644 --- a/apps/plugins/puzzles/help/unequal.c +++ b/apps/plugins/puzzles/help/unequal.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/unruly.c b/apps/plugins/puzzles/help/unruly.c index 88590aca32..b098642a72 100644 --- a/apps/plugins/puzzles/help/unruly.c +++ b/apps/plugins/puzzles/help/unruly.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/help/untangle.c b/apps/plugins/puzzles/help/untangle.c index 6992d97e96..f7f5c05f24 100644 --- a/apps/plugins/puzzles/help/untangle.c +++ b/apps/plugins/puzzles/help/untangle.c @@ -1,4 +1,4 @@ -/* auto-generated on Jun 25 2020 by genhelp.sh */ +/* auto-generated on Dec 7 2020 by genhelp.sh */ /* help text is compressed using LZ4; see compress.c for details */ /* DO NOT EDIT! */ diff --git a/apps/plugins/puzzles/src/Buildscr b/apps/plugins/puzzles/src/Buildscr deleted file mode 100644 index 21a3bdf30c..0000000000 --- a/apps/plugins/puzzles/src/Buildscr +++ /dev/null @@ -1,236 +0,0 @@ -# -*- sh -*- -# Build script to build Puzzles. -# -# You can cut out large components of the build by defining a subset -# of these options on the bob command line: -# -DNOSIGN -DNOWINDOWS -DNOMACOS -DNOICONS -DNOJAVA -DNOJS - -module puzzles - -set Version $(!builddate).$(vcsid) - -# Start by substituting the right version number in configure.ac. -in puzzles do perl -i~ -pe 's/6.66/$(Version)/' configure.ac -in puzzles do rm configure.ac~ - -# And put it into the documentation as a versionid. -# use perl to avoid inconsistent behaviour of echo '\v' -in puzzles do perl -e 'print "\n\\versionid Simon Tatham'\''s Portable Puzzle Collection, version $$ARGV[0]\n"' $(Version) >> puzzles.but -in puzzles do perl -e 'print "\n\\versionid Simon Tatham'\''s Portable Puzzle Collection, version $$ARGV[0]\n"' $(Version) >> devel.but - -# Write out a version.h that contains the real version number. -in puzzles do echo '/* Generated by automated build script */' > version.h -in puzzles do echo '$#define VER "Version $(Version)"' >> version.h - -# And do the same substitution in the OS X metadata. (This is a bit -# icky in principle because it presumes that my version numbers don't -# need XML escaping, but frankly, if they ever do then I should fix -# them!) -in puzzles do perl -i -pe 's/Unidentified build/$(Version)/' osx-info.plist - -ifneq "$(NOICONS)" yes then - # First build some local binaries, to run the icon build. - in puzzles do perl mkfiles.pl -U CFLAGS='-Wwrite-strings -Werror' - in puzzles do make -j$(nproc) - - # Now build the screenshots and icons. - in puzzles/icons do make web winicons gtkicons -j$(nproc) - - # Destroy the local binaries and autoconf detritus, mostly to avoid - # wasting network bandwidth by transferring them to the delegate - # servers. - in puzzles do make distclean - -endif - -# Re-run mkfiles.pl now that it knows the icons are there. (Or for the -# first time, if we didn't bother building the icons.) -in puzzles do perl mkfiles.pl - -# Rebuild the configure script. -in puzzles do ./mkauto.sh - -ifneq "$(NOMACOS)" yes then - # Build the OS X .dmg archive. - delegate osx - in puzzles do make -f Makefile.osx clean - in puzzles do make -f Makefile.osx release VER=-DVER=$(Version) XFLAGS='-Wwrite-strings -Werror' -j$(nproc) - return puzzles/Puzzles.dmg - enddelegate -endif - -ifneq "$(NOWINDOWS)" yes then - # Build the Windows binaries and installer, and the CHM file. - in puzzles do make -f Makefile.doc clean - in puzzles do make -f Makefile.doc -j$(nproc) # build help files for installer - in puzzles do mason.pl --args '{"version":"$(Version)","descfile":"gamedesc.txt"}' winwix.mc > puzzles.wxs - in puzzles do perl winiss.pl $(Version) gamedesc.txt > puzzles.iss - ifneq "$(VISUAL_STUDIO)" "yes" then - in puzzles with clangcl64 do mkdir win64 && Platform=x64 make -f Makefile.clangcl BUILDDIR=win64/ VER=-DVER=$(Version) XFLAGS='-Wwrite-strings -Werror' -j$(nproc) - in puzzles with clangcl32 do mkdir win32 && Platform=x86 make -f Makefile.clangcl BUILDDIR=win32/ SUBSYSVER=,5.01 VER=-DVER=$(Version) XFLAGS='-Wwrite-strings -Werror' -j$(nproc) - # Code-sign the binaries, if the local bob config provides a script - # to do so. We assume here that the script accepts an -i option to - # provide a 'more info' URL, and an optional -n option to provide a - # program name, and that it can take multiple .exe filename - # arguments and sign them all in place. - ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ win64/*.exe win32/*.exe - # Build installers. - in puzzles with wixonlinux do candle -arch x64 puzzles.wxs -dWin64=yes -dBindir=win64/ && light -ext WixUIExtension -sval puzzles.wixobj - in puzzles with wixonlinux do candle -arch x86 puzzles.wxs -dWin64=no -dBindir=win32/ && light -ext WixUIExtension -sval puzzles.wixobj -o puzzles32.msi - ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ -n "Simon Tatham's Portable Puzzle Collection Installer" puzzles.msi puzzles32.msi - else - delegate windows - in puzzles with visualstudio do/win nmake -f Makefile.vc clean - in puzzles with visualstudio do/win nmake -f Makefile.vc VER=-DVER=$(Version) - ifneq "$(winsigncode)" "" in puzzles do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ win64/*.exe - # Build installers. - in puzzles with wix do/win candle puzzles.wxs -dWin64=yes -dBindir=win64/ && light -ext WixUIExtension -sval puzzles.wixobj - in puzzles with innosetup do/win iscc puzzles.iss - return puzzles/win64/*.exe - return puzzles/puzzles.msi - enddelegate - endif - in puzzles do chmod +x win32/*.exe win64/*.exe -endif - -# Build the Pocket PC binaries and CAB. -# -# NOTE: This part of the build script requires the Windows delegate -# server to have the cabwiz program on its PATH. This will -# typically be at -# -# C:\Program Files\Windows CE Tools\WCE420\POCKET PC 2003\Tools -# -# but it might not be if you've installed it somewhere else, or -# have a different version. -# -# NOTE ALSO: This part of the build is commented out, for the -# moment, because cabwiz does unhelpful things when run from within -# a bob delegate process (or, more generally, when run from any -# terminal-based remote login to a Windows machine, including -# Cygwin opensshd and Windows Telnet). The symptom is that cabwiz -# just beeps and sits there. Until I figure out how to build the -# .cab from an automated process (and I'm willing to consider silly -# approaches such as a third-party CAB generator), I don't think I -# can sensibly enable this build. - -#in puzzles do perl wceinf.pl gamedesc.txt > puzzles.inf -#delegate windows -# in puzzles do cmd /c 'wcearmv4 & nmake -f Makefile.wce clean' -# in puzzles do cmd /c 'wcearmv4 & nmake -f Makefile.wce VER=-DVER=$(Version)' -# # Nasty piece of sh here which saves the return code from cabwiz, -# # outputs its errors and/or warnings, and then propagates the -# # return code back to bob. If only cabwiz could output to -# # standard error LIKE EVERY OTHER COMMAND-LINE UTILITY IN THE -# # WORLD, I wouldn't have to do this. -# in puzzles do cat puzzles.inf -# in puzzles do cmd /c 'wcearmv4 & bash -c cabwiz puzzles.inf /err cabwiz.err /cpu ARMV4'; a=$$?; cat cabwiz.err; exit $$a -# return puzzles/puzzles.armv4.cab -#enddelegate - -# Build the HTML docs. -in puzzles do mkdir doc -in puzzles do mkdir devel -in 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 -in 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 - -ifneq "$(NOWINDOWS)" yes then - # Move the deliver-worthy Windows binaries (those specified in - # gamedesc.txt, which is generated by mkfiles.pl and helpfully - # excludes the command-line auxiliary utilities such as solosolver, - # and nullgame.exe) into a subdirectory for easy access. - in puzzles do mkdir winbin64 winbin32 - in puzzles/win64 do mv `cut -f2 -d: ../gamedesc.txt` ../winbin64 - in puzzles/win32 do mv `cut -f2 -d: ../gamedesc.txt` ../winbin32 - - # Make a zip file of the Windows binaries and help files. - in puzzles do zip -j puzzles.zip winbin64/*.exe puzzles.chm puzzles.hlp puzzles.cnt - in puzzles do zip -j puzzles32.zip winbin32/*.exe puzzles.chm puzzles.hlp puzzles.cnt -endif - -# Create the source archive. (That writes the archive into the -# _parent_ directory, so be careful when we deliver it.) -in puzzles do ./makedist.sh $(Version) - -# Build the autogenerated pieces of the main web page. -in puzzles do perl webpage.pl - -ifneq "$(JAVA_UNFINISHED)" "" in puzzles do perl -i~ -pe 'print "!srcdir unfinished/\n" if /!srcdir icons/' Recipe -ifneq "$(JAVA_UNFINISHED)" "" in puzzles do ln -s unfinished/group.R . -ifneq "$(JAVA_UNFINISHED)" "" in puzzles do perl mkfiles.pl - -ifneq "$(NOJAVA)" yes then - # Build the Java applets. - delegate nestedvm - in puzzles do make -f Makefile.nestedvm NESTEDVM="$$NESTEDVM" VER=-DVER=$(Version) XFLAGS="-Wwrite-strings -Werror" -j$(nproc) - return puzzles/*.jar - enddelegate -endif - -# Build the Javascript applets. Since my master build machine doesn't -# have the right dependencies installed for Emscripten, I do this by a -# delegation. -ifneq "$(NOJS)" yes then - in puzzles do mkdir js # so we can tell output .js files from emcc*.js - delegate emscripten - in puzzles do make -f Makefile.emcc OUTPREFIX=js/ clean - in puzzles do make -f Makefile.emcc OUTPREFIX=js/ XFLAGS="-Wwrite-strings -Werror" -j$(nproc) - return puzzles/js/*.js - enddelegate - - # Build a set of wrapping HTML pages for easy testing of the - # Javascript puzzles. These aren't quite the same as the versions that - # will go on my live website, because those ones will substitute in a - # different footer, and not have to link to the .js files with the - # ../js/ prefix. But these ones should be good enough to just open - # using a file:// URL in a browser after running a build, and make - # sure the main functionality works. - in puzzles do mkdir jstest - in puzzles/jstest do ../html/jspage.pl --jspath=../js/ /dev/null ../html/*.html -endif - -# Set up .htaccess containing a redirect for the archive filename. -in puzzles do echo "AddType application/octet-stream .chm" > .htaccess -in puzzles do echo "AddType application/octet-stream .hlp" >> .htaccess -in puzzles do echo "AddType application/octet-stream .cnt" >> .htaccess -in . do set -- puzzles*.tar.gz; echo RedirectMatch temp '(.*/)'puzzles.tar.gz '$$1'"$$1" >> puzzles/.htaccess -in puzzles do echo RedirectMatch temp '(.*/)'puzzles-installer.msi '$$1'puzzles-$(Version)-installer.msi >> .htaccess - -# Phew, we're done. Deliver everything! -ifneq "$(NOICONS)" yes then - deliver puzzles/icons/*-web.png $@ -endif -ifneq "$(NOWINDOWS)" yes then - deliver puzzles/winbin64/*.exe $@ - deliver puzzles/winbin32/*.exe w32/$@ - deliver puzzles/puzzles.zip $@ - deliver puzzles/puzzles32.zip w32/$@ - deliver puzzles/puzzles.msi puzzles-$(Version)-installer.msi - deliver puzzles/puzzles32.msi w32/puzzles-$(Version)-32bit-installer.msi - deliver puzzles/puzzles.chm $@ - deliver puzzles/puzzles.hlp $@ - deliver puzzles/puzzles.cnt $@ -endif -deliver puzzles/.htaccess $@ -deliver puzzles/doc/*.html doc/$@ -deliver puzzles/devel/*.html devel/$@ -ifneq "$(NOMACOS)" yes then - deliver puzzles/Puzzles.dmg $@ -endif -ifneq "$(NOJAVA)" yes then - deliver puzzles/*.jar java/$@ -endif -ifneq "$(NOJS)" yes then - deliver puzzles/js/*.js js/$@ - deliver puzzles/jstest/*.html jstest/$@ - deliver puzzles/html/*.html html/$@ - deliver puzzles/html/*.pl html/$@ -endif -deliver puzzles/wwwspans.html $@ -deliver puzzles/wwwlinks.html $@ - -# deliver puzzles/puzzles.armv4.cab $@ # (not built at the moment) - -# This one isn't in the puzzles subdir, because makedist.sh left it -# one level up. -deliver puzzles*.tar.gz $@ diff --git a/apps/plugins/puzzles/src/CHECKLST.txt b/apps/plugins/puzzles/src/CHECKLST.txt deleted file mode 100644 index 2bef909e14..0000000000 --- a/apps/plugins/puzzles/src/CHECKLST.txt +++ /dev/null @@ -1,70 +0,0 @@ -Useful checklists -================= - -Things to remember when adding a new puzzle -------------------------------------------- - -Write the source file for the new puzzle (duhh). - -Create a .R file for it which: - - defines a _EXTRA symbol for it if it requires auxiliary - object files (make sure that symbol doesn't contain the icon) - - adds it to the `ALL' definition, to ensure it is compiled into - the OS X binary - - adds it as a GTK build target, with the optional GTK icon - - adds it as a Windows build target, with the optional resource - file - - adds auxiliary solver binaries if any - - adds it to $(GAMES) in both the automake and GTK makefiles, for - `make install' - - adds it to list.c for the OS X binary - - adds it to gamedesc.txt, with its Windows executable name, display - name, and slightly longer description. - -If the puzzle is by a new author, modify the copyright notice in -LICENCE and in puzzles.but. (Also in index.html, but that's listed -below under website changes.) - -Double-check that the game structure name in the source file has -been renamed from `nullgame', so that it'll work on OS X. Actually -compiling it on OS X would be a good way to check this, if -convenient. - -Add a documentation section in puzzles.but. - -Make sure there's a Windows help topic name defined in puzzles.but, -and that it's referenced by the help topic field in the game -structure in the source file. - -Check that REQUIRE_RBUTTON and/or REQUIRE_NUMPAD are set as -appropriate. - -Add the new Unix binary name, and the names of any auxiliary solver -binaries, to .gitignore. - -Write an instructions fragment for the webified puzzle pages, as -html/.html . - -Make a screenshot: - - create an appropriate save file in `icons' - - add the puzzle name to icons/Makefile - - set up a REDO property in icons/Makefile if the screenshot wants - to display a move halfway through an animation - - set up a CROP property in icons/Makefile if the icon wants to be - a sub-rectangle of the whole screenshot - -Don't forget to `git add' the new source file, the new .R file and the -save file in `icons', the new .html file, and any other new files that -might have been involved. - -Check in! - -Put the puzzle on the web: - - run puzzlesnap.sh - - adjust the copyright in index-mid.html if the puzzle is by a new - author - - check that the new puzzle has appeared on the staging web page - - test both Windows binary links, the docs link, the Javascript - version and the Java version - - run webupdate - - test all those things once more on the live website diff --git a/apps/plugins/puzzles/src/Makefile.doc b/apps/plugins/puzzles/src/Makefile.doc deleted file mode 100644 index 0fd28db5ed..0000000000 --- a/apps/plugins/puzzles/src/Makefile.doc +++ /dev/null @@ -1,17 +0,0 @@ -all: puzzles.chm puzzles.hlp puzzles.txt HACKING - -preprocessed.but: puzzles.but - sed 's/PREFIX-/$(BINPREFIX)/g' puzzles.but > preprocessed.but - -puzzles.chm: preprocessed.but - halibut --chm=puzzles.chm preprocessed.but -puzzles.hlp: preprocessed.but - halibut --winhelp=puzzles.hlp preprocessed.but -puzzles.txt: preprocessed.but - halibut --text=puzzles.txt preprocessed.but - -HACKING: devel.but - halibut --text=HACKING devel.but - -clean: - rm -f puzzles.hlp puzzles.txt preprocessed.but HACKING *.html *.hh[pck] diff --git a/apps/plugins/puzzles/src/PuzzleApplet.java b/apps/plugins/puzzles/src/PuzzleApplet.java deleted file mode 100644 index 8455734dd1..0000000000 --- a/apps/plugins/puzzles/src/PuzzleApplet.java +++ /dev/null @@ -1,651 +0,0 @@ -/* - * PuzzleApplet.java: NestedVM applet for the puzzle collection - */ -import java.awt.*; -import java.awt.event.*; -import java.awt.image.BufferedImage; -import java.util.*; -import javax.swing.*; -import javax.swing.border.BevelBorder; -import javax.swing.Timer; -import java.util.List; - -import org.ibex.nestedvm.Runtime; - -public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB { - - private static final long serialVersionUID = 1L; - - private static final int CFG_SETTINGS = 0, CFG_SEED = 1, CFG_DESC = 2, - LEFT_BUTTON = 0x0200, MIDDLE_BUTTON = 0x201, RIGHT_BUTTON = 0x202, - LEFT_DRAG = 0x203, MIDDLE_DRAG = 0x204, RIGHT_DRAG = 0x205, - LEFT_RELEASE = 0x206, CURSOR_UP = 0x209, CURSOR_DOWN = 0x20a, - CURSOR_LEFT = 0x20b, CURSOR_RIGHT = 0x20c, MOD_CTRL = 0x1000, - MOD_SHFT = 0x2000, MOD_NUM_KEYPAD = 0x4000, ALIGN_VCENTRE = 0x100, - ALIGN_HCENTRE = 0x001, ALIGN_HRIGHT = 0x002, C_STRING = 0, - C_CHOICES = 1, C_BOOLEAN = 2; - - private JFrame mainWindow; - - private JMenu typeMenu; - private JMenuItem[] typeMenuItems; - private int customMenuItemIndex; - - private JMenuItem solveCommand; - private Color[] colors; - private JLabel statusBar; - private PuzzlePanel pp; - private Runtime runtime; - private String[] puzzle_args; - private Graphics2D gg; - private Timer timer; - private int xarg1, xarg2, xarg3; - private int[] xPoints, yPoints; - private BufferedImage[] blitters = new BufferedImage[512]; - private ConfigDialog dlg; - - static { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - public void init() { - try { - Container cp = getContentPane(); - cp.setLayout(new BorderLayout()); - runtime = (Runtime) Class.forName("PuzzleEngine").newInstance(); - runtime.setCallJavaCB(this); - JMenuBar menubar = new JMenuBar(); - JMenu jm; - menubar.add(jm = new JMenu("Game")); - addMenuItemCallback(jm, "New", "jcallback_newgame_event"); - addMenuItemCallback(jm, "Restart", "jcallback_restart_event"); - addMenuItemCallback(jm, "Specific...", "jcallback_config_event", CFG_DESC); - addMenuItemCallback(jm, "Random Seed...", "jcallback_config_event", CFG_SEED); - jm.addSeparator(); - addMenuItemCallback(jm, "Undo", "jcallback_undo_event"); - addMenuItemCallback(jm, "Redo", "jcallback_redo_event"); - jm.addSeparator(); - solveCommand = addMenuItemCallback(jm, "Solve", "jcallback_solve_event"); - solveCommand.setEnabled(false); - if (mainWindow != null) { - jm.addSeparator(); - addMenuItemCallback(jm, "Exit", "jcallback_quit_event"); - } - menubar.add(typeMenu = new JMenu("Type")); - typeMenu.setVisible(false); - menubar.add(jm = new JMenu("Help")); - addMenuItemCallback(jm, "About", "jcallback_about_event"); - setJMenuBar(menubar); - cp.add(pp = new PuzzlePanel(), BorderLayout.CENTER); - pp.addKeyListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - int key = -1; - int shift = e.isShiftDown() ? MOD_SHFT : 0; - int ctrl = e.isControlDown() ? MOD_CTRL : 0; - switch (e.getKeyCode()) { - case KeyEvent.VK_LEFT: - case KeyEvent.VK_KP_LEFT: - key = shift | ctrl | CURSOR_LEFT; - break; - case KeyEvent.VK_RIGHT: - case KeyEvent.VK_KP_RIGHT: - key = shift | ctrl | CURSOR_RIGHT; - break; - case KeyEvent.VK_UP: - case KeyEvent.VK_KP_UP: - key = shift | ctrl | CURSOR_UP; - break; - case KeyEvent.VK_DOWN: - case KeyEvent.VK_KP_DOWN: - key = shift | ctrl | CURSOR_DOWN; - break; - case KeyEvent.VK_PAGE_UP: - key = shift | ctrl | MOD_NUM_KEYPAD | '9'; - break; - case KeyEvent.VK_PAGE_DOWN: - key = shift | ctrl | MOD_NUM_KEYPAD | '3'; - break; - case KeyEvent.VK_HOME: - key = shift | ctrl | MOD_NUM_KEYPAD | '7'; - break; - case KeyEvent.VK_END: - key = shift | ctrl | MOD_NUM_KEYPAD | '1'; - break; - default: - if (e.getKeyCode() >= KeyEvent.VK_NUMPAD0 && e.getKeyCode() <=KeyEvent.VK_NUMPAD9) { - key = MOD_NUM_KEYPAD | (e.getKeyCode() - KeyEvent.VK_NUMPAD0+'0'); - } - break; - } - if (key != -1) { - runtimeCall("jcallback_key_event", new int[] {0, 0, key}); - } - } - public void keyTyped(KeyEvent e) { - int key = e.getKeyChar(); - if (key == 26 && e.isShiftDown() && e.isControlDown()) { - runtimeCall("jcallback_redo_event", new int[0]); - return; - } - runtimeCall("jcallback_key_event", new int[] {0, 0, key}); - } - }); - pp.addMouseListener(new MouseAdapter() { - public void mouseReleased(MouseEvent e) { - mousePressedReleased(e, true); - } - public void mousePressed(MouseEvent e) { - pp.requestFocus(); - mousePressedReleased(e, false); - } - private void mousePressedReleased(MouseEvent e, boolean released) { - int button; - if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0) - button = MIDDLE_BUTTON; - else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0) - button = RIGHT_BUTTON; - else if ((e.getModifiers() & (InputEvent.BUTTON1_MASK)) != 0) - button = LEFT_BUTTON; - else - return; - if (released) - button += LEFT_RELEASE - LEFT_BUTTON; - runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button}); - } - }); - pp.addMouseMotionListener(new MouseMotionAdapter() { - public void mouseDragged(MouseEvent e) { - int button; - if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0) - button = MIDDLE_DRAG; - else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0) - button = RIGHT_DRAG; - else - button = LEFT_DRAG; - runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button}); - } - }); - pp.addComponentListener(new ComponentAdapter() { - public void componentResized(ComponentEvent e) { - handleResized(); - } - }); - pp.setFocusable(true); - pp.requestFocus(); - timer = new Timer(20, new ActionListener() { - public void actionPerformed(ActionEvent e) { - runtimeCall("jcallback_timer_func", new int[0]); - } - }); - String gameid; - try { - gameid = getParameter("game_id"); - } catch (java.lang.NullPointerException ex) { - gameid = null; - } - if (gameid == null) { - puzzle_args = null; - } else { - puzzle_args = new String[2]; - puzzle_args[0] = "puzzle"; - puzzle_args[1] = gameid; - } - SwingUtilities.invokeLater(new Runnable() { - public void run() { - runtime.start(puzzle_args); - runtime.execute(); - } - }); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - public void destroy() { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - runtime.execute(); - if (mainWindow != null) { - mainWindow.dispose(); - System.exit(0); - } - } - }); - } - - protected void handleResized() { - pp.createBackBuffer(pp.getWidth(), pp.getHeight(), colors[0]); - runtimeCall("jcallback_resize", new int[] {pp.getWidth(), pp.getHeight()}); - } - - private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int arg) { - return addMenuItemCallback(jm, name, callback, new int[] {arg}, false); - } - - private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback) { - return addMenuItemCallback(jm, name, callback, new int[0], false); - } - - private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int[] args, boolean checkbox) { - JMenuItem jmi; - if (checkbox) - jm.add(jmi = new JCheckBoxMenuItem(name)); - else - jm.add(jmi = new JMenuItem(name)); - jmi.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - runtimeCall(callback, args); - } - }); - return jmi; - } - - protected void runtimeCall(String func, int[] args) { - if (runtimeCallWithResult(func, args) == 42 && mainWindow != null) { - destroy(); - } - } - - protected int runtimeCallWithResult(String func, int[] args) { - try { - return runtime.call(func, args); - } catch (Runtime.CallException ex) { - ex.printStackTrace(); - return 42; - } - } - - private void buildConfigureMenuItem() { - if (typeMenu.isVisible()) { - typeMenu.addSeparator(); - } else { - typeMenu.setVisible(true); - } - typeMenuItems[customMenuItemIndex] = - addMenuItemCallback(typeMenu, "Custom...", - "jcallback_config_event", - new int[] {CFG_SETTINGS}, true); - } - - private void addTypeItem - (JMenu targetMenu, String name, int newId, final int ptrGameParams) { - - typeMenu.setVisible(true); - typeMenuItems[newId] = - addMenuItemCallback(targetMenu, name, - "jcallback_preset_event", - new int[] {ptrGameParams}, true); - } - - private void addTypeSubmenu - (JMenu targetMenu, String name, int newId) { - - JMenu newMenu = new JMenu(name); - newMenu.setVisible(true); - typeMenuItems[newId] = newMenu; - targetMenu.add(newMenu); - } - - public int call(int cmd, int arg1, int arg2, int arg3) { - try { - switch(cmd) { - case 0: // initialize - if (mainWindow != null) mainWindow.setTitle(runtime.cstring(arg1)); - if ((arg2 & 1) != 0) buildConfigureMenuItem(); - if ((arg2 & 2) != 0) addStatusBar(); - if ((arg2 & 4) != 0) solveCommand.setEnabled(true); - colors = new Color[arg3]; - return 0; - case 1: // configure Type menu - if (arg1 == 0) { - // preliminary setup - typeMenuItems = new JMenuItem[arg2 + 2]; - typeMenuItems[arg2] = typeMenu; - customMenuItemIndex = arg2 + 1; - return arg2; - } else if (xarg1 != 0) { - addTypeItem((JMenu)typeMenuItems[arg2], - runtime.cstring(arg1), arg3, xarg1); - } else { - addTypeSubmenu((JMenu)typeMenuItems[arg2], - runtime.cstring(arg1), arg3); - } - return 0; - case 2: // MessageBox - JOptionPane.showMessageDialog(this, runtime.cstring(arg2), runtime.cstring(arg1), arg3 == 0 ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE); - return 0; - case 3: // Resize - pp.setPreferredSize(new Dimension(arg1, arg2)); - if (mainWindow != null) mainWindow.pack(); - handleResized(); - if (mainWindow != null) mainWindow.setVisible(true); - return 0; - case 4: // drawing tasks - switch(arg1) { - case 0: - String text = runtime.cstring(arg2); - if (text.equals("")) text = " "; - statusBar.setText(text); - break; - case 1: - gg = pp.backBuffer.createGraphics(); - if (arg2 != 0 || arg3 != 0 || - arg2 + xarg2 != getWidth() || - arg3 + xarg3 != getHeight()) { - int left = arg2, right = arg2 + xarg2; - int top = arg3, bottom = arg3 + xarg3; - int width = getWidth(), height = getHeight(); - gg.setColor(colors != null ? colors[0] : Color.black); - gg.fillRect(0, 0, left, height); - gg.fillRect(right, 0, width-right, height); - gg.fillRect(0, 0, width, top); - gg.fillRect(0, bottom, width, height-bottom); - gg.setClip(left, top, right-left, bottom-top); - } - break; - case 2: gg.dispose(); pp.repaint(); break; - case 3: gg.setClip(arg2, arg3, xarg1, xarg2); break; - case 4: - if (arg2 == 0 && arg3 == 0) { - gg.setClip(0, 0, getWidth(), getHeight()); - } else { - gg.setClip(arg2, arg3, getWidth()-2*arg2, getHeight()-2*arg3); - } - break; - case 5: - gg.setColor(colors[xarg3]); - gg.fillRect(arg2, arg3, xarg1, xarg2); - break; - case 6: - gg.setColor(colors[xarg3]); - gg.drawLine(arg2, arg3, xarg1, xarg2); - break; - case 7: - xPoints = new int[arg2]; - yPoints = new int[arg2]; - break; - case 8: - if (arg3 != -1) { - gg.setColor(colors[arg3]); - gg.fillPolygon(xPoints, yPoints, xPoints.length); - } - gg.setColor(colors[arg2]); - gg.drawPolygon(xPoints, yPoints, xPoints.length); - break; - case 9: - if (arg3 != -1) { - gg.setColor(colors[arg3]); - gg.fillOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2); - } - gg.setColor(colors[arg2]); - gg.drawOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2); - break; - case 10: - for(int i=0; i= 1024 && cmd < 2048) { // palette - colors[cmd-1024] = new Color(arg1, arg2, arg3); - } - if (cmd == 1024) { - pp.setBackground(colors[0]); - if (statusBar != null) statusBar.setBackground(colors[0]); - this.setBackground(colors[0]); - } - return 0; - } - } catch (Throwable ex) { - ex.printStackTrace(); - System.exit(-1); - return 0; - } - } - - private void addStatusBar() { - statusBar = new JLabel("test"); - statusBar.setBorder(new BevelBorder(BevelBorder.LOWERED)); - getContentPane().add(BorderLayout.SOUTH,statusBar); - } - - // Standalone runner - public static void main(String[] args) { - final PuzzleApplet a = new PuzzleApplet(); - JFrame jf = new JFrame("Loading..."); - jf.getContentPane().setLayout(new BorderLayout()); - jf.getContentPane().add(a, BorderLayout.CENTER); - a.mainWindow=jf; - a.init(); - a.start(); - jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - jf.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - a.stop(); - a.destroy(); - } - }); - jf.setVisible(true); - } - - public static class PuzzlePanel extends JPanel { - - private static final long serialVersionUID = 1L; - protected BufferedImage backBuffer; - - public PuzzlePanel() { - setPreferredSize(new Dimension(100,100)); - createBackBuffer(100,100, Color.black); - } - - public void createBackBuffer(int w, int h, Color bg) { - if (w > 0 && h > 0) { - backBuffer = new BufferedImage(w,h, BufferedImage.TYPE_3BYTE_BGR); - Graphics g = backBuffer.createGraphics(); - g.setColor(bg); - g.fillRect(0, 0, w, h); - g.dispose(); - } - } - - protected void paintComponent(Graphics g) { - g.drawImage(backBuffer, 0, 0, this); - } - } - - public static class ConfigComponent { - public int type; - public int configItemPointer; - public JComponent component; - - public ConfigComponent(int type, int configItemPointer, JComponent component) { - this.type = type; - this.configItemPointer = configItemPointer; - this.component = component; - } - } - - public class ConfigDialog extends JDialog { - - private GridBagConstraints gbcLeft = new GridBagConstraints( - GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1, - 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, - new Insets(0, 0, 0, 0), 0, 0); - private GridBagConstraints gbcRight = new GridBagConstraints( - GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, - GridBagConstraints.REMAINDER, 1, 1.0, 0, - GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, - new Insets(5, 5, 5, 5), 0, 0); - private GridBagConstraints gbcBottom = new GridBagConstraints( - GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, - GridBagConstraints.REMAINDER, GridBagConstraints.REMAINDER, - 1.0, 1.0, GridBagConstraints.CENTER, - GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0); - - private static final long serialVersionUID = 1L; - private List components = new ArrayList(); - - public ConfigDialog(JApplet parent, String title) { - super(JOptionPane.getFrameForComponent(parent), title, true); - getContentPane().setLayout(new GridBagLayout()); - } - - public void addTextBox(int ptr, String name, String value) { - getContentPane().add(new JLabel(name), gbcLeft); - JComponent c = new JTextField(value, 25); - getContentPane().add(c, gbcRight); - components.add(new ConfigComponent(C_STRING, ptr, c)); - } - - - public void addCheckBox(int ptr, String name, boolean selected) { - JComponent c = new JCheckBox(name, selected); - getContentPane().add(c, gbcRight); - components.add(new ConfigComponent(C_BOOLEAN, ptr, c)); - } - - public void addComboBox(int ptr, String name, String values, int selected) { - getContentPane().add(new JLabel(name), gbcLeft); - StringTokenizer st = new StringTokenizer(values.substring(1), values.substring(0,1)); - JComboBox c = new JComboBox(); - c.setEditable(false); - while(st.hasMoreTokens()) - c.addItem(st.nextToken()); - c.setSelectedIndex(selected); - getContentPane().add(c, gbcRight); - components.add(new ConfigComponent(C_CHOICES, ptr, c)); - } - - public void finish() { - JPanel buttons = new JPanel(new GridLayout(1, 2, 5, 5)); - getContentPane().add(buttons, gbcBottom); - JButton b; - buttons.add(b=new JButton("OK")); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - save(); - dispose(); - } - }); - getRootPane().setDefaultButton(b); - buttons.add(b=new JButton("Cancel")); - b.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - dispose(); - } - }); - setDefaultCloseOperation(DISPOSE_ON_CLOSE); - pack(); - setLocationRelativeTo(null); - setVisible(true); - } - private void save() { - for (int i = 0; i < components.size(); i++) { - ConfigComponent cc = (ConfigComponent) components.get(i); - switch(cc.type) { - case C_STRING: - JTextField jtf = (JTextField)cc.component; - runtimeCall("jcallback_config_set_string", new int[] {cc.configItemPointer, runtime.strdup(jtf.getText())}); - break; - case C_BOOLEAN: - JCheckBox jcb = (JCheckBox)cc.component; - runtimeCall("jcallback_config_set_boolean", new int[] {cc.configItemPointer, jcb.isSelected()?1:0}); - break; - case C_CHOICES: - JComboBox jcm = (JComboBox)cc.component; - runtimeCall("jcallback_config_set_choice", new int[] {cc.configItemPointer, jcm.getSelectedIndex()}); - break; - } - } - runtimeCall("jcallback_config_ok", new int[0]); - } - } -} diff --git a/apps/plugins/puzzles/src/Recipe b/apps/plugins/puzzles/src/Recipe deleted file mode 100644 index f94b1f9041..0000000000 --- a/apps/plugins/puzzles/src/Recipe +++ /dev/null @@ -1,171 +0,0 @@ -# -*- makefile -*- -# -# This file describes which puzzle binaries are made up from which -# object and resource files. It is processed into the various -# Makefiles by means of a Perl script. Makefile changes should -# really be made by editing this file and/or the Perl script, not -# by editing the actual Makefiles. - -!name puzzles - -!makefile gtk Makefile.gtk -!makefile am Makefile.am -!makefile vc Makefile.vc -!makefile wce Makefile.wce -!makefile cygwin Makefile.cyg -!makefile osx Makefile.osx -!makefile gnustep Makefile.gnustep -!makefile nestedvm Makefile.nestedvm -!makefile emcc Makefile.emcc -!makefile clangcl Makefile.clangcl - -!srcdir icons/ - -WINDOWS_COMMON = printing - + user32.lib gdi32.lib comctl32.lib comdlg32.lib winspool.lib -WINDOWS = windows WINDOWS_COMMON -COMMON = midend drawing misc malloc random version -GTK = gtk printing ps -# Objects needed for auxiliary command-line programs. -STANDALONE = nullfe random misc malloc - -ALL = list - -LATIN_DEPS = matching tree234 -LATIN = latin LATIN_DEPS -LATIN_SOLVER = latin[STANDALONE_SOLVER] LATIN_DEPS - -# First half of list.c. -!begin >list.c -/* - * list.c: List of pointers to puzzle structures, for monolithic - * platforms. - * - * This file is automatically generated by mkfiles.pl. Do not edit - * it directly, or the changes will be lost next time mkfiles.pl runs. - * Instead, edit Recipe and/or its *.R subfiles. - */ -#include "puzzles.h" -#define GAMELIST(A) \ -!end - -# Now each .R file adds part of the macro definition of GAMELIST to list.c. -!include *.R - -# Then we finish up list.c as follows: -!begin >list.c - -#define DECL(x) extern const game x; -#define REF(x) &x, -GAMELIST(DECL) -const game *gamelist[] = { GAMELIST(REF) }; -const int gamecount = lenof(gamelist); -!end - -# Unix standalone application for special-purpose obfuscation. -obfusc : [U] obfusc STANDALONE - -# Test program built from latin.c. -latincheck : [U] latin[STANDALONE_LATIN_TEST] LATIN_DEPS STANDALONE -latincheck : [C] latin[STANDALONE_LATIN_TEST] LATIN_DEPS STANDALONE - -# Test program built from matching.c. -matching : [U] matching[STANDALONE_MATCHING_TEST] tree234 STANDALONE -matching : [C] matching[STANDALONE_MATCHING_TEST] tree234 STANDALONE - -puzzles : [G] windows[COMBINED] WINDOWS_COMMON COMMON ALL noicon.res - -# Mac OS X unified application containing all the puzzles. -Puzzles : [MX] osx osx.icns osx-info.plist COMMON ALL -# For OS X, we must create the online help and include it in the -# application bundle.) Also we add -DCOMBINED to the compiler flags -# so as to inform the code that we're building a single binary for -# all the puzzles. Then I've also got some code in here to build a -# distributable .dmg disk image. -!begin osx -Puzzles_extra = Puzzles.app/Contents/Resources/Help/index.html -Puzzles.app/Contents/Resources/Help/index.html: \ - Puzzles.app/Contents/Resources/Help osx-help.but puzzles.but - cd Puzzles.app/Contents/Resources/Help; \ - halibut --html ../../../../osx-help.but ../../../../puzzles.but -Puzzles.app/Contents/Resources/Help: Puzzles.app/Contents/Resources - mkdir -p Puzzles.app/Contents/Resources/Help - -release: Puzzles.dmg -Puzzles.dmg: Puzzles - rm -f raw.dmg - hdiutil create -megabytes 5 -layout NONE raw.dmg - hdid -nomount raw.dmg > devicename - newfs_hfs -v "Simon Tatham's Puzzle Collection" `cat devicename` - hdiutil eject `cat devicename` - hdid raw.dmg | cut -f1 -d' ' > devicename - cp -R Puzzles.app /Volumes/"Simon Tatham's Puzzle Collection" - hdiutil eject `cat devicename` - rm -f Puzzles.dmg - hdiutil convert -format UDCO raw.dmg -o Puzzles.dmg - rm -f raw.dmg devicename -!end - -!begin am -bin_PROGRAMS = $(GAMES) -!end -!begin am_begin -GAMES = -!end - -# make install for Unix. -!begin gtk -install: - for i in $(GAMES); do \ - $(INSTALL_PROGRAM) -m 755 $(BINPREFIX)$$i $(DESTDIR)$(gamesdir)/$(BINPREFIX)$$i \ - || exit 1; \ - done -!end -!begin nestedvm -%.tmpdir/PuzzleEngine.class: %.mips - mkdir -p $(patsubst %.mips,%,$<).tmpdir - cd $(patsubst %.mips,%,$<).tmpdir && \ - java -cp $(NESTEDVM)/build:$(NESTEDVM)/upstream/build/classgen/build \ - org.ibex.nestedvm.Compiler -outformat class -d . \ - PuzzleEngine ../$< - -org: - mkdir -p org/ibex/nestedvm/util - cp $(NESTEDVM)/build/org/ibex/nestedvm/Registers.class org/ibex/nestedvm - cp $(NESTEDVM)/build/org/ibex/nestedvm/UsermodeConstants.class org/ibex/nestedvm - cp $(NESTEDVM)/build/org/ibex/nestedvm/Runtime*.class org/ibex/nestedvm - cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Platform*.class org/ibex/nestedvm/util - cp $(NESTEDVM)/build/org/ibex/nestedvm/util/Seekable*.class org/ibex/nestedvm/util - -applet.manifest: - echo "Main-Class: PuzzleApplet" >applet.manifest - -PuzzleApplet.class: PuzzleApplet.java org - javac -source 1.7 -target 1.7 PuzzleApplet.java - -%.jar: %.tmpdir/PuzzleEngine.class PuzzleApplet.class applet.manifest org - cd $(patsubst %.jar,%,$@).tmpdir && ln -s ../applet.manifest ../org ../PuzzleApplet*.class . - cd $(patsubst %.jar,%,$@).tmpdir && jar cfm ../$@ applet.manifest PuzzleEngine.class PuzzleApplet*.class org - echo '' >$*.html -!end - -# A benchmarking and testing target for the GTK puzzles. -!begin gtk -test: benchmark.html benchmark.txt - -benchmark.html: benchmark.txt benchmark.pl - ./benchmark.pl benchmark.txt > $@ - -benchmark.txt: benchmark.sh $(GAMES) - ./benchmark.sh > $@ - -!end -!begin am -test: benchmark.html benchmark.txt - -benchmark.html: benchmark.txt benchmark.pl - ./benchmark.pl benchmark.txt > $@ - -benchmark.txt: benchmark.sh $(GAMES) - ./benchmark.sh > $@ -!end diff --git a/apps/plugins/puzzles/src/benchmark.pl b/apps/plugins/puzzles/src/benchmark.pl deleted file mode 100755 index 7ac48abc25..0000000000 --- a/apps/plugins/puzzles/src/benchmark.pl +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/perl - -# Process the raw output from benchmark.sh into Javascript-ified HTML. - -use strict; -use warnings; - -my @presets = (); -my %presets = (); -my $maxval = 0; - -while (<<>>) { - chomp; - if (/^(.*)(#.*): ([\d\.]+)$/) { - push @presets, $1 unless defined $presets{$1}; - push @{$presets{$1}}, $3; - $maxval = $3 if $maxval < $3; - } -} - -print < - - - -Puzzle generation-time benchmarks - - - -

Puzzle generation-time benchmarks

-

Sort order: - - - - - -EOF - -my $index = 0; -for my $preset (@presets) { - my @data = sort { $a <=> $b } @{$presets{$preset}}; - my $median = ($#data % 2 ? - ($data[($#data-1)/2]+$data[($#data+1)/2])/2 : - $data[$#data/2]); - my $mean = 0; map { $mean += $_ } @data; $mean /= @data; - print "\n"; - $index++; -} - -print < - -EOF - -sub escape { - my ($text) = @_; - $text =~ s/&/&/g; - $text =~ s//>/g; - return $text; -} diff --git a/apps/plugins/puzzles/src/benchmark.sh b/apps/plugins/puzzles/src/benchmark.sh deleted file mode 100755 index b3af27765e..0000000000 --- a/apps/plugins/puzzles/src/benchmark.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -# Run every puzzle in benchmarking mode, and generate a file of raw -# data that benchmark.pl will format into a web page. - -# If any arguments are provided, use those as the list of games to -# benchmark. Otherwise, read the full list from gamedesc.txt. -if test $# = 0; then - set -- $(cut -f1 -d: < gamedesc.txt) -fi - -failures=false - -for game in "$@"; do - # Use 'env -i' to suppress any environment variables that might - # change the preset list for a puzzle (e.g. user-defined extras) - presets=$(env -i ./$game --list-presets | cut -f1 -d' ') - for preset in $presets; do - if ! env -i ./$game --test-solve --time-generation \ - --generate 100 $preset; - then - echo "${game} ${preset} failed to generate" >&2 - fi - done -done - -if $failures; then exit 1; fi diff --git a/apps/plugins/puzzles/src/chm.css b/apps/plugins/puzzles/src/chm.css deleted file mode 100644 index d8c316bfc6..0000000000 --- a/apps/plugins/puzzles/src/chm.css +++ /dev/null @@ -1,7 +0,0 @@ -/* Stylesheet for a Windows .CHM help file */ - -body { font-size: 75%; font-family: Verdana, Arial, Helvetica, Sans-Serif; } - -h1 { font-weight: bold; font-size: 150%; } -h2 { font-weight: bold; font-size: 130%; } -h3 { font-weight: bold; font-size: 120%; } diff --git a/apps/plugins/puzzles/src/configure.ac b/apps/plugins/puzzles/src/configure.ac deleted file mode 100644 index 3a38c95602..0000000000 --- a/apps/plugins/puzzles/src/configure.ac +++ /dev/null @@ -1,85 +0,0 @@ -dnl Configure script for the Unix GTK build of puzzles. - -AC_INIT([puzzles], [6.66], [anakin@pobox.com]) -AC_CONFIG_SRCDIR([midend.c]) -AM_INIT_AUTOMAKE([foreign]) -AC_PROG_CC - -AC_ARG_WITH([gtk], - [AS_HELP_STRING([--with-gtk=VER], - [specify GTK version to use (`2' or `3')])], - [gtk_version_desired="$withval"], - [gtk_version_desired="any"]) - -case "$gtk_version_desired" in - 2 | 3 | any) ;; - yes) gtk_version_desired="any" ;; - *) AC_ERROR([Invalid GTK version specified]) -esac - -gtk=none - -case "$gtk_version_desired:$gtk" in - 3:none | any:none) - ifdef([AM_PATH_GTK_3_0],[ - AM_PATH_GTK_3_0([3.0.0], [gtk=3], []) - ],[AC_WARNING([generating configure script without GTK 3 autodetection])]) - ;; -esac - -case "$gtk_version_desired:$gtk" in - 2:none | any:none) - ifdef([AM_PATH_GTK_2_0],[ - AM_PATH_GTK_2_0([2.0.0], [gtk=2], []) - ],[AC_WARNING([generating configure script without GTK 2 autodetection])]) - ;; -esac - -if test "$gtk" = "none"; then - AC_MSG_ERROR([cannot build without GTK 2 or GTK 3]) -fi - -if test "x$GCC" = "xyes"; then - AC_MSG_CHECKING([for usable gcc warning flags]) - gccwarningflags= - for flag in -Wall -Werror -std=c89 -pedantic; do - ac_save_CFLAGS="$CFLAGS" - ac_save_LIBS="$LIBS" - CFLAGS="$CFLAGS$gccwarningflags $flag $GTK_CFLAGS" - LIBS="$GTK_LIBS $LIBS" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - - #include - #include - - #include - - #include - #include - #include - #include - ],[ - return 0; - ])], [gccwarningflags="$gccwarningflags $flag"], []) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" - done - AC_MSG_RESULT($gccwarningflags) - CFLAGS="$CFLAGS$gccwarningflags" -fi - -AC_PROG_RANLIB -AC_PROG_INSTALL -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/apps/plugins/puzzles/src/desktop.pl b/apps/plugins/puzzles/src/desktop.pl deleted file mode 100755 index 204c0ce262..0000000000 --- a/apps/plugins/puzzles/src/desktop.pl +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/perl - -# Make .desktop files for the puzzles. -# -# At present, this script is intended for developer usage: if you're -# working on the puzzles and want to play your bleeding-edge locally -# modified and compiled versions, run this script and it will create a -# collection of desktop files in ~/.local/share/applications where -# XFCE can pick them up and add them to its main menu. (Be sure to run -# 'xfdesktop --reload' after running this.) -# -# (If you don't use XFCE, patches to support other desktop -# environments are welcome :-) - -use strict; -use warnings; -use Cwd 'abs_path'; - -die "usage: desktop.pl [ [ ]]\n" - unless @ARGV == 0 or @ARGV == 1 or @ARGV == 3; - -my ($outdir, $bindir, $icondir) = @ARGV; -$outdir = $ENV{'HOME'}."/.local/share/applications" unless defined $outdir; -$bindir = "." unless defined $bindir; -$icondir = "./icons" unless defined $icondir; -$bindir = abs_path($bindir); -$icondir = abs_path($icondir); - -open my $desc, "<", "gamedesc.txt" - or die "gamedesc.txt: open: $!\n"; - -while (<$desc>) { - chomp; - my ($id, $win, $displayname, $description, $summary) = split /:/, $_; - - open my $desktop, ">", "$outdir/$id.desktop" - or die "$outdir/$id.desktop: open: $!\n"; - - print $desktop "[Desktop Entry]\n"; - print $desktop "Version=1.0\n"; - print $desktop "Type=Application\n"; - print $desktop "Name=$displayname\n"; - print $desktop "Comment=$description\n"; - print $desktop "Exec=$bindir/$id\n"; - print $desktop "Icon=$icondir/$id-48d24.png\n"; - print $desktop "StartupNotify=false\n"; - print $desktop "Categories=Game;\n"; - print $desktop "Terminal=false\n"; - - close $desktop - or die "$outdir/$id.desktop: close: $!\n"; -} diff --git a/apps/plugins/puzzles/src/devel.but b/apps/plugins/puzzles/src/devel.but index 741a7944ac..7521596df9 100644 --- a/apps/plugins/puzzles/src/devel.but +++ b/apps/plugins/puzzles/src/devel.but @@ -1295,7 +1295,8 @@ flag in the \c{game_ui} to indicate which flash type is required.) \c const game_drawstate *ds, \c const game_state *state, \c const game_params *params, -\c int *x, int *y, int *w, int *h); +\c int *x, int *y, +\c int *w, int *h); This function queries the backend for the rectangular region containing the cursor (in games that have one), or other region of @@ -1303,34 +1304,34 @@ interest. This function is called by only \cw{midend_get_cursor_location()}(\k{midend-get-cursor-location}). Its -purpose is to allow frontends to query the location of the backend's -cursor. With knowledge of this location, a frontend can, for example, +purpose is to allow front ends to query the location of the backend's +cursor. With knowledge of this location, a front end can, for example, ensure that the region of interest remains visible if the puzzle is too big to fit on the screen at once. On returning, \cw{*x}, \cw{*y} should be set to the X and Y coordinates of the upper-left corner of the rectangular region of interest, and \cw{*w} and \cw{*h} should be the width and height of -that region, respectively. All return values are in units of pixels in -screenspace coordinates. In the event that a cursor is not visible on -screen, this function should return and leave the return parameters -untouched \dash the mid-end will notice this. The backend need not +that region, respectively. In the event that a cursor is not visible +on screen, this function should return and leave the return parameters +untouched \dash the midend will notice this. The backend need not bother checking that \cw{x}, \cw{y}, \cw{w} and \cw{h} are -non-\cw{NULL} \dash the mid-end guarantees that they will not be. +non-\cw{NULL} \dash the midend guarantees that they will not be. Defining what constitutes a \q{region of interest} is left up to the backend. If a game provides a conventional cursor \dash such as Mines, -Solo, or any of the other grid-based games \dash the most logical choice -is of course the cursor location. However, in other cases such as Cube -or Inertia, there is no \q{cursor} in the conventional sense \dash the -player controls an object moving around the screen. In these cases, it -makes sense to define the region of interest as the bounding box of -the player or another sensible region \dash such as the grid square the -player is sitting on in Cube. +Solo, or any of the other grid-based games \dash the most logical +choice is of course the location of the cursor itself. However, in +other cases such as Cube or Inertia, there is no \q{cursor} in the +conventional sense \dash the player instead controls an object moving +around the screen. In these cases, it makes sense to define the region +of interest as the bounding box of the player object or another +sensible region \dash such as the grid square the player is sitting on +in Cube. If a backend does not provide a cursor mechanism at all, the backend is free to provide an empty implementation of this function, or a -\cw{NULL} pointer in the \cw{game} structure \dash the mid-end will +\cw{NULL} pointer in the \cw{game} structure \dash the midend will notice either of these cases and behave appropriately. \S{backend-status} \cw{status()} @@ -3353,27 +3354,31 @@ function. Some back ends require that \cw{midend_size()} \H{midend-get-cursor-location} \cw{midend_get_cursor_location()} -\c void midend_get_cursor_location(midend *me, -\c int *x, int *y, int *w, int *h); +\c bool midend_get_cursor_location(midend *me, +\c int *x, int *y, +\c int *w, int *h); -This function returns the location of the backend's on-screen cursor -or other region of interest in the parameters \cw{*x}, \cw{*y}, -\cw{*w} and \cw{*h}, which describe a rectangle with an upper-left -corner at \cw{(*x,*y)} and a size of \cw{*w} pixels wide by \cw{*h} -pixels tall. The mid-end will ignore any return parameters that may be -equal to \cw{NULL}. +This function requests the location of the back end's on-screen cursor +or other region of interest. What exactly this region contains is up to the backend, but in general the region will be an area that the player is controlling with the cursor keys \dash such as the player location in Cube and Inertia, or the cursor in any of the conventional grid-based games. With knowledge -of this location, a frontend can, for example, ensure that the region +of this location, a front end can, for example, ensure that the region of interest remains visible even if the entire puzzle is too big to fit on the screen. -If there is no such region (if either the cursor is not visible, or if -the game does not have cursor support), both \cw{*x} and \cw{*y} will -be set to \cw{-1}. +On success, this function returns \cw{true}, and the locations pointed +to by \cw{x}, \cw{y}, \cw{w} and \cw{h} are updated to describe the +cursor region, which has an upper-left corner located at \cw{(*x,*y)} +and a size of \cw{*w} pixels wide by \cw{*h} pixels tall. The caller +may pass \cw{NULL} for any number of these pointers, which will be +ignored. + +On failure, this function returns \cw{false}. Failure can occur if +there is currently no active cursor region, or if the back end lacks +cursor support. \H{midend-status} \cw{midend_status()} diff --git a/apps/plugins/puzzles/src/emcclib.js b/apps/plugins/puzzles/src/emcclib.js deleted file mode 100644 index 907dc19995..0000000000 --- a/apps/plugins/puzzles/src/emcclib.js +++ /dev/null @@ -1,701 +0,0 @@ -/* - * emcclib.js: one of the Javascript components of an Emscripten-based - * web/Javascript front end for Puzzles. - * - * The other parts of this system live in emcc.c and emccpre.js. It - * also depends on being run in the context of a web page containing - * an appropriate collection of bits and pieces (a canvas, some - * buttons and links etc), which is generated for each puzzle by the - * script html/jspage.pl. - * - * This file contains a set of Javascript functions which we insert - * into Emscripten's library object via the --js-library option; this - * allows us to provide JS code which can be called from the - * Emscripten-compiled C, mostly dealing with UI interaction of - * various kinds. - */ - -mergeInto(LibraryManager.library, { - /* - * void js_debug(const char *message); - * - * A function to write a diagnostic to the Javascript console. - * Unused in production, but handy in development. - */ - js_debug: function(ptr) { - console.log(Pointer_stringify(ptr)); - }, - - /* - * void js_error_box(const char *message); - * - * A wrapper around Javascript's alert(), so the C code can print - * simple error message boxes (e.g. when invalid data is entered - * in a configuration dialog). - */ - js_error_box: function(ptr) { - alert(Pointer_stringify(ptr)); - }, - - /* - * void js_remove_type_dropdown(void); - * - * Get rid of the drop-down list on the web page for selecting - * game presets. Called at setup time if the game back end - * provides neither presets nor configurability. - */ - js_remove_type_dropdown: function() { - gametypelist.style.display = "none"; - }, - - /* - * void js_remove_solve_button(void); - * - * Get rid of the Solve button on the web page. Called at setup - * time if the game doesn't support an in-game solve function. - */ - js_remove_solve_button: function() { - document.getElementById("solve").style.display = "none"; - }, - - /* - * void js_add_preset(int menuid, const char *name, int value); - * - * Add a preset to the drop-down types menu, or to a submenu of - * it. 'menuid' specifies an index into our array of submenus - * where the item might be placed; 'value' specifies the number - * that js_get_selected_preset() will return when this item is - * clicked. - */ - js_add_preset: function(menuid, ptr, value) { - var name = Pointer_stringify(ptr); - var item = document.createElement("li"); - item.setAttribute("data-index", value); - var tick = document.createElement("span"); - tick.appendChild(document.createTextNode("\u2713")); - tick.style.color = "transparent"; - tick.style.paddingRight = "0.5em"; - item.appendChild(tick); - item.appendChild(document.createTextNode(name)); - gametypesubmenus[menuid].appendChild(item); - gametypeitems.push(item); - - item.onclick = function(event) { - if (dlg_dimmer === null) { - gametypeselectedindex = value; - command(2); - } - } - }, - - /* - * int js_add_preset_submenu(int menuid, const char *name); - * - * Add a submenu in the presets menu hierarchy. Returns its index, - * for passing as the 'menuid' argument in further calls to - * js_add_preset or this function. - */ - js_add_preset_submenu: function(menuid, ptr, value) { - var name = Pointer_stringify(ptr); - var item = document.createElement("li"); - // We still create a transparent tick element, even though it - // won't ever be selected, to make submenu titles line up - // nicely with their neighbours. - var tick = document.createElement("span"); - tick.appendChild(document.createTextNode("\u2713")); - tick.style.color = "transparent"; - tick.style.paddingRight = "0.5em"; - item.appendChild(tick); - item.appendChild(document.createTextNode(name)); - var submenu = document.createElement("ul"); - item.appendChild(submenu); - gametypesubmenus[menuid].appendChild(item); - var toret = gametypesubmenus.length; - gametypesubmenus.push(submenu); - return toret; - }, - - /* - * int js_get_selected_preset(void); - * - * Return the index of the currently selected value in the type - * dropdown. - */ - js_get_selected_preset: function() { - return gametypeselectedindex; - }, - - /* - * void js_select_preset(int n); - * - * Cause a different value to be selected in the type dropdown - * (for when the user selects values from the Custom configurer - * which turn out to exactly match a preset). - */ - js_select_preset: function(n) { - gametypeselectedindex = n; - for (var i in gametypeitems) { - var item = gametypeitems[i]; - var tick = item.firstChild; - if (item.getAttribute("data-index") == n) { - tick.style.color = "inherit"; - } else { - tick.style.color = "transparent"; - } - } - }, - - /* - * void js_get_date_64(unsigned *p); - * - * Return the current date, in milliseconds since the epoch - * (Javascript's native format), as a 64-bit integer. Used to - * invent an initial random seed for puzzle generation. - */ - js_get_date_64: function(ptr) { - var d = (new Date()).valueOf(); - setValue(ptr, d, 'i64'); - }, - - /* - * void js_update_permalinks(const char *desc, const char *seed); - * - * Update the permalinks on the web page for a new game - * description and optional random seed. desc can never be NULL, - * but seed might be (if the game was generated by entering a - * descriptive id by hand), in which case we suppress display of - * the random seed permalink. - */ - js_update_permalinks: function(desc, seed) { - desc = Pointer_stringify(desc); - permalink_desc.href = "#" + desc; - - if (seed == 0) { - permalink_seed.style.display = "none"; - } else { - seed = Pointer_stringify(seed); - permalink_seed.href = "#" + seed; - permalink_seed.style.display = "inline"; - } - }, - - /* - * void js_enable_undo_redo(int undo, int redo); - * - * Set the enabled/disabled states of the undo and redo buttons, - * after a move. - */ - js_enable_undo_redo: function(undo, redo) { - disable_menu_item(undo_button, (undo == 0)); - disable_menu_item(redo_button, (redo == 0)); - }, - - /* - * void js_activate_timer(); - * - * Start calling the C timer_callback() function every 20ms. - */ - js_activate_timer: function() { - if (timer === null) { - timer_reference_date = (new Date()).valueOf(); - timer = setInterval(function() { - var now = (new Date()).valueOf(); - timer_callback((now - timer_reference_date) / 1000.0); - timer_reference_date = now; - return true; - }, 20); - } - }, - - /* - * void js_deactivate_timer(); - * - * Stop calling the C timer_callback() function every 20ms. - */ - js_deactivate_timer: function() { - if (timer !== null) { - clearInterval(timer); - timer = null; - } - }, - - /* - * void js_canvas_start_draw(void); - * - * Prepare to do some drawing on the canvas. - */ - js_canvas_start_draw: function() { - ctx = offscreen_canvas.getContext('2d'); - update_xmin = update_xmax = update_ymin = update_ymax = undefined; - }, - - /* - * void js_canvas_draw_update(int x, int y, int w, int h); - * - * Mark a rectangle of the off-screen canvas as needing to be - * copied to the on-screen one. - */ - js_canvas_draw_update: function(x, y, w, h) { - /* - * Currently we do this in a really simple way, just by taking - * the smallest rectangle containing all updates so far. We - * could instead keep the data in a richer form (e.g. retain - * multiple smaller rectangles needing update, and only redraw - * the whole thing beyond a certain threshold) but this will - * do for now. - */ - if (update_xmin === undefined || update_xmin > x) update_xmin = x; - if (update_ymin === undefined || update_ymin > y) update_ymin = y; - if (update_xmax === undefined || update_xmax < x+w) update_xmax = x+w; - if (update_ymax === undefined || update_ymax < y+h) update_ymax = y+h; - }, - - /* - * void js_canvas_end_draw(void); - * - * Finish the drawing, by actually copying the newly drawn stuff - * to the on-screen canvas. - */ - js_canvas_end_draw: function() { - if (update_xmin !== undefined) { - var onscreen_ctx = onscreen_canvas.getContext('2d'); - onscreen_ctx.drawImage(offscreen_canvas, - update_xmin, update_ymin, - update_xmax - update_xmin, - update_ymax - update_ymin, - update_xmin, update_ymin, - update_xmax - update_xmin, - update_ymax - update_ymin); - } - ctx = null; - }, - - /* - * void js_canvas_draw_rect(int x, int y, int w, int h, - * const char *colour); - * - * Draw a rectangle. - */ - js_canvas_draw_rect: function(x, y, w, h, colptr) { - ctx.fillStyle = Pointer_stringify(colptr); - ctx.fillRect(x, y, w, h); - }, - - /* - * void js_canvas_clip_rect(int x, int y, int w, int h); - * - * Set a clipping rectangle. - */ - js_canvas_clip_rect: function(x, y, w, h) { - ctx.save(); - ctx.beginPath(); - ctx.rect(x, y, w, h); - ctx.clip(); - }, - - /* - * void js_canvas_unclip(void); - * - * Reset to no clipping. - */ - js_canvas_unclip: function() { - ctx.restore(); - }, - - /* - * void js_canvas_draw_line(float x1, float y1, float x2, float y2, - * int width, const char *colour); - * - * Draw a line. We must adjust the coordinates by 0.5 because - * Javascript's canvas coordinates appear to be pixel corners, - * whereas we want pixel centres. Also, we manually draw the pixel - * at each end of the line, which our clients will expect but - * Javascript won't reliably do by default (in common with other - * Postscriptish drawing frameworks). - */ - js_canvas_draw_line: function(x1, y1, x2, y2, width, colour) { - colour = Pointer_stringify(colour); - - ctx.beginPath(); - ctx.moveTo(x1 + 0.5, y1 + 0.5); - ctx.lineTo(x2 + 0.5, y2 + 0.5); - ctx.lineWidth = width; - ctx.lineCap = 'round'; - ctx.lineJoin = 'round'; - ctx.strokeStyle = colour; - ctx.stroke(); - ctx.fillStyle = colour; - ctx.fillRect(x1, y1, 1, 1); - ctx.fillRect(x2, y2, 1, 1); - }, - - /* - * void js_canvas_draw_poly(int *points, int npoints, - * const char *fillcolour, - * const char *outlinecolour); - * - * Draw a polygon. - */ - js_canvas_draw_poly: function(pointptr, npoints, fill, outline) { - ctx.beginPath(); - ctx.moveTo(getValue(pointptr , 'i32') + 0.5, - getValue(pointptr+4, 'i32') + 0.5); - for (var i = 1; i < npoints; i++) - ctx.lineTo(getValue(pointptr+8*i , 'i32') + 0.5, - getValue(pointptr+8*i+4, 'i32') + 0.5); - ctx.closePath(); - if (fill != 0) { - ctx.fillStyle = Pointer_stringify(fill); - ctx.fill(); - } - ctx.lineWidth = '1'; - ctx.lineCap = 'round'; - ctx.lineJoin = 'round'; - ctx.strokeStyle = Pointer_stringify(outline); - ctx.stroke(); - }, - - /* - * void js_canvas_draw_circle(int x, int y, int r, - * const char *fillcolour, - * const char *outlinecolour); - * - * Draw a circle. - */ - js_canvas_draw_circle: function(x, y, r, fill, outline) { - ctx.beginPath(); - ctx.arc(x + 0.5, y + 0.5, r, 0, 2*Math.PI); - if (fill != 0) { - ctx.fillStyle = Pointer_stringify(fill); - ctx.fill(); - } - ctx.lineWidth = '1'; - ctx.lineCap = 'round'; - ctx.lineJoin = 'round'; - ctx.strokeStyle = Pointer_stringify(outline); - ctx.stroke(); - }, - - /* - * int js_canvas_find_font_midpoint(int height, const char *fontptr); - * - * Return the adjustment required for text displayed using - * ALIGN_VCENTRE. We want to place the midpoint between the - * baseline and the cap-height at the specified position; so this - * function returns the adjustment which, when added to the - * desired centre point, returns the y-coordinate at which you - * should put the baseline. - * - * There is no sensible method of querying this kind of font - * metric in Javascript, so instead we render a piece of test text - * to a throwaway offscreen canvas and then read the pixel data - * back out to find the highest and lowest pixels. That's good - * _enough_ (in that we only needed the answer to the nearest - * pixel anyway), but rather disgusting! - * - * Since this is a very expensive operation, we cache the results - * per (font,height) pair. - */ - js_canvas_find_font_midpoint: function(height, font) { - font = Pointer_stringify(font); - - // Reuse cached value if possible - if (midpoint_cache[font] !== undefined) - return midpoint_cache[font]; - - // Find the width of the string - var ctx1 = onscreen_canvas.getContext('2d'); - ctx1.font = font; - var width = (ctx1.measureText(midpoint_test_str).width + 1) | 0; - - // Construct a test canvas of appropriate size, initialise it to - // black, and draw the string on it in white - var measure_canvas = document.createElement('canvas'); - var ctx2 = measure_canvas.getContext('2d'); - ctx2.canvas.width = width; - ctx2.canvas.height = 2*height; - ctx2.fillStyle = "#000000"; - ctx2.fillRect(0, 0, width, 2*height); - var baseline = (1.5*height) | 0; - ctx2.fillStyle = "#ffffff"; - ctx2.font = font; - ctx2.fillText(midpoint_test_str, 0, baseline); - - // Scan the contents of the test canvas to find the top and bottom - // set pixels. - var pixels = ctx2.getImageData(0, 0, width, 2*height).data; - var ymin = 2*height, ymax = -1; - for (var y = 0; y < 2*height; y++) { - for (var x = 0; x < width; x++) { - if (pixels[4*(y*width+x)] != 0) { - if (ymin > y) ymin = y; - if (ymax < y) ymax = y; - break; - } - } - } - - var ret = (baseline - (ymin + ymax) / 2) | 0; - midpoint_cache[font] = ret; - return ret; - }, - - /* - * void js_canvas_draw_text(int x, int y, int halign, - * const char *colptr, const char *fontptr, - * const char *text); - * - * Draw text. Vertical alignment has been taken care of on the C - * side, by optionally calling the above function. Horizontal - * alignment is handled here, since we can get the canvas draw - * function to do it for us with almost no extra effort. - */ - js_canvas_draw_text: function(x, y, halign, colptr, fontptr, text) { - ctx.font = Pointer_stringify(fontptr); - ctx.fillStyle = Pointer_stringify(colptr); - ctx.textAlign = (halign == 0 ? 'left' : - halign == 1 ? 'center' : 'right'); - ctx.textBaseline = 'alphabetic'; - ctx.fillText(Pointer_stringify(text), x, y); - }, - - /* - * int js_canvas_new_blitter(int w, int h); - * - * Create a new blitter object, which is just an offscreen canvas - * of the specified size. - */ - js_canvas_new_blitter: function(w, h) { - var id = blittercount++; - blitters[id] = document.createElement("canvas"); - blitters[id].width = w; - blitters[id].height = h; - return id; - }, - - /* - * void js_canvas_free_blitter(int id); - * - * Free a blitter (or rather, destroy our reference to it so JS - * can garbage-collect it, and also enforce that we don't - * accidentally use it again afterwards). - */ - js_canvas_free_blitter: function(id) { - blitters[id] = null; - }, - - /* - * void js_canvas_copy_to_blitter(int id, int x, int y, int w, int h); - * - * Copy from the puzzle image to a blitter. The size is passed to - * us, partly so we don't have to remember the size of each - * blitter, but mostly so that the C side can adjust the copy - * rectangle in the case where it partially overlaps the edge of - * the screen. - */ - js_canvas_copy_to_blitter: function(id, x, y, w, h) { - var blitter_ctx = blitters[id].getContext('2d'); - blitter_ctx.drawImage(offscreen_canvas, - x, y, w, h, - 0, 0, w, h); - }, - - /* - * void js_canvas_copy_from_blitter(int id, int x, int y, int w, int h); - * - * Copy from a blitter back to the puzzle image. As above, the - * size of the copied rectangle is passed to us from the C side - * and may already have been modified. - */ - js_canvas_copy_from_blitter: function(id, x, y, w, h) { - ctx.drawImage(blitters[id], - 0, 0, w, h, - x, y, w, h); - }, - - /* - * void js_canvas_make_statusbar(void); - * - * Cause a status bar to exist. Called at setup time if the puzzle - * back end turns out to want one. - */ - js_canvas_make_statusbar: function() { - var statusholder = document.getElementById("statusbarholder"); - statusbar = document.createElement("div"); - statusbar.style.overflow = "hidden"; - statusbar.style.width = (onscreen_canvas.width - 4) + "px"; - statusholder.style.width = onscreen_canvas.width + "px"; - statusbar.style.height = "1.2em"; - statusbar.style.textAlign = "left"; - statusbar.style.background = "#d8d8d8"; - statusbar.style.borderLeft = '2px solid #c8c8c8'; - statusbar.style.borderTop = '2px solid #c8c8c8'; - statusbar.style.borderRight = '2px solid #e8e8e8'; - statusbar.style.borderBottom = '2px solid #e8e8e8'; - statusbar.appendChild(document.createTextNode(" ")); - statusholder.appendChild(statusbar); - }, - - /* - * void js_canvas_set_statusbar(const char *text); - * - * Set the text in the status bar. - */ - js_canvas_set_statusbar: function(ptr) { - var text = Pointer_stringify(ptr); - statusbar.replaceChild(document.createTextNode(text), - statusbar.lastChild); - }, - - /* - * void js_canvas_set_size(int w, int h); - * - * Set the size of the puzzle canvas. Called at setup, and every - * time the user picks new puzzle settings requiring a different - * size. - */ - js_canvas_set_size: function(w, h) { - onscreen_canvas.width = w; - offscreen_canvas.width = w; - if (statusbar !== null) { - statusbar.style.width = (w - 4) + "px"; - document.getElementById("statusbarholder").style.width = w + "px"; - } - resizable_div.style.width = w + "px"; - - onscreen_canvas.height = h; - offscreen_canvas.height = h; - }, - - /* - * void js_dialog_init(const char *title); - * - * Begin constructing a 'dialog box' which will be popped up in an - * overlay on top of the rest of the puzzle web page. - */ - js_dialog_init: function(titletext) { - dialog_init(Pointer_stringify(titletext)); - }, - - /* - * void js_dialog_string(int i, const char *title, const char *initvalue); - * - * Add a string control (that is, an edit box) to the dialog under - * construction. - */ - js_dialog_string: function(index, title, initialtext) { - dlg_form.appendChild(document.createTextNode(Pointer_stringify(title))); - var editbox = document.createElement("input"); - editbox.type = "text"; - editbox.value = Pointer_stringify(initialtext); - dlg_form.appendChild(editbox); - dlg_form.appendChild(document.createElement("br")); - - dlg_return_funcs.push(function() { - dlg_return_sval(index, editbox.value); - }); - }, - - /* - * void js_dialog_choices(int i, const char *title, const char *choicelist, - * int initvalue); - * - * Add a choices control (i.e. a drop-down list) to the dialog - * under construction. The 'choicelist' parameter is unchanged - * from the way the puzzle back end will have supplied it: i.e. - * it's still encoded as a single string whose first character - * gives the separator. - */ - js_dialog_choices: function(index, title, choicelist, initvalue) { - dlg_form.appendChild(document.createTextNode(Pointer_stringify(title))); - var dropdown = document.createElement("select"); - var choicestr = Pointer_stringify(choicelist); - var items = choicestr.slice(1).split(choicestr[0]); - var options = []; - for (var i in items) { - var option = document.createElement("option"); - option.value = i; - option.appendChild(document.createTextNode(items[i])); - if (i == initvalue) option.selected = true; - dropdown.appendChild(option); - options.push(option); - } - dlg_form.appendChild(dropdown); - dlg_form.appendChild(document.createElement("br")); - - dlg_return_funcs.push(function() { - var val = 0; - for (var i in options) { - if (options[i].selected) { - val = options[i].value; - break; - } - } - dlg_return_ival(index, val); - }); - }, - - /* - * void js_dialog_boolean(int i, const char *title, int initvalue); - * - * Add a boolean control (a checkbox) to the dialog under - * construction. Checkboxes are generally expected to be sensitive - * on their label text as well as the box itself, so for this - * control we create an actual label rather than merely a text - * node (and hence we must allocate an id to the checkbox so that - * the label can refer to it). - */ - js_dialog_boolean: function(index, title, initvalue) { - var checkbox = document.createElement("input"); - checkbox.type = "checkbox"; - checkbox.id = "cb" + String(dlg_next_id++); - checkbox.checked = (initvalue != 0); - dlg_form.appendChild(checkbox); - var checkboxlabel = document.createElement("label"); - checkboxlabel.setAttribute("for", checkbox.id); - checkboxlabel.textContent = Pointer_stringify(title); - dlg_form.appendChild(checkboxlabel); - dlg_form.appendChild(document.createElement("br")); - - dlg_return_funcs.push(function() { - dlg_return_ival(index, checkbox.checked ? 1 : 0); - }); - }, - - /* - * void js_dialog_launch(void); - * - * Finish constructing a dialog, and actually display it, dimming - * everything else on the page. - */ - js_dialog_launch: function() { - dialog_launch(function(event) { - for (var i in dlg_return_funcs) - dlg_return_funcs[i](); - command(3); // OK - }, function(event) { - command(4); // Cancel - }); - }, - - /* - * void js_dialog_cleanup(void); - * - * Stop displaying a dialog, and clean up the internal state - * associated with it. - */ - js_dialog_cleanup: function() { - dialog_cleanup(); - }, - - /* - * void js_focus_canvas(void); - * - * Return keyboard focus to the puzzle canvas. Called after a - * puzzle-control button is pressed, which tends to have the side - * effect of taking focus away from the canvas. - */ - js_focus_canvas: function() { - onscreen_canvas.focus(); - } -}); diff --git a/apps/plugins/puzzles/src/emccpre.js b/apps/plugins/puzzles/src/emccpre.js deleted file mode 100644 index 56f69721f7..0000000000 --- a/apps/plugins/puzzles/src/emccpre.js +++ /dev/null @@ -1,500 +0,0 @@ -/* - * emccpre.js: one of the Javascript components of an Emscripten-based - * web/Javascript front end for Puzzles. - * - * The other parts of this system live in emcc.c and emcclib.js. It - * also depends on being run in the context of a web page containing - * an appropriate collection of bits and pieces (a canvas, some - * buttons and links etc), which is generated for each puzzle by the - * script html/jspage.pl. - * - * This file contains the Javascript code which is prefixed unmodified - * to Emscripten's output via the --pre-js option. It declares all our - * global variables, and provides the puzzle init function and a - * couple of other helper functions. - */ - -// To avoid flicker while doing complicated drawing, we use two -// canvases, the same size. One is actually on the web page, and the -// other is off-screen. We do all our drawing on the off-screen one -// first, and then copy rectangles of it to the on-screen canvas in -// response to draw_update() calls by the game backend. -var onscreen_canvas, offscreen_canvas; - -// A persistent drawing context for the offscreen canvas, to save -// constructing one per individual graphics operation. -var ctx; - -// Bounding rectangle for the copy to the onscreen canvas that will be -// done at drawing end time. Updated by js_canvas_draw_update and used -// by js_canvas_end_draw. -var update_xmin, update_xmax, update_ymin, update_ymax; - -// Module object for Emscripten. We fill in these parameters to ensure -// that Module.run() won't be called until we're ready (we want to do -// our own init stuff first), and that when main() returns nothing -// will get cleaned up so we remain able to call the puzzle's various -// callbacks. -var Module = { - 'noInitialRun': true, - 'noExitRuntime': true -}; - -// Variables used by js_canvas_find_font_midpoint(). -var midpoint_test_str = "ABCDEFGHIKLMNOPRSTUVWXYZ0123456789"; -var midpoint_cache = []; - -// Variables used by js_activate_timer() and js_deactivate_timer(). -var timer = null; -var timer_reference_date; - -// void timer_callback(double tplus); -// -// Called every 20ms while timing is active. -var timer_callback; - -// The status bar object, if we create one. -var statusbar = null; - -// Currently live blitters. We keep an integer id for each one on the -// JS side; the C side, which expects a blitter to look like a struct, -// simply defines the struct to contain that integer id. -var blittercount = 0; -var blitters = []; - -// State for the dialog-box mechanism. dlg_dimmer and dlg_form are the -// page-darkening overlay and the actual dialog box respectively; -// dlg_next_id is used to allocate each checkbox a unique id to use -// for linking its label to it (see js_dialog_boolean); -// dlg_return_funcs is a list of JS functions to be called when the OK -// button is pressed, to pass the results back to C. -var dlg_dimmer = null, dlg_form = null; -var dlg_next_id = 0; -var dlg_return_funcs = null; - -// void dlg_return_sval(int index, const char *val); -// void dlg_return_ival(int index, int val); -// -// C-side entry points called by functions in dlg_return_funcs, to -// pass back the final value in each dialog control. -var dlg_return_sval, dlg_return_ival; - -// The
    object implementing the game-type drop-down, and a list of -// the
  • objects inside it. Used by js_add_preset(), -// js_get_selected_preset() and js_select_preset(). -var gametypelist = null, gametypeitems = []; -var gametypeselectedindex = null; -var gametypesubmenus = []; - -// The two anchors used to give permalinks to the current puzzle. Used -// by js_update_permalinks(). -var permalink_seed, permalink_desc; - -// The undo and redo buttons. Used by js_enable_undo_redo(). -var undo_button, redo_button; - -// A div element enclosing both the puzzle and its status bar, used -// for positioning the resize handle. -var resizable_div; - -// Helper function to find the absolute position of a given DOM -// element on a page, by iterating upwards through the DOM finding -// each element's offset from its parent, and thus calculating the -// page-relative position of the target element. -function element_coords(element) { - var ex = 0, ey = 0; - while (element.offsetParent) { - ex += element.offsetLeft; - ey += element.offsetTop; - element = element.offsetParent; - } - return {x: ex, y:ey}; -} - -// Helper function which is passed a mouse event object and a DOM -// element, and returns the coordinates of the mouse event relative to -// the top left corner of the element by subtracting element_coords -// from event.page{X,Y}. -function relative_mouse_coords(event, element) { - var ecoords = element_coords(element); - return {x: event.pageX - ecoords.x, - y: event.pageY - ecoords.y}; -} - -// Enable and disable items in the CSS menus. -function disable_menu_item(item, disabledFlag) { - if (disabledFlag) - item.className = "disabled"; - else - item.className = ""; -} - -// Dialog-box functions called from both C and JS. -function dialog_init(titletext) { - // Create an overlay on the page which darkens everything - // beneath it. - dlg_dimmer = document.createElement("div"); - dlg_dimmer.style.width = "100%"; - dlg_dimmer.style.height = "100%"; - dlg_dimmer.style.background = '#000000'; - dlg_dimmer.style.position = 'fixed'; - dlg_dimmer.style.opacity = 0.3; - dlg_dimmer.style.top = dlg_dimmer.style.left = 0; - dlg_dimmer.style["z-index"] = 99; - - // Now create a form which sits on top of that in turn. - dlg_form = document.createElement("form"); - dlg_form.style.width = (window.innerWidth * 2 / 3) + "px"; - dlg_form.style.opacity = 1; - dlg_form.style.background = '#ffffff'; - dlg_form.style.color = '#000000'; - dlg_form.style.position = 'absolute'; - dlg_form.style.border = "2px solid black"; - dlg_form.style.padding = "20px"; - dlg_form.style.top = (window.innerHeight / 10) + "px"; - dlg_form.style.left = (window.innerWidth / 6) + "px"; - dlg_form.style["z-index"] = 100; - - var title = document.createElement("p"); - title.style.marginTop = "0px"; - title.appendChild(document.createTextNode(titletext)); - dlg_form.appendChild(title); - - dlg_return_funcs = []; - dlg_next_id = 0; -} - -function dialog_launch(ok_function, cancel_function) { - // Put in the OK and Cancel buttons at the bottom. - var button; - - if (ok_function) { - button = document.createElement("input"); - button.type = "button"; - button.value = "OK"; - button.onclick = ok_function; - dlg_form.appendChild(button); - } - - if (cancel_function) { - button = document.createElement("input"); - button.type = "button"; - button.value = "Cancel"; - button.onclick = cancel_function; - dlg_form.appendChild(button); - } - - document.body.appendChild(dlg_dimmer); - document.body.appendChild(dlg_form); -} - -function dialog_cleanup() { - document.body.removeChild(dlg_dimmer); - document.body.removeChild(dlg_form); - dlg_dimmer = dlg_form = null; - onscreen_canvas.focus(); -} - -// Init function called from body.onload. -function initPuzzle() { - // Construct the off-screen canvas used for double buffering. - onscreen_canvas = document.getElementById("puzzlecanvas"); - offscreen_canvas = document.createElement("canvas"); - offscreen_canvas.width = onscreen_canvas.width; - offscreen_canvas.height = onscreen_canvas.height; - - // Stop right-clicks on the puzzle from popping up a context menu. - // We need those right-clicks! - onscreen_canvas.oncontextmenu = function(event) { return false; } - - // Set up mouse handlers. We do a bit of tracking of the currently - // pressed mouse buttons, to avoid sending mousemoves with no - // button down (our puzzles don't want those events). - mousedown = Module.cwrap('mousedown', 'void', - ['number', 'number', 'number']); - - button_phys2log = [null, null, null]; - buttons_down = function() { - var i, toret = 0; - for (i = 0; i < 3; i++) - if (button_phys2log[i] !== null) - toret |= 1 << button_phys2log[i]; - return toret; - }; - - onscreen_canvas.onmousedown = function(event) { - if (event.button >= 3) - return; - - var xy = relative_mouse_coords(event, onscreen_canvas); - var logbutton = event.button; - if (event.shiftKey) - logbutton = 1; // Shift-click overrides to middle button - else if (event.ctrlKey) - logbutton = 2; // Ctrl-click overrides to right button - - mousedown(xy.x, xy.y, logbutton); - button_phys2log[event.button] = logbutton; - - onscreen_canvas.setCapture(true); - }; - mousemove = Module.cwrap('mousemove', 'void', - ['number', 'number', 'number']); - onscreen_canvas.onmousemove = function(event) { - var down = buttons_down(); - if (down) { - var xy = relative_mouse_coords(event, onscreen_canvas); - mousemove(xy.x, xy.y, down); - } - }; - mouseup = Module.cwrap('mouseup', 'void', - ['number', 'number', 'number']); - onscreen_canvas.onmouseup = function(event) { - if (event.button >= 3) - return; - - if (button_phys2log[event.button] !== null) { - var xy = relative_mouse_coords(event, onscreen_canvas); - mouseup(xy.x, xy.y, button_phys2log[event.button]); - button_phys2log[event.button] = null; - } - }; - - // Set up keyboard handlers. We do all the actual keyboard - // handling in onkeydown; but we also call event.preventDefault() - // in both the keydown and keypress handlers. This means that - // while the canvas itself has focus, _all_ keypresses go only to - // the puzzle - so users of this puzzle collection in other media - // can indulge their instinct to press ^R for redo, for example, - // without accidentally reloading the page. - key = Module.cwrap('key', 'void', ['number', 'number', 'string', - 'string', 'number', 'number']); - onscreen_canvas.onkeydown = function(event) { - key(event.keyCode, event.charCode, event.key, event.char, - event.shiftKey ? 1 : 0, event.ctrlKey ? 1 : 0); - event.preventDefault(); - }; - onscreen_canvas.onkeypress = function(event) { - event.preventDefault(); - }; - - // command() is a C function called to pass back events which - // don't fall into other categories like mouse and key events. - // Mostly those are button presses, but there's also one for the - // game-type dropdown having been changed. - command = Module.cwrap('command', 'void', ['number']); - - // Event handlers for buttons and things, which call command(). - document.getElementById("specific").onclick = function(event) { - // Ensure we don't accidentally process these events when a - // dialog is actually active, e.g. because the button still - // has keyboard focus - if (dlg_dimmer === null) - command(0); - }; - document.getElementById("random").onclick = function(event) { - if (dlg_dimmer === null) - command(1); - }; - document.getElementById("new").onclick = function(event) { - if (dlg_dimmer === null) - command(5); - }; - document.getElementById("restart").onclick = function(event) { - if (dlg_dimmer === null) - command(6); - }; - undo_button = document.getElementById("undo"); - undo_button.onclick = function(event) { - if (dlg_dimmer === null) - command(7); - }; - redo_button = document.getElementById("redo"); - redo_button.onclick = function(event) { - if (dlg_dimmer === null) - command(8); - }; - document.getElementById("solve").onclick = function(event) { - if (dlg_dimmer === null) - command(9); - }; - - // 'number' is used for C pointers - get_save_file = Module.cwrap('get_save_file', 'number', []); - free_save_file = Module.cwrap('free_save_file', 'void', ['number']); - load_game = Module.cwrap('load_game', 'void', ['string', 'number']); - - document.getElementById("save").onclick = function(event) { - if (dlg_dimmer === null) { - var savefile_ptr = get_save_file(); - var savefile_text = Pointer_stringify(savefile_ptr); - free_save_file(savefile_ptr); - dialog_init("Download saved-game file"); - dlg_form.appendChild(document.createTextNode( - "Click to download the ")); - var a = document.createElement("a"); - a.download = "puzzle.sav"; - a.href = "data:application/octet-stream," + - encodeURIComponent(savefile_text); - a.appendChild(document.createTextNode("saved-game file")); - dlg_form.appendChild(a); - dlg_form.appendChild(document.createTextNode(".")); - dlg_form.appendChild(document.createElement("br")); - dialog_launch(function(event) { - dialog_cleanup(); - }); - } - }; - - document.getElementById("load").onclick = function(event) { - if (dlg_dimmer === null) { - dialog_init("Upload saved-game file"); - var input = document.createElement("input"); - input.type = "file"; - input.multiple = false; - dlg_form.appendChild(input); - dlg_form.appendChild(document.createElement("br")); - dialog_launch(function(event) { - if (input.files.length == 1) { - var file = input.files.item(0); - var reader = new FileReader(); - reader.addEventListener("loadend", function() { - var string = reader.result; - load_game(string, string.length); - }); - reader.readAsBinaryString(file); - } - dialog_cleanup(); - }, function(event) { - dialog_cleanup(); - }); - } - }; - - gametypelist = document.getElementById("gametype"); - gametypesubmenus.push(gametypelist); - - // In IE, the canvas doesn't automatically gain focus on a mouse - // click, so make sure it does - onscreen_canvas.addEventListener("mousedown", function(event) { - onscreen_canvas.focus(); - }); - - // In our dialog boxes, Return and Escape should be like pressing - // OK and Cancel respectively - document.addEventListener("keydown", function(event) { - - if (dlg_dimmer !== null && event.keyCode == 13) { - for (var i in dlg_return_funcs) - dlg_return_funcs[i](); - command(3); - } - - if (dlg_dimmer !== null && event.keyCode == 27) - command(4); - }); - - // Set up the function pointers we haven't already grabbed. - dlg_return_sval = Module.cwrap('dlg_return_sval', 'void', - ['number','string']); - dlg_return_ival = Module.cwrap('dlg_return_ival', 'void', - ['number','number']); - timer_callback = Module.cwrap('timer_callback', 'void', ['number']); - - // Save references to the two permalinks. - permalink_desc = document.getElementById("permalink-desc"); - permalink_seed = document.getElementById("permalink-seed"); - - // Default to giving keyboard focus to the puzzle. - onscreen_canvas.focus(); - - // Create the resize handle. - var resize_handle = document.createElement("canvas"); - resize_handle.width = 10; - resize_handle.height = 10; - { - var ctx = resize_handle.getContext("2d"); - ctx.beginPath(); - for (var i = 1; i <= 7; i += 3) { - ctx.moveTo(8.5, i + 0.5); - ctx.lineTo(i + 0.5, 8.5); - } - ctx.lineWidth = '1px'; - ctx.lineCap = 'round'; - ctx.lineJoin = 'round'; - ctx.strokeStyle = '#000000'; - ctx.stroke(); - } - resizable_div = document.getElementById("resizable"); - resizable_div.appendChild(resize_handle); - resize_handle.style.position = 'absolute'; - resize_handle.style.zIndex = 98; - resize_handle.style.bottom = "0"; - resize_handle.style.right = "0"; - resize_handle.style.cursor = "se-resize"; - resize_handle.title = "Drag to resize the puzzle. Right-click to restore the default size."; - var resize_xbase = null, resize_ybase = null, restore_pending = false; - var resize_xoffset = null, resize_yoffset = null; - var resize_puzzle = Module.cwrap('resize_puzzle', - 'void', ['number', 'number']); - var restore_puzzle_size = Module.cwrap('restore_puzzle_size', 'void', []); - resize_handle.oncontextmenu = function(event) { return false; } - resize_handle.onmousedown = function(event) { - if (event.button == 0) { - var xy = element_coords(onscreen_canvas); - resize_xbase = xy.x + onscreen_canvas.width / 2; - resize_ybase = xy.y; - resize_xoffset = xy.x + onscreen_canvas.width - event.pageX; - resize_yoffset = xy.y + onscreen_canvas.height - event.pageY; - } else { - restore_pending = true; - } - resize_handle.setCapture(true); - event.preventDefault(); - }; - window.addEventListener("mousemove", function(event) { - if (resize_xbase !== null && resize_ybase !== null) { - resize_puzzle((event.pageX + resize_xoffset - resize_xbase) * 2, - (event.pageY + resize_yoffset - resize_ybase)); - event.preventDefault(); - // Chrome insists on selecting text during a resize drag - // no matter what I do - if (window.getSelection) - window.getSelection().removeAllRanges(); - else - document.selection.empty(); } - }); - window.addEventListener("mouseup", function(event) { - if (resize_xbase !== null && resize_ybase !== null) { - resize_xbase = null; - resize_ybase = null; - onscreen_canvas.focus(); // return focus to the puzzle - event.preventDefault(); - } else if (restore_pending) { - // If you have the puzzle at larger than normal size and - // then right-click to restore, I haven't found any way to - // stop Chrome and IE popping up a context menu on the - // revealed piece of document when you release the button - // except by putting the actual restore into a setTimeout. - // Gah. - setTimeout(function() { - restore_pending = false; - restore_puzzle_size(); - onscreen_canvas.focus(); - }, 20); - event.preventDefault(); - } - }); - - // Run the C setup function, passing argv[1] as the fragment - // identifier (so that permalinks of the form puzzle.html#game-id - // can launch the specified id). - Module.callMain([location.hash]); - - // And if we get here with everything having gone smoothly, i.e. - // we haven't crashed for one reason or another during setup, then - // it's probably safe to hide the 'sorry, no puzzle here' div and - // show the div containing the actual puzzle. - document.getElementById("apology").style.display = "none"; - document.getElementById("puzzle").style.display = "inline"; -} diff --git a/apps/plugins/puzzles/src/emccx.json b/apps/plugins/puzzles/src/emccx.json deleted file mode 100644 index bdab346d79..0000000000 --- a/apps/plugins/puzzles/src/emccx.json +++ /dev/null @@ -1,33 +0,0 @@ -// -*- js -*- -// -// List of entry points exported by the C side of the Emscripten -// puzzle builds. Passed in to emcc via the option '-s -// EXPORTED_FUNCTIONS=[list]'. -// -// This file isn't actually a valid list in its current state, since -// emcc doesn't like comments or newlines. However, it's a nicer -// source form to keep the comments and newlines in, so we sed them -// away at compile time. -[ - // Event handlers for mouse and keyboard input - '_mouseup', - '_mousedown', - '_mousemove', - '_key', - // Callback when the program activates timing - '_timer_callback', - // Callback from button presses in the UI outside the canvas - '_command', - // Game-saving and game-loading functions - '_get_save_file', - '_free_save_file', - '_load_game', - // Callbacks to return values from dialog boxes - '_dlg_return_sval', - '_dlg_return_ival', - // Callbacks when the resizing controls are used - '_resize_puzzle', - '_restore_puzzle_size', - // Main program, run at initialisation time - '_main' -] diff --git a/apps/plugins/puzzles/src/html/blackbox.html b/apps/plugins/puzzles/src/html/blackbox.html deleted file mode 100644 index f98604f12f..0000000000 --- a/apps/plugins/puzzles/src/html/blackbox.html +++ /dev/null @@ -1,16 +0,0 @@ -Black Box -

    -Determine where the hidden balls are in the box, by observing the -behaviour of light beams fired into the box from the sides. -

    -Click in a square around the edge of the box to send a beam into the -box. Possible results are 'H' (the beam hit a ball dead-on and -stopped), 'R' (the beam was either reflected back the way it came or -there was a ball just to one side of its entry point) or a number -appearing in two squares (indicating that the beam entered one of -those squares and emerged from the other). -

    -Click in the middle of the box to place your guessed ball positions. -When you have placed enough, a green button will appear in the top -left; click that to indicate that you think you have the answer. -You can also right-click to mark squares as definitely known. diff --git a/apps/plugins/puzzles/src/html/bridges.html b/apps/plugins/puzzles/src/html/bridges.html deleted file mode 100644 index 06ec2d4d4b..0000000000 --- a/apps/plugins/puzzles/src/html/bridges.html +++ /dev/null @@ -1,13 +0,0 @@ -Bridges -

    -Draw horizontal or vertical bridges to link up all the islands. -Bridges may be single or double; they may not cross; the islands -must all end up connected to each other; the number in each island -must match the number of bridges that end at that island (counting -double bridges as two). Note that loops of bridges are permitted. -

    -Click on an island and drag left, right, up or down to draw a bridge -to the next island in that direction. Do the same again to create a -double bridge, and again to remove the bridge if you change your -mind. Click on an island without dragging to mark the island as -completed once you think you have placed all its bridges. diff --git a/apps/plugins/puzzles/src/html/cube.html b/apps/plugins/puzzles/src/html/cube.html deleted file mode 100644 index f08e16c38d..0000000000 --- a/apps/plugins/puzzles/src/html/cube.html +++ /dev/null @@ -1,14 +0,0 @@ -Cube -

    -Roll the cube around the grid, picking up the blue squares on its -faces. Try to get all the blue squares on to the object at the same -time, in as few moves as possible. -

    -Use the arrow keys to roll the cube, or click the mouse where you -want it to roll towards. After every roll, the grid square and cube -face that you brought into contact swap their colours, so that a -non-blue cube face can pick up a blue square, but a blue face rolled -on to a non-blue square puts it down again. -

    -When you have mastered the cube, use the Type menu to select other -regular solids! diff --git a/apps/plugins/puzzles/src/html/dominosa.html b/apps/plugins/puzzles/src/html/dominosa.html deleted file mode 100644 index d2f672806a..0000000000 --- a/apps/plugins/puzzles/src/html/dominosa.html +++ /dev/null @@ -1,10 +0,0 @@ -Dominosa -

    -Tile the rectangle with dominoes (1×2 rectangles) so that -every possible domino appears exactly once (that is, every possible -pair of numbers, including doubles). -

    -Click between two adjacent numbers to place or remove a domino. -Right-click to place a line between numbers if you think a domino -definitely cannot go there. Dominoes light up red if two identical -ones appear on the grid. diff --git a/apps/plugins/puzzles/src/html/fifteen.html b/apps/plugins/puzzles/src/html/fifteen.html deleted file mode 100644 index 53053b440d..0000000000 --- a/apps/plugins/puzzles/src/html/fifteen.html +++ /dev/null @@ -1,6 +0,0 @@ -Fifteen -

    -Slide the tiles around the box until they appear in numerical order -from the top left, with the hole in the bottom right corner. -

    -Click on a tile to slide it towards the hole. diff --git a/apps/plugins/puzzles/src/html/filling.html b/apps/plugins/puzzles/src/html/filling.html deleted file mode 100644 index 70ce16d4a9..0000000000 --- a/apps/plugins/puzzles/src/html/filling.html +++ /dev/null @@ -1,12 +0,0 @@ -Filling -

    -Write a number in every blank square of the grid. When the grid is -full, every orthogonally connected group of identical numbers should -have an area equal to that number: so 1s always appear alone, 2s in -pairs, and so on. -

    -To place a number, click the mouse in a blank square to select it, -then type the number you want on the keyboard. You can also drag to -select multiple squares, and then type a number to place it in all -of them. To erase numbers, select one or more squares in the same -way and then press Backspace. diff --git a/apps/plugins/puzzles/src/html/flip.html b/apps/plugins/puzzles/src/html/flip.html deleted file mode 100644 index 404aae6b9a..0000000000 --- a/apps/plugins/puzzles/src/html/flip.html +++ /dev/null @@ -1,10 +0,0 @@ -Flip -

    -Try to light up all the squares in the grid by flipping combinations -of them. -

    -Click in a square to flip it and some of its neighbours. The diagram -in each square indicates which other squares will flip. -

    -Select one of the 'Random' settings from the Type menu for more -varied puzzles. diff --git a/apps/plugins/puzzles/src/html/flood.html b/apps/plugins/puzzles/src/html/flood.html deleted file mode 100644 index cf09eac766..0000000000 --- a/apps/plugins/puzzles/src/html/flood.html +++ /dev/null @@ -1,8 +0,0 @@ -Flood -

    -Try to get the whole grid to be the same colour within the given -number of moves, by repeatedly flood-filling the top left corner in -different colours. -

    -Click in a square to flood-fill the top left corner with that square's -colour. diff --git a/apps/plugins/puzzles/src/html/galaxies.html b/apps/plugins/puzzles/src/html/galaxies.html deleted file mode 100644 index 32e4e1d14c..0000000000 --- a/apps/plugins/puzzles/src/html/galaxies.html +++ /dev/null @@ -1,15 +0,0 @@ -Galaxies -

    -Draw lines along grid edges so as to divide the grid up into connected -regions of squares. -

    -Every region should have two-way rotational symmetry, should contain -exactly one dot which is in its centre, and should contain no lines -separating two of its own squares from each other. A region satisfying -all of these requirements will be automatically highlighted. -

    -Click on a grid edge to add or remove a line. Right-click on a dot -and drag the mouse to place an arrow in a grid square pointing to -that dot, to indicate that you think that square must belong in the -same region as that dot. Right-drag an existing arrow to move it, or -drop it off the edge of the grid to remove it. diff --git a/apps/plugins/puzzles/src/html/group.html b/apps/plugins/puzzles/src/html/group.html deleted file mode 100644 index c0f52de629..0000000000 --- a/apps/plugins/puzzles/src/html/group.html +++ /dev/null @@ -1,52 +0,0 @@ -unfinished:Group -

    -Fill in the grid with the letters shown to the top and left of it, so -that the full grid is a valid -Cayley table -for a -group. -

    -If you don't already know what a group is, I don't really recommend -trying to play this game. But if you want to try anyway, the above is -equivalent to saying that the following conditions must be satisfied: -

      -
    • -Latin square. Every row and column must contain -exactly one of each letter. -
    • -Identity. There must be some letter e such -that, for all a, the letter in row e column a and -the one in row a column e are both a. In the -default mode, this letter is always e and its row and column -are filled in for you; by reconfiguring the game using the Type menu, -you can select a mode in which you have to work out which letter is -the identity. -
    • -Inverses. For every letter a, there must be -some letter b (which may sometimes be the same letter -as a) such that the letters in row a column b and -in row b column a are both the identity letter (as -defined above). -
    • -Associativity. For every combination of -letters a, b, and c, denote the letter in -row a column b by d, and the one in row b -column c by e. Then the letters in row d -column c and in row a column e must be the same. -
    -

    -To place a letter, click in a square to select it, then type the -letter on the keyboard. To erase a letter, click to select a square -and then press Backspace. -

    -Right-click in a square and then type a letter to add or remove the -number as a pencil mark, indicating letters that you think -might go in that square. -

    -You can rearrange the order of elements in the rows and columns by -dragging the column or row headings back and forth. (The rows and -columns will stay in sync with each other.) Also, -left-clicking between two row or column headings will add or -remove a thick line between those two rows and the corresponding pair -of columns (which is useful if you're considering a subgroup and its -cosets). diff --git a/apps/plugins/puzzles/src/html/guess.html b/apps/plugins/puzzles/src/html/guess.html deleted file mode 100644 index 1e4fc1ed04..0000000000 --- a/apps/plugins/puzzles/src/html/guess.html +++ /dev/null @@ -1,12 +0,0 @@ -Guess -

    -Try to guess the hidden combination of colours. You will be given -limited information about each guess you make, enabling you to -refine the next guess. -

    -Drag from the colours on the left into the topmost unfilled row to -make a guess; then click on the small circles to submit that guess. -The small circles give you your feedback: black pegs indicate how -many of the colours you guessed were the right colour in the right -place, and white pegs indicate how many of the rest were the right -colours but in the wrong place. diff --git a/apps/plugins/puzzles/src/html/inertia.html b/apps/plugins/puzzles/src/html/inertia.html deleted file mode 100644 index 20077c0048..0000000000 --- a/apps/plugins/puzzles/src/html/inertia.html +++ /dev/null @@ -1,14 +0,0 @@ -Inertia -

    -Slide the ball around the grid picking up the gems. Every time the -ball moves, it will keep sliding until it either hits a wall, or -stops on a stop square (the broken circles). Try to collect every -gem without running into any of the mines. -

    -Use the numeric keypad to slide the ball horizontally, vertically or -diagonally. Alternatively, click on the grid to make the ball move -towards where you clicked. -

    -If you hit a mine and explode, you can select Undo from the Game -menu and continue playing; the game will track how many times you -died. diff --git a/apps/plugins/puzzles/src/html/javapage.pl b/apps/plugins/puzzles/src/html/javapage.pl deleted file mode 100755 index cd5e6a1669..0000000000 --- a/apps/plugins/puzzles/src/html/javapage.pl +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -open my $footerfile, "<", shift @ARGV or die "footer: open: $!\n"; -my $footer = ""; -$footer .= $_ while <$footerfile>; -close $footerfile; - -for my $arg (@ARGV) { - $arg =~ /(.*\/)?([^\/]+)\.html$/ or die; - my $filename = $2; - open my $gamefile, "<", $arg or die "$arg: open: $!\n"; - my $unfinished = 0; - my $docname = $filename; - chomp(my $puzzlename = <$gamefile>); - while ($puzzlename =~ s/^([^:=]+)(=([^:]+))?://) { - if ($1 eq "unfinished") { - $unfinished = 1; - } elsif ($1 eq "docname") { - $docname = $3; - } else { - die "$arg: unknown keyword '$1'\n"; - } - } - my $instructions = ""; - $instructions .= $_ while <$gamefile>; - close $gamefile; - - open my $outpage, ">", "${filename}.html"; - - my $unfinishedtitlefragment = $unfinished ? "an unfinished puzzle " : ""; - my $unfinishedheading = $unfinished ? "

    an unfinished puzzle

    \n" : ""; - my $unfinishedpara; - my $links; - if ($unfinished) { - $unfinishedpara = < -You have found your way to a page containing an unfinished -puzzle in my collection, not linked from the main -puzzles page. Don't be surprised if things are hard to understand -or don't work as you expect. -EOF - $links = < -Back to main puzzles page (which does not link to this) -EOF - } else { - $unfinishedpara = ""; - $links = < -Full instructions -| -Back to main puzzles page -EOF - } - - print $outpage < - -${puzzlename}, ${unfinishedtitlefragment}from Simon Tatham's Portable Puzzle Collection - - - - -

    ${puzzlename}

    -${unfinishedheading} -

    from Simon Tatham's Portable Puzzle Collection

    - -${unfinishedpara} - -

    -

Preset
", &escape($preset), "
- - - - - - - -
- - - -
-
-
-
-
-
- -${instructions} - -${links} - -${footer} - - -EOF - - close $outpage; -} diff --git a/apps/plugins/puzzles/src/html/jspage.pl b/apps/plugins/puzzles/src/html/jspage.pl deleted file mode 100755 index b409783f15..0000000000 --- a/apps/plugins/puzzles/src/html/jspage.pl +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -my $jspath = ""; -while ($ARGV[0] =~ /^-/) { - my $opt = shift @ARGV; - last if $opt eq "--"; - if ($opt =~ /^--jspath=(.+)$/) { - $jspath = $1; - } else { - die "jspage.pl: unrecognised option '$opt'\n"; - } -} - -open my $footerfile, "<", shift @ARGV or die "footer: open: $!\n"; -my $footer = ""; -$footer .= $_ while <$footerfile>; -close $footerfile; - -for my $arg (@ARGV) { - $arg =~ /(.*\/)?([^\/]+)\.html$/ or die; - my $filename = $2; - open my $gamefile, "<", $arg or die "$arg: open: $!\n"; - my $unfinished = 0; - my $docname = $filename; - chomp(my $puzzlename = <$gamefile>); - while ($puzzlename =~ s/^([^:=]+)(=([^:]+))?://) { - if ($1 eq "unfinished") { - $unfinished = 1; - } elsif ($1 eq "docname") { - $docname = $3; - } else { - die "$arg: unknown keyword '$1'\n"; - } - } - my $instructions = ""; - $instructions .= $_ while <$gamefile>; - close $gamefile; - - open my $outpage, ">", "${filename}.html"; - - my $unfinishedtitlefragment = $unfinished ? "an unfinished puzzle " : ""; - my $unfinishedheading = $unfinished ? "

an unfinished puzzle

\n" : ""; - my $unfinishedpara; - my $links; - if ($unfinished) { - $unfinishedpara = < -You have found your way to a page containing an unfinished -puzzle in my collection, not linked from the main -puzzles page. Don't be surprised if things are hard to understand -or don't work as you expect. -EOF - $links = < -Back to main puzzles page (which does not link to this) -EOF - } else { - $unfinishedpara = ""; - $links = < -Full instructions -| -Back to main puzzles page -EOF - } - - print $outpage < - - - -${puzzlename}, ${unfinishedtitlefragment}from Simon Tatham's Portable Puzzle Collection - - - - -

${puzzlename}

-${unfinishedheading} -

from Simon Tatham's Portable Puzzle Collection

- -${unfinishedpara} - -
- -
-Sorry, this Javascript puzzle doesn't seem to work in your web -browser. Perhaps you have Javascript disabled, or perhaps your browser -doesn't provide a feature that the puzzle code requires (such as -typed arrays). -These puzzles have been successfully run in Firefox 19, Chrome 26, -Internet Explorer 10 and Safari 6. -
-
- -${instructions} - -${links} - -${footer} - - -EOF - - close $outpage; -} diff --git a/apps/plugins/puzzles/src/html/keen.html b/apps/plugins/puzzles/src/html/keen.html deleted file mode 100644 index bd0eb3644d..0000000000 --- a/apps/plugins/puzzles/src/html/keen.html +++ /dev/null @@ -1,15 +0,0 @@ -Keen -

-Fill in the grid with digits from 1 to the grid size, so that every -digit appears exactly once in each row and column, and so that all -the arithmetic clues are satisfied (i.e. the clue number in each -thick box should be possible to construct from the digits in the box -using the specified arithmetic operation). -

-To place a number, click in a square to select it, then type the -number on the keyboard. To erase a number, click to select a square -and then press Backspace. -

-Right-click in a square and then type a number to add or remove the -number as a pencil mark, indicating numbers that you think -might go in that square. diff --git a/apps/plugins/puzzles/src/html/lightup.html b/apps/plugins/puzzles/src/html/lightup.html deleted file mode 100644 index 2de2f91bb9..0000000000 --- a/apps/plugins/puzzles/src/html/lightup.html +++ /dev/null @@ -1,10 +0,0 @@ -Light Up -

-Place light bulbs in the grid so as to light up all the blank -squares. A light illuminates its own square and all the squares in -the same row or column unless blocked by walls (black squares). -Lights may not illuminate each other. Each numbered square must be -orthogonally adjacent to exactly the given number of lights. -

-Click on a square to place or remove a light. Right-click to place a -dot indicating that you think there is no light in that square. diff --git a/apps/plugins/puzzles/src/html/loopy.html b/apps/plugins/puzzles/src/html/loopy.html deleted file mode 100644 index 96f3a9d908..0000000000 --- a/apps/plugins/puzzles/src/html/loopy.html +++ /dev/null @@ -1,13 +0,0 @@ -Loopy -

-Form a single closed loop out of the grid edges, in such a way that -every numbered square has exactly that many of its edges included in -the loop. -

-Click on a grid edge to mark it as part of the loop (black), and -again to return to marking it as undecided (yellow). Right-click on -a grid edge to mark it as definitely not part of the loop (faint -grey), and again to mark it as undecided again. -

-When you have mastered the square grid, look in the Type menu for -many other types of tiling! diff --git a/apps/plugins/puzzles/src/html/magnets.html b/apps/plugins/puzzles/src/html/magnets.html deleted file mode 100644 index 2807569a6c..0000000000 --- a/apps/plugins/puzzles/src/html/magnets.html +++ /dev/null @@ -1,17 +0,0 @@ -Magnets -

-Fill each domino shape with either a magnet (consisting of a + and -− pole) or a neutral domino (green). -

-The number of + poles that in each row and column must match the -numbers along the top and left; the number of − poles must -match the numbers along the bottom and right. Two + poles may not be -orthogonally adjacent to each other, and similarly two − poles. -

-Left-click a domino to toggle it between being empty and being a -magnet (the + is placed in the end you click). Right-click to toggle -between empty, neutral, and a ?? mark indicating that you're sure -it's a magnet but don't yet know which way round it goes. -

-Left-click a clue to mark it as done (grey it out). To unmark a clue -as done, left-click it again. diff --git a/apps/plugins/puzzles/src/html/map.html b/apps/plugins/puzzles/src/html/map.html deleted file mode 100644 index 5f81793054..0000000000 --- a/apps/plugins/puzzles/src/html/map.html +++ /dev/null @@ -1,15 +0,0 @@ -Map -

-Colour the map with four colours, so that no two adjacent regions -have the same colour. (Regions touching at only one corner do not -count as adjacent.) There is a unique colouring consistent with the -coloured regions you are already given. -

-Drag from a coloured region to a blank one to colour the latter the -same colour as the former. Drag from outside the grid into a region -to erase its colour. (You cannot change the colours of the regions -you are given at the start of the game.) -

-Right-drag from a coloured region to a blank one to add dots marking -the latter region as possibly the same colour as the -former, or to remove those dots again. diff --git a/apps/plugins/puzzles/src/html/mines.html b/apps/plugins/puzzles/src/html/mines.html deleted file mode 100644 index d17d6ffa80..0000000000 --- a/apps/plugins/puzzles/src/html/mines.html +++ /dev/null @@ -1,18 +0,0 @@ -Mines -

-Try to expose every square in the grid that is not one of the hidden -mines, without opening any square that is a mine. -

-Click in a square to open it. Every opened square are marked with -the number of mines in the surrounding 8 squares, if there are any; -if not, all the surrounding squares are automatically opened. -

-Right-click in a square to mark it with a flag if you think it is a -mine. If a numbered square has exactly the right number of flags -around it, you can click in it to open all the squares around it -that are not flagged. -

-The first square you open is guaranteed to be safe, and (by default) -you are guaranteed to be able to solve the whole grid by deduction -rather than guesswork. (Deductions may require you to think about -the total number of mines.) diff --git a/apps/plugins/puzzles/src/html/net.html b/apps/plugins/puzzles/src/html/net.html deleted file mode 100644 index 08bffbac3e..0000000000 --- a/apps/plugins/puzzles/src/html/net.html +++ /dev/null @@ -1,17 +0,0 @@ -Net -

-Rotate the grid squares so that they all join up into a single -connected network with no loops. -

-Left-click in a square to rotate it anticlockwise. Right-click to -rotate it clockwise. Middle-click, or shift-left-click if you have -no middle mouse button, to lock a square once you think it is -correct (so you don't accidentally rotate it again); do the same -again to unlock it if you change your mind. -

-Squares connected to the middle square are lit up. Aim to light up -every square in the grid (not just the endpoint blobs). -

-When this gets too easy, select a 'wrapping' variant from the Type -menu to enable grid lines to run off one edge of the playing area -and come back on the opposite edge! diff --git a/apps/plugins/puzzles/src/html/netslide.html b/apps/plugins/puzzles/src/html/netslide.html deleted file mode 100644 index f1877417d4..0000000000 --- a/apps/plugins/puzzles/src/html/netslide.html +++ /dev/null @@ -1,14 +0,0 @@ -Netslide -

-Slide the grid squares around so that they all join up into a single -connected network with no loops. -

-Click on the arrows at the edges of the grid to move a row or column -left, right, up or down. The square that falls off the end of the -row comes back on the other end. -

-Squares connected to the middle square are lit up. Aim to light up -every square in the grid (not just the endpoint blobs). -

-Connecting across a red barrier line is forbidden. On harder levels, -there are fewer barriers, which makes it harder rather than easier! diff --git a/apps/plugins/puzzles/src/html/palisade.html b/apps/plugins/puzzles/src/html/palisade.html deleted file mode 100644 index 5b6b933104..0000000000 --- a/apps/plugins/puzzles/src/html/palisade.html +++ /dev/null @@ -1,11 +0,0 @@ -Palisade -

-Draw lines along the grid edges, in such a way that the grid is -divided into connected regions, all of the size shown in the status -line. Also, each square containing a number should have that many of -its edges drawn in. -

-Click on a grid edge to mark it as a division between regions (black), -and again to return to marking it as undecided (yellow). Right-click -on a grid edge to mark it as definitely not part of the loop (faint -grey), and again to mark it as undecided again. diff --git a/apps/plugins/puzzles/src/html/pattern.html b/apps/plugins/puzzles/src/html/pattern.html deleted file mode 100644 index 54e05d6416..0000000000 --- a/apps/plugins/puzzles/src/html/pattern.html +++ /dev/null @@ -1,12 +0,0 @@ -Pattern -

-Fill in the grid with a pattern of black and white squares, so that -the numbers in each row and column match the lengths of consecutive -runs of black squares. -

-Left-click in a square to mark it black; right-click (or hold Ctrl -while left-clicking) to mark it white. Click and drag along a row or -column to mark multiple squares black or white at once. Middle-click -(or hold Shift while left-clicking) to return a square to grey -(meaning undecided): dragging like that can erase a whole rectangle, -not just a row or column. diff --git a/apps/plugins/puzzles/src/html/pearl.html b/apps/plugins/puzzles/src/html/pearl.html deleted file mode 100644 index 2ca25a5ee0..0000000000 --- a/apps/plugins/puzzles/src/html/pearl.html +++ /dev/null @@ -1,13 +0,0 @@ -Pearl -

-Draw a single closed loop by connecting together the centres of -adjacent grid squares, so that some squares end up as corners, some as -straights (horizontal or vertical), and some may be empty. Every -square containing a black circle must be a corner not connected -directly to another corner; every square containing a white circle -must be a straight which is connected to at least one corner. -

-Drag between squares to draw or undraw pieces of the loop. -Alternatively, left-click the edge between two squares to turn it on -or off. Right-click an edge to mark it with a cross indicating that -you are sure the loop does not go through it. diff --git a/apps/plugins/puzzles/src/html/pegs.html b/apps/plugins/puzzles/src/html/pegs.html deleted file mode 100644 index 4a2378873e..0000000000 --- a/apps/plugins/puzzles/src/html/pegs.html +++ /dev/null @@ -1,8 +0,0 @@ -Pegs -

-Jump one peg over another to remove the one you jumped over. Try to -remove all but one peg. -

-Drag a peg into an empty space to make a move. The target space must -be exactly two holes away from the starting peg, in an orthogonal -direction, and there must be a peg in the hole in between. diff --git a/apps/plugins/puzzles/src/html/range.html b/apps/plugins/puzzles/src/html/range.html deleted file mode 100644 index bb5b59c4d2..0000000000 --- a/apps/plugins/puzzles/src/html/range.html +++ /dev/null @@ -1,21 +0,0 @@ -Range -

-Colour some squares black, so as to meet the following conditions: -

    -
  • -No two black squares are orthogonally adjacent. -
  • -No group of white squares is separated from the rest of the grid by -black squares. -
  • -Each numbered cell can see precisely that many white squares in -total by looking in all four orthogonal directions, counting itself. -(Black squares block the view. So, for example, a 2 clue must be -adjacent to three black squares or grid edges, and in the fourth -direction there must be one white square and then a black one beyond -it.) -
- -

-Left-click to colour a square black. Right-click to mark a square -with a dot, if you know it should not be black. diff --git a/apps/plugins/puzzles/src/html/rect.html b/apps/plugins/puzzles/src/html/rect.html deleted file mode 100644 index 4cd22db70c..0000000000 --- a/apps/plugins/puzzles/src/html/rect.html +++ /dev/null @@ -1,10 +0,0 @@ -docname=rect:Rectangles -

-Draw lines along the grid edges to divide the grid into rectangles, -so that each rectangle contains exactly one numbered square and its -area is equal to the number written in that square. -

-Click and drag from one grid corner to another, or from one square -centre to another, to draw a rectangle. You can also drag along a -grid line to just draw a line at a time, or just click on a single -grid edge to draw or erase it. diff --git a/apps/plugins/puzzles/src/html/samegame.html b/apps/plugins/puzzles/src/html/samegame.html deleted file mode 100644 index e6de095210..0000000000 --- a/apps/plugins/puzzles/src/html/samegame.html +++ /dev/null @@ -1,14 +0,0 @@ -Same Game -

-Try to empty the playing area completely, by removing connected -groups of two or more squares of the same colour. Then try to score -as much as possible, by removing large groups at a time instead of -small ones. -

-Click on a coloured square to highlight the rest of its connected -group. The status line will print the number of squares selected, -and the score you would gain by removing them. Click again to remove -the group; other squares will fall down to fill the space, and if -you empty a whole column then the other columns will move up. You -cannot remove a single isolated square: try to avoid dead-end -positions where all remaining squares are isolated. diff --git a/apps/plugins/puzzles/src/html/signpost.html b/apps/plugins/puzzles/src/html/signpost.html deleted file mode 100644 index fa23e99de0..0000000000 --- a/apps/plugins/puzzles/src/html/signpost.html +++ /dev/null @@ -1,14 +0,0 @@ -Signpost -

-Connect all the squares together into a sequence, so that every -square's arrow points towards the square that follows it (though the -next square can be any distance away in that direction). - -

-Left-drag from a square to the square that should follow it, or -right-drag from a square to the square that should precede it. - -

-Left-drag a square off the grid to break all links to it. Right-drag -a square off the grid to break all links to it and everything else -in its connected chain. diff --git a/apps/plugins/puzzles/src/html/singles.html b/apps/plugins/puzzles/src/html/singles.html deleted file mode 100644 index 252bffb380..0000000000 --- a/apps/plugins/puzzles/src/html/singles.html +++ /dev/null @@ -1,11 +0,0 @@ -Singles -

-Black out some of the squares, in such a way that: -

  • no number appears twice in any row or column -
  • no two black squares are adjacent -
  • the white squares form a single connected group (connections -along diagonals do not count).
-

-Click in a square to black it out, and again to uncover it. -Right-click in a square to mark it with a circle, indicating that -you're sure it should not be blacked out. diff --git a/apps/plugins/puzzles/src/html/sixteen.html b/apps/plugins/puzzles/src/html/sixteen.html deleted file mode 100644 index 4530469fe6..0000000000 --- a/apps/plugins/puzzles/src/html/sixteen.html +++ /dev/null @@ -1,8 +0,0 @@ -Sixteen -

-Slide the grid squares around so that the numbers end up in -consecutive order from the top left corner. -

-Click on the arrows at the edges of the grid to move a row or column -left, right, up or down. The square that falls off the end of the -row comes back on the other end. diff --git a/apps/plugins/puzzles/src/html/slant.html b/apps/plugins/puzzles/src/html/slant.html deleted file mode 100644 index d6d31aa302..0000000000 --- a/apps/plugins/puzzles/src/html/slant.html +++ /dev/null @@ -1,9 +0,0 @@ -Slant -

-Fill in a diagonal line in every grid square so that there are no -loops in the grid, and so that every numbered point has that many -lines meeting at it. -

-Left-click in a square to mark it with a \; right-click -to mark it with a /. Keep clicking in a square to -cycle it between \, / and empty. diff --git a/apps/plugins/puzzles/src/html/solo.html b/apps/plugins/puzzles/src/html/solo.html deleted file mode 100644 index 88ebd5cb29..0000000000 --- a/apps/plugins/puzzles/src/html/solo.html +++ /dev/null @@ -1,20 +0,0 @@ -Solo -

-Fill in a number in every square so that every number appears -exactly once in each row, each column and each block marked by thick -lines. -

-To place a number, click in a square to select it, then type the -number on the keyboard. To erase a number, click to select a square -and then press Backspace. -

-Right-click in a square and then type a number to add or remove the -number as a pencil mark, indicating numbers that you think -might go in that square. -

-When you master the basic game, try Jigsaw mode (irregularly shaped -blocks), X mode (the two main diagonals of the grid must also -contain every number once), Killer mode (instead of single-cell -clues you are given regions of the grid each of which must add up to -a given total, again without reusing any digits), or all of those at -once! diff --git a/apps/plugins/puzzles/src/html/tents.html b/apps/plugins/puzzles/src/html/tents.html deleted file mode 100644 index e3f6d5f0ea..0000000000 --- a/apps/plugins/puzzles/src/html/tents.html +++ /dev/null @@ -1,20 +0,0 @@ -Tents -

-Place tents in the empty squares in such a way that: -

    -
  • no two tents are adjacent, even diagonally -
  • the number of tents in each row and column matches the numbers -around the edge of the grid -
  • it is possible to match tents to trees so that each tree is -orthogonally adjacent to its own tent (but may also be adjacent to -other tents). -
-

-Click in a square to place or remove a tent. Right-click to mark a -square as empty (not a tent). Right-click and drag along a row or -column to mark many squares at once as empty. -

-Warning '!' marks appear to indicate adjacent tents. Numbers round -the edge of the grid light up red to indicate they do not match the -number of tents in the row. Groups of tents light up red to indicate -that they have too few trees between them, and vice versa. diff --git a/apps/plugins/puzzles/src/html/towers.html b/apps/plugins/puzzles/src/html/towers.html deleted file mode 100644 index a710e0ab6e..0000000000 --- a/apps/plugins/puzzles/src/html/towers.html +++ /dev/null @@ -1,22 +0,0 @@ -Towers -

-Fill in the grid with towers whose heights range from 1 to the grid -size, so that every possible height appears exactly once in each row -and column, and so that each clue around the edge counts the number -of towers that are visible when looking into the grid from that -direction. (Taller towers hide shorter ones behind them. So the -sequence 2,1,4,3,5 would match a clue of 3 on the left, because the -1 is hidden behind the 2 and the 3 is hidden behind the 4. On the -right, it would match a clue of 1 because the 5 hides everything -else.) -

-To place a tower, click in a square to select it, then type the -desired height on the keyboard. To erase a tower, click to select a -square and then press Backspace. -

-Right-click in a square and then type a number to add or remove the -number as a pencil mark, indicating tower heights that you think -might go in that square. -

-Left-click on a clue to mark it as done (grey it out). To unmark a -clue as done, left-click on it again. \ No newline at end of file diff --git a/apps/plugins/puzzles/src/html/tracks.html b/apps/plugins/puzzles/src/html/tracks.html deleted file mode 100644 index afabed37ac..0000000000 --- a/apps/plugins/puzzles/src/html/tracks.html +++ /dev/null @@ -1,19 +0,0 @@ -Tracks -

-Complete the track from A to B so that the rows and -columns contain the same number of track segments as are indicated in the -clues to the top and right of the grid. There are only straight and -90-degree curved rail sections, and the track may not cross itself. -

-Left-click on an edge between two squares to add a track segment between -the two squares. Right-click on an edge to add a cross on the edge, -indicating no track is possible there. -

-Left-click in a square to add a colour indicator showing that you know the -square must contain a track, even if you don't know which edges it crosses -yet. Right-click in a square to add a cross indicating it contains no -track segment. -

-Left- or right-drag between squares to lay a straight line of is-track or -is-not-track indicators, useful for filling in rows or columns to match the -clue. diff --git a/apps/plugins/puzzles/src/html/twiddle.html b/apps/plugins/puzzles/src/html/twiddle.html deleted file mode 100644 index 5f94e4e120..0000000000 --- a/apps/plugins/puzzles/src/html/twiddle.html +++ /dev/null @@ -1,15 +0,0 @@ -Twiddle -

-Rotate square sections of the grid to arrange the squares into -numerical order starting from the top left. -

-In the basic game, you rotate a 2×2 square section. Left-click -in the centre of that section (i.e. on a corner point between four -squares) to rotate the whole section anticlockwise. Right-click to -rotate the section clockwise. -

-When you master the basic game, go to the Type menu to try it with -larger rotating groups (for a 3×3 group you must click in the -centre of a square to rotate the block around it). Or select the -'orientable' mode in which every square must end up the right way -round as well as in the right place. Or both! diff --git a/apps/plugins/puzzles/src/html/undead.html b/apps/plugins/puzzles/src/html/undead.html deleted file mode 100644 index c21374f6dc..0000000000 --- a/apps/plugins/puzzles/src/html/undead.html +++ /dev/null @@ -1,22 +0,0 @@ -Undead -

-Fill in every grid square which doesn't contain a mirror with either a -ghost, a vampire, or a zombie. The numbers round the grid edges show -how many monsters must be visible along your line of sight if you look -directly into the grid from that position, along a row or column. -Zombies are always visible; ghosts are only visible when reflected in -at least one mirror; vampires are only visible when not reflected in -any mirror. - -

-To place a monster, click in a square to select it, then type the -monster's letter on the keyboard: G for a ghost, V for a vampire or Z -for a zombie. To erase a monster, click to select a square and then -press Backspace. -

-Right-click in a square and then type a letter to add or remove the -monster as a pencil mark, indicating monsters that you think -might go in that square. -

-Left-click on a clue to mark it as done (grey it out). To unmark a -clue as done, left-click on it again. diff --git a/apps/plugins/puzzles/src/html/unequal.html b/apps/plugins/puzzles/src/html/unequal.html deleted file mode 100644 index 085f82effc..0000000000 --- a/apps/plugins/puzzles/src/html/unequal.html +++ /dev/null @@ -1,14 +0,0 @@ -Unequal -

-Fill in the grid with numbers from 1 to the grid size, so that every -number appears exactly once in each row and column, and so that all -the < signs represent true inequalities (i.e. the -number at the pointed end is smaller than the number at the open end). -

-To place a number, click in a square to select it, then type the -number on the keyboard. To erase a number, click to select a square -and then press Backspace. -

-Right-click in a square and then type a number to add or remove the -number as a pencil mark, indicating numbers that you think -might go in that square. diff --git a/apps/plugins/puzzles/src/html/unruly.html b/apps/plugins/puzzles/src/html/unruly.html deleted file mode 100644 index 2cd3fb25f5..0000000000 --- a/apps/plugins/puzzles/src/html/unruly.html +++ /dev/null @@ -1,11 +0,0 @@ -Unruly -

-Colour every square either black or white, in such a way that: -

  • no three consecutive squares, horizontally or vertically, are -the same colour -
  • each row and column contains the same number of black and white -squares.
-

-Left-click in an empty square to turn it black, or right-click to turn -it white. Click again in an already-filled square to cycle it between -black and white and empty; middle-click to reset any square to empty. diff --git a/apps/plugins/puzzles/src/html/untangle.html b/apps/plugins/puzzles/src/html/untangle.html deleted file mode 100644 index 7171a3d55d..0000000000 --- a/apps/plugins/puzzles/src/html/untangle.html +++ /dev/null @@ -1,5 +0,0 @@ -Untangle -

-Move the points around so that none of the lines cross. -

-Click on a point and drag it to move it. diff --git a/apps/plugins/puzzles/src/icons/Makefile b/apps/plugins/puzzles/src/icons/Makefile deleted file mode 100644 index 646580672c..0000000000 --- a/apps/plugins/puzzles/src/icons/Makefile +++ /dev/null @@ -1,162 +0,0 @@ -# Makefile for Puzzles icons. - -PUZZLES = blackbox bridges cube dominosa fifteen filling flip flood \ - galaxies guess inertia keen lightup loopy magnets map mines \ - net netslide palisade pattern pearl pegs range rect \ - samegame signpost singles sixteen slant solo tents towers \ - twiddle tracks undead unequal unruly untangle - -BASE = $(patsubst %,%-base.png,$(PUZZLES)) -WEB = $(patsubst %,%-web.png,$(PUZZLES)) - -IBASE = $(patsubst %,%-ibase.png,$(PUZZLES)) -IBASE4 = $(patsubst %,%-ibase4.png,$(PUZZLES)) -P96D24 = $(patsubst %,%-96d24.png,$(PUZZLES)) -P96D8 = $(patsubst %,%-96d8.png,$(PUZZLES)) -P96D4 = $(patsubst %,%-96d4.png,$(PUZZLES)) -P48D24 = $(patsubst %,%-48d24.png,$(PUZZLES)) -P48D8 = $(patsubst %,%-48d8.png,$(PUZZLES)) -P48D4 = $(patsubst %,%-48d4.png,$(PUZZLES)) -P32D24 = $(patsubst %,%-32d24.png,$(PUZZLES)) -P32D8 = $(patsubst %,%-32d8.png,$(PUZZLES)) -P32D4 = $(patsubst %,%-32d4.png,$(PUZZLES)) -P16D24 = $(patsubst %,%-16d24.png,$(PUZZLES)) -P16D8 = $(patsubst %,%-16d8.png,$(PUZZLES)) -P16D4 = $(patsubst %,%-16d4.png,$(PUZZLES)) -ICONS = $(patsubst %,%.ico,$(PUZZLES)) -CICONS = $(patsubst %,%-icon.c,$(PUZZLES)) -RC = $(patsubst %,%.rc,$(PUZZLES)) - -BIN = ../ -PIC = ./ - -# Work around newer ImageMagick unilaterally distorting colours when -# converting to PNG. -CSP = -set colorspace RGB - -base: $(BASE) -web: $(WEB) -pngicons: $(P96D24) $(P48D24) $(P32D24) $(P16D24) -winicons: $(ICONS) $(RC) -gtkicons: $(CICONS) -all: base web pngicons winicons gtkicons - -# Build the base puzzle screenshots from which all the other images -# are derived. Some of them involve showing a move animation -# part-way through. -fifteen-base.png : override REDO=0.3 -flip-base.png : override REDO=0.3 -netslide-base.png : override REDO=0.3 -sixteen-base.png : override REDO=0.3 -twiddle-base.png : override REDO=0.3 -$(BASE): %-base.png: $(BIN)% $(PIC)%.sav - $(PIC)screenshot.sh $(BIN)$* $(PIC)$*.sav $@ $(REDO) - -# Build the screenshots for the web, by scaling the original base -# images to a uniform size. -$(WEB): %-web.png: %-base.png - $(PIC)square.pl 150 5 $^ $@ - -# Build the base _icon_ images, by careful cropping of the base -# images: icons are very small so it's often necessary to zoom in -# on a smaller portion of the screenshot. -blackbox-ibase.png : override CROP=352x352 144x144+0+208 -bridges-ibase.png : override CROP=264x264 107x107+157+157 -dominosa-ibase.png : override CROP=304x272 152x152+152+0 -fifteen-ibase.png : override CROP=240x240 120x120+0+120 -filling-ibase.png : override CROP=256x256 133x133+14+78 -flip-ibase.png : override CROP=288x288 145x145+120+72 -galaxies-ibase.png : override CROP=288x288 165x165+0+0 -guess-ibase.png : override CROP=263x420 178x178+75+17 -inertia-ibase.png : override CROP=321x321 128x128+193+0 -keen-ibase.png : override CROP=288x288 96x96+24+120 -lightup-ibase.png : override CROP=256x256 112x112+144+0 -loopy-ibase.png : override CROP=257x257 113x113+0+0 -magnets-ibase.png : override CROP=264x232 96x96+36+100 -mines-ibase.png : override CROP=240x240 110x110+130+130 -net-ibase.png : override CROP=193x193 113x113+0+80 -netslide-ibase.png : override CROP=289x289 144x144+0+0 -palisade-ibase.png : override CROP=288x288 192x192+0+0 -pattern-ibase.png : override CROP=384x384 223x223+0+0 -pearl-ibase.png : override CROP=216x216 94x94+108+15 -pegs-ibase.png : override CROP=263x263 147x147+116+0 -range-ibase.png : override CROP=256x256 98x98+111+15 -rect-ibase.png : override CROP=205x205 115x115+90+0 -signpost-ibase.png : override CROP=240x240 98x98+23+23 -singles-ibase.png : override CROP=224x224 98x98+15+15 -sixteen-ibase.png : override CROP=288x288 144x144+144+144 -slant-ibase.png : override CROP=321x321 160x160+160+160 -solo-ibase.png : override CROP=481x481 145x145+24+24 -tents-ibase.png : override CROP=320x320 165x165+142+0 -towers-ibase.png : override CROP=300x300 102x102+151+6 -tracks-ibase.png : override CROP=246x246 118x118+6+6 -twiddle-ibase.png : override CROP=192x192 102x102+69+21 -undead-ibase.png : override CROP=416x480 192x192+16+80 -unequal-ibase.png : override CROP=208x208 104x104+104+104 -untangle-ibase.png : override CROP=320x320 164x164+3+116 -$(IBASE): %-ibase.png: %-base.png - $(PIC)crop.sh $^ $@ $(CROP) - -# Convert the full-size icon images to 4-bit colour, because that -# seems to work better than reducing it in 24 bits and then -# dithering. -$(IBASE4): %-ibase4.png: %-ibase.png - convert -colors 16 +dither $(CSP) -map $(PIC)win16pal.xpm $^ $@ - -# Build the 24-bit PNGs for the icons, at four sizes. -$(P96D24): %-96d24.png: %-ibase.png - $(PIC)square.pl 96 4 $^ $@ -$(P48D24): %-48d24.png: %-ibase.png - $(PIC)square.pl 48 4 $^ $@ -$(P32D24): %-32d24.png: %-ibase.png - $(PIC)square.pl 32 2 $^ $@ -$(P16D24): %-16d24.png: %-ibase.png - $(PIC)square.pl 16 1 $^ $@ - -# The 8-bit icon PNGs are just custom-paletted quantisations of the -# 24-bit ones. -$(P96D8) $(P48D8) $(P32D8) $(P16D8): %d8.png: %d24.png - convert -colors 256 $^ $@ - -# But the depth-4 images work better if we re-shrink from the -# ibase4 versions of the images, and then normalise the colours -# again afterwards. (They're still not very good, but my hope is -# that on most modern Windows machines this won't matter too -# much...) -$(P96D4): %-96d4.png: %-ibase4.png - $(PIC)square.pl 96 1 $^ $@-tmp2.png - convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@ - rm -f $@-tmp2.png -$(P48D4): %-48d4.png: %-ibase4.png - $(PIC)square.pl 48 1 $^ $@-tmp2.png - convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@ - rm -f $@-tmp2.png -$(P32D4): %-32d4.png: %-ibase.png - $(PIC)square.pl 32 1 $^ $@-tmp2.png - convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@ - rm -f $@-tmp2.png -$(P16D4): %-16d4.png: %-ibase.png - $(PIC)square.pl 16 1 $^ $@-tmp2.png - convert -colors 16 $(CSP) -map $(PIC)win16pal.xpm $@-tmp2.png $@ - rm -f $@-tmp2.png - -# Build the actual Windows icons themselves, by feeding all those -# PNGs to my icon builder script. -$(ICONS): %.ico: %-48d24.png %-48d8.png %-48d4.png \ - %-32d24.png %-32d8.png %-32d4.png \ - %-16d24.png %-16d8.png %-16d4.png - $(PIC)icon.pl -24 $*-48d24.png $*-32d24.png $*-16d24.png \ - -8 $*-48d8.png $*-32d8.png $*-16d8.png \ - -4 $*-48d4.png $*-32d4.png $*-16d4.png > $@ - -# Build the .RC files which bind the icons into the applications. -$(RC): %.rc: - echo '#include "puzzles.rc2"' > $@ - echo '200 ICON "$*.ico"' >> $@ - -# Build the GTK icon source files. -$(CICONS): %-icon.c: %-16d24.png %-32d24.png %-48d24.png %-96d24.png - $(PIC)cicon.pl $^ > $@ - -clean: - rm -f *.png *.ico *.rc *-icon.c diff --git a/apps/plugins/puzzles/src/icons/blackbox.sav b/apps/plugins/puzzles/src/icons/blackbox.sav deleted file mode 100644 index 4483f3c81a..0000000000 --- a/apps/plugins/puzzles/src/icons/blackbox.sav +++ /dev/null @@ -1,27 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :9:Black Box -PARAMS :8:w8h8m5M5 -CPARAMS :8:w8h8m5M5 -SEED :15:999785320716678 -DESC :24:c8b9f8528193756b9a2fd24d -UI :2:E0 -NSTATES :2:18 -STATEPOS:2:13 -MOVE :2:F2 -MOVE :2:F4 -MOVE :2:F5 -MOVE :3:F25 -MOVE :3:F26 -MOVE :3:F11 -MOVE :3:F12 -MOVE :3:F15 -MOVE :4:T7,7 -MOVE :4:T7,4 -MOVE :4:T1,7 -MOVE :2:F3 -MOVE :2:F9 -MOVE :3:F27 -MOVE :4:T5,7 -MOVE :4:T1,8 -MOVE :1:R diff --git a/apps/plugins/puzzles/src/icons/bridges.sav b/apps/plugins/puzzles/src/icons/bridges.sav deleted file mode 100644 index ddae7085cd..0000000000 --- a/apps/plugins/puzzles/src/icons/bridges.sav +++ /dev/null @@ -1,72 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Bridges -PARAMS :15:10x10i30e10m2d2 -CPARAMS :15:10x10i30e10m2d2 -SEED :15:944199396008454 -DESC :49:a1a4a4b4k4b6b2a4b2b2b1e2e1a4b4c3j25d2a1f2c3a4d4c3 -AUXINFO :838:bd75eb5f7b129109b5cdcff0925c77ca5c0a135365002b93b44c5013c7a307b9504affcfb8ad934263196fc3e6d0b023abe48d254d46d29520e50a5e423c0fb1bc01ccc51cad61045c439e7c2bb8e5788bc7f3622aaa3a8125ebde11c9cd69b6f2393246fd094ad91e81ae58cd557b73bd1c9839cfad5835c8519e44298204eaca58dfd79289546959bfbabdc5f3cb7a27b8d3fb2d0b062bd5c2e469493c19f8c89989df73d8a3ab02d9afcbfedf245306d15881a01d153122f8374c7526abecc90919f99ff62e9789cabc402249af095ceb14c8c59c0d9ffbcdd731d50114e7c30c31ef0638f4d352abbfd04b4315d368d65bbfe005b6586245bc5244e5050098cf4c1b6986120f40d5ce038c10a3f309286f950cdc287e495aa13c70ab0c1f113a135556d7ce895fd8244afcbad43fe51f275837f223a1cb95151de8a158cb0add7fa8c9f1fa0e09a1ce842136c1679144cead56b164c4ef1a09ed36fd9704ba191b5957bc3d5bb97d8a1f7451d357a6638ac320b0beb0cd35aa404c8f1621c6d400960aa97bf6ce3a944339d7e401c4d98c31773b2a8881352d5653fdb5e8f7c04b -NSTATES :2:63 -STATEPOS:2:41 -MOVE :10:L8,0,5,0,1 -MOVE :10:L8,0,5,0,2 -MOVE :10:L8,0,8,2,1 -MOVE :10:L8,0,8,2,2 -MOVE :4:M8,0 -MOVE :10:L0,2,3,2,1 -MOVE :10:L0,2,3,2,2 -MOVE :10:L0,2,0,7,1 -MOVE :10:L0,2,0,7,2 -MOVE :4:M0,2 -MOVE :10:L1,0,3,0,1 -MOVE :4:M1,0 -MOVE :10:L3,0,5,0,1 -MOVE :10:L3,0,3,2,1 -MOVE :10:L1,3,1,5,1 -MOVE :10:L0,7,5,7,1 -MOVE :10:L0,7,0,9,1 -MOVE :10:L0,9,5,9,1 -MOVE :10:L0,9,5,9,2 -MOVE :10:L0,9,0,7,2 -MOVE :4:M0,9 -MOVE :4:M0,7 -MOVE :10:L4,8,8,8,1 -MOVE :10:L4,8,8,8,2 -MOVE :4:M4,8 -MOVE :10:L5,9,9,9,1 -MOVE :10:L5,9,9,9,2 -MOVE :4:M5,9 -MOVE :10:L9,9,9,6,1 -MOVE :4:M9,9 -MOVE :10:L8,8,8,5,1 -MOVE :4:M8,8 -MOVE :10:L9,6,9,4,1 -MOVE :4:M9,6 -MOVE :4:M9,4 -MOVE :10:L1,5,4,5,1 -MOVE :10:L1,5,4,5,2 -MOVE :10:L1,5,1,3,2 -MOVE :4:M1,3 -MOVE :4:M1,5 -MOVE :10:L3,4,3,2,1 -MOVE :10:L3,4,3,2,2 -MOVE :4:M3,4 -MOVE :10:L4,5,8,5,1 -MOVE :10:L7,7,5,7,1 -MOVE :4:M5,7 -MOVE :4:M7,7 -MOVE :10:L7,3,4,3,1 -MOVE :4:M7,3 -MOVE :10:L5,0,3,0,2 -MOVE :4:M5,0 -MOVE :4:M3,0 -MOVE :10:L3,2,6,2,1 -MOVE :4:M3,2 -MOVE :10:L6,2,8,2,1 -MOVE :4:M6,2 -MOVE :10:L8,2,8,5,1 -MOVE :4:M8,2 -MOVE :4:M8,5 -MOVE :10:L4,5,4,3,1 -MOVE :4:M4,3 -MOVE :4:M4,5 diff --git a/apps/plugins/puzzles/src/icons/cicon.pl b/apps/plugins/puzzles/src/icons/cicon.pl deleted file mode 100755 index 3578bd33fe..0000000000 --- a/apps/plugins/puzzles/src/icons/cicon.pl +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/perl - -# Given a list of input PNGs, create a C source file file -# containing a const array of XPMs, under the name `xpm_icon'. - -$k = 0; -@xpms = (); -foreach $f (@ARGV) { - # XPM format is generated directly by ImageMagick, so that's easy - # enough. We just have to adjust the declaration line so that it - # has the right name, linkage and storage class. - @lines = (); - open XPM, "convert $f xpm:- |"; - push @lines, $_ while ; - close XPM; - die "XPM from $f in unexpected format\n" unless $lines[1] =~ /^static.*\{$/; - $lines[1] = "static const char *const xpm_icon_$k"."[] = {\n"; - $k++; - push @xpms, @lines, "\n"; -} - -# Now output. -foreach $line (@xpms) { print $line; } -print "const char *const *const xpm_icons[] = {\n"; -for ($i = 0; $i < $k; $i++) { print " xpm_icon_$i,\n"; } -print "};\n"; -print "const int n_xpm_icons = $k;\n"; diff --git a/apps/plugins/puzzles/src/icons/crop.sh b/apps/plugins/puzzles/src/icons/crop.sh deleted file mode 100755 index 0d15d3c9b9..0000000000 --- a/apps/plugins/puzzles/src/icons/crop.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -# Crop one image into another, after first checking that the source -# image has the expected size in pixels. -# -# This is used in the Puzzles icon build scripts to construct icons -# which are zoomed in on a particular sub-area of the puzzle's -# basic screenshot. This way I can define crop areas in pixels, -# while not having to worry too much that if I adjust the source -# puzzle so as to alter the layout the crop area might start -# hitting the wrong bit of picture. Most layout changes I can -# conveniently imagine will also alter the overall image size, so -# this script will give a build error and alert me to the fact that -# I need to fiddle with the icon makefile. - -infile="$1" -outfile="$2" -insize="$3" -crop="$4" - -# Special case: if no input size or crop parameter was specified at -# all, we just copy the input to the output file. - -if test $# -lt 3; then - cp "$infile" "$outfile" - exit 0 -fi - -# Check the input image size. -realsize=`identify -format %wx%h "$infile"` -if test "x$insize" != "x$realsize"; then - echo "crop.sh: '$infile' has wrong initial size: $realsize != $insize" >&2 - exit 1 -fi - -# And crop. -convert -crop "$crop" "$infile" "$outfile" diff --git a/apps/plugins/puzzles/src/icons/cube.sav b/apps/plugins/puzzles/src/icons/cube.sav deleted file mode 100644 index bb123f4e74..0000000000 --- a/apps/plugins/puzzles/src/icons/cube.sav +++ /dev/null @@ -1,22 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :4:Cube -PARAMS :4:c4x4 -CPARAMS :4:c4x4 -SEED :15:125146248070163 -DESC :6:C461,3 -NSTATES :2:14 -STATEPOS:1:5 -MOVE :1:D -MOVE :1:D -MOVE :1:D -MOVE :1:U -MOVE :1:L -MOVE :1:L -MOVE :1:D -MOVE :1:U -MOVE :1:D -MOVE :1:U -MOVE :1:U -MOVE :1:U -MOVE :1:L diff --git a/apps/plugins/puzzles/src/icons/dominosa.sav b/apps/plugins/puzzles/src/icons/dominosa.sav deleted file mode 100644 index 5991f3e57e..0000000000 --- a/apps/plugins/puzzles/src/icons/dominosa.sav +++ /dev/null @@ -1,53 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :8:Dominosa -PARAMS :1:6 -CPARAMS :1:6 -DESC :56:55521461210004364611033535444421636022603153156422620503 -NSTATES :2:46 -STATEPOS:2:33 -MOVE :6:D18,19 -MOVE :6:E22,23 -MOVE :6:E22,30 -MOVE :5:E9,17 -MOVE :6:D38,46 -MOVE :6:E14,15 -MOVE :5:E6,14 -MOVE :6:E33,34 -MOVE :6:E34,42 -MOVE :6:E26,34 -MOVE :6:D34,35 -MOVE :6:E42,50 -MOVE :6:E16,24 -MOVE :4:D4,5 -MOVE :4:D6,7 -MOVE :6:D15,23 -MOVE :6:E17,25 -MOVE :6:D16,17 -MOVE :6:E11,12 -MOVE :6:D51,52 -MOVE :5:E3,11 -MOVE :6:D10,11 -MOVE :4:D2,3 -MOVE :6:E37,45 -MOVE :6:D49,50 -MOVE :6:D40,48 -MOVE :6:D25,26 -MOVE :6:D24,32 -MOVE :6:D33,41 -MOVE :6:D42,43 -MOVE :6:D27,28 -MOVE :6:E21,29 -MOVE :6:D31,39 -MOVE :6:D47,55 -MOVE :6:E13,21 -MOVE :6:E13,14 -MOVE :6:D12,13 -MOVE :6:D20,21 -MOVE :6:D14,22 -MOVE :6:D29,30 -MOVE :6:D36,37 -MOVE :6:D44,45 -MOVE :6:D53,54 -MOVE :4:D0,1 -MOVE :4:D8,9 diff --git a/apps/plugins/puzzles/src/icons/fifteen.sav b/apps/plugins/puzzles/src/icons/fifteen.sav deleted file mode 100644 index d81345a7d8..0000000000 --- a/apps/plugins/puzzles/src/icons/fifteen.sav +++ /dev/null @@ -1,74 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Fifteen -PARAMS :3:4x4 -CPARAMS :3:4x4 -SEED :15:307905346810973 -DESC :37:8,11,3,6,14,13,4,2,0,9,12,10,5,1,7,15 -NSTATES :2:66 -STATEPOS:2:47 -MOVE :4:M1,2 -MOVE :4:M1,3 -MOVE :4:M0,3 -MOVE :4:M0,1 -MOVE :4:M1,1 -MOVE :4:M1,2 -MOVE :4:M0,2 -MOVE :4:M0,0 -MOVE :4:M1,0 -MOVE :4:M1,1 -MOVE :4:M0,1 -MOVE :4:M0,0 -MOVE :4:M1,0 -MOVE :4:M3,0 -MOVE :4:M3,1 -MOVE :4:M1,1 -MOVE :4:M1,0 -MOVE :4:M3,0 -MOVE :4:M3,1 -MOVE :4:M1,1 -MOVE :4:M1,0 -MOVE :4:M2,0 -MOVE :4:M2,1 -MOVE :4:M1,1 -MOVE :4:M1,3 -MOVE :4:M0,3 -MOVE :4:M0,1 -MOVE :4:M1,1 -MOVE :4:M1,2 -MOVE :4:M0,2 -MOVE :4:M0,1 -MOVE :4:M2,1 -MOVE :4:M3,1 -MOVE :4:M3,2 -MOVE :4:M2,2 -MOVE :4:M2,3 -MOVE :4:M3,3 -MOVE :4:M3,1 -MOVE :4:M2,1 -MOVE :4:M2,2 -MOVE :4:M1,2 -MOVE :4:M1,3 -MOVE :4:M2,3 -MOVE :4:M2,2 -MOVE :4:M1,2 -MOVE :4:M0,2 -MOVE :4:M0,3 -MOVE :4:M1,3 -MOVE :4:M1,2 -MOVE :4:M2,2 -MOVE :4:M2,3 -MOVE :4:M0,3 -MOVE :4:M0,2 -MOVE :4:M1,2 -MOVE :4:M2,2 -MOVE :4:M2,3 -MOVE :4:M1,3 -MOVE :4:M1,2 -MOVE :4:M3,2 -MOVE :4:M3,3 -MOVE :4:M1,3 -MOVE :4:M1,2 -MOVE :4:M2,2 -MOVE :4:M2,3 -MOVE :4:M3,3 diff --git a/apps/plugins/puzzles/src/icons/filling.sav b/apps/plugins/puzzles/src/icons/filling.sav deleted file mode 100644 index caf0bb2d4c..0000000000 --- a/apps/plugins/puzzles/src/icons/filling.sav +++ /dev/null @@ -1,38 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Filling -PARAMS :3:7x7 -CPARAMS :3:7x7 -SEED :15:279172739852696 -DESC :49:0000000031051240010004000001106171000400001013105 -NSTATES :2:30 -STATEPOS:2:13 -MOVE :4:38_3 -MOVE :4:39_3 -MOVE :4:36_4 -MOVE :4:43_4 -MOVE :4:35_4 -MOVE :4:47_5 -MOVE :4:40_5 -MOVE :4:34_5 -MOVE :4:41_5 -MOVE :4:25_7 -MOVE :4:23_6 -MOVE :4:16_6 -MOVE :4:18_7 -MOVE :4:19_7 -MOVE :4:20_7 -MOVE :4:26_7 -MOVE :4:24_7 -MOVE :4:29_6 -MOVE :4:22_6 -MOVE :4:15_6 -MOVE :3:7_4 -MOVE :3:0_4 -MOVE :3:1_3 -MOVE :3:2_3 -MOVE :3:6_2 -MOVE :3:5_5 -MOVE :3:4_5 -MOVE :3:3_5 -MOVE :4:10_5 diff --git a/apps/plugins/puzzles/src/icons/flip.sav b/apps/plugins/puzzles/src/icons/flip.sav deleted file mode 100644 index 82b4c49357..0000000000 --- a/apps/plugins/puzzles/src/icons/flip.sav +++ /dev/null @@ -1,20 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :4:Flip -PARAMS :4:5x5c -CPARAMS :4:5x5c -SEED :15:158897339725978 -DESC :165:c400007100001c4000071000018400043100011c400047100011c400046100010c400047100011c4000471000118400043100011c400047100011c400046100010c000047000011c0000470000118,5c18b48 -NSTATES :2:12 -STATEPOS:1:4 -MOVE :4:M4,3 -MOVE :4:M3,0 -MOVE :4:M2,2 -MOVE :4:M3,2 -MOVE :4:M2,3 -MOVE :4:M0,2 -MOVE :4:M0,3 -MOVE :4:M1,4 -MOVE :4:M0,0 -MOVE :4:M1,0 -MOVE :4:M1,1 diff --git a/apps/plugins/puzzles/src/icons/flood.sav b/apps/plugins/puzzles/src/icons/flood.sav deleted file mode 100644 index ac4adf7020..0000000000 --- a/apps/plugins/puzzles/src/icons/flood.sav +++ /dev/null @@ -1,14 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Flood -PARAMS :7:6x6c6m5 -CPARAMS :7:6x6c6m5 -SEED :15:967543368167853 -DESC :39:032242034203340350204502505323231342,17 -NSTATES :1:6 -STATEPOS:1:6 -MOVE :2:M3 -MOVE :2:M2 -MOVE :2:M0 -MOVE :2:M5 -MOVE :2:M3 diff --git a/apps/plugins/puzzles/src/icons/galaxies.sav b/apps/plugins/puzzles/src/icons/galaxies.sav deleted file mode 100644 index 42d18bc335..0000000000 --- a/apps/plugins/puzzles/src/icons/galaxies.sav +++ /dev/null @@ -1,51 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :8:Galaxies -PARAMS :5:7x7dh -CPARAMS :5:7x7dh -SEED :15:483644862786314 -DESC :13:ikrqfedzljjsp -NSTATES :2:43 -STATEPOS:2:37 -MOVE :5:E12,9 -MOVE :5:E8,11 -MOVE :5:E5,12 -MOVE :5:E7,12 -MOVE :5:E4,13 -MOVE :5:E2,11 -MOVE :5:E3,10 -MOVE :4:E2,5 -MOVE :4:E4,5 -MOVE :4:E6,7 -MOVE :4:E8,1 -MOVE :5:E10,1 -MOVE :4:E9,2 -MOVE :4:E6,3 -MOVE :4:E7,4 -MOVE :5:E10,3 -MOVE :5:E10,5 -MOVE :5:E11,6 -MOVE :5:E13,6 -MOVE :5:E8,13 -MOVE :5:E12,7 -MOVE :6:E12,11 -MOVE :6:E13,12 -MOVE :4:E8,9 -MOVE :4:E7,8 -MOVE :4:E7,6 -MOVE :4:E9,6 -MOVE :4:E8,5 -MOVE :4:E9,4 -MOVE :4:E5,2 -MOVE :4:E4,1 -MOVE :4:E3,6 -MOVE :4:E2,7 -MOVE :4:E3,8 -MOVE :4:E3,4 -MOVE :4:E4,9 -MOVE :4:E2,9 -MOVE :5:E5,10 -MOVE :5:E6,11 -MOVE :4:E2,3 -MOVE :4:E2,1 -MOVE :5:E1,12 diff --git a/apps/plugins/puzzles/src/icons/guess.sav b/apps/plugins/puzzles/src/icons/guess.sav deleted file mode 100644 index 69852bf769..0000000000 --- a/apps/plugins/puzzles/src/icons/guess.sav +++ /dev/null @@ -1,15 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Guess -PARAMS :9:c6p4g10Bm -CPARAMS :9:c6p4g10Bm -SEED :15:313100730915729 -DESC :8:b5f3faed -UI :7:0,0,0,0 -NSTATES :1:6 -STATEPOS:1:6 -MOVE :8:G1,1,2,2 -MOVE :8:G4,3,1,1 -MOVE :8:G5,5,1,1 -MOVE :8:G4,2,1,6 -MOVE :8:G2,3,1,6 diff --git a/apps/plugins/puzzles/src/icons/icon.pl b/apps/plugins/puzzles/src/icons/icon.pl deleted file mode 100755 index fcb1aa3e2c..0000000000 --- a/apps/plugins/puzzles/src/icons/icon.pl +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/perl - -# Take a collection of input image files and convert them into a -# multi-resolution Windows .ICO icon file. -# -# The input images can be treated as having four different colour -# depths: -# -# - 24-bit true colour -# - 8-bit with custom palette -# - 4-bit using the Windows 16-colour palette (see comment below -# for details) -# - 1-bit using black and white only. -# -# The images can be supplied in any input format acceptable to -# ImageMagick, but their actual colour usage must already be -# appropriate for the specified mode; this script will not do any -# substantive conversion. So if an image intended to be used in 4- -# or 1-bit mode contains any colour not in the appropriate fixed -# palette, that's a fatal error; if an image to be used in 8-bit -# mode contains more than 256 distinct colours, that's also a fatal -# error. -# -# Command-line syntax is: -# -# icon.pl -depth imagefile [imagefile...] [-depth imagefile [imagefile...]] -# -# where `-depth' is one of `-24', `-8', `-4' or `-1', and tells the -# script how to treat all the image files given after that option -# until the next depth option. For example, you might execute -# -# icon.pl -24 48x48x24.png 32x32x24.png -8 32x32x8.png -1 monochrome.png -# -# to build an icon file containing two differently sized 24-bit -# images, one 8-bit image and one black and white image. -# -# Windows .ICO files support a 1-bit alpha channel on all these -# image types. That is, any pixel can be either opaque or fully -# transparent, but not partially transparent. The alpha channel is -# separate from the main image data, meaning that `transparent' is -# not required to take up a palette entry. (So an 8-bit image can -# have 256 distinct _opaque_ colours, plus transparent pixels as -# well.) If the input images have alpha channels, they will be used -# to determine which pixels of the icon are transparent, by simple -# quantisation half way up (e.g. in a PNG image with an 8-bit alpha -# channel, alpha values of 00-7F will be mapped to transparent -# pixels, and 80-FF will become opaque). - -# The Windows 16-colour palette consists of: -# - the eight corners of the colour cube (000000, 0000FF, 00FF00, -# 00FFFF, FF0000, FF00FF, FFFF00, FFFFFF) -# - dim versions of the seven non-black corners, at 128/255 of the -# brightness (000080, 008000, 008080, 800000, 800080, 808000, -# 808080) -# - light grey at 192/255 of full brightness (C0C0C0). -%win16pal = ( - "\x00\x00\x00\x00" => 0, - "\x00\x00\x80\x00" => 1, - "\x00\x80\x00\x00" => 2, - "\x00\x80\x80\x00" => 3, - "\x80\x00\x00\x00" => 4, - "\x80\x00\x80\x00" => 5, - "\x80\x80\x00\x00" => 6, - "\xC0\xC0\xC0\x00" => 7, - "\x80\x80\x80\x00" => 8, - "\x00\x00\xFF\x00" => 9, - "\x00\xFF\x00\x00" => 10, - "\x00\xFF\xFF\x00" => 11, - "\xFF\x00\x00\x00" => 12, - "\xFF\x00\xFF\x00" => 13, - "\xFF\xFF\x00\x00" => 14, - "\xFF\xFF\xFF\x00" => 15, -); -@win16pal = sort { $win16pal{$a} <=> $win16pal{$b} } keys %win16pal; - -# The black and white palette consists of black (000000) and white -# (FFFFFF), obviously. -%win2pal = ( - "\x00\x00\x00\x00" => 0, - "\xFF\xFF\xFF\x00" => 1, -); -@win2pal = sort { $win16pal{$a} <=> $win2pal{$b} } keys %win2pal; - -@hdr = (); -@dat = (); - -$depth = undef; -foreach $_ (@ARGV) { - if (/^-(24|8|4|1)$/) { - $depth = $1; - } elsif (defined $depth) { - &readicon($_, $depth); - } else { - $usage = 1; - } -} -if ($usage || length @hdr == 0) { - print "usage: icon.pl ( -24 | -8 | -4 | -1 ) image [image...]\n"; - print " [ ( -24 | -8 | -4 | -1 ) image [image...] ...]\n"; - exit 0; -} - -# Now write out the output icon file. -print pack "vvv", 0, 1, scalar @hdr; # file-level header -$filepos = 6 + 16 * scalar @hdr; -for ($i = 0; $i < scalar @hdr; $i++) { - print $hdr[$i]; - print pack "V", $filepos; - $filepos += length($dat[$i]); -} -for ($i = 0; $i < scalar @hdr; $i++) { - print $dat[$i]; -} - -sub readicon { - my $filename = shift @_; - my $depth = shift @_; - my $pix; - my $i; - my %pal; - - # Determine the icon's width and height. - my $w = `identify -format %w $filename`; - my $h = `identify -format %h $filename`; - - # Read the file in as RGBA data. We flip vertically at this - # point, to avoid having to do it ourselves (.BMP and hence - # .ICO are bottom-up). - my $data = []; - open IDATA, "convert -set colorspace sRGB -flip -depth 8 $filename rgba:- |"; - push @$data, $rgb while (read IDATA,$rgb,4,0) == 4; - close IDATA; - # Check we have the right amount of data. - $xl = $w * $h; - $al = scalar @$data; - die "wrong amount of image data ($al, expected $xl) from $filename\n" - unless $al == $xl; - - # Build the alpha channel now, so we can exclude transparent - # pixels from the palette analysis. We replace transparent - # pixels with undef in the data array. - # - # We quantise the alpha channel half way up, so that alpha of - # 0x80 or more is taken to be fully opaque and 0x7F or less is - # fully transparent. Nasty, but the best we can do without - # dithering (and don't even suggest we do that!). - my $x; - my $y; - my $alpha = ""; - - for ($y = 0; $y < $h; $y++) { - my $currbyte = 0, $currbits = 0; - for ($x = 0; $x < (($w+31)|31)-31; $x++) { - $pix = ($x < $w ? $data->[$y*$w+$x] : "\x00\x00\x00\xFF"); - my @rgba = unpack "CCCC", $pix; - $currbyte <<= 1; - $currbits++; - if ($rgba[3] < 0x80) { - if ($x < $w) { - $data->[$y*$w+$x] = undef; - } - $currbyte |= 1; # MS has the alpha channel inverted :-) - } else { - # Might as well flip RGBA into BGR0 while we're here. - if ($x < $w) { - $data->[$y*$w+$x] = pack "CCCC", - $rgba[2], $rgba[1], $rgba[0], 0; - } - } - if ($currbits >= 8) { - $alpha .= pack "C", $currbyte; - $currbits -= 8; - } - } - } - - # For an 8-bit image, check we have at most 256 distinct - # colours, and build the palette. - %pal = (); - if ($depth == 8) { - my $palindex = 0; - foreach $pix (@$data) { - next unless defined $pix; - $pal{$pix} = $palindex++ unless defined $pal{$pix}; - } - die "too many colours in 8-bit image $filename\n" unless $palindex <= 256; - } elsif ($depth == 4) { - %pal = %win16pal; - } elsif ($depth == 1) { - %pal = %win2pal; - } - - my $raster = ""; - if ($depth < 24) { - # For a non-24-bit image, flatten the image into one palette - # index per pixel. - $pad = 32 / $depth; # number of pixels to pad scanline to 4-byte align - $pmask = $pad-1; - for ($y = 0; $y < $h; $y++) { - my $currbyte = 0, $currbits = 0; - for ($x = 0; $x < (($w+$pmask)|$pmask)-$pmask; $x++) { - $currbyte <<= $depth; - $currbits += $depth; - if ($x < $w && defined ($pix = $data->[$y*$w+$x])) { - if (!defined $pal{$pix}) { - my $pixprintable = unpack "H*", $pix; - die "illegal colour value $pixprintable at pixel ($x,$y) in $filename\n"; - } - $currbyte |= $pal{$pix}; - } - if ($currbits >= 8) { - $raster .= pack "C", $currbyte; - $currbits -= 8; - } - } - } - } else { - # For a 24-bit image, reverse the order of the R,G,B values - # and stick a padding zero on the end. - # - # (In this loop we don't need to bother padding the - # scanline out to a multiple of four bytes, because every - # pixel takes four whole bytes anyway.) - for ($i = 0; $i < scalar @$data; $i++) { - if (defined $data->[$i]) { - $raster .= $data->[$i]; - } else { - $raster .= "\x00\x00\x00\x00"; - } - } - $depth = 32; # and adjust this - } - - # Prepare the icon data. First the header... - my $data = pack "VVVvvVVVVVV", - 40, # size of bitmap info header - $w, # icon width - $h*2, # icon height (x2 to indicate the subsequent alpha channel) - 1, # 1 plane (common to all MS image formats) - $depth, # bits per pixel - 0, # no compression - length $raster, # image size - 0, 0, 0, 0; # resolution, colours used, colours important (ignored) - # ... then the palette ... - if ($depth <= 8) { - my $ncols = (1 << $depth); - my $palette = "\x00\x00\x00\x00" x $ncols; - foreach $i (keys %pal) { - substr($palette, $pal{$i}*4, 4) = $i; - } - $data .= $palette; - } - # ... the raster data we already had ready ... - $data .= $raster; - # ... and the alpha channel we already had as well. - $data .= $alpha; - - # Prepare the header which will represent this image in the - # icon file. - my $header = pack "CCCCvvV", - $w, $h, # width and height (this time the real height) - 1 << $depth, # number of colours, if less than 256 - 0, # reserved - 1, # planes - $depth, # bits per pixel - length $data; # size of real icon data - - push @hdr, $header; - push @dat, $data; -} diff --git a/apps/plugins/puzzles/src/icons/inertia.sav b/apps/plugins/puzzles/src/icons/inertia.sav deleted file mode 100644 index a6d6faed1b..0000000000 --- a/apps/plugins/puzzles/src/icons/inertia.sav +++ /dev/null @@ -1,30 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Inertia -PARAMS :3:8x8 -CPARAMS :3:8x8 -SEED :15:739970145068932 -DESC :64:sbgwsmswwgggwggmmbwgwbssbwbsbwbbwsSmgbbsbbmsgbmssgmggbmmwmbmmwsw -UI :2:D0 -NSTATES :2:21 -STATEPOS:1:5 -MOVE :1:6 -MOVE :1:5 -MOVE :1:3 -MOVE :1:1 -MOVE :1:6 -MOVE :1:0 -MOVE :1:5 -MOVE :1:7 -MOVE :1:5 -MOVE :1:6 -MOVE :1:1 -MOVE :1:3 -MOVE :1:4 -MOVE :1:2 -MOVE :1:6 -MOVE :1:5 -MOVE :1:3 -MOVE :1:1 -MOVE :1:5 -MOVE :1:3 diff --git a/apps/plugins/puzzles/src/icons/keen.sav b/apps/plugins/puzzles/src/icons/keen.sav deleted file mode 100644 index 4adbd42d17..0000000000 --- a/apps/plugins/puzzles/src/icons/keen.sav +++ /dev/null @@ -1,62 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :4:Keen -PARAMS :3:5de -CPARAMS :3:5de -SEED :15:846699649745236 -DESC :48:a__a_3a_5a_a_a_3b_bac_,a5m15a7a10s2s2d2s3m40m2s2 -AUXINFO :52:6105af67c6ebc8de056b59ebc9a463aa54e75f647055c0a6c1bd -NSTATES :2:53 -STATEPOS:2:39 -MOVE :6:P0,4,2 -MOVE :6:P0,4,4 -MOVE :6:P0,4,5 -MOVE :6:P1,4,2 -MOVE :6:P1,4,4 -MOVE :6:P1,4,5 -MOVE :6:P1,3,2 -MOVE :6:P1,3,4 -MOVE :6:P1,3,5 -MOVE :6:R2,2,4 -MOVE :6:R1,2,2 -MOVE :6:P1,3,2 -MOVE :6:P1,4,2 -MOVE :6:R0,4,2 -MOVE :6:R2,3,2 -MOVE :6:R2,4,1 -MOVE :6:R1,4,4 -MOVE :6:R1,3,5 -MOVE :6:P3,4,3 -MOVE :6:P3,4,5 -MOVE :6:P4,4,3 -MOVE :6:P4,4,5 -MOVE :6:R4,4,5 -MOVE :6:R3,4,3 -MOVE :6:P3,1,2 -MOVE :6:P3,1,5 -MOVE :6:P3,0,2 -MOVE :6:P3,0,5 -MOVE :6:R3,2,1 -MOVE :6:R3,3,4 -MOVE :6:P2,0,3 -MOVE :6:P2,0,5 -MOVE :6:P2,1,3 -MOVE :6:P2,1,5 -MOVE :6:P0,1,1 -MOVE :6:P0,1,3 -MOVE :6:P1,1,1 -MOVE :6:P1,1,3 -MOVE :6:R2,0,3 -MOVE :6:R2,1,5 -MOVE :6:R3,0,5 -MOVE :6:R3,1,2 -MOVE :6:R4,1,4 -MOVE :6:R4,2,3 -MOVE :6:R4,0,2 -MOVE :6:R4,3,1 -MOVE :6:R0,2,5 -MOVE :6:R0,3,3 -MOVE :6:R1,1,3 -MOVE :6:R0,1,1 -MOVE :6:R1,0,1 -MOVE :6:R0,0,4 diff --git a/apps/plugins/puzzles/src/icons/lightup.sav b/apps/plugins/puzzles/src/icons/lightup.sav deleted file mode 100644 index 21b59bdec4..0000000000 --- a/apps/plugins/puzzles/src/icons/lightup.sav +++ /dev/null @@ -1,24 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :8:Light Up -PARAMS :10:7x7b20s4d1 -CPARAMS :10:7x7b25s4d1 -SEED :15:538922615851330 -DESC :25:b3gB0c0dBaBaBa1aBd1c01g0b -NSTATES :2:16 -STATEPOS:2:16 -MOVE :4:L1,0 -MOVE :4:L2,1 -MOVE :4:L3,0 -MOVE :4:L0,3 -MOVE :4:L6,1 -MOVE :4:L3,4 -MOVE :4:I6,5 -MOVE :4:I1,5 -MOVE :4:I2,6 -MOVE :4:I3,6 -MOVE :4:I4,5 -MOVE :4:I5,6 -MOVE :4:L5,5 -MOVE :4:I6,4 -MOVE :4:I4,2 diff --git a/apps/plugins/puzzles/src/icons/loopy.sav b/apps/plugins/puzzles/src/icons/loopy.sav deleted file mode 100644 index 11611818af..0000000000 --- a/apps/plugins/puzzles/src/icons/loopy.sav +++ /dev/null @@ -1,120 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Loopy -PARAMS :7:7x7t0de -CPARAMS :7:7x7t0de -DESC :31:02g222b3b2e2a2b322b2a2a3a2a1d1b -NSTATES :3:113 -STATEPOS:2:75 -MOVE :2:3n -MOVE :2:0n -MOVE :2:1n -MOVE :2:2n -MOVE :2:4n -MOVE :2:6y -MOVE :2:5y -MOVE :3:25n -MOVE :2:9n -MOVE :3:28y -MOVE :3:27y -MOVE :3:42n -MOVE :3:30n -MOVE :3:45y -MOVE :3:44y -MOVE :3:59n -MOVE :3:47n -MOVE :3:62y -MOVE :3:61y -MOVE :3:76n -MOVE :3:64n -MOVE :3:79y -MOVE :3:78y -MOVE :3:93n -MOVE :3:81n -MOVE :4:110y -MOVE :4:111y -MOVE :3:99y -MOVE :3:98y -MOVE :3:24n -MOVE :3:39y -MOVE :3:23y -MOVE :3:22y -MOVE :3:26n -MOVE :3:37n -MOVE :3:38y -MOVE :3:54n -MOVE :3:69y -MOVE :3:53y -MOVE :3:40y -MOVE :2:7y -MOVE :3:88y -MOVE :3:87y -MOVE :4:102n -MOVE :3:90n -MOVE :3:52n -MOVE :3:41y -MOVE :3:55n -MOVE :3:43n -MOVE :2:8n -MOVE :3:10y -MOVE :3:12y -MOVE :3:11n -MOVE :3:13y -MOVE :3:29n -MOVE :3:15y -MOVE :3:14n -MOVE :3:16y -MOVE :3:32y -MOVE :3:31n -MOVE :3:18y -MOVE :3:17n -MOVE :3:19y -MOVE :3:20y -MOVE :3:21n -MOVE :3:33y -MOVE :3:35y -MOVE :3:36n -MOVE :3:50y -MOVE :3:57y -MOVE :3:58y -MOVE :3:72n -MOVE :3:60n -MOVE :3:74y -MOVE :4:104y -MOVE :4:107n -MOVE :4:106n -MOVE :3:92n -MOVE :4:109n -MOVE :3:89y -MOVE :3:77n -MOVE :3:75n -MOVE :3:73y -MOVE :3:85n -MOVE :3:70n -MOVE :3:56y -MOVE :3:67n -MOVE :3:71y -MOVE :3:68y -MOVE :3:84n -MOVE :3:82n -MOVE :3:83y -MOVE :3:97n -MOVE :3:86y -MOVE :4:101y -MOVE :4:100n -MOVE :4:103y -MOVE :4:105y -MOVE :4:108y -MOVE :3:96n -MOVE :3:95y -MOVE :3:94y -MOVE :3:91y -MOVE :3:80y -MOVE :3:66n -MOVE :3:65y -MOVE :3:63y -MOVE :3:51n -MOVE :3:46n -MOVE :3:34y -MOVE :3:48n -MOVE :3:49y diff --git a/apps/plugins/puzzles/src/icons/magnets.sav b/apps/plugins/puzzles/src/icons/magnets.sav deleted file mode 100644 index 3c317b70ce..0000000000 --- a/apps/plugins/puzzles/src/icons/magnets.sav +++ /dev/null @@ -1,33 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Magnets -PARAMS :6:6x5dtS -CPARAMS :6:6x5dtS -SEED :15:705856238774945 -DESC :56:2.2..1,.3.2.,2.21..,2..0.,TLRTLRBLRBTTLRLRBBLRTTTTLRBBBB -AUXINFO :60:ebae280db3eec279c628b6cfe4aca5a03ba24d7eba91169f1bdf275fce3f -NSTATES :2:24 -STATEPOS:2:15 -MOVE :4:.1,3 -MOVE :4:.0,1 -MOVE :4:?0,1 -MOVE :4:.2,1 -MOVE :4:?2,1 -MOVE :4:.2,4 -MOVE :4:?2,4 -MOVE :4:+2,3 -MOVE :4:.3,3 -MOVE :4:.0,2 -MOVE :4:?0,2 -MOVE :4:+1,4 -MOVE :4:+0,2 -MOVE :4:+0,0 -MOVE :4:+1,1 -MOVE :4:.2,2 -MOVE :4:+2,0 -MOVE :4:+3,1 -MOVE :4:.4,0 -MOVE :4:+5,1 -MOVE :4:.5,3 -MOVE :4:+4,3 -MOVE :4:.4,2 diff --git a/apps/plugins/puzzles/src/icons/map.sav b/apps/plugins/puzzles/src/icons/map.sav deleted file mode 100644 index 33863e1eeb..0000000000 --- a/apps/plugins/puzzles/src/icons/map.sav +++ /dev/null @@ -1,27 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :3:Map -PARAMS :10:20x20n30dn -CPARAMS :10:20x20n30dn -SEED :15:794003990129265 -DESC :264:dcbakatgcaaedaccabfabadbaaaagaiaaaeaiadcaaaafabccbdcaaecabggedfaebqbadgbngcblabdaadaaaeagabaaaacaacacbcaebabaabaebaafaaakabdhcdanaaceagakacbbajaaadbacbaaccbcbicdafbadgbaccbkcdaafbacbcaaabcddacaaaaddbabcdbbacabbhagajabbobcdjaecaafabahaaaffead,23a01c3c1a3d2a20b01a3a -AUXINFO :282:738e7a68c5d32445002968f3726646962b3604ef27a3657e0fdc0fd8180d5b747febd4619487bbc8bec5a48c709b154eb8da39c9b49be1e312a381fc2394e53126714079bd82e8444dad92419429635d1c816c53774b8c77b4ce03884c94d12bfb757cd93b5600471cb9726b3f2afe74d9932abeaa2efd6a496cad793ce5b221f943d620e883794f9d56741908 -NSTATES :2:18 -STATEPOS:2:12 -MOVE :4:3:20 -MOVE :4:0:24 -MOVE :4:3:10 -MOVE :4:1:18 -MOVE :4:2:11 -MOVE :4:3:17 -MOVE :4:1:23 -MOVE :4:3:27 -MOVE :4:1:29 -MOVE :4:0:16 -MOVE :4:2:13 -MOVE :3:1:6 -MOVE :3:2:2 -MOVE :3:0:7 -MOVE :3:2:9 -MOVE :4:0:15 -MOVE :3:3:5 diff --git a/apps/plugins/puzzles/src/icons/mines.sav b/apps/plugins/puzzles/src/icons/mines.sav deleted file mode 100644 index a827541163..0000000000 --- a/apps/plugins/puzzles/src/icons/mines.sav +++ /dev/null @@ -1,67 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Mines -PARAMS :6:9x9n35 -CPARAMS :6:9x9n35 -SEED :15:698938038698621 -DESC :26:0,0,me0691ca8a278f3c371688 -PRIVDESC:22:me0691ca8a278f3c371688 -UI :3:D0C -TIME :7:75.2958 -NSTATES :2:56 -STATEPOS:2:41 -MOVE :4:O0,0 -MOVE :4:F1,2 -MOVE :4:F0,2 -MOVE :4:O2,2 -MOVE :4:F2,1 -MOVE :4:F3,1 -MOVE :4:F3,2 -MOVE :4:F3,3 -MOVE :4:F1,3 -MOVE :4:F2,3 -MOVE :4:C1,0 -MOVE :4:C2,0 -MOVE :4:C3,0 -MOVE :4:F5,0 -MOVE :4:F5,1 -MOVE :4:C4,1 -MOVE :4:O6,1 -MOVE :4:O6,2 -MOVE :4:O6,3 -MOVE :4:F7,1 -MOVE :4:O7,4 -MOVE :4:O5,4 -MOVE :4:F5,3 -MOVE :4:F5,5 -MOVE :4:F6,6 -MOVE :4:C6,5 -MOVE :4:F8,6 -MOVE :4:C6,3 -MOVE :4:F8,2 -MOVE :4:C7,2 -MOVE :4:F8,0 -MOVE :4:F7,0 -MOVE :4:F6,0 -MOVE :4:C4,2 -MOVE :4:F4,4 -MOVE :4:F4,5 -MOVE :4:F3,4 -MOVE :4:C5,6 -MOVE :4:F7,7 -MOVE :4:F8,7 -MOVE :4:F7,8 -MOVE :4:O4,8 -MOVE :4:F3,6 -MOVE :4:C4,6 -MOVE :4:F3,8 -MOVE :4:F5,8 -MOVE :4:C6,7 -MOVE :4:C3,7 -MOVE :4:F2,5 -MOVE :4:F2,4 -MOVE :4:F1,8 -MOVE :4:F1,7 -MOVE :4:C2,7 -MOVE :4:C2,6 -MOVE :4:C1,6 diff --git a/apps/plugins/puzzles/src/icons/net.sav b/apps/plugins/puzzles/src/icons/net.sav deleted file mode 100644 index 06a5426280..0000000000 --- a/apps/plugins/puzzles/src/icons/net.sav +++ /dev/null @@ -1,53 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :3:Net -PARAMS :3:5x5 -CPARAMS :3:5x5 -DESC :25:1115337157375775157135131 -UI :9:O0,0;C2,2 -NSTATES :2:45 -STATEPOS:2:45 -MOVE :4:C0,0 -MOVE :4:L0,0 -MOVE :4:L0,1 -MOVE :4:C0,2 -MOVE :4:L0,2 -MOVE :4:A0,3 -MOVE :4:L0,3 -MOVE :4:L0,4 -MOVE :4:L1,4 -MOVE :4:A2,4 -MOVE :4:A2,4 -MOVE :4:L2,4 -MOVE :4:C1,0 -MOVE :4:L1,0 -MOVE :4:L3,0 -MOVE :4:L2,0 -MOVE :4:A4,0 -MOVE :4:A4,0 -MOVE :4:L4,0 -MOVE :4:A4,1 -MOVE :4:L4,1 -MOVE :4:L3,1 -MOVE :4:A3,2 -MOVE :4:A3,2 -MOVE :4:L3,2 -MOVE :4:L2,2 -MOVE :4:A2,1 -MOVE :4:A2,1 -MOVE :4:A1,1 -MOVE :4:A1,1 -MOVE :4:A1,1 -MOVE :4:A1,1 -MOVE :4:A1,1 -MOVE :4:A1,1 -MOVE :4:A1,2 -MOVE :4:A1,2 -MOVE :4:A1,3 -MOVE :4:C2,3 -MOVE :4:A3,3 -MOVE :4:A4,4 -MOVE :4:A4,4 -MOVE :4:A4,3 -MOVE :4:A4,2 -MOVE :4:A4,2 diff --git a/apps/plugins/puzzles/src/icons/netslide.sav b/apps/plugins/puzzles/src/icons/netslide.sav deleted file mode 100644 index f37178ee0c..0000000000 --- a/apps/plugins/puzzles/src/icons/netslide.sav +++ /dev/null @@ -1,47 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :8:Netslide -PARAMS :3:4x4 -CPARAMS :3:4x4 -SEED :15:344208514520242 -DESC :16:49b59aca247714b4 -AUXINFO :34:60d28a22f68cdb6078d67a4d6069b9ff54 -NSTATES :2:38 -STATEPOS:2:31 -MOVE :4:R0,1 -MOVE :4:C1,1 -MOVE :4:R1,1 -MOVE :4:R1,1 -MOVE :4:C3,1 -MOVE :4:C1,1 -MOVE :4:R1,1 -MOVE :4:R1,1 -MOVE :4:R3,1 -MOVE :4:R0,1 -MOVE :4:C1,1 -MOVE :5:R3,-1 -MOVE :5:R0,-1 -MOVE :4:R0,1 -MOVE :4:R0,1 -MOVE :4:C0,1 -MOVE :5:R0,-1 -MOVE :5:R0,-1 -MOVE :5:C1,-1 -MOVE :4:R1,1 -MOVE :4:C1,1 -MOVE :5:R1,-1 -MOVE :5:C0,-1 -MOVE :5:C0,-1 -MOVE :4:R3,1 -MOVE :4:C0,1 -MOVE :4:C0,1 -MOVE :5:R3,-1 -MOVE :4:C0,1 -MOVE :5:R3,-1 -MOVE :5:C1,-1 -MOVE :4:R0,1 -MOVE :4:C0,1 -MOVE :5:R0,-1 -MOVE :5:C0,-1 -MOVE :4:C1,1 -MOVE :4:R3,1 diff --git a/apps/plugins/puzzles/src/icons/palisade.sav b/apps/plugins/puzzles/src/icons/palisade.sav deleted file mode 100644 index a935e890bb..0000000000 --- a/apps/plugins/puzzles/src/icons/palisade.sav +++ /dev/null @@ -1,50 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :8:Palisade -PARAMS :5:5x5n5 -CPARAMS :5:5x5n5 -SEED :15:930059588777257 -DESC :13:2d23c33e2c1b2 -AUXINFO :52:14a191be1282597737537139d11d87fb4f21ad4a8f31e67b4441 -NSTATES :2:41 -STATEPOS:2:27 -MOVE :14:F0,1,16F0,0,64 -MOVE :15:F0,0,32F1,0,128 -MOVE :12:F1,1,8F0,1,2 -MOVE :12:F1,0,4F1,1,1 -MOVE :14:F0,2,16F0,1,64 -MOVE :12:F1,2,8F0,2,2 -MOVE :12:F0,3,1F0,2,4 -MOVE :15:F2,0,128F1,0,32 -MOVE :12:F2,0,4F2,1,1 -MOVE :12:F3,0,8F2,0,2 -MOVE :15:F1,4,128F0,4,32 -MOVE :14:F1,4,16F1,3,64 -MOVE :15:F2,4,128F1,4,32 -MOVE :14:F0,3,64F0,4,16 -MOVE :15:F1,3,128F0,3,32 -MOVE :12:F1,3,1F1,2,4 -MOVE :15:F4,4,128F3,4,32 -MOVE :14:F4,4,16F4,3,64 -MOVE :12:F3,4,8F2,4,2 -MOVE :12:F2,4,1F2,3,4 -MOVE :12:F2,3,8F1,3,2 -MOVE :14:F2,2,64F2,3,16 -MOVE :15:F2,3,32F3,3,128 -MOVE :12:F3,3,4F3,4,1 -MOVE :12:F4,3,8F3,3,2 -MOVE :14:F4,3,16F4,2,64 -MOVE :12:F1,2,1F1,1,4 -MOVE :15:F2,1,128F1,1,32 -MOVE :15:F2,2,128F1,2,32 -MOVE :12:F2,2,1F2,1,4 -MOVE :15:F3,2,128F2,2,32 -MOVE :14:F3,2,64F3,3,16 -MOVE :12:F4,2,8F3,2,2 -MOVE :12:F3,2,1F3,1,4 -MOVE :15:F2,1,32F3,1,128 -MOVE :14:F4,2,16F4,1,64 -MOVE :12:F4,1,8F3,1,2 -MOVE :14:F3,0,64F3,1,16 -MOVE :15:F4,0,128F3,0,32 -MOVE :12:F4,1,1F4,0,4 diff --git a/apps/plugins/puzzles/src/icons/pattern.sav b/apps/plugins/puzzles/src/icons/pattern.sav deleted file mode 100644 index 97c2396052..0000000000 --- a/apps/plugins/puzzles/src/icons/pattern.sav +++ /dev/null @@ -1,29 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Pattern -PARAMS :5:10x10 -CPARAMS :5:10x10 -DESC :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 -NSTATES :2:22 -STATEPOS:2:22 -MOVE :8:F6,4,1,2 -MOVE :8:F7,4,1,2 -MOVE :8:F4,5,2,1 -MOVE :8:F5,4,1,1 -MOVE :8:E0,5,2,1 -MOVE :8:E0,4,3,1 -MOVE :8:F0,6,1,4 -MOVE :8:F0,1,1,2 -MOVE :8:F0,1,5,1 -MOVE :8:E1,2,1,1 -MOVE :8:F2,0,1,4 -MOVE :8:E3,2,1,1 -MOVE :8:F3,0,1,1 -MOVE :8:F4,0,1,1 -MOVE :8:F3,3,1,1 -MOVE :8:E6,3,4,1 -MOVE :8:F6,6,1,4 -MOVE :8:F7,6,1,4 -MOVE :8:E6,0,1,4 -MOVE :8:E7,0,1,3 -MOVE :8:E5,1,1,1 diff --git a/apps/plugins/puzzles/src/icons/pearl.sav b/apps/plugins/puzzles/src/icons/pearl.sav deleted file mode 100644 index 730ca85149..0000000000 --- a/apps/plugins/puzzles/src/icons/pearl.sav +++ /dev/null @@ -1,23 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Pearl -PARAMS :5:6x6dt -CPARAMS :5:6x6dt -SEED :15:901944054393278 -DESC :17:BbBfWcWbWBaBeWgWa -AUXINFO :72:f8bbe71b9be753d5fa143df207d7797ba62a9b3996eb8b8889487e1a2bd659d91a5e73e1 -NSTATES :2:14 -STATEPOS:1:7 -MOVE :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 -MOVE :27:F1,0,3;F4,1,3;F1,1,3;F4,2,3 -MOVE :27:F8,3,0;F2,3,1;F8,3,1;F2,3,2 -MOVE :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 -MOVE :13:F4,4,2;F1,3,2 -MOVE :13:F4,3,0;F1,2,0 -MOVE :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 -MOVE :41:F8,0,3;F2,0,4;F8,0,4;F2,0,5;F1,0,5;F4,1,5 -MOVE :27:F1,1,4;F4,2,4;F1,2,4;F4,3,4 -MOVE :13:F8,1,4;F2,1,5 -MOVE :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 -MOVE :13:F2,3,5;F8,3,4 -MOVE :13:F2,4,4;F8,4,3 diff --git a/apps/plugins/puzzles/src/icons/pegs.sav b/apps/plugins/puzzles/src/icons/pegs.sav deleted file mode 100644 index 22b8a0d82f..0000000000 --- a/apps/plugins/puzzles/src/icons/pegs.sav +++ /dev/null @@ -1,16 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :4:Pegs -PARAMS :8:7x7cross -CPARAMS :8:7x7cross -SEED :15:103342250484448 -DESC :49:OOPPPOOOOPPPOOPPPPPPPPPPHPPPPPPPPPPOOPPPOOOOPPPOO -NSTATES :1:8 -STATEPOS:1:8 -MOVE :7:3,1-3,3 -MOVE :7:5,2-3,2 -MOVE :7:5,4-5,2 -MOVE :7:3,4-5,4 -MOVE :7:6,4-4,4 -MOVE :7:4,0-4,2 -MOVE :7:2,0-4,0 diff --git a/apps/plugins/puzzles/src/icons/range.sav b/apps/plugins/puzzles/src/icons/range.sav deleted file mode 100644 index 708e7db248..0000000000 --- a/apps/plugins/puzzles/src/icons/range.sav +++ /dev/null @@ -1,36 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Range -PARAMS :3:7x7 -CPARAMS :3:7x7 -SEED :15:989032078841515 -DESC :22:d7b3e8e5c7a7c13e4e8b4d -UI :1:0 -NSTATES :2:27 -STATEPOS:2:27 -MOVE :5:W,4,2 -MOVE :5:W,4,3 -MOVE :5:W,4,4 -MOVE :5:W,4,5 -MOVE :5:W,4,6 -MOVE :5:W,4,0 -MOVE :5:W,3,1 -MOVE :5:W,2,1 -MOVE :5:W,1,1 -MOVE :5:W,0,1 -MOVE :5:W,6,1 -MOVE :5:W,5,1 -MOVE :5:W,5,5 -MOVE :5:W,1,5 -MOVE :5:B,5,2 -MOVE :5:W,5,3 -MOVE :5:W,6,3 -MOVE :5:W,3,6 -MOVE :5:W,2,6 -MOVE :5:B,3,5 -MOVE :5:W,2,4 -MOVE :5:W,2,2 -MOVE :5:B,2,3 -MOVE :5:W,1,3 -MOVE :5:W,3,3 -MOVE :5:W,0,5 diff --git a/apps/plugins/puzzles/src/icons/rect.sav b/apps/plugins/puzzles/src/icons/rect.sav deleted file mode 100644 index 17264daeeb..0000000000 --- a/apps/plugins/puzzles/src/icons/rect.sav +++ /dev/null @@ -1,17 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :10:Rectangles -PARAMS :3:7x7 -CPARAMS :3:7x7 -DESC :33:a3d2b2a3_2a4a8h2a3c4_2b2c3a3_3c3b -NSTATES :2:10 -STATEPOS:2:10 -MOVE :8:R0,6,3,1 -MOVE :8:R6,4,1,3 -MOVE :8:R3,6,3,1 -MOVE :8:R4,4,2,1 -MOVE :8:R3,5,3,1 -MOVE :8:R6,1,1,3 -MOVE :8:R5,0,2,1 -MOVE :8:R5,1,1,2 -MOVE :8:R4,3,2,1 diff --git a/apps/plugins/puzzles/src/icons/samegame.sav b/apps/plugins/puzzles/src/icons/samegame.sav deleted file mode 100644 index f92b52d1b6..0000000000 --- a/apps/plugins/puzzles/src/icons/samegame.sav +++ /dev/null @@ -1,34 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :9:Same Game -PARAMS :9:10x10c3s2 -CPARAMS :9:10x10c3s2 -SEED :15:785412408200083 -DESC :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 -NSTATES :2:26 -STATEPOS:2:13 -MOVE :6:M94,95 -MOVE :6:M83,84 -MOVE :9:M83,93,94 -MOVE :6:M93,94 -MOVE :6:M20,21 -MOVE :15:M20,21,22,31,32 -MOVE :6:M70,71 -MOVE :6:M80,90 -MOVE :9:M73,82,83 -MOVE :18:M72,73,74,82,83,92 -MOVE :12:M51,61,62,72 -MOVE :9:M35,36,46 -MOVE :12:M49,57,58,59 -MOVE :6:M59,69 -MOVE :9:M69,79,89 -MOVE :12:M78,79,89,99 -MOVE :24:M45,46,47,54,55,57,64,67 -MOVE :36:M36,46,55,56,57,66,67,68,77,78,88,98 -MOVE :9:M76,77,87 -MOVE :6:M97,98 -MOVE :6:M94,95 -MOVE :45:M50,60,61,70,71,81,82,83,84,85,90,91,92,93,94 -MOVE :12:M73,81,82,83 -MOVE :6:M92,93 -MOVE :9:M81,90,91 diff --git a/apps/plugins/puzzles/src/icons/screenshot.sh b/apps/plugins/puzzles/src/icons/screenshot.sh deleted file mode 100755 index 0e2a06eea7..0000000000 --- a/apps/plugins/puzzles/src/icons/screenshot.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -# Generate a screenshot from a puzzle save file. Takes the -# following arguments, in order: -# -# - the name of the puzzle binary -# - the name of the save file -# - the name of the output image file -# - (optionally) the proportion of the next move to redo before -# taking the screenshot. -# -# This script requires access to an X server in order to run, but -# seems to work fine under xvfb-run if you haven't got a real one -# available (or if you don't want to use it for some reason). - -binary="$1" -save="$2" -image="$3" -if test "x$4" != "x"; then - redo="--redo $4" -else - redo= -fi - -"$binary" $redo --screenshot "$image" --load "$save" diff --git a/apps/plugins/puzzles/src/icons/signpost.sav b/apps/plugins/puzzles/src/icons/signpost.sav deleted file mode 100644 index 9ad1958ddf..0000000000 --- a/apps/plugins/puzzles/src/icons/signpost.sav +++ /dev/null @@ -1,23 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :8:Signpost -PARAMS :4:4x4c -CPARAMS :4:4x4c -SEED :15:230468784719861 -DESC :19:1eceebecfbfhgcaa16a -NSTATES :2:15 -STATEPOS:2:11 -MOVE :8:L2,1-3,1 -MOVE :8:L0,1-1,0 -MOVE :8:L2,2-1,1 -MOVE :8:L1,2-0,3 -MOVE :8:L0,2-2,0 -MOVE :8:L1,3-1,2 -MOVE :8:L1,1-1,3 -MOVE :8:L1,0-3,0 -MOVE :8:L0,0-0,1 -MOVE :8:L3,0-3,2 -MOVE :8:L3,2-0,2 -MOVE :8:L3,1-2,2 -MOVE :8:L2,3-2,1 -MOVE :8:L2,0-2,3 diff --git a/apps/plugins/puzzles/src/icons/singles.sav b/apps/plugins/puzzles/src/icons/singles.sav deleted file mode 100644 index 260fd1f2b3..0000000000 --- a/apps/plugins/puzzles/src/icons/singles.sav +++ /dev/null @@ -1,45 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Singles -PARAMS :5:6x6dk -CPARAMS :5:6x6dk -SEED :15:781273601054598 -DESC :36:361566412253452144234115163346553461 -NSTATES :2:37 -STATEPOS:2:22 -MOVE :4:B1,0 -MOVE :4:C0,0 -MOVE :4:C1,1 -MOVE :4:C2,0 -MOVE :4:C0,1 -MOVE :4:B0,2 -MOVE :4:C0,3 -MOVE :4:C1,2 -MOVE :4:C4,3 -MOVE :4:B3,3 -MOVE :4:C3,2 -MOVE :4:C2,3 -MOVE :4:C3,4 -MOVE :4:B2,4 -MOVE :4:C1,4 -MOVE :4:C2,5 -MOVE :4:B1,5 -MOVE :4:C0,5 -MOVE :4:C0,4 -MOVE :4:C1,3 -MOVE :4:C3,5 -MOVE :4:B5,4 -MOVE :4:C4,4 -MOVE :4:C5,5 -MOVE :4:C5,3 -MOVE :4:C4,5 -MOVE :4:B4,0 -MOVE :4:C3,0 -MOVE :4:C4,1 -MOVE :4:C5,0 -MOVE :4:C5,1 -MOVE :4:B4,2 -MOVE :4:C5,2 -MOVE :4:C3,1 -MOVE :4:B2,1 -MOVE :4:C2,2 diff --git a/apps/plugins/puzzles/src/icons/sixteen.sav b/apps/plugins/puzzles/src/icons/sixteen.sav deleted file mode 100644 index 076b1fbd4d..0000000000 --- a/apps/plugins/puzzles/src/icons/sixteen.sav +++ /dev/null @@ -1,39 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Sixteen -PARAMS :3:4x4 -CPARAMS :3:4x4 -SEED :15:601798566229573 -DESC :38:2,16,3,10,13,8,7,4,9,14,12,11,15,1,5,6 -NSTATES :2:31 -STATEPOS:2:24 -MOVE :5:C3,-1 -MOVE :4:R0,1 -MOVE :4:C1,1 -MOVE :5:R0,-1 -MOVE :5:C1,-1 -MOVE :5:C3,-1 -MOVE :4:R2,1 -MOVE :4:R2,1 -MOVE :4:C3,1 -MOVE :5:C2,-1 -MOVE :5:C2,-1 -MOVE :4:R1,1 -MOVE :4:R1,1 -MOVE :4:C2,1 -MOVE :4:C2,1 -MOVE :4:R3,1 -MOVE :4:R3,1 -MOVE :4:C1,1 -MOVE :5:R2,-1 -MOVE :5:R2,-1 -MOVE :5:C1,-1 -MOVE :4:R2,1 -MOVE :4:C0,1 -MOVE :4:R3,1 -MOVE :5:R2,-1 -MOVE :5:C1,-1 -MOVE :4:R2,1 -MOVE :4:C1,1 -MOVE :5:R3,-1 -MOVE :5:C0,-1 diff --git a/apps/plugins/puzzles/src/icons/slant.sav b/apps/plugins/puzzles/src/icons/slant.sav deleted file mode 100644 index 02017e5d48..0000000000 --- a/apps/plugins/puzzles/src/icons/slant.sav +++ /dev/null @@ -1,51 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Slant -PARAMS :5:8x8de -CPARAMS :5:8x8de -DESC :47:a10h23a32a02b22e3a2c1g3a20d32a0c221a210i0a101b0 -NSTATES :2:44 -STATEPOS:2:44 -MOVE :4:/7,0 -MOVE :4:\7,1 -MOVE :4:\1,0 -MOVE :4:/2,0 -MOVE :4:\0,4 -MOVE :4:/0,5 -MOVE :4:\0,6 -MOVE :4:/0,7 -MOVE :4:\1,7 -MOVE :4:/7,7 -MOVE :4:/3,7 -MOVE :4:\4,7 -MOVE :4:\5,7 -MOVE :4:/2,7 -MOVE :4:/7,4 -MOVE :4:\7,5 -MOVE :4:\7,3 -MOVE :4:\7,2 -MOVE :4:/6,2 -MOVE :4:\6,3 -MOVE :4:\7,6 -MOVE :4:/3,0 -MOVE :4:/2,1 -MOVE :4:\3,1 -MOVE :4:/2,2 -MOVE :4:\3,2 -MOVE :4:/2,3 -MOVE :4:\3,3 -MOVE :4:\1,1 -MOVE :4:/0,1 -MOVE :4:\0,2 -MOVE :4:\1,2 -MOVE :4:\1,3 -MOVE :4:/0,3 -MOVE :4:\1,4 -MOVE :4:\0,0 -MOVE :4:\5,3 -MOVE :4:\6,4 -MOVE :4:/5,4 -MOVE :4:/5,5 -MOVE :4:\6,5 -MOVE :4:/4,5 -MOVE :4:\4,6 diff --git a/apps/plugins/puzzles/src/icons/solo.sav b/apps/plugins/puzzles/src/icons/solo.sav deleted file mode 100644 index 385cc68fe5..0000000000 --- a/apps/plugins/puzzles/src/icons/solo.sav +++ /dev/null @@ -1,36 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :4:Solo -PARAMS :3:3x3 -CPARAMS :3:3x3 -DESC :73:a2e9a5b6a2b3_7a1_4a9_6a2b4_1a7_2b1_7e6_9b2_5a8_1b6a5_9a3_8a7_2b8a6b1a1e4a -NSTATES :2:29 -STATEPOS:2:29 -MOVE :6:R7,1,1 -MOVE :6:R4,6,1 -MOVE :6:R5,0,1 -MOVE :6:R0,0,4 -MOVE :6:R2,0,6 -MOVE :6:R1,2,3 -MOVE :6:R0,3,9 -MOVE :6:R1,3,5 -MOVE :6:R2,4,8 -MOVE :6:R0,5,3 -MOVE :6:R1,5,6 -MOVE :6:R1,6,4 -MOVE :6:R4,7,4 -MOVE :6:R7,6,2 -MOVE :6:R7,7,5 -MOVE :6:R8,8,6 -MOVE :6:R7,3,3 -MOVE :6:R8,3,7 -MOVE :6:R8,3,8 -MOVE :6:R6,4,5 -MOVE :6:R7,5,7 -MOVE :6:R8,5,4 -MOVE :6:R7,2,8 -MOVE :6:R8,0,5 -MOVE :6:R4,2,5 -MOVE :6:R4,3,6 -MOVE :6:R5,4,4 -MOVE :6:R4,5,9 diff --git a/apps/plugins/puzzles/src/icons/square.pl b/apps/plugins/puzzles/src/icons/square.pl deleted file mode 100755 index 815b94b532..0000000000 --- a/apps/plugins/puzzles/src/icons/square.pl +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/perl - -# Read an input image, crop its border to a standard width, and -# convert it into a square output image. Parameters are: -# -# - the required total image size -# - the output border thickness -# - the input image file name -# - the output image file name. - -($osize, $oborder, $infile, $outfile) = @ARGV; - -# Determine the input image's size. -$ident = `identify -format "%w %h" $infile`; -$ident =~ /(\d+) (\d+)/ or die "unable to get size for $infile\n"; -($w, $h) = ($1, $2); - -# Read the input image data. -$data = []; -open IDATA, "convert -depth 8 $infile rgb:- |"; -push @$data, $rgb while (read IDATA,$rgb,3,0) == 3; -close IDATA; -# Check we have the right amount of data. -$xl = $w * $h; -$al = scalar @$data; -die "wrong amount of image data ($al, expected $xl) from $infile\n" - unless $al == $xl; - -# Find the background colour, by looking around the entire border -# and finding the most popular pixel colour. -for ($i = 0; $i < $w; $i++) { - $pcount{$data->[$i]}++; # top row - $pcount{$data->[($h-1)*$w+$i]}++; # bottom row -} -for ($i = 1; $i < $h-1; $i++) { - $pcount{$data->[$i*$w]}++; # left column - $pcount{$data->[$i*$w+$w-1]}++; # right column -} -@plist = sort { $pcount{$b} <=> $pcount{$a} } keys %pcount; -$back = $plist[0]; - -# Crop rows and columns off the image to find the central rectangle -# of non-background stuff. -$ystart = 0; -$ystart++ while $ystart < $h and scalar(grep { $_ ne $back } map { $data->[$ystart*$w+$_] } 0 .. ($w-1)) == 0; -$yend = $h-1; -$yend-- while $yend >= $ystart and scalar(grep { $_ ne $back } map { $data->[$yend*$w+$_] } 0 .. ($w-1)) == 0; -$xstart = 0; -$xstart++ while $xstart < $w and scalar(grep { $_ ne $back } map { $data->[$_*$w+$xstart] } 0 .. ($h-1)) == 0; -$xend = $w-1; -$xend-- while $xend >= $xstart and scalar(grep { $_ ne $back } map { $data->[$_*$w+$xend] } 0 .. ($h-1)) == 0; - -# Decide how much border we're going to put back on to make the -# image perfectly square. -$hexpand = ($yend-$ystart) - ($xend-$xstart); -if ($hexpand > 0) { - $left = int($hexpand / 2); - $xstart -= $left; - $xend += $hexpand - $left; -} elsif ($hexpand < 0) { - $vexpand = -$hexpand; - $top = int($vexpand / 2); - $ystart -= $top; - $yend += $vexpand - $top; -} -$ow = $xend - $xstart + 1; -$oh = $yend - $ystart + 1; -die "internal computation problem" if $ow != $oh; # should be square - -# And decide how much _more_ border goes on to add the bit around -# the edge. -$realow = int($ow * ($osize / ($osize - 2*$oborder))); -$extra = $realow - $ow; -$left = int($extra / 2); -$xstart -= $left; -$xend += $extra - $left; -$top = int($extra / 2); -$ystart -= $top; -$yend += $extra - $top; -$ow = $xend - $xstart + 1; -$oh = $yend - $ystart + 1; -die "internal computation problem" if $ow != $oh; # should be square - -# Now write out the resulting image, and resize it appropriately. -open IDATA, "| convert -size ${ow}x${oh} -depth 8 -resize ${osize}x${osize}! rgb:- $outfile"; -for ($y = $ystart; $y <= $yend; $y++) { - for ($x = $xstart; $x <= $xend; $x++) { - if ($x >= 0 && $x < $w && $y >= 0 && $y < $h) { - print IDATA $data->[$y*$w+$x]; - } else { - print IDATA $back; - } - } -} -close IDATA; diff --git a/apps/plugins/puzzles/src/icons/tents.sav b/apps/plugins/puzzles/src/icons/tents.sav deleted file mode 100644 index 292c2d2d75..0000000000 --- a/apps/plugins/puzzles/src/icons/tents.sav +++ /dev/null @@ -1,32 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :5:Tents -PARAMS :5:8x8de -CPARAMS :5:8x8de -DESC :45:ea_ddidfaabkd,3,0,2,1,2,2,1,1,3,1,1,1,1,1,3,1 -NSTATES :2:25 -STATEPOS:2:25 -MOVE :9:N5,6;N6,6 -MOVE :14:N5,7;N6,7;N7,7 -MOVE :9:N0,7;N1,7 -MOVE :4:N1,6 -MOVE :14:N6,2;N6,3;N6,4 -MOVE :9:N7,2;N7,3 -MOVE :14:N1,0;N2,0;N3,0 -MOVE :4:N3,1 -MOVE :4:N0,3 -MOVE :4:N3,4 -MOVE :4:N5,4 -MOVE :4:T6,0 -MOVE :4:T4,0 -MOVE :4:T0,0 -MOVE :4:N1,1 -MOVE :4:N4,1 -MOVE :9:N6,1;N7,1 -MOVE :4:T2,1 -MOVE :9:N1,2;N3,2 -MOVE :4:T5,2 -MOVE :4:N4,2 -MOVE :4:N5,3 -MOVE :4:N0,2 -MOVE :9:N1,3;N1,5 diff --git a/apps/plugins/puzzles/src/icons/towers.sav b/apps/plugins/puzzles/src/icons/towers.sav deleted file mode 100644 index 351d473a63..0000000000 --- a/apps/plugins/puzzles/src/icons/towers.sav +++ /dev/null @@ -1,26 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :6:Towers -PARAMS :3:4de -CPARAMS :3:4de -SEED :15:888431554483015 -DESC :31:2/2/1/3/2/2/3/1/3/1/2/2/2/3/2/1 -AUXINFO :34:297d7a2fcf9e14403a74c976fe0fefd306 -NSTATES :2:17 -STATEPOS:2:10 -MOVE :6:R2,0,4 -MOVE :6:R0,1,4 -MOVE :6:R3,3,4 -MOVE :6:R1,2,4 -MOVE :6:R0,3,3 -MOVE :6:R1,0,3 -MOVE :6:R3,2,3 -MOVE :6:R2,1,3 -MOVE :6:R3,0,2 -MOVE :6:R3,1,1 -MOVE :6:R1,1,2 -MOVE :6:R1,3,1 -MOVE :6:R2,3,2 -MOVE :6:R2,2,1 -MOVE :6:R0,2,2 -MOVE :6:R0,0,1 diff --git a/apps/plugins/puzzles/src/icons/tracks.sav b/apps/plugins/puzzles/src/icons/tracks.sav deleted file mode 100644 index d13ef95f06..0000000000 --- a/apps/plugins/puzzles/src/icons/tracks.sav +++ /dev/null @@ -1,31 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :12:Tracks -PARAMS :5:6x6dt -CPARAMS :5:6x6dt -SEED :15:145870397370785 -DESC :31:l6t9b,3,2,1,S4,5,4,2,6,S3,2,3,3 -NSTATES :2:23 -STATEPOS:2:20 -MOVE :5:TD0,0 -MOVE :5:TR0,0 -MOVE :5:TL5,5 -MOVE :5:TU5,5 -MOVE :29:TS1,1;TS2,1;TS3,1;TS4,1;TS5,1 -MOVE :29:NS2,0;NS2,2;NS2,3;NS2,4;NS2,5 -MOVE :5:TU1,1 -MOVE :17:NS0,3;NS0,4;NS0,5 -MOVE :23:NS1,2;NS1,3;NS1,4;NS1,5 -MOVE :5:TL2,1 -MOVE :5:TL3,1 -MOVE :5:TS4,4 -MOVE :5:TS3,4 -MOVE :17:NS3,0;NS4,0;NS5,0 -MOVE :5:TS4,2 -MOVE :5:TS4,3 -MOVE :5:TU3,4 -MOVE :5:TL4,4 -MOVE :5:TL5,1 -MOVE :5:TU5,2 -MOVE :5:NS5,3 -MOVE :5:NS3,2 diff --git a/apps/plugins/puzzles/src/icons/twiddle.sav b/apps/plugins/puzzles/src/icons/twiddle.sav deleted file mode 100644 index 2863033f99..0000000000 --- a/apps/plugins/puzzles/src/icons/twiddle.sav +++ /dev/null @@ -1,35 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Twiddle -PARAMS :5:3x3n2 -CPARAMS :5:3x3n2 -SEED :15:635499951462226 -DESC :17:3,7,2,6,5,1,8,4,9 -NSTATES :2:27 -STATEPOS:2:22 -MOVE :7:M0,0,-1 -MOVE :7:M1,0,-1 -MOVE :6:M1,1,1 -MOVE :6:M0,1,1 -MOVE :6:M0,0,1 -MOVE :6:M0,0,1 -MOVE :7:M1,1,-1 -MOVE :7:M0,1,-1 -MOVE :7:M0,1,-1 -MOVE :7:M1,1,-1 -MOVE :6:M0,1,1 -MOVE :7:M0,1,-1 -MOVE :6:M1,1,1 -MOVE :6:M1,1,1 -MOVE :6:M0,1,1 -MOVE :6:M0,1,1 -MOVE :7:M0,1,-1 -MOVE :7:M1,1,-1 -MOVE :7:M0,1,-1 -MOVE :7:M1,1,-1 -MOVE :6:M0,1,1 -MOVE :7:M1,0,-1 -MOVE :7:M0,1,-1 -MOVE :6:M1,0,1 -MOVE :6:M1,1,1 -MOVE :6:M1,1,1 diff --git a/apps/plugins/puzzles/src/icons/undead.sav b/apps/plugins/puzzles/src/icons/undead.sav deleted file mode 100644 index 3c314dde8a..0000000000 --- a/apps/plugins/puzzles/src/icons/undead.sav +++ /dev/null @@ -1,14 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :6:Undead -PARAMS :6:4x4de2 -CPARAMS :6:4x4de2 -DESC :48:5,2,2,aRLgLLaLRL,2,0,1,2,1,1,2,5,0,0,0,2,1,3,1,1 -NSTATES :1:7 -STATEPOS:1:7 -MOVE :2:G0 -MOVE :2:V0 -MOVE :2:G2 -MOVE :2:G3 -MOVE :2:V3 -MOVE :2:Z3 diff --git a/apps/plugins/puzzles/src/icons/unequal.sav b/apps/plugins/puzzles/src/icons/unequal.sav deleted file mode 100644 index c414513a22..0000000000 --- a/apps/plugins/puzzles/src/icons/unequal.sav +++ /dev/null @@ -1,25 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :7:Unequal -PARAMS :3:4de -CPARAMS :3:4de -SEED :15:143029490219212 -DESC :37:0D,0,0L,0,0,0,0,0,0D,0U,0R,0,0,0,0,4, -AUXINFO :34:f51274dc41e0a39caa38942fc525ed0108 -NSTATES :2:16 -STATEPOS:1:6 -MOVE :6:R2,1,4 -MOVE :6:R1,2,4 -MOVE :6:R0,0,4 -MOVE :6:R2,3,1 -MOVE :6:R3,2,1 -MOVE :6:R0,2,3 -MOVE :6:R2,2,2 -MOVE :6:R0,3,2 -MOVE :6:R1,3,3 -MOVE :6:R0,1,1 -MOVE :6:R1,1,2 -MOVE :6:R3,1,3 -MOVE :6:R3,0,2 -MOVE :6:R2,0,3 -MOVE :6:R1,0,1 diff --git a/apps/plugins/puzzles/src/icons/unruly.sav b/apps/plugins/puzzles/src/icons/unruly.sav deleted file mode 100644 index 0f7ca1b9dd..0000000000 --- a/apps/plugins/puzzles/src/icons/unruly.sav +++ /dev/null @@ -1,22 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :6:Unruly -PARAMS :5:6x6de -CPARAMS :5:6x6de -DESC :10:faCADAJeBd -NSTATES :2:15 -STATEPOS:2:15 -MOVE :6:P0,1,2 -MOVE :6:P0,4,2 -MOVE :6:P0,3,3 -MOVE :6:P0,3,0 -MOVE :6:P0,5,1 -MOVE :6:P0,2,1 -MOVE :6:P1,4,0 -MOVE :6:P1,1,1 -MOVE :6:P1,5,2 -MOVE :6:P0,0,2 -MOVE :6:P1,0,3 -MOVE :6:P1,0,0 -MOVE :6:P1,0,4 -MOVE :6:P0,2,4 diff --git a/apps/plugins/puzzles/src/icons/untangle.sav b/apps/plugins/puzzles/src/icons/untangle.sav deleted file mode 100644 index 016318a521..0000000000 --- a/apps/plugins/puzzles/src/icons/untangle.sav +++ /dev/null @@ -1,16 +0,0 @@ -SAVEFILE:41:Simon Tatham's Portable Puzzle Collection -VERSION :1:1 -GAME :8:Untangle -PARAMS :2:10 -CPARAMS :2:10 -SEED :15:761628688787632 -DESC :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 -AUXINFO :182:01bee8258e3164fe966f294b2837b6584b965b8d8e97571ba48f26c9bc0a91ac4b49fb4652bfaa5c340c82c57afbaa4620f2f6d49d7a7b330a66594d2b88c499d57c4093379b7dc322f2afa1ebab81004585751c39c19f8f9930c4 -NSTATES :1:7 -STATEPOS:1:6 -MOVE :12:P8:168,16/64 -MOVE :12:P0:186,85/64 -MOVE :12:P2:47,254/64 -MOVE :13:P5:131,153/64 -MOVE :12:P3:75,126/64 -MOVE :12:P7:93,303/64 diff --git a/apps/plugins/puzzles/src/icons/win16pal.xpm b/apps/plugins/puzzles/src/icons/win16pal.xpm deleted file mode 100644 index 66fd60a480..0000000000 --- a/apps/plugins/puzzles/src/icons/win16pal.xpm +++ /dev/null @@ -1,23 +0,0 @@ -/* XPM */ -static char *win16pal[] = { -/* columns rows colors chars-per-pixel */ -"16 1 16 1", -" c #000000", -". c #800000", -"X c #008000", -"o c #808000", -"O c #000080", -"+ c #800080", -"@ c #008080", -"# c #C0C0C0", -"$ c #808080", -"% c #FF0000", -"& c #00FF00", -"* c #FFFF00", -"= c #0000FF", -"- c #FF00FF", -"; c #00FFFF", -": c #FFFFFF", -/* pixels */ -" .XoO+@#$%&*=-;:" -}; diff --git a/apps/plugins/puzzles/src/makedist.sh b/apps/plugins/puzzles/src/makedist.sh deleted file mode 100755 index 22b4f5d0ae..0000000000 --- a/apps/plugins/puzzles/src/makedist.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh - -# Build a Unix source distribution from the Puzzles SVN area. -# -# Pass a version number argument to have the archive tagged with that -# version number. Otherwise, the script will not version-tag the -# archive at all. - -version="$1" - -if test "x$version" != "x"; then - arcsuffix="-$version" - ver="-DVER=$version" -else - arcsuffix= - ver= -fi - -perl mkfiles.pl -./mkauto.sh - -mkdir tmp.$$ -mkdir tmp.$$/puzzles$arcsuffix -mkdir tmp.$$/puzzles$arcsuffix/icons - -# Build Windows Help and text versions of the manual for convenience. -halibut --winhelp=puzzles.hlp --text=puzzles.txt puzzles.but - -# Build a text version of the HACKING document. -halibut --text=HACKING devel.but - -for i in *.c *.m *.h *.R *.rc *.but *.plist *.icns LICENCE README Recipe \ - *.rc2 mkfiles.pl Makefile Makefile.* \ - HACKING puzzles.txt puzzles.hlp puzzles.cnt puzzles.chm \ - icons/Makefile icons/*.sav icons/*.pl icons/*.sh icons/win16pal.xpm \ - icons/*.png icons/*.ico icons/*.rc icons/*.c \ - configure.ac mkauto.sh aclocal.m4 \ - configure depcomp install-sh missing compile; do - case $i in - */*) ln -s ../../../$i tmp.$$/puzzles$arcsuffix/$i;; - *) ln -s ../../$i tmp.$$/puzzles$arcsuffix/$i;; - esac -done - -tar -C tmp.$$ -chzf - puzzles$arcsuffix > ../puzzles$arcsuffix.tar.gz - -rm -rf tmp.$$ diff --git a/apps/plugins/puzzles/src/midend.c b/apps/plugins/puzzles/src/midend.c index 87605a9da8..f2d27d3e31 100644 --- a/apps/plugins/puzzles/src/midend.c +++ b/apps/plugins/puzzles/src/midend.c @@ -1464,14 +1464,23 @@ void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx) me->game_id_change_notify_ctx = ctx; } -void midend_get_cursor_location(midend *me, int *x_out, int *y_out, int *w_out, int *h_out) +bool midend_get_cursor_location(midend *me, + int *x_out, int *y_out, + int *w_out, int *h_out) { int x, y, w, h; x = y = -1; w = h = 1; if(me->ourgame->get_cursor_location) - me->ourgame->get_cursor_location(me->ui, me->drawstate, me->states[me->statepos-1].state, me->params, &x, &y, &w, &h); + me->ourgame->get_cursor_location(me->ui, + me->drawstate, + me->states[me->statepos-1].state, + me->params, + &x, &y, &w, &h); + + if(x == -1 && y == -1) + return false; if(x_out) *x_out = x; @@ -1481,6 +1490,7 @@ void midend_get_cursor_location(midend *me, int *x_out, int *y_out, int *w_out, *w_out = w; if(h_out) *h_out = h; + return true; } void midend_supersede_game_desc(midend *me, const char *desc, diff --git a/apps/plugins/puzzles/src/mkauto.sh b/apps/plugins/puzzles/src/mkauto.sh deleted file mode 100755 index 297212ad4d..0000000000 --- a/apps/plugins/puzzles/src/mkauto.sh +++ /dev/null @@ -1,2 +0,0 @@ -#! /bin/sh -autoreconf -i && rm -rf autom4te.cache diff --git a/apps/plugins/puzzles/src/mkfiles.pl b/apps/plugins/puzzles/src/mkfiles.pl deleted file mode 100755 index d92bcca11d..0000000000 --- a/apps/plugins/puzzles/src/mkfiles.pl +++ /dev/null @@ -1,1953 +0,0 @@ -#!/usr/bin/env perl -# -# Cross-platform Makefile generator. -# -# Reads the file `Recipe' to determine the list of generated -# executables and their component objects. Then reads the source -# files to compute #include dependencies. Finally, writes out the -# various target Makefiles. - -# PuTTY specifics which could still do with removing: -# - Mac makefile is not portabilised at all. Include directories -# are hardwired, and also the libraries are fixed. This is -# mainly because I was too scared to go anywhere near it. -# - sbcsgen.pl is still run at startup. - -# Other things undone: -# - special-define objects (foo.o[PREPROCSYMBOL]) are not -# supported in the mac or vcproj makefiles. - -use warnings; -use IO::Handle; -use Cwd; -use File::Basename; - -while ($#ARGV >= 0) { - if ($ARGV[0] eq "-U") { - # Convenience for Unix users: -U means that after we finish what - # we're doing here, we also run mkauto.sh and then 'configure'. So - # it's a one-stop shop for regenerating the actual end-product - # Unix makefile. - # - # Arguments supplied after -U go to configure. - $do_unix = 1; - shift @ARGV; - @confargs = @ARGV; - @ARGV = (); - } else { - die "unrecognised command-line argument '$ARGV[0]'\n"; - } -} - -@filestack = (); -$in = new IO::Handle; -open $in, "Recipe" or do { - # We want to deal correctly with being run from one of the - # subdirs in the source tree. So if we can't find Recipe here, - # try one level up. - chdir ".."; - open $in, "Recipe" or die "unable to open Recipe file\n"; -}; -push @filestack, $in; - -# HACK: One of the source files in `charset' is auto-generated by -# sbcsgen.pl. We need to generate that _now_, before attempting -# dependency analysis. -eval 'chdir "charset"; require "sbcsgen.pl"; chdir ".."'; - -@srcdirs = ("./"); - -$divert = undef; # ref to array of refs of scalars in which text is - # currently being put -$help = ""; # list of newline-free lines of help text -$project_name = "project"; # this is a good enough default -%makefiles = (); # maps makefile types to output makefile pathnames -%makefile_extra = (); # maps makefile types to extra Makefile text -%programs = (); # maps prog name + type letter to listref of objects/resources -%groups = (); # maps group name to listref of objects/resources - -@allobjs = (); # all object file names - -readinput: while (1) { - $in = $filestack[$#filestack]; - while (not defined ($_ = <$in>)) { - close $filestack[$#filestack]; - pop @filestack; - last readinput if 0 == scalar @filestack; - $in = $filestack[$#filestack]; - } - chomp; - @_ = split; - - # If we're gathering help text, keep doing so. - if (defined $divert) { - if ((defined $_[0]) && $_[0] eq "!end") { - $divert = undef; - } else { - for my $ref (@$divert) { - ${$ref} .= "$_\n"; - } - } - next; - } - # Skip comments and blank lines. - next if /^\s*#/ or scalar @_ == 0; - - if ($_[0] eq "!begin" and $_[1] eq "help") { $divert = [\$help]; next; } - if ($_[0] eq "!name") { $project_name = $_[1]; next; } - if ($_[0] eq "!srcdir") { push @srcdirs, $_[1]; next; } - if ($_[0] eq "!makefile" and &mfval($_[1])) { $makefiles{$_[1]}=$_[2]; next;} - if ($_[0] eq "!specialobj" and &mfval($_[1])) { $specialobj{$_[1]}->{$_[2]} = 1; next;} - if ($_[0] eq "!cflags" and &mfval($_[1])) { - ($rest = $_) =~ s/^\s*\S+\s+\S+\s+\S+\s*//; # find rest of input line - $rest = 1 if $rest eq ""; - $cflags{$_[1]}->{$_[2]} = $rest; - next; - } - if ($_[0] eq "!begin") { - my @args = @_; - shift @args; - $divert = []; - for my $component (@args) { - if ($component =~ /^>(.*)/) { - push @$divert, \$auxfiles{$1}; - } elsif ($component =~ /^([^_]*)(_.*)?$/ and &mfval($1)) { - push @$divert, \$makefile_extra{$component}; - } - } - next; - } - if ($_[0] eq "!include") { - @newfiles = (); - for ($i = 1; $i <= $#_; $i++) { - push @newfiles, (sort glob $_[$i]); - } - for ($i = $#newfiles; $i >= 0; $i--) { - $file = $newfiles[$i]; - $f = new IO::Handle; - open $f, "<$file" or die "unable to open include file '$file'\n"; - push @filestack, $f; - } - next; - } - - # Now we have an ordinary line. See if it's an = line, a : line - # or a + line. - @objs = @_; - - if ($_[0] eq "+") { - $listref = $lastlistref; - $prog = undef; - die "$.: unexpected + line\n" if !defined $lastlistref; - } elsif ($_[1] eq "=") { - $groups{$_[0]} = []; - $listref = $groups{$_[0]}; - $prog = undef; - shift @objs; # eat the group name - } elsif ($_[1] eq "+=") { - $groups{$_[0]} = [] if !defined $groups{$_[0]}; - $listref = $groups{$_[0]}; - $prog = undef; - shift @objs; # eat the group name - } elsif ($_[1] eq ":") { - $listref = []; - $prog = $_[0]; - shift @objs; # eat the program name - } else { - die "$.: unrecognised line type: '$_'\n"; - } - shift @objs; # eat the +, the = or the : - - while (scalar @objs > 0) { - $i = shift @objs; - if ($groups{$i}) { - foreach $j (@{$groups{$i}}) { unshift @objs, $j; } - } elsif (($i eq "[G]" or $i eq "[C]" or $i eq "[M]" or - $i eq "[X]" or $i eq "[U]" or $i eq "[MX]") and defined $prog) { - $type = substr($i,1,(length $i)-2); - } else { - if ($i =~ /\?$/) { - # Object files with a trailing question mark are optional: - # the build can proceed fine without them, so we only use - # them if their primary source files are present. - $i =~ s/\?$//; - $i = undef unless defined &finddep($i); - } elsif ($i =~ /\|/) { - # Object file descriptions containing a vertical bar are - # lists of choices: we use the _first_ one whose primary - # source file is present. - @options = split /\|/, $i; - $j = undef; - foreach $k (@options) { - $j=$k, last if defined &finddep($k); - } - die "no alternative found for $i\n" unless defined $j; - $i = $j; - } - if (defined $i) { - push @$listref, $i; - push @allobjs, $i; - } - } - } - if ($prog and $type) { - die "multiple program entries for $prog [$type]\n" - if defined $programs{$prog . "," . $type}; - $programs{$prog . "," . $type} = $listref; - } - $lastlistref = $listref; -} - -foreach $aux (sort keys %auxfiles) { - open AUX, ">$aux"; - print AUX $auxfiles{$aux}; - close AUX; -} - -# Find object file names with predefines (in square brackets after -# the module name), and decide on actual object names for them. -foreach $i (@allobjs) { - if ($i !~ /\[/) { - $objname{$i} = $i; - $srcname{$i} = $i; - $usedobjname{$i} = 1; - } -} -foreach $i (@allobjs) { - if ($i =~ /^(.*)\[([^\]]*)/) { - $defs{$i} = [ split ",",$2 ]; - $srcname{$i} = $s = $1; - $index = 1; - while (1) { - $maxlen = length $s; - $maxlen = 8 if $maxlen < 8; - $chop = $maxlen - length $index; - $chop = length $s if $chop > length $s; - $chop = 0 if $chop < 0; - $name = substr($s, 0, $chop) . $index; - $index++, next if $usedobjname{$name}; - $objname{$i} = $name; - $usedobjname{$name} = 1; - last; - } - } -} - -# Now retrieve the complete list of objects and resource files, and -# construct dependency data for them. While we're here, expand the -# object list for each program, and complain if its type isn't set. -@prognames = sort keys %programs; -%depends = (); -@scanlist = (); -foreach $i (@prognames) { - ($prog, $type) = split ",", $i; - # Strip duplicate object names. - $prev = ''; - @list = grep { $status = ($prev ne $_); $prev=$_; $status } - sort @{$programs{$i}}; - $programs{$i} = [@list]; - foreach $jj (@list) { - $j = $srcname{$jj}; - $file = &finddep($j); - if (defined $file) { - $depends{$jj} = [$file]; - push @scanlist, $file; - } - } -} - -# Scan each file on @scanlist and find further inclusions. -# Inclusions are given by lines of the form `#include "otherfile"' -# (system headers are automatically ignored by this because they'll -# be given in angle brackets). Files included by this method are -# added back on to @scanlist to be scanned in turn (if not already -# done). -# -# Resource scripts (.rc) can also include a file by means of a line -# ending `ICON "filename"'. Files included by this method are not -# added to @scanlist because they can never include further files. -# -# In this pass we write out a hash %further which maps a source -# file name into a listref containing further source file names. - -%further = (); -while (scalar @scanlist > 0) { - $file = shift @scanlist; - next if defined $further{$file}; # skip if we've already done it - $further{$file} = []; - $dirfile = &findfile($file); - open IN, "$dirfile" or die "unable to open source file $file\n"; - while () { - chomp; - /^\s*#include\s+\"([^\"]+)\"/ and do { - push @{$further{$file}}, $1; - push @scanlist, $1; - next; - }; - /ICON\s+\"([^\"]+)\"\s*$/ and do { - push @{$further{$file}}, $1; - next; - } - } - close IN; -} - -# Now we're ready to generate the final dependencies section. For -# each key in %depends, we must expand the dependencies list by -# iteratively adding entries from %further. -foreach $i (keys %depends) { - %dep = (); - @scanlist = @{$depends{$i}}; - foreach $i (@scanlist) { $dep{$i} = 1; } - while (scalar @scanlist > 0) { - $file = shift @scanlist; - foreach $j (@{$further{$file}}) { - if (!$dep{$j}) { - $dep{$j} = 1; - push @{$depends{$i}}, $j; - push @scanlist, $j; - } - } - } -# printf "%s: %s\n", $i, join ' ',@{$depends{$i}}; -} - -# Validation of input. - -sub mfval($) { - my ($type) = @_; - # Returns true if the argument is a known makefile type. Otherwise, - # prints a warning and returns false; - if (grep { $type eq $_ } - ("vc","vcproj","cygwin","borland","lcc","gtk","am","mpw","nestedvm","osx","wce","gnustep","emcc","clangcl")) { - return 1; - } - warn "$.:unknown makefile type '$type'\n"; - return 0; -} - -# Utility routines while writing out the Makefiles. - -sub dirpfx { - my ($path) = shift @_; - my ($sep) = shift @_; - my $ret = ""; - my $i; - while (($i = index $path, $sep) >= 0) { - $path = substr $path, ($i + length $sep); - $ret .= "..$sep"; - } - return $ret; -} - -sub findfile { - my ($name) = @_; - my $dir; - my $i; - my $outdir = undef; - unless (defined $findfilecache{$name}) { - $i = 0; - foreach $dir (@srcdirs) { - $outdir = $dir, $i++ if -f "$dir$name"; - } - die "multiple instances of source file $name\n" if $i > 1; - $findfilecache{$name} = (defined $outdir ? $outdir . $name : undef); - } - return $findfilecache{$name}; -} - -sub finddep { - my $j = shift @_; - my $file; - # Find the first dependency of an object. - - # Dependencies for "x" start with "x.c" or "x.m" (depending on - # which one exists). - # Dependencies for "x.res" start with "x.rc". - # Dependencies for "x.rsrc" start with "x.r". - # Both types of file are pushed on the list of files to scan. - # Libraries (.lib) don't have dependencies at all. - if ($j =~ /^(.*)\.res$/) { - $file = "$1.rc"; - } elsif ($j =~ /^(.*)\.rsrc$/) { - $file = "$1.r"; - } elsif ($j !~ /\./) { - $file = "$j.c"; - $file = "$j.m" unless &findfile($file); - } else { - # For everything else, we assume it's its own dependency. - $file = $j; - } - $file = undef unless &findfile($file); - return $file; -} - -sub objects { - my ($prog, $otmpl, $rtmpl, $ltmpl, $prefix, $dirsep) = @_; - my @ret; - my ($i, $x, $y); - ($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl); - @ret = (); - foreach $ii (@{$programs{$prog}}) { - $i = $objname{$ii}; - $x = ""; - if ($i =~ /^(.*)\.(res|rsrc)/) { - $y = $1; - ($x = $rtmpl) =~ s/X/$y/; - } elsif ($i =~ /^(.*)\.lib/) { - $y = $1; - ($x = $ltmpl) =~ s/X/$y/; - } elsif ($i !~ /\./) { - ($x = $otmpl) =~ s/X/$i/; - } - push @ret, $x if $x ne ""; - } - return join " ", @ret; -} - -sub special { - my ($prog, $suffix) = @_; - my @ret; - my ($i, $x, $y); - @ret = (); - foreach $ii (@{$programs{$prog}}) { - $i = $objname{$ii}; - if (substr($i, (length $i) - (length $suffix)) eq $suffix) { - push @ret, $i; - } - } - return join " ", @ret; -} - -sub splitline { - my ($line, $width, $splitchar) = @_; - my $result = ""; - my $len; - $len = (defined $width ? $width : 76); - $splitchar = (defined $splitchar ? $splitchar : '\\'); - while (length $line > $len) { - $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,}?\s(.*)$/; - $result .= $1; - $result .= " ${splitchar}\n\t\t" if $2 ne ''; - $line = $2; - $len = 60; - } - return $result . $line; -} - -sub deps { - my ($otmpl, $rtmpl, $prefix, $dirsep, $depchar, $splitchar) = @_; - my ($i, $x, $y); - my @deps; - my @ret; - @ret = (); - $depchar ||= ':'; - foreach $ii (sort keys %depends) { - $i = $objname{$ii}; - next if $specialobj{$mftyp}->{$i}; - if ($i =~ /^(.*)\.(res|rsrc)/) { - next if !defined $rtmpl; - $y = $1; - ($x = $rtmpl) =~ s/X/$y/; - } else { - ($x = $otmpl) =~ s/X/$i/; - } - @deps = @{$depends{$ii}}; - # Skip things which are their own dependency. - next if grep { $_ eq $i } @deps; - @deps = map { - $_ = &findfile($_); - s/\//$dirsep/g; - $_ = $prefix . $_; - } @deps; - push @ret, {obj => $x, deps => [@deps], defs => $defs{$ii}}; - } - return @ret; -} - -sub prognames { - my ($types) = @_; - my ($n, $prog, $type); - my @ret; - @ret = (); - foreach $n (@prognames) { - ($prog, $type) = split ",", $n; - push @ret, $n if index(":$types:", ":$type:") >= 0; - } - return @ret; -} - -sub progrealnames { - my ($types) = @_; - my ($n, $prog, $type); - my @ret; - @ret = (); - foreach $n (@prognames) { - ($prog, $type) = split ",", $n; - push @ret, $prog if index(":$types:", ":$type:") >= 0; - } - return @ret; -} - -sub manpages { - my ($types,$suffix) = @_; - - # assume that all UNIX programs have a man page - if($suffix eq "1" && $types =~ /:X:/) { - return map("$_.1", &progrealnames($types)); - } - return (); -} - -$orig_dir = cwd; - -# Now we're ready to output the actual Makefiles. - -if (defined $makefiles{'clangcl'}) { - $mftyp = 'clangcl'; - $dirpfx = &dirpfx($makefiles{'clangcl'}, "/"); - - ##-- Makefile for cross-compiling using clang-cl, lld-link, and - ## MinGW's windres for resource compilation. - # - # This makefile allows a complete Linux-based cross-compile, but - # using the real Visual Studio header files and libraries. In - # order to run it, you will need: - # - # - MinGW windres on your PATH. - # * On Ubuntu as of 16.04, you can apt-get install - # binutils-mingw-w64-x86-64 and binutils-mingw-w64-i686 - # which will provide (respectively) 64- and 32-bit versions, - # under the names to which RCCMD is defined below. - # - clang-cl and lld-link on your PATH. - # * I built these from the up-to-date LLVM project trunk git - # repositories, as of 2017-02-05. - # - case-mashed copies of the Visual Studio include directories. - # * On a real VS installation, run vcvars32.bat and look at - # the resulting value of %INCLUDE%. Take a full copy of each - # of those directories, and inside the copy, for each - # include file that has an uppercase letter in its name, - # make a lowercased symlink to it. Additionally, one of the - # directories will contain files called driverspecs.h and - # specstrings.h, and those will need symlinks called - # DriverSpecs.h and SpecStrings.h. - # * Now, on Linux, define the environment variable INCLUDE to - # be a list, separated by *semicolons* (in the Windows - # style), of those directories, but before all of them you - # must also include lib/clang/5.0.0/include from the clang - # installation area (which contains in particular a - # clang-compatible stdarg.h overriding the Visual Studio - # one). - # - similarly case-mashed copies of the library directories. - # * Again, on a real VS installation, run vcvars32 or - # vcvarsx86_amd64 (as appropriate), look at %LIB%, make a - # copy of each directory, and provide symlinks within that - # directory so that all the files can be opened as - # lowercase. - # * Then set LIB to be a semicolon-separated list of those - # directories (but you'll need to change which set of - # directories depending on whether you want to do a 32-bit - # or 64-bit build). - # - for a 64-bit build, set 'Platform=x64' in the environment as - # well, or else on the make command line. - # * This is a variable understood only by this makefile - none - # of the tools we invoke will know it - but it's consistent - # with the way the VS scripts like vcvarsx86_amd64.bat set - # things up, and since the environment has to change - # _anyway_ between 32- and 64-bit builds (different set of - # paths in $LIB) it's reasonable to have the choice of - # compilation target driven by another environment variable - # set in parallel with that one. - # - for older versions of the VS libraries you may also have to - # set EXTRA_console and/or EXTRA_windows to the name of an - # object file manually extracted from one of those libraries. - # * This is because old VS seems to manage its startup code by - # having libcmt.lib contain lots of *crt0.obj objects, one - # for each possible user entry point (main, WinMain and the - # wide-char versions of both), of which the linker arranges - # to include the right one by special-case code. But lld - # only seems to mimic half of that code - it does include - # the right crt0 object, but it doesn't also deliberately - # _avoid_ including the _wrong_ ones, and since all those - # objects define a common set of global symbols for other - # parts of the library to use, lld may well select an - # arbitrary one of them the first time it sees a reference - # to one of those global symbols, and then later also select - # the _right_ one for the application's entry point, causing - # a multiple-definitions crash. - # * So the workaround is to explicitly include the right - # *crt0.obj file on the linker command line before lld even - # begins searching libraries. Hence, for a console - # application, you might extract crt0.obj from the library - # in question and set EXTRA_console=crt0.obj, and for a GUI - # application, do the same with wincrt0.obj. Then this - # makefile will include the right one of those objects - # alongside the matching /subsystem linker option. - - open OUT, ">$makefiles{'clangcl'}"; select OUT; - print - "# Makefile for cross-compiling $project_name using clang-cl, lld-link,\n". - "# and MinGW's windres, using GNU make on Linux.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - print $help; - print - "\n". - "CCCMD = clang-cl\n". - "ifeq (\$(Platform),x64)\n". - "CCTARGET = x86_64-pc-windows-msvc18.0.0\n". - "RCCMD = x86_64-w64-mingw32-windres\n". - "else\n". - "CCTARGET = i386-pc-windows-msvc18.0.0\n". - "RCCMD = i686-w64-mingw32-windres\n". - "endif\n". - "CC = \$(CCCMD) --target=\$(CCTARGET)\n". - &splitline("RC = \$(RCCMD) --preprocessor=\$(CCCMD) ". - "--preprocessor-arg=/TC --preprocessor-arg=/E")."\n". - "LD = lld-link\n". - "\n". - "# C compilation flags\n". - &splitline("CFLAGS = /nologo /W3 /O1 " . - (join " ", map {"-I$dirpfx$_"} @srcdirs) . - " /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 ". - "/D_CRT_SECURE_NO_WARNINGS")."\n". - "LFLAGS = /incremental:no /dynamicbase /nxcompat\n". - &splitline("RCFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs). - " -DWIN32 -D_WIN32 -DWINVER=0x0400 --define MINGW32_FIX=1")."\n". - "\n". - "\n"; - print &splitline("all:" . join "", map { " \$(BUILDDIR)$_.exe" } &progrealnames("G:C")); - print "\n\n"; - foreach $p (&prognames("G:C")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef); - print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n"; - - $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib"); - $subsys = ($type eq "G") ? "windows" : "console"; - print &splitline("\t\$(LD) \$(LFLAGS) \$(XLFLAGS) ". - "/out:\$(BUILDDIR)$prog.exe ". - "/lldmap:\$(BUILDDIR)$prog.map ". - "/subsystem:$subsys\$(SUBSYSVER) ". - "\$(EXTRA_$subsys) $objstr")."\n\n"; - } - foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "/", "vc")) { - print &splitline(sprintf("%s: %s", $d->{obj}, - join " ", @{$d->{deps}})), "\n"; - if ($d->{obj} =~ /\.res$/) { - print "\t\$(RC) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n"; - } else { - $deflist = join "", map { " /D$_" } @{$d->{defs}}; - print "\t\$(CC) /Fo".$d->{obj}." \$(COMPAT) \$(CFLAGS) \$(XFLAGS)$deflist /c \$<\n\n"; - } - } - print "\nclean:\n". - &splitline("\trm -f \$(BUILDDIR)*.obj \$(BUILDDIR)*.exe ". - "\$(BUILDDIR)*.res \$(BUILDDIR)*.map ". - "\$(BUILDDIR)*.exe.manifest")."\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'cygwin'}) { - $mftyp = 'cygwin'; - $dirpfx = &dirpfx($makefiles{'cygwin'}, "/"); - - ##-- CygWin makefile - open OUT, ">$makefiles{'cygwin'}"; select OUT; - print - "# Makefile for $project_name under cygwin.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # gcc command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "\n". - "# You can define this path to point at your tools if you need to\n". - "# TOOLPATH = c:\\cygwin\\bin\\ # or similar, if you're running Windows\n". - "# TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/\n". - "CC = \$(TOOLPATH)gcc\n". - "RC = \$(TOOLPATH)windres\n". - "# Uncomment the following two lines to compile under Winelib\n". - "# CC = winegcc\n". - "# RC = wrc\n". - "# You may also need to tell windres where to find include files:\n". - "# RCINC = --include-dir c:\\cygwin\\include\\\n". - "\n". - &splitline("CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT". - " -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP " . - (join " ", map {"-I$dirpfx$_"} @srcdirs)) . - "\n". - "LDFLAGS = -mno-cygwin -s\n". - &splitline("RCFLAGS = \$(RCINC) --define WIN32=1 --define _WIN32=1". - " --define WINVER=0x0400 --define MINGW32_FIX=1 " . - (join " ", map {"--include $dirpfx$_"} @srcdirs) )."\n". - "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); - print "\n\n"; - foreach $p (&prognames("G:C")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.o", "X.res.o", undef); - print &splitline($prog . ".exe: " . $objstr), "\n"; - my $mw = $type eq "G" ? " -mwindows" : ""; - $libstr = &objects($p, undef, undef, "-lX"); - print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " . - "-Wl,-Map,$prog.map " . - $objstr . " $libstr", 69), "\n\n"; - } - foreach $d (&deps("X.o", "X.res.o", $dirpfx, "/")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - if ($d->{obj} =~ /\.res\.o$/) { - print "\t\$(RC) \$(FWHACK) \$(RCFL) \$(RCFLAGS) \$< \$\@\n"; - } else { - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS)" . - " \$(XFLAGS)$deflist -c \$< -o \$\@\n"; - } - } - print "\n"; - print $makefile_extra{'cygwin'} || ""; - print "\nclean:\n". - "\trm -f *.o *.exe *.res.o *.map\n". - "\n"; - select STDOUT; close OUT; - -} - -##-- Borland makefile -if (defined $makefiles{'borland'}) { - $mftyp = 'borland'; - $dirpfx = &dirpfx($makefiles{'borland'}, "\\"); - - %stdlibs = ( # Borland provides many Win32 API libraries intrinsically - "advapi32" => 1, - "comctl32" => 1, - "comdlg32" => 1, - "gdi32" => 1, - "imm32" => 1, - "shell32" => 1, - "user32" => 1, - "winmm" => 1, - "winspool" => 1, - "wsock32" => 1, - ); - open OUT, ">$makefiles{'borland'}"; select OUT; - print - "# Makefile for $project_name under Borland C.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # bcc32 command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "\n". - "# If you rename this file to `Makefile', you should change this line,\n". - "# so that the .rsp files still depend on the correct makefile.\n". - "MAKEFILE = Makefile.bor\n". - "\n". - "# C compilation flags\n". - "CFLAGS = -D_WINDOWS -DWINVER=0x0401\n". - "\n". - "# Get include directory for resource compiler\n". - "!if !\$d(BCB)\n". - "BCB = \$(MAKEDIR)\\..\n". - "!endif\n". - "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); - print "\n\n"; - foreach $p (&prognames("G:C")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.obj", "X.res", undef); - print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; - my $ap = ($type eq "G") ? "-aa" : "-ap"; - print "\tilink32 $ap -Gn -L\$(BCB)\\lib \@$prog.rsp\n\n"; - } - foreach $p (&prognames("G:C")) { - ($prog, $type) = split ",", $p; - print $prog, ".rsp: \$(MAKEFILE)\n"; - $objstr = &objects($p, "X.obj", undef, undef); - @objlist = split " ", $objstr; - @objlines = (""); - foreach $i (@objlist) { - if (length($objlines[$#objlines] . " $i") > 50) { - push @objlines, ""; - } - $objlines[$#objlines] .= " $i"; - } - $c0w = ($type eq "G") ? "c0w32" : "c0x32"; - print "\techo $c0w + > $prog.rsp\n"; - for ($i=0; $i<=$#objlines; $i++) { - $plus = ($i < $#objlines ? " +" : ""); - print "\techo$objlines[$i]$plus >> $prog.rsp\n"; - } - print "\techo $prog.exe >> $prog.rsp\n"; - $objstr = &objects($p, "X.obj", "X.res", undef); - @libs = split " ", &objects($p, undef, undef, "X"); - @libs = grep { !$stdlibs{$_} } @libs; - unshift @libs, "cw32", "import32"; - $libstr = join ' ', @libs; - print "\techo nul,$libstr, >> $prog.rsp\n"; - print "\techo " . &objects($p, undef, "X.res", undef) . " >> $prog.rsp\n"; - print "\n"; - } - foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - if ($d->{obj} =~ /\.res$/) { - print &splitline("\tbrcc32 \$(FWHACK) \$(RCFL) " . - "-i \$(BCB)\\include -r -DNO_WINRESRC_H -DWIN32". - " -D_WIN32 -DWINVER=0x0401 \$*.rc",69)."\n"; - } else { - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - print &splitline("\tbcc32 -w-aus -w-ccc -w-par -w-pia \$(COMPAT)" . - " \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist ". - (join " ", map {"-I$dirpfx$_"} @srcdirs) . - " /o$d->{obj} /c ".$d->{deps}->[0],69)."\n"; - } - } - print "\n"; - print $makefile_extra{'borland'} || ""; - print "\nclean:\n". - "\t-del *.obj\n". - "\t-del *.exe\n". - "\t-del *.res\n". - "\t-del *.pch\n". - "\t-del *.aps\n". - "\t-del *.il*\n". - "\t-del *.pdb\n". - "\t-del *.rsp\n". - "\t-del *.tds\n". - "\t-del *.\$\$\$\$\$\$\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'vc'}) { - $mftyp = 'vc'; - $dirpfx = &dirpfx($makefiles{'vc'}, "\\"); - - ##-- Visual C++ makefile - open OUT, ">$makefiles{'vc'}"; select OUT; - print - "# Makefile for $project_name under Visual C.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - print $help; - print - "\n". - "# If you rename this file to `Makefile', you should change this line,\n". - "# so that the .rsp files still depend on the correct makefile.\n". - "MAKEFILE = Makefile.vc\n". - "\n". - "# C compilation flags\n". - "CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 /I.\n". - "LFLAGS = /incremental:no /fixed\n". - "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); - print "\n\n"; - foreach $p (&prognames("G:C")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.obj", "X.res", undef); - print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; - print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n"; - } - foreach $p (&prognames("G:C")) { - ($prog, $type) = split ",", $p; - print $prog, ".rsp: \$(MAKEFILE)\n"; - $objstr = &objects($p, "X.obj", "X.res", "X.lib"); - @objlist = split " ", $objstr; - @objlines = (""); - foreach $i (@objlist) { - if (length($objlines[$#objlines] . " $i") > 50) { - push @objlines, ""; - } - $objlines[$#objlines] .= " $i"; - } - $subsys = ($type eq "G") ? "windows" : "console"; - print "\techo /nologo /subsystem:$subsys > $prog.rsp\n"; - for ($i=0; $i<=$#objlines; $i++) { - print "\techo$objlines[$i] >> $prog.rsp\n"; - } - print "\n"; - } - foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - if ($d->{obj} =~ /\.res$/) { - print "\trc \$(FWHACK) \$(RCFL) -r -DWIN32 -D_WIN32 ". - "-DWINVER=0x0400 -fo".$d->{obj}." ".$d->{deps}->[0]."\n"; - } else { - $deflist = join "", map { " /D$_" } @{$d->{defs}}; - print "\tcl \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist". - " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n"; - } - } - print "\n"; - print $makefile_extra{'vc'} || ""; - print "\nclean: tidy\n". - "\t-del *.exe\n\n". - "tidy:\n". - "\t-del *.obj\n". - "\t-del *.res\n". - "\t-del *.pch\n". - "\t-del *.aps\n". - "\t-del *.ilk\n". - "\t-del *.pdb\n". - "\t-del *.rsp\n". - "\t-del *.dsp\n". - "\t-del *.dsw\n". - "\t-del *.ncb\n". - "\t-del *.opt\n". - "\t-del *.plg\n". - "\t-del *.map\n". - "\t-del *.idb\n". - "\t-del debug.log\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'wce'}) { - $mftyp = 'wce'; - $dirpfx = &dirpfx($makefiles{'wce'}, "\\"); - - ##-- eMbedded Visual C PocketPC makefile - open OUT, ">$makefiles{'wce'}"; select OUT; - print - "# Makefile for $project_name on PocketPC using eMbedded Visual C.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - print $help; - print - "\n". - "# If you rename this file to `Makefile', you should change this line,\n". - "# so that the .rsp files still depend on the correct makefile.\n". - "MAKEFILE = Makefile.wce\n". - "\n". - "# This makefile expects the environment to have been set up by one\n". - "# of the PocketPC batch files wcearmv4.bat and wceemulator.bat. No\n". - "# other build targets are currently supported, because they would\n". - "# need a section in this if statement.\n". - "!if \"\$(TARGETCPU)\" == \"emulator\"\n". - "PLATFORM_DEFS=/D \"_i386_\" /D \"i_386_\" /D \"_X86_\" /D \"x86\"\n". - "CC=cl\n". - "BASELIBS=commctrl.lib coredll.lib corelibc.lib aygshell.lib\n". - "MACHINE=IX86\n". - "!else\n". - "PLATFORM_DEFS=/D \"ARM\" /D \"_ARM_\" /D \"ARMV4\"\n". - "CC=clarm\n". - "BASELIBS=commctrl.lib coredll.lib aygshell.lib\n". - "MACHINE=ARM\n". - "!endif\n". - "\n". - "# C compilation flags\n". - "CFLAGS = /nologo /W3 /O1 /MC /D _WIN32_WCE=420 /D \"WIN32_PLATFORM_PSPC=400\" /D UNDER_CE=420 \\\n". - " \$(PLATFORM_DEFS) \\\n". - " /D \"UNICODE\" /D \"_UNICODE\" /D \"NDEBUG\" /D \"NO_HTMLHELP\"\n". - "\n". - "LFLAGS = /nologo /incremental:no \\\n". - " /base:0x00010000 /stack:0x10000,0x1000 /entry:WinMainCRTStartup \\\n". - " /nodefaultlib:libc.lib /nodefaultlib:libcmt.lib /nodefaultlib:msvcrt.lib /nodefaultlib:OLDNAMES.lib \\\n". - " /subsystem:windowsce,4.20 /align:4096 /MACHINE:\$(MACHINE)\n". - "\n". - "RCFL = /d UNDER_CE=420 /d _WIN32_WCE=420 /d \"WIN32_PLATFORM_PSPC=400\" \\\n". - " \$(PLATFORM_DEFS) \\\n". - " /d \"NDEBUG\" /d \"UNICODE\" /d \"_UNICODE\"\n". - "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G")); - print "\n\n"; - foreach $p (&prognames("G")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.obj", "X.res", undef); - print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; - print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n"; - } - foreach $p (&prognames("G")) { - ($prog, $type) = split ",", $p; - print $prog, ".rsp: \$(MAKEFILE)\n"; - $objstr = &objects($p, "X.obj", "X.res", undef); - @objlist = split " ", $objstr; - @objlines = (""); - foreach $i (@objlist) { - if (length($objlines[$#objlines] . " $i") > 50) { - push @objlines, ""; - } - $objlines[$#objlines] .= " $i"; - } - print "\techo \$(BASELIBS) > $prog.rsp\n"; - for ($i=0; $i<=$#objlines; $i++) { - print "\techo$objlines[$i] >> $prog.rsp\n"; - } - print "\n"; - } - foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - if ($d->{obj} =~ /\.res$/) { - print "\trc \$(FWHACK) \$(RCFL) -r -fo". - $d->{obj}." ".$d->{deps}->[0]."\n"; - } else { - $deflist = join "", map { " /D$_" } @{$d->{defs}}; - print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist". - " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n"; - } - } - print "\n"; - print $makefile_extra{'wce'} || ""; - print "\nclean: tidy\n". - "\t-del *.exe\n\n". - "tidy:\n". - "\t-del *.obj\n". - "\t-del *.res\n". - "\t-del *.pch\n". - "\t-del *.aps\n". - "\t-del *.ilk\n". - "\t-del *.pdb\n". - "\t-del *.rsp\n". - "\t-del *.dsp\n". - "\t-del *.dsw\n". - "\t-del *.ncb\n". - "\t-del *.opt\n". - "\t-del *.plg\n". - "\t-del *.map\n". - "\t-del *.idb\n". - "\t-del debug.log\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'vcproj'}) { - $mftyp = 'vcproj'; - - ##-- MSVC 6 Workspace and projects - # - # Note: All files created in this section are written in binary - # mode, because although MSVC's command-line make can deal with - # LF-only line endings, MSVC project files really _need_ to be - # CRLF. Hence, in order for mkfiles.pl to generate usable project - # files even when run from Unix, I make sure all files are binary - # and explicitly write the CRLFs. - # - # Create directories if necessary - mkdir $makefiles{'vcproj'} - if(! -d $makefiles{'vcproj'}); - chdir $makefiles{'vcproj'}; - @deps = &deps("X.obj", "X.res", "", "\\"); - %all_object_deps = map {$_->{obj} => $_->{deps}} @deps; - # Create the project files - # Get names of all Windows projects (GUI and console) - my @prognames = &prognames("G:C"); - foreach $progname (@prognames) { - create_project(\%all_object_deps, $progname); - } - # Create the workspace file - open OUT, ">$project_name.dsw"; binmode OUT; select OUT; - print - "Microsoft Developer Studio Workspace File, Format Version 6.00\r\n". - "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r\n". - "\r\n". - "###############################################################################\r\n". - "\r\n"; - # List projects - foreach $progname (@prognames) { - ($windows_project, $type) = split ",", $progname; - print "Project: \"$windows_project\"=\".\\$windows_project\\$windows_project.dsp\" - Package Owner=<4>\r\n"; - } - print - "\r\n". - "Package=<5>\r\n". - "{{{\r\n". - "}}}\r\n". - "\r\n". - "Package=<4>\r\n". - "{{{\r\n". - "}}}\r\n". - "\r\n". - "###############################################################################\r\n". - "\r\n". - "Global:\r\n". - "\r\n". - "Package=<5>\r\n". - "{{{\r\n". - "}}}\r\n". - "\r\n". - "Package=<3>\r\n". - "{{{\r\n". - "}}}\r\n". - "\r\n". - "###############################################################################\r\n". - "\r\n"; - select STDOUT; close OUT; - chdir $orig_dir; - - sub create_project { - my ($all_object_deps, $progname) = @_; - # Construct program's dependency info - %seen_objects = (); - %lib_files = (); - %source_files = (); - %header_files = (); - %resource_files = (); - @object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib"); - foreach $object_file (@object_files) { - next if defined $seen_objects{$object_file}; - $seen_objects{$object_file} = 1; - if($object_file =~ /\.lib$/io) { - $lib_files{$object_file} = 1; - next; - } - $object_deps = $all_object_deps{$object_file}; - foreach $object_dep (@$object_deps) { - if($object_dep =~ /\.c$/io) { - $source_files{$object_dep} = 1; - next; - } - if($object_dep =~ /\.h$/io) { - $header_files{$object_dep} = 1; - next; - } - if($object_dep =~ /\.(rc|ico)$/io) { - $resource_files{$object_dep} = 1; - next; - } - } - } - $libs = join " ", sort keys %lib_files; - @source_files = sort keys %source_files; - @header_files = sort keys %header_files; - @resources = sort keys %resource_files; - ($windows_project, $type) = split ",", $progname; - mkdir $windows_project - if(! -d $windows_project); - chdir $windows_project; - $subsys = ($type eq "G") ? "windows" : "console"; - open OUT, ">$windows_project.dsp"; binmode OUT; select OUT; - print - "# Microsoft Developer Studio Project File - Name=\"$windows_project\" - Package Owner=<4>\r\n". - "# Microsoft Developer Studio Generated Build File, Format Version 6.00\r\n". - "# ** DO NOT EDIT **\r\n". - "\r\n". - "# TARGTYPE \"Win32 (x86) Application\" 0x0101\r\n". - "\r\n". - "CFG=$windows_project - Win32 Debug\r\n". - "!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r\n". - "!MESSAGE use the Export Makefile command and run\r\n". - "!MESSAGE \r\n". - "!MESSAGE NMAKE /f \"$windows_project.mak\".\r\n". - "!MESSAGE \r\n". - "!MESSAGE You can specify a configuration when running NMAKE\r\n". - "!MESSAGE by defining the macro CFG on the command line. For example:\r\n". - "!MESSAGE \r\n". - "!MESSAGE NMAKE /f \"$windows_project.mak\" CFG=\"$windows_project - Win32 Debug\"\r\n". - "!MESSAGE \r\n". - "!MESSAGE Possible choices for configuration are:\r\n". - "!MESSAGE \r\n". - "!MESSAGE \"$windows_project - Win32 Release\" (based on \"Win32 (x86) Application\")\r\n". - "!MESSAGE \"$windows_project - Win32 Debug\" (based on \"Win32 (x86) Application\")\r\n". - "!MESSAGE \r\n". - "\r\n". - "# Begin Project\r\n". - "# PROP AllowPerConfigDependencies 0\r\n". - "# PROP Scc_ProjName \"\"\r\n". - "# PROP Scc_LocalPath \"\"\r\n". - "CPP=cl.exe\r\n". - "MTL=midl.exe\r\n". - "RSC=rc.exe\r\n". - "\r\n". - "!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n". - "\r\n". - "# PROP BASE Use_MFC 0\r\n". - "# PROP BASE Use_Debug_Libraries 0\r\n". - "# PROP BASE Output_Dir \"Release\"\r\n". - "# PROP BASE Intermediate_Dir \"Release\"\r\n". - "# PROP BASE Target_Dir \"\"\r\n". - "# PROP Use_MFC 0\r\n". - "# PROP Use_Debug_Libraries 0\r\n". - "# PROP Output_Dir \"Release\"\r\n". - "# PROP Intermediate_Dir \"Release\"\r\n". - "# PROP Ignore_Export_Lib 0\r\n". - "# PROP Target_Dir \"\"\r\n". - "# ADD BASE CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n". - "# ADD CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n". - "# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n". - "# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n". - "# ADD BASE RSC /l 0x809 /d \"NDEBUG\"\r\n". - "# ADD RSC /l 0x809 /d \"NDEBUG\"\r\n". - "BSC32=bscmake.exe\r\n". - "# ADD BASE BSC32 /nologo\r\n". - "# ADD BSC32 /nologo\r\n". - "LINK32=link.exe\r\n". - "# 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". - "# ADD LINK32 $libs /nologo /subsystem:$subsys /machine:I386\r\n". - "# SUBTRACT LINK32 /pdb:none\r\n". - "\r\n". - "!ELSEIF \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n". - "\r\n". - "# PROP BASE Use_MFC 0\r\n". - "# PROP BASE Use_Debug_Libraries 1\r\n". - "# PROP BASE Output_Dir \"Debug\"\r\n". - "# PROP BASE Intermediate_Dir \"Debug\"\r\n". - "# PROP BASE Target_Dir \"\"\r\n". - "# PROP Use_MFC 0\r\n". - "# PROP Use_Debug_Libraries 1\r\n". - "# PROP Output_Dir \"Debug\"\r\n". - "# PROP Intermediate_Dir \"Debug\"\r\n". - "# PROP Ignore_Export_Lib 0\r\n". - "# PROP Target_Dir \"\"\r\n". - "# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n". - "# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n". - "# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n". - "# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n". - "# ADD BASE RSC /l 0x809 /d \"_DEBUG\"\r\n". - "# ADD RSC /l 0x809 /d \"_DEBUG\"\r\n". - "BSC32=bscmake.exe\r\n". - "# ADD BASE BSC32 /nologo\r\n". - "# ADD BSC32 /nologo\r\n". - "LINK32=link.exe\r\n". - "# 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". - "# ADD LINK32 $libs /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n". - "# SUBTRACT LINK32 /pdb:none\r\n". - "\r\n". - "!ENDIF \r\n". - "\r\n". - "# Begin Target\r\n". - "\r\n". - "# Name \"$windows_project - Win32 Release\"\r\n". - "# Name \"$windows_project - Win32 Debug\"\r\n". - "# Begin Group \"Source Files\"\r\n". - "\r\n". - "# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\r\n"; - foreach $source_file (@source_files) { - print - "# Begin Source File\r\n". - "\r\n". - "SOURCE=..\\..\\$source_file\r\n"; - if($source_file =~ /ssh\.c/io) { - # Disable 'Edit and continue' as Visual Studio can't handle the macros - print - "\r\n". - "!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n". - "\r\n". - "!ELSEIF \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n". - "\r\n". - "# ADD CPP /Zi\r\n". - "\r\n". - "!ENDIF \r\n". - "\r\n"; - } - print "# End Source File\r\n"; - } - print - "# End Group\r\n". - "# Begin Group \"Header Files\"\r\n". - "\r\n". - "# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\r\n"; - foreach $header_file (@header_files) { - print - "# Begin Source File\r\n". - "\r\n". - "SOURCE=..\\..\\$header_file\r\n". - "# End Source File\r\n"; - } - print - "# End Group\r\n". - "# Begin Group \"Resource Files\"\r\n". - "\r\n". - "# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe\"\r\n"; - foreach $resource_file (@resources) { - print - "# Begin Source File\r\n". - "\r\n". - "SOURCE=..\\..\\$resource_file\r\n". - "# End Source File\r\n"; - } - print - "# End Group\r\n". - "# End Target\r\n". - "# End Project\r\n"; - select STDOUT; close OUT; - chdir ".."; - } -} - -if (defined $makefiles{'gtk'}) { - $mftyp = 'gtk'; - $dirpfx = &dirpfx($makefiles{'gtk'}, "/"); - - ##-- X/GTK/Unix makefile - open OUT, ">$makefiles{'gtk'}"; select OUT; - print - "# Makefile for $project_name under X/GTK and Unix.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # gcc command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "\n". - "# You can define this path to point at your tools if you need to\n". - "# TOOLPATH = /opt/gcc/bin\n". - "CC := \$(TOOLPATH)\$(CC)\n". - "# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'\n". - "# (depending on what works on your system) if you want to enforce\n". - "# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0'\n". - "# if you want to enforce 2.0. The default is to try 2.0 and fall back\n". - "# to 1.2 if it isn't found.\n". - "GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 \$\$0 2>/dev/null || gtk-config \$\$0'\n". - "\n". - &splitline("CFLAGS := -O2 -Wall -Werror -ansi -pedantic -g " . - (join " ", map {"-I$dirpfx$_"} @srcdirs) . - " `\$(GTK_CONFIG) --cflags` \$(CFLAGS)")."\n". - "XLIBS = `\$(GTK_CONFIG) --libs` -lm\n". - "ULIBS = -lm#\n". - "INSTALL=install\n", - "INSTALL_PROGRAM=\$(INSTALL)\n", - "INSTALL_DATA=\$(INSTALL)\n", - "prefix=/usr/local\n", - "exec_prefix=\$(prefix)\n", - "bindir=\$(exec_prefix)/bin\n", - "gamesdir=\$(exec_prefix)/games\n", - "mandir=\$(prefix)/man\n", - "man1dir=\$(mandir)/man1\n", - "\n"; - print &splitline("all:" . join "", map { " \$(BINPREFIX)$_" } - &progrealnames("X:U")); - print "\n\n"; - foreach $p (&prognames("X:U")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.o", undef, undef); - print &splitline("\$(BINPREFIX)" . $prog . ": " . $objstr), "\n"; - $libstr = &objects($p, undef, undef, "-lX"); - print &splitline("\t\$(CC) -o \$@ $objstr $libstr \$(XLFLAGS) \$(${type}LIBS)", 69), - "\n\n"; - } - foreach $d (&deps("X.o", undef, $dirpfx, "/")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" . - " -c \$< -o \$\@\n"; - } - print "\n"; - print $makefile_extra{'gtk'} || ""; - print "\nclean:\n". - "\trm -f *.o". (join "", map { " \$(BINPREFIX)$_" } &progrealnames("X:U")) . "\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'am'}) { - $mftyp = 'am'; - die "Makefile.am in a subdirectory is not supported\n" - if &dirpfx($makefiles{'am'}, "/") ne ""; - - ##-- Unix/autoconf Makefile.am - open OUT, ">$makefiles{'am'}"; select OUT; - print - "# Makefile.am for $project_name under Unix with Autoconf/Automake.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n\n"; - - print $makefile_extra{'am_begin'} || ""; - - # All programs go in noinstprogs by default. If you want them - # installed anywhere else, you have to also add them to - # bin_PROGRAMS using '!begin am'. (Automake doesn't seem to mind - # having a program name in _both_ of bin_PROGRAMS and - # noinst_PROGRAMS.) - @noinstprogs = (); - foreach $p (&prognames("X:U")) { - ($prog, $type) = split ",", $p; - push @noinstprogs, $prog; - } - print &splitline(join " ", "noinst_PROGRAMS", "=", @noinstprogs), "\n"; - - %objtosrc = (); - %amspeciallibs = (); - %amlibobjname = (); - %allsources = (); - foreach $d (&deps("X", undef, "", "/", "am")) { - my $obj = $d->{obj}; - my $use_archive = 0; - - if (defined $d->{defs}) { - # This file needs to go in an archive, so that we can - # change the preprocess flags to include some -Ds - $use_archive = 1; - $archivecppflags{$obj} = [map { " -D$_" } @{$d->{defs}}]; - } - if (defined $cflags{'am'} && $cflags{'am'}->{$obj}) { - # This file needs to go in an archive, so that we can - # change the compile flags as specified in Recipe - $use_archive = 1; - $archivecflags{$obj} = [$cflags{'am'}->{$obj}]; - } - if ($use_archive) { - $amspeciallibs{$obj} = "lib${obj}.a"; - $amlibobjname{$obj} = "lib${obj}_a-" . - basename($d->{deps}->[0], ".c", ".m") . - ".\$(OBJEXT)"; - } - $objtosrc{$obj} = $d->{deps}; - map { $allsources{$_} = 1 } @{$d->{deps}}; - } - - # 2014-02-22: as of automake-1.14 we begin to get complained at if - # we don't use this option - print "AUTOMAKE_OPTIONS = subdir-objects\n\n"; - - # Complete list of source and header files. Not used by the - # auto-generated parts of this makefile, but Recipe might like to - # have it available as a variable so that mandatory-rebuild things - # (version.o) can conveniently be made to depend on it. - print &splitline(join " ", "allsources", "=", - sort {$a cmp $b} keys %allsources), "\n\n"; - - @amcppflags = map {"-I\$(srcdir)/$_"} @srcdirs; - print &splitline(join " ", "AM_CPPFLAGS", "=", @amcppflags, "\n"); - - @amcflags = ("\$(GTK_CFLAGS)", "\$(WARNINGOPTS)"); - print &splitline(join " ", "AM_CFLAGS", "=", @amcflags), "\n"; - - %amlibsused = (); - foreach $p (&prognames("X:U")) { - ($prog, $type) = split ",", $p; - @progsources = ("${prog}_SOURCES", "="); - %sourcefiles = (); - @ldadd = (); - $objstr = &objects($p, "X", undef, undef); - foreach $obj (split / /,$objstr) { - if ($amspeciallibs{$obj}) { - $amlibsused{$obj} = 1; - push @ldadd, $amlibobjname{$obj}; - } else { - map { $sourcefiles{$_} = 1 } @{$objtosrc{$obj}}; - } - } - push @progsources, sort { $a cmp $b } keys %sourcefiles; - print &splitline(join " ", @progsources), "\n"; - if ($type eq "X") { - push @ldadd, "\$(GTK_LIBS)"; - } - push @ldadd, "-lm"; - print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n"; - print "\n"; - } - - foreach $obj (sort { $a cmp $b } keys %amlibsused) { - print &splitline(join " ", "lib${obj}_a_SOURCES", "=", - @{$objtosrc{$obj}}), "\n"; - print &splitline(join " ", "lib${obj}_a_CPPFLAGS", "=", - @amcflags, @{$archivecppflags{$obj}}), "\n" - if $archivecppflags{$obj}; - print &splitline(join " ", "lib${obj}_a_CFLAGS", "=", - @amcflags, @{$archivecflags{$obj}}), "\n" - if $archivecflags{$obj}; - } - print &splitline(join " ", "noinst_LIBRARIES", "=", - sort { $a cmp $b } - map { $amspeciallibs{$_} } - keys %amlibsused), - "\n\n"; - - print $makefile_extra{'am'} || ""; - select STDOUT; close OUT; -} - -if (defined $makefiles{'mpw'}) { - $mftyp = 'mpw'; - ##-- MPW Makefile - open OUT, ">$makefiles{'mpw'}"; select OUT; - print - "# Makefile for $project_name under MPW.\n#\n". - "# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # MPW command line option is -d not /D - ($_ = $help) =~ s/=\/D/=-d /gs; - print $_; - print "\n\n". - "ROptions = `Echo \"{VER}\" | StreamEdit -e \"1,\$ replace /=(\xc5)\xa81\xb0/ 'STR=\xb6\xb6\xb6\xb6\xb6\"' \xa81 '\xb6\xb6\xb6\xb6\xb6\"'\"`". - "\n". - "C_68K = {C}\n". - "C_CFM68K = {C}\n". - "C_PPC = {PPCC}\n". - "C_Carbon = {PPCC}\n". - "\n". - "# -w 35 disables \"unused parameter\" warnings\n". - "COptions = -i : -i :: -i ::charset -w 35 -w err -proto strict -ansi on \xb6\n". - " -notOnce\n". - "COptions_68K = {COptions} -model far -opt time\n". - "# Enabling \"-opt space\" for CFM-68K gives me undefined references to\n". - "# _\$LDIVT and _\$LMODT.\n". - "COptions_CFM68K = {COptions} -model cfmSeg -opt time\n". - "COptions_PPC = {COptions} -opt size -traceback\n". - "COptions_Carbon = {COptions} -opt size -traceback -d TARGET_API_MAC_CARBON\n". - "\n". - "Link_68K = ILink\n". - "Link_CFM68K = ILink\n". - "Link_PPC = PPCLink\n". - "Link_Carbon = PPCLink\n". - "\n". - "LinkOptions = -c 'pTTY'\n". - "LinkOptions_68K = {LinkOptions} -br 68k -model far -compact\n". - "LinkOptions_CFM68K = {LinkOptions} -br 020 -model cfmseg -compact\n". - "LinkOptions_PPC = {LinkOptions}\n". - "LinkOptions_Carbon = -m __appstart -w {LinkOptions}\n". - "\n". - "Libs_68K = \"{CLibraries}StdCLib.far.o\" \xb6\n". - " \"{Libraries}MacRuntime.o\" \xb6\n". - " \"{Libraries}MathLib.far.o\" \xb6\n". - " \"{Libraries}IntEnv.far.o\" \xb6\n". - " \"{Libraries}Interface.o\" \xb6\n". - " \"{Libraries}Navigation.far.o\" \xb6\n". - " \"{Libraries}OpenTransport.o\" \xb6\n". - " \"{Libraries}OpenTransportApp.o\" \xb6\n". - " \"{Libraries}OpenTptInet.o\" \xb6\n". - " \"{Libraries}UnicodeConverterLib.far.o\"\n". - "\n". - "Libs_CFM = \"{SharedLibraries}InterfaceLib\" \xb6\n". - " \"{SharedLibraries}StdCLib\" \xb6\n". - " \"{SharedLibraries}AppearanceLib\" \xb6\n". - " -weaklib AppearanceLib \xb6\n". - " \"{SharedLibraries}NavigationLib\" \xb6\n". - " -weaklib NavigationLib \xb6\n". - " \"{SharedLibraries}TextCommon\" \xb6\n". - " -weaklib TextCommon \xb6\n". - " \"{SharedLibraries}UnicodeConverter\" \xb6\n". - " -weaklib UnicodeConverter\n". - "\n". - "Libs_CFM68K = {Libs_CFM} \xb6\n". - " \"{CFM68KLibraries}NuMacRuntime.o\"\n". - "\n". - "Libs_PPC = {Libs_CFM} \xb6\n". - " \"{SharedLibraries}ControlsLib\" \xb6\n". - " -weaklib ControlsLib \xb6\n". - " \"{SharedLibraries}WindowsLib\" \xb6\n". - " -weaklib WindowsLib \xb6\n". - " \"{SharedLibraries}OpenTransportLib\" \xb6\n". - " -weaklib OTClientLib \xb6\n". - " -weaklib OTClientUtilLib \xb6\n". - " \"{SharedLibraries}OpenTptInternetLib\" \xb6\n". - " -weaklib OTInetClientLib \xb6\n". - " \"{PPCLibraries}StdCRuntime.o\" \xb6\n". - " \"{PPCLibraries}PPCCRuntime.o\" \xb6\n". - " \"{PPCLibraries}CarbonAccessors.o\" \xb6\n". - " \"{PPCLibraries}OpenTransportAppPPC.o\" \xb6\n". - " \"{PPCLibraries}OpenTptInetPPC.o\"\n". - "\n". - "Libs_Carbon = \"{PPCLibraries}CarbonStdCLib.o\" \xb6\n". - " \"{PPCLibraries}StdCRuntime.o\" \xb6\n". - " \"{PPCLibraries}PPCCRuntime.o\" \xb6\n". - " \"{SharedLibraries}CarbonLib\" \xb6\n". - " \"{SharedLibraries}StdCLib\"\n". - "\n"; - print &splitline("all \xc4 " . join(" ", &progrealnames("M")), undef, "\xb6"); - print "\n\n"; - foreach $p (&prognames("M")) { - ($prog, $type) = split ",", $p; - - print &splitline("$prog \xc4 $prog.68k $prog.ppc $prog.carbon", - undef, "\xb6"), "\n\n"; - - $rsrc = &objects($p, "", "X.rsrc", undef); - - foreach $arch (qw(68K CFM68K PPC Carbon)) { - $objstr = &objects($p, "X.\L$arch\E.o", "", undef); - print &splitline("$prog.\L$arch\E \xc4 $objstr $rsrc", undef, "\xb6"); - print "\n"; - print &splitline("\tDuplicate -y $rsrc {Targ}", 69, "\xb6"), "\n"; - print &splitline("\t{Link_$arch} -o {Targ} -fragname $prog " . - "{LinkOptions_$arch} " . - $objstr . " {Libs_$arch}", 69, "\xb6"), "\n"; - print &splitline("\tSetFile -a BMi {Targ}", 69, "\xb6"), "\n\n"; - } - - } - foreach $d (&deps("", "X.rsrc", "::", ":")) { - next unless $d->{obj}; - print &splitline(sprintf("%s \xc4 %s", $d->{obj}, join " ", @{$d->{deps}}), - undef, "\xb6"), "\n"; - print "\tRez ", $d->{deps}->[0], " -o {Targ} {ROptions}\n\n"; - } - foreach $arch (qw(68K CFM68K)) { - foreach $d (&deps("X.\L$arch\E.o", "", "::", ":")) { - next unless $d->{obj}; - print &splitline(sprintf("%s \xc4 %s", $d->{obj}, - join " ", @{$d->{deps}}), - undef, "\xb6"), "\n"; - print "\t{C_$arch} ", $d->{deps}->[0], - " -o {Targ} {COptions_$arch}\n\n"; - } - } - foreach $arch (qw(PPC Carbon)) { - foreach $d (&deps("X.\L$arch\E.o", "", "::", ":")) { - next unless $d->{obj}; - print &splitline(sprintf("%s \xc4 %s", $d->{obj}, - join " ", @{$d->{deps}}), - undef, "\xb6"), "\n"; - # The odd stuff here seems to stop afpd getting confused. - print "\techo -n > {Targ}\n"; - print "\tsetfile -t XCOF {Targ}\n"; - print "\t{C_$arch} ", $d->{deps}->[0], - " -o {Targ} {COptions_$arch}\n\n"; - } - } - select STDOUT; close OUT; -} - -if (defined $makefiles{'lcc'}) { - $mftyp = 'lcc'; - $dirpfx = &dirpfx($makefiles{'lcc'}, "\\"); - - ##-- lcc makefile - open OUT, ">$makefiles{'lcc'}"; select OUT; - print - "# Makefile for $project_name under lcc.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # lcc command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "\n". - "# If you rename this file to `Makefile', you should change this line,\n". - "# so that the .rsp files still depend on the correct makefile.\n". - "MAKEFILE = Makefile.lcc\n". - "\n". - "# C compilation flags\n". - "CFLAGS = -D_WINDOWS " . - (join " ", map {"-I$dirpfx$_"} @srcdirs) . - "\n". - "\n". - "# Get include directory for resource compiler\n". - "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); - print "\n\n"; - foreach $p (&prognames("G:C")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.obj", "X.res", undef); - print &splitline("$prog.exe: " . $objstr ), "\n"; - $subsystemtype = undef; - if ($type eq "G") { $subsystemtype = "-subsystem windows"; } - my $libss = "shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib"; - print &splitline("\tlcclnk $subsystemtype -o $prog.exe $objstr $libss"); - print "\n\n"; - } - - foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - if ($d->{obj} =~ /\.res$/) { - print &splitline("\tlrc \$(FWHACK) \$(RCFL) -r \$*.rc",69)."\n"; - } else { - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - print &splitline("\tlcc -O -p6 \$(COMPAT) \$(FWHACK) \$(CFLAGS)". - " \$(XFLAGS)$deflist ".$d->{deps}->[0]." -o \$\@",69)."\n"; - } - } - print "\n"; - print $makefile_extra{'lcc'} || ""; - print "\nclean:\n". - "\t-del *.obj\n". - "\t-del *.exe\n". - "\t-del *.res\n"; - - select STDOUT; close OUT; -} - -if (defined $makefiles{'nestedvm'}) { - $mftyp = 'nestedvm'; - $dirpfx = &dirpfx($makefiles{'nestedvm'}, "/"); - - ##-- NestedVM makefile - open OUT, ">$makefiles{'nestedvm'}"; select OUT; - print - "# Makefile for $project_name under NestedVM.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # gcc command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "\n". - "# This path points at the nestedvm root directory\n". - "NESTEDVM = /opt/nestedvm\n". - "# You can define this path to point at your tools if you need to\n". - "TOOLPATH = \$(NESTEDVM)/upstream/install/bin\n". - "CC = \$(TOOLPATH)/mips-unknown-elf-gcc\n". - "\n". - &splitline("CFLAGS = -O2 -Wall -Werror -DSLOW_SYSTEM -g " . - (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". - "\n"; - print &splitline("all:" . join "", map { " $_.jar" } &progrealnames("X")); - print "\n\n"; - foreach $p (&prognames("X")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.o", undef, undef); - $objstr =~ s/gtk\.o/nestedvm\.o/g; - print &splitline($prog . ".mips: " . $objstr), "\n"; - $libstr = &objects($p, undef, undef, "-lX"); - print &splitline("\t\$(CC) \$(${type}LDFLAGS) -o \$@ " . - $objstr . " $libstr -lm", 69), "\n\n"; - } - foreach $d (&deps("X.o", undef, $dirpfx, "/")) { - $oobjs = $d->{obj}; - $ddeps= join " ", @{$d->{deps}}; - $oobjs =~ s/gtk/nestedvm/g; - $ddeps =~ s/gtk/nestedvm/g; - print &splitline(sprintf("%s: %s", $oobjs, $ddeps)), - "\n"; - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" . - " -c \$< -o \$\@\n"; - } - print "\n"; - print $makefile_extra{'nestedvm'} || ""; - print "\nclean:\n". - "\trm -rf *.o *.mips *.class *.html *.jar org applet.manifest\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'osx'}) { - $mftyp = 'osx'; - $dirpfx = &dirpfx($makefiles{'osx'}, "/"); - @osxarchs = ('x86_64'); - my $osxminver = "10.6"; - - ##-- Mac OS X makefile - open OUT, ">$makefiles{'osx'}"; select OUT; - print - "# Makefile for $project_name under Mac OS X.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # gcc command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "CC = \$(TOOLPATH)gcc\n". - "LIPO = \$(TOOLPATH)lipo\n". - "\n". - &splitline("CFLAGS = -O2 -Wall -Werror -g " . - (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". - "LDFLAGS = -framework Cocoa\n". - &splitline("all:" . join "", map { " $_" } &progrealnames("MX:U")) . - "\n"; - print $makefile_extra{'osx'} || ""; - print "\n". - ".SUFFIXES: .o .c .m\n". - "\n"; - print "\n\n"; - foreach $p (&prognames("MX")) { - ($prog, $type) = split ",", $p; - $icon = &special($p, ".icns"); - $infoplist = &special($p, "info.plist"); - print "${prog}.app:\n\tmkdir -p \$\@\n"; - print "${prog}.app/Contents: ${prog}.app\n\tmkdir -p \$\@\n"; - print "${prog}.app/Contents/MacOS: ${prog}.app/Contents\n\tmkdir -p \$\@\n"; - $targets = "${prog}.app/Contents/MacOS/$prog"; - if (defined $icon) { - print "${prog}.app/Contents/Resources: ${prog}.app/Contents\n\tmkdir -p \$\@\n"; - print "${prog}.app/Contents/Resources/${prog}.icns: ${prog}.app/Contents/Resources $icon\n\tcp $icon \$\@\n"; - $targets .= " ${prog}.app/Contents/Resources/${prog}.icns"; - } - if (defined $infoplist) { - print "${prog}.app/Contents/Info.plist: ${prog}.app/Contents/Resources $infoplist\n\tcp $infoplist \$\@\n"; - $targets .= " ${prog}.app/Contents/Info.plist"; - } - $targets .= " \$(${prog}_extra)"; - print &splitline("${prog}: $targets", 69) . "\n\n"; - $libstr = &objects($p, undef, undef, "-lX"); - $archbins = ""; - foreach $arch (@osxarchs) { - $objstr = &objects($p, "X.${arch}.o", undef, undef); - print &splitline("${prog}.${arch}.bin: " . $objstr), "\n"; - print &splitline("\t\$(CC) -arch ${arch} -mmacosx-version-min=${osxminver} \$(LDFLAGS) -o \$@ " . - $objstr . " $libstr", 69), "\n\n"; - $archbins .= " ${prog}.${arch}.bin"; - } - print &splitline("${prog}.app/Contents/MacOS/$prog: ". - "${prog}.app/Contents/MacOS" . $archbins), "\n"; - print &splitline("\t\$(LIPO) -create $archbins -output \$@", 69), "\n\n"; - } - foreach $p (&prognames("U")) { - ($prog, $type) = split ",", $p; - $libstr = &objects($p, undef, undef, "-lX"); - $archbins = ""; - foreach $arch (@osxarchs) { - $objstr = &objects($p, "X.${arch}.o", undef, undef); - print &splitline("${prog}.${arch}: " . $objstr), "\n"; - print &splitline("\t\$(CC) -arch ${arch} -mmacosx-version-min=${osxminver} \$(ULDFLAGS) -o \$@ " . - $objstr . " $libstr", 69), "\n\n"; - $archbins .= " ${prog}.${arch}"; - } - print &splitline("${prog}:" . $archbins), "\n"; - print &splitline("\t\$(LIPO) -create $archbins -output \$@", 69), "\n\n"; - } - foreach $arch (@osxarchs) { - foreach $d (&deps("X.${arch}.o", undef, $dirpfx, "/")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - if ($d->{deps}->[0] =~ /\.m$/) { - print "\t\$(CC) -arch $arch -mmacosx-version-min=${osxminver} -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS)". - " \$(XFLAGS)$deflist -c \$< -o \$\@\n"; - } else { - print "\t\$(CC) -arch $arch -mmacosx-version-min=${osxminver} \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" . - " -c \$< -o \$\@\n"; - } - } - } - print "\nclean:\n". - "\trm -f *.o *.dmg". (join "", map { my $a=$_; (" $a", map { " ${a}.$_" } @osxarchs) } &progrealnames("U")) . "\n". - "\trm -rf *.app\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'gnustep'}) { - $mftyp = 'gnustep'; - $dirpfx = &dirpfx($makefiles{'gnustep'}, "/"); - - ##-- GNUstep makefile (use with 'gs_make -f Makefile.gnustep') - - # This is a pretty evil way to do things. In an ideal world, I'd - # use the approved GNUstep makefile mechanism which just defines a - # variable or two saying what source files go into what binary and - # then includes application.make. Unfortunately, that has the - # automake-ish limitation that it doesn't let you choose different - # command lines for each object, so I can't arrange for all those - # files with -DTHIS and -DTHAT to Just Work. - # - # A simple if ugly fix would be to have mkfiles.pl construct a - # directory full of stub C files of the form '#define thing', - # '#include "real_source_file"', and then reference those in this - # makefile. That would also make it easy to build a proper - # automake makefile. - open OUT, ">$makefiles{'gnustep'}"; select OUT; - print - "# Makefile for $project_name under GNUstep.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # gcc command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "NEEDS_GUI=yes\n". - "include \$(GNUSTEP_MAKEFILES)/common.make\n". - "include \$(GNUSTEP_MAKEFILES)/rules.make\n". - "include \$(GNUSTEP_MAKEFILES)/Instance/rules.make\n". - "\n". - &splitline("all::" . join "", map { " $_" } &progrealnames("MX:U")) . - "\n"; - print $makefile_extra{'gnustep'} || ""; - print "\n". - ".SUFFIXES: .o .c .m\n". - "\n"; - print "\n\n"; - foreach $p (&prognames("MX")) { - ($prog, $type) = split ",", $p; - $icon = &special($p, ".icns"); - $infoplist = &special($p, "info.plist"); - print "${prog}.app:\n\tmkdir -p \$\@\n"; - $targets = "${prog}.app ${prog}.app/$prog"; - if (defined $icon) { - print "${prog}.app/Resources: ${prog}.app\n\tmkdir -p \$\@\n"; - print "${prog}.app/Resources/${prog}.icns: ${prog}.app/Resources $icon\n\tcp $icon \$\@\n"; - $targets .= " ${prog}.app/Resources/${prog}.icns"; - } - if (defined $infoplist) { - print "${prog}.app/Info.plist: ${prog}.app $infoplist\n\tcp $infoplist \$\@\n"; - $targets .= " ${prog}.app/Info.plist"; - } - $targets .= " \$(${prog}_extra)"; - print &splitline("${prog}: $targets", 69) . "\n\n"; - $libstr = &objects($p, undef, undef, "-lX"); - $objstr = &objects($p, "X.o", undef, undef); - print &splitline("${prog}.app/$prog: " . $objstr), "\n"; - print &splitline("\t\$(CC) \$(ALL_LDFLAGS) -o \$@ " . $objstr . " \$(ALL_LIB_DIRS) $libstr \$(ALL_LIBS)", 69), "\n\n"; - } - foreach $p (&prognames("U")) { - ($prog, $type) = split ",", $p; - $libstr = &objects($p, undef, undef, "-lX"); - $objstr = &objects($p, "X.o", undef, undef); - print &splitline("${prog}: " . $objstr), "\n"; - print &splitline("\t\$(CC) \$(ULDFLAGS) -o \$@ " . $objstr . " $libstr", 69), "\n\n"; - } - foreach $d (&deps("X.o", undef, $dirpfx, "/")) { - print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), - "\n"; - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - if ($d->{deps}->[0] =~ /\.m$/) { - print "\t\$(CC) -DGNUSTEP \$(ALL_OBJCFLAGS) \$(COMPAT) \$(FWHACK) \$(OBJCFLAGS)". - " \$(XFLAGS)$deflist -c \$< -o \$\@\n"; - } else { - print "\t\$(CC) \$(ALL_CFLAGS) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist" . - " -c \$< -o \$\@\n"; - } - } - print "\nclean::\n". - "\trm -f *.o ". (join " ", &progrealnames("U")) . "\n". - "\trm -rf *.app\n"; - select STDOUT; close OUT; -} - -if (defined $makefiles{'emcc'}) { - $mftyp = 'emcc'; - $dirpfx = &dirpfx($makefiles{'emcc'}, "/"); - - ##-- Makefile for building Javascript puzzles via Emscripten - - open OUT, ">$makefiles{'emcc'}"; select OUT; - print - "# Makefile for $project_name using Emscripten. Requires GNU make.\n". - "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". - "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; - # emcc command line option is -D not /D - ($_ = $help) =~ s/=\/D/=-D/gs; - print $_; - print - "\n". - "# This can be set on the command line to point at the emcc command,\n". - "# if it is not on your PATH.\n". - "EMCC = emcc\n". - "\n". - &splitline("CFLAGS = -DSLOW_SYSTEM " . - (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". - "\n"; - $output_js_files = join "", map { " \$(OUTPREFIX)$_.js" } &progrealnames("X"); - print &splitline("all:" . $output_js_files); - print "\n\n"; - foreach $p (&prognames("X")) { - ($prog, $type) = split ",", $p; - $objstr = &objects($p, "X.o", undef, undef); - $objstr =~ s/gtk\.o/emcc\.o/g; - print &splitline("\$(OUTPREFIX)" . $prog . ".js: " . $objstr . " emccpre.js emcclib.js emccx.json"), "\n"; - print "\t\$(EMCC) -o \$(OUTPREFIX)".$prog.".js ". - "-O2 ". - "-s ASM_JS=1 ". - "--pre-js emccpre.js ". - "--js-library emcclib.js ". - "-s EXPORTED_FUNCTIONS=\"`sed 's://.*::' emccx.json | tr -d ' \\n'`\" " . $objstr . "\n\n"; - } - foreach $d (&deps("X.o", undef, $dirpfx, "/")) { - $oobjs = $d->{obj}; - $ddeps= join " ", @{$d->{deps}}; - $oobjs =~ s/gtk/emcc/g; - $ddeps =~ s/gtk/emcc/g; - print &splitline(sprintf("%s: %s", $oobjs, $ddeps)), - "\n"; - $deflist = join "", map { " -D$_" } @{$d->{defs}}; - print "\t\$(EMCC) \$(CFLAGS) \$(XFLAGS)$deflist" . - " -c \$< -o \$\@\n"; - } - print "\n"; - print $makefile_extra{'emcc'} || ""; - print "\nclean:\n". - "\trm -rf *.o $output_js_files\n"; - select STDOUT; close OUT; -} - -# All done, so do the Unix postprocessing if asked to. - -if ($do_unix) { - chdir $orig_dir; - system "./mkauto.sh"; - die "mkfiles.pl: mkauto.sh returned $?\n" if $? > 0; - system "./configure", @confargs; - die "mkfiles.pl: configure returned $?\n" if $? > 0; -} diff --git a/apps/plugins/puzzles/src/noicon.rc b/apps/plugins/puzzles/src/noicon.rc deleted file mode 100644 index 1de605d605..0000000000 --- a/apps/plugins/puzzles/src/noicon.rc +++ /dev/null @@ -1,11 +0,0 @@ -/* Puzzle resource file without an icon, used in the absence of icons/foo.rc */ - -#include "puzzles.rc2" - -/* XXX this probably isn't the right test, but it'll do. */ -#ifdef MINGW32_FIX -/* XXX The MinGW toolchain (specifically, windres) doesn't like a resource - * file with no resources. Give it a dummy one. - * This can go if/when VERSIONINFO resources are added. */ -200 RCDATA { 0 } -#endif diff --git a/apps/plugins/puzzles/src/osx-info.plist b/apps/plugins/puzzles/src/osx-info.plist deleted file mode 100644 index ec90b9f224..0000000000 --- a/apps/plugins/puzzles/src/osx-info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleIconFile - Puzzles.icns - CFBundleHelpBookFolder - Help - CFBundleHelpBookName - Puzzles Help - CFBundleName - Puzzles - CFBundleDisplayName - Puzzles - CFBundleExecutable - Puzzles - CFBundleVersion - Unidentified build - CFBundleShortVersionString - Unidentified build - CFBundleDevelopmentRegion - en - CFBundleIdentifier - uk.org.greenend.chiark.sgtatham.puzzles - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleSignature - ???? - NSHumanReadableCopyright - This software is copyright (c) 2004-2014 Simon Tatham - NSHighResolutionCapable - - - diff --git a/apps/plugins/puzzles/src/osx.icns b/apps/plugins/puzzles/src/osx.icns deleted file mode 100644 index b4346a0da1..0000000000 Binary files a/apps/plugins/puzzles/src/osx.icns and /dev/null differ diff --git a/apps/plugins/puzzles/src/osx.m b/apps/plugins/puzzles/src/osx.m deleted file mode 100644 index 0793817776..0000000000 --- a/apps/plugins/puzzles/src/osx.m +++ /dev/null @@ -1,1768 +0,0 @@ -/* - * Mac OS X / Cocoa front end to puzzles. - * - * Still to do: - * - * - I'd like to be able to call up context help for a specific - * game at a time. - * - * Mac interface issues that possibly could be done better: - * - * - is there a better approach to frontend_default_colour? - * - * - do we need any more options in the Window menu? - * - * - can / should we be doing anything with the titles of the - * configuration boxes? - * - * - not sure what I should be doing about default window - * placement. Centring new windows is a bit feeble, but what's - * better? Is there a standard way to tell the OS "here's the - * _size_ of window I want, now use your best judgment about the - * initial position"? - * + there's a standard _policy_ on window placement, given in - * the HI guidelines. Have to implement it ourselves though, - * bah. - * - * - a brief frob of the Mac numeric keypad suggests that it - * generates numbers no matter what you do. I wonder if I should - * try to figure out a way of detecting keypad codes so I can - * implement UP_LEFT and friends. Alternatively, perhaps I - * should simply assign the number keys to UP_LEFT et al? - * They're not in use for anything else right now. - * - * - see if we can do anything to one-button-ise the multi-button - * dependent puzzle UIs: - * - Pattern is a _little_ unwieldy but not too bad (since - * generally you never need the middle button unless you've - * made a mistake, so it's just click versus command-click). - * - Net is utterly vile; having normal click be one rotate and - * command-click be the other introduces a horrid asymmetry, - * and yet requiring a shift key for _each_ click would be - * even worse because rotation feels as if it ought to be the - * default action. I fear this is why the Flash Net had the - * UI it did... - * + I've tried out an alternative dragging interface for - * Net; it might work nicely for stylus-based platforms - * where you have better hand/eye feedback for the thing - * you're clicking on, but it's rather unwieldy on the - * Mac. I fear even shift-clicking is better than that. - * - * - Should we _return_ to a game configuration sheet once an - * error is reported by midend_set_config, to allow the user to - * correct the one faulty input and keep the other five OK ones? - * The Apple `one sheet at a time' restriction would require me - * to do this by closing the config sheet, opening the alert - * sheet, and then reopening the config sheet when the alert is - * closed; and the human interface types, who presumably - * invented the one-sheet-at-a-time rule for good reasons, might - * look with disfavour on me trying to get round them to fake a - * nested sheet. On the other hand I think there are good - * practical reasons for wanting it that way. Uncertain. - * - * - User feedback dislikes nothing happening when you start the - * app; they suggest a finder-like window containing an icon for - * each puzzle type, enabling you to start one easily. Needs - * thought. - * - * Grotty implementation details that could probably be improved: - * - * - I am _utterly_ unconvinced that NSImageView was the right way - * to go about having a window with a reliable backing store! It - * just doesn't feel right; NSImageView is a _control_. Is there - * a simpler way? - * - * - Resizing is currently very bad; rather than bother to work - * out how to resize the NSImageView, I just splatter and - * recreate it. - */ - -#define COMBINED /* we put all the puzzles in one binary in this port */ - -#include -#include -#include -#import -#include "puzzles.h" - -/* ---------------------------------------------------------------------- - * Global variables. - */ - -/* - * The `Type' menu. We frob this dynamically to allow the user to - * choose a preset set of settings from the current game. - */ -NSMenu *typemenu; - -/* - * Forward reference. - */ -extern const struct drawing_api osx_drawing; - -/* - * The NSApplication shared instance, which I'll want to refer to from - * a few places here and there. - */ -NSApplication *app; - -/* ---------------------------------------------------------------------- - * Miscellaneous support routines that aren't part of any object or - * clearly defined subsystem. - */ - -void fatal(const char *fmt, ...) -{ - va_list ap; - char errorbuf[2048]; - NSAlert *alert; - - va_start(ap, fmt); - vsnprintf(errorbuf, lenof(errorbuf), fmt, ap); - va_end(ap); - - alert = [NSAlert alloc]; - /* - * We may have come here because we ran out of memory, in which - * case it's entirely likely that that alloc will fail, so we - * should have a fallback of some sort. - */ - if (!alert) { - fprintf(stderr, "fatal error (and NSAlert failed): %s\n", errorbuf); - } else { - alert = [[alert init] autorelease]; - [alert addButtonWithTitle:@"Oh dear"]; - [alert setInformativeText:[NSString stringWithUTF8String:errorbuf]]; - [alert runModal]; - } - exit(1); -} - -void frontend_default_colour(frontend *fe, float *output) -{ - /* FIXME: Is there a system default we can tap into for this? */ - output[0] = output[1] = output[2] = 0.8F; -} - -void get_random_seed(void **randseed, int *randseedsize) -{ - time_t *tp = snew(time_t); - time(tp); - *randseed = (void *)tp; - *randseedsize = sizeof(time_t); -} - -static void savefile_write(void *wctx, const void *buf, int len) -{ - FILE *fp = (FILE *)wctx; - fwrite(buf, 1, len, fp); -} - -static bool savefile_read(void *wctx, void *buf, int len) -{ - FILE *fp = (FILE *)wctx; - int ret; - - ret = fread(buf, 1, len, fp); - return (ret == len); -} - -/* - * Since this front end does not support printing (yet), we need - * this stub to satisfy the reference in midend_print_puzzle(). - */ -void document_add_puzzle(document *doc, const game *game, game_params *par, - game_state *st, game_state *st2) -{ -} - -/* - * setAppleMenu isn't listed in the NSApplication header, but an - * NSApp responds to it, so we're adding it here to silence - * warnings. (This was removed from the headers in 10.4, so we - * only need to include it for 10.4+.) - */ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 -@interface NSApplication(NSAppleMenu) -- (void)setAppleMenu:(NSMenu *)menu; -@end -#endif - -/* ---------------------------------------------------------------------- - * Tiny extension to NSMenuItem which carries a payload of a `void - * *', allowing several menu items to invoke the same message but - * pass different data through it. - */ -@interface DataMenuItem : NSMenuItem -{ - void *payload; -} -- (void)setPayload:(void *)d; -- (void *)getPayload; -@end -@implementation DataMenuItem -- (void)setPayload:(void *)d -{ - payload = d; -} -- (void *)getPayload -{ - return payload; -} -@end - -/* ---------------------------------------------------------------------- - * Utility routines for constructing OS X menus. - */ - -NSMenu *newmenu(const char *title) -{ - return [[[NSMenu allocWithZone:[NSMenu menuZone]] - initWithTitle:[NSString stringWithUTF8String:title]] - autorelease]; -} - -NSMenu *newsubmenu(NSMenu *parent, const char *title) -{ - NSMenuItem *item; - NSMenu *child; - - item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] - initWithTitle:[NSString stringWithUTF8String:title] - action:NULL - keyEquivalent:@""] - autorelease]; - child = newmenu(title); - [item setEnabled:YES]; - [item setSubmenu:child]; - [parent addItem:item]; - return child; -} - -id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title, - const char *key, id target, SEL action) -{ - unsigned mask = NSCommandKeyMask; - - if (key[strcspn(key, "-")]) { - while (*key && *key != '-') { - int c = tolower((unsigned char)*key); - if (c == 's') { - mask |= NSShiftKeyMask; - } else if (c == 'o' || c == 'a') { - mask |= NSAlternateKeyMask; - } - key++; - } - if (*key) - key++; - } - - item = [[item initWithTitle:[NSString stringWithUTF8String:title] - action:NULL - keyEquivalent:[NSString stringWithUTF8String:key]] - autorelease]; - - if (*key) - [item setKeyEquivalentModifierMask: mask]; - - [item setEnabled:YES]; - [item setTarget:target]; - [item setAction:action]; - - [parent addItem:item]; - - return item; -} - -NSMenuItem *newitem(NSMenu *parent, const char *title, const char *key, - id target, SEL action) -{ - return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]], - parent, title, key, target, action); -} - -/* ---------------------------------------------------------------------- - * About box. - */ - -@class AboutBox; - -@interface AboutBox : NSWindow -{ -} -- (id)init; -@end - -@implementation AboutBox -- (id)init -{ - NSRect totalrect; - NSView *views[16]; - int nviews = 0; - NSImageView *iv; - NSTextField *tf; - NSFont *font1 = [NSFont systemFontOfSize:0]; - NSFont *font2 = [NSFont boldSystemFontOfSize:[font1 pointSize] * 1.1]; - const int border = 24; - int i; - double y; - - /* - * Construct the controls that go in the About box. - */ - - iv = [[NSImageView alloc] initWithFrame:NSMakeRect(0,0,64,64)]; - [iv setImage:[NSImage imageNamed:@"NSApplicationIcon"]]; - views[nviews++] = iv; - - tf = [[NSTextField alloc] - initWithFrame:NSMakeRect(0,0,400,1)]; - [tf setEditable:NO]; - [tf setSelectable:NO]; - [tf setBordered:NO]; - [tf setDrawsBackground:NO]; - [tf setFont:font2]; - [tf setStringValue:@"Simon Tatham's Portable Puzzle Collection"]; - [tf sizeToFit]; - views[nviews++] = tf; - - tf = [[NSTextField alloc] - initWithFrame:NSMakeRect(0,0,400,1)]; - [tf setEditable:NO]; - [tf setSelectable:NO]; - [tf setBordered:NO]; - [tf setDrawsBackground:NO]; - [tf setFont:font1]; - [tf setStringValue:[NSString stringWithUTF8String:ver]]; - [tf sizeToFit]; - views[nviews++] = tf; - - /* - * Lay the controls out. - */ - totalrect = NSMakeRect(0,0,0,0); - for (i = 0; i < nviews; i++) { - NSRect r = [views[i] frame]; - if (totalrect.size.width < r.size.width) - totalrect.size.width = r.size.width; - totalrect.size.height += border + r.size.height; - } - totalrect.size.width += 2 * border; - totalrect.size.height += border; - y = totalrect.size.height; - for (i = 0; i < nviews; i++) { - NSRect r = [views[i] frame]; - r.origin.x = (totalrect.size.width - r.size.width) / 2; - y -= border + r.size.height; - r.origin.y = y; - [views[i] setFrame:r]; - } - - self = [super initWithContentRect:totalrect - styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask | - NSClosableWindowMask) - backing:NSBackingStoreBuffered - defer:YES]; - - for (i = 0; i < nviews; i++) - [[self contentView] addSubview:views[i]]; - - [self center]; /* :-) */ - - return self; -} -@end - -/* ---------------------------------------------------------------------- - * The front end presented to midend.c. - * - * This is mostly a subclass of NSWindow. The actual `frontend' - * structure passed to the midend contains a variety of pointers, - * including that window object but also including the image we - * draw on, an ImageView to display it in the window, and so on. - */ - -@class GameWindow; -@class MyImageView; - -struct frontend { - GameWindow *window; - NSImage *image; - MyImageView *view; - NSColor **colours; - int ncolours; - bool clipped; - int w, h; -}; - -@interface MyImageView : NSImageView -{ - GameWindow *ourwin; -} -- (void)setWindow:(GameWindow *)win; -- (void)mouseEvent:(NSEvent *)ev button:(int)b; -- (void)mouseDown:(NSEvent *)ev; -- (void)mouseDragged:(NSEvent *)ev; -- (void)mouseUp:(NSEvent *)ev; -- (void)rightMouseDown:(NSEvent *)ev; -- (void)rightMouseDragged:(NSEvent *)ev; -- (void)rightMouseUp:(NSEvent *)ev; -- (void)otherMouseDown:(NSEvent *)ev; -- (void)otherMouseDragged:(NSEvent *)ev; -- (void)otherMouseUp:(NSEvent *)ev; -@end - -@interface GameWindow : NSWindow -{ - const game *ourgame; - midend *me; - struct frontend fe; - struct timeval last_time; - NSTimer *timer; - NSWindow *sheet; - config_item *cfg; - int cfg_which; - NSView **cfg_controls; - int cfg_ncontrols; - NSTextField *status; - struct preset_menu *preset_menu; - NSMenuItem **preset_menu_items; - int n_preset_menu_items; -} -- (id)initWithGame:(const game *)g; -- (void)dealloc; -- (void)processButton:(int)b x:(int)x y:(int)y; -- (void)processKey:(int)b; -- (void)keyDown:(NSEvent *)ev; -- (void)activateTimer; -- (void)deactivateTimer; -- (void)setStatusLine:(const char *)text; -- (void)resizeForNewGameParams; -- (void)updateTypeMenuTick; -@end - -@implementation MyImageView - -- (void)setWindow:(GameWindow *)win -{ - ourwin = win; -} - -- (void)mouseEvent:(NSEvent *)ev button:(int)b -{ - NSPoint point = [self convertPoint:[ev locationInWindow] fromView:nil]; - [ourwin processButton:b x:point.x y:point.y]; -} - -- (void)mouseDown:(NSEvent *)ev -{ - unsigned mod = [ev modifierFlags]; - [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_BUTTON : - (mod & NSShiftKeyMask) ? MIDDLE_BUTTON : - LEFT_BUTTON)]; -} -- (void)mouseDragged:(NSEvent *)ev -{ - unsigned mod = [ev modifierFlags]; - [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_DRAG : - (mod & NSShiftKeyMask) ? MIDDLE_DRAG : - LEFT_DRAG)]; -} -- (void)mouseUp:(NSEvent *)ev -{ - unsigned mod = [ev modifierFlags]; - [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_RELEASE : - (mod & NSShiftKeyMask) ? MIDDLE_RELEASE : - LEFT_RELEASE)]; -} -- (void)rightMouseDown:(NSEvent *)ev -{ - unsigned mod = [ev modifierFlags]; - [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_BUTTON : - RIGHT_BUTTON)]; -} -- (void)rightMouseDragged:(NSEvent *)ev -{ - unsigned mod = [ev modifierFlags]; - [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_DRAG : - RIGHT_DRAG)]; -} -- (void)rightMouseUp:(NSEvent *)ev -{ - unsigned mod = [ev modifierFlags]; - [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_RELEASE : - RIGHT_RELEASE)]; -} -- (void)otherMouseDown:(NSEvent *)ev -{ - [self mouseEvent:ev button:MIDDLE_BUTTON]; -} -- (void)otherMouseDragged:(NSEvent *)ev -{ - [self mouseEvent:ev button:MIDDLE_DRAG]; -} -- (void)otherMouseUp:(NSEvent *)ev -{ - [self mouseEvent:ev button:MIDDLE_RELEASE]; -} -@end - -@implementation GameWindow -- (void)setupContentView -{ - NSRect frame; - int w, h; - - if (status) { - frame = [status frame]; - frame.origin.y = frame.size.height; - } else - frame.origin.y = 0; - frame.origin.x = 0; - - w = h = INT_MAX; - midend_size(me, &w, &h, false); - frame.size.width = w; - frame.size.height = h; - fe.w = w; - fe.h = h; - - fe.image = [[NSImage alloc] initWithSize:frame.size]; - fe.view = [[MyImageView alloc] initWithFrame:frame]; - [fe.view setImage:fe.image]; - [fe.view setWindow:self]; - - midend_redraw(me); - - [[self contentView] addSubview:fe.view]; -} -- (id)initWithGame:(const game *)g -{ - NSRect rect = { {0,0}, {0,0} }, rect2; - int w, h; - - ourgame = g; - preset_menu = NULL; - preset_menu_items = NULL; - - fe.window = self; - - me = midend_new(&fe, ourgame, &osx_drawing, &fe); - /* - * If we ever need to open a fresh window using a provided game - * ID, I think the right thing is to move most of this method - * into a new initWithGame:gameID: method, and have - * initWithGame: simply call that one and pass it NULL. - */ - midend_new_game(me); - w = h = INT_MAX; - midend_size(me, &w, &h, false); - rect.size.width = w; - rect.size.height = h; - fe.w = w; - fe.h = h; - - /* - * Create the status bar, which will just be an NSTextField. - */ - if (midend_wants_statusbar(me)) { - status = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,100,50)]; - [status setEditable:NO]; - [status setSelectable:NO]; - [status setBordered:YES]; - [status setBezeled:YES]; - [status setBezelStyle:NSTextFieldSquareBezel]; - [status setDrawsBackground:YES]; - [[status cell] setTitle:@DEFAULT_STATUSBAR_TEXT]; - [status sizeToFit]; - rect2 = [status frame]; - rect.size.height += rect2.size.height; - rect2.size.width = rect.size.width; - rect2.origin.x = rect2.origin.y = 0; - [status setFrame:rect2]; - } else - status = nil; - - self = [super initWithContentRect:rect - styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask | - NSClosableWindowMask) - backing:NSBackingStoreBuffered - defer:YES]; - [self setTitle:[NSString stringWithUTF8String:ourgame->name]]; - - { - float *colours; - int i, ncolours; - - colours = midend_colours(me, &ncolours); - fe.ncolours = ncolours; - fe.colours = snewn(ncolours, NSColor *); - - for (i = 0; i < ncolours; i++) { - fe.colours[i] = [[NSColor colorWithDeviceRed:colours[i*3] - green:colours[i*3+1] blue:colours[i*3+2] - alpha:1.0] retain]; - } - } - - [self setupContentView]; - if (status) - [[self contentView] addSubview:status]; - [self setIgnoresMouseEvents:NO]; - - [self center]; /* :-) */ - - return self; -} - -- (void)dealloc -{ - int i; - for (i = 0; i < fe.ncolours; i++) { - [fe.colours[i] release]; - } - sfree(fe.colours); - sfree(preset_menu_items); - midend_free(me); - [super dealloc]; -} - -- (void)processButton:(int)b x:(int)x y:(int)y -{ - if (!midend_process_key(me, x, fe.h - 1 - y, b)) - [self close]; -} - -- (void)processKey:(int)b -{ - if (!midend_process_key(me, -1, -1, b)) - [self close]; -} - -- (void)keyDown:(NSEvent *)ev -{ - NSString *s = [ev characters]; - int i, n = [s length]; - - for (i = 0; i < n; i++) { - int c = [s characterAtIndex:i]; - - /* - * ASCII gets passed straight to midend_process_key. - * Anything above that has to be translated to our own - * function key codes. - */ - if (c >= 0x80) { - bool mods = false; - switch (c) { - case NSUpArrowFunctionKey: - c = CURSOR_UP; - mods = true; - break; - case NSDownArrowFunctionKey: - c = CURSOR_DOWN; - mods = true; - break; - case NSLeftArrowFunctionKey: - c = CURSOR_LEFT; - mods = true; - break; - case NSRightArrowFunctionKey: - c = CURSOR_RIGHT; - mods = true; - break; - default: - continue; - } - - if (mods) { - if ([ev modifierFlags] & NSShiftKeyMask) - c |= MOD_SHFT; - if ([ev modifierFlags] & NSControlKeyMask) - c |= MOD_CTRL; - } - } - - if (c >= '0' && c <= '9' && ([ev modifierFlags] & NSNumericPadKeyMask)) - c |= MOD_NUM_KEYPAD; - - if (c == 26 && - !((NSShiftKeyMask | NSControlKeyMask) & ~[ev modifierFlags])) - c = UI_REDO; - - [self processKey:c]; - } -} - -- (void)activateTimer -{ - if (timer != nil) - return; - - timer = [NSTimer scheduledTimerWithTimeInterval:0.02 - target:self selector:@selector(timerTick:) - userInfo:nil repeats:YES]; - gettimeofday(&last_time, NULL); -} - -- (void)deactivateTimer -{ - if (timer == nil) - return; - - [timer invalidate]; - timer = nil; -} - -- (void)timerTick:(id)sender -{ - struct timeval now; - float elapsed; - gettimeofday(&now, NULL); - elapsed = ((now.tv_usec - last_time.tv_usec) * 0.000001F + - (now.tv_sec - last_time.tv_sec)); - midend_timer(me, elapsed); - last_time = now; -} - -- (void)showError:(const char *)message -{ - NSAlert *alert; - - alert = [[[NSAlert alloc] init] autorelease]; - [alert addButtonWithTitle:@"Bah"]; - [alert setInformativeText:[NSString stringWithUTF8String:message]]; - [alert beginSheetModalForWindow:self modalDelegate:nil - didEndSelector:NULL contextInfo:nil]; -} - -- (void)newGame:(id)sender -{ - [self processKey:UI_NEWGAME]; -} -- (void)restartGame:(id)sender -{ - midend_restart_game(me); -} -- (void)saveGame:(id)sender -{ - NSSavePanel *sp = [NSSavePanel savePanel]; - - if ([sp runModal] == NSFileHandlingPanelOKButton) { - const char *name = [[sp URL] fileSystemRepresentation]; - - FILE *fp = fopen(name, "w"); - - if (!fp) { - [self showError:"Unable to open save file"]; - return; - } - - midend_serialise(me, savefile_write, fp); - - fclose(fp); - } -} -- (void)loadSavedGame:(id)sender -{ - NSOpenPanel *op = [NSOpenPanel openPanel]; - - [op setAllowsMultipleSelection:NO]; - - if ([op runModal] == NSOKButton) { - const char *name = [[[op URLs] objectAtIndex:0] - fileSystemRepresentation]; - const char *err; - - FILE *fp = fopen(name, "r"); - - if (!fp) { - [self showError:"Unable to open saved game file"]; - return; - } - - err = midend_deserialise(me, savefile_read, fp); - - fclose(fp); - - if (err) { - [self showError:err]; - return; - } - - [self resizeForNewGameParams]; - [self updateTypeMenuTick]; - } -} -- (void)undoMove:(id)sender -{ - [self processKey:UI_UNDO]; -} -- (void)redoMove:(id)sender -{ - [self processKey:UI_REDO]; -} - -- (void)copy:(id)sender -{ - char *text; - - if ((text = midend_text_format(me)) != NULL) { - NSPasteboard *pb = [NSPasteboard generalPasteboard]; - NSArray *a = [NSArray arrayWithObject:NSStringPboardType]; - [pb declareTypes:a owner:nil]; - [pb setString:[NSString stringWithUTF8String:text] - forType:NSStringPboardType]; - } else - NSBeep(); -} - -- (void)solveGame:(id)sender -{ - const char *msg; - - msg = midend_solve(me); - - if (msg) - [self showError:msg]; -} - -- (BOOL)validateMenuItem:(NSMenuItem *)item -{ - if ([item action] == @selector(copy:)) - return (ourgame->can_format_as_text_ever && - midend_can_format_as_text_now(me) ? YES : NO); - else if ([item action] == @selector(solveGame:)) - return (ourgame->can_solve ? YES : NO); - else - return [super validateMenuItem:item]; -} - -- (void)clearTypeMenu -{ - int i; - - while ([typemenu numberOfItems] > 1) - [typemenu removeItemAtIndex:0]; - [[typemenu itemAtIndex:0] setState:NSOffState]; - - for (i = 0; i < n_preset_menu_items; i++) - preset_menu_items[i] = NULL; -} - -- (void)updateTypeMenuTick -{ - int i, n; - - n = midend_which_preset(me); - - for (i = 0; i < n_preset_menu_items; i++) - if (preset_menu_items[i]) - [preset_menu_items[i] setState:(i == n ? NSOnState : NSOffState)]; - - /* - * The Custom menu item is always right at the bottom of the - * Type menu. - */ - [[typemenu itemAtIndex:[typemenu numberOfItems]-1] - setState:(n < 0 ? NSOnState : NSOffState)]; -} - -- (void)populateTypeMenu:(NSMenu *)nsmenu from:(struct preset_menu *)menu -{ - int i; - - /* - * We process the entries in reverse order so that (in the - * top-level Type menu at least) we don't disturb the 'Custom' - * item which remains fixed even when we change back and forth - * between puzzle type windows. - */ - for (i = menu->n_entries; i-- > 0 ;) { - struct preset_menu_entry *entry = &menu->entries[i]; - NSMenuItem *item; - - if (entry->params) { - DataMenuItem *ditem; - ditem = [[[DataMenuItem alloc] - initWithTitle:[NSString stringWithUTF8String: - entry->title] - action:NULL keyEquivalent:@""] - autorelease]; - - [ditem setTarget:self]; - [ditem setAction:@selector(presetGame:)]; - [ditem setPayload:entry->params]; - - preset_menu_items[entry->id] = ditem; - - item = ditem; - } else { - NSMenu *nssubmenu; - - item = [[[NSMenuItem alloc] - initWithTitle:[NSString stringWithUTF8String: - entry->title] - action:NULL keyEquivalent:@""] - autorelease]; - nssubmenu = newmenu(entry->title); - [item setSubmenu:nssubmenu]; - - [self populateTypeMenu:nssubmenu from:entry->submenu]; - } - - [item setEnabled:YES]; - [nsmenu insertItem:item atIndex:0]; - } -} - -- (void)becomeKeyWindow -{ - [self clearTypeMenu]; - - [super becomeKeyWindow]; - - if (!preset_menu) { - int i; - preset_menu = midend_get_presets(me, &n_preset_menu_items); - preset_menu_items = snewn(n_preset_menu_items, NSMenuItem *); - for (i = 0; i < n_preset_menu_items; i++) - preset_menu_items[i] = NULL; - } - - if (preset_menu->n_entries > 0) { - [typemenu insertItem:[NSMenuItem separatorItem] atIndex:0]; - [self populateTypeMenu:typemenu from:preset_menu]; - } - - [self updateTypeMenuTick]; -} - -- (void)resignKeyWindow -{ - [self clearTypeMenu]; - [super resignKeyWindow]; -} - -- (void)close -{ - [self clearTypeMenu]; - [super close]; -} - -- (void)resizeForNewGameParams -{ - NSSize size = {0,0}; - int w, h; - - w = h = INT_MAX; - midend_size(me, &w, &h, false); - size.width = w; - size.height = h; - fe.w = w; - fe.h = h; - - if (status) { - NSRect frame = [status frame]; - size.height += frame.size.height; - frame.size.width = size.width; - [status setFrame:frame]; - } - -#ifndef GNUSTEP - NSDisableScreenUpdates(); -#endif - [self setContentSize:size]; - [self setupContentView]; -#ifndef GNUSTEP - NSEnableScreenUpdates(); -#endif -} - -- (void)presetGame:(id)sender -{ - game_params *params = [sender getPayload]; - - midend_set_params(me, params); - midend_new_game(me); - - [self resizeForNewGameParams]; - [self updateTypeMenuTick]; -} - -- (void)startConfigureSheet:(int)which -{ - NSButton *ok, *cancel; - int actw, acth, leftw, rightw, totalw, h, thish, y; - int k; - NSRect rect, tmprect; - const int SPACING = 16; - char *title; - config_item *i; - int cfg_controlsize; - NSTextField *tf; - NSButton *b; - NSPopUpButton *pb; - - assert(sheet == NULL); - - /* - * Every control we create here is going to have this size - * until we tell it to calculate a better one. - */ - tmprect = NSMakeRect(0, 0, 100, 50); - - /* - * Set up OK and Cancel buttons. (Actually, MacOS doesn't seem - * to be fond of generic OK and Cancel wording, so I'm going to - * rename them to something nicer.) - */ - actw = acth = 0; - - cancel = [[NSButton alloc] initWithFrame:tmprect]; - [cancel setBezelStyle:NSRoundedBezelStyle]; - [cancel setTitle:@"Abandon"]; - [cancel setTarget:self]; - [cancel setKeyEquivalent:@"\033"]; - [cancel setAction:@selector(sheetCancelButton:)]; - [cancel sizeToFit]; - rect = [cancel frame]; - if (actw < rect.size.width) actw = rect.size.width; - if (acth < rect.size.height) acth = rect.size.height; - - ok = [[NSButton alloc] initWithFrame:tmprect]; - [ok setBezelStyle:NSRoundedBezelStyle]; - [ok setTitle:@"Accept"]; - [ok setTarget:self]; - [ok setKeyEquivalent:@"\r"]; - [ok setAction:@selector(sheetOKButton:)]; - [ok sizeToFit]; - rect = [ok frame]; - if (actw < rect.size.width) actw = rect.size.width; - if (acth < rect.size.height) acth = rect.size.height; - - totalw = SPACING + 2 * actw; - h = 2 * SPACING + acth; - - /* - * Now fetch the midend config data and go through it creating - * controls. - */ - cfg = midend_get_config(me, which, &title); - sfree(title); /* FIXME: should we use this somehow? */ - cfg_which = which; - - cfg_ncontrols = cfg_controlsize = 0; - cfg_controls = NULL; - leftw = rightw = 0; - for (i = cfg; i->type != C_END; i++) { - if (cfg_controlsize < cfg_ncontrols + 5) { - cfg_controlsize = cfg_ncontrols + 32; - cfg_controls = sresize(cfg_controls, cfg_controlsize, NSView *); - } - - thish = 0; - - switch (i->type) { - case C_STRING: - /* - * Two NSTextFields, one being a label and the other - * being an edit box. - */ - - tf = [[NSTextField alloc] initWithFrame:tmprect]; - [tf setEditable:NO]; - [tf setSelectable:NO]; - [tf setBordered:NO]; - [tf setDrawsBackground:NO]; - [[tf cell] setTitle:[NSString stringWithUTF8String:i->name]]; - [tf sizeToFit]; - rect = [tf frame]; - if (thish < rect.size.height + 1) thish = rect.size.height + 1; - if (leftw < rect.size.width + 1) leftw = rect.size.width + 1; - cfg_controls[cfg_ncontrols++] = tf; - - tf = [[NSTextField alloc] initWithFrame:tmprect]; - [tf setEditable:YES]; - [tf setSelectable:YES]; - [tf setBordered:YES]; - [[tf cell] setTitle:[NSString - stringWithUTF8String:i->u.string.sval]]; - [tf sizeToFit]; - rect = [tf frame]; - /* - * We impose a minimum and maximum width on editable - * NSTextFields. If we allow them to size themselves to - * the contents of the text within them, then they will - * look very silly if that text is only one or two - * characters, and equally silly if it's an absolutely - * enormous Rectangles or Pattern game ID! - */ - if (rect.size.width < 75) rect.size.width = 75; - if (rect.size.width > 400) rect.size.width = 400; - - if (thish < rect.size.height + 1) thish = rect.size.height + 1; - if (rightw < rect.size.width + 1) rightw = rect.size.width + 1; - cfg_controls[cfg_ncontrols++] = tf; - break; - - case C_BOOLEAN: - /* - * A checkbox is an NSButton with a type of - * NSSwitchButton. - */ - b = [[NSButton alloc] initWithFrame:tmprect]; - [b setBezelStyle:NSRoundedBezelStyle]; - [b setButtonType:NSSwitchButton]; - [b setTitle:[NSString stringWithUTF8String:i->name]]; - [b sizeToFit]; - [b setState:(i->u.boolean.bval ? NSOnState : NSOffState)]; - rect = [b frame]; - if (totalw < rect.size.width + 1) totalw = rect.size.width + 1; - if (thish < rect.size.height + 1) thish = rect.size.height + 1; - cfg_controls[cfg_ncontrols++] = b; - break; - - case C_CHOICES: - /* - * A pop-up menu control is an NSPopUpButton, which - * takes an embedded NSMenu. We also need an - * NSTextField to act as a label. - */ - - tf = [[NSTextField alloc] initWithFrame:tmprect]; - [tf setEditable:NO]; - [tf setSelectable:NO]; - [tf setBordered:NO]; - [tf setDrawsBackground:NO]; - [[tf cell] setTitle:[NSString stringWithUTF8String:i->name]]; - [tf sizeToFit]; - rect = [tf frame]; - if (thish < rect.size.height + 1) thish = rect.size.height + 1; - if (leftw < rect.size.width + 1) leftw = rect.size.width + 1; - cfg_controls[cfg_ncontrols++] = tf; - - pb = [[NSPopUpButton alloc] initWithFrame:tmprect pullsDown:NO]; - [pb setBezelStyle:NSRoundedBezelStyle]; - { - char c; - const char *p; - - p = i->u.choices.choicenames; - c = *p++; - while (*p) { - const char *q; - char *copy; - - q = p; - while (*p && *p != c) p++; - - copy = snewn((p-q) + 1, char); - memcpy(copy, q, p-q); - copy[p-q] = '\0'; - [pb addItemWithTitle:[NSString stringWithUTF8String:copy]]; - sfree(copy); - - if (*p) p++; - } - } - [pb selectItemAtIndex:i->u.choices.selected]; - [pb sizeToFit]; - - rect = [pb frame]; - if (rightw < rect.size.width + 1) rightw = rect.size.width + 1; - if (thish < rect.size.height + 1) thish = rect.size.height + 1; - cfg_controls[cfg_ncontrols++] = pb; - break; - } - - h += SPACING + thish; - } - - if (totalw < leftw + SPACING + rightw) - totalw = leftw + SPACING + rightw; - if (totalw > leftw + SPACING + rightw) { - int excess = totalw - (leftw + SPACING + rightw); - int leftexcess = leftw * excess / (leftw + rightw); - int rightexcess = excess - leftexcess; - leftw += leftexcess; - rightw += rightexcess; - } - - /* - * Now go through the list again, setting the final position - * for each control. - */ - k = 0; - y = h; - for (i = cfg; i->type != C_END; i++) { - y -= SPACING; - thish = 0; - switch (i->type) { - case C_STRING: - case C_CHOICES: - /* - * These two are treated identically, since both expect - * a control on the left and another on the right. - */ - rect = [cfg_controls[k] frame]; - if (thish < rect.size.height + 1) - thish = rect.size.height + 1; - rect = [cfg_controls[k+1] frame]; - if (thish < rect.size.height + 1) - thish = rect.size.height + 1; - rect = [cfg_controls[k] frame]; - rect.origin.y = y - thish/2 - rect.size.height/2; - rect.origin.x = SPACING; - rect.size.width = leftw; - [cfg_controls[k] setFrame:rect]; - rect = [cfg_controls[k+1] frame]; - rect.origin.y = y - thish/2 - rect.size.height/2; - rect.origin.x = 2 * SPACING + leftw; - rect.size.width = rightw; - [cfg_controls[k+1] setFrame:rect]; - k += 2; - break; - - case C_BOOLEAN: - rect = [cfg_controls[k] frame]; - if (thish < rect.size.height + 1) - thish = rect.size.height + 1; - rect.origin.y = y - thish/2 - rect.size.height/2; - rect.origin.x = SPACING; - rect.size.width = totalw; - [cfg_controls[k] setFrame:rect]; - k++; - break; - } - y -= thish; - } - - assert(k == cfg_ncontrols); - - [cancel setFrame:NSMakeRect(SPACING+totalw/4-actw/2, SPACING, actw, acth)]; - [ok setFrame:NSMakeRect(SPACING+3*totalw/4-actw/2, SPACING, actw, acth)]; - - sheet = [[NSWindow alloc] - initWithContentRect:NSMakeRect(0,0,totalw + 2*SPACING,h) - styleMask:NSTitledWindowMask | NSClosableWindowMask - backing:NSBackingStoreBuffered - defer:YES]; - - [[sheet contentView] addSubview:cancel]; - [[sheet contentView] addSubview:ok]; - - for (k = 0; k < cfg_ncontrols; k++) - [[sheet contentView] addSubview:cfg_controls[k]]; - - [app beginSheet:sheet modalForWindow:self - modalDelegate:nil didEndSelector:NULL contextInfo:nil]; -} - -- (void)specificGame:(id)sender -{ - [self startConfigureSheet:CFG_DESC]; -} - -- (void)specificRandomGame:(id)sender -{ - [self startConfigureSheet:CFG_SEED]; -} - -- (void)customGameType:(id)sender -{ - [self startConfigureSheet:CFG_SETTINGS]; -} - -- (void)sheetEndWithStatus:(bool)update -{ - assert(sheet != NULL); - [app endSheet:sheet]; - [sheet orderOut:self]; - sheet = NULL; - if (update) { - int k; - config_item *i; - const char *error; - - k = 0; - for (i = cfg; i->type != C_END; i++) { - switch (i->type) { - case C_STRING: - sfree(i->u.string.sval); - i->u.string.sval = dupstr([[[(id)cfg_controls[k+1] cell] - title] UTF8String]); - k += 2; - break; - case C_BOOLEAN: - i->u.boolean.bval = [(id)cfg_controls[k] state] == NSOnState; - k++; - break; - case C_CHOICES: - i->u.choices.selected = - [(id)cfg_controls[k+1] indexOfSelectedItem]; - k += 2; - break; - } - } - - error = midend_set_config(me, cfg_which, cfg); - if (error) { - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert addButtonWithTitle:@"Bah"]; - [alert setInformativeText:[NSString stringWithUTF8String:error]]; - [alert beginSheetModalForWindow:self modalDelegate:nil - didEndSelector:NULL contextInfo:nil]; - } else { - midend_new_game(me); - [self resizeForNewGameParams]; - [self updateTypeMenuTick]; - } - } - sfree(cfg_controls); - cfg_controls = NULL; -} -- (void)sheetOKButton:(id)sender -{ - [self sheetEndWithStatus:true]; -} -- (void)sheetCancelButton:(id)sender -{ - [self sheetEndWithStatus:false]; -} - -- (void)setStatusLine:(const char *)text -{ - [[status cell] setTitle:[NSString stringWithUTF8String:text]]; -} - -@end - -/* - * Drawing routines called by the midend. - */ -static void osx_draw_polygon(void *handle, int *coords, int npoints, - int fillcolour, int outlinecolour) -{ - frontend *fe = (frontend *)handle; - NSBezierPath *path = [NSBezierPath bezierPath]; - int i; - - [[NSGraphicsContext currentContext] setShouldAntialias:YES]; - - for (i = 0; i < npoints; i++) { - NSPoint p = { coords[i*2] + 0.5, fe->h - coords[i*2+1] - 0.5 }; - if (i == 0) - [path moveToPoint:p]; - else - [path lineToPoint:p]; - } - - [path closePath]; - - if (fillcolour >= 0) { - assert(fillcolour >= 0 && fillcolour < fe->ncolours); - [fe->colours[fillcolour] set]; - [path fill]; - } - - assert(outlinecolour >= 0 && outlinecolour < fe->ncolours); - [fe->colours[outlinecolour] set]; - [path stroke]; -} -static void osx_draw_circle(void *handle, int cx, int cy, int radius, - int fillcolour, int outlinecolour) -{ - frontend *fe = (frontend *)handle; - NSBezierPath *path = [NSBezierPath bezierPath]; - - [[NSGraphicsContext currentContext] setShouldAntialias:YES]; - - [path appendBezierPathWithArcWithCenter:NSMakePoint(cx+0.5, fe->h-cy-0.5) - radius:radius startAngle:0.0 endAngle:360.0]; - - [path closePath]; - - if (fillcolour >= 0) { - assert(fillcolour >= 0 && fillcolour < fe->ncolours); - [fe->colours[fillcolour] set]; - [path fill]; - } - - assert(outlinecolour >= 0 && outlinecolour < fe->ncolours); - [fe->colours[outlinecolour] set]; - [path stroke]; -} -static void osx_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) -{ - frontend *fe = (frontend *)handle; - NSBezierPath *path = [NSBezierPath bezierPath]; - NSPoint p1 = { x1 + 0.5, fe->h - y1 - 0.5 }; - NSPoint p2 = { x2 + 0.5, fe->h - y2 - 0.5 }; - - [[NSGraphicsContext currentContext] setShouldAntialias:NO]; - - assert(colour >= 0 && colour < fe->ncolours); - [fe->colours[colour] set]; - - [path moveToPoint:p1]; - [path lineToPoint:p2]; - [path stroke]; - NSRectFill(NSMakeRect(x1, fe->h-y1-1, 1, 1)); - NSRectFill(NSMakeRect(x2, fe->h-y2-1, 1, 1)); -} - -static void osx_draw_thick_line( - void *handle, float thickness, - float x1, float y1, - float x2, float y2, - int colour) -{ - frontend *fe = (frontend *)handle; - NSBezierPath *path = [NSBezierPath bezierPath]; - - assert(colour >= 0 && colour < fe->ncolours); - [fe->colours[colour] set]; - [[NSGraphicsContext currentContext] setShouldAntialias: YES]; - [path setLineWidth: thickness]; - [path setLineCapStyle: NSButtLineCapStyle]; - [path moveToPoint: NSMakePoint(x1, fe->h-y1)]; - [path lineToPoint: NSMakePoint(x2, fe->h-y2)]; - [path stroke]; -} - -static void osx_draw_rect(void *handle, int x, int y, int w, int h, int colour) -{ - frontend *fe = (frontend *)handle; - NSRect r = { {x, fe->h - y - h}, {w,h} }; - - [[NSGraphicsContext currentContext] setShouldAntialias:NO]; - - assert(colour >= 0 && colour < fe->ncolours); - [fe->colours[colour] set]; - - NSRectFill(r); -} -static void osx_draw_text(void *handle, int x, int y, int fonttype, - int fontsize, int align, int colour, - const char *text) -{ - frontend *fe = (frontend *)handle; - NSString *string = [NSString stringWithUTF8String:text]; - NSDictionary *attr; - NSFont *font; - NSSize size; - NSPoint point; - - [[NSGraphicsContext currentContext] setShouldAntialias:YES]; - - assert(colour >= 0 && colour < fe->ncolours); - - if (fonttype == FONT_FIXED) - font = [NSFont userFixedPitchFontOfSize:fontsize]; - else - font = [NSFont userFontOfSize:fontsize]; - - attr = [NSDictionary dictionaryWithObjectsAndKeys: - fe->colours[colour], NSForegroundColorAttributeName, - font, NSFontAttributeName, nil]; - - point.x = x; - point.y = fe->h - y; - - size = [string sizeWithAttributes:attr]; - if (align & ALIGN_HRIGHT) - point.x -= size.width; - else if (align & ALIGN_HCENTRE) - point.x -= size.width / 2; - if (align & ALIGN_VCENTRE) - point.y -= size.height / 2; - - [string drawAtPoint:point withAttributes:attr]; -} -static char *osx_text_fallback(void *handle, const char *const *strings, - int nstrings) -{ - /* - * We assume OS X can cope with any UTF-8 likely to be emitted - * by a puzzle. - */ - return dupstr(strings[0]); -} -struct blitter { - int w, h; - int x, y; - NSImage *img; -}; -static blitter *osx_blitter_new(void *handle, int w, int h) -{ - blitter *bl = snew(blitter); - bl->x = bl->y = -1; - bl->w = w; - bl->h = h; - bl->img = [[NSImage alloc] initWithSize:NSMakeSize(w, h)]; - return bl; -} -static void osx_blitter_free(void *handle, blitter *bl) -{ - [bl->img release]; - sfree(bl); -} -static void osx_blitter_save(void *handle, blitter *bl, int x, int y) -{ - frontend *fe = (frontend *)handle; - int sx, sy, sX, sY, dx, dy, dX, dY; - [fe->image unlockFocus]; - [bl->img lockFocus]; - - /* - * Find the intersection of the source and destination rectangles, - * so as to avoid trying to copy from outside the source image, - * which GNUstep dislikes. - * - * Lower-case x,y coordinates are bottom left box corners; - * upper-case X,Y are the top right. - */ - sx = x; sy = fe->h - y - bl->h; - sX = sx + bl->w; sY = sy + bl->h; - dx = dy = 0; - dX = bl->w; dY = bl->h; - if (sx < 0) { - dx += -sx; - sx = 0; - } - if (sy < 0) { - dy += -sy; - sy = 0; - } - if (sX > fe->w) { - dX -= (sX - fe->w); - sX = fe->w; - } - if (sY > fe->h) { - dY -= (sY - fe->h); - sY = fe->h; - } - - [fe->image drawInRect:NSMakeRect(dx, dy, dX-dx, dY-dy) - fromRect:NSMakeRect(sx, sy, sX-sx, sY-sy) - operation:NSCompositeCopy fraction:1.0]; - [bl->img unlockFocus]; - [fe->image lockFocus]; - bl->x = x; - bl->y = y; -} -static void osx_blitter_load(void *handle, blitter *bl, int x, int y) -{ - frontend *fe = (frontend *)handle; - if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) { - x = bl->x; - y = bl->y; - } - [bl->img drawInRect:NSMakeRect(x, fe->h - y - bl->h, bl->w, bl->h) - fromRect:NSMakeRect(0, 0, bl->w, bl->h) - operation:NSCompositeCopy fraction:1.0]; -} -static void osx_draw_update(void *handle, int x, int y, int w, int h) -{ - frontend *fe = (frontend *)handle; - [fe->view setNeedsDisplayInRect:NSMakeRect(x, fe->h - y - h, w, h)]; -} -static void osx_clip(void *handle, int x, int y, int w, int h) -{ - frontend *fe = (frontend *)handle; - NSRect r = { {x, fe->h - y - h}, {w, h} }; - - if (!fe->clipped) - [[NSGraphicsContext currentContext] saveGraphicsState]; - [NSBezierPath clipRect:r]; - fe->clipped = true; -} -static void osx_unclip(void *handle) -{ - frontend *fe = (frontend *)handle; - if (fe->clipped) - [[NSGraphicsContext currentContext] restoreGraphicsState]; - fe->clipped = false; -} -static void osx_start_draw(void *handle) -{ - frontend *fe = (frontend *)handle; - [fe->image lockFocus]; - fe->clipped = false; -} -static void osx_end_draw(void *handle) -{ - frontend *fe = (frontend *)handle; - [fe->image unlockFocus]; -} -static void osx_status_bar(void *handle, const char *text) -{ - frontend *fe = (frontend *)handle; - [fe->window setStatusLine:text]; -} - -const struct drawing_api osx_drawing = { - osx_draw_text, - osx_draw_rect, - osx_draw_line, - osx_draw_polygon, - osx_draw_circle, - osx_draw_update, - osx_clip, - osx_unclip, - osx_start_draw, - osx_end_draw, - osx_status_bar, - osx_blitter_new, - osx_blitter_free, - osx_blitter_save, - osx_blitter_load, - NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */ - NULL, NULL, /* line_width, line_dotted */ - osx_text_fallback, - osx_draw_thick_line, -}; - -void deactivate_timer(frontend *fe) -{ - [fe->window deactivateTimer]; -} -void activate_timer(frontend *fe) -{ - [fe->window activateTimer]; -} - -/* ---------------------------------------------------------------------- - * AppController: the object which receives the messages from all - * menu selections that aren't standard OS X functions. - */ -@interface AppController : NSObject -{ -} -- (void)newGameWindow:(id)sender; -- (void)about:(id)sender; -@end - -@implementation AppController - -- (void)newGameWindow:(id)sender -{ - const game *g = [sender getPayload]; - id win; - - win = [[GameWindow alloc] initWithGame:g]; - [win makeKeyAndOrderFront:self]; -} - -- (void)about:(id)sender -{ - id win; - - win = [[AboutBox alloc] init]; - [win makeKeyAndOrderFront:self]; -} - -- (NSMenu *)applicationDockMenu:(NSApplication *)sender -{ - NSMenu *menu = newmenu("Dock Menu"); - { - int i; - - for (i = 0; i < gamecount; i++) { - id item = - initnewitem([DataMenuItem allocWithZone:[NSMenu menuZone]], - menu, gamelist[i]->name, "", self, - @selector(newGameWindow:)); - [item setPayload:(void *)gamelist[i]]; - } - } - return menu; -} - -@end - -/* ---------------------------------------------------------------------- - * Main program. Constructs the menus and runs the application. - */ -int main(int argc, char **argv) -{ - NSAutoreleasePool *pool; - NSMenu *menu; - AppController *controller; - NSImage *icon; - - pool = [[NSAutoreleasePool alloc] init]; - - icon = [NSImage imageNamed:@"NSApplicationIcon"]; - app = [NSApplication sharedApplication]; - [app setApplicationIconImage:icon]; - - controller = [[[AppController alloc] init] autorelease]; - [app setDelegate:controller]; - - [app setMainMenu: newmenu("Main Menu")]; - - menu = newsubmenu([app mainMenu], "Apple Menu"); - newitem(menu, "About Puzzles", "", NULL, @selector(about:)); - [menu addItem:[NSMenuItem separatorItem]]; - [app setServicesMenu:newsubmenu(menu, "Services")]; - [menu addItem:[NSMenuItem separatorItem]]; - newitem(menu, "Hide Puzzles", "h", app, @selector(hide:)); - newitem(menu, "Hide Others", "o-h", app, @selector(hideOtherApplications:)); - newitem(menu, "Show All", "", app, @selector(unhideAllApplications:)); - [menu addItem:[NSMenuItem separatorItem]]; - newitem(menu, "Quit", "q", app, @selector(terminate:)); - [app setAppleMenu: menu]; - - menu = newsubmenu([app mainMenu], "File"); - newitem(menu, "Open", "o", NULL, @selector(loadSavedGame:)); - newitem(menu, "Save As", "s", NULL, @selector(saveGame:)); - newitem(menu, "New Game", "n", NULL, @selector(newGame:)); - newitem(menu, "Restart Game", "r", NULL, @selector(restartGame:)); - newitem(menu, "Specific Game", "", NULL, @selector(specificGame:)); - newitem(menu, "Specific Random Seed", "", NULL, - @selector(specificRandomGame:)); - [menu addItem:[NSMenuItem separatorItem]]; - { - NSMenu *submenu = newsubmenu(menu, "New Window"); - int i; - - for (i = 0; i < gamecount; i++) { - id item = - initnewitem([DataMenuItem allocWithZone:[NSMenu menuZone]], - submenu, gamelist[i]->name, "", controller, - @selector(newGameWindow:)); - [item setPayload:(void *)gamelist[i]]; - } - } - [menu addItem:[NSMenuItem separatorItem]]; - newitem(menu, "Close", "w", NULL, @selector(performClose:)); - - menu = newsubmenu([app mainMenu], "Edit"); - newitem(menu, "Undo", "z", NULL, @selector(undoMove:)); - newitem(menu, "Redo", "S-z", NULL, @selector(redoMove:)); - [menu addItem:[NSMenuItem separatorItem]]; - newitem(menu, "Cut", "x", NULL, @selector(cut:)); - newitem(menu, "Copy", "c", NULL, @selector(copy:)); - newitem(menu, "Paste", "v", NULL, @selector(paste:)); - [menu addItem:[NSMenuItem separatorItem]]; - newitem(menu, "Solve", "S-s", NULL, @selector(solveGame:)); - - menu = newsubmenu([app mainMenu], "Type"); - typemenu = menu; - newitem(menu, "Custom", "", NULL, @selector(customGameType:)); - - menu = newsubmenu([app mainMenu], "Window"); - [app setWindowsMenu: menu]; - newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:)); - - menu = newsubmenu([app mainMenu], "Help"); - newitem(menu, "Puzzles Help", "?", app, @selector(showHelp:)); - - [app run]; - [pool release]; - - return 0; -} diff --git a/apps/plugins/puzzles/src/padtoolbar.bmp b/apps/plugins/puzzles/src/padtoolbar.bmp deleted file mode 100644 index 46c3e0dcae..0000000000 Binary files a/apps/plugins/puzzles/src/padtoolbar.bmp and /dev/null differ diff --git a/apps/plugins/puzzles/src/puzzles.h b/apps/plugins/puzzles/src/puzzles.h index 5560aabe03..29b00ddaf0 100644 --- a/apps/plugins/puzzles/src/puzzles.h +++ b/apps/plugins/puzzles/src/puzzles.h @@ -347,7 +347,7 @@ const char *identify_game(char **name, bool (*read)(void *ctx, void *buf, int len), void *rctx); void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx); -void midend_get_cursor_location(midend *me, int *x, int *y, int *w, int *h); +bool midend_get_cursor_location(midend *me, int *x, int *y, int *w, int *h); /* Printing functions supplied by the mid-end */ const char *midend_print_puzzle(midend *me, document *doc, bool with_soln); diff --git a/apps/plugins/puzzles/src/puzzles.rc2 b/apps/plugins/puzzles/src/puzzles.rc2 deleted file mode 100644 index 4442a9b138..0000000000 --- a/apps/plugins/puzzles/src/puzzles.rc2 +++ /dev/null @@ -1,65 +0,0 @@ -/* Standard stuff that goes into the Windows resources for all puzzles. */ - -#ifdef _WIN32_WCE - -#include -#include - -#define SHMENUBAR RCDATA -#define I_IMAGENONE (-2) -#define IDC_STATIC (-1) - -#include "resource.h" - -IDR_MENUBAR1 MENU DISCARDABLE -BEGIN - POPUP "Game" - BEGIN - MENUITEM "Dummy", 0 - END - POPUP "Type" - BEGIN - MENUITEM "Dummy", 0 - END -END - -IDR_MENUBAR1 SHMENUBAR DISCARDABLE -BEGIN - IDR_MENUBAR1, 2, - I_IMAGENONE, ID_GAME, TBSTATE_ENABLED, - TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_GAME, 0, 0, - I_IMAGENONE, ID_TYPE, TBSTATE_ENABLED, - TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_TYPE, 0, 1, -END - -STRINGTABLE DISCARDABLE -BEGIN - IDS_CAP_GAME "Game" - IDS_CAP_TYPE "Type" -END - -IDD_ABOUT DIALOG DISCARDABLE 0, 0, 0, 0 -STYLE WS_POPUP -FONT 8, "Tahoma" -BEGIN - LTEXT "", IDC_ABOUT_CAPTION, 4, 4, 150, 8 - LTEXT "", IDC_ABOUT_LINE, 4, 16, 150, 1, WS_BORDER - LTEXT "", IDC_ABOUT_GAME, 4, 22, 150, 8 - LTEXT "from Simon Tatham's Portable Puzzle Collection", - IDC_STATIC, 4, 36, 150, 8, SS_LEFTNOWORDWRAP - LTEXT "Pocket PC port by Darek Olszewski", - IDC_STATIC, 4, 46, 150, 8 - LTEXT "", IDC_ABOUT_VERSION, 4, 60, 150, 8 -END - -IDD_CONFIG DIALOG DISCARDABLE 0, 0, 0, 0 -STYLE WS_POPUP -FONT 8, "Tahoma" -BEGIN - LTEXT "", IDC_CONFIG_CAPTION, 4, 4, 150, 8 - LTEXT "", IDC_CONFIG_LINE, 4, 16, 150, 1, WS_BORDER -END - -IDR_PADTOOLBAR BITMAP DISCARDABLE "padtoolbar.bmp" - -#endif /* _WIN32_WCE */ diff --git a/apps/plugins/puzzles/src/unfinished/README b/apps/plugins/puzzles/src/unfinished/README deleted file mode 100644 index 0f8bb41d7c..0000000000 --- a/apps/plugins/puzzles/src/unfinished/README +++ /dev/null @@ -1,9 +0,0 @@ -This subdirectory contains puzzle implementations which are -half-written, fundamentally flawed, or in other ways unready to be -shipped as part of the polished Puzzles collection. - -Those puzzles which have .R files can be built as part of the -Puzzles collection by symlinking their source files into the parent -directory and re-running mkfiles.pl. Anything without a .R file -isn't even finished enough to do that, and you should read the -source file itself to find out the status. diff --git a/apps/plugins/puzzles/src/unfinished/group.R b/apps/plugins/puzzles/src/unfinished/group.R deleted file mode 100644 index 394e364a06..0000000000 --- a/apps/plugins/puzzles/src/unfinished/group.R +++ /dev/null @@ -1,25 +0,0 @@ -# -*- makefile -*- - -GROUP_EXTRA = LATIN -GROUP_EXTRA_SOLVER = LATIN_SOLVER - -group : [X] GTK COMMON group GROUP_EXTRA group-icon|no-icon - -group : [G] WINDOWS COMMON group GROUP_EXTRA group.res|noicon.res - -groupsolver : [U] group[STANDALONE_SOLVER] GROUP_EXTRA_SOLVER STANDALONE -groupsolver : [C] group[STANDALONE_SOLVER] GROUP_EXTRA_SOLVER STANDALONE - -ALL += group[COMBINED] GROUP_EXTRA - -!begin am gtk -GAMES += group -!end - -!begin >list.c - A(group) \ -!end - -!begin >gamedesc.txt -group:group.exe:Group:Group theory puzzle:Complete the unfinished Cayley table of a group. -!end diff --git a/apps/plugins/puzzles/src/unfinished/group.c b/apps/plugins/puzzles/src/unfinished/group.c deleted file mode 100644 index 8e0185741e..0000000000 --- a/apps/plugins/puzzles/src/unfinished/group.c +++ /dev/null @@ -1,2439 +0,0 @@ -/* - * group.c: a Latin-square puzzle, but played with groups' Cayley - * tables. That is, you are given a Cayley table of a group with - * most elements blank and a few clues, and you must fill it in - * so as to preserve the group axioms. - * - * This is a perfectly playable and fully working puzzle, but I'm - * leaving it for the moment in the 'unfinished' directory because - * it's just too esoteric (not to mention _hard_) for me to be - * comfortable presenting it to the general public as something they - * might (implicitly) actually want to play. - * - * TODO: - * - * - more solver techniques? - * * Inverses: once we know that gh = e, we can immediately - * deduce hg = e as well; then for any gx=y we can deduce - * hy=x, and for any xg=y we have yh=x. - * * Hard-mode associativity: we currently deduce based on - * definite numbers in the grid, but we could also winnow - * based on _possible_ numbers. - * * My overambitious original thoughts included wondering if we - * could infer that there must be elements of certain orders - * (e.g. a group of order divisible by 5 must contain an - * element of order 5), but I think in fact this is probably - * silly. - */ - -#include -#include -#include -#include -#include -#include - -#include "puzzles.h" -#include "latin.h" - -/* - * Difficulty levels. I do some macro ickery here to ensure that my - * enum and the various forms of my name list always match up. - */ -#define DIFFLIST(A) \ - A(TRIVIAL,Trivial,NULL,t) \ - A(NORMAL,Normal,solver_normal,n) \ - A(HARD,Hard,solver_hard,h) \ - A(EXTREME,Extreme,NULL,x) \ - A(UNREASONABLE,Unreasonable,NULL,u) -#define ENUM(upper,title,func,lower) DIFF_ ## upper, -#define TITLE(upper,title,func,lower) #title, -#define ENCODE(upper,title,func,lower) #lower -#define CONFIG(upper,title,func,lower) ":" #title -enum { DIFFLIST(ENUM) DIFFCOUNT }; -static char const *const group_diffnames[] = { DIFFLIST(TITLE) }; -static char const group_diffchars[] = DIFFLIST(ENCODE); -#define DIFFCONFIG DIFFLIST(CONFIG) - -enum { - COL_BACKGROUND, - COL_GRID, - COL_USER, - COL_HIGHLIGHT, - COL_ERROR, - COL_PENCIL, - COL_DIAGONAL, - NCOLOURS -}; - -/* - * In identity mode, we number the elements e,a,b,c,d,f,g,h,... - * Otherwise, they're a,b,c,d,e,f,g,h,... in the obvious way. - */ -#define E_TO_FRONT(c,id) ( (id) && (c)<=5 ? (c) % 5 + 1 : (c) ) -#define E_FROM_FRONT(c,id) ( (id) && (c)<=5 ? ((c) + 3) % 5 + 1 : (c) ) - -#define FROMCHAR(c,id) E_TO_FRONT((((c)-('A'-1)) & ~0x20), id) -#define ISCHAR(c) (((c)>='A'&&(c)<='Z') || ((c)>='a'&&(c)<='z')) -#define TOCHAR(c,id) (E_FROM_FRONT(c,id) + ('a'-1)) - -struct game_params { - int w, diff; - bool id; -}; - -typedef struct group_common { - int refcount; - bool *immutable; -} group_common; - -struct game_state { - game_params par; - digit *grid; - int *pencil; /* bitmaps using bits 1<<1..1<w = 6; - ret->diff = DIFF_NORMAL; - ret->id = true; - - return ret; -} - -const static struct game_params group_presets[] = { - { 6, DIFF_NORMAL, true }, - { 6, DIFF_NORMAL, false }, - { 8, DIFF_NORMAL, true }, - { 8, DIFF_NORMAL, false }, - { 8, DIFF_HARD, true }, - { 8, DIFF_HARD, false }, - { 12, DIFF_NORMAL, true }, -}; - -static bool game_fetch_preset(int i, char **name, game_params **params) -{ - game_params *ret; - char buf[80]; - - if (i < 0 || i >= lenof(group_presets)) - return false; - - ret = snew(game_params); - *ret = group_presets[i]; /* structure copy */ - - sprintf(buf, "%dx%d %s%s", ret->w, ret->w, group_diffnames[ret->diff], - ret->id ? "" : ", identity hidden"); - - *name = dupstr(buf); - *params = ret; - return true; -} - -static void free_params(game_params *params) -{ - sfree(params); -} - -static game_params *dup_params(const game_params *params) -{ - game_params *ret = snew(game_params); - *ret = *params; /* structure copy */ - return ret; -} - -static void decode_params(game_params *params, char const *string) -{ - char const *p = string; - - params->w = atoi(p); - while (*p && isdigit((unsigned char)*p)) p++; - params->diff = DIFF_NORMAL; - params->id = true; - - while (*p) { - if (*p == 'd') { - int i; - p++; - params->diff = DIFFCOUNT+1; /* ...which is invalid */ - if (*p) { - for (i = 0; i < DIFFCOUNT; i++) { - if (*p == group_diffchars[i]) - params->diff = i; - } - p++; - } - } else if (*p == 'i') { - params->id = false; - p++; - } else { - /* unrecognised character */ - p++; - } - } -} - -static char *encode_params(const game_params *params, bool full) -{ - char ret[80]; - - sprintf(ret, "%d", params->w); - if (full) - sprintf(ret + strlen(ret), "d%c", group_diffchars[params->diff]); - if (!params->id) - sprintf(ret + strlen(ret), "i"); - - return dupstr(ret); -} - -static config_item *game_configure(const game_params *params) -{ - config_item *ret; - char buf[80]; - - ret = snewn(4, config_item); - - ret[0].name = "Grid size"; - ret[0].type = C_STRING; - sprintf(buf, "%d", params->w); - ret[0].u.string.sval = dupstr(buf); - - ret[1].name = "Difficulty"; - ret[1].type = C_CHOICES; - ret[1].u.choices.choicenames = DIFFCONFIG; - ret[1].u.choices.selected = params->diff; - - ret[2].name = "Show identity"; - ret[2].type = C_BOOLEAN; - ret[2].u.boolean.bval = params->id; - - ret[3].name = NULL; - ret[3].type = C_END; - - return ret; -} - -static game_params *custom_params(const config_item *cfg) -{ - game_params *ret = snew(game_params); - - ret->w = atoi(cfg[0].u.string.sval); - ret->diff = cfg[1].u.choices.selected; - ret->id = cfg[2].u.boolean.bval; - - return ret; -} - -static const char *validate_params(const game_params *params, bool full) -{ - if (params->w < 3 || params->w > 26) - return "Grid size must be between 3 and 26"; - if (params->diff >= DIFFCOUNT) - return "Unknown difficulty rating"; - if (!params->id && params->diff == DIFF_TRIVIAL) { - /* - * We can't have a Trivial-difficulty puzzle (i.e. latin - * square deductions only) without a clear identity, because - * identityless puzzles always have two rows and two columns - * entirely blank, and no latin-square deduction permits the - * distinguishing of two such rows. - */ - return "Trivial puzzles must have an identity"; - } - if (!params->id && params->w == 3) { - /* - * We can't have a 3x3 puzzle without an identity either, - * because 3x3 puzzles can't ever be harder than Trivial - * (there are no 3x3 latin squares which aren't also valid - * group tables, so enabling group-based deductions doesn't - * rule out any possible solutions) and - as above - Trivial - * puzzles can't not have an identity. - */ - return "3x3 puzzles must have an identity"; - } - return NULL; -} - -/* ---------------------------------------------------------------------- - * Solver. - */ - -static int find_identity(struct latin_solver *solver) -{ - int w = solver->o; - digit *grid = solver->grid; - int i, j; - - for (i = 0; i < w; i++) - for (j = 0; j < w; j++) { - if (grid[i*w+j] == i+1) - return j+1; - if (grid[i*w+j] == j+1) - return i+1; - } - - return 0; -} - -static int solver_normal(struct latin_solver *solver, void *vctx) -{ - int w = solver->o; -#ifdef STANDALONE_SOLVER - char **names = solver->names; -#endif - digit *grid = solver->grid; - int i, j, k; - - /* - * Deduce using associativity: (ab)c = a(bc). - * - * So we pick any a,b,c we like; then if we know ab, bc, and - * (ab)c we can fill in a(bc). - */ - for (i = 0; i < w; i++) - for (j = 0; j < w; j++) - for (k = 0; k < w; k++) { - if (!grid[i*w+j] || !grid[j*w+k]) - continue; - if (grid[(grid[i*w+j]-1)*w+k] && - !grid[i*w+(grid[j*w+k]-1)]) { - int x = grid[j*w+k]-1, y = i; - int n = grid[(grid[i*w+j]-1)*w+k]; -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n", - solver_recurse_depth*4, "", - names[i], names[j], names[k], - names[grid[i*w+j]-1], names[k], - names[i], names[grid[j*w+k]-1]); - printf("%*s placing %s at (%d,%d)\n", - solver_recurse_depth*4, "", - names[n-1], x+1, y+1); - } -#endif - if (solver->cube[(x*w+y)*w+n-1]) { - latin_solver_place(solver, x, y, n); - return 1; - } else { -#ifdef STANDALONE_SOLVER - if (solver_show_working) - printf("%*s contradiction!\n", - solver_recurse_depth*4, ""); - return -1; -#endif - } - } - if (!grid[(grid[i*w+j]-1)*w+k] && - grid[i*w+(grid[j*w+k]-1)]) { - int x = k, y = grid[i*w+j]-1; - int n = grid[i*w+(grid[j*w+k]-1)]; -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*sassociativity on %s,%s,%s: %s*%s = %s*%s\n", - solver_recurse_depth*4, "", - names[i], names[j], names[k], - names[grid[i*w+j]-1], names[k], - names[i], names[grid[j*w+k]-1]); - printf("%*s placing %s at (%d,%d)\n", - solver_recurse_depth*4, "", - names[n-1], x+1, y+1); - } -#endif - if (solver->cube[(x*w+y)*w+n-1]) { - latin_solver_place(solver, x, y, n); - return 1; - } else { -#ifdef STANDALONE_SOLVER - if (solver_show_working) - printf("%*s contradiction!\n", - solver_recurse_depth*4, ""); - return -1; -#endif - } - } - } - - /* - * Fill in the row and column for the group identity, if it's not - * already known and if we've just found out what it is. - */ - i = find_identity(solver); - if (i) { - bool done_something = false; - for (j = 1; j <= w; j++) { - if (!grid[(i-1)*w+(j-1)] || !grid[(j-1)*w+(i-1)]) { - done_something = true; - } - } - if (done_something) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*s%s is the group identity\n", - solver_recurse_depth*4, "", names[i-1]); - } -#endif - for (j = 1; j <= w; j++) { - if (!grid[(j-1)*w+(i-1)]) { - if (!cube(i-1, j-1, j)) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*s but %s cannot go at (%d,%d) - " - "contradiction!\n", - solver_recurse_depth*4, "", - names[j-1], i, j); - } -#endif - return -1; - } -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*s placing %s at (%d,%d)\n", - solver_recurse_depth*4, "", - names[j-1], i, j); - } -#endif - latin_solver_place(solver, i-1, j-1, j); - } - if (!grid[(i-1)*w+(j-1)]) { - if (!cube(j-1, i-1, j)) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*s but %s cannot go at (%d,%d) - " - "contradiction!\n", - solver_recurse_depth*4, "", - names[j-1], j, i); - } -#endif - return -1; - } -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*s placing %s at (%d,%d)\n", - solver_recurse_depth*4, "", - names[j-1], j, i); - } -#endif - latin_solver_place(solver, j-1, i-1, j); - } - } - return 1; - } - } - - return 0; -} - -static int solver_hard(struct latin_solver *solver, void *vctx) -{ - bool done_something = false; - int w = solver->o; -#ifdef STANDALONE_SOLVER - char **names = solver->names; -#endif - int i, j; - - /* - * In identity-hidden mode, systematically rule out possibilities - * for the group identity. - * - * In solver_normal, we used the fact that any filled square in - * the grid whose contents _does_ match one of the elements it's - * the product of - that is, ab=a or ab=b - tells you immediately - * that the other element is the identity. - * - * Here, we use the flip side of that: any filled square in the - * grid whose contents does _not_ match either its row or column - - * that is, if ab is neither a nor b - tells you immediately that - * _neither_ of those elements is the identity. And if that's - * true, then we can also immediately rule out the possibility - * that it acts as the identity on any element at all. - */ - for (i = 0; i < w; i++) { - bool i_can_be_id = true; -#ifdef STANDALONE_SOLVER - char title[80]; -#endif - - for (j = 0; j < w; j++) { - if (grid(i,j) && grid(i,j) != j+1) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) - sprintf(title, "%s cannot be the identity: " - "%s%s = %s =/= %s", names[i], names[i], names[j], - names[grid(i,j)-1], names[j]); -#endif - i_can_be_id = false; - break; - } - if (grid(j,i) && grid(j,i) != j+1) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) - sprintf(title, "%s cannot be the identity: " - "%s%s = %s =/= %s", names[i], names[j], names[i], - names[grid(j,i)-1], names[j]); -#endif - i_can_be_id = false; - break; - } - } - - if (!i_can_be_id) { - /* Now rule out ij=j or ji=j for all j. */ - for (j = 0; j < w; j++) { - if (cube(i, j, j+1)) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - if (title[0]) { - printf("%*s%s\n", solver_recurse_depth*4, "", - title); - title[0] = '\0'; - } - printf("%*s ruling out %s at (%d,%d)\n", - solver_recurse_depth*4, "", names[j], i, j); - } -#endif - cube(i, j, j+1) = false; - } - if (cube(j, i, j+1)) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - if (title[0]) { - printf("%*s%s\n", solver_recurse_depth*4, "", - title); - title[0] = '\0'; - } - printf("%*s ruling out %s at (%d,%d)\n", - solver_recurse_depth*4, "", names[j], j, i); - } -#endif - cube(j, i, j+1) = false; - } - } - } - } - - return done_something; -} - -#define SOLVER(upper,title,func,lower) func, -static usersolver_t const group_solvers[] = { DIFFLIST(SOLVER) }; - -static bool group_valid(struct latin_solver *solver, void *ctx) -{ - int w = solver->o; -#ifdef STANDALONE_SOLVER - char **names = solver->names; -#endif - int i, j, k; - - for (i = 0; i < w; i++) - for (j = 0; j < w; j++) - for (k = 0; k < w; k++) { - int ij = grid(i, j) - 1; - int jk = grid(j, k) - 1; - int ij_k = grid(ij, k) - 1; - int i_jk = grid(i, jk) - 1; - if (ij_k != i_jk) { -#ifdef STANDALONE_SOLVER - if (solver_show_working) { - printf("%*sfailure of associativity: " - "(%s%s)%s = %s%s = %s but " - "%s(%s%s) = %s%s = %s\n", - solver_recurse_depth*4, "", - names[i], names[j], names[k], - names[ij], names[k], names[ij_k], - names[i], names[j], names[k], - names[i], names[jk], names[i_jk]); - } -#endif - return false; - } - } - - return true; -} - -static int solver(const game_params *params, digit *grid, int maxdiff) -{ - int w = params->w; - int ret; - struct latin_solver solver; -#ifdef STANDALONE_SOLVER - char *p, text[100], *names[50]; - int i; -#endif - - latin_solver_alloc(&solver, grid, w); -#ifdef STANDALONE_SOLVER - for (i = 0, p = text; i < w; i++) { - names[i] = p; - *p++ = TOCHAR(i+1, params->id); - *p++ = '\0'; - } - solver.names = names; -#endif - - ret = latin_solver_main(&solver, maxdiff, - DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME, - DIFF_EXTREME, DIFF_UNREASONABLE, - group_solvers, group_valid, NULL, NULL, NULL); - - latin_solver_free(&solver); - - return ret; -} - -/* ---------------------------------------------------------------------- - * Grid generation. - */ - -static char *encode_grid(char *desc, digit *grid, int area) -{ - int run, i; - char *p = desc; - - run = 0; - for (i = 0; i <= area; i++) { - int n = (i < area ? grid[i] : -1); - - if (!n) - run++; - else { - if (run) { - while (run > 0) { - int c = 'a' - 1 + run; - if (run > 26) - c = 'z'; - *p++ = c; - run -= c - ('a' - 1); - } - } else { - /* - * If there's a number in the very top left or - * bottom right, there's no point putting an - * unnecessary _ before or after it. - */ - if (p > desc && n > 0) - *p++ = '_'; - } - if (n > 0) - p += sprintf(p, "%d", n); - run = 0; - } - } - return p; -} - -/* ----- data generated by group.gap begins ----- */ - -struct group { - unsigned long autosize; - int order, ngens; - const char *gens; -}; -struct groups { - int ngroups; - const struct group *groups; -}; - -static const struct group groupdata[] = { - /* order 2 */ - {1L, 2, 1, "BA"}, - /* order 3 */ - {2L, 3, 1, "BCA"}, - /* order 4 */ - {2L, 4, 1, "BCDA"}, - {6L, 4, 2, "BADC" "CDAB"}, - /* order 5 */ - {4L, 5, 1, "BCDEA"}, - /* order 6 */ - {6L, 6, 2, "CFEBAD" "BADCFE"}, - {2L, 6, 1, "DCFEBA"}, - /* order 7 */ - {6L, 7, 1, "BCDEFGA"}, - /* order 8 */ - {4L, 8, 1, "BCEFDGHA"}, - {8L, 8, 2, "BDEFGAHC" "EGBHDCFA"}, - {8L, 8, 2, "EGBHDCFA" "BAEFCDHG"}, - {24L, 8, 2, "BDEFGAHC" "CHDGBEAF"}, - {168L, 8, 3, "BAEFCDHG" "CEAGBHDF" "DFGAHBCE"}, - /* order 9 */ - {6L, 9, 1, "BDECGHFIA"}, - {48L, 9, 2, "BDEAGHCIF" "CEFGHAIBD"}, - /* order 10 */ - {20L, 10, 2, "CJEBGDIFAH" "BADCFEHGJI"}, - {4L, 10, 1, "DCFEHGJIBA"}, - /* order 11 */ - {10L, 11, 1, "BCDEFGHIJKA"}, - /* order 12 */ - {12L, 12, 2, "GLDKJEHCBIAF" "BCEFAGIJDKLH"}, - {4L, 12, 1, "EHIJKCBLDGFA"}, - {24L, 12, 2, "BEFGAIJKCDLH" "FJBKHLEGDCIA"}, - {12L, 12, 2, "GLDKJEHCBIAF" "BAEFCDIJGHLK"}, - {12L, 12, 2, "FDIJGHLBKAEC" "GIDKFLHCJEAB"}, - /* order 13 */ - {12L, 13, 1, "BCDEFGHIJKLMA"}, - /* order 14 */ - {42L, 14, 2, "ELGNIBKDMFAHCJ" "BADCFEHGJILKNM"}, - {6L, 14, 1, "FEHGJILKNMBADC"}, - /* order 15 */ - {8L, 15, 1, "EGHCJKFMNIOBLDA"}, - /* order 16 */ - {8L, 16, 1, "MKNPFOADBGLCIEHJ"}, - {96L, 16, 2, "ILKCONFPEDJHGMAB" "BDFGHIAKLMNCOEPJ"}, - {32L, 16, 2, "MIHPFDCONBLAKJGE" "BEFGHJKALMNOCDPI"}, - {32L, 16, 2, "IFACOGLMDEJBNPKH" "BEFGHJKALMNOCDPI"}, - {16L, 16, 2, "MOHPFKCINBLADJGE" "BDFGHIEKLMNJOAPC"}, - {16L, 16, 2, "MIHPFDJONBLEKCGA" "BDFGHIEKLMNJOAPC"}, - {32L, 16, 2, "MOHPFDCINBLEKJGA" "BAFGHCDELMNIJKPO"}, - {16L, 16, 2, "MIHPFKJONBLADCGE" "GDPHNOEKFLBCIAMJ"}, - {32L, 16, 2, "MIBPFDJOGHLEKCNA" "CLEIJGMPKAOHNFDB"}, - {192L, 16, 3, - "MCHPFAIJNBLDEOGK" "BEFGHJKALMNOCDPI" "GKLBNOEDFPHJIAMC"}, - {64L, 16, 3, "MCHPFAIJNBLDEOGK" "LOGFPKJIBNMEDCHA" "CMAIJHPFDEONBLKG"}, - {192L, 16, 3, - "IPKCOGMLEDJBNFAH" "BEFGHJKALMNOCDPI" "CMEIJBPFKAOGHLDN"}, - {48L, 16, 3, "IPDJONFLEKCBGMAH" "FJBLMEOCGHPKAIND" "DGIEKLHNJOAMPBCF"}, - {20160L, 16, 4, - "EHJKAMNBOCDPFGIL" "BAFGHCDELMNIJKPO" "CFAIJBLMDEOGHPKN" - "DGIAKLBNCOEFPHJM"}, - /* order 17 */ - {16L, 17, 1, "EFGHIJKLMNOPQABCD"}, - /* order 18 */ - {54L, 18, 2, "MKIQOPNAGLRECDBJHF" "BAEFCDJKLGHIOPMNRQ"}, - {6L, 18, 1, "ECJKGHFOPDMNLRIQBA"}, - {12L, 18, 2, "ECJKGHBOPAMNFRDQLI" "KNOPQCFREIGHLJAMBD"}, - {432L, 18, 3, - "IFNAKLQCDOPBGHREMJ" "NOQCFRIGHKLJAMPBDE" "BAEFCDJKLGHIOPMNRQ"}, - {48L, 18, 2, "ECJKGHBOPAMNFRDQLI" "FDKLHIOPBMNAREQCJG"}, - /* order 19 */ - {18L, 19, 1, "EFGHIJKLMNOPQRSABCD"}, - /* order 20 */ - {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "EABICDFMGHJQKLNTOPRS"}, - {8L, 20, 1, "EHIJLCMNPGQRSKBTDOFA"}, - {20L, 20, 2, "DJSHQNCLTRGPEBKAIFOM" "EABICDFMGHJQKLNTOPRS"}, - {40L, 20, 2, "GTDKREHOBILSFMPCJQAN" "ECBIAGFMDKJQHONTLSRP"}, - {24L, 20, 2, "IGFMDKJQHONTLSREPCBA" "FDIJGHMNKLQROPTBSAEC"}, - /* order 21 */ - {42L, 21, 2, "ITLSBOUERDHAGKCJNFMQP" "EJHLMKOPNRSQAUTCDBFGI"}, - {12L, 21, 1, "EGHCJKFMNIPQLSTOUBRDA"}, - /* order 22 */ - {110L, 22, 2, "ETGVIBKDMFOHQJSLUNAPCR" "BADCFEHGJILKNMPORQTSVU"}, - {10L, 22, 1, "FEHGJILKNMPORQTSVUBADC"}, - /* order 23 */ - {22L, 23, 1, "EFGHIJKLMNOPQRSTUVWABCD"}, - /* order 24 */ - {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "HRNOPSWCTUVBLDIJXFGAKQME"}, - {8L, 24, 1, "MQBTUDRWFGHXJELINOPKSAVC"}, - {24L, 24, 2, "IOQRBEUVFWGHKLAXMNPSCDTJ" "NJXOVGDKSMTFIPQELCURBWAH"}, - {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "HSNOPWLDTUVBRIAKXFGCQEMJ"}, - {24L, 24, 2, "QXEJWPUMKLRIVBFTSACGHNDO" "TWHNXLRIOPUMSACQVBFDEJGK"}, - {48L, 24, 2, "QUEJWVXFKLRIPGMNSACBOTDH" "BAFGHCDEMNOPIJKLTUVQRSXW"}, - {48L, 24, 3, - "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO" - "HSNOPWLDTUVBRIAKXFGCQEMJ"}, - {24L, 24, 3, - "QUKJWPXFESRIVBMNLDCGHTAO" "JXEQRVUMKLWCPGFTSAIBONDH" - "TRONXLWCHVUMSAIJPGFDEQBK"}, - {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "VKXHOQASNTPBCWDEUFGIJLMR"}, - {16L, 24, 2, "MRGTULWIOPFXSDJQBVNEKCHA" "RMLWIGTUSDJQOPFXEKCBVNAH"}, - {48L, 24, 2, "IULQRGXMSDCWOPNTEKJBVFAH" "GLMOPRSDTUBVWIEKFXHJQANC"}, - {24L, 24, 2, "UJPXMRCSNHGTLWIKFVBEDQOA" "NRUFVLWIPXMOJEDQHGTCSABK"}, - {24L, 24, 2, "MIBTUAQRFGHXCDEWNOPJKLVS" "OKXVFWSCGUTNDRQJBPMALIHE"}, - {144L, 24, 3, - "QXKJWVUMESRIPGFTLDCBONAH" "JUEQRPXFKLWCVBMNSAIGHTDO" - "BAFGHCDEMNOPIJKLTUVQRSXW"}, - {336L, 24, 3, - "QTKJWONXESRIHVUMLDCPGFAB" "JNEQRHTUKLWCOPXFSAIVBMDG" - "HENOPJKLTUVBQRSAXFGWCDMI"}, - /* order 25 */ - {20L, 25, 1, "EHILMNPQRSFTUVBJWXDOYGAKC"}, - {480L, 25, 2, "EHILMNPQRSCTUVBFWXDJYGOKA" "BDEGHIKLMNAPQRSCTUVFWXJYO"}, - /* order 26 */ - {156L, 26, 2, - "EXGZIBKDMFOHQJSLUNWPYRATCV" "BADCFEHGJILKNMPORQTSVUXWZY"}, - {12L, 26, 1, "FEHGJILKNMPORQTSVUXWZYBADC"}, -}; - -static const struct groups groups[] = { - {0, NULL}, /* trivial case: 0 */ - {0, NULL}, /* trivial case: 1 */ - {1, groupdata + 0}, /* 2 */ - {1, groupdata + 1}, /* 3 */ - {2, groupdata + 2}, /* 4 */ - {1, groupdata + 4}, /* 5 */ - {2, groupdata + 5}, /* 6 */ - {1, groupdata + 7}, /* 7 */ - {5, groupdata + 8}, /* 8 */ - {2, groupdata + 13}, /* 9 */ - {2, groupdata + 15}, /* 10 */ - {1, groupdata + 17}, /* 11 */ - {5, groupdata + 18}, /* 12 */ - {1, groupdata + 23}, /* 13 */ - {2, groupdata + 24}, /* 14 */ - {1, groupdata + 26}, /* 15 */ - {14, groupdata + 27}, /* 16 */ - {1, groupdata + 41}, /* 17 */ - {5, groupdata + 42}, /* 18 */ - {1, groupdata + 47}, /* 19 */ - {5, groupdata + 48}, /* 20 */ - {2, groupdata + 53}, /* 21 */ - {2, groupdata + 55}, /* 22 */ - {1, groupdata + 57}, /* 23 */ - {15, groupdata + 58}, /* 24 */ - {2, groupdata + 73}, /* 25 */ - {2, groupdata + 75}, /* 26 */ -}; - -/* ----- data generated by group.gap ends ----- */ - -static char *new_game_desc(const game_params *params, random_state *rs, - char **aux, bool interactive) -{ - int w = params->w, a = w*w; - digit *grid, *soln, *soln2; - int *indices; - int i, j, k, qh, qt; - int diff = params->diff; - const struct group *group; - char *desc, *p; - - /* - * Difficulty exceptions: some combinations of size and - * difficulty cannot be satisfied, because all puzzles of at - * most that difficulty are actually even easier. - * - * Remember to re-test this whenever a change is made to the - * solver logic! - * - * I tested it using the following shell command: - -for d in t n h x u; do - for id in '' i; do - for i in {3..9}; do - echo -n "./group --generate 1 ${i}d${d}${id}: " - perl -e 'alarm 30; exec @ARGV' \ - ./group --generate 1 ${i}d${d}${id} >/dev/null && echo ok - done - done -done - - * Of course, it's better to do that after taking the exceptions - * _out_, so as to detect exceptions that should be removed as - * well as those which should be added. - */ - if (w < 5 && diff == DIFF_UNREASONABLE) - diff--; - if ((w < 5 || ((w == 6 || w == 8) && params->id)) && diff == DIFF_EXTREME) - diff--; - if ((w < 6 || (w == 6 && params->id)) && diff == DIFF_HARD) - diff--; - if ((w < 4 || (w == 4 && params->id)) && diff == DIFF_NORMAL) - diff--; - - grid = snewn(a, digit); - soln = snewn(a, digit); - soln2 = snewn(a, digit); - indices = snewn(a, int); - - while (1) { - /* - * Construct a valid group table, by picking a group from - * the above data table, decompressing it into a full - * representation by BFS, and then randomly permuting its - * non-identity elements. - * - * We build the canonical table in 'soln' (and use 'grid' as - * our BFS queue), then transfer the table into 'grid' - * having shuffled the rows. - */ - assert(w >= 2); - assert(w < lenof(groups)); - group = groups[w].groups + random_upto(rs, groups[w].ngroups); - assert(group->order == w); - memset(soln, 0, a); - for (i = 0; i < w; i++) - soln[i] = i+1; - qh = qt = 0; - grid[qt++] = 1; - while (qh < qt) { - digit *row, *newrow; - - i = grid[qh++]; - row = soln + (i-1)*w; - - for (j = 0; j < group->ngens; j++) { - int nri; - const char *gen = group->gens + j*w; - - /* - * Apply each group generator to row, constructing a - * new row. - */ - nri = gen[row[0]-1] - 'A' + 1; /* which row is it? */ - newrow = soln + (nri-1)*w; - if (!newrow[0]) { /* not done yet */ - for (k = 0; k < w; k++) - newrow[k] = gen[row[k]-1] - 'A' + 1; - grid[qt++] = nri; - } - } - } - /* That's got the canonical table. Now shuffle it. */ - for (i = 0; i < w; i++) - soln2[i] = i; - if (params->id) /* do we shuffle in the identity? */ - shuffle(soln2+1, w-1, sizeof(*soln2), rs); - else - shuffle(soln2, w, sizeof(*soln2), rs); - for (i = 0; i < w; i++) - for (j = 0; j < w; j++) - grid[(soln2[i])*w+(soln2[j])] = soln2[soln[i*w+j]-1]+1; - - /* - * Remove entries one by one while the puzzle is still - * soluble at the appropriate difficulty level. - */ - memcpy(soln, grid, a); - if (!params->id) { - /* - * Start by blanking the entire identity row and column, - * and also another row and column so that the player - * can't trivially determine which element is the - * identity. - */ - - j = 1 + random_upto(rs, w-1); /* pick a second row/col to blank */ - for (i = 0; i < w; i++) { - grid[(soln2[0])*w+i] = grid[i*w+(soln2[0])] = 0; - grid[(soln2[j])*w+i] = grid[i*w+(soln2[j])] = 0; - } - - memcpy(soln2, grid, a); - if (solver(params, soln2, diff) > diff) - continue; /* go round again if that didn't work */ - } - - k = 0; - for (i = (params->id ? 1 : 0); i < w; i++) - for (j = (params->id ? 1 : 0); j < w; j++) - if (grid[i*w+j]) - indices[k++] = i*w+j; - shuffle(indices, k, sizeof(*indices), rs); - - for (i = 0; i < k; i++) { - memcpy(soln2, grid, a); - soln2[indices[i]] = 0; - if (solver(params, soln2, diff) <= diff) - grid[indices[i]] = 0; - } - - /* - * Make sure the puzzle isn't too easy. - */ - if (diff > 0) { - memcpy(soln2, grid, a); - if (solver(params, soln2, diff-1) < diff) - continue; /* go round and try again */ - } - - /* - * Done. - */ - break; - } - - /* - * Encode the puzzle description. - */ - desc = snewn(a*20, char); - p = encode_grid(desc, grid, a); - *p++ = '\0'; - desc = sresize(desc, p - desc, char); - - /* - * Encode the solution. - */ - *aux = snewn(a+2, char); - (*aux)[0] = 'S'; - for (i = 0; i < a; i++) - (*aux)[i+1] = TOCHAR(soln[i], params->id); - (*aux)[a+1] = '\0'; - - sfree(grid); - sfree(soln); - sfree(soln2); - sfree(indices); - - return desc; -} - -/* ---------------------------------------------------------------------- - * Gameplay. - */ - -static const char *validate_grid_desc(const char **pdesc, int range, int area) -{ - const char *desc = *pdesc; - int squares = 0; - while (*desc && *desc != ',') { - int n = *desc++; - if (n >= 'a' && n <= 'z') { - squares += n - 'a' + 1; - } else if (n == '_') { - /* do nothing */; - } else if (n > '0' && n <= '9') { - int val = atoi(desc-1); - if (val < 1 || val > range) - return "Out-of-range number in game description"; - squares++; - while (*desc >= '0' && *desc <= '9') - desc++; - } else - return "Invalid character in game description"; - } - - if (squares < area) - return "Not enough data to fill grid"; - - if (squares > area) - return "Too much data to fit in grid"; - *pdesc = desc; - return NULL; -} - -static const char *validate_desc(const game_params *params, const char *desc) -{ - int w = params->w, a = w*w; - const char *p = desc; - - return validate_grid_desc(&p, w, a); -} - -static const char *spec_to_grid(const char *desc, digit *grid, int area) -{ - int i = 0; - while (*desc && *desc != ',') { - int n = *desc++; - if (n >= 'a' && n <= 'z') { - int run = n - 'a' + 1; - assert(i + run <= area); - while (run-- > 0) - grid[i++] = 0; - } else if (n == '_') { - /* do nothing */; - } else if (n > '0' && n <= '9') { - assert(i < area); - grid[i++] = atoi(desc-1); - while (*desc >= '0' && *desc <= '9') - desc++; - } else { - assert(!"We can't get here"); - } - } - assert(i == area); - return desc; -} - -static game_state *new_game(midend *me, const game_params *params, - const char *desc) -{ - int w = params->w, a = w*w; - game_state *state = snew(game_state); - int i; - - state->par = *params; /* structure copy */ - state->grid = snewn(a, digit); - state->common = snew(group_common); - state->common->refcount = 1; - state->common->immutable = snewn(a, bool); - state->pencil = snewn(a, int); - for (i = 0; i < a; i++) { - state->grid[i] = 0; - state->common->immutable[i] = false; - state->pencil[i] = 0; - } - state->sequence = snewn(w, digit); - state->dividers = snewn(w, int); - for (i = 0; i < w; i++) { - state->sequence[i] = i; - state->dividers[i] = -1; - } - - desc = spec_to_grid(desc, state->grid, a); - for (i = 0; i < a; i++) - if (state->grid[i] != 0) - state->common->immutable[i] = true; - - state->completed = false; - state->cheated = false; - - return state; -} - -static game_state *dup_game(const game_state *state) -{ - int w = state->par.w, a = w*w; - game_state *ret = snew(game_state); - - ret->par = state->par; /* structure copy */ - - ret->grid = snewn(a, digit); - ret->common = state->common; - ret->common->refcount++; - ret->pencil = snewn(a, int); - ret->sequence = snewn(w, digit); - ret->dividers = snewn(w, int); - memcpy(ret->grid, state->grid, a*sizeof(digit)); - memcpy(ret->pencil, state->pencil, a*sizeof(int)); - memcpy(ret->sequence, state->sequence, w*sizeof(digit)); - memcpy(ret->dividers, state->dividers, w*sizeof(int)); - - ret->completed = state->completed; - ret->cheated = state->cheated; - - return ret; -} - -static void free_game(game_state *state) -{ - sfree(state->grid); - if (--state->common->refcount == 0) { - sfree(state->common->immutable); - sfree(state->common); - } - sfree(state->pencil); - sfree(state->sequence); - sfree(state); -} - -static char *solve_game(const game_state *state, const game_state *currstate, - const char *aux, const char **error) -{ - int w = state->par.w, a = w*w; - int i, ret; - digit *soln; - char *out; - - if (aux) - return dupstr(aux); - - soln = snewn(a, digit); - memcpy(soln, state->grid, a*sizeof(digit)); - - ret = solver(&state->par, soln, DIFFCOUNT-1); - - if (ret == diff_impossible) { - *error = "No solution exists for this puzzle"; - out = NULL; - } else if (ret == diff_ambiguous) { - *error = "Multiple solutions exist for this puzzle"; - out = NULL; - } else { - out = snewn(a+2, char); - out[0] = 'S'; - for (i = 0; i < a; i++) - out[i+1] = TOCHAR(soln[i], state->par.id); - out[a+1] = '\0'; - } - - sfree(soln); - return out; -} - -static bool game_can_format_as_text_now(const game_params *params) -{ - return true; -} - -static char *game_text_format(const game_state *state) -{ - int w = state->par.w; - int x, y; - char *ret, *p, ch; - - ret = snewn(2*w*w+1, char); /* leave room for terminating NUL */ - - p = ret; - for (y = 0; y < w; y++) { - for (x = 0; x < w; x++) { - digit d = state->grid[y*w+x]; - - if (d == 0) { - ch = '.'; - } else { - ch = TOCHAR(d, state->par.id); - } - - *p++ = ch; - if (x == w-1) { - *p++ = '\n'; - } else { - *p++ = ' '; - } - } - } - - assert(p - ret == 2*w*w); - *p = '\0'; - return ret; -} - -struct game_ui { - /* - * These are the coordinates of the primary highlighted square on - * the grid, if hshow = 1. - */ - int hx, hy; - /* - * These are the coordinates hx,hy _before_ they go through - * state->sequence. - */ - int ohx, ohy; - /* - * These variables give the length and displacement of a diagonal - * sequence of highlighted squares starting at ohx,ohy (still if - * hshow = 1). To find the squares' real coordinates, for 0<=isequence. - */ - int odx, ody, odn; - /* - * This indicates whether the current highlight is a - * pencil-mark one or a real one. - */ - bool hpencil; - /* - * This indicates whether or not we're showing the highlight - * (used to be hx = hy = -1); important so that when we're - * using the cursor keys it doesn't keep coming back at a - * fixed position. When hshow = 1, pressing a valid number - * or letter key or Space will enter that number or letter in the grid. - */ - bool hshow; - /* - * This indicates whether we're using the highlight as a cursor; - * it means that it doesn't vanish on a keypress, and that it is - * allowed on immutable squares. - */ - bool hcursor; - /* - * This indicates whether we're dragging a table header to - * reposition an entire row or column. - */ - int drag; /* 0=none 1=row 2=col */ - int dragnum; /* element being dragged */ - int dragpos; /* its current position */ - int edgepos; -}; - -static game_ui *new_ui(const game_state *state) -{ - game_ui *ui = snew(game_ui); - - ui->hx = ui->hy = 0; - ui->hpencil = false; - ui->hshow = false; - ui->hcursor = false; - ui->drag = 0; - - return ui; -} - -static void free_ui(game_ui *ui) -{ - sfree(ui); -} - -static char *encode_ui(const game_ui *ui) -{ - return NULL; -} - -static void decode_ui(game_ui *ui, const char *encoding) -{ -} - -static void game_changed_state(game_ui *ui, const game_state *oldstate, - const game_state *newstate) -{ - int w = newstate->par.w; - /* - * We prevent pencil-mode highlighting of a filled square, unless - * we're using the cursor keys. So if the user has just filled in - * a square which we had a pencil-mode highlight in (by Undo, or - * by Redo, or by Solve), then we cancel the highlight. - */ - if (ui->hshow && ui->hpencil && !ui->hcursor && - newstate->grid[ui->hy * w + ui->hx] != 0) { - ui->hshow = false; - } - if (ui->hshow && ui->odn > 1) { - /* - * Reordering of rows or columns within the range of a - * multifill selection cancels the multifill and deselects - * everything. - */ - int i; - for (i = 0; i < ui->odn; i++) { - if (oldstate->sequence[ui->ohx + i*ui->odx] != - newstate->sequence[ui->ohx + i*ui->odx]) { - ui->hshow = false; - break; - } - if (oldstate->sequence[ui->ohy + i*ui->ody] != - newstate->sequence[ui->ohy + i*ui->ody]) { - ui->hshow = false; - break; - } - } - } else if (ui->hshow && - (newstate->sequence[ui->ohx] != ui->hx || - newstate->sequence[ui->ohy] != ui->hy)) { - /* - * Otherwise, reordering of the row or column containing the - * selection causes the selection to move with it. - */ - int i; - for (i = 0; i < w; i++) { - if (newstate->sequence[i] == ui->hx) - ui->ohx = i; - if (newstate->sequence[i] == ui->hy) - ui->ohy = i; - } - } -} - -#define PREFERRED_TILESIZE 48 -#define TILESIZE (ds->tilesize) -#define BORDER (TILESIZE / 2) -#define LEGEND (TILESIZE) -#define GRIDEXTRA max((TILESIZE / 32),1) -#define COORD(x) ((x)*TILESIZE + BORDER + LEGEND) -#define FROMCOORD(x) (((x)+(TILESIZE-BORDER-LEGEND)) / TILESIZE - 1) - -#define FLASH_TIME 0.4F - -#define DF_DIVIDER_TOP 0x1000 -#define DF_DIVIDER_BOT 0x2000 -#define DF_DIVIDER_LEFT 0x4000 -#define DF_DIVIDER_RIGHT 0x8000 -#define DF_HIGHLIGHT 0x0400 -#define DF_HIGHLIGHT_PENCIL 0x0200 -#define DF_IMMUTABLE 0x0100 -#define DF_LEGEND 0x0080 -#define DF_DIGIT_MASK 0x001F - -#define EF_DIGIT_SHIFT 5 -#define EF_DIGIT_MASK ((1 << EF_DIGIT_SHIFT) - 1) -#define EF_LEFT_SHIFT 0 -#define EF_RIGHT_SHIFT (3*EF_DIGIT_SHIFT) -#define EF_LEFT_MASK ((1UL << (3*EF_DIGIT_SHIFT)) - 1UL) -#define EF_RIGHT_MASK (EF_LEFT_MASK << EF_RIGHT_SHIFT) -#define EF_LATIN (1UL << (6*EF_DIGIT_SHIFT)) - -struct game_drawstate { - game_params par; - int w, tilesize; - bool started; - long *tiles, *legend, *pencil, *errors; - long *errtmp; - digit *sequence; -}; - -static bool check_errors(const game_state *state, long *errors) -{ - int w = state->par.w, a = w*w; - digit *grid = state->grid; - int i, j, k, x, y; - bool errs = false; - - /* - * To verify that we have a valid group table, it suffices to - * test latin-square-hood and associativity only. All the other - * group axioms follow from those two. - * - * Proof: - * - * Associativity is given; closure is obvious from latin- - * square-hood. We need to show that an identity exists and that - * every element has an inverse. - * - * Identity: take any element a. There will be some element e - * such that ea=a (in a latin square, every element occurs in - * every row and column, so a must occur somewhere in the a - * column, say on row e). For any other element b, there must - * exist x such that ax=b (same argument from latin-square-hood - * again), and then associativity gives us eb = e(ax) = (ea)x = - * ax = b. Hence eb=b for all b, i.e. e is a left-identity. A - * similar argument tells us that there must be some f which is - * a right-identity, and then we show they are the same element - * by observing that ef must simultaneously equal e and equal f. - * - * Inverses: given any a, by the latin-square argument again, - * there must exist p and q such that pa=e and aq=e (i.e. left- - * and right-inverses). We can show these are equal by - * associativity: p = pe = p(aq) = (pa)q = eq = q. [] - */ - - if (errors) - for (i = 0; i < a; i++) - errors[i] = 0; - - for (y = 0; y < w; y++) { - unsigned long mask = 0, errmask = 0; - for (x = 0; x < w; x++) { - unsigned long bit = 1UL << grid[y*w+x]; - errmask |= (mask & bit); - mask |= bit; - } - - if (mask != (1 << (w+1)) - (1 << 1)) { - errs = true; - errmask &= ~1UL; - if (errors) { - for (x = 0; x < w; x++) - if (errmask & (1UL << grid[y*w+x])) - errors[y*w+x] |= EF_LATIN; - } - } - } - - for (x = 0; x < w; x++) { - unsigned long mask = 0, errmask = 0; - for (y = 0; y < w; y++) { - unsigned long bit = 1UL << grid[y*w+x]; - errmask |= (mask & bit); - mask |= bit; - } - - if (mask != (1 << (w+1)) - (1 << 1)) { - errs = true; - errmask &= ~1UL; - if (errors) { - for (y = 0; y < w; y++) - if (errmask & (1UL << grid[y*w+x])) - errors[y*w+x] |= EF_LATIN; - } - } - } - - for (i = 1; i < w; i++) - for (j = 1; j < w; j++) - for (k = 1; k < w; k++) - if (grid[i*w+j] && grid[j*w+k] && - grid[(grid[i*w+j]-1)*w+k] && - grid[i*w+(grid[j*w+k]-1)] && - grid[(grid[i*w+j]-1)*w+k] != grid[i*w+(grid[j*w+k]-1)]) { - if (errors) { - int a = i+1, b = j+1, c = k+1; - int ab = grid[i*w+j], bc = grid[j*w+k]; - int left = (ab-1)*w+(c-1), right = (a-1)*w+(bc-1); - /* - * If the appropriate error slot is already - * used for one of the squares, we don't - * fill either of them. - */ - if (!(errors[left] & EF_LEFT_MASK) && - !(errors[right] & EF_RIGHT_MASK)) { - long err; - err = a; - err = (err << EF_DIGIT_SHIFT) | b; - err = (err << EF_DIGIT_SHIFT) | c; - errors[left] |= err << EF_LEFT_SHIFT; - errors[right] |= err << EF_RIGHT_SHIFT; - } - } - errs = true; - } - - return errs; -} - -static int find_in_sequence(digit *seq, int len, digit n) -{ - int i; - - for (i = 0; i < len; i++) - if (seq[i] == n) - return i; - - assert(!"Should never get here"); - return -1; -} - -static char *interpret_move(const game_state *state, game_ui *ui, - const game_drawstate *ds, - int x, int y, int button) -{ - int w = state->par.w; - int tx, ty; - char buf[80]; - - button &= ~MOD_MASK; - - tx = FROMCOORD(x); - ty = FROMCOORD(y); - - if (ui->drag) { - if (IS_MOUSE_DRAG(button)) { - int tcoord = ((ui->drag &~ 4) == 1 ? ty : tx); - ui->drag |= 4; /* some movement has happened */ - if (tcoord >= 0 && tcoord < w) { - ui->dragpos = tcoord; - return UI_UPDATE; - } - } else if (IS_MOUSE_RELEASE(button)) { - if (ui->drag & 4) { - ui->drag = 0; /* end drag */ - if (state->sequence[ui->dragpos] == ui->dragnum) - return UI_UPDATE; /* drag was a no-op overall */ - sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos); - return dupstr(buf); - } else { - ui->drag = 0; /* end 'drag' */ - if (ui->edgepos > 0 && ui->edgepos < w) { - sprintf(buf, "V%d,%d", - state->sequence[ui->edgepos-1], - state->sequence[ui->edgepos]); - return dupstr(buf); - } else - return UI_UPDATE; /* no-op */ - } - } - } else if (IS_MOUSE_DOWN(button)) { - if (tx >= 0 && tx < w && ty >= 0 && ty < w) { - int otx = tx, oty = ty; - tx = state->sequence[tx]; - ty = state->sequence[ty]; - if (button == LEFT_BUTTON) { - if (tx == ui->hx && ty == ui->hy && - ui->hshow && !ui->hpencil) { - ui->hshow = false; - } else { - ui->hx = tx; - ui->hy = ty; - ui->ohx = otx; - ui->ohy = oty; - ui->odx = ui->ody = 0; - ui->odn = 1; - ui->hshow = !state->common->immutable[ty*w+tx]; - ui->hpencil = false; - } - ui->hcursor = false; - return UI_UPDATE; - } - if (button == RIGHT_BUTTON) { - /* - * Pencil-mode highlighting for non filled squares. - */ - if (state->grid[ty*w+tx] == 0) { - if (tx == ui->hx && ty == ui->hy && - ui->hshow && ui->hpencil) { - ui->hshow = false; - } else { - ui->hpencil = true; - ui->hx = tx; - ui->hy = ty; - ui->ohx = otx; - ui->ohy = oty; - ui->odx = ui->ody = 0; - ui->odn = 1; - ui->hshow = true; - } - } else { - ui->hshow = false; - } - ui->hcursor = false; - return UI_UPDATE; - } - } else if (tx >= 0 && tx < w && ty == -1) { - ui->drag = 2; - ui->dragnum = state->sequence[tx]; - ui->dragpos = tx; - ui->edgepos = FROMCOORD(x + TILESIZE/2); - return UI_UPDATE; - } else if (ty >= 0 && ty < w && tx == -1) { - ui->drag = 1; - ui->dragnum = state->sequence[ty]; - ui->dragpos = ty; - ui->edgepos = FROMCOORD(y + TILESIZE/2); - return UI_UPDATE; - } - } else if (IS_MOUSE_DRAG(button)) { - if (!ui->hpencil && - tx >= 0 && tx < w && ty >= 0 && ty < w && - abs(tx - ui->ohx) == abs(ty - ui->ohy)) { - ui->odn = abs(tx - ui->ohx) + 1; - ui->odx = (tx < ui->ohx ? -1 : +1); - ui->ody = (ty < ui->ohy ? -1 : +1); - } else { - ui->odx = ui->ody = 0; - ui->odn = 1; - } - return UI_UPDATE; - } - - if (IS_CURSOR_MOVE(button)) { - int cx = find_in_sequence(state->sequence, w, ui->hx); - int cy = find_in_sequence(state->sequence, w, ui->hy); - move_cursor(button, &cx, &cy, w, w, false); - ui->hx = state->sequence[cx]; - ui->hy = state->sequence[cy]; - ui->hshow = true; - ui->hcursor = true; - return UI_UPDATE; - } - if (ui->hshow && - (button == CURSOR_SELECT)) { - ui->hpencil = !ui->hpencil; - ui->hcursor = true; - return UI_UPDATE; - } - - if (ui->hshow && - ((ISCHAR(button) && FROMCHAR(button, state->par.id) <= w) || - button == CURSOR_SELECT2 || button == '\b')) { - int n = FROMCHAR(button, state->par.id); - int i, buflen; - char *movebuf; - - if (button == CURSOR_SELECT2 || button == '\b') - n = 0; - - for (i = 0; i < ui->odn; i++) { - int x = state->sequence[ui->ohx + i*ui->odx]; - int y = state->sequence[ui->ohy + i*ui->ody]; - int index = y*w+x; - - /* - * Can't make pencil marks in a filled square. This can only - * become highlighted if we're using cursor keys. - */ - if (ui->hpencil && state->grid[index]) - return NULL; - - /* - * Can't do anything to an immutable square. Exception: - * trying to set it to what it already was is OK (so that - * multifilling can set a whole diagonal to a without - * having to detour round the one immutable square in the - * middle that already said a). - */ - if (!ui->hpencil && state->grid[index] == n) - /* OK even if it is immutable */; - else if (state->common->immutable[index]) - return NULL; - } - - movebuf = snewn(80 * ui->odn, char); - buflen = sprintf(movebuf, "%c%d,%d,%d", - (char)(ui->hpencil && n > 0 ? 'P' : 'R'), - ui->hx, ui->hy, n); - for (i = 1; i < ui->odn; i++) { - assert(buflen < i*80); - buflen += sprintf(movebuf + buflen, "+%d,%d", - state->sequence[ui->ohx + i*ui->odx], - state->sequence[ui->ohy + i*ui->ody]); - } - movebuf = sresize(movebuf, buflen+1, char); - - if (!ui->hcursor) ui->hshow = false; - - return movebuf; - } - - if (button == 'M' || button == 'm') - return dupstr("M"); - - return NULL; -} - -static game_state *execute_move(const game_state *from, const char *move) -{ - int w = from->par.w, a = w*w; - game_state *ret; - int x, y, i, j, n, pos; - - if (move[0] == 'S') { - ret = dup_game(from); - ret->completed = ret->cheated = true; - - for (i = 0; i < a; i++) { - if (!ISCHAR(move[i+1]) || FROMCHAR(move[i+1], from->par.id) > w) { - free_game(ret); - return NULL; - } - ret->grid[i] = FROMCHAR(move[i+1], from->par.id); - ret->pencil[i] = 0; - } - - if (move[a+1] != '\0') { - free_game(ret); - return NULL; - } - - return ret; - } else if ((move[0] == 'P' || move[0] == 'R') && - sscanf(move+1, "%d,%d,%d%n", &x, &y, &n, &pos) == 3 && - n >= 0 && n <= w) { - const char *mp = move + 1 + pos; - bool pencil = (move[0] == 'P'); - ret = dup_game(from); - - while (1) { - if (x < 0 || x >= w || y < 0 || y >= w) { - free_game(ret); - return NULL; - } - if (from->common->immutable[y*w+x] && - !(!pencil && from->grid[y*w+x] == n)) - return NULL; - - if (move[0] == 'P' && n > 0) { - ret->pencil[y*w+x] ^= 1 << n; - } else { - ret->grid[y*w+x] = n; - ret->pencil[y*w+x] = 0; - } - - if (!*mp) - break; - - if (*mp != '+') - return NULL; - if (sscanf(mp, "+%d,%d%n", &x, &y, &pos) < 2) - return NULL; - mp += pos; - } - - if (!ret->completed && !check_errors(ret, NULL)) - ret->completed = true; - - return ret; - } else if (move[0] == 'M') { - /* - * Fill in absolutely all pencil marks everywhere. (I - * wouldn't use this for actual play, but it's a handy - * starting point when following through a set of - * diagnostics output by the standalone solver.) - */ - ret = dup_game(from); - for (i = 0; i < a; i++) { - if (!ret->grid[i]) - ret->pencil[i] = (1 << (w+1)) - (1 << 1); - } - return ret; - } else if (move[0] == 'D' && - sscanf(move+1, "%d,%d", &x, &y) == 2) { - /* - * Reorder the rows and columns so that digit x is in position - * y. - */ - ret = dup_game(from); - for (i = j = 0; i < w; i++) { - if (i == y) { - ret->sequence[i] = x; - } else { - if (from->sequence[j] == x) - j++; - ret->sequence[i] = from->sequence[j++]; - } - } - /* - * Eliminate any obsoleted dividers. - */ - for (x = 0; x < w; x++) { - int i = ret->sequence[x]; - int j = (x+1 < w ? ret->sequence[x+1] : -1); - if (ret->dividers[i] != j) - ret->dividers[i] = -1; - } - return ret; - } else if (move[0] == 'V' && - sscanf(move+1, "%d,%d", &i, &j) == 2) { - ret = dup_game(from); - if (ret->dividers[i] == j) - ret->dividers[i] = -1; - else - ret->dividers[i] = j; - return ret; - } else - return NULL; /* couldn't parse move string */ -} - -/* ---------------------------------------------------------------------- - * Drawing routines. - */ - -#define SIZE(w) ((w) * TILESIZE + 2*BORDER + LEGEND) - -static void game_compute_size(const game_params *params, int tilesize, - int *x, int *y) -{ - /* Ick: fake up `ds->tilesize' for macro expansion purposes */ - struct { int tilesize; } ads, *ds = &ads; - ads.tilesize = tilesize; - - *x = *y = SIZE(params->w); -} - -static void game_set_size(drawing *dr, game_drawstate *ds, - const game_params *params, int tilesize) -{ - ds->tilesize = tilesize; -} - -static float *game_colours(frontend *fe, int *ncolours) -{ - float *ret = snewn(3 * NCOLOURS, float); - - frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); - - ret[COL_GRID * 3 + 0] = 0.0F; - ret[COL_GRID * 3 + 1] = 0.0F; - ret[COL_GRID * 3 + 2] = 0.0F; - - ret[COL_USER * 3 + 0] = 0.0F; - ret[COL_USER * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1]; - ret[COL_USER * 3 + 2] = 0.0F; - - ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0]; - ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1]; - ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2]; - - ret[COL_ERROR * 3 + 0] = 1.0F; - ret[COL_ERROR * 3 + 1] = 0.0F; - ret[COL_ERROR * 3 + 2] = 0.0F; - - ret[COL_PENCIL * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; - ret[COL_PENCIL * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; - ret[COL_PENCIL * 3 + 2] = ret[COL_BACKGROUND * 3 + 2]; - - ret[COL_DIAGONAL * 3 + 0] = 0.95F * ret[COL_BACKGROUND * 3 + 0]; - ret[COL_DIAGONAL * 3 + 1] = 0.95F * ret[COL_BACKGROUND * 3 + 1]; - ret[COL_DIAGONAL * 3 + 2] = 0.95F * ret[COL_BACKGROUND * 3 + 2]; - - *ncolours = NCOLOURS; - return ret; -} - -static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) -{ - int w = state->par.w, a = w*w; - struct game_drawstate *ds = snew(struct game_drawstate); - int i; - - ds->w = w; - ds->par = state->par; /* structure copy */ - ds->tilesize = 0; - ds->started = false; - ds->tiles = snewn(a, long); - ds->legend = snewn(w, long); - ds->pencil = snewn(a, long); - ds->errors = snewn(a, long); - ds->sequence = snewn(a, digit); - for (i = 0; i < a; i++) - ds->tiles[i] = ds->pencil[i] = -1; - for (i = 0; i < w; i++) - ds->legend[i] = -1; - ds->errtmp = snewn(a, long); - - return ds; -} - -static void game_free_drawstate(drawing *dr, game_drawstate *ds) -{ - sfree(ds->tiles); - sfree(ds->pencil); - sfree(ds->errors); - sfree(ds->errtmp); - sfree(ds->sequence); - sfree(ds); -} - -static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile, - long pencil, long error) -{ - int w = ds->w /* , a = w*w */; - int tx, ty, tw, th; - int cx, cy, cw, ch; - char str[64]; - - tx = BORDER + LEGEND + x * TILESIZE + 1; - ty = BORDER + LEGEND + y * TILESIZE + 1; - - cx = tx; - cy = ty; - cw = tw = TILESIZE-1; - ch = th = TILESIZE-1; - - if (tile & DF_LEGEND) { - cx += TILESIZE/10; - cy += TILESIZE/10; - cw -= TILESIZE/5; - ch -= TILESIZE/5; - tile |= DF_IMMUTABLE; - } - - clip(dr, cx, cy, cw, ch); - - /* background needs erasing */ - draw_rect(dr, cx, cy, cw, ch, - (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : - (x == y) ? COL_DIAGONAL : COL_BACKGROUND); - - /* dividers */ - if (tile & DF_DIVIDER_TOP) - draw_rect(dr, cx, cy, cw, 1, COL_GRID); - if (tile & DF_DIVIDER_BOT) - draw_rect(dr, cx, cy+ch-1, cw, 1, COL_GRID); - if (tile & DF_DIVIDER_LEFT) - draw_rect(dr, cx, cy, 1, ch, COL_GRID); - if (tile & DF_DIVIDER_RIGHT) - draw_rect(dr, cx+cw-1, cy, 1, ch, COL_GRID); - - /* pencil-mode highlight */ - if (tile & DF_HIGHLIGHT_PENCIL) { - int coords[6]; - coords[0] = cx; - coords[1] = cy; - coords[2] = cx+cw/2; - coords[3] = cy; - coords[4] = cx; - coords[5] = cy+ch/2; - draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); - } - - /* new number needs drawing? */ - if (tile & DF_DIGIT_MASK) { - str[1] = '\0'; - str[0] = TOCHAR(tile & DF_DIGIT_MASK, ds->par.id); - draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, - FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, - (error & EF_LATIN) ? COL_ERROR : - (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str); - - if (error & EF_LEFT_MASK) { - int a = (error >> (EF_LEFT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; - int b = (error >> (EF_LEFT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; - int c = (error >> (EF_LEFT_SHIFT ))&EF_DIGIT_MASK; - char buf[10]; - sprintf(buf, "(%c%c)%c", TOCHAR(a, ds->par.id), - TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id)); - draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/6, - FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE, - COL_ERROR, buf); - } - if (error & EF_RIGHT_MASK) { - int a = (error >> (EF_RIGHT_SHIFT+2*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; - int b = (error >> (EF_RIGHT_SHIFT+1*EF_DIGIT_SHIFT))&EF_DIGIT_MASK; - int c = (error >> (EF_RIGHT_SHIFT ))&EF_DIGIT_MASK; - char buf[10]; - sprintf(buf, "%c(%c%c)", TOCHAR(a, ds->par.id), - TOCHAR(b, ds->par.id), TOCHAR(c, ds->par.id)); - draw_text(dr, tx + TILESIZE/2, ty + TILESIZE - TILESIZE/6, - FONT_VARIABLE, TILESIZE/6, ALIGN_VCENTRE | ALIGN_HCENTRE, - COL_ERROR, buf); - } - } else { - int i, j, npencil; - int pl, pr, pt, pb; - float bestsize; - int pw, ph, minph, pbest, fontsize; - - /* Count the pencil marks required. */ - for (i = 1, npencil = 0; i <= w; i++) - if (pencil & (1 << i)) - npencil++; - if (npencil) { - - minph = 2; - - /* - * Determine the bounding rectangle within which we're going - * to put the pencil marks. - */ - /* Start with the whole square */ - pl = tx + GRIDEXTRA; - pr = pl + TILESIZE - GRIDEXTRA; - pt = ty + GRIDEXTRA; - pb = pt + TILESIZE - GRIDEXTRA; - - /* - * We arrange our pencil marks in a grid layout, with - * the number of rows and columns adjusted to allow the - * maximum font size. - * - * So now we work out what the grid size ought to be. - */ - bestsize = 0.0; - pbest = 0; - /* Minimum */ - for (pw = 3; pw < max(npencil,4); pw++) { - float fw, fh, fs; - - ph = (npencil + pw - 1) / pw; - ph = max(ph, minph); - fw = (pr - pl) / (float)pw; - fh = (pb - pt) / (float)ph; - fs = min(fw, fh); - if (fs > bestsize) { - bestsize = fs; - pbest = pw; - } - } - assert(pbest > 0); - pw = pbest; - ph = (npencil + pw - 1) / pw; - ph = max(ph, minph); - - /* - * Now we've got our grid dimensions, work out the pixel - * size of a grid element, and round it to the nearest - * pixel. (We don't want rounding errors to make the - * grid look uneven at low pixel sizes.) - */ - fontsize = min((pr - pl) / pw, (pb - pt) / ph); - - /* - * Centre the resulting figure in the square. - */ - pl = tx + (TILESIZE - fontsize * pw) / 2; - pt = ty + (TILESIZE - fontsize * ph) / 2; - - /* - * Now actually draw the pencil marks. - */ - for (i = 1, j = 0; i <= w; i++) - if (pencil & (1 << i)) { - int dx = j % pw, dy = j / pw; - - str[1] = '\0'; - str[0] = TOCHAR(i, ds->par.id); - draw_text(dr, pl + fontsize * (2*dx+1) / 2, - pt + fontsize * (2*dy+1) / 2, - FONT_VARIABLE, fontsize, - ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str); - j++; - } - } - } - - unclip(dr); - - draw_update(dr, cx, cy, cw, ch); -} - -static void game_redraw(drawing *dr, game_drawstate *ds, - const game_state *oldstate, const game_state *state, - int dir, const game_ui *ui, - float animtime, float flashtime) -{ - int w = state->par.w /*, a = w*w */; - int x, y, i, j; - - if (!ds->started) { - /* - * The initial contents of the window are not guaranteed and - * can vary with front ends. To be on the safe side, all - * games should start by drawing a big background-colour - * rectangle covering the whole window. - */ - draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND); - - /* - * Big containing rectangle. - */ - draw_rect(dr, COORD(0) - GRIDEXTRA, COORD(0) - GRIDEXTRA, - w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2, - COL_GRID); - - draw_update(dr, 0, 0, SIZE(w), SIZE(w)); - - ds->started = true; - } - - check_errors(state, ds->errtmp); - - /* - * Construct a modified version of state->sequence which takes - * into account an unfinished drag operation. - */ - if (ui->drag) { - x = ui->dragnum; - y = ui->dragpos; - } else { - x = y = -1; - } - for (i = j = 0; i < w; i++) { - if (i == y) { - ds->sequence[i] = x; - } else { - if (state->sequence[j] == x) - j++; - ds->sequence[i] = state->sequence[j++]; - } - } - - /* - * Draw the table legend. - */ - for (x = 0; x < w; x++) { - int sx = ds->sequence[x]; - long tile = (sx+1) | DF_LEGEND; - if (ds->legend[x] != tile) { - ds->legend[x] = tile; - draw_tile(dr, ds, -1, x, tile, 0, 0); - draw_tile(dr, ds, x, -1, tile, 0, 0); - } - } - - for (y = 0; y < w; y++) { - int sy = ds->sequence[y]; - for (x = 0; x < w; x++) { - long tile = 0L, pencil = 0L, error; - int sx = ds->sequence[x]; - - if (state->grid[sy*w+sx]) - tile = state->grid[sy*w+sx]; - else - pencil = (long)state->pencil[sy*w+sx]; - - if (state->common->immutable[sy*w+sx]) - tile |= DF_IMMUTABLE; - - if ((ui->drag == 5 && ui->dragnum == sy) || - (ui->drag == 6 && ui->dragnum == sx)) { - tile |= DF_HIGHLIGHT; - } else if (ui->hshow) { - int i = abs(x - ui->ohx); - bool highlight = false; - if (ui->odn > 1) { - /* - * When a diagonal multifill selection is shown, - * we show it in its original grid position - * regardless of in-progress row/col drags. Moving - * every square about would be horrible. - */ - if (i >= 0 && i < ui->odn && - x == ui->ohx + i*ui->odx && - y == ui->ohy + i*ui->ody) - highlight = true; - } else { - /* - * For a single square, we move its highlight - * around with the drag. - */ - highlight = (ui->hx == sx && ui->hy == sy); - } - if (highlight) - tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT); - } - - if (flashtime > 0 && - (flashtime <= FLASH_TIME/3 || - flashtime >= FLASH_TIME*2/3)) - tile |= DF_HIGHLIGHT; /* completion flash */ - - if (y <= 0 || state->dividers[ds->sequence[y-1]] == sy) - tile |= DF_DIVIDER_TOP; - if (y+1 >= w || state->dividers[sy] == ds->sequence[y+1]) - tile |= DF_DIVIDER_BOT; - if (x <= 0 || state->dividers[ds->sequence[x-1]] == sx) - tile |= DF_DIVIDER_LEFT; - if (x+1 >= w || state->dividers[sx] == ds->sequence[x+1]) - tile |= DF_DIVIDER_RIGHT; - - error = ds->errtmp[sy*w+sx]; - - if (ds->tiles[y*w+x] != tile || - ds->pencil[y*w+x] != pencil || - ds->errors[y*w+x] != error) { - ds->tiles[y*w+x] = tile; - ds->pencil[y*w+x] = pencil; - ds->errors[y*w+x] = error; - draw_tile(dr, ds, x, y, tile, pencil, error); - } - } - } -} - -static float game_anim_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - return 0.0F; -} - -static float game_flash_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - if (!oldstate->completed && newstate->completed && - !oldstate->cheated && !newstate->cheated) - return FLASH_TIME; - return 0.0F; -} - -static void game_get_cursor_location(const game_ui *ui, - const game_drawstate *ds, - const game_state *state, - const game_params *params, - int *x, int *y, int *w, int *h) -{ -} - -static int game_status(const game_state *state) -{ - return state->completed ? +1 : 0; -} - -static bool game_timing_state(const game_state *state, game_ui *ui) -{ - if (state->completed) - return false; - return true; -} - -static void game_print_size(const game_params *params, float *x, float *y) -{ - int pw, ph; - - /* - * We use 9mm squares by default, like Solo. - */ - game_compute_size(params, 900, &pw, &ph); - *x = pw / 100.0F; - *y = ph / 100.0F; -} - -static void game_print(drawing *dr, const game_state *state, int tilesize) -{ - int w = state->par.w; - int ink = print_mono_colour(dr, 0); - int x, y; - - /* Ick: fake up `ds->tilesize' for macro expansion purposes */ - game_drawstate ads, *ds = &ads; - game_set_size(dr, ds, NULL, tilesize); - - /* - * Border. - */ - print_line_width(dr, 3 * TILESIZE / 40); - draw_rect_outline(dr, BORDER + LEGEND, BORDER + LEGEND, - w*TILESIZE, w*TILESIZE, ink); - - /* - * Legend on table. - */ - for (x = 0; x < w; x++) { - char str[2]; - str[1] = '\0'; - str[0] = TOCHAR(x+1, state->par.id); - draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2, - BORDER + TILESIZE/2, - FONT_VARIABLE, TILESIZE/2, - ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); - draw_text(dr, BORDER + TILESIZE/2, - BORDER+LEGEND + x*TILESIZE + TILESIZE/2, - FONT_VARIABLE, TILESIZE/2, - ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); - } - - /* - * Main grid. - */ - for (x = 1; x < w; x++) { - print_line_width(dr, TILESIZE / 40); - draw_line(dr, BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND, - BORDER+LEGEND+x*TILESIZE, BORDER+LEGEND+w*TILESIZE, ink); - } - for (y = 1; y < w; y++) { - print_line_width(dr, TILESIZE / 40); - draw_line(dr, BORDER+LEGEND, BORDER+LEGEND+y*TILESIZE, - BORDER+LEGEND+w*TILESIZE, BORDER+LEGEND+y*TILESIZE, ink); - } - - /* - * Numbers. - */ - for (y = 0; y < w; y++) - for (x = 0; x < w; x++) - if (state->grid[y*w+x]) { - char str[2]; - str[1] = '\0'; - str[0] = TOCHAR(state->grid[y*w+x], state->par.id); - draw_text(dr, BORDER+LEGEND + x*TILESIZE + TILESIZE/2, - BORDER+LEGEND + y*TILESIZE + TILESIZE/2, - FONT_VARIABLE, TILESIZE/2, - ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); - } -} - -#ifdef COMBINED -#define thegame group -#endif - -const struct game thegame = { - "Group", NULL, NULL, - default_params, - game_fetch_preset, NULL, - decode_params, - encode_params, - free_params, - dup_params, - true, game_configure, custom_params, - validate_params, - new_game_desc, - validate_desc, - new_game, - dup_game, - free_game, - true, solve_game, - true, game_can_format_as_text_now, game_text_format, - new_ui, - free_ui, - encode_ui, - decode_ui, - NULL, /* game_request_keys */ - game_changed_state, - interpret_move, - execute_move, - PREFERRED_TILESIZE, game_compute_size, game_set_size, - game_colours, - game_new_drawstate, - game_free_drawstate, - game_redraw, - game_anim_length, - game_flash_length, - game_get_cursor_location, - game_status, - true, false, game_print_size, game_print, - false, /* wants_statusbar */ - false, game_timing_state, - REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ -}; - -#ifdef STANDALONE_SOLVER - -#include - -int main(int argc, char **argv) -{ - game_params *p; - game_state *s; - char *id = NULL, *desc; - const char *err; - digit *grid; - bool grade = false; - int ret, diff; - bool really_show_working = false; - - while (--argc > 0) { - char *p = *++argv; - if (!strcmp(p, "-v")) { - really_show_working = true; - } else if (!strcmp(p, "-g")) { - grade = true; - } else if (*p == '-') { - fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); - return 1; - } else { - id = p; - } - } - - if (!id) { - fprintf(stderr, "usage: %s [-g | -v] \n", argv[0]); - return 1; - } - - desc = strchr(id, ':'); - if (!desc) { - fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); - return 1; - } - *desc++ = '\0'; - - p = default_params(); - decode_params(p, id); - err = validate_desc(p, desc); - if (err) { - fprintf(stderr, "%s: %s\n", argv[0], err); - return 1; - } - s = new_game(NULL, p, desc); - - grid = snewn(p->w * p->w, digit); - - /* - * When solving a Normal puzzle, we don't want to bother the - * user with Hard-level deductions. For this reason, we grade - * the puzzle internally before doing anything else. - */ - ret = -1; /* placate optimiser */ - solver_show_working = 0; - for (diff = 0; diff < DIFFCOUNT; diff++) { - memcpy(grid, s->grid, p->w * p->w); - ret = solver(&s->par, grid, diff); - if (ret <= diff) - break; - } - - if (diff == DIFFCOUNT) { - if (really_show_working) { - solver_show_working = true; - memcpy(grid, s->grid, p->w * p->w); - ret = solver(&s->par, grid, DIFFCOUNT - 1); - } - if (grade) - printf("Difficulty rating: ambiguous\n"); - else - printf("Unable to find a unique solution\n"); - } else { - if (grade) { - if (ret == diff_impossible) - printf("Difficulty rating: impossible (no solution exists)\n"); - else - printf("Difficulty rating: %s\n", group_diffnames[ret]); - } else { - solver_show_working = really_show_working; - memcpy(grid, s->grid, p->w * p->w); - ret = solver(&s->par, grid, diff); - if (ret != diff) - printf("Puzzle is inconsistent\n"); - else { - memcpy(s->grid, grid, p->w * p->w); - fputs(game_text_format(s), stdout); - } - } - } - - return 0; -} - -#endif - -/* vim: set shiftwidth=4 tabstop=8: */ diff --git a/apps/plugins/puzzles/src/unfinished/group.gap b/apps/plugins/puzzles/src/unfinished/group.gap deleted file mode 100644 index 280adf4664..0000000000 --- a/apps/plugins/puzzles/src/unfinished/group.gap +++ /dev/null @@ -1,97 +0,0 @@ -# run this file with -# gap -b -q < /dev/null group.gap | perl -pe 's/\\\n//s' | indent -kr - -Print("/* ----- data generated by group.gap begins ----- */\n\n"); -Print("struct group {\n unsigned long autosize;\n"); -Print(" int order, ngens;\n const char *gens;\n};\n"); -Print("struct groups {\n int ngroups;\n"); -Print(" const struct group *groups;\n};\n\n"); -Print("static const struct group groupdata[] = {\n"); -offsets := [0]; -offset := 0; -for n in [2..26] do - Print(" /* order ", n, " */\n"); - for G in AllSmallGroups(n) do - - # Construct a representation of the group G as a subgroup - # of a permutation group, and find its generators in that - # group. - - # GAP has the 'IsomorphismPermGroup' function, but I don't want - # to use it because it doesn't guarantee that the permutation - # representation of the group forms a Cayley table. For example, - # C_4 could be represented as a subgroup of S_4 in many ways, - # and not all of them work: the group generated by (12) and (34) - # is clearly isomorphic to C_4 but its four elements do not form - # a Cayley table. The group generated by (12)(34) and (13)(24) - # is OK, though. - # - # Hence I construct the permutation representation _as_ the - # Cayley table, and then pick generators of that. This - # guarantees that when we rebuild the full group by BFS in - # group.c, we will end up with the right thing. - - ge := Elements(G); - gi := []; - for g in ge do - gr := []; - for h in ge do - k := g*h; - for i in [1..n] do - if k = ge[i] then - Add(gr, i); - fi; - od; - od; - Add(gi, PermList(gr)); - od; - - # GAP has the 'GeneratorsOfGroup' function, but we don't want to - # use it because it's bad at picking generators - it thinks the - # generators of C_4 are [ (1,2)(3,4), (1,3,2,4) ] and that those - # of C_6 are [ (1,2,3)(4,5,6), (1,4)(2,5)(3,6) ] ! - - gl := ShallowCopy(Elements(gi)); - Sort(gl, function(v,w) return Order(v) > Order(w); end); - - gens := []; - for x in gl do - if gens = [] or not (x in gp) then - Add(gens, x); - gp := GroupWithGenerators(gens); - fi; - od; - - # Construct the C representation of the group generators. - s := []; - for x in gens do - if Size(s) > 0 then - Add(s, '"'); - Add(s, ' '); - Add(s, '"'); - fi; - sep := "\\0"; - for i in ListPerm(x) do - chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - Add(s, chars[i]); - od; - od; - s := JoinStringsWithSeparator([" {", String(Size(AutomorphismGroup(G))), - "L, ", String(Size(G)), - ", ", String(Size(gens)), - ", \"", s, "\"},\n"],""); - Print(s); - offset := offset + 1; - od; - Add(offsets, offset); -od; -Print("};\n\nstatic const struct groups groups[] = {\n"); -Print(" {0, NULL}, /* trivial case: 0 */\n"); -Print(" {0, NULL}, /* trivial case: 1 */\n"); -n := 2; -for i in [1..Size(offsets)-1] do - Print(" {", offsets[i+1] - offsets[i], ", groupdata+", - offsets[i], "}, /* ", i+1, " */\n"); -od; -Print("};\n\n/* ----- data generated by group.gap ends ----- */\n"); -quit; diff --git a/apps/plugins/puzzles/src/unfinished/numgame.c b/apps/plugins/puzzles/src/unfinished/numgame.c deleted file mode 100644 index d444b2257b..0000000000 --- a/apps/plugins/puzzles/src/unfinished/numgame.c +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * This program implements a breadth-first search which - * exhaustively solves the Countdown numbers game, and related - * games with slightly different rule sets such as `Flippo'. - * - * Currently it is simply a standalone command-line utility to - * which you provide a set of numbers and it tells you everything - * it can make together with how many different ways it can be - * made. I would like ultimately to turn it into the generator for - * a Puzzles puzzle, but I haven't even started on writing a - * Puzzles user interface yet. - */ - -/* - * TODO: - * - * - start thinking about difficulty ratings - * + anything involving associative operations will be flagged - * as many-paths because of the associative options (e.g. - * 2*3*4 can be (2*3)*4 or 2*(3*4), or indeed (2*4)*3). This - * is probably a _good_ thing, since those are unusually - * easy. - * + tree-structured calculations ((a*b)/(c+d)) have multiple - * paths because the independent branches of the tree can be - * evaluated in either order, whereas straight-line - * calculations with no branches will be considered easier. - * Can we do anything about this? It's certainly not clear to - * me that tree-structure calculations are _easier_, although - * I'm also not convinced they're harder. - * + I think for a realistic difficulty assessment we must also - * consider the `obviousness' of the arithmetic operations in - * some heuristic sense, and also (in Countdown) how many - * numbers ended up being used. - * - actually try some generations - * - at this point we're probably ready to start on the Puzzles - * integration. - */ - -#include -#include -#include -#include -#include - -#include "puzzles.h" -#include "tree234.h" - -/* - * To search for numbers we can make, we employ a breadth-first - * search across the space of sets of input numbers. That is, for - * example, we start with the set (3,6,25,50,75,100); we apply - * moves which involve combining two numbers (e.g. adding the 50 - * and the 75 takes us to the set (3,6,25,100,125); and then we see - * if we ever end up with a set containing (say) 952. - * - * If the rules are changed so that all the numbers must be used, - * this is easy to adjust to: we simply see if we end up with a set - * containing _only_ (say) 952. - * - * Obviously, we can vary the rules about permitted arithmetic - * operations simply by altering the set of valid moves in the bfs. - * However, there's one common rule in this sort of puzzle which - * takes a little more thought, and that's _concatenation_. For - * example, if you are given (say) four 4s and required to make 10, - * you are permitted to combine two of the 4s into a 44 to begin - * with, making (44-4)/4 = 10. However, you are generally not - * allowed to concatenate two numbers that _weren't_ both in the - * original input set (you couldn't multiply two 4s to get 16 and - * then concatenate a 4 on to it to make 164), so concatenation is - * not an operation which is valid in all situations. - * - * We could enforce this restriction by storing a flag alongside - * each number indicating whether or not it's an original number; - * the rules being that concatenation of two numbers is only valid - * if they both have the original flag, and that its output _also_ - * has the original flag (so that you can concatenate three 4s into - * a 444), but that applying any other arithmetic operation clears - * the original flag on the output. However, we can get marginally - * simpler than that by observing that since concatenation has to - * happen to a number before any other operation, we can simply - * place all the concatenations at the start of the search. In - * other words, we have a global flag on an entire number _set_ - * which indicates whether we are still permitted to perform - * concatenations; if so, we can concatenate any of the numbers in - * that set. Performing any other operation clears the flag. - */ - -#define SETFLAG_CONCAT 1 /* we can do concatenation */ - -struct sets; - -struct ancestor { - struct set *prev; /* index of ancestor set in set list */ - unsigned char pa, pb, po, pr; /* operation that got here from prev */ -}; - -struct set { - int *numbers; /* rationals stored as n,d pairs */ - short nnumbers; /* # of rationals, so half # of ints */ - short flags; /* SETFLAG_CONCAT only, at present */ - int npaths; /* number of ways to reach this set */ - struct ancestor a; /* primary ancestor */ - struct ancestor *as; /* further ancestors, if we care */ - int nas, assize; -}; - -struct output { - int number; - struct set *set; - int index; /* which number in the set is it? */ - int npaths; /* number of ways to reach this */ -}; - -#define SETLISTLEN 1024 -#define NUMBERLISTLEN 32768 -#define OUTPUTLISTLEN 1024 -struct operation; -struct sets { - struct set **setlists; - int nsets, nsetlists, setlistsize; - tree234 *settree; - int **numberlists; - int nnumbers, nnumberlists, numberlistsize; - struct output **outputlists; - int noutputs, noutputlists, outputlistsize; - tree234 *outputtree; - const struct operation *const *ops; -}; - -#define OPFLAG_NEEDS_CONCAT 1 -#define OPFLAG_KEEPS_CONCAT 2 -#define OPFLAG_UNARY 4 -#define OPFLAG_UNARYPREFIX 8 -#define OPFLAG_FN 16 - -struct operation { - /* - * Most operations should be shown in the output working, but - * concatenation should not; we just take the result of the - * concatenation and assume that it's obvious how it was - * derived. - */ - int display; - - /* - * Text display of the operator, in expressions and for - * debugging respectively. - */ - char *text, *dbgtext; - - /* - * Flags dictating when the operator can be applied. - */ - int flags; - - /* - * Priority of the operator (for avoiding unnecessary - * parentheses when formatting it into a string). - */ - int priority; - - /* - * Associativity of the operator. Bit 0 means we need parens - * when the left operand of one of these operators is another - * instance of it, e.g. (2^3)^4. Bit 1 means we need parens - * when the right operand is another instance of the same - * operator, e.g. 2-(3-4). Thus: - * - * - this field is 0 for a fully associative operator, since - * we never need parens. - * - it's 1 for a right-associative operator. - * - it's 2 for a left-associative operator. - * - it's 3 for a _non_-associative operator (which always - * uses parens just to be sure). - */ - int assoc; - - /* - * Whether the operator is commutative. Saves time in the - * search if we don't have to try it both ways round. - */ - int commutes; - - /* - * Function which implements the operator. Returns true on - * success, false on failure. Takes two rationals and writes - * out a third. - */ - int (*perform)(int *a, int *b, int *output); -}; - -struct rules { - const struct operation *const *ops; - int use_all; -}; - -#define MUL(r, a, b) do { \ - (r) = (a) * (b); \ - if ((b) && (a) && (r) / (b) != (a)) return false; \ -} while (0) - -#define ADD(r, a, b) do { \ - (r) = (a) + (b); \ - if ((a) > 0 && (b) > 0 && (r) < 0) return false; \ - if ((a) < 0 && (b) < 0 && (r) > 0) return false; \ -} while (0) - -#define OUT(output, n, d) do { \ - int g = gcd((n),(d)); \ - if (g < 0) g = -g; \ - if ((d) < 0) g = -g; \ - if (g == -1 && (n) < -INT_MAX) return false; \ - if (g == -1 && (d) < -INT_MAX) return false; \ - (output)[0] = (n)/g; \ - (output)[1] = (d)/g; \ - assert((output)[1] > 0); \ -} while (0) - -static int gcd(int x, int y) -{ - while (x != 0 && y != 0) { - int t = x; - x = y; - y = t % y; - } - - return abs(x + y); /* i.e. whichever one isn't zero */ -} - -static int perform_add(int *a, int *b, int *output) -{ - int at, bt, tn, bn; - /* - * a0/a1 + b0/b1 = (a0*b1 + b0*a1) / (a1*b1) - */ - MUL(at, a[0], b[1]); - MUL(bt, b[0], a[1]); - ADD(tn, at, bt); - MUL(bn, a[1], b[1]); - OUT(output, tn, bn); - return true; -} - -static int perform_sub(int *a, int *b, int *output) -{ - int at, bt, tn, bn; - /* - * a0/a1 - b0/b1 = (a0*b1 - b0*a1) / (a1*b1) - */ - MUL(at, a[0], b[1]); - MUL(bt, b[0], a[1]); - ADD(tn, at, -bt); - MUL(bn, a[1], b[1]); - OUT(output, tn, bn); - return true; -} - -static int perform_mul(int *a, int *b, int *output) -{ - int tn, bn; - /* - * a0/a1 * b0/b1 = (a0*b0) / (a1*b1) - */ - MUL(tn, a[0], b[0]); - MUL(bn, a[1], b[1]); - OUT(output, tn, bn); - return true; -} - -static int perform_div(int *a, int *b, int *output) -{ - int tn, bn; - - /* - * Division by zero is outlawed. - */ - if (b[0] == 0) - return false; - - /* - * a0/a1 / b0/b1 = (a0*b1) / (a1*b0) - */ - MUL(tn, a[0], b[1]); - MUL(bn, a[1], b[0]); - OUT(output, tn, bn); - return true; -} - -static int perform_exact_div(int *a, int *b, int *output) -{ - int tn, bn; - - /* - * Division by zero is outlawed. - */ - if (b[0] == 0) - return false; - - /* - * a0/a1 / b0/b1 = (a0*b1) / (a1*b0) - */ - MUL(tn, a[0], b[1]); - MUL(bn, a[1], b[0]); - OUT(output, tn, bn); - - /* - * Exact division means we require the result to be an integer. - */ - return (output[1] == 1); -} - -static int max_p10(int n, int *p10_r) -{ - /* - * Find the smallest power of ten strictly greater than n. - * - * Special case: we must return at least 10, even if n is - * zero. (This is because this function is used for finding - * the power of ten by which to multiply a number being - * concatenated to the front of n, and concatenating 1 to 0 - * should yield 10 and not 1.) - */ - int p10 = 10; - while (p10 <= (INT_MAX/10) && p10 <= n) - p10 *= 10; - if (p10 > INT_MAX/10) - return false; /* integer overflow */ - *p10_r = p10; - return true; -} - -static int perform_concat(int *a, int *b, int *output) -{ - int t1, t2, p10; - - /* - * We can't concatenate anything which isn't a non-negative - * integer. - */ - if (a[1] != 1 || b[1] != 1 || a[0] < 0 || b[0] < 0) - return false; - - /* - * For concatenation, we can safely assume leading zeroes - * aren't an issue. It isn't clear whether they `should' be - * allowed, but it turns out not to matter: concatenating a - * leading zero on to a number in order to harmlessly get rid - * of the zero is never necessary because unwanted zeroes can - * be disposed of by adding them to something instead. So we - * disallow them always. - * - * The only other possibility is that you might want to - * concatenate a leading zero on to something and then - * concatenate another non-zero digit on to _that_ (to make, - * for example, 106); but that's also unnecessary, because you - * can make 106 just as easily by concatenating the 0 on to the - * _end_ of the 1 first. - */ - if (a[0] == 0) - return false; - - if (!max_p10(b[0], &p10)) return false; - - MUL(t1, p10, a[0]); - ADD(t2, t1, b[0]); - OUT(output, t2, 1); - return true; -} - -#define IPOW(ret, x, y) do { \ - int ipow_limit = (y); \ - if ((x) == 1 || (x) == 0) ipow_limit = 1; \ - else if ((x) == -1) ipow_limit &= 1; \ - (ret) = 1; \ - while (ipow_limit-- > 0) { \ - int tmp; \ - MUL(tmp, ret, x); \ - ret = tmp; \ - } \ -} while (0) - -static int perform_exp(int *a, int *b, int *output) -{ - int an, ad, xn, xd; - - /* - * Exponentiation is permitted if the result is rational. This - * means that: - * - * - first we see whether we can take the (denominator-of-b)th - * root of a and get a rational; if not, we give up. - * - * - then we do take that root of a - * - * - then we multiply by itself (numerator-of-b) times. - */ - if (b[1] > 1) { - an = (int)(0.5 + pow(a[0], 1.0/b[1])); - ad = (int)(0.5 + pow(a[1], 1.0/b[1])); - IPOW(xn, an, b[1]); - IPOW(xd, ad, b[1]); - if (xn != a[0] || xd != a[1]) - return false; - } else { - an = a[0]; - ad = a[1]; - } - if (b[0] >= 0) { - IPOW(xn, an, b[0]); - IPOW(xd, ad, b[0]); - } else { - IPOW(xd, an, -b[0]); - IPOW(xn, ad, -b[0]); - } - if (xd == 0) - return false; - - OUT(output, xn, xd); - return true; -} - -static int perform_factorial(int *a, int *b, int *output) -{ - int ret, t, i; - - /* - * Factorials of non-negative integers are permitted. - */ - if (a[1] != 1 || a[0] < 0) - return false; - - /* - * However, a special case: we don't take a factorial of - * anything which would thereby remain the same. - */ - if (a[0] == 1 || a[0] == 2) - return false; - - ret = 1; - for (i = 1; i <= a[0]; i++) { - MUL(t, ret, i); - ret = t; - } - - OUT(output, ret, 1); - return true; -} - -static int perform_decimal(int *a, int *b, int *output) -{ - int p10; - - /* - * Add a decimal digit to the front of a number; - * fail if it's not an integer. - * So, 1 --> 0.1, 15 --> 0.15, - * or, rather, 1 --> 1/10, 15 --> 15/100, - * x --> x / (smallest power of 10 > than x) - * - */ - if (a[1] != 1) return false; - - if (!max_p10(a[0], &p10)) return false; - - OUT(output, a[0], p10); - return true; -} - -static int perform_recur(int *a, int *b, int *output) -{ - int p10, tn, bn; - - /* - * This converts a number like .4 to .44444..., or .45 to .45454... - * The input number must be -1 < a < 1. - * - * Calculate the smallest power of 10 that divides the denominator exactly, - * returning if no such power of 10 exists. Then multiply the numerator - * up accordingly, and the new denominator becomes that power of 10 - 1. - */ - if (abs(a[0]) >= abs(a[1])) return false; /* -1 < a < 1 */ - - p10 = 10; - while (p10 <= (INT_MAX/10)) { - if ((a[1] <= p10) && (p10 % a[1]) == 0) goto found; - p10 *= 10; - } - return false; -found: - tn = a[0] * (p10 / a[1]); - bn = p10 - 1; - - OUT(output, tn, bn); - return true; -} - -static int perform_root(int *a, int *b, int *output) -{ - /* - * A root B is: 1 iff a == 0 - * B ^ (1/A) otherwise - */ - int ainv[2], res; - - if (a[0] == 0) { - OUT(output, 1, 1); - return true; - } - - OUT(ainv, a[1], a[0]); - res = perform_exp(b, ainv, output); - return res; -} - -static int perform_perc(int *a, int *b, int *output) -{ - if (a[0] == 0) return false; /* 0% = 0, uninteresting. */ - if (a[1] > (INT_MAX/100)) return false; - - OUT(output, a[0], a[1]*100); - return true; -} - -static int perform_gamma(int *a, int *b, int *output) -{ - int asub1[2]; - - /* - * gamma(a) = (a-1)! - * - * special case not caught by perform_fact: gamma(1) is 1 so - * don't bother. - */ - if (a[0] == 1 && a[1] == 1) return false; - - OUT(asub1, a[0]-a[1], a[1]); - return perform_factorial(asub1, b, output); -} - -static int perform_sqrt(int *a, int *b, int *output) -{ - int half[2] = { 1, 2 }; - - /* - * sqrt(0) == 0, sqrt(1) == 1: don't perform unary noops. - */ - if (a[0] == 0 || (a[0] == 1 && a[1] == 1)) return false; - - return perform_exp(a, half, output); -} - -const static struct operation op_add = { - true, "+", "+", 0, 10, 0, true, perform_add -}; -const static struct operation op_sub = { - true, "-", "-", 0, 10, 2, false, perform_sub -}; -const static struct operation op_mul = { - true, "*", "*", 0, 20, 0, true, perform_mul -}; -const static struct operation op_div = { - true, "/", "/", 0, 20, 2, false, perform_div -}; -const static struct operation op_xdiv = { - true, "/", "/", 0, 20, 2, false, perform_exact_div -}; -const static struct operation op_concat = { - false, "", "concat", OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, - 1000, 0, false, perform_concat -}; -const static struct operation op_exp = { - true, "^", "^", 0, 30, 1, false, perform_exp -}; -const static struct operation op_factorial = { - true, "!", "!", OPFLAG_UNARY, 40, 0, false, perform_factorial -}; -const static struct operation op_decimal = { - true, ".", ".", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_NEEDS_CONCAT | OPFLAG_KEEPS_CONCAT, 50, 0, false, perform_decimal -}; -const static struct operation op_recur = { - true, "...", "recur", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 2, false, perform_recur -}; -const static struct operation op_root = { - true, "v~", "root", 0, 30, 1, false, perform_root -}; -const static struct operation op_perc = { - true, "%", "%", OPFLAG_UNARY | OPFLAG_NEEDS_CONCAT, 45, 1, false, perform_perc -}; -const static struct operation op_gamma = { - true, "gamma", "gamma", OPFLAG_UNARY | OPFLAG_UNARYPREFIX | OPFLAG_FN, 1, 3, false, perform_gamma -}; -const static struct operation op_sqrt = { - true, "v~", "sqrt", OPFLAG_UNARY | OPFLAG_UNARYPREFIX, 30, 1, false, perform_sqrt -}; - -/* - * In Countdown, divisions resulting in fractions are disallowed. - * http://www.askoxford.com/wordgames/countdown/rules/ - */ -const static struct operation *const ops_countdown[] = { - &op_add, &op_mul, &op_sub, &op_xdiv, NULL -}; -const static struct rules rules_countdown = { - ops_countdown, false -}; - -/* - * A slightly different rule set which handles the reasonably well - * known puzzle of making 24 using two 3s and two 8s. For this we - * need rational rather than integer division. - */ -const static struct operation *const ops_3388[] = { - &op_add, &op_mul, &op_sub, &op_div, NULL -}; -const static struct rules rules_3388 = { - ops_3388, true -}; - -/* - * A still more permissive rule set usable for the four-4s problem - * and similar things. Permits concatenation. - */ -const static struct operation *const ops_four4s[] = { - &op_add, &op_mul, &op_sub, &op_div, &op_concat, NULL -}; -const static struct rules rules_four4s = { - ops_four4s, true -}; - -/* - * The most permissive ruleset I can think of. Permits - * exponentiation, and also silly unary operators like factorials. - */ -const static struct operation *const ops_anythinggoes[] = { - &op_add, &op_mul, &op_sub, &op_div, &op_concat, &op_exp, &op_factorial, - &op_decimal, &op_recur, &op_root, &op_perc, &op_gamma, &op_sqrt, NULL -}; -const static struct rules rules_anythinggoes = { - ops_anythinggoes, true -}; - -#define ratcmp(a,op,b) ( (long long)(a)[0] * (b)[1] op \ - (long long)(b)[0] * (a)[1] ) - -static int addtoset(struct set *set, int newnumber[2]) -{ - int i, j; - - /* Find where we want to insert the new number */ - for (i = 0; i < set->nnumbers && - ratcmp(set->numbers+2*i, <, newnumber); i++); - - /* Move everything else up */ - for (j = set->nnumbers; j > i; j--) { - set->numbers[2*j] = set->numbers[2*j-2]; - set->numbers[2*j+1] = set->numbers[2*j-1]; - } - - /* Insert the new number */ - set->numbers[2*i] = newnumber[0]; - set->numbers[2*i+1] = newnumber[1]; - - set->nnumbers++; - - return i; -} - -#define ensure(array, size, newlen, type) do { \ - if ((newlen) > (size)) { \ - (size) = (newlen) + 512; \ - (array) = sresize((array), (size), type); \ - } \ -} while (0) - -static int setcmp(void *av, void *bv) -{ - struct set *a = (struct set *)av; - struct set *b = (struct set *)bv; - int i; - - if (a->nnumbers < b->nnumbers) - return -1; - else if (a->nnumbers > b->nnumbers) - return +1; - - if (a->flags < b->flags) - return -1; - else if (a->flags > b->flags) - return +1; - - for (i = 0; i < a->nnumbers; i++) { - if (ratcmp(a->numbers+2*i, <, b->numbers+2*i)) - return -1; - else if (ratcmp(a->numbers+2*i, >, b->numbers+2*i)) - return +1; - } - - return 0; -} - -static int outputcmp(void *av, void *bv) -{ - struct output *a = (struct output *)av; - struct output *b = (struct output *)bv; - - if (a->number < b->number) - return -1; - else if (a->number > b->number) - return +1; - - return 0; -} - -static int outputfindcmp(void *av, void *bv) -{ - int *a = (int *)av; - struct output *b = (struct output *)bv; - - if (*a < b->number) - return -1; - else if (*a > b->number) - return +1; - - return 0; -} - -static void addset(struct sets *s, struct set *set, int multiple, - struct set *prev, int pa, int po, int pb, int pr) -{ - struct set *s2; - int npaths = (prev ? prev->npaths : 1); - - assert(set == s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN); - s2 = add234(s->settree, set); - if (s2 == set) { - /* - * New set added to the tree. - */ - set->a.prev = prev; - set->a.pa = pa; - set->a.po = po; - set->a.pb = pb; - set->a.pr = pr; - set->npaths = npaths; - s->nsets++; - s->nnumbers += 2 * set->nnumbers; - set->as = NULL; - set->nas = set->assize = 0; - } else { - /* - * Rediscovered an existing set. Update its npaths. - */ - s2->npaths += npaths; - /* - * And optionally enter it as an additional ancestor. - */ - if (multiple) { - if (s2->nas >= s2->assize) { - s2->assize = s2->nas * 3 / 2 + 4; - s2->as = sresize(s2->as, s2->assize, struct ancestor); - } - s2->as[s2->nas].prev = prev; - s2->as[s2->nas].pa = pa; - s2->as[s2->nas].po = po; - s2->as[s2->nas].pb = pb; - s2->as[s2->nas].pr = pr; - s2->nas++; - } - } -} - -static struct set *newset(struct sets *s, int nnumbers, int flags) -{ - struct set *sn; - - ensure(s->setlists, s->setlistsize, s->nsets/SETLISTLEN+1, struct set *); - while (s->nsetlists <= s->nsets / SETLISTLEN) - s->setlists[s->nsetlists++] = snewn(SETLISTLEN, struct set); - sn = s->setlists[s->nsets / SETLISTLEN] + s->nsets % SETLISTLEN; - - if (s->nnumbers + nnumbers * 2 > s->nnumberlists * NUMBERLISTLEN) - s->nnumbers = s->nnumberlists * NUMBERLISTLEN; - ensure(s->numberlists, s->numberlistsize, - s->nnumbers/NUMBERLISTLEN+1, int *); - while (s->nnumberlists <= s->nnumbers / NUMBERLISTLEN) - s->numberlists[s->nnumberlists++] = snewn(NUMBERLISTLEN, int); - sn->numbers = s->numberlists[s->nnumbers / NUMBERLISTLEN] + - s->nnumbers % NUMBERLISTLEN; - - /* - * Start the set off empty. - */ - sn->nnumbers = 0; - - sn->flags = flags; - - return sn; -} - -static int addoutput(struct sets *s, struct set *ss, int index, int *n) -{ - struct output *o, *o2; - - /* - * Target numbers are always integers. - */ - if (ss->numbers[2*index+1] != 1) - return false; - - ensure(s->outputlists, s->outputlistsize, s->noutputs/OUTPUTLISTLEN+1, - struct output *); - while (s->noutputlists <= s->noutputs / OUTPUTLISTLEN) - s->outputlists[s->noutputlists++] = snewn(OUTPUTLISTLEN, - struct output); - o = s->outputlists[s->noutputs / OUTPUTLISTLEN] + - s->noutputs % OUTPUTLISTLEN; - - o->number = ss->numbers[2*index]; - o->set = ss; - o->index = index; - o->npaths = ss->npaths; - o2 = add234(s->outputtree, o); - if (o2 != o) { - o2->npaths += o->npaths; - } else { - s->noutputs++; - } - *n = o->number; - return true; -} - -static struct sets *do_search(int ninputs, int *inputs, - const struct rules *rules, int *target, - int debug, int multiple) -{ - struct sets *s; - struct set *sn; - int qpos, i; - const struct operation *const *ops = rules->ops; - - s = snew(struct sets); - s->setlists = NULL; - s->nsets = s->nsetlists = s->setlistsize = 0; - s->numberlists = NULL; - s->nnumbers = s->nnumberlists = s->numberlistsize = 0; - s->outputlists = NULL; - s->noutputs = s->noutputlists = s->outputlistsize = 0; - s->settree = newtree234(setcmp); - s->outputtree = newtree234(outputcmp); - s->ops = ops; - - /* - * Start with the input set. - */ - sn = newset(s, ninputs, SETFLAG_CONCAT); - for (i = 0; i < ninputs; i++) { - int newnumber[2]; - newnumber[0] = inputs[i]; - newnumber[1] = 1; - addtoset(sn, newnumber); - } - addset(s, sn, multiple, NULL, 0, 0, 0, 0); - - /* - * Now perform the breadth-first search: keep looping over sets - * until we run out of steam. - */ - qpos = 0; - while (qpos < s->nsets) { - struct set *ss = s->setlists[qpos / SETLISTLEN] + qpos % SETLISTLEN; - struct set *sn; - int i, j, k, m; - - if (debug) { - int i; - printf("processing set:"); - for (i = 0; i < ss->nnumbers; i++) { - printf(" %d", ss->numbers[2*i]); - if (ss->numbers[2*i+1] != 1) - printf("/%d", ss->numbers[2*i+1]); - } - printf("\n"); - } - - /* - * Record all the valid output numbers in this state. We - * can always do this if there's only one number in the - * state; otherwise, we can only do it if we aren't - * required to use all the numbers in coming to our answer. - */ - if (ss->nnumbers == 1 || !rules->use_all) { - for (i = 0; i < ss->nnumbers; i++) { - int n; - - if (addoutput(s, ss, i, &n) && target && n == *target) - return s; - } - } - - /* - * Try every possible operation from this state. - */ - for (k = 0; ops[k] && ops[k]->perform; k++) { - if ((ops[k]->flags & OPFLAG_NEEDS_CONCAT) && - !(ss->flags & SETFLAG_CONCAT)) - continue; /* can't use this operation here */ - for (i = 0; i < ss->nnumbers; i++) { - int jlimit = (ops[k]->flags & OPFLAG_UNARY ? 1 : ss->nnumbers); - for (j = 0; j < jlimit; j++) { - int n[2], newnn = ss->nnumbers; - int pa, po, pb, pr; - - if (!(ops[k]->flags & OPFLAG_UNARY)) { - if (i == j) - continue; /* can't combine a number with itself */ - if (i > j && ops[k]->commutes) - continue; /* no need to do this both ways round */ - newnn--; - } - if (!ops[k]->perform(ss->numbers+2*i, ss->numbers+2*j, n)) - continue; /* operation failed */ - - sn = newset(s, newnn, ss->flags); - - if (!(ops[k]->flags & OPFLAG_KEEPS_CONCAT)) - sn->flags &= ~SETFLAG_CONCAT; - - for (m = 0; m < ss->nnumbers; m++) { - if (m == i || (!(ops[k]->flags & OPFLAG_UNARY) && - m == j)) - continue; - sn->numbers[2*sn->nnumbers] = ss->numbers[2*m]; - sn->numbers[2*sn->nnumbers + 1] = ss->numbers[2*m + 1]; - sn->nnumbers++; - } - pa = i; - if (ops[k]->flags & OPFLAG_UNARY) - pb = sn->nnumbers+10; - else - pb = j; - po = k; - pr = addtoset(sn, n); - addset(s, sn, multiple, ss, pa, po, pb, pr); - if (debug) { - int i; - if (ops[k]->flags & OPFLAG_UNARYPREFIX) - printf(" %s %d ->", ops[po]->dbgtext, pa); - else if (ops[k]->flags & OPFLAG_UNARY) - printf(" %d %s ->", pa, ops[po]->dbgtext); - else - printf(" %d %s %d ->", pa, ops[po]->dbgtext, pb); - for (i = 0; i < sn->nnumbers; i++) { - printf(" %d", sn->numbers[2*i]); - if (sn->numbers[2*i+1] != 1) - printf("/%d", sn->numbers[2*i+1]); - } - printf("\n"); - } - } - } - } - - qpos++; - } - - return s; -} - -static void free_sets(struct sets *s) -{ - int i; - - freetree234(s->settree); - freetree234(s->outputtree); - for (i = 0; i < s->nsetlists; i++) - sfree(s->setlists[i]); - sfree(s->setlists); - for (i = 0; i < s->nnumberlists; i++) - sfree(s->numberlists[i]); - sfree(s->numberlists); - for (i = 0; i < s->noutputlists; i++) - sfree(s->outputlists[i]); - sfree(s->outputlists); - sfree(s); -} - -/* - * Print a text formula for producing a given output. - */ -void print_recurse(struct sets *s, struct set *ss, int pathindex, int index, - int priority, int assoc, int child); -void print_recurse_inner(struct sets *s, struct set *ss, - struct ancestor *a, int pathindex, int index, - int priority, int assoc, int child) -{ - if (a->prev && index != a->pr) { - int pi; - - /* - * This number was passed straight down from this set's - * predecessor. Find its index in the previous set and - * recurse to there. - */ - pi = index; - assert(pi != a->pr); - if (pi > a->pr) - pi--; - if (pi >= min(a->pa, a->pb)) { - pi++; - if (pi >= max(a->pa, a->pb)) - pi++; - } - print_recurse(s, a->prev, pathindex, pi, priority, assoc, child); - } else if (a->prev && index == a->pr && - s->ops[a->po]->display) { - /* - * This number was created by a displayed operator in the - * transition from this set to its predecessor. Hence we - * write an open paren, then recurse into the first - * operand, then write the operator, then the second - * operand, and finally close the paren. - */ - char *op; - int parens, thispri, thisassoc; - - /* - * Determine whether we need parentheses. - */ - thispri = s->ops[a->po]->priority; - thisassoc = s->ops[a->po]->assoc; - parens = (thispri < priority || - (thispri == priority && (assoc & child))); - - if (parens) - putchar('('); - - if (s->ops[a->po]->flags & OPFLAG_UNARYPREFIX) - for (op = s->ops[a->po]->text; *op; op++) - putchar(*op); - - if (s->ops[a->po]->flags & OPFLAG_FN) - putchar('('); - - print_recurse(s, a->prev, pathindex, a->pa, thispri, thisassoc, 1); - - if (s->ops[a->po]->flags & OPFLAG_FN) - putchar(')'); - - if (!(s->ops[a->po]->flags & OPFLAG_UNARYPREFIX)) - for (op = s->ops[a->po]->text; *op; op++) - putchar(*op); - - if (!(s->ops[a->po]->flags & OPFLAG_UNARY)) - print_recurse(s, a->prev, pathindex, a->pb, thispri, thisassoc, 2); - - if (parens) - putchar(')'); - } else { - /* - * This number is either an original, or something formed - * by a non-displayed operator (concatenation). Either way, - * we display it as is. - */ - printf("%d", ss->numbers[2*index]); - if (ss->numbers[2*index+1] != 1) - printf("/%d", ss->numbers[2*index+1]); - } -} -void print_recurse(struct sets *s, struct set *ss, int pathindex, int index, - int priority, int assoc, int child) -{ - if (!ss->a.prev || pathindex < ss->a.prev->npaths) { - print_recurse_inner(s, ss, &ss->a, pathindex, - index, priority, assoc, child); - } else { - int i; - pathindex -= ss->a.prev->npaths; - for (i = 0; i < ss->nas; i++) { - if (pathindex < ss->as[i].prev->npaths) { - print_recurse_inner(s, ss, &ss->as[i], pathindex, - index, priority, assoc, child); - break; - } - pathindex -= ss->as[i].prev->npaths; - } - } -} -void print(int pathindex, struct sets *s, struct output *o) -{ - print_recurse(s, o->set, pathindex, o->index, 0, 0, 0); -} - -/* - * gcc -g -O0 -o numgame numgame.c -I.. ../{malloc,tree234,nullfe}.c -lm - */ -int main(int argc, char **argv) -{ - int doing_opts = true; - const struct rules *rules = NULL; - char *pname = argv[0]; - int got_target = false, target = 0; - int numbers[10], nnumbers = 0; - int verbose = false; - int pathcounts = false; - int multiple = false; - int debug_bfs = false; - int got_range = false, rangemin = 0, rangemax = 0; - - struct output *o; - struct sets *s; - int i, start, limit; - - while (--argc) { - char *p = *++argv; - int c; - - if (doing_opts && *p == '-') { - p++; - - if (!strcmp(p, "-")) { - doing_opts = false; - continue; - } else if (*p == '-') { - p++; - if (!strcmp(p, "debug-bfs")) { - debug_bfs = true; - } else { - fprintf(stderr, "%s: option '--%s' not recognised\n", - pname, p); - } - } else while (p && *p) switch (c = *p++) { - case 'C': - rules = &rules_countdown; - break; - case 'B': - rules = &rules_3388; - break; - case 'D': - rules = &rules_four4s; - break; - case 'A': - rules = &rules_anythinggoes; - break; - case 'v': - verbose = true; - break; - case 'p': - pathcounts = true; - break; - case 'm': - multiple = true; - break; - case 't': - case 'r': - { - char *v; - if (*p) { - v = p; - p = NULL; - } else if (--argc) { - v = *++argv; - } else { - fprintf(stderr, "%s: option '-%c' expects an" - " argument\n", pname, c); - return 1; - } - switch (c) { - case 't': - got_target = true; - target = atoi(v); - break; - case 'r': - { - char *sep = strchr(v, '-'); - got_range = true; - if (sep) { - rangemin = atoi(v); - rangemax = atoi(sep+1); - } else { - rangemin = 0; - rangemax = atoi(v); - } - } - break; - } - } - break; - default: - fprintf(stderr, "%s: option '-%c' not" - " recognised\n", pname, c); - return 1; - } - } else { - if (nnumbers >= lenof(numbers)) { - fprintf(stderr, "%s: internal limit of %d numbers exceeded\n", - pname, (int)lenof(numbers)); - return 1; - } else { - numbers[nnumbers++] = atoi(p); - } - } - } - - if (!rules) { - fprintf(stderr, "%s: no rule set specified; use -C,-B,-D,-A\n", pname); - return 1; - } - - if (!nnumbers) { - fprintf(stderr, "%s: no input numbers specified\n", pname); - return 1; - } - - if (got_range) { - if (got_target) { - fprintf(stderr, "%s: only one of -t and -r may be specified\n", pname); - return 1; - } - if (rangemin >= rangemax) { - fprintf(stderr, "%s: range not sensible (%d - %d)\n", pname, rangemin, rangemax); - return 1; - } - } - - s = do_search(nnumbers, numbers, rules, (got_target ? &target : NULL), - debug_bfs, multiple); - - if (got_target) { - o = findrelpos234(s->outputtree, &target, outputfindcmp, - REL234_LE, &start); - if (!o) - start = -1; - o = findrelpos234(s->outputtree, &target, outputfindcmp, - REL234_GE, &limit); - if (!o) - limit = -1; - assert(start != -1 || limit != -1); - if (start == -1) - start = limit; - else if (limit == -1) - limit = start; - limit++; - } else if (got_range) { - if (!findrelpos234(s->outputtree, &rangemin, outputfindcmp, - REL234_GE, &start) || - !findrelpos234(s->outputtree, &rangemax, outputfindcmp, - REL234_LE, &limit)) { - printf("No solutions available in specified range %d-%d\n", rangemin, rangemax); - return 1; - } - limit++; - } else { - start = 0; - limit = count234(s->outputtree); - } - - for (i = start; i < limit; i++) { - char buf[256]; - - o = index234(s->outputtree, i); - - sprintf(buf, "%d", o->number); - - if (pathcounts) - sprintf(buf + strlen(buf), " [%d]", o->npaths); - - if (got_target || verbose) { - int j, npaths; - - if (multiple) - npaths = o->npaths; - else - npaths = 1; - - for (j = 0; j < npaths; j++) { - printf("%s = ", buf); - print(j, s, o); - putchar('\n'); - } - } else { - printf("%s\n", buf); - } - } - - free_sets(s); - - return 0; -} - -/* vim: set shiftwidth=4 tabstop=8: */ diff --git a/apps/plugins/puzzles/src/unfinished/path.c b/apps/plugins/puzzles/src/unfinished/path.c deleted file mode 100644 index fe5a47fd2a..0000000000 --- a/apps/plugins/puzzles/src/unfinished/path.c +++ /dev/null @@ -1,883 +0,0 @@ -/* - * Experimental grid generator for Nikoli's `Number Link' puzzle. - */ - -#include -#include -#include -#include "puzzles.h" - -/* - * 2005-07-08: This is currently a Path grid generator which will - * construct valid grids at a plausible speed. However, the grids - * are not of suitable quality to be used directly as puzzles. - * - * The basic strategy is to start with an empty grid, and - * repeatedly either (a) add a new path to it, or (b) extend one - * end of a path by one square in some direction and push other - * paths into new shapes in the process. The effect of this is that - * we are able to construct a set of paths which between them fill - * the entire grid. - * - * Quality issues: if we set the main loop to do (a) where possible - * and (b) only where necessary, we end up with a grid containing a - * few too many small paths, which therefore doesn't make for an - * interesting puzzle. If we reverse the priority so that we do (b) - * where possible and (a) only where necessary, we end up with some - * staggeringly interwoven grids with very very few separate paths, - * but the result of this is that there's invariably a solution - * other than the intended one which leaves many grid squares - * unfilled. There's also a separate problem which is that many - * grids have really boring and obvious paths in them, such as the - * entire bottom row of the grid being taken up by a single path. - * - * It's not impossible that a few tweaks might eliminate or reduce - * the incidence of boring paths, and might also find a happy - * medium between too many and too few. There remains the question - * of unique solutions, however. I fear there is no alternative but - * to write - somehow! - a solver. - * - * While I'm here, some notes on UI strategy for the parts of the - * puzzle implementation that _aren't_ the generator: - * - * - data model is to track connections between adjacent squares, - * so that you aren't limited to extending a path out from each - * number but can also mark sections of path which you know - * _will_ come in handy later. - * - * - user interface is to click in one square and drag to an - * adjacent one, thus creating a link between them. We can - * probably tolerate rapid mouse motion causing a drag directly - * to a square which is a rook move away, but any other rapid - * motion is ambiguous and probably the best option is to wait - * until the mouse returns to a square we know how to reach. - * - * - a drag causing the current path to backtrack has the effect - * of removing bits of it. - * - * - the UI should enforce at all times the constraint that at - * most two links can come into any square. - * - * - my Cunning Plan for actually implementing this: the game_ui - * contains a grid-sized array, which is copied from the current - * game_state on starting a drag. While a drag is active, the - * contents of the game_ui is adjusted with every mouse motion, - * and is displayed _in place_ of the game_state itself. On - * termination of a drag, the game_ui array is copied back into - * the new game_state (or rather, a string move is encoded which - * has precisely the set of link changes to cause that effect). - */ - -/* - * 2020-05-11: some thoughts on a solver. - * - * Consider this example puzzle, from Wikipedia: - * - * ---4--- - * -3--25- - * ---31-- - * ---5--- - * ------- - * --1---- - * 2---4-- - * - * The kind of deduction that a human wants to make here is: which way - * does the path between the 4s go? In particular, does it go round - * the left of the W-shaped cluster of endpoints, or round the right - * of it? It's clear at a glance that it must go to the right, because - * _any_ path between the 4s that goes to the left of that cluster, no - * matter what detailed direction it takes, will disconnect the - * remaining grid squares into two components, with the two 2s not in - * the same component. So we immediately know that the path between - * the 4s _must_ go round the right-hand side of the grid. - * - * How do you model that global and topological reasoning in a - * computer? - * - * The most plausible idea I've seen so far is to use fundamental - * groups. The fundamental group of loops based at a given point in a - * space is a free group, under loop concatenation and up to homotopy, - * generated by the loops that go in each direction around each hole - * in the space. In this case, the 'holes' are clues, or connected - * groups of clues. - * - * So you might be able to enumerate all the homotopy classes of paths - * between (say) the two 4s as follows. Start with any old path - * between them (say, find the first one that breadth-first search - * will give you). Choose one of the 4s to regard as the base point - * (arbitrarily). Then breadth-first search among the space of _paths_ - * by the following procedure. Given a candidate path, append to it - * each of the possible loops that starts from the base point, - * circumnavigates one clue cluster, and returns to the base point. - * The result will typically be a path that retraces its steps and - * self-intersects. Now adjust it homotopically so that it doesn't. If - * that can't be done, then we haven't generated a fresh candidate - * path; if it can, then we've got a new path that is not homotopic to - * any path we already had, so add it to our list and queue it up to - * become the starting point of this search later. - * - * The idea is that this should exhaustively enumerate, up to - * homotopy, the different ways in which the two 4s can connect to - * each other within the constraint that you have to actually fit the - * path non-self-intersectingly into this grid. Then you can keep a - * list of those homotopy classes in mind, and start ruling them out - * by techniques like the connectivity approach described above. - * Hopefully you end up narrowing down to few enough homotopy classes - * that you can deduce something concrete about actual squares of the - * grid - for example, here, that if the path between 4s has to go - * round the right, then we know some specific squares it must go - * through, so we can fill those in. And then, having filled in a - * piece of the middle of a path, you can now regard connecting the - * ultimate endpoints to that mid-section as two separate subproblems, - * so you've reduced to a simpler instance of the same puzzle. - * - * But I don't know whether all of this actually works. I more or less - * believe the process for enumerating elements of the free group; but - * I'm not as confident that when you find a group element that won't - * fit in the grid, you'll never have to consider its descendants in - * the BFS either. And I'm assuming that 'unwind the self-intersection - * homotopically' is a thing that can actually be turned into a - * sensible algorithm. - * - * -------- - * - * Another thing that might be needed is to characterise _which_ - * homotopy class a given path is in. - * - * For this I think it's sufficient to choose a collection of paths - * along the _edges_ of the square grid, each of which connects two of - * the holes in the grid (including the grid exterior, which counts as - * a huge hole), such that they form a spanning tree between the - * holes. Then assign each of those paths an orientation, so that - * crossing it in one direction counts as 'positive' and the other - * 'negative'. Now analyse a candidate path from one square to another - * by following it and noting down which of those paths it crosses in - * which direction, then simplifying the result like a free group word - * (i.e. adjacent + and - crossings of the same path cancel out). - * - * -------- - * - * If we choose those paths to be of minimal length, then we can get - * an upper bound on the number of homotopy classes by observing that - * you can't traverse any of those barriers more times than will fit - * non-self-intersectingly in the grid. That might be an alternative - * method of bounding the search through the fundamental group to only - * finitely many possibilities. - */ - -/* - * Standard notation for directions. - */ -#define L 0 -#define U 1 -#define R 2 -#define D 3 -#define DX(dir) ( (dir)==L ? -1 : (dir)==R ? +1 : 0) -#define DY(dir) ( (dir)==U ? -1 : (dir)==D ? +1 : 0) - -/* - * Perform a breadth-first search over a grid of squares with the - * colour of square (X,Y) given by grid[Y*w+X]. The search begins - * at (x,y), and finds all squares which are the same colour as - * (x,y) and reachable from it by orthogonal moves. On return: - * - dist[Y*w+X] gives the distance of (X,Y) from (x,y), or -1 if - * unreachable or a different colour - * - the returned value is the number of reachable squares, - * including (x,y) itself - * - list[0] up to list[returned value - 1] list those squares, in - * increasing order of distance from (x,y) (and in arbitrary - * order within that). - */ -static int bfs(int w, int h, int *grid, int x, int y, int *dist, int *list) -{ - int i, j, c, listsize, listdone; - - /* - * Start by clearing the output arrays. - */ - for (i = 0; i < w*h; i++) - dist[i] = list[i] = -1; - - /* - * Set up the initial list. - */ - listsize = 1; - listdone = 0; - list[0] = y*w+x; - dist[y*w+x] = 0; - c = grid[y*w+x]; - - /* - * Repeatedly process a square and add any extra squares to the - * end of list. - */ - while (listdone < listsize) { - i = list[listdone++]; - y = i / w; - x = i % w; - for (j = 0; j < 4; j++) { - int xx, yy, ii; - - xx = x + DX(j); - yy = y + DY(j); - ii = yy*w+xx; - - if (xx >= 0 && xx < w && yy >= 0 && yy < h && - grid[ii] == c && dist[ii] == -1) { - dist[ii] = dist[i] + 1; - assert(listsize < w*h); - list[listsize++] = ii; - } - } - } - - return listsize; -} - -struct genctx { - int w, h; - int *grid, *sparegrid, *sparegrid2, *sparegrid3; - int *dist, *list; - - int npaths, pathsize; - int *pathends, *sparepathends; /* 2*npaths entries */ - int *pathspare; /* npaths entries */ - int *extends; /* 8*npaths entries */ -}; - -static struct genctx *new_genctx(int w, int h) -{ - struct genctx *ctx = snew(struct genctx); - ctx->w = w; - ctx->h = h; - ctx->grid = snewn(w * h, int); - ctx->sparegrid = snewn(w * h, int); - ctx->sparegrid2 = snewn(w * h, int); - ctx->sparegrid3 = snewn(w * h, int); - ctx->dist = snewn(w * h, int); - ctx->list = snewn(w * h, int); - ctx->npaths = ctx->pathsize = 0; - ctx->pathends = ctx->sparepathends = ctx->pathspare = ctx->extends = NULL; - return ctx; -} - -static void free_genctx(struct genctx *ctx) -{ - sfree(ctx->grid); - sfree(ctx->sparegrid); - sfree(ctx->sparegrid2); - sfree(ctx->sparegrid3); - sfree(ctx->dist); - sfree(ctx->list); - sfree(ctx->pathends); - sfree(ctx->sparepathends); - sfree(ctx->pathspare); - sfree(ctx->extends); -} - -static int newpath(struct genctx *ctx) -{ - int n; - - n = ctx->npaths++; - if (ctx->npaths > ctx->pathsize) { - ctx->pathsize += 16; - ctx->pathends = sresize(ctx->pathends, ctx->pathsize*2, int); - ctx->sparepathends = sresize(ctx->sparepathends, ctx->pathsize*2, int); - ctx->pathspare = sresize(ctx->pathspare, ctx->pathsize, int); - ctx->extends = sresize(ctx->extends, ctx->pathsize*8, int); - } - return n; -} - -static int is_endpoint(struct genctx *ctx, int x, int y) -{ - int w = ctx->w, h = ctx->h, c; - - assert(x >= 0 && x < w && y >= 0 && y < h); - - c = ctx->grid[y*w+x]; - if (c < 0) - return false; /* empty square is not an endpoint! */ - assert(c >= 0 && c < ctx->npaths); - if (ctx->pathends[c*2] == y*w+x || ctx->pathends[c*2+1] == y*w+x) - return true; - return false; -} - -/* - * Tries to extend a path by one square in the given direction, - * pushing other paths around if necessary. Returns true on success - * or false on failure. - */ -static int extend_path(struct genctx *ctx, int path, int end, int direction) -{ - int w = ctx->w, h = ctx->h; - int x, y, xe, ye, cut; - int i, j, jp, n, first, last; - - assert(path >= 0 && path < ctx->npaths); - assert(end == 0 || end == 1); - - /* - * Find the endpoint of the path and the point we plan to - * extend it into. - */ - y = ctx->pathends[path * 2 + end] / w; - x = ctx->pathends[path * 2 + end] % w; - assert(x >= 0 && x < w && y >= 0 && y < h); - - xe = x + DX(direction); - ye = y + DY(direction); - if (xe < 0 || xe >= w || ye < 0 || ye >= h) - return false; /* could not extend in this direction */ - - /* - * We don't extend paths _directly_ into endpoints of other - * paths, although we don't mind too much if a knock-on effect - * of an extension is to push part of another path into a third - * path's endpoint. - */ - if (is_endpoint(ctx, xe, ye)) - return false; - - /* - * We can't extend a path back the way it came. - */ - if (ctx->grid[ye*w+xe] == path) - return false; - - /* - * Paths may not double back on themselves. Check if the new - * point is adjacent to any point of this path other than (x,y). - */ - for (j = 0; j < 4; j++) { - int xf, yf; - - xf = xe + DX(j); - yf = ye + DY(j); - - if (xf >= 0 && xf < w && yf >= 0 && yf < h && - (xf != x || yf != y) && ctx->grid[yf*w+xf] == path) - return false; - } - - /* - * Now we're convinced it's valid to _attempt_ the extension. - * It may still fail if we run out of space to push other paths - * into. - * - * So now we can set up our temporary data structures. We will - * need: - * - * - a spare copy of the grid on which to gradually move paths - * around (sparegrid) - * - * - a second spare copy with which to remember how paths - * looked just before being cut (sparegrid2). FIXME: is - * sparegrid2 necessary? right now it's never different from - * grid itself - * - * - a third spare copy with which to do the internal - * calculations involved in reconstituting a cut path - * (sparegrid3) - * - * - something to track which paths currently need - * reconstituting after being cut, and which have already - * been cut (pathspare) - * - * - a spare copy of pathends to store the altered states in - * (sparepathends) - */ - memcpy(ctx->sparegrid, ctx->grid, w*h*sizeof(int)); - memcpy(ctx->sparegrid2, ctx->grid, w*h*sizeof(int)); - memcpy(ctx->sparepathends, ctx->pathends, ctx->npaths*2*sizeof(int)); - for (i = 0; i < ctx->npaths; i++) - ctx->pathspare[i] = 0; /* 0=untouched, 1=broken, 2=fixed */ - - /* - * Working in sparegrid, actually extend the path. If it cuts - * another, begin a loop in which we restore any cut path by - * moving it out of the way. - */ - cut = ctx->sparegrid[ye*w+xe]; - ctx->sparegrid[ye*w+xe] = path; - ctx->sparepathends[path*2+end] = ye*w+xe; - ctx->pathspare[path] = 2; /* this one is sacrosanct */ - if (cut >= 0) { - assert(cut >= 0 && cut < ctx->npaths); - ctx->pathspare[cut] = 1; /* broken */ - - while (1) { - for (i = 0; i < ctx->npaths; i++) - if (ctx->pathspare[i] == 1) - break; - if (i == ctx->npaths) - break; /* we're done */ - - /* - * Path i needs restoring. So walk along its original - * track (as given in sparegrid2) and see where it's - * been cut. Where it has, surround the cut points in - * the same colour, without overwriting already-fixed - * paths. - */ - memcpy(ctx->sparegrid3, ctx->sparegrid, w*h*sizeof(int)); - n = bfs(w, h, ctx->sparegrid2, - ctx->pathends[i*2] % w, ctx->pathends[i*2] / w, - ctx->dist, ctx->list); - first = last = -1; -if (ctx->sparegrid3[ctx->pathends[i*2]] != i || - ctx->sparegrid3[ctx->pathends[i*2+1]] != i) return false;/* FIXME */ - for (j = 0; j < n; j++) { - jp = ctx->list[j]; - assert(ctx->dist[jp] == j); - assert(ctx->sparegrid2[jp] == i); - - /* - * Wipe out the original path in sparegrid. - */ - if (ctx->sparegrid[jp] == i) - ctx->sparegrid[jp] = -1; - - /* - * Be prepared to shorten the path at either end if - * the endpoints have been stomped on. - */ - if (ctx->sparegrid3[jp] == i) { - if (first < 0) - first = jp; - last = jp; - } - - if (ctx->sparegrid3[jp] != i) { - int jx = jp % w, jy = jp / w; - int dx, dy; - for (dy = -1; dy <= +1; dy++) - for (dx = -1; dx <= +1; dx++) { - int newp, newv; - if (!dy && !dx) - continue; /* central square */ - if (jx+dx < 0 || jx+dx >= w || - jy+dy < 0 || jy+dy >= h) - continue; /* out of range */ - newp = (jy+dy)*w+(jx+dx); - newv = ctx->sparegrid3[newp]; - if (newv >= 0 && (newv == i || - ctx->pathspare[newv] == 2)) - continue; /* can't use this square */ - ctx->sparegrid3[newp] = i; - } - } - } - - if (first < 0 || last < 0) - return false; /* path is completely wiped out! */ - - /* - * Now we've covered sparegrid3 in possible squares for - * the new layout of path i. Find the actual layout - * we're going to use by bfs: we want the shortest path - * from one endpoint to the other. - */ - n = bfs(w, h, ctx->sparegrid3, first % w, first / w, - ctx->dist, ctx->list); - if (ctx->dist[last] < 2) { - /* - * Either there is no way to get between the path's - * endpoints, or the remaining endpoints simply - * aren't far enough apart to make the path viable - * any more. This means the entire push operation - * has failed. - */ - return false; - } - - /* - * Write the new path into sparegrid. Also save the new - * endpoint locations, in case they've changed. - */ - jp = last; - j = ctx->dist[jp]; - while (1) { - int d; - - if (ctx->sparegrid[jp] >= 0) { - if (ctx->pathspare[ctx->sparegrid[jp]] == 2) - return false; /* somehow we've hit a fixed path */ - ctx->pathspare[ctx->sparegrid[jp]] = 1; /* broken */ - } - ctx->sparegrid[jp] = i; - - if (j == 0) - break; - - /* - * Now look at the neighbours of jp to find one - * which has dist[] one less. - */ - for (d = 0; d < 4; d++) { - int jx = (jp % w) + DX(d), jy = (jp / w) + DY(d); - if (jx >= 0 && jx < w && jy >= 0 && jy < w && - ctx->dist[jy*w+jx] == j-1) { - jp = jy*w+jx; - j--; - break; - } - } - assert(d < 4); - } - - ctx->sparepathends[i*2] = first; - ctx->sparepathends[i*2+1] = last; -//printf("new ends of path %d: %d,%d\n", i, first, last); - ctx->pathspare[i] = 2; /* fixed */ - } - } - - /* - * If we got here, the extension was successful! - */ - memcpy(ctx->grid, ctx->sparegrid, w*h*sizeof(int)); - memcpy(ctx->pathends, ctx->sparepathends, ctx->npaths*2*sizeof(int)); - return true; -} - -/* - * Tries to add a new path to the grid. - */ -static int add_path(struct genctx *ctx, random_state *rs) -{ - int w = ctx->w, h = ctx->h; - int i, ii, n; - - /* - * Our strategy is: - * - randomly choose an empty square in the grid - * - do a BFS from that point to find a long path starting - * from it - * - if we run out of viable empty squares, return failure. - */ - - /* - * Use `sparegrid' to collect a list of empty squares. - */ - n = 0; - for (i = 0; i < w*h; i++) - if (ctx->grid[i] == -1) - ctx->sparegrid[n++] = i; - - /* - * Shuffle the grid. - */ - for (i = n; i-- > 1 ;) { - int k = random_upto(rs, i+1); - if (k != i) { - int t = ctx->sparegrid[i]; - ctx->sparegrid[i] = ctx->sparegrid[k]; - ctx->sparegrid[k] = t; - } - } - - /* - * Loop over it trying to add paths. This looks like a - * horrifying N^4 algorithm (that is, (w*h)^2), but I predict - * that in fact the worst case will very rarely arise because - * when there's lots of grid space an attempt will succeed very - * quickly. - */ - for (ii = 0; ii < n; ii++) { - int i = ctx->sparegrid[ii]; - int y = i / w, x = i % w, nsq; - int r, c, j; - - /* - * BFS from here to find long paths. - */ - nsq = bfs(w, h, ctx->grid, x, y, ctx->dist, ctx->list); - - /* - * If there aren't any long enough, give up immediately. - */ - assert(nsq > 0); /* must be the start square at least! */ - if (ctx->dist[ctx->list[nsq-1]] < 3) - continue; - - /* - * Find the first viable endpoint in ctx->list (i.e. the - * first point with distance at least three). I could - * binary-search for this, but that would be O(log N) - * whereas in fact I can get a constant time bound by just - * searching up from the start - after all, there can be at - * most 13 points at _less_ than distance 3 from the - * starting one! - */ - for (j = 0; j < nsq; j++) - if (ctx->dist[ctx->list[j]] >= 3) - break; - assert(j < nsq); /* we tested above that there was one */ - - /* - * Now we know that any element of `list' between j and nsq - * would be valid in principle. However, we want a few long - * paths rather than many small ones, so select only those - * elements which are either the maximum length or one - * below it. - */ - while (ctx->dist[ctx->list[j]] + 1 < ctx->dist[ctx->list[nsq-1]]) - j++; - r = j + random_upto(rs, nsq - j); - j = ctx->list[r]; - - /* - * And that's our endpoint. Mark the new path on the grid. - */ - c = newpath(ctx); - ctx->pathends[c*2] = i; - ctx->pathends[c*2+1] = j; - ctx->grid[j] = c; - while (j != i) { - int d, np, index, pts[4]; - np = 0; - for (d = 0; d < 4; d++) { - int xn = (j % w) + DX(d), yn = (j / w) + DY(d); - if (xn >= 0 && xn < w && yn >= 0 && yn < w && - ctx->dist[yn*w+xn] == ctx->dist[j] - 1) - pts[np++] = yn*w+xn; - } - if (np > 1) - index = random_upto(rs, np); - else - index = 0; - j = pts[index]; - ctx->grid[j] = c; - } - - return true; - } - - return false; -} - -/* - * The main grid generation loop. - */ -static void gridgen_mainloop(struct genctx *ctx, random_state *rs) -{ - int w = ctx->w, h = ctx->h; - int i, n; - - /* - * The generation algorithm doesn't always converge. Loop round - * until it does. - */ - while (1) { - for (i = 0; i < w*h; i++) - ctx->grid[i] = -1; - ctx->npaths = 0; - - while (1) { - /* - * See if the grid is full. - */ - for (i = 0; i < w*h; i++) - if (ctx->grid[i] < 0) - break; - if (i == w*h) - return; - -#ifdef GENERATION_DIAGNOSTICS - { - int x, y; - for (y = 0; y < h; y++) { - printf("|"); - for (x = 0; x < w; x++) { - if (ctx->grid[y*w+x] >= 0) - printf("%2d", ctx->grid[y*w+x]); - else - printf(" ."); - } - printf(" |\n"); - } - } -#endif - /* - * Try adding a path. - */ - if (add_path(ctx, rs)) { -#ifdef GENERATION_DIAGNOSTICS - printf("added path\n"); -#endif - continue; - } - - /* - * Try extending a path. First list all the possible - * extensions. - */ - for (i = 0; i < ctx->npaths * 8; i++) - ctx->extends[i] = i; - n = i; - - /* - * Then shuffle the list. - */ - for (i = n; i-- > 1 ;) { - int k = random_upto(rs, i+1); - if (k != i) { - int t = ctx->extends[i]; - ctx->extends[i] = ctx->extends[k]; - ctx->extends[k] = t; - } - } - - /* - * Now try each one in turn until one works. - */ - for (i = 0; i < n; i++) { - int p, d, e; - p = ctx->extends[i]; - d = p % 4; - p /= 4; - e = p % 2; - p /= 2; - -#ifdef GENERATION_DIAGNOSTICS - printf("trying to extend path %d end %d (%d,%d) in dir %d\n", p, e, - ctx->pathends[p*2+e] % w, - ctx->pathends[p*2+e] / w, d); -#endif - if (extend_path(ctx, p, e, d)) { -#ifdef GENERATION_DIAGNOSTICS - printf("extended path %d end %d (%d,%d) in dir %d\n", p, e, - ctx->pathends[p*2+e] % w, - ctx->pathends[p*2+e] / w, d); -#endif - break; - } - } - - if (i < n) - continue; - - break; - } - } -} - -/* - * Wrapper function which deals with the boring bits such as - * removing the solution from the generated grid, shuffling the - * numeric labels and creating/disposing of the context structure. - */ -static int *gridgen(int w, int h, random_state *rs) -{ - struct genctx *ctx; - int *ret; - int i; - - ctx = new_genctx(w, h); - - gridgen_mainloop(ctx, rs); - - /* - * There is likely to be an ordering bias in the numbers - * (longer paths on lower numbers due to there having been more - * grid space when laying them down). So we must shuffle the - * numbers. We use ctx->pathspare for this. - * - * This is also as good a time as any to shift to numbering - * from 1, for display to the user. - */ - for (i = 0; i < ctx->npaths; i++) - ctx->pathspare[i] = i+1; - for (i = ctx->npaths; i-- > 1 ;) { - int k = random_upto(rs, i+1); - if (k != i) { - int t = ctx->pathspare[i]; - ctx->pathspare[i] = ctx->pathspare[k]; - ctx->pathspare[k] = t; - } - } - - /* FIXME: remove this at some point! */ - { - int y, x; - for (y = 0; y < h; y++) { - printf("|"); - for (x = 0; x < w; x++) { - assert(ctx->grid[y*w+x] >= 0); - printf("%2d", ctx->pathspare[ctx->grid[y*w+x]]); - } - printf(" |\n"); - } - printf("\n"); - } - - /* - * Clear the grid, and write in just the endpoints. - */ - for (i = 0; i < w*h; i++) - ctx->grid[i] = 0; - for (i = 0; i < ctx->npaths; i++) { - ctx->grid[ctx->pathends[i*2]] = - ctx->grid[ctx->pathends[i*2+1]] = ctx->pathspare[i]; - } - - ret = ctx->grid; - ctx->grid = NULL; - - free_genctx(ctx); - - return ret; -} - -#ifdef TEST_GEN - -#define TEST_GENERAL - -int main(void) -{ - int w = 10, h = 8; - random_state *rs = random_init("12345", 5); - int x, y, i, *grid; - - for (i = 0; i < 10; i++) { - grid = gridgen(w, h, rs); - - for (y = 0; y < h; y++) { - printf("|"); - for (x = 0; x < w; x++) { - if (grid[y*w+x] > 0) - printf("%2d", grid[y*w+x]); - else - printf(" ."); - } - printf(" |\n"); - } - printf("\n"); - - sfree(grid); - } - - return 0; -} -#endif - -#ifdef TEST_GENERAL -#include - -void fatal(const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "fatal error: "); - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - fprintf(stderr, "\n"); - exit(1); -} -#endif diff --git a/apps/plugins/puzzles/src/unfinished/separate.R b/apps/plugins/puzzles/src/unfinished/separate.R deleted file mode 100644 index f861c8f4fe..0000000000 --- a/apps/plugins/puzzles/src/unfinished/separate.R +++ /dev/null @@ -1,21 +0,0 @@ -# -*- makefile -*- - -SEPARATE_EXTRA = divvy dsf - -separate : [X] GTK COMMON separate SEPARATE_EXTRA separate-icon|no-icon - -separate : [G] WINDOWS COMMON separate SEPARATE_EXTRA separate.res|noicon.res - -ALL += separate[COMBINED] SEPARATE_EXTRA - -!begin am gtk -GAMES += separate -!end - -!begin >list.c - A(separate) \ -!end - -!begin >gamedesc.txt -separate:separate.exe:Separate:Rectangle-dividing puzzle:Partition the grid into regions containing one of each letter. -!end diff --git a/apps/plugins/puzzles/src/unfinished/separate.c b/apps/plugins/puzzles/src/unfinished/separate.c deleted file mode 100644 index 39243afb92..0000000000 --- a/apps/plugins/puzzles/src/unfinished/separate.c +++ /dev/null @@ -1,870 +0,0 @@ -/* - * separate.c: Implementation of `Block Puzzle', a Japanese-only - * Nikoli puzzle seen at - * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/ - * - * It's difficult to be absolutely sure of the rules since online - * Japanese translators are so bad, but looking at the sample - * puzzle it seems fairly clear that the rules of this one are - * very simple. You have an mxn grid in which every square - * contains a letter, there are k distinct letters with k dividing - * mn, and every letter occurs the same number of times; your aim - * is to find a partition of the grid into disjoint k-ominoes such - * that each k-omino contains exactly one of each letter. - * - * (It may be that Nikoli always have m,n,k equal to one another. - * However, I don't see that that's critical to the puzzle; k|mn - * is the only really important constraint, and even that could - * probably be dispensed with if some squares were marked as - * unused.) - */ - -/* - * Current status: only the solver/generator is yet written, and - * although working in principle it's _very_ slow. It generates - * 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and - * 7x7n7 only with a serious strain. I haven't dared try it higher - * than that yet. - * - * One idea to speed it up is to implement more of the solver. - * Ideas I've so far had include: - * - * - Generalise the deduction currently expressed as `an - * undersized chain with only one direction to extend must take - * it'. More generally, the deduction should say `if all the - * possible k-ominoes containing a given chain also contain - * square x, then mark square x as part of that k-omino'. - * + For example, consider this case: - * - * a ? b This represents the top left of a board; the letters - * ? ? ? a,b,c do not represent the letters used in the puzzle, - * c ? ? but indicate that those three squares are known to be - * of different ominoes. Now if k >= 4, we can immediately - * deduce that the square midway between b and c belongs to the - * same omino as a, because there is no way we can make a 4-or- - * more-omino containing a which does not also contain that square. - * (Most easily seen by imagining cutting that square out of the - * grid; then, clearly, the omino containing a has only two - * squares to expand into, and needs at least three.) - * - * The key difficulty with this mode of reasoning is - * identifying such squares. I can't immediately think of a - * simple algorithm for finding them on a wholesale basis. - * - * - Bfs out from a chain looking for the letters it lacks. For - * example, in this situation (top three rows of a 7x7n7 grid): - * - * +-----------+-+ - * |E-A-F-B-C D|D| - * +------- || - * |E-C-G-D G|G E| - * +-+--- | - * |E|E G A B F A| - * - * In this situation we can be sure that the top left chain - * E-A-F-B-C does extend rightwards to the D, because there is - * no other D within reach of that chain. Note also that the - * bfs can skip squares which are known to belong to other - * ominoes than this one. - * - * (This deduction, I fear, should only be used in an - * emergency, because it relies on _all_ squares within range - * of the bfs having particular values and so using it during - * incremental generation rather nails down a lot of the grid.) - * - * It's conceivable that another thing we could do would be to - * increase the flexibility in the grid generator: instead of - * nailing down the _value_ of any square depended on, merely nail - * down its equivalence to other squares. Unfortunately this turns - * the letter-selection phase of generation into a general graph - * colouring problem (we must draw a graph with equivalence - * classes of squares as the vertices, and an edge between any two - * vertices representing equivalence classes which contain squares - * that share an omino, and then k-colour the result) and hence - * requires recursion, which bodes ill for something we're doing - * that many times per generation. - * - * I suppose a simple thing I could try would be tuning the retry - * count, just in case it's set too high or too low for efficient - * generation. - */ - -#include -#include -#include -#include -#include -#include - -#include "puzzles.h" - -enum { - COL_BACKGROUND, - NCOLOURS -}; - -struct game_params { - int w, h, k; -}; - -struct game_state { - int FIXME; -}; - -static game_params *default_params(void) -{ - game_params *ret = snew(game_params); - - ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */ - - return ret; -} - -static bool game_fetch_preset(int i, char **name, game_params **params) -{ - return false; -} - -static void free_params(game_params *params) -{ - sfree(params); -} - -static game_params *dup_params(const game_params *params) -{ - game_params *ret = snew(game_params); - *ret = *params; /* structure copy */ - return ret; -} - -static void decode_params(game_params *params, char const *string) -{ - params->w = params->h = params->k = atoi(string); - while (*string && isdigit((unsigned char)*string)) string++; - if (*string == 'x') { - string++; - params->h = atoi(string); - while (*string && isdigit((unsigned char)*string)) string++; - } - if (*string == 'n') { - string++; - params->k = atoi(string); - while (*string && isdigit((unsigned char)*string)) string++; - } -} - -static char *encode_params(const game_params *params, bool full) -{ - char buf[256]; - sprintf(buf, "%dx%dn%d", params->w, params->h, params->k); - return dupstr(buf); -} - -static config_item *game_configure(const game_params *params) -{ - return NULL; -} - -static game_params *custom_params(const config_item *cfg) -{ - return NULL; -} - -static const char *validate_params(const game_params *params, bool full) -{ - return NULL; -} - -/* ---------------------------------------------------------------------- - * Solver and generator. - */ - -struct solver_scratch { - int w, h, k; - - /* - * Tracks connectedness between squares. - */ - int *dsf; - - /* - * size[dsf_canonify(dsf, yx)] tracks the size of the - * connected component containing yx. - */ - int *size; - - /* - * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not - * the connected component containing yx includes letter i. If - * the value is -1, it doesn't; otherwise its value is the - * index in the main grid of the square which contributes that - * letter to the component. - */ - int *contents; - - /* - * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)] - * tracks whether or not the connected components containing - * yx1 and yx2 are known to be distinct. - */ - bool *disconnect; - - /* - * Temporary space used only inside particular solver loops. - */ - int *tmp; -}; - -struct solver_scratch *solver_scratch_new(int w, int h, int k) -{ - int wh = w*h; - struct solver_scratch *sc = snew(struct solver_scratch); - - sc->w = w; - sc->h = h; - sc->k = k; - - sc->dsf = snew_dsf(wh); - sc->size = snewn(wh, int); - sc->contents = snewn(wh * k, int); - sc->disconnect = snewn(wh*wh, bool); - sc->tmp = snewn(wh, int); - - return sc; -} - -void solver_scratch_free(struct solver_scratch *sc) -{ - sfree(sc->dsf); - sfree(sc->size); - sfree(sc->contents); - sfree(sc->disconnect); - sfree(sc->tmp); - sfree(sc); -} - -void solver_connect(struct solver_scratch *sc, int yx1, int yx2) -{ - int w = sc->w, h = sc->h, k = sc->k; - int wh = w*h; - int i, yxnew; - - yx1 = dsf_canonify(sc->dsf, yx1); - yx2 = dsf_canonify(sc->dsf, yx2); - assert(yx1 != yx2); - - /* - * To connect two components together into a bigger one, we - * start by merging them in the dsf itself. - */ - dsf_merge(sc->dsf, yx1, yx2); - yxnew = dsf_canonify(sc->dsf, yx2); - - /* - * The size of the new component is the sum of the sizes of the - * old ones. - */ - sc->size[yxnew] = sc->size[yx1] + sc->size[yx2]; - - /* - * The contents bitmap of the new component is the union of the - * contents of the old ones. - * - * Given two numbers at most one of which is not -1, we can - * find the other one by adding the two and adding 1; this - * will yield -1 if both were -1 to begin with, otherwise the - * other. - * - * (A neater approach would be to take their bitwise AND, but - * this is unfortunately not well-defined standard C when done - * to signed integers.) - */ - for (i = 0; i < k; i++) { - assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0); - sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] + - sc->contents[yx2*k+i] + 1); - } - - /* - * We must combine the rows _and_ the columns in the disconnect - * matrix. - */ - for (i = 0; i < wh; i++) - sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] || - sc->disconnect[yx2*wh+i]); - for (i = 0; i < wh; i++) - sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] || - sc->disconnect[i*wh+yx2]); -} - -void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2) -{ - int w = sc->w, h = sc->h; - int wh = w*h; - - yx1 = dsf_canonify(sc->dsf, yx1); - yx2 = dsf_canonify(sc->dsf, yx2); - assert(yx1 != yx2); - assert(!sc->disconnect[yx1*wh+yx2]); - assert(!sc->disconnect[yx2*wh+yx1]); - - /* - * Mark the components as disconnected from each other in the - * disconnect matrix. - */ - sc->disconnect[yx1*wh+yx2] = true; - sc->disconnect[yx2*wh+yx1] = true; -} - -void solver_init(struct solver_scratch *sc) -{ - int w = sc->w, h = sc->h; - int wh = w*h; - int i; - - /* - * Set up most of the scratch space. We don't set up the - * contents array, however, because this will change if we - * adjust the letter arrangement and re-run the solver. - */ - dsf_init(sc->dsf, wh); - for (i = 0; i < wh; i++) sc->size[i] = 1; - memset(sc->disconnect, 0, wh*wh * sizeof(bool)); -} - -int solver_attempt(struct solver_scratch *sc, const unsigned char *grid, - bool *gen_lock) -{ - int w = sc->w, h = sc->h, k = sc->k; - int wh = w*h; - int i, x, y; - bool done_something_overall = false; - - /* - * Set up the contents array from the grid. - */ - for (i = 0; i < wh*k; i++) - sc->contents[i] = -1; - for (i = 0; i < wh; i++) - sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i; - - while (1) { - bool done_something = false; - - /* - * Go over the grid looking for reasons to add to the - * disconnect matrix. We're after pairs of squares which: - * - * - are adjacent in the grid - * - belong to distinct dsf components - * - their components are not already marked as - * disconnected - * - their components share a letter in common. - */ - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - int dir; - for (dir = 0; dir < 2; dir++) { - int x2 = x + dir, y2 = y + 1 - dir; - int yx = y*w+x, yx2 = y2*w+x2; - - if (x2 >= w || y2 >= h) - continue; /* one square is outside the grid */ - - yx = dsf_canonify(sc->dsf, yx); - yx2 = dsf_canonify(sc->dsf, yx2); - if (yx == yx2) - continue; /* same dsf component */ - - if (sc->disconnect[yx*wh+yx2]) - continue; /* already known disconnected */ - - for (i = 0; i < k; i++) - if (sc->contents[yx*k+i] >= 0 && - sc->contents[yx2*k+i] >= 0) - break; - if (i == k) - continue; /* no letter in common */ - - /* - * We've found one. Mark yx and yx2 as - * disconnected from each other. - */ -#ifdef SOLVER_DIAGNOSTICS - printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i); -#endif - solver_disconnect(sc, yx, yx2); - done_something = done_something_overall = true; - - /* - * We have just made a deduction which hinges - * on two particular grid squares being the - * same. If we are feeding back to a generator - * loop, we must therefore mark those squares - * as fixed in the generator, so that future - * rearrangement of the grid will not break - * the information on which we have already - * based deductions. - */ - if (gen_lock) { - gen_lock[sc->contents[yx*k+i]] = true; - gen_lock[sc->contents[yx2*k+i]] = true; - } - } - } - } - - /* - * Now go over the grid looking for dsf components which - * are below maximum size and only have one way to extend, - * and extending them. - */ - for (i = 0; i < wh; i++) - sc->tmp[i] = -1; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - int yx = dsf_canonify(sc->dsf, y*w+x); - int dir; - - if (sc->size[yx] == k) - continue; - - for (dir = 0; dir < 4; dir++) { - int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0); - int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0); - int yx2, yx2c; - - if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w) - continue; - yx2 = y2*w+x2; - yx2c = dsf_canonify(sc->dsf, yx2); - - if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) { - /* - * Component yx can be extended into square - * yx2. - */ - if (sc->tmp[yx] == -1) - sc->tmp[yx] = yx2; - else if (sc->tmp[yx] != yx2) - sc->tmp[yx] = -2; /* multiple choices found */ - } - } - } - } - for (i = 0; i < wh; i++) { - if (sc->tmp[i] >= 0) { - /* - * Make sure we haven't connected the two already - * during this loop (which could happen if for - * _both_ components this was the only way to - * extend them). - */ - if (dsf_canonify(sc->dsf, i) == - dsf_canonify(sc->dsf, sc->tmp[i])) - continue; - -#ifdef SOLVER_DIAGNOSTICS - printf("Connecting %d and %d\n", i, sc->tmp[i]); -#endif - solver_connect(sc, i, sc->tmp[i]); - done_something = done_something_overall = true; - break; - } - } - - if (!done_something) - break; - } - - /* - * Return 0 if we haven't made any progress; 1 if we've done - * something but not solved it completely; 2 if we've solved - * it completely. - */ - for (i = 0; i < wh; i++) - if (sc->size[dsf_canonify(sc->dsf, i)] != k) - break; - if (i == wh) - return 2; - if (done_something_overall) - return 1; - return 0; -} - -unsigned char *generate(int w, int h, int k, random_state *rs) -{ - int wh = w*h; - int n = wh/k; - struct solver_scratch *sc; - unsigned char *grid; - unsigned char *shuffled; - int i, j, m, retries; - int *permutation; - bool *gen_lock; - extern int *divvy_rectangle(int w, int h, int k, random_state *rs); - - sc = solver_scratch_new(w, h, k); - grid = snewn(wh, unsigned char); - shuffled = snewn(k, unsigned char); - permutation = snewn(wh, int); - gen_lock = snewn(wh, bool); - - do { - int *dsf = divvy_rectangle(w, h, k, rs); - - /* - * Go through the dsf and find the indices of all the - * squares involved in each omino, in a manner conducive - * to per-omino indexing. We set permutation[i*k+j] to be - * the index of the jth square (ordered arbitrarily) in - * omino i. - */ - for (i = j = 0; i < wh; i++) - if (dsf_canonify(dsf, i) == i) { - sc->tmp[i] = j; - /* - * During this loop and the following one, we use - * the last element of each row of permutation[] - * as a counter of the number of indices so far - * placed in it. When we place the final index of - * an omino, that counter is overwritten, but that - * doesn't matter because we'll never use it - * again. Of course this depends critically on - * divvy_rectangle() having returned correct - * results, or else chaos would ensue. - */ - permutation[j*k+k-1] = 0; - j++; - } - for (i = 0; i < wh; i++) { - j = sc->tmp[dsf_canonify(dsf, i)]; - m = permutation[j*k+k-1]++; - permutation[j*k+m] = i; - } - - /* - * Track which squares' letters we have already depended - * on for deductions. This is gradually updated by - * solver_attempt(). - */ - memset(gen_lock, 0, wh * sizeof(bool)); - - /* - * Now repeatedly fill the grid with letters, and attempt - * to solve it. If the solver makes progress but does not - * fail completely, then gen_lock will have been updated - * and we try again. On a complete failure, though, we - * have no option but to give up and abandon this set of - * ominoes. - */ - solver_init(sc); - retries = k*k; - while (1) { - /* - * Fill the grid with letters. We can safely use - * sc->tmp to hold the set of letters required at each - * stage, since it's at least size k and is currently - * unused. - */ - for (i = 0; i < n; i++) { - /* - * First, determine the set of letters already - * placed in this omino by gen_lock. - */ - for (j = 0; j < k; j++) - sc->tmp[j] = j; - for (j = 0; j < k; j++) { - int index = permutation[i*k+j]; - int letter = grid[index]; - if (gen_lock[index]) - sc->tmp[letter] = -1; - } - /* - * Now collect together all the remaining letters - * and randomly shuffle them. - */ - for (j = m = 0; j < k; j++) - if (sc->tmp[j] >= 0) - sc->tmp[m++] = sc->tmp[j]; - shuffle(sc->tmp, m, sizeof(*sc->tmp), rs); - /* - * Finally, write the shuffled letters into the - * grid. - */ - for (j = 0; j < k; j++) { - int index = permutation[i*k+j]; - if (!gen_lock[index]) - grid[index] = sc->tmp[--m]; - } - assert(m == 0); - } - - /* - * Now we have a candidate grid. Attempt to progress - * the solution. - */ - m = solver_attempt(sc, grid, gen_lock); - if (m == 2 || /* success */ - (m == 0 && retries-- <= 0)) /* failure */ - break; - if (m == 1) - retries = k*k; /* reset this counter, and continue */ - } - - sfree(dsf); - } while (m == 0); - - sfree(gen_lock); - sfree(permutation); - sfree(shuffled); - solver_scratch_free(sc); - - return grid; -} - -/* ---------------------------------------------------------------------- - * End of solver/generator code. - */ - -static char *new_game_desc(const game_params *params, random_state *rs, - char **aux, bool interactive) -{ - int w = params->w, h = params->h, wh = w*h, k = params->k; - unsigned char *grid; - char *desc; - int i; - - grid = generate(w, h, k, rs); - - desc = snewn(wh+1, char); - for (i = 0; i < wh; i++) - desc[i] = 'A' + grid[i]; - desc[wh] = '\0'; - - sfree(grid); - - return desc; -} - -static const char *validate_desc(const game_params *params, const char *desc) -{ - return NULL; -} - -static game_state *new_game(midend *me, const game_params *params, - const char *desc) -{ - game_state *state = snew(game_state); - - state->FIXME = 0; - - return state; -} - -static game_state *dup_game(const game_state *state) -{ - game_state *ret = snew(game_state); - - ret->FIXME = state->FIXME; - - return ret; -} - -static void free_game(game_state *state) -{ - sfree(state); -} - -static char *solve_game(const game_state *state, const game_state *currstate, - const char *aux, const char **error) -{ - return NULL; -} - -static bool game_can_format_as_text_now(const game_params *params) -{ - return true; -} - -static char *game_text_format(const game_state *state) -{ - return NULL; -} - -static game_ui *new_ui(const game_state *state) -{ - return NULL; -} - -static void free_ui(game_ui *ui) -{ -} - -static char *encode_ui(const game_ui *ui) -{ - return NULL; -} - -static void decode_ui(game_ui *ui, const char *encoding) -{ -} - -static void game_changed_state(game_ui *ui, const game_state *oldstate, - const game_state *newstate) -{ -} - -struct game_drawstate { - int tilesize; - int FIXME; -}; - -static char *interpret_move(const game_state *state, game_ui *ui, - const game_drawstate *ds, - int x, int y, int button) -{ - return NULL; -} - -static game_state *execute_move(const game_state *state, const char *move) -{ - return NULL; -} - -/* ---------------------------------------------------------------------- - * Drawing routines. - */ - -static void game_compute_size(const game_params *params, int tilesize, - int *x, int *y) -{ - *x = *y = 10 * tilesize; /* FIXME */ -} - -static void game_set_size(drawing *dr, game_drawstate *ds, - const game_params *params, int tilesize) -{ - ds->tilesize = tilesize; -} - -static float *game_colours(frontend *fe, int *ncolours) -{ - float *ret = snewn(3 * NCOLOURS, float); - - frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); - - *ncolours = NCOLOURS; - return ret; -} - -static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) -{ - struct game_drawstate *ds = snew(struct game_drawstate); - - ds->tilesize = 0; - ds->FIXME = 0; - - return ds; -} - -static void game_free_drawstate(drawing *dr, game_drawstate *ds) -{ - sfree(ds); -} - -static void game_redraw(drawing *dr, game_drawstate *ds, - const game_state *oldstate, const game_state *state, - int dir, const game_ui *ui, - float animtime, float flashtime) -{ - /* - * The initial contents of the window are not guaranteed and - * can vary with front ends. To be on the safe side, all games - * should start by drawing a big background-colour rectangle - * covering the whole window. - */ - draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND); -} - -static float game_anim_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - return 0.0F; -} - -static float game_flash_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - return 0.0F; -} - -static void game_get_cursor_location(const game_ui *ui, - const game_drawstate *ds, - const game_state *state, - const game_params *params, - int *x, int *y, int *w, int *h) -{ -} - -static int game_status(const game_state *state) -{ - return 0; -} - -static bool game_timing_state(const game_state *state, game_ui *ui) -{ - return true; -} - -static void game_print_size(const game_params *params, float *x, float *y) -{ -} - -static void game_print(drawing *dr, const game_state *state, int tilesize) -{ -} - -#ifdef COMBINED -#define thegame separate -#endif - -const struct game thegame = { - "Separate", NULL, NULL, - default_params, - game_fetch_preset, NULL, - decode_params, - encode_params, - free_params, - dup_params, - false, game_configure, custom_params, - validate_params, - new_game_desc, - validate_desc, - new_game, - dup_game, - free_game, - false, solve_game, - false, game_can_format_as_text_now, game_text_format, - new_ui, - free_ui, - encode_ui, - decode_ui, - NULL, /* game_request_keys */ - game_changed_state, - interpret_move, - execute_move, - 20 /* FIXME */, game_compute_size, game_set_size, - game_colours, - game_new_drawstate, - game_free_drawstate, - game_redraw, - game_anim_length, - game_flash_length, - game_get_cursor_location, - game_status, - false, false, game_print_size, game_print, - false, /* wants_statusbar */ - false, game_timing_state, - 0, /* flags */ -}; diff --git a/apps/plugins/puzzles/src/unfinished/slide.R b/apps/plugins/puzzles/src/unfinished/slide.R deleted file mode 100644 index 189ed652d1..0000000000 --- a/apps/plugins/puzzles/src/unfinished/slide.R +++ /dev/null @@ -1,24 +0,0 @@ -# -*- makefile -*- - -SLIDE_EXTRA = dsf tree234 - -slide : [X] GTK COMMON slide SLIDE_EXTRA slide-icon|no-icon - -slide : [G] WINDOWS COMMON slide SLIDE_EXTRA slide.res|noicon.res - -slidesolver : [U] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE -slidesolver : [C] slide[STANDALONE_SOLVER] SLIDE_EXTRA STANDALONE - -ALL += slide[COMBINED] SLIDE_EXTRA - -!begin am gtk -GAMES += slide -!end - -!begin >list.c - A(slide) \ -!end - -!begin >gamedesc.txt -slide:slide.exe:Slide:Sliding block puzzle:Slide the blocks to let the key block out. -!end diff --git a/apps/plugins/puzzles/src/unfinished/slide.c b/apps/plugins/puzzles/src/unfinished/slide.c deleted file mode 100644 index c7a3dcecf7..0000000000 --- a/apps/plugins/puzzles/src/unfinished/slide.c +++ /dev/null @@ -1,2458 +0,0 @@ -/* - * slide.c: Implementation of the block-sliding puzzle `Klotski'. - */ - -/* - * TODO: - * - * - Improve the generator. - * * actually, we seem to be mostly sensible already now. I - * want more choice over the type of main block and location - * of the exit/target, and I think I probably ought to give - * up on compactness and just bite the bullet and have the - * target area right outside the main wall, but mostly I - * think it's OK. - * * the move limit tends to make the game _slower_ to - * generate, which is odd. Perhaps investigate why. - * - * - Improve the graphics. - * * All the colours are a bit wishy-washy. _Some_ dark - * colours would surely not be excessive? Probably darken - * the tiles, the walls and the main block, and leave the - * target marker pale. - * * The cattle grid effect is still disgusting. Think of - * something completely different. - * * The highlight for next-piece-to-move in the solver is - * excessive, and the shadow blends in too well with the - * piece lowlights. Adjust both. - */ - -#include -#include -#include -#include -#include -#include - -#include "puzzles.h" -#include "tree234.h" - -/* - * The implementation of this game revolves around the insight - * which makes an exhaustive-search solver feasible: although - * there are many blocks which can be rearranged in many ways, any - * two blocks of the same shape are _indistinguishable_ and hence - * the number of _distinct_ board layouts is generally much - * smaller. So we adopt a representation for board layouts which - * is inherently canonical, i.e. there are no two distinct - * representations which encode indistinguishable layouts. - * - * The way we do this is to encode each square of the board, in - * the normal left-to-right top-to-bottom order, as being one of - * the following things: - * - the first square (in the given order) of a block (`anchor') - * - special case of the above: the anchor for the _main_ block - * (i.e. the one which the aim of the game is to get to the - * target position) - * - a subsequent square of a block whose previous square was N - * squares ago - * - an impassable wall - * - * (We also separately store data about which board positions are - * forcefields only passable by the main block. We can't encode - * that in the main board data, because then the main block would - * destroy forcefields as it went over them.) - * - * Hence, for example, a 2x2 square block would be encoded as - * ANCHOR, followed by DIST(1), and w-2 squares later on there - * would be DIST(w-1) followed by DIST(1). So if you start at the - * last of those squares, the DIST numbers give you a linked list - * pointing back through all the other squares in the same block. - * - * So the solver simply does a bfs over all reachable positions, - * encoding them in this format and storing them in a tree234 to - * ensure it doesn't ever revisit an already-analysed position. - */ - -enum { - /* - * The colours are arranged here so that every base colour is - * directly followed by its highlight colour and then its - * lowlight colour. Do not break this, or draw_tile() will get - * confused. - */ - COL_BACKGROUND, - COL_HIGHLIGHT, - COL_LOWLIGHT, - COL_DRAGGING, - COL_DRAGGING_HIGHLIGHT, - COL_DRAGGING_LOWLIGHT, - COL_MAIN, - COL_MAIN_HIGHLIGHT, - COL_MAIN_LOWLIGHT, - COL_MAIN_DRAGGING, - COL_MAIN_DRAGGING_HIGHLIGHT, - COL_MAIN_DRAGGING_LOWLIGHT, - COL_TARGET, - COL_TARGET_HIGHLIGHT, - COL_TARGET_LOWLIGHT, - NCOLOURS -}; - -/* - * Board layout is a simple array of bytes. Each byte holds: - */ -#define ANCHOR 255 /* top-left-most square of some piece */ -#define MAINANCHOR 254 /* anchor of _main_ piece */ -#define EMPTY 253 /* empty square */ -#define WALL 252 /* immovable wall */ -#define MAXDIST 251 -/* all other values indicate distance back to previous square of same block */ -#define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 ) -#define DIST(x) (x) -#define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR ) -#define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) ) - -/* - * MAXDIST is the largest DIST value we can encode. This must - * therefore also be the maximum puzzle width in theory (although - * solver running time will dictate a much smaller limit in - * practice). - */ -#define MAXWID MAXDIST - -struct game_params { - int w, h; - int maxmoves; -}; - -struct game_immutable_state { - int refcount; - bool *forcefield; -}; - -struct game_solution { - int nmoves; - int *moves; /* just like from solve_board() */ - int refcount; -}; - -struct game_state { - int w, h; - unsigned char *board; - int tx, ty; /* target coords for MAINANCHOR */ - int minmoves; /* for display only */ - int lastmoved, lastmoved_pos; /* for move counting */ - int movecount; - int completed; - bool cheated; - struct game_immutable_state *imm; - struct game_solution *soln; - int soln_index; -}; - -static game_params *default_params(void) -{ - game_params *ret = snew(game_params); - - ret->w = 7; - ret->h = 6; - ret->maxmoves = 40; - - return ret; -} - -static const struct game_params slide_presets[] = { - {7, 6, 25}, - {7, 6, -1}, - {8, 6, -1}, -}; - -static bool game_fetch_preset(int i, char **name, game_params **params) -{ - game_params *ret; - char str[80]; - - if (i < 0 || i >= lenof(slide_presets)) - return false; - - ret = snew(game_params); - *ret = slide_presets[i]; - - sprintf(str, "%dx%d", ret->w, ret->h); - if (ret->maxmoves >= 0) - sprintf(str + strlen(str), ", max %d moves", ret->maxmoves); - else - sprintf(str + strlen(str), ", no move limit"); - - *name = dupstr(str); - *params = ret; - return true; -} - -static void free_params(game_params *params) -{ - sfree(params); -} - -static game_params *dup_params(const game_params *params) -{ - game_params *ret = snew(game_params); - *ret = *params; /* structure copy */ - return ret; -} - -static void decode_params(game_params *params, char const *string) -{ - params->w = params->h = atoi(string); - while (*string && isdigit((unsigned char)*string)) string++; - if (*string == 'x') { - string++; - params->h = atoi(string); - while (*string && isdigit((unsigned char)*string)) string++; - } - if (*string == 'm') { - string++; - params->maxmoves = atoi(string); - while (*string && isdigit((unsigned char)*string)) string++; - } else if (*string == 'u') { - string++; - params->maxmoves = -1; - } -} - -static char *encode_params(const game_params *params, bool full) -{ - char data[256]; - - sprintf(data, "%dx%d", params->w, params->h); - if (params->maxmoves >= 0) - sprintf(data + strlen(data), "m%d", params->maxmoves); - else - sprintf(data + strlen(data), "u"); - - return dupstr(data); -} - -static config_item *game_configure(const game_params *params) -{ - config_item *ret; - char buf[80]; - - ret = snewn(4, config_item); - - ret[0].name = "Width"; - ret[0].type = C_STRING; - sprintf(buf, "%d", params->w); - ret[0].u.string.sval = dupstr(buf); - - ret[1].name = "Height"; - ret[1].type = C_STRING; - sprintf(buf, "%d", params->h); - ret[1].u.string.sval = dupstr(buf); - - ret[2].name = "Solution length limit"; - ret[2].type = C_STRING; - sprintf(buf, "%d", params->maxmoves); - ret[2].u.string.sval = dupstr(buf); - - ret[3].name = NULL; - ret[3].type = C_END; - - return ret; -} - -static game_params *custom_params(const config_item *cfg) -{ - game_params *ret = snew(game_params); - - ret->w = atoi(cfg[0].u.string.sval); - ret->h = atoi(cfg[1].u.string.sval); - ret->maxmoves = atoi(cfg[2].u.string.sval); - - return ret; -} - -static const char *validate_params(const game_params *params, bool full) -{ - if (params->w > MAXWID) - return "Width must be at most " STR(MAXWID); - - if (params->w < 5) - return "Width must be at least 5"; - if (params->h < 4) - return "Height must be at least 4"; - - return NULL; -} - -static char *board_text_format(int w, int h, unsigned char *data, - bool *forcefield) -{ - int wh = w*h; - int *dsf = snew_dsf(wh); - int i, x, y; - int retpos, retlen = (w*2+2)*(h*2+1)+1; - char *ret = snewn(retlen, char); - - for (i = 0; i < wh; i++) - if (ISDIST(data[i])) - dsf_merge(dsf, i - data[i], i); - retpos = 0; - for (y = 0; y < 2*h+1; y++) { - for (x = 0; x < 2*w+1; x++) { - int v; - int i = (y/2)*w+(x/2); - -#define dtype(i) (ISBLOCK(data[i]) ? \ - dsf_canonify(dsf, i) : data[i]) -#define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \ - data[t] == MAINANCHOR ? '*' : '%') - - if (y % 2 && x % 2) { - int j = dtype(i); - v = dchar(j); - } else if (y % 2 && !(x % 2)) { - int j1 = (x > 0 ? dtype(i-1) : -1); - int j2 = (x < 2*w ? dtype(i) : -1); - if (j1 != j2) - v = '|'; - else - v = dchar(j1); - } else if (!(y % 2) && (x % 2)) { - int j1 = (y > 0 ? dtype(i-w) : -1); - int j2 = (y < 2*h ? dtype(i) : -1); - if (j1 != j2) - v = '-'; - else - v = dchar(j1); - } else { - int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1); - int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1); - int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1); - int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1); - if (j1 == j2 && j2 == j3 && j3 == j4) - v = dchar(j1); - else if (j1 == j2 && j3 == j4) - v = '|'; - else if (j1 == j3 && j2 == j4) - v = '-'; - else - v = '+'; - } - - assert(retpos < retlen); - ret[retpos++] = v; - } - assert(retpos < retlen); - ret[retpos++] = '\n'; - } - assert(retpos < retlen); - ret[retpos++] = '\0'; - assert(retpos == retlen); - - return ret; -} - -/* ---------------------------------------------------------------------- - * Solver. - */ - -/* - * During solver execution, the set of visited board positions is - * stored as a tree234 of the following structures. `w', `h' and - * `data' are obvious in meaning; `dist' represents the minimum - * distance to reach this position from the starting point. - * - * `prev' links each board to the board position from which it was - * most efficiently derived. - */ -struct board { - int w, h; - int dist; - struct board *prev; - unsigned char *data; -}; - -static int boardcmp(void *av, void *bv) -{ - struct board *a = (struct board *)av; - struct board *b = (struct board *)bv; - return memcmp(a->data, b->data, a->w * a->h); -} - -static struct board *newboard(int w, int h, unsigned char *data) -{ - struct board *b = malloc(sizeof(struct board) + w*h); - b->data = (unsigned char *)b + sizeof(struct board); - memcpy(b->data, data, w*h); - b->w = w; - b->h = h; - b->dist = -1; - b->prev = NULL; - return b; -} - -/* - * The actual solver. Given a board, attempt to find the minimum - * length of move sequence which moves MAINANCHOR to (tx,ty), or - * -1 if no solution exists. Returns that minimum length. - * - * Also, if `moveout' is provided, writes out the moves in the - * form of a sequence of pairs of integers indicating the source - * and destination points of the anchor of the moved piece in each - * move. Exactly twice as many integers are written as the number - * returned from solve_board(), and `moveout' receives an int * - * which is a pointer to a dynamically allocated array. - */ -static int solve_board(int w, int h, unsigned char *board, - bool *forcefield, int tx, int ty, - int movelimit, int **moveout) -{ - int wh = w*h; - struct board *b, *b2, *b3; - int *next, *which; - bool *anchors, *movereached; - int *movequeue, mqhead, mqtail; - tree234 *sorted, *queue; - int i, j, dir; - int qlen, lastdist; - int ret; - -#ifdef SOLVER_DIAGNOSTICS - { - char *t = board_text_format(w, h, board); - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - int c = board[i*w+j]; - if (ISDIST(c)) - printf("D%-3d", c); - else if (c == MAINANCHOR) - printf("M "); - else if (c == ANCHOR) - printf("A "); - else if (c == WALL) - printf("W "); - else if (c == EMPTY) - printf("E "); - } - printf("\n"); - } - - printf("Starting solver for:\n%s\n", t); - sfree(t); - } -#endif - - sorted = newtree234(boardcmp); - queue = newtree234(NULL); - - b = newboard(w, h, board); - b->dist = 0; - add234(sorted, b); - addpos234(queue, b, 0); - qlen = 1; - - next = snewn(wh, int); - anchors = snewn(wh, bool); - which = snewn(wh, int); - movereached = snewn(wh, bool); - movequeue = snewn(wh, int); - lastdist = -1; - - while ((b = delpos234(queue, 0)) != NULL) { - qlen--; - if (movelimit >= 0 && b->dist >= movelimit) { - /* - * The problem is not soluble in under `movelimit' - * moves, so we can quit right now. - */ - b2 = NULL; - goto done; - } - if (b->dist != lastdist) { -#ifdef SOLVER_DIAGNOSTICS - printf("dist %d (%d)\n", b->dist, count234(sorted)); -#endif - lastdist = b->dist; - } - /* - * Find all the anchors and form a linked list of the - * squares within each block. - */ - for (i = 0; i < wh; i++) { - next[i] = -1; - anchors[i] = false; - which[i] = -1; - if (ISANCHOR(b->data[i])) { - anchors[i] = true; - which[i] = i; - } else if (ISDIST(b->data[i])) { - j = i - b->data[i]; - next[j] = i; - which[i] = which[j]; - } - } - - /* - * For each anchor, do an array-based BFS to find all the - * places we can slide it to. - */ - for (i = 0; i < wh; i++) { - if (!anchors[i]) - continue; - - mqhead = mqtail = 0; - for (j = 0; j < wh; j++) - movereached[j] = false; - movequeue[mqtail++] = i; - while (mqhead < mqtail) { - int pos = movequeue[mqhead++]; - - /* - * Try to move in each direction from here. - */ - for (dir = 0; dir < 4; dir++) { - int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0); - int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0); - int offset = dy*w + dx; - int newpos = pos + offset; - int d = newpos - i; - - /* - * For each square involved in this block, - * check to see if the square d spaces away - * from it is either empty or part of the same - * block. - */ - for (j = i; j >= 0; j = next[j]) { - int jy = (pos+j-i) / w + dy, jx = (pos+j-i) % w + dx; - if (jy >= 0 && jy < h && jx >= 0 && jx < w && - ((b->data[j+d] == EMPTY || which[j+d] == i) && - (b->data[i] == MAINANCHOR || !forcefield[j+d]))) - /* ok */; - else - break; - } - if (j >= 0) - continue; /* this direction wasn't feasible */ - - /* - * If we've already tried moving this piece - * here, leave it. - */ - if (movereached[newpos]) - continue; - movereached[newpos] = true; - movequeue[mqtail++] = newpos; - - /* - * We have a viable move. Make it. - */ - b2 = newboard(w, h, b->data); - for (j = i; j >= 0; j = next[j]) - b2->data[j] = EMPTY; - for (j = i; j >= 0; j = next[j]) - b2->data[j+d] = b->data[j]; - - b3 = add234(sorted, b2); - if (b3 != b2) { - sfree(b2); /* we already got one */ - } else { - b2->dist = b->dist + 1; - b2->prev = b; - addpos234(queue, b2, qlen++); - if (b2->data[ty*w+tx] == MAINANCHOR) - goto done; /* search completed! */ - } - } - } - } - } - b2 = NULL; - - done: - - if (b2) { - ret = b2->dist; - if (moveout) { - /* - * Now b2 represents the solved position. Backtrack to - * output the solution. - */ - *moveout = snewn(ret * 2, int); - j = ret * 2; - - while (b2->prev) { - int from = -1, to = -1; - - b = b2->prev; - - /* - * Scan b and b2 to find out which piece has - * moved. - */ - for (i = 0; i < wh; i++) { - if (ISANCHOR(b->data[i]) && !ISANCHOR(b2->data[i])) { - assert(from == -1); - from = i; - } else if (!ISANCHOR(b->data[i]) && ISANCHOR(b2->data[i])){ - assert(to == -1); - to = i; - } - } - - assert(from >= 0 && to >= 0); - assert(j >= 2); - (*moveout)[--j] = to; - (*moveout)[--j] = from; - - b2 = b; - } - assert(j == 0); - } - } else { - ret = -1; /* no solution */ - if (moveout) - *moveout = NULL; - } - - freetree234(queue); - - while ((b = delpos234(sorted, 0)) != NULL) - sfree(b); - freetree234(sorted); - - sfree(next); - sfree(anchors); - sfree(movereached); - sfree(movequeue); - sfree(which); - - return ret; -} - -/* ---------------------------------------------------------------------- - * Random board generation. - */ - -static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves, - random_state *rs, unsigned char **rboard, - bool **rforcefield, int movelimit) -{ - int wh = w*h; - unsigned char *board, *board2; - bool *forcefield; - bool *tried_merge; - int *dsf; - int *list, nlist, pos; - int tx, ty; - int i, j; - int moves = 0; /* placate optimiser */ - - /* - * Set up a board and fill it with singletons, except for a - * border of walls. - */ - board = snewn(wh, unsigned char); - forcefield = snewn(wh, bool); - board2 = snewn(wh, unsigned char); - memset(board, ANCHOR, wh); - memset(forcefield, 0, wh * sizeof(bool)); - for (i = 0; i < w; i++) - board[i] = board[i+w*(h-1)] = WALL; - for (i = 0; i < h; i++) - board[i*w] = board[i*w+(w-1)] = WALL; - - tried_merge = snewn(wh * wh, bool); - memset(tried_merge, 0, wh*wh * sizeof(bool)); - dsf = snew_dsf(wh); - - /* - * Invent a main piece at one extreme. (FIXME: vary the - * extreme, and the piece.) - */ - board[w+1] = MAINANCHOR; - board[w+2] = DIST(1); - board[w*2+1] = DIST(w-1); - board[w*2+2] = DIST(1); - - /* - * Invent a target position. (FIXME: vary this too.) - */ - tx = w-2; - ty = h-3; - forcefield[ty*w+tx+1] = true; - forcefield[(ty+1)*w+tx+1] = true; - board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY; - - /* - * Gradually remove singletons until the game becomes soluble. - */ - for (j = w; j-- > 0 ;) - for (i = h; i-- > 0 ;) - if (board[i*w+j] == ANCHOR) { - /* - * See if the board is already soluble. - */ - if ((moves = solve_board(w, h, board, forcefield, - tx, ty, movelimit, NULL)) >= 0) - goto soluble; - - /* - * Otherwise, remove this piece. - */ - board[i*w+j] = EMPTY; - } - assert(!"We shouldn't get here"); - soluble: - - /* - * Make a list of all the inter-block edges on the board. - */ - list = snewn(wh*2, int); - nlist = 0; - for (i = 0; i+1 < w; i++) - for (j = 0; j < h; j++) - list[nlist++] = (j*w+i) * 2 + 0; /* edge to the right of j*w+i */ - for (j = 0; j+1 < h; j++) - for (i = 0; i < w; i++) - list[nlist++] = (j*w+i) * 2 + 1; /* edge below j*w+i */ - - /* - * Now go through that list in random order, trying to merge - * the blocks on each side of each edge. - */ - shuffle(list, nlist, sizeof(*list), rs); - while (nlist > 0) { - int x1, y1, p1, c1; - int x2, y2, p2, c2; - - pos = list[--nlist]; - y1 = y2 = pos / (w*2); - x1 = x2 = (pos / 2) % w; - if (pos % 2) - y2++; - else - x2++; - p1 = y1*w+x1; - p2 = y2*w+x2; - - /* - * Immediately abandon the attempt if we've already tried - * to merge the same pair of blocks along a different - * edge. - */ - c1 = dsf_canonify(dsf, p1); - c2 = dsf_canonify(dsf, p2); - if (tried_merge[c1 * wh + c2]) - continue; - - /* - * In order to be mergeable, these two squares must each - * either be, or belong to, a non-main anchor, and their - * anchors must also be distinct. - */ - if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2])) - continue; - while (ISDIST(board[p1])) - p1 -= board[p1]; - while (ISDIST(board[p2])) - p2 -= board[p2]; - if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2) - continue; - - /* - * We can merge these blocks. Try it, and see if the - * puzzle remains soluble. - */ - memcpy(board2, board, wh); - j = -1; - while (p1 < wh || p2 < wh) { - /* - * p1 and p2 are the squares at the head of each block - * list. Pick the smaller one and put it on the output - * block list. - */ - i = min(p1, p2); - if (j < 0) { - board[i] = ANCHOR; - } else { - assert(i - j <= MAXDIST); - board[i] = DIST(i - j); - } - j = i; - - /* - * Now advance whichever list that came from. - */ - if (i == p1) { - do { - p1++; - } while (p1 < wh && board[p1] != DIST(p1-i)); - } else { - do { - p2++; - } while (p2 < wh && board[p2] != DIST(p2-i)); - } - } - j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL); - if (j < 0) { - /* - * Didn't work. Revert the merge. - */ - memcpy(board, board2, wh); - tried_merge[c1 * wh + c2] = true; - tried_merge[c2 * wh + c1] = true; - } else { - int c; - - moves = j; - - dsf_merge(dsf, c1, c2); - c = dsf_canonify(dsf, c1); - for (i = 0; i < wh; i++) - tried_merge[c*wh+i] = (tried_merge[c1*wh+i] || - tried_merge[c2*wh+i]); - for (i = 0; i < wh; i++) - tried_merge[i*wh+c] = (tried_merge[i*wh+c1] || - tried_merge[i*wh+c2]); - } - } - - sfree(dsf); - sfree(list); - sfree(tried_merge); - sfree(board2); - - *rtx = tx; - *rty = ty; - *rboard = board; - *rforcefield = forcefield; - *minmoves = moves; -} - -/* ---------------------------------------------------------------------- - * End of solver/generator code. - */ - -static char *new_game_desc(const game_params *params, random_state *rs, - char **aux, bool interactive) -{ - int w = params->w, h = params->h, wh = w*h; - int tx, ty, minmoves; - unsigned char *board; - bool *forcefield; - char *ret, *p; - int i; - - generate_board(params->w, params->h, &tx, &ty, &minmoves, rs, - &board, &forcefield, params->maxmoves); -#ifdef GENERATOR_DIAGNOSTICS - { - char *t = board_text_format(params->w, params->h, board); - printf("%s\n", t); - sfree(t); - } -#endif - - /* - * Encode as a game ID. - */ - ret = snewn(wh * 6 + 40, char); - p = ret; - i = 0; - while (i < wh) { - if (ISDIST(board[i])) { - p += sprintf(p, "d%d", board[i]); - i++; - } else { - int count = 1; - int b = board[i]; - bool f = forcefield[i]; - int c = (b == ANCHOR ? 'a' : - b == MAINANCHOR ? 'm' : - b == EMPTY ? 'e' : - /* b == WALL ? */ 'w'); - if (f) *p++ = 'f'; - *p++ = c; - i++; - while (i < wh && board[i] == b && forcefield[i] == f) - i++, count++; - if (count > 1) - p += sprintf(p, "%d", count); - } - } - p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves); - ret = sresize(ret, p+1 - ret, char); - - sfree(board); - sfree(forcefield); - - return ret; -} - -static const char *validate_desc(const game_params *params, const char *desc) -{ - int w = params->w, h = params->h, wh = w*h; - bool *active; - int *link; - int mains = 0; - int i, tx, ty, minmoves; - char *ret; - - active = snewn(wh, bool); - link = snewn(wh, int); - i = 0; - - while (*desc && *desc != ',') { - if (i >= wh) { - ret = "Too much data in game description"; - goto done; - } - link[i] = -1; - active[i] = false; - if (*desc == 'f' || *desc == 'F') { - desc++; - if (!*desc) { - ret = "Expected another character after 'f' in game " - "description"; - goto done; - } - } - - if (*desc == 'd' || *desc == 'D') { - int dist; - - desc++; - if (!isdigit((unsigned char)*desc)) { - ret = "Expected a number after 'd' in game description"; - goto done; - } - dist = atoi(desc); - while (*desc && isdigit((unsigned char)*desc)) desc++; - - if (dist <= 0 || dist > i) { - ret = "Out-of-range number after 'd' in game description"; - goto done; - } - - if (!active[i - dist]) { - ret = "Invalid back-reference in game description"; - goto done; - } - - link[i] = i - dist; - - active[i] = true; - active[link[i]] = false; - i++; - } else { - int c = *desc++; - int count = 1; - - if (!strchr("aAmMeEwW", c)) { - ret = "Invalid character in game description"; - goto done; - } - if (isdigit((unsigned char)*desc)) { - count = atoi(desc); - while (*desc && isdigit((unsigned char)*desc)) desc++; - } - if (i + count > wh) { - ret = "Too much data in game description"; - goto done; - } - while (count-- > 0) { - active[i] = (strchr("aAmM", c) != NULL); - link[i] = -1; - if (strchr("mM", c) != NULL) { - mains++; - } - i++; - } - } - } - if (mains != 1) { - ret = (mains == 0 ? "No main piece specified in game description" : - "More than one main piece specified in game description"); - goto done; - } - if (i < wh) { - ret = "Not enough data in game description"; - goto done; - } - - /* - * Now read the target coordinates. - */ - i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves); - if (i < 2) { - ret = "No target coordinates specified"; - goto done; - /* - * (but minmoves is optional) - */ - } - - ret = NULL; - - done: - sfree(active); - sfree(link); - return ret; -} - -static game_state *new_game(midend *me, const game_params *params, - const char *desc) -{ - int w = params->w, h = params->h, wh = w*h; - game_state *state; - int i; - - state = snew(game_state); - state->w = w; - state->h = h; - state->board = snewn(wh, unsigned char); - state->lastmoved = state->lastmoved_pos = -1; - state->movecount = 0; - state->imm = snew(struct game_immutable_state); - state->imm->refcount = 1; - state->imm->forcefield = snewn(wh, bool); - - i = 0; - - while (*desc && *desc != ',') { - bool f = false; - - assert(i < wh); - - if (*desc == 'f') { - f = true; - desc++; - assert(*desc); - } - - if (*desc == 'd' || *desc == 'D') { - int dist; - - desc++; - dist = atoi(desc); - while (*desc && isdigit((unsigned char)*desc)) desc++; - - state->board[i] = DIST(dist); - state->imm->forcefield[i] = f; - - i++; - } else { - int c = *desc++; - int count = 1; - - if (isdigit((unsigned char)*desc)) { - count = atoi(desc); - while (*desc && isdigit((unsigned char)*desc)) desc++; - } - assert(i + count <= wh); - - c = (c == 'a' || c == 'A' ? ANCHOR : - c == 'm' || c == 'M' ? MAINANCHOR : - c == 'e' || c == 'E' ? EMPTY : - /* c == 'w' || c == 'W' ? */ WALL); - - while (count-- > 0) { - state->board[i] = c; - state->imm->forcefield[i] = f; - i++; - } - } - } - - /* - * Now read the target coordinates. - */ - state->tx = state->ty = 0; - state->minmoves = -1; - i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves); - - if (state->board[state->ty*w+state->tx] == MAINANCHOR) - state->completed = 0; /* already complete! */ - else - state->completed = -1; - - state->cheated = false; - state->soln = NULL; - state->soln_index = -1; - - return state; -} - -static game_state *dup_game(const game_state *state) -{ - int w = state->w, h = state->h, wh = w*h; - game_state *ret = snew(game_state); - - ret->w = state->w; - ret->h = state->h; - ret->board = snewn(wh, unsigned char); - memcpy(ret->board, state->board, wh); - ret->tx = state->tx; - ret->ty = state->ty; - ret->minmoves = state->minmoves; - ret->lastmoved = state->lastmoved; - ret->lastmoved_pos = state->lastmoved_pos; - ret->movecount = state->movecount; - ret->completed = state->completed; - ret->cheated = state->cheated; - ret->imm = state->imm; - ret->imm->refcount++; - ret->soln = state->soln; - ret->soln_index = state->soln_index; - if (ret->soln) - ret->soln->refcount++; - - return ret; -} - -static void free_game(game_state *state) -{ - if (--state->imm->refcount <= 0) { - sfree(state->imm->forcefield); - sfree(state->imm); - } - if (state->soln && --state->soln->refcount <= 0) { - sfree(state->soln->moves); - sfree(state->soln); - } - sfree(state->board); - sfree(state); -} - -static char *solve_game(const game_state *state, const game_state *currstate, - const char *aux, const char **error) -{ - int *moves; - int nmoves; - int i; - char *ret, *p, sep; - - /* - * Run the solver and attempt to find the shortest solution - * from the current position. - */ - nmoves = solve_board(state->w, state->h, state->board, - state->imm->forcefield, state->tx, state->ty, - -1, &moves); - - if (nmoves < 0) { - *error = "Unable to find a solution to this puzzle"; - return NULL; - } - if (nmoves == 0) { - *error = "Puzzle is already solved"; - return NULL; - } - - /* - * Encode the resulting solution as a move string. - */ - ret = snewn(nmoves * 40, char); - p = ret; - sep = 'S'; - - for (i = 0; i < nmoves; i++) { - p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]); - sep = ','; - } - - sfree(moves); - assert(p - ret < nmoves * 40); - ret = sresize(ret, p+1 - ret, char); - - return ret; -} - -static bool game_can_format_as_text_now(const game_params *params) -{ - return true; -} - -static char *game_text_format(const game_state *state) -{ - return board_text_format(state->w, state->h, state->board, - state->imm->forcefield); -} - -struct game_ui { - bool dragging; - int drag_anchor; - int drag_offset_x, drag_offset_y; - int drag_currpos; - bool *reachable; - int *bfs_queue; /* used as scratch in interpret_move */ -}; - -static game_ui *new_ui(const game_state *state) -{ - int w = state->w, h = state->h, wh = w*h; - game_ui *ui = snew(game_ui); - - ui->dragging = false; - ui->drag_anchor = ui->drag_currpos = -1; - ui->drag_offset_x = ui->drag_offset_y = -1; - ui->reachable = snewn(wh, bool); - memset(ui->reachable, 0, wh * sizeof(bool)); - ui->bfs_queue = snewn(wh, int); - - return ui; -} - -static void free_ui(game_ui *ui) -{ - sfree(ui->bfs_queue); - sfree(ui->reachable); - sfree(ui); -} - -static char *encode_ui(const game_ui *ui) -{ - return NULL; -} - -static void decode_ui(game_ui *ui, const char *encoding) -{ -} - -static void game_changed_state(game_ui *ui, const game_state *oldstate, - const game_state *newstate) -{ -} - -#define PREFERRED_TILESIZE 32 -#define TILESIZE (ds->tilesize) -#define BORDER (TILESIZE/2) -#define COORD(x) ( (x) * TILESIZE + BORDER ) -#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) -#define BORDER_WIDTH (1 + TILESIZE/20) -#define HIGHLIGHT_WIDTH (1 + TILESIZE/16) - -#define FLASH_INTERVAL 0.10F -#define FLASH_TIME 3*FLASH_INTERVAL - -struct game_drawstate { - int tilesize; - int w, h; - unsigned long *grid; /* what's currently displayed */ - bool started; -}; - -static char *interpret_move(const game_state *state, game_ui *ui, - const game_drawstate *ds, - int x, int y, int button) -{ - int w = state->w, h = state->h, wh = w*h; - int tx, ty, i, j; - int qhead, qtail; - - if (button == LEFT_BUTTON) { - tx = FROMCOORD(x); - ty = FROMCOORD(y); - - if (tx < 0 || tx >= w || ty < 0 || ty >= h || - !ISBLOCK(state->board[ty*w+tx])) - return NULL; /* this click has no effect */ - - /* - * User has clicked on a block. Find the block's anchor - * and register that we've started dragging it. - */ - i = ty*w+tx; - while (ISDIST(state->board[i])) - i -= state->board[i]; - assert(i >= 0 && i < wh); - - ui->dragging = true; - ui->drag_anchor = i; - ui->drag_offset_x = tx - (i % w); - ui->drag_offset_y = ty - (i / w); - ui->drag_currpos = i; - - /* - * Now we immediately bfs out from the current location of - * the anchor, to find all the places to which this block - * can be dragged. - */ - memset(ui->reachable, 0, wh * sizeof(bool)); - qhead = qtail = 0; - ui->reachable[i] = true; - ui->bfs_queue[qtail++] = i; - for (j = i; j < wh; j++) - if (state->board[j] == DIST(j - i)) - i = j; - while (qhead < qtail) { - int pos = ui->bfs_queue[qhead++]; - int x = pos % w, y = pos / w; - int dir; - - for (dir = 0; dir < 4; dir++) { - int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0); - int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0); - int newpos; - - if (x + dx < 0 || x + dx >= w || - y + dy < 0 || y + dy >= h) - continue; - - newpos = pos + dy*w + dx; - if (ui->reachable[newpos]) - continue; /* already done this one */ - - /* - * Now search the grid to see if the block we're - * dragging could fit into this space. - */ - for (j = i; j >= 0; j = (ISDIST(state->board[j]) ? - j - state->board[j] : -1)) { - int jx = (j+pos-ui->drag_anchor) % w; - int jy = (j+pos-ui->drag_anchor) / w; - int j2; - - if (jx + dx < 0 || jx + dx >= w || - jy + dy < 0 || jy + dy >= h) - break; /* this position isn't valid at all */ - - j2 = (j+pos-ui->drag_anchor) + dy*w + dx; - - if (state->board[j2] == EMPTY && - (!state->imm->forcefield[j2] || - state->board[ui->drag_anchor] == MAINANCHOR)) - continue; - while (ISDIST(state->board[j2])) - j2 -= state->board[j2]; - assert(j2 >= 0 && j2 < wh); - if (j2 == ui->drag_anchor) - continue; - else - break; - } - - if (j < 0) { - /* - * If we got to the end of that loop without - * disqualifying this position, mark it as - * reachable for this drag. - */ - ui->reachable[newpos] = true; - ui->bfs_queue[qtail++] = newpos; - } - } - } - - /* - * And that's it. Update the display to reflect the start - * of a drag. - */ - return UI_UPDATE; - } else if (button == LEFT_DRAG && ui->dragging) { - int dist, distlimit, dx, dy, s, px, py; - - tx = FROMCOORD(x); - ty = FROMCOORD(y); - - tx -= ui->drag_offset_x; - ty -= ui->drag_offset_y; - - /* - * Now search outwards from (tx,ty), in order of Manhattan - * distance, until we find a reachable square. - */ - distlimit = w+tx; - distlimit = max(distlimit, h+ty); - distlimit = max(distlimit, tx); - distlimit = max(distlimit, ty); - for (dist = 0; dist <= distlimit; dist++) { - for (dx = -dist; dx <= dist; dx++) - for (s = -1; s <= +1; s += 2) { - dy = s * (dist - abs(dx)); - px = tx + dx; - py = ty + dy; - if (px >= 0 && px < w && py >= 0 && py < h && - ui->reachable[py*w+px]) { - ui->drag_currpos = py*w+px; - return UI_UPDATE; - } - } - } - return NULL; /* give up - this drag has no effect */ - } else if (button == LEFT_RELEASE && ui->dragging) { - char data[256], *str; - - /* - * Terminate the drag, and if the piece has actually moved - * then return a move string quoting the old and new - * locations of the piece's anchor. - */ - if (ui->drag_anchor != ui->drag_currpos) { - sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos); - str = dupstr(data); - } else - str = ""; /* null move; just update the UI */ - - ui->dragging = false; - ui->drag_anchor = ui->drag_currpos = -1; - ui->drag_offset_x = ui->drag_offset_y = -1; - memset(ui->reachable, 0, wh * sizeof(bool)); - - return str; - } else if (button == ' ' && state->soln) { - /* - * Make the next move in the stored solution. - */ - char data[256]; - int a1, a2; - - a1 = state->soln->moves[state->soln_index*2]; - a2 = state->soln->moves[state->soln_index*2+1]; - if (a1 == state->lastmoved_pos) - a1 = state->lastmoved; - - sprintf(data, "M%d-%d", a1, a2); - return dupstr(data); - } - - return NULL; -} - -static bool move_piece(int w, int h, const unsigned char *src, - unsigned char *dst, bool *ff, int from, int to) -{ - int wh = w*h; - int i, j; - - if (!ISANCHOR(dst[from])) - return false; - - /* - * Scan to the far end of the piece's linked list. - */ - for (i = j = from; j < wh; j++) - if (src[j] == DIST(j - i)) - i = j; - - /* - * Remove the piece from its old location in the new - * game state. - */ - for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) - dst[j] = EMPTY; - - /* - * And put it back in at the new location. - */ - for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) { - int jn = j + to - from; - if (jn < 0 || jn >= wh) - return false; - if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) { - dst[jn] = src[j]; - } else { - return false; - } - } - - return true; -} - -static game_state *execute_move(const game_state *state, const char *move) -{ - int w = state->w, h = state->h /* , wh = w*h */; - char c; - int a1, a2, n, movesize; - game_state *ret = dup_game(state); - - while (*move) { - c = *move; - if (c == 'S') { - /* - * This is a solve move, so we just set up a stored - * solution path. - */ - if (ret->soln && --ret->soln->refcount <= 0) { - sfree(ret->soln->moves); - sfree(ret->soln); - } - ret->soln = snew(struct game_solution); - ret->soln->nmoves = 0; - ret->soln->moves = NULL; - ret->soln->refcount = 1; - ret->soln_index = 0; - ret->cheated = true; - - movesize = 0; - move++; - while (1) { - if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) { - free_game(ret); - return NULL; - } - - /* - * Special case: if the first move in the solution - * involves the piece for which we already have a - * partial stored move, adjust the source point to - * the original starting point of that piece. - */ - if (ret->soln->nmoves == 0 && a1 == ret->lastmoved) - a1 = ret->lastmoved_pos; - - if (ret->soln->nmoves >= movesize) { - movesize = (ret->soln->nmoves + 48) * 4 / 3; - ret->soln->moves = sresize(ret->soln->moves, - 2*movesize, int); - } - - ret->soln->moves[2*ret->soln->nmoves] = a1; - ret->soln->moves[2*ret->soln->nmoves+1] = a2; - ret->soln->nmoves++; - move += n; - if (*move != ',') - break; - move++; /* eat comma */ - } - } else if (c == 'M') { - move++; - if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 || - !move_piece(w, h, state->board, ret->board, - state->imm->forcefield, a1, a2)) { - free_game(ret); - return NULL; - } - if (a1 == ret->lastmoved) { - /* - * If the player has moved the same piece as they - * moved last time, don't increment the move - * count. In fact, if they've put the piece back - * where it started from, _decrement_ the move - * count. - */ - if (a2 == ret->lastmoved_pos) { - ret->movecount--; /* reverted last move */ - ret->lastmoved = ret->lastmoved_pos = -1; - } else { - ret->lastmoved = a2; - /* don't change lastmoved_pos */ - } - } else { - ret->lastmoved = a2; - ret->lastmoved_pos = a1; - ret->movecount++; - } - - /* - * If we have a stored solution path, see if we've - * strayed from it or successfully made the next move - * along it. - */ - if (ret->soln && ret->lastmoved_pos >= 0) { - if (ret->lastmoved_pos != - ret->soln->moves[ret->soln_index*2]) { - /* strayed from the path */ - ret->soln->refcount--; - assert(ret->soln->refcount > 0); - /* `state' at least still exists */ - ret->soln = NULL; - ret->soln_index = -1; - } else if (ret->lastmoved == - ret->soln->moves[ret->soln_index*2+1]) { - /* advanced along the path */ - ret->soln_index++; - if (ret->soln_index >= ret->soln->nmoves) { - /* finished the path! */ - ret->soln->refcount--; - assert(ret->soln->refcount > 0); - /* `state' at least still exists */ - ret->soln = NULL; - ret->soln_index = -1; - } - } - } - - if (ret->board[a2] == MAINANCHOR && - a2 == ret->ty * w + ret->tx && ret->completed < 0) - ret->completed = ret->movecount; - move += n; - } else { - free_game(ret); - return NULL; - } - if (*move == ';') - move++; - else if (*move) { - free_game(ret); - return NULL; - } - } - - return ret; -} - -/* ---------------------------------------------------------------------- - * Drawing routines. - */ - -static void game_compute_size(const game_params *params, int tilesize, - int *x, int *y) -{ - /* fool the macros */ - struct dummy { int tilesize; } dummy, *ds = &dummy; - dummy.tilesize = tilesize; - - *x = params->w * TILESIZE + 2*BORDER; - *y = params->h * TILESIZE + 2*BORDER; -} - -static void game_set_size(drawing *dr, game_drawstate *ds, - const game_params *params, int tilesize) -{ - ds->tilesize = tilesize; -} - -static void raise_colour(float *target, float *src, float *limit) -{ - int i; - for (i = 0; i < 3; i++) - target[i] = (2*src[i] + limit[i]) / 3; -} - -static float *game_colours(frontend *fe, int *ncolours) -{ - float *ret = snewn(3 * NCOLOURS, float); - - game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); - - /* - * When dragging a tile, we light it up a bit. - */ - raise_colour(ret+3*COL_DRAGGING, - ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT); - raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT, - ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT); - raise_colour(ret+3*COL_DRAGGING_LOWLIGHT, - ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT); - - /* - * The main tile is tinted blue. - */ - ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; - ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1]; - ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2]; - game_mkhighlight_specific(fe, ret, COL_MAIN, - COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT); - - /* - * And we light that up a bit too when dragging. - */ - raise_colour(ret+3*COL_MAIN_DRAGGING, - ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT); - raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT, - ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT); - raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT, - ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT); - - /* - * The target area on the floor is tinted green. - */ - ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; - ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1]; - ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2]; - game_mkhighlight_specific(fe, ret, COL_TARGET, - COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT); - - *ncolours = NCOLOURS; - return ret; -} - -static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) -{ - int w = state->w, h = state->h, wh = w*h; - struct game_drawstate *ds = snew(struct game_drawstate); - int i; - - ds->tilesize = 0; - ds->w = w; - ds->h = h; - ds->started = false; - ds->grid = snewn(wh, unsigned long); - for (i = 0; i < wh; i++) - ds->grid[i] = ~(unsigned long)0; - - return ds; -} - -static void game_free_drawstate(drawing *dr, game_drawstate *ds) -{ - sfree(ds->grid); - sfree(ds); -} - -#define BG_NORMAL 0x00000001UL -#define BG_TARGET 0x00000002UL -#define BG_FORCEFIELD 0x00000004UL -#define FLASH_LOW 0x00000008UL -#define FLASH_HIGH 0x00000010UL -#define FG_WALL 0x00000020UL -#define FG_MAIN 0x00000040UL -#define FG_NORMAL 0x00000080UL -#define FG_DRAGGING 0x00000100UL -#define FG_SHADOW 0x00000200UL -#define FG_SOLVEPIECE 0x00000400UL -#define FG_MAINPIECESH 11 -#define FG_SHADOWSH 19 - -#define PIECE_LBORDER 0x00000001UL -#define PIECE_TBORDER 0x00000002UL -#define PIECE_RBORDER 0x00000004UL -#define PIECE_BBORDER 0x00000008UL -#define PIECE_TLCORNER 0x00000010UL -#define PIECE_TRCORNER 0x00000020UL -#define PIECE_BLCORNER 0x00000040UL -#define PIECE_BRCORNER 0x00000080UL -#define PIECE_MASK 0x000000FFUL - -/* - * Utility function. - */ -#define TYPE_MASK 0xF000 -#define COL_MASK 0x0FFF -#define TYPE_RECT 0x0000 -#define TYPE_TLCIRC 0x4000 -#define TYPE_TRCIRC 0x5000 -#define TYPE_BLCIRC 0x6000 -#define TYPE_BRCIRC 0x7000 -static void maybe_rect(drawing *dr, int x, int y, int w, int h, - int coltype, int col2) -{ - int colour = coltype & COL_MASK, type = coltype & TYPE_MASK; - - if (colour > NCOLOURS) - return; - if (type == TYPE_RECT) { - draw_rect(dr, x, y, w, h, colour); - } else { - int cx, cy, r; - - clip(dr, x, y, w, h); - - cx = x; - cy = y; - r = w-1; - if (type & 0x1000) - cx += r; - if (type & 0x2000) - cy += r; - - if (col2 == -1 || col2 == coltype) { - assert(w == h); - draw_circle(dr, cx, cy, r, colour, colour); - } else { - /* - * We aim to draw a quadrant of a circle in two - * different colours. We do this using Bresenham's - * algorithm directly, because the Puzzles drawing API - * doesn't have a draw-sector primitive. - */ - int bx, by, bd, bd2; - int xm = (type & 0x1000 ? -1 : +1); - int ym = (type & 0x2000 ? -1 : +1); - - by = r; - bx = 0; - bd = 0; - while (by >= bx) { - /* - * Plot the point. - */ - { - int x1 = cx+xm*bx, y1 = cy+ym*bx; - int x2, y2; - - x2 = cx+xm*by; y2 = y1; - draw_rect(dr, min(x1,x2), min(y1,y2), - abs(x1-x2)+1, abs(y1-y2)+1, colour); - x2 = x1; y2 = cy+ym*by; - draw_rect(dr, min(x1,x2), min(y1,y2), - abs(x1-x2)+1, abs(y1-y2)+1, col2); - } - - bd += 2*bx + 1; - bd2 = bd - (2*by - 1); - if (abs(bd2) < abs(bd)) { - bd = bd2; - by--; - } - bx++; - } - } - - unclip(dr); - } -} - -static void draw_wallpart(drawing *dr, game_drawstate *ds, - int tx, int ty, unsigned long val, - int cl, int cc, int ch) -{ - int coords[6]; - - draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc); - if (val & PIECE_LBORDER) - draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE, - ch); - if (val & PIECE_RBORDER) - draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, - HIGHLIGHT_WIDTH, TILESIZE, cl); - if (val & PIECE_TBORDER) - draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch); - if (val & PIECE_BBORDER) - draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, - TILESIZE, HIGHLIGHT_WIDTH, cl); - if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) { - draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); - clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); - coords[0] = tx - 1; - coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[2] = tx + HIGHLIGHT_WIDTH; - coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[4] = tx - 1; - coords[5] = ty + TILESIZE; - draw_polygon(dr, coords, 3, ch, ch); - unclip(dr); - } else if (val & PIECE_BLCORNER) { - draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); - clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); - coords[0] = tx - 1; - coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[2] = tx + HIGHLIGHT_WIDTH; - coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[4] = tx - 1; - coords[5] = ty + TILESIZE; - draw_polygon(dr, coords, 3, cl, cl); - unclip(dr); - } - if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) { - draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); - clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); - coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[1] = ty - 1; - coords[2] = tx + TILESIZE; - coords[3] = ty - 1; - coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[5] = ty + HIGHLIGHT_WIDTH; - draw_polygon(dr, coords, 3, ch, ch); - unclip(dr); - } else if (val & PIECE_TRCORNER) { - draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); - clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH); - coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[1] = ty - 1; - coords[2] = tx + TILESIZE; - coords[3] = ty - 1; - coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1; - coords[5] = ty + HIGHLIGHT_WIDTH; - draw_polygon(dr, coords, 3, cl, cl); - unclip(dr); - } - if (val & PIECE_TLCORNER) - draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch); - if (val & PIECE_BRCORNER) - draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, - ty+TILESIZE-HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl); -} - -static void draw_piecepart(drawing *dr, game_drawstate *ds, - int tx, int ty, unsigned long val, - int cl, int cc, int ch) -{ - int x[6], y[6]; - - /* - * Drawing the blocks is hellishly fiddly. The blocks don't - * stretch to the full size of the tile; there's a border - * around them of size BORDER_WIDTH. Then they have bevelled - * borders of size HIGHLIGHT_WIDTH, and also rounded corners. - * - * I tried for some time to find a clean and clever way to - * figure out what needed drawing from the corner and border - * flags, but in the end the cleanest way I could find was the - * following. We divide the grid square into 25 parts by - * ruling four horizontal and four vertical lines across it; - * those lines are at BORDER_WIDTH and BORDER_WIDTH + - * HIGHLIGHT_WIDTH from the top, from the bottom, from the - * left and from the right. Then we carefully consider each of - * the resulting 25 sections of square, and decide separately - * what needs to go in it based on the flags. In complicated - * cases there can be up to five possibilities affecting any - * given section (no corner or border flags, just the corner - * flag, one border flag, the other border flag, both border - * flags). So there's a lot of very fiddly logic here and all - * I could really think to do was give it my best shot and - * then test it and correct all the typos. Not fun to write, - * and I'm sure it isn't fun to read either, but it seems to - * work. - */ - - x[0] = tx; - x[1] = x[0] + BORDER_WIDTH; - x[2] = x[1] + HIGHLIGHT_WIDTH; - x[5] = tx + TILESIZE; - x[4] = x[5] - BORDER_WIDTH; - x[3] = x[4] - HIGHLIGHT_WIDTH; - - y[0] = ty; - y[1] = y[0] + BORDER_WIDTH; - y[2] = y[1] + HIGHLIGHT_WIDTH; - y[5] = ty + TILESIZE; - y[4] = y[5] - BORDER_WIDTH; - y[3] = y[4] - HIGHLIGHT_WIDTH; - -#define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q] - - maybe_rect(dr, RECT(0,0), - (val & (PIECE_TLCORNER | PIECE_TBORDER | - PIECE_LBORDER)) ? -1 : cc, -1); - maybe_rect(dr, RECT(1,0), - (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 : - (val & PIECE_LBORDER) ? ch : cc, -1); - maybe_rect(dr, RECT(2,0), - (val & PIECE_TBORDER) ? -1 : cc, -1); - maybe_rect(dr, RECT(3,0), - (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 : - (val & PIECE_RBORDER) ? cl : cc, -1); - maybe_rect(dr, RECT(4,0), - (val & (PIECE_TRCORNER | PIECE_TBORDER | - PIECE_RBORDER)) ? -1 : cc, -1); - maybe_rect(dr, RECT(0,1), - (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 : - (val & PIECE_TBORDER) ? ch : cc, -1); - maybe_rect(dr, RECT(1,1), - (val & PIECE_TLCORNER) ? cc : -1, -1); - maybe_rect(dr, RECT(1,1), - (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC : - !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC : - (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1); - maybe_rect(dr, RECT(2,1), - (val & PIECE_TBORDER) ? ch : cc, -1); - maybe_rect(dr, RECT(3,1), - (val & PIECE_TRCORNER) ? cc : -1, -1); - maybe_rect(dr, RECT(3,1), - (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch : - (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl : - !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC : - (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC : - cc, ch); - maybe_rect(dr, RECT(4,1), - (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 : - (val & PIECE_TBORDER) ? ch : cc, -1); - maybe_rect(dr, RECT(0,2), - (val & PIECE_LBORDER) ? -1 : cc, -1); - maybe_rect(dr, RECT(1,2), - (val & PIECE_LBORDER) ? ch : cc, -1); - maybe_rect(dr, RECT(2,2), - cc, -1); - maybe_rect(dr, RECT(3,2), - (val & PIECE_RBORDER) ? cl : cc, -1); - maybe_rect(dr, RECT(4,2), - (val & PIECE_RBORDER) ? -1 : cc, -1); - maybe_rect(dr, RECT(0,3), - (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 : - (val & PIECE_BBORDER) ? cl : cc, -1); - maybe_rect(dr, RECT(1,3), - (val & PIECE_BLCORNER) ? cc : -1, -1); - maybe_rect(dr, RECT(1,3), - (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl : - (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch : - !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC : - (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC : - cc, cl); - maybe_rect(dr, RECT(2,3), - (val & PIECE_BBORDER) ? cl : cc, -1); - maybe_rect(dr, RECT(3,3), - (val & PIECE_BRCORNER) ? cc : -1, -1); - maybe_rect(dr, RECT(3,3), - (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC : - !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC : - (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1); - maybe_rect(dr, RECT(4,3), - (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 : - (val & PIECE_BBORDER) ? cl : cc, -1); - maybe_rect(dr, RECT(0,4), - (val & (PIECE_BLCORNER | PIECE_BBORDER | - PIECE_LBORDER)) ? -1 : cc, -1); - maybe_rect(dr, RECT(1,4), - (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 : - (val & PIECE_LBORDER) ? ch : cc, -1); - maybe_rect(dr, RECT(2,4), - (val & PIECE_BBORDER) ? -1 : cc, -1); - maybe_rect(dr, RECT(3,4), - (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 : - (val & PIECE_RBORDER) ? cl : cc, -1); - maybe_rect(dr, RECT(4,4), - (val & (PIECE_BRCORNER | PIECE_BBORDER | - PIECE_RBORDER)) ? -1 : cc, -1); - -#undef RECT -} - -static void draw_tile(drawing *dr, game_drawstate *ds, - int x, int y, unsigned long val) -{ - int tx = COORD(x), ty = COORD(y); - int cc, ch, cl; - - /* - * Draw the tile background. - */ - if (val & BG_TARGET) - cc = COL_TARGET; - else - cc = COL_BACKGROUND; - ch = cc+1; - cl = cc+2; - if (val & FLASH_LOW) - cc = cl; - else if (val & FLASH_HIGH) - cc = ch; - - draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc); - if (val & BG_FORCEFIELD) { - /* - * Cattle-grid effect to indicate that nothing but the - * main block can slide over this square. - */ - int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH)); - int i; - - for (i = 1; i < n; i += 3) { - draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl); - draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl); - } - } - - /* - * Draw the tile midground: a shadow of a block, for - * displaying partial solutions. - */ - if (val & FG_SHADOW) { - draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK, - cl, cl, cl); - } - - /* - * Draw the tile foreground, i.e. some section of a block or - * wall. - */ - if (val & FG_WALL) { - cc = COL_BACKGROUND; - ch = cc+1; - cl = cc+2; - if (val & FLASH_LOW) - cc = cl; - else if (val & FLASH_HIGH) - cc = ch; - - draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK, - cl, cc, ch); - } else if (val & (FG_MAIN | FG_NORMAL)) { - if (val & FG_DRAGGING) - cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING); - else - cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND); - ch = cc+1; - cl = cc+2; - - if (val & FLASH_LOW) - cc = cl; - else if (val & (FLASH_HIGH | FG_SOLVEPIECE)) - cc = ch; - - draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK, - cl, cc, ch); - } - - draw_update(dr, tx, ty, TILESIZE, TILESIZE); -} - -static unsigned long find_piecepart(int w, int h, int *dsf, int x, int y) -{ - int i = y*w+x; - int canon = dsf_canonify(dsf, i); - unsigned long val = 0; - - if (x == 0 || canon != dsf_canonify(dsf, i-1)) - val |= PIECE_LBORDER; - if (y== 0 || canon != dsf_canonify(dsf, i-w)) - val |= PIECE_TBORDER; - if (x == w-1 || canon != dsf_canonify(dsf, i+1)) - val |= PIECE_RBORDER; - if (y == h-1 || canon != dsf_canonify(dsf, i+w)) - val |= PIECE_BBORDER; - if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) && - canon != dsf_canonify(dsf, i-1-w)) - val |= PIECE_TLCORNER; - if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) && - canon != dsf_canonify(dsf, i+1-w)) - val |= PIECE_TRCORNER; - if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) && - canon != dsf_canonify(dsf, i-1+w)) - val |= PIECE_BLCORNER; - if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) && - canon != dsf_canonify(dsf, i+1+w)) - val |= PIECE_BRCORNER; - return val; -} - -static void game_redraw(drawing *dr, game_drawstate *ds, - const game_state *oldstate, const game_state *state, - int dir, const game_ui *ui, - float animtime, float flashtime) -{ - int w = state->w, h = state->h, wh = w*h; - unsigned char *board; - int *dsf; - int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst; - - if (!ds->started) { - /* - * The initial contents of the window are not guaranteed - * and can vary with front ends. To be on the safe side, - * all games should start by drawing a big - * background-colour rectangle covering the whole window. - */ - draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND); - ds->started = true; - } - - /* - * Construct the board we'll be displaying (which may be - * different from the one in state if ui describes a drag in - * progress). - */ - board = snewn(wh, unsigned char); - memcpy(board, state->board, wh); - if (ui->dragging) { - bool mpret = move_piece(w, h, state->board, board, - state->imm->forcefield, - ui->drag_anchor, ui->drag_currpos); - assert(mpret); - } - - if (state->soln) { - solvesrc = state->soln->moves[state->soln_index*2]; - solvedst = state->soln->moves[state->soln_index*2+1]; - if (solvesrc == state->lastmoved_pos) - solvesrc = state->lastmoved; - if (solvesrc == ui->drag_anchor) - solvesrc = ui->drag_currpos; - } else - solvesrc = solvedst = -1; - - /* - * Build a dsf out of that board, so we can conveniently tell - * which edges are connected and which aren't. - */ - dsf = snew_dsf(wh); - mainanchor = -1; - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) { - int i = y*w+x; - - if (ISDIST(board[i])) - dsf_merge(dsf, i, i - board[i]); - if (board[i] == MAINANCHOR) - mainanchor = i; - if (board[i] == WALL) { - if (x > 0 && board[i-1] == WALL) - dsf_merge(dsf, i, i-1); - if (y > 0 && board[i-w] == WALL) - dsf_merge(dsf, i, i-w); - } - } - assert(mainanchor >= 0); - mainpos = dsf_canonify(dsf, mainanchor); - dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1; - solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1; - - /* - * Now we can construct the data about what we want to draw. - */ - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) { - int i = y*w+x; - int j; - unsigned long val; - int canon; - - /* - * See if this square is part of the target area. - */ - j = i + mainanchor - (state->ty * w + state->tx); - while (j >= 0 && j < wh && ISDIST(board[j])) - j -= board[j]; - if (j == mainanchor) - val = BG_TARGET; - else - val = BG_NORMAL; - - if (state->imm->forcefield[i]) - val |= BG_FORCEFIELD; - - if (flashtime > 0) { - int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1; - val |= (flashtype ? FLASH_LOW : FLASH_HIGH); - } - - if (board[i] != EMPTY) { - canon = dsf_canonify(dsf, i); - - if (board[i] == WALL) - val |= FG_WALL; - else if (canon == mainpos) - val |= FG_MAIN; - else - val |= FG_NORMAL; - if (canon == dragpos) - val |= FG_DRAGGING; - if (canon == solvepos) - val |= FG_SOLVEPIECE; - - /* - * Now look around to see if other squares - * belonging to the same block are adjacent to us. - */ - val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH; - } - - /* - * If we're in the middle of showing a solution, - * display a shadow piece for the target of the - * current move. - */ - if (solvepos >= 0) { - int si = i - solvedst + solvesrc; - if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) { - val |= find_piecepart(w, h, dsf, - si % w, si / w) << FG_SHADOWSH; - val |= FG_SHADOW; - } - } - - if (val != ds->grid[i]) { - draw_tile(dr, ds, x, y, val); - ds->grid[i] = val; - } - } - - /* - * Update the status bar. - */ - { - char statusbuf[256]; - - sprintf(statusbuf, "%sMoves: %d", - (state->completed >= 0 ? - (state->cheated ? "Auto-solved. " : "COMPLETED! ") : - (state->cheated ? "Auto-solver used. " : "")), - (state->completed >= 0 ? state->completed : state->movecount)); - if (state->minmoves >= 0) - sprintf(statusbuf+strlen(statusbuf), " (min %d)", - state->minmoves); - - status_bar(dr, statusbuf); - } - - sfree(dsf); - sfree(board); -} - -static float game_anim_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - return 0.0F; -} - -static float game_flash_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - if (oldstate->completed < 0 && newstate->completed >= 0) - return FLASH_TIME; - - return 0.0F; -} - -static void game_get_cursor_location(const game_ui *ui, - const game_drawstate *ds, - const game_state *state, - const game_params *params, - int *x, int *y, int *w, int *h) -{ -} - -static int game_status(const game_state *state) -{ - return state->completed ? +1 : 0; -} - -static bool game_timing_state(const game_state *state, game_ui *ui) -{ - return true; -} - -static void game_print_size(const game_params *params, float *x, float *y) -{ -} - -static void game_print(drawing *dr, const game_state *state, int tilesize) -{ -} - -#ifdef COMBINED -#define thegame slide -#endif - -const struct game thegame = { - "Slide", NULL, NULL, - default_params, - game_fetch_preset, NULL, - decode_params, - encode_params, - free_params, - dup_params, - true, game_configure, custom_params, - validate_params, - new_game_desc, - validate_desc, - new_game, - dup_game, - free_game, - true, solve_game, - true, game_can_format_as_text_now, game_text_format, - new_ui, - free_ui, - encode_ui, - decode_ui, - NULL, /* game_request_keys */ - game_changed_state, - interpret_move, - execute_move, - PREFERRED_TILESIZE, game_compute_size, game_set_size, - game_colours, - game_new_drawstate, - game_free_drawstate, - game_redraw, - game_anim_length, - game_flash_length, - game_get_cursor_location, - game_status, - false, false, game_print_size, game_print, - true, /* wants_statusbar */ - false, game_timing_state, - 0, /* flags */ -}; - -#ifdef STANDALONE_SOLVER - -#include - -int main(int argc, char **argv) -{ - game_params *p; - game_state *s; - char *id = NULL, *desc; - const char *err; - bool count = false; - int ret; - int *moves; - - while (--argc > 0) { - char *p = *++argv; - /* - if (!strcmp(p, "-v")) { - verbose = true; - } else - */ - if (!strcmp(p, "-c")) { - count = true; - } else if (*p == '-') { - fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); - return 1; - } else { - id = p; - } - } - - if (!id) { - fprintf(stderr, "usage: %s [-c | -v] \n", argv[0]); - return 1; - } - - desc = strchr(id, ':'); - if (!desc) { - fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); - return 1; - } - *desc++ = '\0'; - - p = default_params(); - decode_params(p, id); - err = validate_desc(p, desc); - if (err) { - fprintf(stderr, "%s: %s\n", argv[0], err); - return 1; - } - s = new_game(NULL, p, desc); - - ret = solve_board(s->w, s->h, s->board, s->imm->forcefield, - s->tx, s->ty, -1, &moves); - if (ret < 0) { - printf("No solution found\n"); - } else { - int index = 0; - if (count) { - printf("%d moves required\n", ret); - return 0; - } - while (1) { - bool moveret; - char *text = board_text_format(s->w, s->h, s->board, - s->imm->forcefield); - game_state *s2; - - printf("position %d:\n%s", index, text); - - if (index >= ret) - break; - - s2 = dup_game(s); - moveret = move_piece(s->w, s->h, s->board, - s2->board, s->imm->forcefield, - moves[index*2], moves[index*2+1]); - assert(moveret); - - free_game(s); - s = s2; - index++; - } - } - - return 0; -} - -#endif diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.R b/apps/plugins/puzzles/src/unfinished/sokoban.R deleted file mode 100644 index 3b6dab56ba..0000000000 --- a/apps/plugins/puzzles/src/unfinished/sokoban.R +++ /dev/null @@ -1,19 +0,0 @@ -# -*- makefile -*- - -sokoban : [X] GTK COMMON sokoban sokoban-icon|no-icon - -sokoban : [G] WINDOWS COMMON sokoban sokoban.res? - -ALL += sokoban[COMBINED] - -!begin am gtk -GAMES += sokoban -!end - -!begin >list.c - A(sokoban) \ -!end - -!begin >gamedesc.txt -sokoban:sokoban.exe:Sokoban:Barrel-pushing puzzle:Push all the barrels into the target squares. -!end diff --git a/apps/plugins/puzzles/src/unfinished/sokoban.c b/apps/plugins/puzzles/src/unfinished/sokoban.c deleted file mode 100644 index ecc222c906..0000000000 --- a/apps/plugins/puzzles/src/unfinished/sokoban.c +++ /dev/null @@ -1,1486 +0,0 @@ -/* - * sokoban.c: An implementation of the well-known Sokoban barrel- - * pushing game. Random generation is too simplistic to be - * credible, but the rest of the gameplay works well enough to use - * it with hand-written level descriptions. - */ - -/* - * TODO: - * - * - I think it would be better to ditch the `prev' array, and - * instead make the `dist' array strictly monotonic (by having - * each distance be something like I*A+S, where A is the grid - * area, I the number of INITIAL squares trampled on, and S the - * number of harmless spaces moved through). This would permit - * the path-tracing when a pull is actually made to choose - * randomly from all the possible shortest routes, which would - * be superior in terms of eliminating directional bias. - * + So when tracing the path back to the current px,py, we - * look at all four adjacent squares, find the minimum - * distance, check that it's _strictly smaller_ than that of - * the current square, and restrict our choice to precisely - * those squares with that minimum distance. - * + The other place `prev' is currently used is in the check - * for consistency of a pull. We would have to replace the - * check for whether prev[ny*w+nx]==oy*w+ox with a check that - * made sure there was at least one adjacent square with a - * smaller distance which _wasn't_ oy*w+ox. Then when we did - * the path-tracing we'd also have to take this special case - * into account. - * - * - More discriminating choice of pull. (Snigger.) - * + favour putting targets in clumps - * + try to shoot for a reasonably consistent number of barrels - * (adjust willingness to generate a new barrel depending on - * how many are already present) - * + adjust willingness to break new ground depending on how - * much is already broken - * - * - generation time parameters: - * + enable NetHack mode (and find a better place for the hole) - * + decide how many of the remaining Is should be walls - * - * - at the end of generation, randomly position the starting - * player coordinates, probably by (somehow) reusing the same - * bfs currently inside the loop. - * - * - possible backtracking? - * - * - IWBNI we could spot completely unreachable bits of level at - * the outside, and not bother drawing grid lines for them. The - * NH levels currently look a bit weird with grid lines on the - * outside of the boundary. - */ - -#include -#include -#include -#include -#include -#include - -#include "puzzles.h" - -/* - * Various subsets of these constants are used during game - * generation, game play, game IDs and the game_drawstate. - */ -#define INITIAL 'i' /* used only in game generation */ -#define SPACE 's' -#define WALL 'w' -#define PIT 'p' -#define DEEP_PIT 'd' -#define TARGET 't' -#define BARREL 'b' -#define BARRELTARGET 'f' /* target is 'f'illed */ -#define PLAYER 'u' /* yo'u'; used in game IDs */ -#define PLAYERTARGET 'v' /* bad letter: v is to u as t is to s */ -#define INVALID '!' /* used in drawstate to force redraw */ -/* - * We also support the use of any capital letter as a barrel, which - * will be displayed with that letter as a label. (This facilitates - * people distributing annotated game IDs for particular Sokoban - * levels, so they can accompany them with verbal instructions - * about pushing particular barrels in particular ways.) Therefore, - * to find out whether something is a barrel, we need a test - * function which does a bit more than just comparing to BARREL. - * - * When resting on target squares, capital-letter barrels are - * replaced with their control-character value (A -> ^A). - */ -#define IS_PLAYER(c) ( (c)==PLAYER || (c)==PLAYERTARGET ) -#define IS_BARREL(c) ( (c)==BARREL || (c)==BARRELTARGET || \ - ((c)>='A' && (c)<='Z') || ((c)>=1 && (c)<=26) ) -#define IS_ON_TARGET(c) ( (c)==TARGET || (c)==BARRELTARGET || \ - (c)==PLAYERTARGET || ((c)>=1 && (c)<=26) ) -#define TARGETISE(b) ( (b)==BARREL ? BARRELTARGET : (b)-('A'-1) ) -#define DETARGETISE(b) ( (b)==BARRELTARGET ? BARREL : (b)+('A'-1) ) -#define BARREL_LABEL(b) ( (b)>='A'&&(b)<='Z' ? (b) : \ - (b)>=1 && (b)<=26 ? (b)+('A'-1) : 0 ) - -#define DX(d) (d == 0 ? -1 : d == 2 ? +1 : 0) -#define DY(d) (d == 1 ? -1 : d == 3 ? +1 : 0) - -#define FLASH_LENGTH 0.3F - -enum { - COL_BACKGROUND, - COL_TARGET, - COL_PIT, - COL_DEEP_PIT, - COL_BARREL, - COL_PLAYER, - COL_TEXT, - COL_GRID, - COL_OUTLINE, - COL_HIGHLIGHT, - COL_LOWLIGHT, - COL_WALL, - NCOLOURS -}; - -struct game_params { - int w, h; - /* - * FIXME: a parameter involving degree of filling in? - */ -}; - -struct game_state { - game_params p; - unsigned char *grid; - int px, py; - bool completed; -}; - -static game_params *default_params(void) -{ - game_params *ret = snew(game_params); - - ret->w = 12; - ret->h = 10; - - return ret; -} - -static void free_params(game_params *params) -{ - sfree(params); -} - -static game_params *dup_params(const game_params *params) -{ - game_params *ret = snew(game_params); - *ret = *params; /* structure copy */ - return ret; -} - -static const struct game_params sokoban_presets[] = { - { 12, 10 }, - { 16, 12 }, - { 20, 16 }, -}; - -static bool game_fetch_preset(int i, char **name, game_params **params) -{ - game_params p, *ret; - char *retname; - char namebuf[80]; - - if (i < 0 || i >= lenof(sokoban_presets)) - return false; - - p = sokoban_presets[i]; - ret = dup_params(&p); - sprintf(namebuf, "%dx%d", ret->w, ret->h); - retname = dupstr(namebuf); - - *params = ret; - *name = retname; - return true; -} - -static void decode_params(game_params *params, char const *string) -{ - params->w = params->h = atoi(string); - while (*string && isdigit((unsigned char)*string)) string++; - if (*string == 'x') { - string++; - params->h = atoi(string); - } -} - -static char *encode_params(const game_params *params, bool full) -{ - char data[256]; - - sprintf(data, "%dx%d", params->w, params->h); - - return dupstr(data); -} - -static config_item *game_configure(const game_params *params) -{ - config_item *ret; - char buf[80]; - - ret = snewn(3, config_item); - - ret[0].name = "Width"; - ret[0].type = C_STRING; - sprintf(buf, "%d", params->w); - ret[0].u.string.sval = dupstr(buf); - - ret[1].name = "Height"; - ret[1].type = C_STRING; - sprintf(buf, "%d", params->h); - ret[1].u.string.sval = dupstr(buf); - - ret[2].name = NULL; - ret[2].type = C_END; - - return ret; -} - -static game_params *custom_params(const config_item *cfg) -{ - game_params *ret = snew(game_params); - - ret->w = atoi(cfg[0].u.string.sval); - ret->h = atoi(cfg[1].u.string.sval); - - return ret; -} - -static const char *validate_params(const game_params *params, bool full) -{ - if (params->w < 4 || params->h < 4) - return "Width and height must both be at least 4"; - - return NULL; -} - -/* ---------------------------------------------------------------------- - * Game generation mechanism. - * - * To generate a Sokoban level, we begin with a completely blank - * grid and make valid inverse moves. Grid squares can be in a - * number of states. The states are: - * - * - INITIAL: this square has not as yet been touched by any - * inverse move, which essentially means we haven't decided what - * it is yet. - * - * - SPACE: this square is a space. - * - * - TARGET: this square is a space which is also the target for a - * barrel. - * - * - BARREL: this square contains a barrel. - * - * - BARRELTARGET: this square contains a barrel _on_ a target. - * - * - WALL: this square is a wall. - * - * - PLAYER: this square contains the player. - * - * - PLAYERTARGET: this square contains the player on a target. - * - * We begin with every square of the in state INITIAL, apart from a - * solid ring of WALLs around the edge. We randomly position the - * PLAYER somewhere. Thereafter our valid moves are: - * - * - to move the PLAYER in one direction _pulling_ a barrel after - * us. For this to work, we must have SPACE or INITIAL in the - * direction we're moving, and BARREL or BARRELTARGET in the - * direction we're moving away from. We leave SPACE or TARGET - * respectively in the vacated square. - * - * - to create a new barrel by transforming an INITIAL square into - * BARRELTARGET. - * - * - to move the PLAYER freely through SPACE and TARGET squares, - * leaving SPACE or TARGET where it started. - * - * - to move the player through INITIAL squares, carving a tunnel - * of SPACEs as it goes. - * - * We try to avoid destroying INITIAL squares wherever possible (if - * there's a path to where we want to be using only SPACE, then we - * should always use that). At the end of generation, every square - * still in state INITIAL is one which was not required at any - * point during generation, which means we can randomly choose - * whether to make it SPACE or WALL. - * - * It's unclear as yet what the right strategy for wall placement - * should be. Too few WALLs will yield many alternative solutions - * to the puzzle, whereas too many might rule out so many - * possibilities that the intended solution becomes obvious. - */ - -static void sokoban_generate(int w, int h, unsigned char *grid, int moves, - bool nethack, random_state *rs) -{ - struct pull { - int ox, oy, nx, ny, score; - }; - - struct pull *pulls; - int *dist, *prev, *heap; - int x, y, px, py, i, j, d, heapsize, npulls; - - pulls = snewn(w * h * 4, struct pull); - dist = snewn(w * h, int); - prev = snewn(w * h, int); - heap = snewn(w * h, int); - - /* - * Configure the initial grid. - */ - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - grid[y*w+x] = (x == 0 || y == 0 || x == w-1 || y == h-1 ? - WALL : INITIAL); - if (nethack) - grid[1] = DEEP_PIT; - - /* - * Place the player. - */ - i = random_upto(rs, (w-2) * (h-2)); - x = 1 + i % (w-2); - y = 1 + i / (w-2); - grid[y*w+x] = SPACE; - px = x; - py = y; - - /* - * Now loop around making random inverse Sokoban moves. In this - * loop we aim to make one actual barrel-pull per iteration, - * plus as many free moves as are necessary to get into - * position for that pull. - */ - while (moves-- >= 0) { - /* - * First enumerate all the viable barrel-pulls we can - * possibly make, counting two pulls of the same barrel in - * different directions as different. We also include pulls - * we can perform by creating a new barrel. Each pull is - * marked with the amount of violence it would have to do - * to the grid. - */ - npulls = 0; - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - for (d = 0; d < 4; d++) { - int dx = DX(d); - int dy = DY(d); - int nx = x + dx, ny = y + dy; - int npx = nx + dx, npy = ny + dy; - int score = 0; - - /* - * The candidate move is to put the player at - * (nx,ny), and move him to (npx,npy), pulling - * a barrel at (x,y) to (nx,ny). So first we - * must check that all those squares are within - * the boundaries of the grid. For this it is - * sufficient to check npx,npy. - */ - if (npx < 0 || npx >= w || npy < 0 || npy >= h) - continue; - - /* - * (x,y) must either be a barrel, or a square - * which we can convert into a barrel. - */ - switch (grid[y*w+x]) { - case BARREL: case BARRELTARGET: - break; - case INITIAL: - if (nethack) - continue; - score += 10 /* new_barrel_score */; - break; - case DEEP_PIT: - if (!nethack) - continue; - break; - default: - continue; - } - - /* - * (nx,ny) must either be a space, or a square - * which we can convert into a space. - */ - switch (grid[ny*w+nx]) { - case SPACE: case TARGET: - break; - case INITIAL: - score += 3 /* new_space_score */; - break; - default: - continue; - } - - /* - * (npx,npy) must also either be a space, or a - * square which we can convert into a space. - */ - switch (grid[npy*w+npx]) { - case SPACE: case TARGET: - break; - case INITIAL: - score += 3 /* new_space_score */; - break; - default: - continue; - } - - /* - * That's sufficient to tag this as a possible - * pull right now. We still don't know if we - * can reach the required player position, but - * that's a job for the subsequent BFS phase to - * tell us. - */ - pulls[npulls].ox = x; - pulls[npulls].oy = y; - pulls[npulls].nx = nx; - pulls[npulls].ny = ny; - pulls[npulls].score = score; -#ifdef GENERATION_DIAGNOSTICS - printf("found potential pull: (%d,%d)-(%d,%d) cost %d\n", - pulls[npulls].ox, pulls[npulls].oy, - pulls[npulls].nx, pulls[npulls].ny, - pulls[npulls].score); -#endif - npulls++; - } -#ifdef GENERATION_DIAGNOSTICS - printf("found %d potential pulls\n", npulls); -#endif - - /* - * If there are no pulls available at all, we give up. - * - * (FIXME: or perhaps backtrack?) - */ - if (npulls == 0) - break; - - /* - * Now we do a BFS from our current position, to find all - * the squares we can get the player into. - * - * This BFS is unusually tricky. We want to give a positive - * distance only to squares which we have to carve through - * INITIALs to get to, which means we can't just stick - * every square we reach on the end of our to-do list. - * Instead, we must maintain our list as a proper priority - * queue. - */ - for (i = 0; i < w*h; i++) - dist[i] = prev[i] = -1; - - heap[0] = py*w+px; - heapsize = 1; - dist[py*w+px] = 0; - -#define PARENT(n) ( ((n)-1)/2 ) -#define LCHILD(n) ( 2*(n)+1 ) -#define RCHILD(n) ( 2*(n)+2 ) -#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0) - - while (heapsize > 0) { - /* - * Pull the smallest element off the heap: it's at - * position 0. Move the arbitrary element from the very - * end of the heap into position 0. - */ - y = heap[0] / w; - x = heap[0] % w; - - heapsize--; - heap[0] = heap[heapsize]; - - /* - * Now repeatedly move that arbitrary element down the - * heap by swapping it with the more suitable of its - * children. - */ - i = 0; - while (1) { - int lc, rc; - - lc = LCHILD(i); - rc = RCHILD(i); - - if (lc >= heapsize) - break; /* we've hit bottom */ - - if (rc >= heapsize) { - /* - * Special case: there is only one child to - * check. - */ - if (dist[heap[i]] > dist[heap[lc]]) - SWAP(heap[i], heap[lc]); - - /* _Now_ we've hit bottom. */ - break; - } else { - /* - * The common case: there are two children and - * we must check them both. - */ - if (dist[heap[i]] > dist[heap[lc]] || - dist[heap[i]] > dist[heap[rc]]) { - /* - * Pick the more appropriate child to swap with - * (i.e. the one which would want to be the - * parent if one were above the other - as one - * is about to be). - */ - if (dist[heap[lc]] > dist[heap[rc]]) { - SWAP(heap[i], heap[rc]); - i = rc; - } else { - SWAP(heap[i], heap[lc]); - i = lc; - } - } else { - /* This element is in the right place; we're done. */ - break; - } - } - } - - /* - * OK, that's given us (x,y) for this phase of the - * search. Now try all directions from here. - */ - - for (d = 0; d < 4; d++) { - int dx = DX(d); - int dy = DY(d); - int nx = x + dx, ny = y + dy; - if (nx < 0 || nx >= w || ny < 0 || ny >= h) - continue; - if (grid[ny*w+nx] != SPACE && grid[ny*w+nx] != TARGET && - grid[ny*w+nx] != INITIAL) - continue; - if (dist[ny*w+nx] == -1) { - dist[ny*w+nx] = dist[y*w+x] + (grid[ny*w+nx] == INITIAL); - prev[ny*w+nx] = y*w+x; - - /* - * Now insert ny*w+nx at the end of the heap, - * and move it down to its appropriate resting - * place. - */ - i = heapsize; - heap[heapsize++] = ny*w+nx; - - /* - * Swap element n with its parent repeatedly to - * preserve the heap property. - */ - - while (i > 0) { - int p = PARENT(i); - - if (dist[heap[p]] > dist[heap[i]]) { - SWAP(heap[p], heap[i]); - i = p; - } else - break; - } - } - } - } - -#undef PARENT -#undef LCHILD -#undef RCHILD -#undef SWAP - -#ifdef GENERATION_DIAGNOSTICS - printf("distance map:\n"); - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - int d = dist[i*w+j]; - int c; - if (d < 0) - c = '#'; - else if (d >= 36) - c = '!'; - else if (d >= 10) - c = 'A' - 10 + d; - else - c = '0' + d; - putchar(c); - } - putchar('\n'); - } -#endif - - /* - * Now we can go back through the `pulls' array, adjusting - * the score for each pull depending on how hard it is to - * reach its starting point, and also throwing out any - * whose starting points are genuinely unreachable even - * with the possibility of carving through INITIAL squares. - */ - for (i = j = 0; i < npulls; i++) { -#ifdef GENERATION_DIAGNOSTICS - printf("potential pull (%d,%d)-(%d,%d)", - pulls[i].ox, pulls[i].oy, - pulls[i].nx, pulls[i].ny); -#endif - x = pulls[i].nx; - y = pulls[i].ny; - if (dist[y*w+x] < 0) { -#ifdef GENERATION_DIAGNOSTICS - printf(" unreachable\n"); -#endif - continue; /* this pull isn't feasible at all */ - } else { - /* - * Another nasty special case we have to check is - * whether the initial barrel location (ox,oy) is - * on the path used to reach the square. This can - * occur if that square is in state INITIAL: the - * pull is initially considered valid on the basis - * that the INITIAL can become BARRELTARGET, and - * it's also considered reachable on the basis that - * INITIAL can be turned into SPACE, but it can't - * be both at once. - * - * Fortunately, if (ox,oy) is on the path at all, - * it must be only one space from the end, so this - * is easy to spot and rule out. - */ - if (prev[y*w+x] == pulls[i].oy*w+pulls[i].ox) { -#ifdef GENERATION_DIAGNOSTICS - printf(" goes through itself\n"); -#endif - continue; /* this pull isn't feasible at all */ - } - pulls[j] = pulls[i]; /* structure copy */ - pulls[j].score += dist[y*w+x] * 3 /* new_space_score */; -#ifdef GENERATION_DIAGNOSTICS - printf(" reachable at distance %d (cost now %d)\n", - dist[y*w+x], pulls[j].score); -#endif - j++; - } - } - npulls = j; - - /* - * Again, if there are no pulls available at all, we give - * up. - * - * (FIXME: or perhaps backtrack?) - */ - if (npulls == 0) - break; - - /* - * Now choose which pull to make. On the one hand we should - * prefer pulls which do less damage to the INITIAL squares - * (thus, ones for which we can already get into position - * via existing SPACEs, and for which the barrel already - * exists and doesn't have to be invented); on the other, - * we want to avoid _always_ preferring such pulls, on the - * grounds that that will lead to levels without very much - * stuff in. - * - * When creating new barrels, we prefer creations which are - * next to existing TARGET squares. - * - * FIXME: for the moment I'll make this very simple indeed. - */ - i = random_upto(rs, npulls); - - /* - * Actually make the pull, including carving a path to get - * to the site if necessary. - */ - x = pulls[i].nx; - y = pulls[i].ny; - while (prev[y*w+x] >= 0) { - int p; - - if (grid[y*w+x] == INITIAL) - grid[y*w+x] = SPACE; - - p = prev[y*w+x]; - y = p / w; - x = p % w; - } - px = 2*pulls[i].nx - pulls[i].ox; - py = 2*pulls[i].ny - pulls[i].oy; - if (grid[py*w+px] == INITIAL) - grid[py*w+px] = SPACE; - if (grid[pulls[i].ny*w+pulls[i].nx] == TARGET) - grid[pulls[i].ny*w+pulls[i].nx] = BARRELTARGET; - else - grid[pulls[i].ny*w+pulls[i].nx] = BARREL; - if (grid[pulls[i].oy*w+pulls[i].ox] == BARREL) - grid[pulls[i].oy*w+pulls[i].ox] = SPACE; - else if (grid[pulls[i].oy*w+pulls[i].ox] != DEEP_PIT) - grid[pulls[i].oy*w+pulls[i].ox] = TARGET; - } - - sfree(heap); - sfree(prev); - sfree(dist); - sfree(pulls); - - if (grid[py*w+px] == TARGET) - grid[py*w+px] = PLAYERTARGET; - else - grid[py*w+px] = PLAYER; -} - -static char *new_game_desc(const game_params *params, random_state *rs, - char **aux, bool interactive) -{ - int w = params->w, h = params->h; - char *desc; - int desclen, descpos, descsize, prev, count; - unsigned char *grid; - int i, j; - - /* - * FIXME: perhaps some more interesting means of choosing how - * many moves to try? - */ - grid = snewn(w*h, unsigned char); - sokoban_generate(w, h, grid, w*h, false, rs); - - desclen = descpos = descsize = 0; - desc = NULL; - prev = -1; - count = 0; - for (i = 0; i < w*h; i++) { - if (descsize < desclen + 40) { - descsize = desclen + 100; - desc = sresize(desc, descsize, char); - desc[desclen] = '\0'; - } - switch (grid[i]) { - case INITIAL: - j = 'w'; /* FIXME: make some of these 's'? */ - break; - case SPACE: - j = 's'; - break; - case WALL: - j = 'w'; - break; - case TARGET: - j = 't'; - break; - case BARREL: - j = 'b'; - break; - case BARRELTARGET: - j = 'f'; - break; - case DEEP_PIT: - j = 'd'; - break; - case PLAYER: - j = 'u'; - break; - case PLAYERTARGET: - j = 'v'; - break; - default: - j = '?'; - break; - } - assert(j != '?'); - if (j != prev) { - desc[desclen++] = j; - descpos = desclen; - prev = j; - count = 1; - } else { - count++; - desclen = descpos + sprintf(desc+descpos, "%d", count); - } - } - - sfree(grid); - - return desc; -} - -static const char *validate_desc(const game_params *params, const char *desc) -{ - int w = params->w, h = params->h; - int area = 0; - int nplayers = 0; - - while (*desc) { - int c = *desc++; - int n = 1; - if (*desc && isdigit((unsigned char)*desc)) { - n = atoi(desc); - while (*desc && isdigit((unsigned char)*desc)) desc++; - } - - area += n; - - if (c == PLAYER || c == PLAYERTARGET) - nplayers += n; - else if (c == INITIAL || c == SPACE || c == WALL || c == TARGET || - c == PIT || c == DEEP_PIT || IS_BARREL(c)) - /* ok */; - else - return "Invalid character in game description"; - } - - if (area > w*h) - return "Too much data in game description"; - if (area < w*h) - return "Too little data in game description"; - if (nplayers < 1) - return "No starting player position specified"; - if (nplayers > 1) - return "More than one starting player position specified"; - - return NULL; -} - -static game_state *new_game(midend *me, const game_params *params, - const char *desc) -{ - int w = params->w, h = params->h; - game_state *state = snew(game_state); - int i; - - state->p = *params; /* structure copy */ - state->grid = snewn(w*h, unsigned char); - state->px = state->py = -1; - state->completed = false; - - i = 0; - - while (*desc) { - int c = *desc++; - int n = 1; - if (*desc && isdigit((unsigned char)*desc)) { - n = atoi(desc); - while (*desc && isdigit((unsigned char)*desc)) desc++; - } - - if (c == PLAYER || c == PLAYERTARGET) { - state->py = i / w; - state->px = i % w; - c = IS_ON_TARGET(c) ? TARGET : SPACE; - } - - while (n-- > 0) - state->grid[i++] = c; - } - - assert(i == w*h); - assert(state->px != -1 && state->py != -1); - - return state; -} - -static game_state *dup_game(const game_state *state) -{ - int w = state->p.w, h = state->p.h; - game_state *ret = snew(game_state); - - ret->p = state->p; /* structure copy */ - ret->grid = snewn(w*h, unsigned char); - memcpy(ret->grid, state->grid, w*h); - ret->px = state->px; - ret->py = state->py; - ret->completed = state->completed; - - return ret; -} - -static void free_game(game_state *state) -{ - sfree(state->grid); - sfree(state); -} - -static char *solve_game(const game_state *state, const game_state *currstate, - const char *aux, const char **error) -{ - return NULL; -} - -static bool game_can_format_as_text_now(const game_params *params) -{ - return true; -} - -static char *game_text_format(const game_state *state) -{ - return NULL; -} - -static game_ui *new_ui(const game_state *state) -{ - return NULL; -} - -static void free_ui(game_ui *ui) -{ -} - -static char *encode_ui(const game_ui *ui) -{ - return NULL; -} - -static void decode_ui(game_ui *ui, const char *encoding) -{ -} - -static void game_changed_state(game_ui *ui, const game_state *oldstate, - const game_state *newstate) -{ -} - -struct game_drawstate { - game_params p; - int tilesize; - bool started; - unsigned short *grid; -}; - -#define PREFERRED_TILESIZE 32 -#define TILESIZE (ds->tilesize) -#define BORDER (TILESIZE) -#define HIGHLIGHT_WIDTH (TILESIZE / 10) -#define COORD(x) ( (x) * TILESIZE + BORDER ) -#define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) - -/* - * I'm going to need to do most of the move-type analysis in both - * interpret_move and execute_move, so I'll abstract it out into a - * subfunction. move_type() returns -1 for an illegal move, 0 for a - * movement, and 1 for a push. - */ -int move_type(const game_state *state, int dx, int dy) -{ - int w = state->p.w, h = state->p.h; - int px = state->px, py = state->py; - int nx, ny, nbx, nby; - - assert(dx >= -1 && dx <= +1); - assert(dy >= -1 && dy <= +1); - assert(dx || dy); - - nx = px + dx; - ny = py + dy; - - /* - * Disallow any move that goes off the grid. - */ - if (nx < 0 || nx >= w || ny < 0 || ny >= h) - return -1; - - /* - * Examine the target square of the move to see whether it's a - * space, a barrel, or a wall. - */ - - if (state->grid[ny*w+nx] == WALL || - state->grid[ny*w+nx] == PIT || - state->grid[ny*w+nx] == DEEP_PIT) - return -1; /* this one's easy; just disallow it */ - - if (IS_BARREL(state->grid[ny*w+nx])) { - /* - * This is a push move. For a start, that means it must not - * be diagonal. - */ - if (dy && dx) - return -1; - - /* - * Now find the location of the third square involved in - * the push, and stop if it's off the edge. - */ - nbx = nx + dx; - nby = ny + dy; - if (nbx < 0 || nbx >= w || nby < 0 || nby >= h) - return -1; - - /* - * That third square must be able to accept a barrel. - */ - if (state->grid[nby*w+nbx] == SPACE || - state->grid[nby*w+nbx] == TARGET || - state->grid[nby*w+nbx] == PIT || - state->grid[nby*w+nbx] == DEEP_PIT) { - /* - * The push is valid. - */ - return 1; - } else { - return -1; - } - } else { - /* - * This is just an ordinary move. We've already checked the - * target square, so the only thing left to check is that a - * diagonal move has a space on one side to have notionally - * gone through. - */ - if (dx && dy && - state->grid[(py+dy)*w+px] != SPACE && - state->grid[(py+dy)*w+px] != TARGET && - state->grid[py*w+(px+dx)] != SPACE && - state->grid[py*w+(px+dx)] != TARGET) - return -1; - - /* - * Otherwise, the move is valid. - */ - return 0; - } -} - -static char *interpret_move(const game_state *state, game_ui *ui, - const game_drawstate *ds, - int x, int y, int button) -{ - int dx=0, dy=0; - char *move; - - /* - * Diagonal movement is supported as it is in NetHack: it's - * for movement only (never pushing), and one of the two - * squares adjacent to both the source and destination - * squares must be free to move through. In other words, it - * is only a shorthand for two orthogonal moves and cannot - * change the nature of the actual puzzle game. - */ - if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8')) - dx = 0, dy = -1; - else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2')) - dx = 0, dy = +1; - else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4')) - dx = -1, dy = 0; - else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6')) - dx = +1, dy = 0; - else if (button == (MOD_NUM_KEYPAD | '7')) - dx = -1, dy = -1; - else if (button == (MOD_NUM_KEYPAD | '9')) - dx = +1, dy = -1; - else if (button == (MOD_NUM_KEYPAD | '1')) - dx = -1, dy = +1; - else if (button == (MOD_NUM_KEYPAD | '3')) - dx = +1, dy = +1; - else if (button == LEFT_BUTTON) - { - if(x < COORD(state->px)) - dx = -1; - else if (x > COORD(state->px + 1)) - dx = 1; - if(y < COORD(state->py)) - dy = -1; - else if (y > COORD(state->py + 1)) - dy = 1; - } - else - return NULL; - - if((dx == 0) && (dy == 0)) - return(NULL); - - if (move_type(state, dx, dy) < 0) - return NULL; - - move = snewn(2, char); - move[1] = '\0'; - move[0] = '5' - 3*dy + dx; - return move; -} - -static game_state *execute_move(const game_state *state, const char *move) -{ - int w = state->p.w, h = state->p.h; - int px = state->px, py = state->py; - int dx, dy, nx, ny, nbx, nby, type, m, i; - bool freebarrels, freetargets; - game_state *ret; - - if (*move < '1' || *move == '5' || *move > '9' || move[1]) - return NULL; /* invalid move string */ - - m = *move - '0'; - dx = (m + 2) % 3 - 1; - dy = 2 - (m + 2) / 3; - type = move_type(state, dx, dy); - if (type < 0) - return NULL; - - ret = dup_game(state); - - nx = px + dx; - ny = py + dy; - nbx = nx + dx; - nby = ny + dy; - - if (type) { - int b; - - /* - * Push. - */ - b = ret->grid[ny*w+nx]; - if (IS_ON_TARGET(b)) { - ret->grid[ny*w+nx] = TARGET; - b = DETARGETISE(b); - } else - ret->grid[ny*w+nx] = SPACE; - - if (ret->grid[nby*w+nbx] == PIT) - ret->grid[nby*w+nbx] = SPACE; - else if (ret->grid[nby*w+nbx] == DEEP_PIT) - /* do nothing - the pit eats the barrel and remains there */; - else if (ret->grid[nby*w+nbx] == TARGET) - ret->grid[nby*w+nbx] = TARGETISE(b); - else - ret->grid[nby*w+nbx] = b; - } - - ret->px = nx; - ret->py = ny; - - /* - * Check for completion. This is surprisingly complicated, - * given the presence of pits and deep pits, and also the fact - * that some Sokoban levels with pits have fewer pits than - * barrels (due to providing spares, e.g. NetHack's). I think - * the completion condition in fact must be that the game - * cannot become any _more_ complete. That is, _either_ there - * are no remaining barrels not on targets, _or_ there is a - * good reason why any such barrels cannot be placed. The only - * available good reason is that there are no remaining pits, - * no free target squares, and no deep pits at all. - */ - if (!ret->completed) { - freebarrels = false; - freetargets = false; - for (i = 0; i < w*h; i++) { - int v = ret->grid[i]; - - if (IS_BARREL(v) && !IS_ON_TARGET(v)) - freebarrels = true; - if (v == DEEP_PIT || v == PIT || - (!IS_BARREL(v) && IS_ON_TARGET(v))) - freetargets = true; - } - - if (!freebarrels || !freetargets) - ret->completed = true; - } - - return ret; -} - -/* ---------------------------------------------------------------------- - * Drawing routines. - */ - -static void game_compute_size(const game_params *params, int tilesize, - int *x, int *y) -{ - /* Ick: fake up `ds->tilesize' for macro expansion purposes */ - struct { int tilesize; } ads, *ds = &ads; - ads.tilesize = tilesize; - - *x = 2 * BORDER + 1 + params->w * TILESIZE; - *y = 2 * BORDER + 1 + params->h * TILESIZE; -} - -static void game_set_size(drawing *dr, game_drawstate *ds, - const game_params *params, int tilesize) -{ - ds->tilesize = tilesize; -} - -static float *game_colours(frontend *fe, int *ncolours) -{ - float *ret = snewn(3 * NCOLOURS, float); - int i; - - game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); - - ret[COL_OUTLINE * 3 + 0] = 0.0F; - ret[COL_OUTLINE * 3 + 1] = 0.0F; - ret[COL_OUTLINE * 3 + 2] = 0.0F; - - ret[COL_PLAYER * 3 + 0] = 0.0F; - ret[COL_PLAYER * 3 + 1] = 1.0F; - ret[COL_PLAYER * 3 + 2] = 0.0F; - - ret[COL_BARREL * 3 + 0] = 0.6F; - ret[COL_BARREL * 3 + 1] = 0.3F; - ret[COL_BARREL * 3 + 2] = 0.0F; - - ret[COL_TARGET * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0]; - ret[COL_TARGET * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1]; - ret[COL_TARGET * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2]; - - ret[COL_PIT * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0] / 2; - ret[COL_PIT * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1] / 2; - ret[COL_PIT * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2] / 2; - - ret[COL_DEEP_PIT * 3 + 0] = 0.0F; - ret[COL_DEEP_PIT * 3 + 1] = 0.0F; - ret[COL_DEEP_PIT * 3 + 2] = 0.0F; - - ret[COL_TEXT * 3 + 0] = 1.0F; - ret[COL_TEXT * 3 + 1] = 1.0F; - ret[COL_TEXT * 3 + 2] = 1.0F; - - ret[COL_GRID * 3 + 0] = ret[COL_LOWLIGHT * 3 + 0]; - ret[COL_GRID * 3 + 1] = ret[COL_LOWLIGHT * 3 + 1]; - ret[COL_GRID * 3 + 2] = ret[COL_LOWLIGHT * 3 + 2]; - - ret[COL_OUTLINE * 3 + 0] = 0.0F; - ret[COL_OUTLINE * 3 + 1] = 0.0F; - ret[COL_OUTLINE * 3 + 2] = 0.0F; - - for (i = 0; i < 3; i++) { - ret[COL_WALL * 3 + i] = (3 * ret[COL_BACKGROUND * 3 + i] + - 1 * ret[COL_HIGHLIGHT * 3 + i]) / 4; - } - - *ncolours = NCOLOURS; - return ret; -} - -static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) -{ - int w = state->p.w, h = state->p.h; - struct game_drawstate *ds = snew(struct game_drawstate); - int i; - - ds->tilesize = 0; - ds->p = state->p; /* structure copy */ - ds->grid = snewn(w*h, unsigned short); - for (i = 0; i < w*h; i++) - ds->grid[i] = INVALID; - ds->started = false; - - return ds; -} - -static void game_free_drawstate(drawing *dr, game_drawstate *ds) -{ - sfree(ds->grid); - sfree(ds); -} - -static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v) -{ - int tx = COORD(x), ty = COORD(y); - int bg = (v & 0x100 ? COL_HIGHLIGHT : COL_BACKGROUND); - - v &= 0xFF; - - clip(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1); - draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, bg); - - if (v == WALL) { - int coords[6]; - - coords[0] = tx + TILESIZE; - coords[1] = ty + TILESIZE; - coords[2] = tx + TILESIZE; - coords[3] = ty + 1; - coords[4] = tx + 1; - coords[5] = ty + TILESIZE; - draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT); - - coords[0] = tx + 1; - coords[1] = ty + 1; - draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); - - draw_rect(dr, tx + 1 + HIGHLIGHT_WIDTH, ty + 1 + HIGHLIGHT_WIDTH, - TILESIZE - 2*HIGHLIGHT_WIDTH, - TILESIZE - 2*HIGHLIGHT_WIDTH, COL_WALL); - } else if (v == PIT) { - draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, - TILESIZE*3/7, COL_PIT, COL_OUTLINE); - } else if (v == DEEP_PIT) { - draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, - TILESIZE*3/7, COL_DEEP_PIT, COL_OUTLINE); - } else { - if (IS_ON_TARGET(v)) { - draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, - TILESIZE*3/7, COL_TARGET, COL_OUTLINE); - } - if (IS_PLAYER(v)) { - draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, - TILESIZE/3, COL_PLAYER, COL_OUTLINE); - } else if (IS_BARREL(v)) { - char str[2]; - - draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, - TILESIZE/3, COL_BARREL, COL_OUTLINE); - str[1] = '\0'; - str[0] = BARREL_LABEL(v); - if (str[0]) { - draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, - FONT_VARIABLE, TILESIZE/2, - ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str); - } - } - } - - unclip(dr); - draw_update(dr, tx, ty, TILESIZE, TILESIZE); -} - -static void game_redraw(drawing *dr, game_drawstate *ds, - const game_state *oldstate, const game_state *state, - int dir, const game_ui *ui, - float animtime, float flashtime) -{ - int w = state->p.w, h = state->p.h /*, wh = w*h */; - int x, y; - int flashtype; - - if (flashtime && - !((int)(flashtime * 3 / FLASH_LENGTH) % 2)) - flashtype = 0x100; - else - flashtype = 0; - - /* - * Initialise a fresh drawstate. - */ - if (!ds->started) { - int wid, ht; - - /* - * Blank out the window initially. - */ - game_compute_size(&ds->p, TILESIZE, &wid, &ht); - draw_rect(dr, 0, 0, wid, ht, COL_BACKGROUND); - draw_update(dr, 0, 0, wid, ht); - - /* - * Draw the grid lines. - */ - for (y = 0; y <= h; y++) - draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), - COL_LOWLIGHT); - for (x = 0; x <= w; x++) - draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), - COL_LOWLIGHT); - - ds->started = true; - } - - /* - * Draw the grid contents. - */ - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) { - int v = state->grid[y*w+x]; - if (y == state->py && x == state->px) { - if (v == TARGET) - v = PLAYERTARGET; - else { - assert(v == SPACE); - v = PLAYER; - } - } - - v |= flashtype; - - if (ds->grid[y*w+x] != v) { - draw_tile(dr, ds, x, y, v); - ds->grid[y*w+x] = v; - } - } - -} - -static float game_anim_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - return 0.0F; -} - -static float game_flash_length(const game_state *oldstate, - const game_state *newstate, int dir, game_ui *ui) -{ - if (!oldstate->completed && newstate->completed) - return FLASH_LENGTH; - else - return 0.0F; -} - -static void game_get_cursor_location(const game_ui *ui, - const game_drawstate *ds, - const game_state *state, - const game_params *params, - int *x, int *y, int *w, int *h) -{ -} - -static int game_status(const game_state *state) -{ - return state->completed ? +1 : 0; -} - -static bool game_timing_state(const game_state *state, game_ui *ui) -{ - return true; -} - -static void game_print_size(const game_params *params, float *x, float *y) -{ -} - -static void game_print(drawing *dr, const game_state *state, int tilesize) -{ -} - -#ifdef COMBINED -#define thegame sokoban -#endif - -const struct game thegame = { - "Sokoban", NULL, NULL, - default_params, - game_fetch_preset, NULL, - decode_params, - encode_params, - free_params, - dup_params, - true, game_configure, custom_params, - validate_params, - new_game_desc, - validate_desc, - new_game, - dup_game, - free_game, - false, solve_game, - false, game_can_format_as_text_now, game_text_format, - new_ui, - free_ui, - encode_ui, - decode_ui, - NULL, /* game_request_keys */ - game_changed_state, - interpret_move, - execute_move, - PREFERRED_TILESIZE, game_compute_size, game_set_size, - game_colours, - game_new_drawstate, - game_free_drawstate, - game_redraw, - game_anim_length, - game_flash_length, - game_get_cursor_location, - game_status, - false, false, game_print_size, game_print, - false, /* wants_statusbar */ - false, game_timing_state, - 0, /* flags */ -}; diff --git a/apps/plugins/puzzles/src/wceinf.pl b/apps/plugins/puzzles/src/wceinf.pl deleted file mode 100644 index 4756f3c2b8..0000000000 --- a/apps/plugins/puzzles/src/wceinf.pl +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/perl - -# Perl script to generate a .INF file for building a Pocket PC .CAB -# archive of Puzzles. This has to be scripted so that it can read -# gamedesc.txt and automatically adjust to the current available -# set of puzzles. - -# Usage: -# -# $ ./wceinf.pl gamedesc.txt > puzzles.inf - -$desc = shift @ARGV; -open DESC, "<", $desc; -while () { - chomp; - @_ = split /:/; - push @exes, $_[1]; - $names{$_[1]} = $_[2]; -} -close DESC; - -print '[Version]'."\n"; -print 'Signature = "$Windows NT$" ; required as-is'."\n"; -print 'Provider = "Simon Tatham" ; full app name will be " "'."\n"; -print 'CESignature = "$Windows CE$" ; required as-is'."\n"; -print ''."\n"; -print '[CEStrings]'."\n"; -print 'AppName = "Puzzle Collection" ; full app name will be " "'."\n"; -print 'InstallDir = %CE8%\%AppName% ; "\Program Files\Games\Puzzle Collection" (default install directory)'."\n"; -print ''."\n"; -print '[CEDevice.x86]'."\n"; -print 'ProcessorType = 686'."\n"; -print ''."\n"; -print '[CEDevice.ARM]'."\n"; -print 'ProcessorType = 2577'."\n"; -print ''."\n"; -print '[SourceDisksNames.x86] ; CPU-dependent files'."\n"; -print '2 = ,"x86 Files",,.'."\n"; -print ''."\n"; -print '[SourceDisksNames.ARMV4] ; CPU-dependent files'."\n"; -print '2 = ,"ARM Files",,.'."\n"; -print ''."\n"; -print '[SourceDisksFiles]'."\n"; -for $exe (@exes) { - print $exe.' = 2'."\n"; -} -print ''."\n"; -print '[DefaultInstall]'."\n"; -print 'CopyFiles = PuzzleFiles'."\n"; -print 'CEShortcuts = Links'."\n"; -print ''."\n"; -print '[DestinationDirs]'."\n"; -print 'PuzzleFiles = 0, %InstallDir%'."\n"; -print 'Links = 0, %CE14%\Puzzles'."\n"; -print ''."\n"; -print ';File copy list.'."\n"; -print '[PuzzleFiles]'."\n"; -for $exe (@exes) { - print $exe."\n"; -} -print ''."\n"; -print '[Links]'."\n"; -for $exe (@exes) { - print '"'.$names{$exe}.'",0,'.$exe."\n"; -} diff --git a/apps/plugins/puzzles/src/webpage.pl b/apps/plugins/puzzles/src/webpage.pl deleted file mode 100755 index c6144bb467..0000000000 --- a/apps/plugins/puzzles/src/webpage.pl +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/perl - -# Construct the two pieces of my main puzzle collection web page that -# need to vary with the set of puzzles: the big list of s with -# puzzle pictures and links etc, and the list of Windows executable -# files down in the downloads section. - -use strict; -use warnings; -use HTML::Entities; - -open my $desc, "<", "gamedesc.txt" - or die "gamedesc.txt: open: $!\n"; - -open my $spans, ">", "wwwspans.html" - or die "wwwspans.html: open: $!\n"; - -open my $links, ">", "wwwlinks.html" - or die "wwwspans.html: open: $!\n"; - -my $n = 0; -while (<$desc>) { - chomp; - my ($id, $win, $displayname, $description, $summary) = split /:/, $_; - - printf $spans - ''. - ''. - ''. - ''. - '
%s
'. - ''. - '
['. - ' java '. - '|'. - ' js '. - '|'. - ' manual '. - ']
['. - ' %s '. - ']
%s
'. - "\n", - encode_entities($displayname), - encode_entities($id), - encode_entities($description), - encode_entities($id), - encode_entities($id), - encode_entities($id), - encode_entities($id), - encode_entities($id), - encode_entities($win), - encode_entities($win), - encode_entities($summary); - - if ($n > 0) { - if ($n % 5 == 0) { - print $links "
"; - } else { - print $links " | "; - } - } - printf $links '%s', - encode_entities($win), encode_entities($win); - - $n++; -} - -close $desc; -close $spans; -close $links; diff --git a/apps/plugins/puzzles/src/website.url b/apps/plugins/puzzles/src/website.url deleted file mode 100644 index 2913e622c6..0000000000 --- a/apps/plugins/puzzles/src/website.url +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ diff --git a/apps/plugins/puzzles/src/winiss.pl b/apps/plugins/puzzles/src/winiss.pl deleted file mode 100755 index 3200337138..0000000000 --- a/apps/plugins/puzzles/src/winiss.pl +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/perl - -# Perl script to generate an Inno Setup installer script for -# Puzzles. This has to be scripted so that it can read gamedesc.txt -# and automatically adjust to the current available set of puzzles. - -# Usage: -# -# $ ./winiss.pl 20140922.sdfsdf gamedesc.txt > puzzles.iss -# -# where the first argument is the version number which will be encoded -# in the installer's version indicators. The first component of that -# version number will be expected to be a YYYYMMDD-format date. - -use warnings; -use Time::Local; - -$ver = shift @ARGV; - -# Parse the date out of $ver, and convert it into an integer number of -# days since an arbitrary epoch. This number is used for the Windows -# version resource (which wants a monotonic 16-bit integer). The epoch -# is chosen so that the first build using this date-based mechanism -# has a higher number than the last build in which that number was -# derived from a Subversion revision. -die "bad date format" if $ver !~ /^(\d{4})(\d{2})(\d{2})/; -$date = timegm(0,0,0,$3,$2-1,$1); -$integer_date = int($date / 86400) - 6000; - -$desc = shift @ARGV; -open DESC, "<", $desc; -while () { - chomp; - @_ = split /:/; - push @exes, $_[1]; - $names{$_[1]} = $_[2]; -} -close DESC; - -print '; -*- no -*-'."\n"; -print ';'."\n"; -print '; -- Inno Setup installer script for Puzzles.'."\n"; -print ''."\n"; -print '[Setup]'."\n"; -print 'AppName=Simon Tatham\'s Portable Puzzle Collection'."\n"; -print 'AppVerName=Puzzles version '.$ver."\n"; -print 'VersionInfoTextVersion=Version '.$ver."\n"; -print 'AppVersion=r'.$ver."\n"; -print 'VersionInfoVersion=0.0.'.$integer_date.'.0'."\n"; -print 'AppPublisher=Simon Tatham'."\n"; -print 'AppPublisherURL=https://www.chiark.greenend.org.uk/~sgtatham/puzzles/'."\n"; -print 'DefaultDirName={pf}\Simon Tatham\'s Portable Puzzle Collection'."\n"; -print 'DefaultGroupName=Simon Tatham\'s Puzzles'."\n"; -# print 'SetupIconFile=fixmethinkoneup.ico'."\n"; -# print 'UninstallDisplayIcon={app}\fixmethinkoneup.exe'."\n"; -print 'ChangesAssociations=no'."\n"; -print 'Compression=zip/9'."\n"; -print 'AllowNoIcons=yes'."\n"; -print 'OutputBaseFilename=installer'."\n"; -print ''."\n"; -print '[Files]'."\n"; -for $exe (@exes) { - print 'Source: "'.$exe.'"; DestDir: "{app}"; Flags: promptifolder replacesameversion uninsrestartdelete'."\n"; -} -print 'Source: "website.url"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n"; -print 'Source: "puzzles.chm"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n"; -print 'Source: "puzzles.hlp"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n"; -print 'Source: "puzzles.cnt"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n"; -print 'Source: "LICENCE"; DestDir: "{app}"; Flags: uninsrestartdelete'."\n"; -print ''."\n"; -print '[Icons]'."\n"; -for $exe (@exes) { - print 'Name: "{group}\\'.$names{$exe}.'"; Filename: "{app}\\'.$exe.'"'."\n"; -} -print '; We have to fall back from the .chm to the older .hlp file on some Windows'."\n"; -print '; versions.'."\n"; -print 'Name: "{group}\Puzzles Manual"; Filename: "{app}\puzzles.chm"; MinVersion: 4.1,5.0'."\n"; -print 'Name: "{group}\Puzzles Manual"; Filename: "{app}\puzzles.hlp"; OnlyBelowVersion: 4.1,5.0'."\n"; -print 'Name: "{group}\Puzzles Web Site"; Filename: "{app}\website.url"'."\n"; diff --git a/apps/plugins/puzzles/src/winwix.mc b/apps/plugins/puzzles/src/winwix.mc deleted file mode 100644 index 58dbdbeefb..0000000000 --- a/apps/plugins/puzzles/src/winwix.mc +++ /dev/null @@ -1,340 +0,0 @@ -% # Source code for the Puzzles installer. -% # -% # The installer is built using WiX, but this file itself is not valid -% # XML input to WiX's candle.exe; instead, this is a template intended -% # to be processed through the standalone 'mason.pl' script provided -% # with Perl's Mason module. - -<%class> -has 'version' => (required => 1); -has 'descfile' => (required => 1); - - - - - - - - - - - - -% # Product tag. The Id component is set to "*", which causes WiX to -% # make up a new GUID every time it's run, whereas UpgradeCode is set -% # to a fixed GUID. This combination allows Windows to -% # recognise each new installer as different (because of Id) -% # versions of the same underlying thing (because of the common -% # UpgradeCode). - - -% # We force the install scope to perMachine, largely because I -% # don't really understand how to make it usefully switchable -% # between the two. If anyone is a WiX expert and does want to -% # install Puzzles locally in a user account, I hope they'll send a -% # well explained patch! - - -% # Permit installing an arbitrary one of these installers -% # over the top of an existing one, whether it's an upgrade or a -% # downgrade. -% # -% # Setting the REINSTALLMODE property to "amus" (from its default -% # of "omus") forces every component replaced by a different -% # version of the installer to be _actually_ reinstalled; the 'o' -% # flag in the default setting breaks the downgrade case by -% # causing Windows to disallow installation of an older version -% # over the top of a newer one - and to do so _silently_, so the -% # installer claims to have worked fine but the files that would have -% # been downgraded aren't there. - - - -% # Boilerplate - - -% # The actual directory structure and list of 'components' -% # (individual files or shortcuts or additions to PATH) that are -% # installed. - - - - -% # The following components all install things in the main -% # install directory (implicitly, by being nested where -% # they are in the XML structure). Most of them also put a -% # shortcut in a subdir of the Start menu, though some of -% # the more obscure things like LICENCE are just there for -% # the sake of being _somewhere_ and don't rate a shortcut. - -<%method file_component ($prefix, $filename, $shortcutname)> -% my $filename_id = file_component_name($filename); - - -% if (defined $shortcutname) { - -% } - - - - -% for my $exe (@exes) { -<% $.file_component('$(var.Bindir)', $exe, $names{$exe}) %> -% } - -<% $.file_component('', "puzzles.chm", "Puzzles Manual") %> -<% $.file_component('', "website.url", "Puzzles Web Site") %> -<% $.file_component('', "LICENCE") %> - - - - -% # This component doesn't actually install anything, but it -% # arranges for the Start Menu _directory_ to be removed again -% # on uninstall. All the actual shortcuts inside the directory -% # are placed by code above here. - - - - - - - - - - -% # Detect an installation of Puzzles made by the old Inno Setup -% # installer, and refuse to run if we find one. I don't know what -% # would happen if you tried anyway, but since they install files -% # at the same pathnames, it surely wouldn't end well. -% # -% # It could be argued that a better approach would be to actually -% # _launch_ the Inno Setup uninstaller automatically at this -% # point (prompting the user first, of course), but I'm not -% # nearly skilled enough with WiX to know how, or even if it's -% # feasible. - - - - - - - - - - -% # Separate the installation into 'features', which are parts of -% # the install that can be chosen separately. Trivial: there is only -% # one feature here. - -% for my $exe (@exes) { - -% } - " /> - " /> - " /> - - - -% # Installer user interface. -% # -% # Basically like WixUI_InstallDir, only I've ripped out the EULA. - - - - - - - - - - - - - - - - - - - - - - 1 - "1"]]> - - 1 - - NOT Installed - Installed - - 1 - 1 - NOT WIXUI_DONTVALIDATEPATH - "1"]]> - WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" - 1 - 1 - - NOT Installed - Installed AND NOT PATCH - Installed AND PATCH - - 1 - - 1 - 1 - 1 - -% # This ARPNOMODIFY flag prohibits changing the set of -% # installed features, because we don't have any. - - - -% # Glue: tell the install dir part of the UI what id my actual -% # install dir is known by. Otherwise the former won't know how -% # to alter the setting of the latter. - - -% # Include my custom installer artwork, created in Buildscr. FIXME: -% # create some! -% # -% # - - - - -<%init> - -use Digest::SHA qw(sha512_hex); -use Time::Local; - -die "bad date format" if $.version !~ /^(\d{4})(\d{2})(\d{2})/; -my $date = timegm(0,0,0,$3,$2-1,$1); -my $integer_date = int($date / 86400) - 6000; -my $winver = sprintf "0.0.%d.0", $integer_date; - -my @exes; -my %names; -{ - open my $descfh, "<", $.descfile or die "$.descfile: open: $!\n"; - while (<$descfh>) { - chomp; - my @fields = split /:/; - push @exes, $fields[1]; - $names{$fields[1]} = $fields[2]; - } - close $descfh; -} - -sub file_component_name($) { - my ($id) = @_; - $id =~ s!.*\\!!; - $id =~ y!A-Za-z0-9!_!cs; - return $id; -} - -sub invent_guid($) { - my ($name) = @_; - - # Invent a GUID. We'll need a lot of these in this installer - one - # per puzzle game and a few standard ones - and we'd like to - # arrange for them to be stable between versions of the installer, - # while not having to _manually_ invent one every time. - # - # So we generate our GUIDs by hashing a pile of fixed (but - # originally randomly generated) data with an identifying string - # that acts as source for the particular GUID. For example, - # hashing (fixed_random_data || "upgradecode") produces the GUID - # used as the upgrade code, and (fixed_random_data || - # "installfile:" || filename) gives the GUID for an MSI component - # that installs a specific file. - # - # Hashing _just_ the id strings would clearly be cheating (it's - # quite conceivable that someone might hash the same string for - # another reason and so generate a colliding GUID), but hashing a - # whole SHA-512 data block of random gibberish as well should make - # these GUIDs pseudo-random enough to not collide with anyone - # else's. - - my $randdata = pack "N*", - 0xCCAA8D31,0x42931BD9,0xA9D9878A,0x72E4FB9C,0xEA9B11DE,0x4FF17AEC, - 0x1AFA2DEC,0xB662640A,0x780143F5,0xBFFFF0FC,0x01CB46CF,0x832AE842, - 0xBCCDDA12,0x4DB24889,0xEC7A9BCD,0xBCCF70ED,0x85800659,0x8ABA9524, - 0xE484F8D6,0x5CBE55B3,0x95AD9B3D,0x0555F432,0x46737F89,0xE981471C, - 0x4B3419AD,0xD4E49DF4,0xB3EF69DE,0x2A7E391E,0xF3C3D370,0x3EA5E9FC, - 0xB35A57ED,0x52B21099,0x9BD99092,0x7B5097AE,0x4DBE59BD,0x2FCC709B, - 0xC555A779,0x4AE2E5AB,0xB7C74314,0x7F9194CF,0x8FFBCA88,0x46263306, - 0x4C714DF7,0x07FE8CEE,0x28974885,0x0869865D,0xBB5B0EA4,0x4064A928, - 0x28C41910,0x07030758,0x19E66319,0x050C9D4E,0xD79A37FB,0xF232D98B, - 0x0C3E4C25,0xC94F865B,0xB6D86BED,0x87DB718D,0xC65D4C43,0x7C8BBF6A, - 0x9DFDD26A,0x8C478976,0x53E47640,0x263E04AA,0xDD7C5456,0x766BDF50, - 0x86946E34,0xE80D8DE3,0xFB92949E,0x691FDAD0,0x96AB210D,0xB278D60B, - 0x71C7DC6B,0xD49846FC,0x38953F66,0x39777D72,0x4A0F80E5,0xFE1DD1A4, - 0xDA9D191A,0xA32844AD,0x171BFBCC,0xA7D556F6,0xF9F6D944,0xF9687060, - 0xDDDB81D0,0xE9AF4F2F,0xEF84685A,0x8A172492,0x50615EFC,0x20817881, - 0xC3E507E5,0x33189771,0xB9E2EBBD,0x2AAE34A3,0x8D3E7486,0x0A448F13, - 0x94F92A81,0x5150A34F,0x5ED50563,0xAD801A42,0xD0617AFA,0xB78F85F7, - 0x0019D384,0xF0F1C06F,0x6EF8D5B3,0x38092D09,0xC87CD4B3,0xE9D8C84F, - 0xE036648C,0xF2500BD9,0xCF069B5C,0x835326BA,0xCD107B6A,0x64F152B3, - 0xA9663E24,0x33ED5E08,0xC3B24F7E,0xA83205C8,0xA0560F30,0xDFF1226E, - 0xF1A404B7,0x9C2B4EF2,0x62802F88,0xE393A05F,0xC7F72F7B,0x1CD989BD, - 0x725AB516,0xA84F7E39,0xACC3572A,0x820ACB2D,0xAFF5BF06,0x476A2405, - 0x90953482,0x8E8035E1,0x1FB95F6E,0x01FD6766,0x1E63D78E,0xD7D42220, - 0x188D23E4,0x1941BCC5,0xEE1E6487,0x6E197F82,0x32772265,0x9B79D0C8, - 0xB4B617A1,0xCD2475B4,0xDE0F410B,0xE9CF69E4,0x831AC9A4,0xD549A00E, - 0x12ECC561,0x3D636A43,0x1FFFC99A,0xF79401C5,0xAA1D8251,0x84D29609, - 0x5464CB71,0xB28AAE5A,0x4AD934FC,0x347E8A5D,0xC87BCBA0,0x67172E33, - 0xEC70E245,0x4289A9EF,0xA8AF6BA5,0x1528FE0C,0xA87CBFF8,0x79AE1554, - 0xBD59DB9E,0xF1879F94,0x14D7E9F6,0x85196447,0xC4363A67,0x7E02A325, - 0x54051E05,0xABAFE646,0x65D5DF96,0xD3F8173B,0x09D475E7,0x9BF7BD0C, - 0x2DAF371A,0x793D063C,0xA68FD47B,0xBE2500A7,0x0D5071C4,0x08384AC8, - 0xF6CFE74E,0x124A5086,0x03475917,0x47267765,0x56F7DF31,0xE5696381, - 0xEB2B4668,0x78345B5B,0x6E2AFC0F,0x3AD0D43B,0x5C3C2BC9,0x833AB4A0, - 0x1DE2CDBF,0x4DDDCF58,0xEA25D69B,0x36E9B3B0,0xC8B11465,0x066A997E, - 0x9D51C7CD,0x8C6AE686,0xAFB06CE6,0xCC3F3017,0x6E4E4CC0,0x85A34875, - 0x498FE759,0xC24B6332,0xEBCD2257,0xE70FC659,0x439EC788,0xB47F2A06, - 0x696EE8A7,0xF70A31B8,0xECD840F7,0x80AE5E7A,0xC6EDF8AE,0x8165EAFD, - 0x5DAE5ADE,0x9FFD39CE,0xFC6B4C23,0x02BCA024,0xC1497A68,0xD18153EF, - 0xD787EA51,0x91386720,0xBF6E2200,0x98638391,0x144B9148,0x9A554DE1, - 0xA940DC7F,0x37C08265,0x7B782C60,0xC081FDD7,0x62B47201,0x43427563, - 0x1B66E983,0x3DAC0305,0x21E9DEA8,0xA9490EE0,0xE2EFD37D,0x3501F306, - 0x16361BD5,0x668B219D,0x17177844,0x3861A9A4,0x222403B2,0xB29F6714, - 0x7A2A938A,0xBC797418,0x3B241624,0x9163C3F2; - my $digest = sha512_hex($name . "\0" . $randdata); - return sprintf("%s-%s-%04x-%04x-%s", - substr($digest,0,8), - substr($digest,8,4), - 0x4000 | (0xFFF & hex(substr($digest,12,4))), - 0x8000 | (0x3FFF & hex(substr($digest,16,4))), - substr($digest,20,12)); -} -